diff --git a/.evergreen/auth_aws/README.md b/.evergreen/auth_aws/README.md deleted file mode 100644 index 028e3d0f2..000000000 --- a/.evergreen/auth_aws/README.md +++ /dev/null @@ -1,4 +0,0 @@ -# Configuration Scripts for End-to-end Testing - -These scripts were taken from [mongo-enterprise-modules](https://github.com/10gen/mongo-enterprise-modules/tree/master/jstests/external_auth_aws) -and intended to simplify creating users, attaching roles to existing EC2 instances, launching an Amazon ECS container instance, etc. \ No newline at end of file diff --git a/.evergreen/auth_aws/aws_e2e_assume_role.js b/.evergreen/auth_aws/aws_e2e_assume_role.js deleted file mode 100644 index ae5169667..000000000 --- a/.evergreen/auth_aws/aws_e2e_assume_role.js +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Verify the AWS IAM Auth works with temporary credentials from sts:AssumeRole - */ - -load("lib/aws_e2e_lib.js"); - -(function() { -"use strict"; - -const ASSUMED_ROLE = "arn:aws:sts::557821124784:assumed-role/authtest_user_assume_role/*"; - -function getAssumeCredentials() { - const config = readSetupJson(); - - const env = { - AWS_ACCESS_KEY_ID: config["iam_auth_assume_aws_account"], - AWS_SECRET_ACCESS_KEY: config["iam_auth_assume_aws_secret_access_key"], - }; - - const role_name = config["iam_auth_assume_role_name"]; - - const python_command = getPython3Binary() + - ` -u lib/aws_assume_role.py --role_name=${role_name} > creds.json`; - - const ret = runShellCmdWithEnv(python_command, env); - assert.eq(ret, 0, "Failed to assume role on the current machine"); - - const result = cat("creds.json"); - try { - return JSON.parse(result); - } catch (e) { - jsTestLog("Failed to parse: " + result); - throw e; - } -} - -const credentials = getAssumeCredentials(); -const admin = Mongo().getDB("admin"); -const external = admin.getMongo().getDB("$external"); - -assert(admin.auth("bob", "pwd123")); -assert.commandWorked(external.runCommand({createUser: ASSUMED_ROLE, roles:[{role: 'read', db: "aws"}]})); -assert(external.auth({ - user: credentials["AccessKeyId"], - pwd: credentials["SecretAccessKey"], - awsIamSessionToken: credentials["SessionToken"], - mechanism: 'MONGODB-AWS' -})); -}()); diff --git a/.evergreen/auth_aws/aws_e2e_ec2.js b/.evergreen/auth_aws/aws_e2e_ec2.js deleted file mode 100644 index a492db86d..000000000 --- a/.evergreen/auth_aws/aws_e2e_ec2.js +++ /dev/null @@ -1,58 +0,0 @@ -/** - * Verify the AWS IAM EC2 hosted auth works - */ -load("lib/aws_e2e_lib.js"); - -(function() { -"use strict"; - -// This varies based on hosting EC2 as the account id and role name can vary -const AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/authtest_instance_profile_role/*"; - -function assignInstanceProfile() { - const config = readSetupJson(); - - const env = { - AWS_ACCESS_KEY_ID: config["iam_auth_ec2_instance_account"], - AWS_SECRET_ACCESS_KEY: config["iam_auth_ec2_instance_secret_access_key"], - }; - - const instanceProfileName = config["iam_auth_ec2_instance_profile"]; - const python_command = getPython3Binary() + - ` -u lib/aws_assign_instance_profile.py --instance_profile_arn=${instanceProfileName}`; - - const ret = runShellCmdWithEnv(python_command, env); - if (ret == 2) { - print("WARNING: Request limit exceeded for AWS API"); - return false; - } - - assert.eq(ret, 0, "Failed to assign an instance profile to the current machine"); - return true; -} - -if (!assignInstanceProfile()) { - return; -} - -const admin = Mongo().getDB("admin"); -const external = admin.getMongo().getDB("$external"); - -assert(admin.auth("bob", "pwd123")); -assert.commandWorked(external.runCommand({createUser: AWS_ACCOUNT_ARN, roles:[{role: 'read', db: "aws"}]})); - -// Try the command line -const smoke = runMongoProgram("mongo", - "--host", - "localhost", - '--authenticationMechanism', - 'MONGODB-AWS', - '--authenticationDatabase', - '$external', - "--eval", - "1"); -assert.eq(smoke, 0, "Could not auth with smoke user"); - -// Try the auth function -assert(external.auth({mechanism: 'MONGODB-AWS'})); -}()); diff --git a/.evergreen/auth_aws/aws_e2e_ecs.js b/.evergreen/auth_aws/aws_e2e_ecs.js deleted file mode 100644 index 8efd8cb43..000000000 --- a/.evergreen/auth_aws/aws_e2e_ecs.js +++ /dev/null @@ -1,44 +0,0 @@ -/** - * Validate that MONGODB-AWS auth works from ECS temporary credentials. - */ -load("lib/aws_e2e_lib.js"); - -(function() { - 'use strict'; - - assert.eq(typeof mongo_binaries != 'undefined', true, "mongo_binaries must be set"); - assert.eq(typeof project_dir != 'undefined', true, "project_dir must be set"); - - const config = readSetupJson(); - - const base_command = getPython3Binary() + " -u lib/container_tester.py"; - const run_prune_command = base_command + ' -v remote_gc_services ' + - ' --cluster ' + config['iam_auth_ecs_cluster']; - - const run_test_command = base_command + ' -d -v run_e2e_test' + - ' --cluster ' + config['iam_auth_ecs_cluster'] + ' --task_definition ' + - config['iam_auth_ecs_task_definition'] + ' --subnets ' + - config['iam_auth_ecs_subnet_a'] + ' --subnets ' + - config['iam_auth_ecs_subnet_b'] + ' --security_group ' + - config['iam_auth_ecs_security_group'] + - ` --files ${mongo_binaries}/mongod:/root/mongod ${mongo_binaries}/mongo:/root/mongo ` + - " lib/ecs_hosted_test.js:/root/ecs_hosted_test.js " + - `${project_dir}:/root` + - " --script lib/ecs_hosted_test.sh"; - - // Pass in the AWS credentials as environment variables - // AWS_SHARED_CREDENTIALS_FILE does not work in evergreen for an unknown - // reason - const env = { - AWS_ACCESS_KEY_ID: config['iam_auth_ecs_account'], - AWS_SECRET_ACCESS_KEY: config['iam_auth_ecs_secret_access_key'], - }; - - // Prune other containers - let ret = runWithEnv(['/bin/sh', '-c', run_prune_command], env); - assert.eq(ret, 0, 'Prune Container failed'); - - // Run the test in a container - ret = runWithEnv(['/bin/sh', '-c', run_test_command], env); - assert.eq(ret, 0, 'Container Test failed'); -}()); diff --git a/.evergreen/auth_aws/aws_e2e_regular_aws.js b/.evergreen/auth_aws/aws_e2e_regular_aws.js deleted file mode 100644 index 1c4f2d032..000000000 --- a/.evergreen/auth_aws/aws_e2e_regular_aws.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * Validate that the server supports real credentials from AWS and can talk to a real AWS STS - * service - */ -load("lib/aws_e2e_lib.js"); - -(function() { -"use strict"; - -const admin = Mongo().getDB("admin"); -const external = admin.getMongo().getDB("$external"); -assert(admin.auth("bob", "pwd123")); - -const config = readSetupJson(); -assert.commandWorked( - external.runCommand({createUser: config["iam_auth_ecs_account_arn"], roles:[{role: 'read', db: "aws"}]})); - -assert(external.auth({ - user: config["iam_auth_ecs_account"], - pwd: config["iam_auth_ecs_secret_access_key"], - mechanism: 'MONGODB-AWS' -})); -}()); \ No newline at end of file diff --git a/.evergreen/auth_aws/lib/aws_assign_instance_profile.py b/.evergreen/auth_aws/lib/aws_assign_instance_profile.py deleted file mode 100644 index cb3ad154d..000000000 --- a/.evergreen/auth_aws/lib/aws_assign_instance_profile.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python3 -""" -Script for assign an instance policy to the current machine. -""" - -import argparse -import urllib.request -import logging -import sys -import time - -import boto3 -import botocore - -LOGGER = logging.getLogger(__name__) - -def _get_local_instance_id(): - return urllib.request.urlopen('http://169.254.169.254/latest/meta-data/instance-id').read().decode() - -def _has_instance_profile(): - base_url = "http://169.254.169.254/latest/meta-data/iam/security-credentials/" - try: - print("Reading: " + base_url) - iam_role = urllib.request.urlopen(base_url).read().decode() - except urllib.error.HTTPError as e: - print(e) - if e.code == 404: - return False - raise e - - try: - url = base_url + iam_role - print("Reading: " + url) - req = urllib.request.urlopen(url) - except urllib.error.HTTPError as e: - print(e) - if e.code == 404: - return False - raise e - - return True - -def _wait_instance_profile(): - retry = 60 - while not _has_instance_profile() and retry: - time.sleep(5) - retry -= 1 - - if retry == 0: - raise ValueError("Timeout on waiting for instance profile") - -def _assign_instance_policy(iam_instance_arn): - - if _has_instance_profile(): - print("IMPORTANT: Found machine already has instance profile, skipping the assignment") - return - - instance_id = _get_local_instance_id() - - ec2_client = boto3.client("ec2", 'us-east-1') - - #https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ec2.html#EC2.Client.associate_iam_instance_profile - try: - response = ec2_client.associate_iam_instance_profile( - IamInstanceProfile={ - 'Arn' : iam_instance_arn, - }, - InstanceId = instance_id) - - print(response) - - # Wait for the instance profile to be assigned by polling the local instance metadata service - _wait_instance_profile() - - except botocore.exceptions.ClientError as ce: - if ce.response["Error"]["Code"] == "RequestLimitExceeded": - print("WARNING: RequestLimitExceeded, exiting with error code 2") - sys.exit(2) - raise - -def main() -> None: - """Execute Main entry point.""" - - parser = argparse.ArgumentParser(description='IAM Assign Instance frontend.') - - parser.add_argument('-v', "--verbose", action='store_true', help="Enable verbose logging") - parser.add_argument('-d', "--debug", action='store_true', help="Enable debug logging") - - parser.add_argument('--instance_profile_arn', type=str, help="Name of instance profile") - - args = parser.parse_args() - - if args.debug: - logging.basicConfig(level=logging.DEBUG) - elif args.verbose: - logging.basicConfig(level=logging.INFO) - - _assign_instance_policy(args.instance_profile_arn) - - -if __name__ == "__main__": - main() diff --git a/.evergreen/auth_aws/lib/aws_assume_role.py b/.evergreen/auth_aws/lib/aws_assume_role.py deleted file mode 100644 index 6df1fc7ef..000000000 --- a/.evergreen/auth_aws/lib/aws_assume_role.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python3 -""" -Script for assuming an aws role. -""" - -import argparse -import uuid -import logging - -import boto3 - -LOGGER = logging.getLogger(__name__) - -STS_DEFAULT_ROLE_NAME = "arn:aws:iam::579766882180:role/mark.benvenuto" - -def _assume_role(role_name): - sts_client = boto3.client("sts") - - response = sts_client.assume_role(RoleArn=role_name, RoleSessionName=str(uuid.uuid4()), DurationSeconds=900) - - creds = response["Credentials"] - - - print(f"""{{ - "AccessKeyId" : "{creds["AccessKeyId"]}", - "SecretAccessKey" : "{creds["SecretAccessKey"]}", - "SessionToken" : "{creds["SessionToken"]}", - "Expiration" : "{str(creds["Expiration"])}" -}}""") - - -def main() -> None: - """Execute Main entry point.""" - - parser = argparse.ArgumentParser(description='Assume Role frontend.') - - parser.add_argument('-v', "--verbose", action='store_true', help="Enable verbose logging") - parser.add_argument('-d', "--debug", action='store_true', help="Enable debug logging") - - parser.add_argument('--role_name', type=str, default=STS_DEFAULT_ROLE_NAME, help="Role to assume") - - args = parser.parse_args() - - if args.debug: - logging.basicConfig(level=logging.DEBUG) - elif args.verbose: - logging.basicConfig(level=logging.INFO) - - _assume_role(args.role_name) - - -if __name__ == "__main__": - main() diff --git a/.evergreen/auth_aws/lib/aws_e2e_lib.js b/.evergreen/auth_aws/lib/aws_e2e_lib.js deleted file mode 100644 index d38471ac8..000000000 --- a/.evergreen/auth_aws/lib/aws_e2e_lib.js +++ /dev/null @@ -1,39 +0,0 @@ - -function readSetupJson() { - let result; - try { - result = cat("aws_e2e_setup.json"); - } catch (e) { - jsTestLog( - "Failed to parse read aws_e2e_setup.json. See evergreen.yml for how to generate this file which contains evergreen secrets."); - throw e; - } - - try { - return JSON.parse(result); - } catch (e) { - jsTestLog("Failed to parse: aws_e2e_setup.json"); - throw e; - } -} - -function runWithEnv(args, env) { - const pid = _startMongoProgram({args: args, env: env}); - return waitProgram(pid); -} - -function runShellCmdWithEnv(argStr, env) { - if (_isWindows()) { - return runWithEnv(['cmd.exe', '/c', argStr], env); - } else { - return runWithEnv(['/bin/sh', '-c', argStr], env); - } -} - -function getPython3Binary() { - if (_isWindows()) { - return "python.exe"; - } - - return "python3"; -} diff --git a/.evergreen/auth_aws/lib/container_tester.py b/.evergreen/auth_aws/lib/container_tester.py deleted file mode 100644 index eb3703c8f..000000000 --- a/.evergreen/auth_aws/lib/container_tester.py +++ /dev/null @@ -1,385 +0,0 @@ -#!/usr/bin/env python3 -""" -Script for testing mongodb in containers. - -Requires ssh, scp, and sh on local and remote hosts. -Assumes remote host is Linux -""" - -import argparse -import datetime -import logging -import os -import pprint -import subprocess -import uuid - -import boto3 - -LOGGER = logging.getLogger(__name__) - - -############################################################################ -# Default configuration settings for working with a ECS cluster in a region -# - -# These settings depend on a cluster, task subnets, and security group already setup -ECS_DEFAULT_CLUSTER = "arn:aws:ecs:us-east-2:579766882180:cluster/tf-mcb-ecs-cluster" -ECS_DEFAULT_TASK_DEFINITION = "arn:aws:ecs:us-east-2:579766882180:task-definition/tf-app:2" -ECS_DEFAULT_SUBNETS = ['subnet-a5e114cc'] -# Must allow ssh from 0.0.0.0 -ECS_DEFAULT_SECURITY_GROUP = 'sg-051a91d96332f8f3a' - -# This is just a string local to this file -DEFAULT_SERVICE_NAME = 'script-test' - -# Garbage collection threshold for old/stale services -DEFAULT_GARBAGE_COLLECTION_THRESHOLD = datetime.timedelta(hours=1) - -############################################################################ - - -def _run_process(params, cwd=None): - LOGGER.info("RUNNING COMMAND: %s", params) - ret = subprocess.run(params, cwd=cwd) - return ret.returncode - -def _userandhostandport(endpoint): - user_and_host = endpoint.find("@") - if user_and_host == -1: - raise ValueError("Invalid endpoint, Endpoint must be user@host:port") - (user, host) = (endpoint[:user_and_host], endpoint[user_and_host + 1:]) - - colon = host.find(":") - if colon == -1: - return (user, host, "22") - return (user, host[:colon], host[colon + 1:]) - -def _scp(endpoint, src, dest): - (user, host, port) = _userandhostandport(endpoint) - cmd = ["scp", "-o", "StrictHostKeyChecking=no", "-P", port, src, "%s@%s:%s" % (user, host, dest)] - if os.path.isdir(src): - cmd.insert(5, "-r") - _run_process(cmd) - -def _ssh(endpoint, cmd): - (user, host, port) = _userandhostandport(endpoint) - cmd = ["ssh", "-o", "StrictHostKeyChecking=no", "-p", port, "%s@%s" % (user, host), cmd ] - ret = _run_process(cmd) - LOGGER.info("RETURN CODE: %s", ret) - return ret - -def _run_test_args(args): - run_test(args.endpoint, args.script, args.files) - -def run_test(endpoint, script, files): - """ - Run a test on a machine - - Steps - 1. Copy over a files which are tuples of (src, dest) - 2. Copy over the test script to "/tmp/test.sh" - 3. Run the test script and return the results - """ - LOGGER.info("Copying files to %s", endpoint) - - for file in files: - colon = file.find(":") - (src, dest) = (file[:colon], file[colon + 1:]) - _scp(endpoint, src, dest) - - LOGGER.info("Copying script to %s", endpoint) - _scp(endpoint, script, "/tmp/test.sh") - return_code = _ssh(endpoint, "/bin/bash -x /tmp/test.sh") - if return_code != 0: - LOGGER.error("FAILED: %s", return_code) - raise ValueError(f"test failed with {return_code}") - -def _get_region(arn): - return arn.split(':')[3] - - -def _remote_ps_container_args(args): - remote_ps_container(args.cluster) - -def remote_ps_container(cluster): - """ - Get a list of task running in the cluster with their network addresses. - - Emulates the docker ps and ecs-cli ps commands. - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - ec2_client = boto3.client('ec2', region_name=_get_region(cluster)) - - tasks = ecs_client.list_tasks(cluster=cluster) - - task_list = ecs_client.describe_tasks(cluster=cluster, tasks=tasks['taskArns']) - - #Example from ecs-cli tool - #Name State Ports TaskDefinition Health - #aa2c2642-3013-4370-885e-8b8d956e753d/sshd RUNNING 3.15.149.114:22->22/tcp sshd:1 UNKNOWN - - print("Name State Public IP Private IP TaskDefinition Health") - for task in task_list['tasks']: - - taskDefinition = task['taskDefinitionArn'] - taskDefinition_short = taskDefinition[taskDefinition.rfind('/') + 1:] - - private_ip_address = None - enis = [] - for b in [ a['details'] for a in task["attachments"] if a['type'] == 'ElasticNetworkInterface']: - for c in b: - if c['name'] == 'networkInterfaceId': - enis.append(c['value']) - elif c['name'] == 'privateIPv4Address': - private_ip_address = c['value'] - assert enis - assert private_ip_address - - eni = ec2_client.describe_network_interfaces(NetworkInterfaceIds=enis) - public_ip = [n["Association"]["PublicIp"] for n in eni["NetworkInterfaces"]][0] - - for container in task['containers']: - taskArn = container['taskArn'] - task_id = taskArn[taskArn.rfind('/')+ 1:] - name = container['name'] - task_id = task_id + "/" + name - lastStatus = container['lastStatus'] - - print("{:<43}{:<9}{:<25}{:<25}{:<16}".format(task_id, lastStatus, public_ip, private_ip_address, taskDefinition_short )) - -def _remote_create_container_args(args): - remote_create_container(args.cluster, args.task_definition, args.service, args.subnets, args.security_group) - -def remote_create_container(cluster, task_definition, service_name, subnets, security_group): - """ - Create a task in ECS - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - - resp = ecs_client.create_service(cluster=cluster, serviceName=service_name, - taskDefinition = task_definition, - desiredCount = 1, - launchType='FARGATE', - networkConfiguration={ - 'awsvpcConfiguration': { - 'subnets': subnets, - 'securityGroups': [ - security_group, - ], - 'assignPublicIp': "ENABLED" - } - } - ) - - pprint.pprint(resp) - - service_arn = resp["service"]["serviceArn"] - print(f"Waiting for Service {service_arn} to become active...") - - waiter = ecs_client.get_waiter('services_stable') - - waiter.wait(cluster=cluster, services=[service_arn]) - -def _remote_stop_container_args(args): - remote_stop_container(args.cluster, args.service) - -def remote_stop_container(cluster, service_name): - """ - Stop a ECS task - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - - resp = ecs_client.delete_service(cluster=cluster, service=service_name, force=True) - pprint.pprint(resp) - - service_arn = resp["service"]["serviceArn"] - - print(f"Waiting for Service {service_arn} to become inactive...") - waiter = ecs_client.get_waiter('services_inactive') - - waiter.wait(cluster=cluster, services=[service_arn]) - -def _remote_gc_services_container_args(args): - remote_gc_services_container(args.cluster) - -def remote_gc_services_container(cluster): - """ - Delete all ECS services over then a given treshold. - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - - services = ecs_client.list_services(cluster=cluster) - if not services["serviceArns"]: - return - - services_details = ecs_client.describe_services(cluster=cluster, services=services["serviceArns"]) - - not_expired_now = datetime.datetime.now().astimezone() - DEFAULT_GARBAGE_COLLECTION_THRESHOLD - - for service in services_details["services"]: - created_at = service["createdAt"] - - # Find the services that we created "too" long ago - if created_at < not_expired_now: - print("DELETING expired service %s which was created at %s." % (service["serviceName"], created_at)) - - remote_stop_container(cluster, service["serviceName"]) - -def remote_get_public_endpoint_str(cluster, service_name): - """ - Get an SSH connection string for the remote service via the public ip address - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - ec2_client = boto3.client('ec2', region_name=_get_region(cluster)) - - tasks = ecs_client.list_tasks(cluster=cluster, serviceName=service_name) - - task_list = ecs_client.describe_tasks(cluster=cluster, tasks=tasks['taskArns']) - - for task in task_list['tasks']: - - enis = [] - for b in [ a['details'] for a in task["attachments"] if a['type'] == 'ElasticNetworkInterface']: - for c in b: - if c['name'] == 'networkInterfaceId': - enis.append(c['value']) - assert enis - - eni = ec2_client.describe_network_interfaces(NetworkInterfaceIds=enis) - public_ip = [n["Association"]["PublicIp"] for n in eni["NetworkInterfaces"]][0] - break - - return f"root@{public_ip}:22" - -def remote_get_endpoint_str(cluster, service_name): - """ - Get an SSH connection string for the remote service via the private ip address - """ - ecs_client = boto3.client('ecs', region_name=_get_region(cluster)) - - tasks = ecs_client.list_tasks(cluster=cluster, serviceName=service_name) - - task_list = ecs_client.describe_tasks(cluster=cluster, tasks=tasks['taskArns']) - - for task in task_list['tasks']: - - private_ip_address = None - for b in [ a['details'] for a in task["attachments"] if a['type'] == 'ElasticNetworkInterface']: - for c in b: - if c['name'] == 'privateIPv4Address': - private_ip_address = c['value'] - assert private_ip_address - break - - return f"root@{private_ip_address}:22" - -def _remote_get_endpoint_args(args): - _remote_get_endpoint(args.cluster, args.service) - -def _remote_get_endpoint(cluster, service_name): - endpoint = remote_get_endpoint_str(cluster, service_name) - print(endpoint) - -def _get_caller_identity(args): - sts_client = boto3.client('sts') - - pprint.pprint(sts_client.get_caller_identity()) - - -def _run_e2e_test_args(args): - _run_e2e_test(args.script, args.files, args.cluster, args.task_definition, args.subnets, args.security_group) - -def _run_e2e_test(script, files, cluster, task_definition, subnets, security_group): - """ - Run a test end-to-end - - 1. Start an ECS service - 2. Copy the files over and run the test - 3. Stop the ECS service - """ - service_name = str(uuid.uuid4()) - - remote_create_container(cluster, task_definition, service_name, subnets, security_group) - - # The build account hosted ECS tasks are only available via the private ip address - endpoint = remote_get_endpoint_str(cluster, service_name) - if cluster == ECS_DEFAULT_CLUSTER: - # The test account hosted ECS tasks are the opposite, only public ip address access - endpoint = remote_get_public_endpoint_str(cluster, service_name) - - try: - run_test(endpoint, script, files) - finally: - remote_stop_container(cluster, service_name) - - -def main() -> None: - """Execute Main entry point.""" - - parser = argparse.ArgumentParser(description='ECS container tester.') - - parser.add_argument('-v', "--verbose", action='store_true', help="Enable verbose logging") - parser.add_argument('-d', "--debug", action='store_true', help="Enable debug logging") - - sub = parser.add_subparsers(title="Container Tester subcommands", help="sub-command help") - - run_test_cmd = sub.add_parser('run_test', help='Run Test') - run_test_cmd.add_argument("--endpoint", required=True, type=str, help="User and Host and port, ie user@host:port") - run_test_cmd.add_argument("--script", required=True, type=str, help="script to run") - run_test_cmd.add_argument("--files", type=str, nargs="*", help="Files to copy, each string must be a pair of src:dest joined by a colon") - run_test_cmd.set_defaults(func=_run_test_args) - - remote_ps_cmd = sub.add_parser('remote_ps', help='Stop Local Container') - remote_ps_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - remote_ps_cmd.set_defaults(func=_remote_ps_container_args) - - remote_create_cmd = sub.add_parser('remote_create', help='Create Remote Container') - remote_create_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - remote_create_cmd.add_argument("--service", type=str, default=DEFAULT_SERVICE_NAME, help="ECS Service to create") - remote_create_cmd.add_argument("--task_definition", type=str, default=ECS_DEFAULT_TASK_DEFINITION, help="ECS Task Definition to use to create service") - remote_create_cmd.add_argument("--subnets", type=str, nargs="*", default=ECS_DEFAULT_SUBNETS, help="EC2 subnets to use") - remote_create_cmd.add_argument("--security_group", type=str, default=ECS_DEFAULT_SECURITY_GROUP, help="EC2 security group use") - remote_create_cmd.set_defaults(func=_remote_create_container_args) - - remote_stop_cmd = sub.add_parser('remote_stop', help='Stop Remote Container') - remote_stop_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - remote_stop_cmd.add_argument("--service", type=str, default=DEFAULT_SERVICE_NAME, help="ECS Service to stop") - remote_stop_cmd.set_defaults(func=_remote_stop_container_args) - - remote_gc_services_cmd = sub.add_parser('remote_gc_services', help='GC Remote Container') - remote_gc_services_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - remote_gc_services_cmd.set_defaults(func=_remote_gc_services_container_args) - - get_caller_identity_cmd = sub.add_parser('get_caller_identity', help='Get the AWS IAM caller identity') - get_caller_identity_cmd.set_defaults(func=_get_caller_identity) - - remote_get_endpoint_cmd = sub.add_parser('remote_get_endpoint', help='Get SSH remote endpoint') - remote_get_endpoint_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - remote_get_endpoint_cmd.add_argument("--service", type=str, default=DEFAULT_SERVICE_NAME, help="ECS Service to stop") - remote_get_endpoint_cmd.set_defaults(func=_remote_get_endpoint_args) - - run_e2e_test_cmd = sub.add_parser('run_e2e_test', help='Run Test') - run_e2e_test_cmd.add_argument("--script", required=True, type=str, help="script to run") - run_e2e_test_cmd.add_argument("--files", type=str, nargs="*", help="Files to copy, each string must be a pair of src:dest joined by a colon") - run_e2e_test_cmd.add_argument("--cluster", type=str, default=ECS_DEFAULT_CLUSTER, help="ECS Cluster to target") - run_e2e_test_cmd.add_argument("--task_definition", type=str, default=ECS_DEFAULT_TASK_DEFINITION, help="ECS Task Definition to use to create service") - run_e2e_test_cmd.add_argument("--subnets", type=str, nargs="*", default=ECS_DEFAULT_SUBNETS, help="EC2 subnets to use") - run_e2e_test_cmd.add_argument("--security_group", type=str, default=ECS_DEFAULT_SECURITY_GROUP, help="EC2 security group use") - run_e2e_test_cmd.set_defaults(func=_run_e2e_test_args) - - args = parser.parse_args() - - print("AWS_SHARED_CREDENTIALS_FILE: %s" % (os.getenv("AWS_SHARED_CREDENTIALS_FILE"))) - - if args.debug: - logging.basicConfig(level=logging.DEBUG) - elif args.verbose: - logging.basicConfig(level=logging.INFO) - - - args.func(args) - - -if __name__ == "__main__": - main() diff --git a/.evergreen/auth_aws/lib/ecs_hosted_test.js b/.evergreen/auth_aws/lib/ecs_hosted_test.js deleted file mode 100644 index 17d4b3703..000000000 --- a/.evergreen/auth_aws/lib/ecs_hosted_test.js +++ /dev/null @@ -1,37 +0,0 @@ -/** - * Verify the AWS IAM ECS hosted auth works - */ - -(function() { -"use strict"; - -// This varies based on hosting ECS task as the account id and role name can vary -const AWS_ACCOUNT_ARN = "arn:aws:sts::557821124784:assumed-role/ecsTaskExecutionRole/*"; - -const conn = MongoRunner.runMongod({ - setParameter: { - "authenticationMechanisms": "MONGODB-AWS,SCRAM-SHA-256", - }, - auth: "", -}); - -const external = conn.getDB("$external"); -const admin = conn.getDB("admin"); - -assert.commandWorked(admin.runCommand({createUser: "admin", pwd: "pwd", roles: ['root']})); -assert(admin.auth("admin", "pwd")); - -assert.commandWorked(external.runCommand({createUser: AWS_ACCOUNT_ARN, roles:[{role: 'read', db: "aws"}]})); - -const uri = "mongodb://127.0.0.1:20000/aws?authMechanism=MONGODB-AWS"; -const program = "/root/src/.evergreen/run-mongodb-aws-ecs-test.sh"; - -// Try the command line -const smoke = runMongoProgram(program, uri); -assert.eq(smoke, 0, "Could not auth with smoke user"); - -// Try the auth function -assert(external.auth({mechanism: 'MONGODB-AWS'})); - -MongoRunner.stopMongod(conn); -}()); diff --git a/.evergreen/auth_aws/lib/ecs_hosted_test.sh b/.evergreen/auth_aws/lib/ecs_hosted_test.sh deleted file mode 100644 index 7dddbc80b..000000000 --- a/.evergreen/auth_aws/lib/ecs_hosted_test.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/bash -# A shell script to run in an ECS hosted task - -# The environment variable is always set during interactive logins -# But for non-interactive logs, ~/.bashrc does not appear to be read on Ubuntu but it works on Fedora -[[ -z "${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}" ]] && export $(strings /proc/1/environ | grep AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) - -env - -mkdir -p /data/db || true - -/root/mongo --verbose --nodb ecs_hosted_test.js - -RET_CODE=$? -echo RETURN CODE: $RET_CODE -exit $RET_CODE diff --git a/.evergreen/compile-extension.sh b/.evergreen/compile-extension.sh new file mode 100644 index 000000000..793ad66f3 --- /dev/null +++ b/.evergreen/compile-extension.sh @@ -0,0 +1,42 @@ +#!/usr/bin/env bash +set -o errexit # Exit the script with error if any of the commands fail + +PATH="$PHP_PATH/bin:$PATH" + +install_extension () +{ + rm -f ${PHP_PATH}/lib/php.ini + + if [ "x${EXTENSION_BRANCH}" != "x" ] || [ "x${EXTENSION_REPO}" != "x" ]; then + CLONE_REPO=${EXTENSION_REPO:-https://github.com/mongodb/mongo-php-driver} + CHECKOUT_BRANCH=${EXTENSION_BRANCH:-master} + + echo "Compiling driver branch ${CHECKOUT_BRANCH} from repository ${CLONE_REPO}" + + mkdir -p /tmp/compile + rm -rf /tmp/compile/mongo-php-driver + git clone ${CLONE_REPO} /tmp/compile/mongo-php-driver + cd /tmp/compile/mongo-php-driver + + git checkout ${CHECKOUT_BRANCH} + git submodule update --init + phpize + ./configure --enable-mongodb-developer-flags + make all -j20 > /dev/null + make install + + cd ${PROJECT_DIRECTORY} + elif [ "${EXTENSION_VERSION}" != "" ]; then + echo "Installing driver version ${EXTENSION_VERSION} from PECL" + MAKEFLAGS=-j20 pecl install -f mongodb-${EXTENSION_VERSION} + else + echo "Installing latest driver version from PECL" + MAKEFLAGS=-j20 pecl install -f mongodb + fi + + cp ${PROJECT_DIRECTORY}/.evergreen/config/php.ini ${PHP_PATH}/lib/php.ini + + php --ri mongodb +} + +install_extension diff --git a/.evergreen/config.yml b/.evergreen/config.yml index 4eb025e7a..490093811 100644 --- a/.evergreen/config.yml +++ b/.evergreen/config.yml @@ -1,865 +1,75 @@ -######################################## -# Evergreen Template for MongoDB Drivers -######################################## - -# When a task that used to pass starts to fail -# Go through all versions that may have been skipped to detect -# when the task started failing +# When a task that used to pass starts to fail, go through all versions that may have been skipped to detect when the +# task started failing. stepback: true -# Mark a failure as a system/bootstrap failure (purple box) rather then a task -# failure by default. +# Mark a failure as a system/bootstrap failure (purple box) rather than a task failure by default. # Actual testing tasks are marked with `type: test` command_type: system -# Protect ourself against rogue test case, or curl gone wild, that runs forever -# Good rule of thumb: the averageish length a task takes, times 5 -# That roughly accounts for variable system performance for various buildvariants -exec_timeout_secs: 1800 # 6 minutes is the longest we'll ever run - -# What to do when evergreen hits the timeout (`post:` tasks are run automatically) -timeout: - - command: shell.exec - params: - script: | - ls -la - -functions: - "fetch source": - # Executes git clone and applies the submitted patch, if any - - command: git.get_project - params: - directory: "src" - # Make an evergreen exapanstion file with dynamic values - - command: shell.exec - params: - working_dir: "src" - script: | - # Get the current unique version of this checkout - if [ "${is_patch}" = "true" ]; then - CURRENT_VERSION=$(git describe)-patch-${version_id} - else - CURRENT_VERSION=latest - fi - - export DRIVERS_TOOLS="$(pwd)/../drivers-tools" - export PROJECT_DIRECTORY="$(pwd)" - - # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory - if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin - export DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS) - export PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY) - fi - - export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration" - export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" - export UPLOAD_BUCKET="${project}" - - cat < expansion.yml - CURRENT_VERSION: "$CURRENT_VERSION" - DRIVERS_TOOLS: "$DRIVERS_TOOLS" - MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME" - MONGODB_BINARIES: "$MONGODB_BINARIES" - UPLOAD_BUCKET: "$UPLOAD_BUCKET" - PROJECT_DIRECTORY: "$PROJECT_DIRECTORY" - PREPARE_SHELL: | - set -o errexit - export DRIVERS_TOOLS="$DRIVERS_TOOLS" - export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME" - export MONGODB_BINARIES="$MONGODB_BINARIES" - export UPLOAD_BUCKET="$UPLOAD_BUCKET" - export PROJECT_DIRECTORY="$PROJECT_DIRECTORY" - - export TMPDIR="$MONGO_ORCHESTRATION_HOME/db" - export PATH="$MONGODB_BINARIES:$PATH" - export PROJECT="${project}" - EOT - # See what we've done - cat expansion.yml - - # Load the expansion file to make an evergreen variable with the current unique version - - command: expansions.update - params: - file: src/expansion.yml - - "prepare resources": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - rm -rf $DRIVERS_TOOLS - if [ "${project}" = "drivers-tools" ]; then - # If this was a patch build, doing a fresh clone would not actually test the patch - cp -R ${PROJECT_DIRECTORY}/ $DRIVERS_TOOLS - else - git clone https://github.com/mongodb-labs/drivers-evergreen-tools.git --depth 1 $DRIVERS_TOOLS - fi - echo "{ \"releases\": { \"default\": \"$MONGODB_BINARIES\" }}" > $MONGO_ORCHESTRATION_HOME/orchestration.config - - "upload mo artifacts": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - find $MONGO_ORCHESTRATION_HOME -name \*.log | xargs tar czf mongodb-logs.tar.gz - - command: s3.put - params: - aws_key: ${aws_key} - aws_secret: ${aws_secret} - local_file: mongodb-logs.tar.gz - remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-mongodb-logs.tar.gz - bucket: mciuploads - permissions: public-read - content_type: ${content_type|application/x-gzip} - display_name: "mongodb-logs.tar.gz" - - command: s3.put - params: - aws_key: ${aws_key} - aws_secret: ${aws_secret} - local_file: ${DRIVERS_TOOLS}/.evergreen/orchestration/server.log - remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/logs/${task_id}-${execution}-orchestration.log - bucket: mciuploads - permissions: public-read - content_type: ${content_type|text/plain} - display_name: "orchestration.log" - - "upload working dir": - - command: archive.targz_pack - params: - target: "working-dir.tar.gz" - source_dir: ${PROJECT_DIRECTORY}/ - include: - - "./**" - - command: s3.put - params: - aws_key: ${aws_key} - aws_secret: ${aws_secret} - local_file: working-dir.tar.gz - remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-working-dir.tar.gz - bucket: mciuploads - permissions: public-read - content_type: ${content_type|application/x-gzip} - display_name: "working-dir.tar.gz" - - command: archive.targz_pack - params: - target: "drivers-dir.tar.gz" - source_dir: ${DRIVERS_TOOLS} - include: - - "./**" - exclude_files: - # Windows cannot read the mongod *.lock files because they are locked. - - "*.lock" - - command: s3.put - params: - aws_key: ${aws_key} - aws_secret: ${aws_secret} - local_file: drivers-dir.tar.gz - remote_file: ${UPLOAD_BUCKET}/${build_variant}/${revision}/${version_id}/${build_id}/artifacts/${task_id}-${execution}-drivers-dir.tar.gz - bucket: mciuploads - permissions: public-read - content_type: ${content_type|application/x-gzip} - display_name: "drivers-dir.tar.gz" - - "upload test results": - - command: attach.xunit_results - params: - # Uploading test results does not work when using ${PROJECT_DIRECTORY}, - # so we use an absolute path to work around this - file: "src/test-results.xml" - - command: attach.results - params: - file_location: "${DRIVERS_TOOLS}/results.json" - - "bootstrap mongo-orchestration": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - MONGODB_VERSION=${VERSION} ORCHESTRATION_FILE=${ORCHESTRATION_FILE} TOPOLOGY=${TOPOLOGY} AUTH=${AUTH} SSL=${SSL} STORAGE_ENGINE=${STORAGE_ENGINE} LOAD_BALANCER=${LOAD_BALANCER} REQUIRE_API_VERSION=${REQUIRE_API_VERSION} sh ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh - # run-orchestration generates expansion file with MONGODB_URI and CRYPT_SHARED_LIB_PATH - - command: expansions.update - params: - file: mo-expansion.yml - - "stop mongo-orchestration": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - sh ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh - - "bootstrap mongohoused": - - command: shell.exec - params: - script: | - VARIANT=${VARIANT} DRIVERS_TOOLS="${DRIVERS_TOOLS}" sh ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/build-mongohouse-local.sh - - command: shell.exec - params: - background: true - script: | - DRIVERS_TOOLS="${DRIVERS_TOOLS}" sh ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake/run-mongohouse-local.sh - - "create serverless instance": - - command: shell.exec - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \ - SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \ - SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \ - bash ${DRIVERS_TOOLS}/.evergreen/serverless/create-instance.sh - - command: expansions.update - params: - file: src/serverless-expansion.yml - - command: shell.exec - params: - shell: "bash" - script: | - ${PREPARE_SHELL} - - if [ -z "${SERVERLESS_MONGODB_VERSION}" ]; then - echo "expected SERVERLESS_MONGODB_VERSION to be set" - exit 1 - fi - - # Download the enterprise server download for current platform to $MONGODB_BINARIES. - # This is required for tests that need mongocryptd. - # $MONGODB_BINARIES is added to the $PATH in fetch-source. - ${PYTHON3_BINARY} $DRIVERS_TOOLS/.evergreen/mongodl.py \ - --component archive \ - --version ${SERVERLESS_MONGODB_VERSION} \ - --edition enterprise \ - --out $MONGODB_BINARIES \ - --strip-path-components 2 - - # Download the crypt_shared dynamic library for the current platform. - ${PYTHON3_BINARY} $DRIVERS_TOOLS/.evergreen/mongodl.py \ - --component crypt_shared \ - --version ${SERVERLESS_MONGODB_VERSION} \ - --edition enterprise \ - --out . \ - --only "**/mongo_crypt_v1.*" \ - --strip-path-components 1 - - # Find the crypt_shared library file in the current directory and set the CRYPT_SHARED_LIB_PATH to - # the path of that file. Only look for .so, .dll, or .dylib files to prevent matching any other - # downloaded files. - CRYPT_SHARED_LIB_PATH="$(find $(pwd) -maxdepth 1 -type f \ - -name 'mongo_crypt_v1.so' -o \ - -name 'mongo_crypt_v1.dll' -o \ - -name 'mongo_crypt_v1.dylib')" - - # If we're on Windows, convert the "cygdrive" path to Windows-style paths. - if [ "Windows_NT" = "$OS" ]; then - CRYPT_SHARED_LIB_PATH=$(cygpath -m $CRYPT_SHARED_LIB_PATH) - fi - - echo "CRYPT_SHARED_LIB_PATH: $CRYPT_SHARED_LIB_PATH" >> crypt-expansion.yml +# Fail builds when pre tasks fail. +pre_error_fails_task: true - # Load the expansion file to make an evergreen variable with the current unique version - - command: expansions.update - params: - file: crypt-expansion.yml - - "delete serverless instance": - - command: shell.exec - params: - script: | - # Only run if a serverless instance was started - if [ -n "${SERVERLESS_INSTANCE_NAME}" ]; then - SERVERLESS_INSTANCE_NAME=${SERVERLESS_INSTANCE_NAME} \ - SERVERLESS_DRIVERS_GROUP=${SERVERLESS_DRIVERS_GROUP} \ - SERVERLESS_API_PUBLIC_KEY=${SERVERLESS_API_PUBLIC_KEY} \ - SERVERLESS_API_PRIVATE_KEY=${SERVERLESS_API_PRIVATE_KEY} \ - bash ${DRIVERS_TOOLS}/.evergreen/serverless/delete-instance.sh - fi - - "run tests": - - command: shell.exec - type: test - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" - export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" - export AZURE_TENANT_ID="${client_side_encryption_azure_tenant_id}" - export AZURE_CLIENT_ID="${client_side_encryption_azure_client_id}" - export AZURE_CLIENT_SECRET="${client_side_encryption_azure_client_secret}" - export GCP_EMAIL="${client_side_encryption_gcp_email}" - export GCP_PRIVATE_KEY="${client_side_encryption_gcp_privatekey}" - export KMIP_ENDPOINT="${client_side_encryption_kmip_endpoint}" - export KMS_ENDPOINT_EXPIRED="${client_side_encryption_kms_endpoint_expired}" - export KMS_ENDPOINT_WRONG_HOST="${client_side_encryption_kms_endpoint_wrong_host}" - export KMS_ENDPOINT_REQUIRE_CLIENT_CERT="${client_side_encryption_kms_endpoint_require_client_cert}" - export KMS_TLS_CA_FILE="${client_side_encryption_kms_tls_ca_file}" - export KMS_TLS_CERTIFICATE_KEY_FILE="${client_side_encryption_kms_tls_certificate_key_file}" - export PATH="${PHP_PATH}/bin:$PATH" - - API_VERSION=${API_VERSION} \ - CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ - MONGODB_URI="${MONGODB_URI}" \ - PHP_VERSION=${PHP_VERSION} \ - SKIP_CRYPT_SHARED=${SKIP_CRYPT_SHARED} \ - SSL=${SSL} \ - TESTS=${TESTS} \ - sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh - - "run atlas data lake test": - - command: shell.exec - type: test - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - export PATH="${PHP_PATH}/bin:$PATH" - - MONGODB_URI="mongodb://mhuser:pencil@127.0.0.1:27017" \ - TESTS="atlas-data-lake" \ - sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh - - "run serverless tests": - - command: shell.exec - type: test - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" - export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" - export AZURE_TENANT_ID="${client_side_encryption_azure_tenant_id}" - export AZURE_CLIENT_ID="${client_side_encryption_azure_client_id}" - export AZURE_CLIENT_SECRET="${client_side_encryption_azure_client_secret}" - export GCP_EMAIL="${client_side_encryption_gcp_email}" - export GCP_PRIVATE_KEY="${client_side_encryption_gcp_privatekey}" - export KMIP_ENDPOINT="${client_side_encryption_kmip_endpoint}" - export KMS_ENDPOINT_EXPIRED="${client_side_encryption_kms_endpoint_expired}" - export KMS_ENDPOINT_WRONG_HOST="${client_side_encryption_kms_endpoint_wrong_host}" - export KMS_ENDPOINT_REQUIRE_CLIENT_CERT="${client_side_encryption_kms_endpoint_require_client_cert}" - export KMS_TLS_CA_FILE="${client_side_encryption_kms_tls_ca_file}" - export KMS_TLS_CERTIFICATE_KEY_FILE="${client_side_encryption_kms_tls_certificate_key_file}" - export MONGODB_IS_SERVERLESS=on - export MONGODB_USERNAME=${SERVERLESS_ATLAS_USER} - export MONGODB_PASSWORD=${SERVERLESS_ATLAS_PASSWORD} - export PATH="${PHP_PATH}/bin:$PATH" - - CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ - MONGODB_URI="${SERVERLESS_URI}" \ - SKIP_CRYPT_SHARED=${SKIP_CRYPT_SHARED} \ - TESTS="serverless" \ - sh ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh - - "cleanup": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - rm -rf $DRIVERS_TOOLS || true - - "fix absolute paths": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - for filename in $(find ${DRIVERS_TOOLS} -name \*.json); do - perl -p -i -e "s|ABSOLUTE_PATH_REPLACEMENT_TOKEN|${DRIVERS_TOOLS}|g" $filename - done - - "windows fix": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - for i in $(find ${DRIVERS_TOOLS}/.evergreen ${PROJECT_DIRECTORY}/.evergreen -name \*.sh); do - cat $i | tr -d '\r' > $i.new - mv $i.new $i - done - # Copy client certificate because symlinks do not work on Windows. - cp ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem ${MONGO_ORCHESTRATION_HOME}/lib/client.pem - - "make files executable": - - command: shell.exec - params: - script: | - ${PREPARE_SHELL} - for i in $(find ${DRIVERS_TOOLS}/.evergreen ${PROJECT_DIRECTORY}/.evergreen -name \*.sh); do - chmod +x $i - done - - "install dependencies": - - command: shell.exec - params: - working_dir: "src" - script: | - ${PREPARE_SHELL} - file="${PROJECT_DIRECTORY}/.evergreen/install-dependencies.sh" - # Don't use ${file} syntax here because evergreen treats it as an empty expansion. - [ -f "$file" ] && PHP_VERSION=${PHP_VERSION} EXTENSION_VERSION=${EXTENSION_VERSION} EXTENSION_REPO=${EXTENSION_REPO} EXTENSION_BRANCH=${EXTENSION_BRANCH} DEPENDENCIES=${DEPENDENCIES} sh $file || echo "$file not available, skipping" - # install-dependencies generates expansion file with the PHP_PATH for the chosen PHP version - - command: expansions.update - params: - file: src/php-expansion.yml - - "start load balancer": - - command: shell.exec - params: - script: | - MONGODB_URI="${MONGODB_URI}" ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh start - - command: expansions.update - params: - file: lb-expansion.yml - - "stop load balancer": - - command: shell.exec - params: - script: | - # Only run if a load balancer was started - if [ -n "${SINGLE_MONGOS_LB_URI}" ]; then - ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop - fi - - "start kms servers": - - command: shell.exec - # Init venv without background:true to install dependencies - params: - script: |- - set -o errexit - cd ${DRIVERS_TOOLS}/.evergreen/csfle - . ./activate_venv.sh - - command: shell.exec - params: - background: true - # Use different ports for KMS HTTP servers to avoid conflicts with load balancers - script: |- - set -o errexit - cd ${DRIVERS_TOOLS}/.evergreen/csfle - . ./activate_venv.sh - python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8100 & - python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8101 & - python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8102 --require_client_cert & - python -u kms_kmip_server.py --port 5698 & - - command: expansions.update - params: - updates: - - key: client_side_encryption_kms_tls_ca_file - value: ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem - - key: client_side_encryption_kms_tls_certificate_key_file - value: ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem - - key: client_side_encryption_kms_endpoint_expired - value: 127.0.0.1:8100 - - key: client_side_encryption_kms_endpoint_wrong_host - value: 127.0.0.1:8101 - - key: client_side_encryption_kms_endpoint_require_client_cert - value: 127.0.0.1:8102 - - key: client_side_encryption_kmip_endpoint - value: localhost:5698 +# Protect ourselves against rogue test case that runs forever. Tasks are killed after 30 minutes, which shouldn't occur +# under normal circumstances. +exec_timeout_secs: 1800 +# These pre and post rules apply to all tasks not part of a task group, which should only ever be tests against local +# MongoDB instances. All other tasks that require special preparation should be run from within a task group pre: - func: "fetch source" - func: "prepare resources" - - func: "windows fix" - func: "fix absolute paths" - - func: "make files executable" - func: "install dependencies" - + - func: "locate PHP binaries" + - func: "fetch extension" + - func: "install composer" post: - # Skip: uploading the full working directory is not needed by drivers-evergreen-tools. - # - func: "upload working dir" - - func: "upload mo artifacts" - func: "upload test results" - - func: "delete serverless instance" - func: "stop load balancer" - func: "stop mongo-orchestration" - func: "cleanup" -tasks: - - # Wildcard task. Do you need to find out what tools are available and where? - # Throw it here, and execute this task on all buildvariants - - name: getdata - commands: - - command: shell.exec - type: test - params: - script: | - . ${DRIVERS_TOOLS}/.evergreen/download-mongodb.sh || true - get_distro || true - echo $DISTRO - echo $MARCH - echo $OS - uname -a || true - ls /etc/*release* || true - cc --version || true - gcc --version || true - clang --version || true - gcov --version || true - lcov --version || true - llvm-cov --version || true - echo $PATH - ls -la /usr/local/Cellar/llvm/*/bin/ || true - ls -la /usr/local/Cellar/ || true - scan-build --version || true - genhtml --version || true - valgrind --version || true - - -# Standard test tasks {{{ - - - name: "test-standalone" - tags: ["standalone"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "server" - - func: "start kms servers" - - func: "run tests" - - - name: "test-replica_set" - tags: ["replica_set"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "replica_set" - - func: "start kms servers" - - func: "run tests" - - - name: "test-sharded_cluster" - tags: ["sharded_cluster"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "sharded_cluster" - - func: "start kms servers" - - func: "run tests" - - - name: "test-atlas-data-lake" - commands: - - func: "bootstrap mongohoused" - - func: "run atlas data lake test" - - - name: "test-requireApiVersion" - tags: ["versioned-api"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "server" - AUTH: "auth" - REQUIRE_API_VERSION: "yes" - - func: "start kms servers" - - func: "run tests" - vars: - API_VERSION: "1" - - - name: "test-acceptApiVersion2" - tags: ["versioned-api"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "server" - ORCHESTRATION_FILE: "versioned-api-testing.json" - - func: "start kms servers" - - func: "run tests" - vars: - TESTS: "versioned-api" - - - name: "test-serverless" - tags: ["serverless"] - commands: - - func: "create serverless instance" - - func: "start kms servers" - - func: "run serverless tests" - - - name: "test-loadBalanced" - tags: ["loadbalanced"] - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "sharded_cluster" - LOAD_BALANCER: "true" - SSL: "yes" - - func: "start load balancer" - - func: "start kms servers" - - func: "run tests" - vars: - # Note: loadBalanced=true should already be appended to SINGLE_MONGOS_LB_URI - MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" - SSL: "yes" - # Note: "stop load balancer" will be called from "post" - - - name: "test-skip_crypt_shared" - commands: - - func: "bootstrap mongo-orchestration" - vars: - TOPOLOGY: "replica_set" - - func: "start kms servers" - - func: "run tests" - vars: - SKIP_CRYPT_SHARED: "yes" - TESTS: "csfle" - -# }}} - - -axes: - # Note: install-dependencies.sh will search for the latest minor version - # matching the PHP_VERSION constant - - id: php-versions - display_name: PHP Version - values: - - id: "8.2" - display_name: "PHP 8.2" - variables: - PHP_VERSION: "8.2" - - id: "8.1" - display_name: "PHP 8.1" - variables: - PHP_VERSION: "8.1" - - id: "8.0" - display_name: "PHP 8.0" - variables: - PHP_VERSION: "8.0" - - id: "7.4" - display_name: "PHP 7.4" - variables: - PHP_VERSION: "7.4" - - id: "7.3" - display_name: "PHP 7.3" - variables: - PHP_VERSION: "7.3" - - id: "7.2" - display_name: "PHP 7.2" - variables: - PHP_VERSION: "7.2" - - - id: php-edge-versions - display_name: PHP Version - values: - - id: "latest-stable" - display_name: "PHP 8.1" - variables: - PHP_VERSION: "8.1" - - id: "oldest-supported" - display_name: "PHP 7.2" - variables: - PHP_VERSION: "7.2" - - - id: mongodb-versions - display_name: MongoDB Version - values: - - id: "latest" - display_name: "MongoDB latest" - variables: - VERSION: "latest" - - id: "6.0" - display_name: "MongoDB 6.0" - variables: - VERSION: "6.0" - - id: "5.0" - display_name: "MongoDB 5.0" - variables: - VERSION: "5.0" - - id: "4.4" - display_name: "MongoDB 4.4" - variables: - VERSION: "4.4" - - id: "4.2" - display_name: "MongoDB 4.2" - variables: - VERSION: "4.2" - - id: "4.0" - display_name: "MongoDB 4.0" - variables: - VERSION: "4.0" - - id: "3.6" - display_name: "MongoDB 3.6" - variables: - VERSION: "3.6" - - - id: mongodb-edge-versions - display_name: MongoDB Version - values: - - id: "latest-stable" - display_name: "MongoDB 6.0" - variables: - VERSION: "6.0" - - id: "oldest-supported" - display_name: "MongoDB 3.6" - variables: - VERSION: "3.6" - - - id: driver-versions - display_name: Driver Version - values: - - id: "oldest-supported" - display_name: "PHPC 1.15.0" - variables: - EXTENSION_VERSION: "1.15.0" - - id: "latest-stable" - display_name: "PHPC (stable)" - variables: - EXTENSION_VERSION: "stable" - - id: "latest-dev" - display_name: "PHPC (master)" - variables: - EXTENSION_BRANCH: "master" - - - id: os - display_name: OS - values: - - id: debian11 - display_name: "Debian 11" - run_on: debian11 - - id: debian10 - display_name: "Debian 10" - run_on: debian10 - - id: debian92 - display_name: "Debian 9.2" - run_on: debian92 - - id: rhel70 - display_name: "RHEL 7.0" - run_on: rhel70 - - id: rhel71-power8 - display_name: "RHEL 7.1 Power 8" - run_on: rhel71-power8-build - - id: rhel72-zseries - display_name: "RHEL 7.2 zSeries" - run_on: rhel72-zseries-build - - id: ubuntu1804-arm64 - display_name: "Ubuntu 18.04 ARM64" - run_on: ubuntu1804-arm64-test - - - id: topology - display_name: Topology - values: - - id: standalone - display_name: Standalone - variables: - TOPOLOGY: "server" - - id: replicaset - display_name: Replica Set - variables: - TOPOLOGY: "replica_set" - - id: sharded-cluster - display_name: Sharded Cluster - variables: - TOPOLOGY: "sharded_cluster" - - - id: auth - display_name: Authentication - values: - - id: auth - display_name: Auth - variables: - AUTH: "auth" - - id: noauth - display_name: NoAuth - variables: - AUTH: "noauth" - - - id: ssl - display_name: SSL - values: - - id: ssl - display_name: SSL - variables: - SSL: "ssl" - - id: nossl - display_name: NoSSL - variables: - SSL: "nossl" - - - id: storage-engine - display_name: Storage - values: - - id: mmapv1 - display_name: MMAPv1 - variables: - STORAGE_ENGINE: "mmapv1" - - id: wiredtiger - display_name: WiredTiger - variables: - STORAGE_ENGINE: "wiredtiger" - - id: inmemory - display_name: InMemory - variables: - STORAGE_ENGINE: "inmemory" - - - id: dependencies - display_name: Dependencies - values: - - id: lowest - display_name: Lowest - variables: - DEPENDENCIES: "lowest" - -buildvariants: -# Test all PHP versions with latest-stable MongoDB and PHPC on Debian -- matrix_name: "test-php-versions" - matrix_spec: { "os": "debian11", "mongodb-edge-versions": "latest-stable", "php-versions": "*", "driver-versions": "latest-stable" } - display_name: "${os}, ${mongodb-edge-versions}, ${php-versions}, ${driver-versions}" - exclude_spec: - # Exclude "latest-stable" PHP version for Debian 11 (see: test-mongodb-versions matrix) - - { "os": "debian11", "mongodb-edge-versions": "latest-stable", "php-versions": "8.1", "driver-versions": "latest-stable" } - tasks: - - name: "test-standalone" - - name: "test-replica_set" - - name: "test-sharded_cluster" - -# Test all topologies and MongoDB versions with latest-stable PHP and PHPC on Debian -- matrix_name: "test-mongodb-versions" - matrix_spec: { "os": ["debian92", "debian11"], "mongodb-versions": "*", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "${os}, ${mongodb-versions}, ${php-edge-versions}, ${driver-versions}" - exclude_spec: - # Debian 9.2 only supports up to MongoDB 5.0 - - { "os": "debian92", "mongodb-versions": ["6.0", "latest"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - - { "os": "debian11", "mongodb-versions": ["3.6", "4.0", "4.2", "4.4", "5.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - tasks: - - name: "test-standalone" - - name: "test-replica_set" - - name: "test-sharded_cluster" - -# Test oldest-supported PHP, MongoDB, and driver versions with lowest dependencies on Debian -- matrix_name: "test-oldest-supported" - matrix_spec: { "os": "debian92", "mongodb-edge-versions": "oldest-supported", "php-edge-versions": "oldest-supported", "driver-versions": "oldest-supported", "dependencies": "lowest" } - display_name: "Lowest Dependencies: ${os}, ${mongodb-edge-versions}, ${php-edge-versions}, ${driver-versions}" - tasks: - - name: "test-standalone" - - name: "test-replica_set" - - name: "test-sharded_cluster" - -- matrix_name: "atlas-data-lake-test" - matrix_spec: { "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "Atlas Data Lake" - run_on: debian11 - expansions: - VARIANT: debian11 # Referenced by ADL build script for downloading MQLRun - tasks: - - name: "test-atlas-data-lake" - -# Stable API is available from MongoDB 5.0+ -- matrix_name: "test-requireApiVersion" - matrix_spec: { "os": "debian11", "mongodb-versions": ["5.0", "6.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "Versioned API - ${mongodb-versions}" - tasks: - - .versioned-api - -- matrix_name: "serverless" - matrix_spec: { "os": "debian11", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "Serverless" - tasks: - - .serverless - -# Load balancer is available from MongoDB 5.0+ -- matrix_name: "test-loadBalanced" - matrix_spec: { "os": "debian11", "mongodb-versions": ["5.0", "6.0"], "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "Load balanced - ${mongodb-versions}" - tasks: - - name: "test-loadBalanced" - -# CSFLE crypt_shared is available from MongoDB 6.0+, so explicitly test without it to allow use of mongocryptd -- matrix_name: "test-csfle-skip_crypt_shared" - matrix_spec: { "os": "debian11", "mongodb-versions": "6.0", "php-edge-versions": "latest-stable", "driver-versions": "latest-stable" } - display_name: "CSFLE skip_crypt_shared - ${mongodb-versions}" - tasks: - - name: "test-skip_crypt_shared" +# These aliases define the default variant/tasks to test for pull requests and merge queue +github_pr_aliases: &github_pr_aliases + # Always test all builds for consistency + - variant_tags: ["pr build"] + task_tags: ["pr"] + # Run all tasks in PR variants for PHP 8.3 (excluding MongoDB latest) + - variant_tags: ["pr php8.3"] + task_tags: ["pr !latest"] + # Run PR tasks for all PR variants (only MongoDB 7.0) + - variant_tags: ["pr"] + task_tags: ["pr 7.0"] + +commit_queue_aliases: *github_pr_aliases + +git_tag_aliases: + - git_tag: "^[0-9]+.[0-9]+.[0-9]+" + remote_path: "" + variant_tags: ["tag"] + task_tags: ["tag !latest"] + +github_checks_aliases: + - variant: ".*" + task: ".*" + +# Include files that contain various tasks, task groups, and build variant definitions +include: + - filename: .evergreen/config/functions.yml + + - filename: .evergreen/config/build-task-groups.yml + - filename: .evergreen/config/build-variants.yml + + - filename: .evergreen/config/test-tasks.yml + - filename: .evergreen/config/test-task-groups.yml + - filename: .evergreen/config/test-variants.yml + + # Automatically generated files + - filename: .evergreen/config/generated/build/build-extension.yml + - filename: .evergreen/config/generated/test/local.yml + - filename: .evergreen/config/generated/test/load-balanced.yml + - filename: .evergreen/config/generated/test/require-api-version.yml + - filename: .evergreen/config/generated/test/csfle.yml + - filename: .evergreen/config/generated/test-variant/modern-php-full.yml + - filename: .evergreen/config/generated/test-variant/phpc.yml + - filename: .evergreen/config/generated/test-variant/lowest.yml diff --git a/.evergreen/config/build-task-groups.yml b/.evergreen/config/build-task-groups.yml new file mode 100644 index 000000000..f745f78ad --- /dev/null +++ b/.evergreen/config/build-task-groups.yml @@ -0,0 +1,20 @@ +variables: + build_setup: &build_setup + - func: "fetch source" + - func: "prepare resources" + - func: "fix absolute paths" + - func: "install dependencies" + build_teardown: &build_teardown + - func: "cleanup" + +task_groups: + # Builds all versions of PHP + - name: "build-all-php" + # Keep this number in sync with the number of PHP versions to allow for parallel builds + max_hosts: 3 + setup_task: *build_setup + setup_task_can_fail_task: true + setup_task_timeout_secs: 1800 + teardown_task: *build_teardown + tasks: + - ".build" diff --git a/.evergreen/config/build-variants.yml b/.evergreen/config/build-variants.yml new file mode 100644 index 000000000..43b7ed4f2 --- /dev/null +++ b/.evergreen/config/build-variants.yml @@ -0,0 +1,75 @@ +# +# Build variants to build the driver - these are run for all operating systems and PHP versions we support +# +buildvariants: + # Debian + - name: build-debian12 + display_name: "Build: Debian 12" + tags: ["build", "debian", "x64"] + run_on: debian12-small + tasks: + - name: "build-all-php" + - name: build-debian11 + display_name: "Build: Debian 11" + tags: ["build", "debian", "x64", "pr", "tag"] + run_on: debian11-small + tasks: + - name: "build-all-php" + + # RHEL + - name: build-rhel90 + display_name: "Build: RHEL 9.0" + tags: ["build", "rhel", "x64", "pr", "tag"] + run_on: rhel90-small + tasks: + - name: "build-all-php" + - name: build-rhel9-zseries + display_name: "Build: RHEL 9 Zseries" + tags: ["build", "rhel", "zseries", "tag"] + run_on: rhel9-zseries-small + tasks: + - name: "build-all-php" + - name: build-rhel9-power + display_name: "Build: RHEL 9 PPC" + tags: ["build", "rhel", "power", "tag"] + run_on: rhel9-power-small + tasks: + - name: "build-all-php" + - name: build-rhel82-arm64 + display_name: "Build: RHEL 8.2 ARM64" + tags: ["build", "rhel", "arm64", "tag"] + run_on: rhel82-arm64 + tasks: + - name: "build-all-php" + - name: build-rhel80 + display_name: "Build: RHEL 8.0" + tags: ["build", "rhel", "x64", "pr", "tag"] + run_on: rhel80-small + tasks: + - name: "build-all-php" + + # Ubuntu LTS + - name: build-ubuntu2204 + display_name: "Build: Ubuntu 22.04 x64" + tags: ["build", "ubuntu", "x64", "pr", "tag"] + run_on: ubuntu2204-small + tasks: + - name: "build-all-php" + - name: build-ubuntu2204-arm64 + display_name: "Build: Ubuntu 22.04 ARM64" + tags: ["build", "ubuntu", "arm64", "tag"] + run_on: ubuntu2204-arm64-small + tasks: + - name: "build-all-php" + - name: build-ubuntu2004 + display_name: "Build: Ubuntu 20.04 x64" + tags: ["build", "ubuntu", "x64", "pr", "tag"] + run_on: ubuntu2004-small + tasks: + - name: "build-all-php" + - name: build-ubuntu2004-arm64 + display_name: "Build: Ubuntu 20.04 ARM64" + tags: ["build", "ubuntu", "arm64", "tag"] + run_on: ubuntu2004-arm64-small + tasks: + - name: "build-all-php" diff --git a/.evergreen/config/functions.yml b/.evergreen/config/functions.yml new file mode 100644 index 000000000..e91c21f87 --- /dev/null +++ b/.evergreen/config/functions.yml @@ -0,0 +1,411 @@ +functions: + "fetch source": + # Executes git clone and applies the submitted patch, if any + - command: git.get_project + params: + directory: "src" + # Make an evergreen expansion file with dynamic values + - command: shell.exec + params: + working_dir: "src" + script: | + # Get the current unique version of this checkout + if [ "${is_patch}" = "true" ]; then + CURRENT_VERSION=$(git describe)-patch-${version_id} + else + CURRENT_VERSION=latest + fi + + export DRIVERS_TOOLS="$(pwd)/tests/drivers-evergreen-tools" + export PROJECT_DIRECTORY="$(pwd)" + + # Python has cygwin path problems on Windows. Detect prospective mongo-orchestration home directory + if [ "Windows_NT" = "$OS" ]; then # Magic variable in cygwin + export DRIVERS_TOOLS=$(cygpath -m $DRIVERS_TOOLS) + export PROJECT_DIRECTORY=$(cygpath -m $PROJECT_DIRECTORY) + fi + + export MONGO_ORCHESTRATION_HOME="$DRIVERS_TOOLS/.evergreen/orchestration" + export MONGODB_BINARIES="$DRIVERS_TOOLS/mongodb/bin" + export UPLOAD_BUCKET="${project}" + + cat < expansion.yml + CURRENT_VERSION: "$CURRENT_VERSION" + DRIVERS_TOOLS: "$DRIVERS_TOOLS" + MONGO_ORCHESTRATION_HOME: "$MONGO_ORCHESTRATION_HOME" + MONGODB_BINARIES: "$MONGODB_BINARIES" + UPLOAD_BUCKET: "$UPLOAD_BUCKET" + PROJECT_DIRECTORY: "$PROJECT_DIRECTORY" + PREPARE_SHELL: | + set -o errexit + export DRIVERS_TOOLS="$DRIVERS_TOOLS" + export MONGO_ORCHESTRATION_HOME="$MONGO_ORCHESTRATION_HOME" + export MONGODB_BINARIES="$MONGODB_BINARIES" + export UPLOAD_BUCKET="$UPLOAD_BUCKET" + export PROJECT_DIRECTORY="$PROJECT_DIRECTORY" + + export TMPDIR="$MONGO_ORCHESTRATION_HOME/db" + export PATH="$MONGODB_BINARIES:$PATH" + export PROJECT="${project}" + EOT + # See what we've done + cat expansion.yml + + # Load the expansion file to make an evergreen variable with the current unique version + - command: expansions.update + params: + file: src/expansion.yml + + # Upload build artifacts that other tasks may depend on + # Note this URL needs to be totally unique, while predictable for the next task + # so it can automatically download the artifacts + "upload extension": + # Copy compiled extension to source directory for archiving + - command: subprocess.exec + type: setup + params: + working_dir: "src" + binary: bash + args: + - -c + - cp `${PHP_PATH}/bin/php -r "echo ini_get('extension_dir');"`/mongodb.so . + # Compress and upload the entire build directory + - command: archive.targz_pack + params: + target: "${build_id}.tar.gz" + source_dir: src + include: + - "mongodb.so" + - command: s3.put + params: + aws_key: ${aws_key} + aws_secret: ${aws_secret} + bucket: mciuploads + content_type: ${content_type|application/x-gzip} + permissions: public-read + local_file: ${build_id}.tar.gz + remote_file: mongo-php-driver/${build_variant}/${revision}/${task_name}/${version_id}.tar.gz + + "fetch extension": + - command: s3.get + params: + aws_key: ${aws_key} + aws_secret: ${aws_secret} + bucket: mciuploads + remote_file: mongo-php-driver/${FETCH_BUILD_VARIANT}/${revision}/${FETCH_BUILD_TASK}/${version_id}.tar.gz + local_file: build.tar.gz + - command: archive.targz_extract + params: + destination: src + path: build.tar.gz + # Move compiled extension to correct ini path + - command: subprocess.exec + type: setup + params: + working_dir: "src" + binary: bash + args: + - -c + - mv mongodb.so `${PHP_PATH}/bin/php -r "echo ini_get('extension_dir');"` + + "prepare resources": + - command: shell.exec + params: + working_dir: src + script: | + ${PREPARE_SHELL} + git submodule update --init + echo "{ \"releases\": { \"default\": \"$MONGODB_BINARIES\" }}" > $MONGO_ORCHESTRATION_HOME/orchestration.config + + "upload test results": + - command: attach.xunit_results + params: + # Uploading test results does not work when using ${PROJECT_DIRECTORY}, + # so we use an absolute path to work around this + file: "src/test-results.xml" + - command: attach.results + params: + file_location: "${DRIVERS_TOOLS}/results.json" + - command: shell.exec + params: + working_dir: src + script: | + ${PREPARE_SHELL} + if [ -f test-results.xml ]; then + curl -Os https://cli.codecov.io/latest/linux/codecov + curl -Os https://cli.codecov.io/latest/linux/codecov.SHA256SUM + shasum -a 256 -c codecov.SHA256SUM + sudo chmod +x codecov + ./codecov upload-process \ + --report-type test_results \ + --disable-search \ + --fail-on-error \ + --token ${CODECOV_TOKEN} \ + --flag "${MONGODB_VERSION}-${TOPOLOGY}" \ + --file test-results.xml + else + echo "Skipping codecov test result upload" + fi + + "bootstrap mongo-orchestration": + - command: shell.exec + params: + script: | + ${PREPARE_SHELL} + SKIP_CRYPT_SHARED=${SKIP_CRYPT_SHARED} \ + SKIP_LEGACY_SHELL=true \ + MONGODB_VERSION=${MONGODB_VERSION} \ + ORCHESTRATION_FILE=${ORCHESTRATION_FILE} \ + TOPOLOGY=${TOPOLOGY} \ + AUTH=${AUTH} \ + SSL=${SSL} \ + STORAGE_ENGINE=${STORAGE_ENGINE} \ + LOAD_BALANCER=${LOAD_BALANCER} \ + REQUIRE_API_VERSION=${REQUIRE_API_VERSION} \ + bash ${DRIVERS_TOOLS}/.evergreen/run-orchestration.sh + - command: shell.exec + params: + script: | + printf "\n" >> mo-expansion.yml + printf "MONGODB_VERSION: '%s'\n" "${MONGODB_VERSION}" >> mo-expansion.yml + printf "TOPOLOGY: '%s'\n" "${TOPOLOGY}" >> mo-expansion.yml + # run-orchestration generates expansion file with MONGODB_URI and CRYPT_SHARED_LIB_PATH + - command: expansions.update + params: + file: mo-expansion.yml + + "stop mongo-orchestration": + - command: shell.exec + params: + script: | + ${PREPARE_SHELL} + bash ${DRIVERS_TOOLS}/.evergreen/stop-orchestration.sh + + "bootstrap mongohoused": + - command: shell.exec + params: + include_expansions_in_env: [AWS_SECRET_ACCESS_KEY, AWS_ACCESS_KEY_ID, AWS_SESSION_TOKEN] + script: | + cd ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake + + DRIVERS_TOOLS="${DRIVERS_TOOLS}" \ + bash ./pull-mongohouse-image.sh + - command: shell.exec + params: + script: | + cd ${DRIVERS_TOOLS}/.evergreen/atlas_data_lake + + DRIVERS_TOOLS="${DRIVERS_TOOLS}" \ + bash ./run-mongohouse-image.sh + + "run tests": + - command: shell.exec + type: test + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" + export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" + export AWS_TEMP_ACCESS_KEY_ID="${client_side_encryption_aws_temp_access_key_id}" + export AWS_TEMP_SECRET_ACCESS_KEY="${client_side_encryption_aws_temp_secret_access_key_key}" + export AWS_TEMP_SESSION_TOKEN="${client_side_encryption_aws_temp_session_token}" + export AZURE_TENANT_ID="${client_side_encryption_azure_tenant_id}" + export AZURE_CLIENT_ID="${client_side_encryption_azure_client_id}" + export AZURE_CLIENT_SECRET="${client_side_encryption_azure_client_secret}" + export GCP_EMAIL="${client_side_encryption_gcp_email}" + export GCP_PRIVATE_KEY="${client_side_encryption_gcp_privatekey}" + export KMIP_ENDPOINT="${client_side_encryption_kmip_endpoint}" + export KMS_ENDPOINT_EXPIRED="${client_side_encryption_kms_endpoint_expired}" + export KMS_ENDPOINT_WRONG_HOST="${client_side_encryption_kms_endpoint_wrong_host}" + export KMS_ENDPOINT_REQUIRE_CLIENT_CERT="${client_side_encryption_kms_endpoint_require_client_cert}" + export KMS_TLS_CA_FILE="${client_side_encryption_kms_tls_ca_file}" + export KMS_TLS_CERTIFICATE_KEY_FILE="${client_side_encryption_kms_tls_certificate_key_file}" + export PATH="${PHP_PATH}/bin:$PATH" + + API_VERSION=${API_VERSION} \ + CRYPT_SHARED_LIB_PATH=${CRYPT_SHARED_LIB_PATH} \ + MONGODB_URI="${MONGODB_URI}" \ + MONGODB_SINGLE_MONGOS_LB_URI="${SINGLE_MONGOS_LB_URI}" \ + MONGODB_MULTI_MONGOS_LB_URI="${MULTI_MONGOS_LB_URI}" \ + PHP_VERSION=${PHP_VERSION} \ + SSL=${SSL} \ + TESTS=${TESTS} \ + bash ${PROJECT_DIRECTORY}/.evergreen/run-tests.sh + + "cleanup": + - command: shell.exec + params: + script: | + ${PREPARE_SHELL} + rm -rf $DRIVERS_TOOLS || true + + "fix absolute paths": + - command: shell.exec + params: + script: | + ${PREPARE_SHELL} + for filename in $(find ${DRIVERS_TOOLS} -name \*.json); do + perl -p -i -e "s|ABSOLUTE_PATH_REPLACEMENT_TOKEN|${DRIVERS_TOOLS}|g" $filename + done + + "install dependencies": + - command: shell.exec + params: + working_dir: "src" + script: | + ${PREPARE_SHELL} + file="${PROJECT_DIRECTORY}/.evergreen/install-dependencies.sh" + # Don't use ${file} syntax here because evergreen treats it as an empty expansion. + [ -f "$file" ] && bash $file || echo "$file not available, skipping" + + "install composer": + - command: shell.exec + params: + add_expansions_to_env: true + working_dir: "src" + script: | + ${PREPARE_SHELL} + DEPENDENCIES=${DEPENDENCIES} \ + PHP_VERSION=${PHP_VERSION} \ + bash ${PROJECT_DIRECTORY}/.evergreen/install-composer.sh + + "start load balancer": + - command: shell.exec + params: + script: | + MONGODB_URI="${MONGODB_URI}" \ + bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh start + - command: expansions.update + params: + file: lb-expansion.yml + + "stop load balancer": + - command: shell.exec + params: + script: | + # Only run if a load balancer was started + if [ -n "${SINGLE_MONGOS_LB_URI}" ]; then + bash ${DRIVERS_TOOLS}/.evergreen/run-load-balancer.sh stop + fi + + "start kms servers": + - command: shell.exec + # Init venv without background:true to install dependencies + params: + shell: bash + script: |- + set -o errexit + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + - command: shell.exec + params: + background: true + shell: bash + # Use different ports for KMS HTTP servers to avoid conflicts with load balancers + script: |- + set -o errexit + cd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/expired.pem --port 8100 & + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/wrong-host.pem --port 8101 & + python -u kms_http_server.py --ca_file ../x509gen/ca.pem --cert_file ../x509gen/server.pem --port 8102 --require_client_cert & + python -u kms_kmip_server.py --port 5698 & + - command: expansions.update + params: + updates: + - key: client_side_encryption_kms_tls_ca_file + value: ${DRIVERS_TOOLS}/.evergreen/x509gen/ca.pem + - key: client_side_encryption_kms_tls_certificate_key_file + value: ${DRIVERS_TOOLS}/.evergreen/x509gen/client.pem + - key: client_side_encryption_kms_endpoint_expired + value: 127.0.0.1:8100 + - key: client_side_encryption_kms_endpoint_wrong_host + value: 127.0.0.1:8101 + - key: client_side_encryption_kms_endpoint_require_client_cert + value: 127.0.0.1:8102 + - key: client_side_encryption_kmip_endpoint + value: localhost:5698 + + "set aws temp creds": + - command: shell.exec + params: + shell: bash + script: |- + set -o errexit + + export AWS_ACCESS_KEY_ID="${client_side_encryption_aws_access_key_id}" + export AWS_SECRET_ACCESS_KEY="${client_side_encryption_aws_secret_access_key}" + export AWS_DEFAULT_REGION="us-east-1" + + pushd ${DRIVERS_TOOLS}/.evergreen/csfle + . ./activate-kmstlsvenv.sh + . ./set-temp-creds.sh + popd + + if [ -z "$CSFLE_AWS_TEMP_ACCESS_KEY_ID" ]; then + echo "Failed to set AWS temporary credentials!" + exit 1 + fi + + cat < aws-expansion.yml + client_side_encryption_aws_temp_access_key_id: "$CSFLE_AWS_TEMP_ACCESS_KEY_ID" + client_side_encryption_aws_temp_secret_access_key_key: "$CSFLE_AWS_TEMP_SECRET_ACCESS_KEY" + client_side_encryption_aws_temp_session_token: "$CSFLE_AWS_TEMP_SESSION_TOKEN" + EOT + - command: expansions.update + params: + file: aws-expansion.yml + + "locate PHP binaries": + - command: shell.exec + params: + shell: bash + add_expansions_to_env: true + script: | + if [ ! -d "/opt/php" ]; then + echo "PHP is not available" + exit 1 + fi + + if [ -d "/opt/php/${PHP_VERSION}-64bit/bin" ]; then + export PHP_PATH="/opt/php/${PHP_VERSION}-64bit" + else + # Try to find the newest version matching our constant + export PHP_PATH=`find /opt/php/ -maxdepth 1 -type d -name "${PHP_VERSION}*-64bit" -print | sort -V -r | head -n 1` + fi + + if [ ! -d "$PHP_PATH" ]; then + echo "Could not find PHP binaries for version ${PHP_VERSION}. Listing available versions..." + ls -1 /opt/php + exit 1 + fi + + echo "Found PHP: $PHP_PATH" + echo 'PHP_PATH: "'$PHP_PATH'"' > php-expansion.yml + - command: expansions.update + params: + file: php-expansion.yml + + "compile extension": + - command: subprocess.exec + type: test + params: + working_dir: src + add_expansions_to_env: true + binary: bash + args: + - .evergreen/compile-extension.sh + + # Run benchmarks. The filter skips the benchAmpWorkers subjects as they fail due to socket exceptions + "run benchmark": + - command: shell.exec + type: test + params: + working_dir: "src/benchmark" + script: | + ${PREPARE_SHELL} + export PATH="${PHP_PATH}/bin:$PATH" + + php ../composer.phar install --no-suggest + vendor/bin/phpbench run --report=env --report=evergreen --report=aggregate --output html --filter='bench(?!AmpWorkers)' diff --git a/.evergreen/config/generate-config.php b/.evergreen/config/generate-config.php new file mode 100755 index 000000000..c39a10829 --- /dev/null +++ b/.evergreen/config/generate-config.php @@ -0,0 +1,100 @@ +#!/usr/bin/env php + in_array($version, ['latest', 'rapid']) || version_compare($version, '5.0', '>='), +); +$requireApiServerVersions = array_filter( + $supportedMongoDBVersions, + // requireApiVersion supports MongoDB 5.0+ + fn (string $version): bool => in_array($version, ['latest', 'rapid']) || version_compare($version, '5.0', '>='), +); +$csfleServerVersions = array_filter( + $supportedMongoDBVersions, + // Test CSFLE on MongoDB 4.2+ + fn (string $version): bool => in_array($version, ['latest', 'rapid']) || version_compare($version, '4.2', '>='), +); + +$allFiles = []; + +// Build tasks +$allFiles[] = generateConfigs('tasks', 'build', 'phpVersion', 'build-extension.yml', $supportedPhpVersions); + +// Test tasks +$allFiles[] = generateConfigs('tasks', 'test', 'mongodbVersion', 'local.yml', $localServerVersions); +$allFiles[] = generateConfigs('tasks', 'test', 'mongodbVersion', 'load-balanced.yml', $loadBalancedServerVersions); +$allFiles[] = generateConfigs('tasks', 'test', 'mongodbVersion', 'require-api-version.yml', $requireApiServerVersions); +$allFiles[] = generateConfigs('tasks', 'test', 'mongodbVersion', 'csfle.yml', $csfleServerVersions); + +// Test variants +$allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'modern-php-full.yml', $supportedPhpVersions); +$allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'phpc.yml', [$latestPhpVersion]); +$allFiles[] = generateConfigs('buildvariants', 'test-variant', 'phpVersion', 'lowest.yml', [$lowestPhpVersion]); + +echo "Generated config. Use the following list to import files:\n"; +echo implode("\n", array_map('getImportConfig', $allFiles)) . "\n"; + +function getImportConfig(string $filename): string +{ + return '- filename: ' . $filename; +} + +function generateConfigs( + string $type, + string $directory, + string $replacementName, + string $templateFile, + array $versions, +): string { + $templateRelativePath = 'templates/' . $directory . '/' . $templateFile; + $template = file_get_contents(__DIR__ . '/' . $templateRelativePath); + $header = sprintf( + '# This file is generated automatically - please edit the "%s" template file instead.', + $templateRelativePath + ); + + $contents = <<
$version], + ); + } + + $filename = '/generated/' . $directory . '/' . $templateFile; + file_put_contents(__DIR__ . $filename, $contents); + + return '.evergreen/config' . $filename; +} diff --git a/.evergreen/config/generated/build/build-extension.yml b/.evergreen/config/generated/build/build-extension.yml new file mode 100644 index 000000000..48c7c26a3 --- /dev/null +++ b/.evergreen/config/generated/build/build-extension.yml @@ -0,0 +1,182 @@ +# This file is generated automatically - please edit the "templates/build/build-extension.yml" template file instead. +tasks: + - name: "build-php-8.4" + tags: ["build", "php8.4", "stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.4" + - func: "compile extension" + # TODO: Remove vars to switch to latest stable version when 2.1.0 is releeased + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.4-lowest" + tags: ["build", "php8.4", "lowest", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.4" + - func: "compile extension" + vars: + # TODO: Switch to 2.1.0 when it is released + # EXTENSION_VERSION: "2.0.0" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.4-next-stable" + tags: ["build", "php8.4", "next-stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.4" + - func: "compile extension" + vars: + # TODO: Switch to v2.1 when 2.1.0 is released + # EXTENSION_VERSION: "v2.1" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.4-next-minor" + tags: ["build", "php8.4", "next-minor"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.4" + - func: "compile extension" + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.3" + tags: ["build", "php8.3", "stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.3" + - func: "compile extension" + # TODO: Remove vars to switch to latest stable version when 2.1.0 is releeased + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.3-lowest" + tags: ["build", "php8.3", "lowest", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.3" + - func: "compile extension" + vars: + # TODO: Switch to 2.1.0 when it is released + # EXTENSION_VERSION: "2.0.0" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.3-next-stable" + tags: ["build", "php8.3", "next-stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.3" + - func: "compile extension" + vars: + # TODO: Switch to v2.1 when 2.1.0 is released + # EXTENSION_VERSION: "v2.1" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.3-next-minor" + tags: ["build", "php8.3", "next-minor"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.3" + - func: "compile extension" + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.2" + tags: ["build", "php8.2", "stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.2" + - func: "compile extension" + # TODO: Remove vars to switch to latest stable version when 2.1.0 is releeased + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.2-lowest" + tags: ["build", "php8.2", "lowest", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.2" + - func: "compile extension" + vars: + # TODO: Switch to 2.1.0 when it is released + # EXTENSION_VERSION: "2.0.0" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.2-next-stable" + tags: ["build", "php8.2", "next-stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.2" + - func: "compile extension" + vars: + # TODO: Switch to v2.1 when 2.1.0 is released + # EXTENSION_VERSION: "v2.1" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.2-next-minor" + tags: ["build", "php8.2", "next-minor"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.2" + - func: "compile extension" + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.1" + tags: ["build", "php8.1", "stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.1" + - func: "compile extension" + # TODO: Remove vars to switch to latest stable version when 2.1.0 is releeased + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.1-lowest" + tags: ["build", "php8.1", "lowest", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.1" + - func: "compile extension" + vars: + # TODO: Switch to 2.1.0 when it is released + # EXTENSION_VERSION: "2.0.0" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.1-next-stable" + tags: ["build", "php8.1", "next-stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.1" + - func: "compile extension" + vars: + # TODO: Switch to v2.1 when 2.1.0 is released + # EXTENSION_VERSION: "v2.1" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-8.1-next-minor" + tags: ["build", "php8.1", "next-minor"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "8.1" + - func: "compile extension" + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" diff --git a/.evergreen/config/generated/test-variant/lowest.yml b/.evergreen/config/generated/test-variant/lowest.yml new file mode 100644 index 000000000..0e3b79709 --- /dev/null +++ b/.evergreen/config/generated/test-variant/lowest.yml @@ -0,0 +1,16 @@ +# This file is generated automatically - please edit the "templates/test-variant/lowest.yml" template file instead. +buildvariants: + - name: test-rhel80-php-8.1-local-lowest + tags: ["test", "rhel", "x64", "php8.1", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP 8.1, Lowest Dependencies" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-8.1-lowest" + PHP_VERSION: "8.1" + DEPENDENCIES: "lowest" + depends_on: + - variant: "build-rhel80" + name: "build-php-8.1-lowest" + tasks: + - ".replicaset .local .4.2 !.csfle" diff --git a/.evergreen/config/generated/test-variant/modern-php-full.yml b/.evergreen/config/generated/test-variant/modern-php-full.yml new file mode 100644 index 000000000..1186c35a5 --- /dev/null +++ b/.evergreen/config/generated/test-variant/modern-php-full.yml @@ -0,0 +1,234 @@ +# This file is generated automatically - please edit the "templates/test-variant/modern-php-full.yml" template file instead. +buildvariants: + # Test MongoDB >= 7.0 + - name: test-debian12-php-8.4-local + tags: ["test", "debian", "x64", "php8.4", "pr", "tag"] + display_name: "Test: Debian 12, PHP 8.4" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.4" + PHP_VERSION: "8.4" + VARIANT: debian12 + depends_on: + - variant: "build-debian12" + name: "build-php-8.4" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + + # Test MongoDB 5.0 and 6.0 + - name: test-debian11-php-8.4-local + tags: ["test", "debian", "x64", "php8.4", "pr", "tag"] + display_name: "Test: Debian 11, PHP 8.4" + run_on: debian11-small + expansions: + FETCH_BUILD_VARIANT: "build-debian11" + FETCH_BUILD_TASK: "build-php-8.4" + PHP_VERSION: "8.4" + depends_on: + - variant: "build-debian11" + name: "build-php-8.4" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + + # Test versions < 5.0 + - name: test-rhel80-php-8.4 + tags: ["test", "debian", "x64", "php8.4", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP 8.4" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-8.4" + PHP_VERSION: "8.4" + depends_on: + - variant: "build-rhel80" + name: "build-php-8.4" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - "test_atlas_task_group" + - ".csfle" + # Test MongoDB >= 7.0 + - name: test-debian12-php-8.3-local + tags: ["test", "debian", "x64", "php8.3", "pr", "tag"] + display_name: "Test: Debian 12, PHP 8.3" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.3" + PHP_VERSION: "8.3" + VARIANT: debian12 + depends_on: + - variant: "build-debian12" + name: "build-php-8.3" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + + # Test MongoDB 5.0 and 6.0 + - name: test-debian11-php-8.3-local + tags: ["test", "debian", "x64", "php8.3", "pr", "tag"] + display_name: "Test: Debian 11, PHP 8.3" + run_on: debian11-small + expansions: + FETCH_BUILD_VARIANT: "build-debian11" + FETCH_BUILD_TASK: "build-php-8.3" + PHP_VERSION: "8.3" + depends_on: + - variant: "build-debian11" + name: "build-php-8.3" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + + # Test versions < 5.0 + - name: test-rhel80-php-8.3 + tags: ["test", "debian", "x64", "php8.3", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP 8.3" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-8.3" + PHP_VERSION: "8.3" + depends_on: + - variant: "build-rhel80" + name: "build-php-8.3" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - "test_atlas_task_group" + - ".csfle" + # Test MongoDB >= 7.0 + - name: test-debian12-php-8.2-local + tags: ["test", "debian", "x64", "php8.2", "pr", "tag"] + display_name: "Test: Debian 12, PHP 8.2" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.2" + PHP_VERSION: "8.2" + VARIANT: debian12 + depends_on: + - variant: "build-debian12" + name: "build-php-8.2" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + + # Test MongoDB 5.0 and 6.0 + - name: test-debian11-php-8.2-local + tags: ["test", "debian", "x64", "php8.2", "pr", "tag"] + display_name: "Test: Debian 11, PHP 8.2" + run_on: debian11-small + expansions: + FETCH_BUILD_VARIANT: "build-debian11" + FETCH_BUILD_TASK: "build-php-8.2" + PHP_VERSION: "8.2" + depends_on: + - variant: "build-debian11" + name: "build-php-8.2" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + + # Test versions < 5.0 + - name: test-rhel80-php-8.2 + tags: ["test", "debian", "x64", "php8.2", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP 8.2" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-8.2" + PHP_VERSION: "8.2" + depends_on: + - variant: "build-rhel80" + name: "build-php-8.2" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - "test_atlas_task_group" + - ".csfle" + # Test MongoDB >= 7.0 + - name: test-debian12-php-8.1-local + tags: ["test", "debian", "x64", "php8.1", "pr", "tag"] + display_name: "Test: Debian 12, PHP 8.1" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.1" + PHP_VERSION: "8.1" + VARIANT: debian12 + depends_on: + - variant: "build-debian12" + name: "build-php-8.1" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + + # Test MongoDB 5.0 and 6.0 + - name: test-debian11-php-8.1-local + tags: ["test", "debian", "x64", "php8.1", "pr", "tag"] + display_name: "Test: Debian 11, PHP 8.1" + run_on: debian11-small + expansions: + FETCH_BUILD_VARIANT: "build-debian11" + FETCH_BUILD_TASK: "build-php-8.1" + PHP_VERSION: "8.1" + depends_on: + - variant: "build-debian11" + name: "build-php-8.1" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + + # Test versions < 5.0 + - name: test-rhel80-php-8.1 + tags: ["test", "debian", "x64", "php8.1", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP 8.1" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-8.1" + PHP_VERSION: "8.1" + depends_on: + - variant: "build-rhel80" + name: "build-php-8.1" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - "test_atlas_task_group" + - ".csfle" diff --git a/.evergreen/config/generated/test-variant/phpc.yml b/.evergreen/config/generated/test-variant/phpc.yml new file mode 100644 index 000000000..65907fba4 --- /dev/null +++ b/.evergreen/config/generated/test-variant/phpc.yml @@ -0,0 +1,37 @@ +# This file is generated automatically - please edit the "templates/test-variant/phpc.yml" template file instead. +buildvariants: + # Variants with different PHPC versions + - name: test-debian12-php-8.4-phpc-next-stable + tags: ["test", "debian", "x64", "php8.4", "pr", "tag"] + display_name: "Test: Debian 12, PHP 8.4, PHPC next-stable" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.4-next-stable" + PHP_VERSION: "8.4" + depends_on: + - variant: "build-debian12" + name: "build-php-8.4-next-stable" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - "test-atlas-data-lake" + + - name: test-debian12-php-8.4-phpc-next-minor + tags: ["test", "debian", "x64", "php8.4"] + display_name: "Test: Debian 12, PHP 8.4, PHPC next-minor" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-8.4-next-minor" + PHP_VERSION: "8.4" + depends_on: + - variant: "build-debian12" + name: "build-php-8.4-next-minor" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" diff --git a/.evergreen/config/generated/test/csfle.yml b/.evergreen/config/generated/test/csfle.yml new file mode 100644 index 000000000..23bca1f06 --- /dev/null +++ b/.evergreen/config/generated/test/csfle.yml @@ -0,0 +1,322 @@ +# This file is generated automatically - please edit the "templates/test/csfle.yml" template file instead. +tasks: + - name: "test-mongodb-latest-crypt-shared" + tags: ["replicaset", "local", "latest", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-latest-mongocryptd" + tags: ["replicaset", "local", "latest", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-latest-no-aws-creds" + tags: ["replicaset", "local", "latest", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-rapid-crypt-shared" + tags: ["replicaset", "local", "rapid", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-rapid-mongocryptd" + tags: ["replicaset", "local", "rapid", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-rapid-no-aws-creds" + tags: ["replicaset", "local", "rapid", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-8.0-crypt-shared" + tags: ["replicaset", "local", "8.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-8.0-mongocryptd" + tags: ["replicaset", "local", "8.0", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-8.0-no-aws-creds" + tags: ["replicaset", "local", "8.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-7.0-crypt-shared" + tags: ["replicaset", "local", "7.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-7.0-mongocryptd" + tags: ["replicaset", "local", "7.0", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-7.0-no-aws-creds" + tags: ["replicaset", "local", "7.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-6.0-crypt-shared" + tags: ["replicaset", "local", "6.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-6.0-mongocryptd" + tags: ["replicaset", "local", "6.0", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-6.0-no-aws-creds" + tags: ["replicaset", "local", "6.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-5.0-crypt-shared" + tags: ["replicaset", "local", "5.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-5.0-mongocryptd" + tags: ["replicaset", "local", "5.0", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-5.0-no-aws-creds" + tags: ["replicaset", "local", "5.0", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-4.4-crypt-shared" + tags: ["replicaset", "local", "4.4", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-4.4-mongocryptd" + tags: ["replicaset", "local", "4.4", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-4.4-no-aws-creds" + tags: ["replicaset", "local", "4.4", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" + - name: "test-mongodb-4.2-crypt-shared" + tags: ["replicaset", "local", "4.2", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-4.2-mongocryptd" + tags: ["replicaset", "local", "4.2", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-4.2-no-aws-creds" + tags: ["replicaset", "local", "4.2", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" diff --git a/.evergreen/config/generated/test/load-balanced.yml b/.evergreen/config/generated/test/load-balanced.yml new file mode 100644 index 000000000..1bd2bb104 --- /dev/null +++ b/.evergreen/config/generated/test/load-balanced.yml @@ -0,0 +1,98 @@ +# This file is generated automatically - please edit the "templates/test/load-balanced.yml" template file instead. +tasks: + - name: "test-mongodb-latest-loadbalanced" + tags: ["loadbalanced", "local", "latest", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "latest" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + - name: "test-mongodb-rapid-loadbalanced" + tags: ["loadbalanced", "local", "rapid", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "rapid" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + - name: "test-mongodb-8.0-loadbalanced" + tags: ["loadbalanced", "local", "8.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "8.0" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + - name: "test-mongodb-7.0-loadbalanced" + tags: ["loadbalanced", "local", "7.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "7.0" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + - name: "test-mongodb-6.0-loadbalanced" + tags: ["loadbalanced", "local", "6.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "6.0" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" + - name: "test-mongodb-5.0-loadbalanced" + tags: ["loadbalanced", "local", "5.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "5.0" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" diff --git a/.evergreen/config/generated/test/local.yml b/.evergreen/config/generated/test/local.yml new file mode 100644 index 000000000..cee192412 --- /dev/null +++ b/.evergreen/config/generated/test/local.yml @@ -0,0 +1,258 @@ +# This file is generated automatically - please edit the "templates/test/local.yml" template file instead. +tasks: + - name: "test-mongodb-latest-standalone-noauth-nossl" + tags: ["standalone", "local", "latest", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-latest-replicaset-noauth-nossl" + tags: ["replicaset", "local", "latest", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-latest-sharded-noauth-nossl" + tags: ["sharded", "local", "latest", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-rapid-standalone-noauth-nossl" + tags: ["standalone", "local", "rapid", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-rapid-replicaset-noauth-nossl" + tags: ["replicaset", "local", "rapid", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-rapid-sharded-noauth-nossl" + tags: ["sharded", "local", "rapid", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-8.0-standalone-noauth-nossl" + tags: ["standalone", "local", "8.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-8.0-replicaset-noauth-nossl" + tags: ["replicaset", "local", "8.0", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-8.0-sharded-noauth-nossl" + tags: ["sharded", "local", "8.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-7.0-standalone-noauth-nossl" + tags: ["standalone", "local", "7.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-7.0-replicaset-noauth-nossl" + tags: ["replicaset", "local", "7.0", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-7.0-sharded-noauth-nossl" + tags: ["sharded", "local", "7.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-6.0-standalone-noauth-nossl" + tags: ["standalone", "local", "6.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-6.0-replicaset-noauth-nossl" + tags: ["replicaset", "local", "6.0", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-6.0-sharded-noauth-nossl" + tags: ["sharded", "local", "6.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-5.0-standalone-noauth-nossl" + tags: ["standalone", "local", "5.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-5.0-replicaset-noauth-nossl" + tags: ["replicaset", "local", "5.0", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-5.0-sharded-noauth-nossl" + tags: ["sharded", "local", "5.0", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-4.4-standalone-noauth-nossl" + tags: ["standalone", "local", "4.4", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-4.4-replicaset-noauth-nossl" + tags: ["replicaset", "local", "4.4", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-4.4-sharded-noauth-nossl" + tags: ["sharded", "local", "4.4", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "4.4" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + - name: "test-mongodb-4.2-standalone-noauth-nossl" + tags: ["standalone", "local", "4.2", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-4.2-replicaset-noauth-nossl" + tags: ["replicaset", "local", "4.2", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-4.2-sharded-noauth-nossl" + tags: ["sharded", "local", "4.2", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "4.2" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" diff --git a/.evergreen/config/generated/test/require-api-version.yml b/.evergreen/config/generated/test/require-api-version.yml new file mode 100644 index 000000000..fb16e28a3 --- /dev/null +++ b/.evergreen/config/generated/test/require-api-version.yml @@ -0,0 +1,170 @@ +# This file is generated automatically - please edit the "templates/test/require-api-version.yml" template file instead. +tasks: + - name: "test-mongodb-latest-requireApiVersion" + tags: ["standalone", "local", "latest", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-latest-acceptApiVersion2" + tags: ["standalone", "local", "latest", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "latest" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + - name: "test-mongodb-rapid-requireApiVersion" + tags: ["standalone", "local", "rapid", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-rapid-acceptApiVersion2" + tags: ["standalone", "local", "rapid", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "rapid" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + - name: "test-mongodb-8.0-requireApiVersion" + tags: ["standalone", "local", "8.0", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-8.0-acceptApiVersion2" + tags: ["standalone", "local", "8.0", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "8.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + - name: "test-mongodb-7.0-requireApiVersion" + tags: ["standalone", "local", "7.0", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-7.0-acceptApiVersion2" + tags: ["standalone", "local", "7.0", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "7.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + - name: "test-mongodb-6.0-requireApiVersion" + tags: ["standalone", "local", "6.0", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-6.0-acceptApiVersion2" + tags: ["standalone", "local", "6.0", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "6.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" + - name: "test-mongodb-5.0-requireApiVersion" + tags: ["standalone", "local", "5.0", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-5.0-acceptApiVersion2" + tags: ["standalone", "local", "5.0", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "5.0" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" diff --git a/.evergreen/config/php.ini b/.evergreen/config/php.ini index 45969d065..0b1af3129 100644 --- a/.evergreen/config/php.ini +++ b/.evergreen/config/php.ini @@ -1 +1,2 @@ extension=mongodb.so +memory_limit=-1 diff --git a/.evergreen/config/templates/build/build-extension.yml b/.evergreen/config/templates/build/build-extension.yml new file mode 100644 index 000000000..c6b1f411c --- /dev/null +++ b/.evergreen/config/templates/build/build-extension.yml @@ -0,0 +1,45 @@ + - name: "build-php-%phpVersion%" + tags: ["build", "php%phpVersion%", "stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "%phpVersion%" + - func: "compile extension" + # TODO: Remove vars to switch to latest stable version when 2.1.0 is releeased + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-%phpVersion%-lowest" + tags: ["build", "php%phpVersion%", "lowest", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "%phpVersion%" + - func: "compile extension" + vars: + # TODO: Switch to 2.1.0 when it is released + # EXTENSION_VERSION: "2.0.0" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-%phpVersion%-next-stable" + tags: ["build", "php%phpVersion%", "next-stable", "pr", "tag"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "%phpVersion%" + - func: "compile extension" + vars: + # TODO: Switch to v2.1 when 2.1.0 is released + # EXTENSION_VERSION: "v2.1" + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" + - name: "build-php-%phpVersion%-next-minor" + tags: ["build", "php%phpVersion%", "next-minor"] + commands: + - func: "locate PHP binaries" + vars: + PHP_VERSION: "%phpVersion%" + - func: "compile extension" + vars: + EXTENSION_BRANCH: "v2.x" + - func: "upload extension" diff --git a/.evergreen/config/templates/test-variant/lowest.yml b/.evergreen/config/templates/test-variant/lowest.yml new file mode 100644 index 000000000..0959127fd --- /dev/null +++ b/.evergreen/config/templates/test-variant/lowest.yml @@ -0,0 +1,14 @@ + - name: test-rhel80-php-%phpVersion%-local-lowest + tags: ["test", "rhel", "x64", "php%phpVersion%", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP %phpVersion%, Lowest Dependencies" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-%phpVersion%-lowest" + PHP_VERSION: "%phpVersion%" + DEPENDENCIES: "lowest" + depends_on: + - variant: "build-rhel80" + name: "build-php-%phpVersion%-lowest" + tasks: + - ".replicaset .local .4.2 !.csfle" diff --git a/.evergreen/config/templates/test-variant/modern-php-full.yml b/.evergreen/config/templates/test-variant/modern-php-full.yml new file mode 100644 index 000000000..5a57d1c24 --- /dev/null +++ b/.evergreen/config/templates/test-variant/modern-php-full.yml @@ -0,0 +1,58 @@ + # Test MongoDB >= 7.0 + - name: test-debian12-php-%phpVersion%-local + tags: ["test", "debian", "x64", "php%phpVersion%", "pr", "tag"] + display_name: "Test: Debian 12, PHP %phpVersion%" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-%phpVersion%" + PHP_VERSION: "%phpVersion%" + VARIANT: debian12 + depends_on: + - variant: "build-debian12" + name: "build-php-%phpVersion%" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + + # Test MongoDB 5.0 and 6.0 + - name: test-debian11-php-%phpVersion%-local + tags: ["test", "debian", "x64", "php%phpVersion%", "pr", "tag"] + display_name: "Test: Debian 11, PHP %phpVersion%" + run_on: debian11-small + expansions: + FETCH_BUILD_VARIANT: "build-debian11" + FETCH_BUILD_TASK: "build-php-%phpVersion%" + PHP_VERSION: "%phpVersion%" + depends_on: + - variant: "build-debian11" + name: "build-php-%phpVersion%" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.7.0 !.8.0 !.rapid !.latest" + + # Test versions < 5.0 + - name: test-rhel80-php-%phpVersion% + tags: ["test", "debian", "x64", "php%phpVersion%", "pr", "tag"] + display_name: "Test: RHEL 8.0, PHP %phpVersion%" + run_on: rhel80-small + expansions: + FETCH_BUILD_VARIANT: "build-rhel80" + FETCH_BUILD_TASK: "build-php-%phpVersion%" + PHP_VERSION: "%phpVersion%" + depends_on: + - variant: "build-rhel80" + name: "build-php-%phpVersion%" + tasks: + # Remember to add new major versions here as they are released + - ".standalone .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".replicaset .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".sharded .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - ".loadbalanced .local !.csfle !.6.0 !.7.0 !.8.0 !.rapid !.latest" + - "test_atlas_task_group" + - ".csfle" diff --git a/.evergreen/config/templates/test-variant/phpc.yml b/.evergreen/config/templates/test-variant/phpc.yml new file mode 100644 index 000000000..667bda98a --- /dev/null +++ b/.evergreen/config/templates/test-variant/phpc.yml @@ -0,0 +1,35 @@ + # Variants with different PHPC versions + - name: test-debian12-php-%phpVersion%-phpc-next-stable + tags: ["test", "debian", "x64", "php%phpVersion%", "pr", "tag"] + display_name: "Test: Debian 12, PHP %phpVersion%, PHPC next-stable" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-%phpVersion%-next-stable" + PHP_VERSION: "%phpVersion%" + depends_on: + - variant: "build-debian12" + name: "build-php-%phpVersion%-next-stable" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - "test-atlas-data-lake" + + - name: test-debian12-php-%phpVersion%-phpc-next-minor + tags: ["test", "debian", "x64", "php%phpVersion%"] + display_name: "Test: Debian 12, PHP %phpVersion%, PHPC next-minor" + run_on: debian12-small + expansions: + FETCH_BUILD_VARIANT: "build-debian12" + FETCH_BUILD_TASK: "build-php-%phpVersion%-next-minor" + PHP_VERSION: "%phpVersion%" + depends_on: + - variant: "build-debian12" + name: "build-php-%phpVersion%-next-minor" + tasks: + - ".standalone .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".replicaset .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".sharded .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" + - ".loadbalanced .local !.csfle !.4.2 !.4.4 !.5.0 !.6.0" diff --git a/.evergreen/config/templates/test/csfle.yml b/.evergreen/config/templates/test/csfle.yml new file mode 100644 index 000000000..995af2cba --- /dev/null +++ b/.evergreen/config/templates/test/csfle.yml @@ -0,0 +1,40 @@ + - name: "test-mongodb-%mongodbVersion%-crypt-shared" + tags: ["replicaset", "local", "%mongodbVersion%", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-%mongodbVersion%-mongocryptd" + tags: ["replicaset", "local", "%mongodbVersion%", "csfle", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + SKIP_CRYPT_SHARED: "yes" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "csfle" + + - name: "test-mongodb-%mongodbVersion%-no-aws-creds" + tags: ["replicaset", "local", "%mongodbVersion%", "csfle", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "run tests" + vars: + client_side_encryption_aws_access_key_id: "" + client_side_encryption_aws_secret_access_key: "" + TESTS: "csfle-without-aws-creds" diff --git a/.evergreen/config/templates/test/load-balanced.yml b/.evergreen/config/templates/test/load-balanced.yml new file mode 100644 index 000000000..5b3c4233b --- /dev/null +++ b/.evergreen/config/templates/test/load-balanced.yml @@ -0,0 +1,16 @@ + - name: "test-mongodb-%mongodbVersion%-loadbalanced" + tags: ["loadbalanced", "local", "%mongodbVersion%", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "%mongodbVersion%" + LOAD_BALANCER: "true" + SSL: "yes" + - func: "start load balancer" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + MONGODB_URI: "${SINGLE_MONGOS_LB_URI}" + SSL: "yes" diff --git a/.evergreen/config/templates/test/local.yml b/.evergreen/config/templates/test/local.yml new file mode 100644 index 000000000..e527f18fd --- /dev/null +++ b/.evergreen/config/templates/test/local.yml @@ -0,0 +1,32 @@ + - name: "test-mongodb-%mongodbVersion%-standalone-noauth-nossl" + tags: ["standalone", "local", "%mongodbVersion%", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-%mongodbVersion%-replicaset-noauth-nossl" + tags: ["replicaset", "local", "%mongodbVersion%", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "replica_set" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + + - name: "test-mongodb-%mongodbVersion%-sharded-noauth-nossl" + tags: ["sharded", "local", "%mongodbVersion%", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "sharded_cluster" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" diff --git a/.evergreen/config/templates/test/require-api-version.yml b/.evergreen/config/templates/test/require-api-version.yml new file mode 100644 index 000000000..54a00c94a --- /dev/null +++ b/.evergreen/config/templates/test/require-api-version.yml @@ -0,0 +1,28 @@ + - name: "test-mongodb-%mongodbVersion%-requireApiVersion" + tags: ["standalone", "local", "%mongodbVersion%", "versioned_api", "pr", "tag"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + AUTH: "auth" + REQUIRE_API_VERSION: "yes" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + API_VERSION: "1" + + - name: "test-mongodb-%mongodbVersion%-acceptApiVersion2" + tags: ["standalone", "local", "%mongodbVersion%", "versioned_api"] + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + ORCHESTRATION_FILE: "versioned-api-testing.json" + MONGODB_VERSION: "%mongodbVersion%" + - func: "start kms servers" + - func: "set aws temp creds" + - func: "run tests" + vars: + TESTS: "versioned-api" diff --git a/.evergreen/config/test-task-groups.yml b/.evergreen/config/test-task-groups.yml new file mode 100644 index 000000000..06f159ac2 --- /dev/null +++ b/.evergreen/config/test-task-groups.yml @@ -0,0 +1,39 @@ +task_groups: + - name: test_atlas_task_group + setup_group: + - func: "fetch source" + - func: "prepare resources" + - func: "fix absolute paths" + - func: "install dependencies" + - func: "locate PHP binaries" + - func: "fetch extension" + - func: "install composer" + - command: ec2.assume_role + params: + role_arn: ${aws_test_secrets_role} + - command: subprocess.exec + params: + working_dir: src + binary: bash + add_expansions_to_env: true + env: + MONGODB_VERSION: '7.0' + args: + - ${DRIVERS_TOOLS}/.evergreen/atlas/setup-atlas-cluster.sh + - command: expansions.update + params: + file: src/atlas-expansion.yml + teardown_group: + - command: subprocess.exec + params: + working_dir: src + binary: bash + add_expansions_to_env: true + args: + - ${DRIVERS_TOOLS}/.evergreen/atlas/teardown-atlas-cluster.sh + - func: "upload test results" + - func: "cleanup" + setup_group_can_fail_task: true + setup_group_timeout_secs: 1800 + tasks: + - test-atlas diff --git a/.evergreen/config/test-tasks.yml b/.evergreen/config/test-tasks.yml new file mode 100644 index 000000000..5dabc8a10 --- /dev/null +++ b/.evergreen/config/test-tasks.yml @@ -0,0 +1,49 @@ +tasks: + - name: "test-atlas" + exec_timeout_secs: 1800 + commands: + - func: "start kms servers" + - func: "run tests" + vars: + TESTS: "atlas" + + - name: "run-benchmark" + exec_timeout_secs: 3600 + commands: + - func: "bootstrap mongo-orchestration" + vars: + TOPOLOGY: "server" + MONGODB_VERSION: "v6.0-perf" + - func: "run benchmark" + - command: shell.exec + params: + script: | + # We use the requester expansion to determine whether the data is from a mainline evergreen run or not + if [ "${requester}" == "commit" ]; then + is_mainline=true + else + is_mainline=false + fi + + # We parse the username out of the order_id as patches append that in and SPS does not need that information + parsed_order_id=$(echo "${revision_order_id}" | awk -F'_' '{print $NF}') + + # Submit the performance data to the SPS endpoint + response=$(curl -s -w "\nHTTP_STATUS:%{http_code}" -X 'POST' \ + "https://performance-monitoring-api.corp.mongodb.com/raw_perf_results/cedar_report?project=${project_id}&version=${version_id}&variant=${build_variant}&order=$parsed_order_id&task_name=${task_name}&task_id=${task_id}&execution=${execution}&mainline=$is_mainline" \ + -H 'accept: application/json' \ + -H 'Content-Type: application/json' \ + -d @src/benchmark/.phpbench/results.json) + + http_status=$(echo "$response" | grep "HTTP_STATUS" | awk -F':' '{print $2}') + response_body=$(echo "$response" | sed '/HTTP_STATUS/d') + + # We want to throw an error if the data was not successfully submitted + if [ "$http_status" -ne 200 ]; then + echo "Error: Received HTTP status $http_status" + echo "Response Body: $response_body" + exit 1 + fi + + echo "Response Body: $response_body" + echo "HTTP Status: $http_status" diff --git a/.evergreen/config/test-variants.yml b/.evergreen/config/test-variants.yml new file mode 100644 index 000000000..816d1bc76 --- /dev/null +++ b/.evergreen/config/test-variants.yml @@ -0,0 +1,15 @@ +buildvariants: + # Run benchmarks + - name: benchmark-rhel90 + tags: ["benchmark", "rhel", "x64"] + display_name: "Benchmark: RHEL 9.0, MongoDB 6.0" + run_on: rhel90-dbx-perf-large + expansions: + FETCH_BUILD_VARIANT: "build-rhel90" + FETCH_BUILD_TASK: "build-php-8.2" + PHP_VERSION: "8.2" + depends_on: + - variant: "build-rhel90" + name: "build-php-8.2" + tasks: + - "run-benchmark" diff --git a/.evergreen/install-composer.sh b/.evergreen/install-composer.sh new file mode 100644 index 000000000..771644e56 --- /dev/null +++ b/.evergreen/install-composer.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -o errexit # Exit the script with error if any of the commands fail + +# Supported environment variables +DEPENDENCIES=${DEPENDENCIES:-} # Specify "lowest" to prefer lowest dependencies +COMPOSER_FLAGS="${COMPOSER_FLAGS:-}" # Optional, additional Composer flags + +PATH="$PHP_PATH/bin:$PATH" + +install_composer () +{ + EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" + php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" + ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" + + if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then + >&2 echo 'ERROR: Invalid installer checksum' + rm composer-setup.php + exit 1 + fi + + php composer-setup.php --quiet + rm composer-setup.php +} + +case "$DEPENDENCIES" in + lowest*) + COMPOSER_FLAGS="${COMPOSER_FLAGS} --prefer-lowest" + ;; + + *) + ;; +esac + +cp ${PROJECT_DIRECTORY}/.evergreen/config/php.ini ${PHP_PATH}/lib/php.ini + +php --ri mongodb + +install_composer + +# Remove psalm as it's not compatible with PHP 8.4: https://github.com/vimeo/psalm/pull/10928 +if [ "$PHP_VERSION" == "8.4" ]; then + php composer.phar remove --no-update --dev vimeo/psalm +fi + +php composer.phar update $COMPOSER_FLAGS diff --git a/.evergreen/install-dependencies.sh b/.evergreen/install-dependencies.sh index 587558c7c..9768f5ba9 100644 --- a/.evergreen/install-dependencies.sh +++ b/.evergreen/install-dependencies.sh @@ -1,84 +1,6 @@ -#!/bin/sh +#!/usr/bin/env bash set -o errexit # Exit the script with error if any of the commands fail -set_php_version () -{ - PHP_VERSION=$1 - - if [ ! -d "/opt/php" ]; then - echo "PHP is not available" - exit 1 - fi - - if [ -d "/opt/php/${PHP_VERSION}-64bit/bin" ]; then - export PHP_PATH="/opt/php/${PHP_VERSION}-64bit" - else - # Try to find the newest version matching our constant - export PHP_PATH=`find /opt/php/ -maxdepth 1 -type d -name "${PHP_VERSION}.*-64bit" -print | sort -V -r | head -1` - fi - - if [ ! -d "$PHP_PATH" ]; then - echo "Could not find PHP binaries for version ${PHP_VERSION}. Listing available versions..." - ls -1 /opt/php - exit 1 - fi - - echo 'PHP_PATH: "'$PHP_PATH'"' > php-expansion.yml - export PATH=$PHP_PATH/bin:$PATH -} - -install_extension () -{ - rm -f ${PHP_PATH}/lib/php.ini - - if [ "x${EXTENSION_BRANCH}" != "x" ] || [ "x${EXTENSION_REPO}" != "x" ]; then - CLONE_REPO=${EXTENSION_REPO:-https://github.com/mongodb/mongo-php-driver} - CHECKOUT_BRANCH=${EXTENSION_BRANCH:-master} - - echo "Compiling driver branch ${CHECKOUT_BRANCH} from repository ${CLONE_REPO}" - - mkdir -p /tmp/compile - rm -rf /tmp/compile/mongo-php-driver - git clone ${CLONE_REPO} /tmp/compile/mongo-php-driver - cd /tmp/compile/mongo-php-driver - - git checkout ${CHECKOUT_BRANCH} - git submodule update --init - phpize - ./configure --enable-mongodb-developer-flags - make all -j20 > /dev/null - make install - - cd ${PROJECT_DIRECTORY} - elif [ "x${EXTENSION_VERSION}" != "x" ]; then - echo "Installing driver version ${EXTENSION_VERSION} from PECL" - pecl install -f mongodb-${EXTENSION_VERSION} - else - echo "Installing latest driver version from PECL" - pecl install -f mongodb - fi - - sudo cp ${PROJECT_DIRECTORY}/.evergreen/config/php.ini ${PHP_PATH}/lib/php.ini - - php --ri mongodb -} - -install_composer () -{ - EXPECTED_CHECKSUM="$(php -r 'copy("https://composer.github.io/installer.sig", "php://stdout");')" - php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" - ACTUAL_CHECKSUM="$(php -r "echo hash_file('sha384', 'composer-setup.php');")" - - if [ "$EXPECTED_CHECKSUM" != "$ACTUAL_CHECKSUM" ]; then - >&2 echo 'ERROR: Invalid installer checksum' - rm composer-setup.php - exit 1 - fi - - php composer-setup.php --quiet - rm composer-setup.php -} - # Functions to fetch MongoDB binaries . ${DRIVERS_TOOLS}/.evergreen/download-mongodb.sh OS=$(uname -s | tr '[:upper:]' '[:lower:]') @@ -112,18 +34,3 @@ case "$DISTRO" in echo "All other platforms..." ;; esac - -case "$DEPENDENCIES" in - lowest*) - COMPOSER_FLAGS="${COMPOSER_FLAGS} --prefer-lowest" - ;; - - *) - ;; -esac - -set_php_version $PHP_VERSION -install_extension -install_composer - -php composer.phar update $COMPOSER_FLAGS diff --git a/.evergreen/ocsp/README.md b/.evergreen/ocsp/README.md deleted file mode 100644 index 845d64996..000000000 --- a/.evergreen/ocsp/README.md +++ /dev/null @@ -1,34 +0,0 @@ -# Generating Test Certificates - -The test certificates here were generating using a fork of the server -team's -[`mkcert.py`](https://github.com/mongodb/mongo/blob/master/jstests/ssl/x509/mkcert.py) -tool. - -In order to generate a fresh set of certificates, clone this branch of -a fork of the -[`mongo` repository](https://github.com/vincentkam/mongo/tree/mkcert-ecdsa) and -run the following command from the root of the `mongo` repository: - -`python3 jstests/ssl/x509/mkcert.py --config ../drivers-evergreen-tools/.evergreen/ocsp/certs.yml` - -Passing a certificate ID as the final parameter will limit certificate -generation to that certificate and all its leaves. Note: if -regenerating ECDSA leaf certificates, ``ecsda/ca.pem`` will need to be -temporarily renamed back to ``ecdsa-ca-ocsp.pem``. - -The ECDSA certificates will be output into the folder specified by the -`global.output_path` option in the `certs.yml` file, which defaults to -`ecsda` directory contained in this directory. The RSA certificate -definitions override this value on a per certificate basis and are -output into the `rsa` directory. The default configuration also -assumes that the `mongo` repository and the `driver-evergreen-tools` -repository have the same parent directory. - -After generating the RSA root certificate, one must manually split the -`rsa/ca.pem` file, which contains both the private key and the public -certificate, into two files. `rsa/ca.crt` should contain the public -certificate, and `ras/ca.key` should contain the private certificate. - -When generating ECDSA certificates, one must normalize the ECDSA -certificate names by running `ecdsa/rename.sh`. diff --git a/.evergreen/ocsp/certs.yml b/.evergreen/ocsp/certs.yml deleted file mode 100755 index 3fa364da3..000000000 --- a/.evergreen/ocsp/certs.yml +++ /dev/null @@ -1,169 +0,0 @@ - -global: - # All subject names will have these elements automatically, - # unless `explicit_subject: true` is specified. - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/ecdsa/' # See README.md if customizing this path - Subject: - C: 'US' - ST: 'New York' - L: 'New York City' - O: 'MongoDB' - OU: 'Kernel' - -certs: - -### -# OCSP Tree -### -- name: 'ca.pem' - description: >- - Primary Root Certificate Authority - Most Certificates are issued by this CA. - Subject: {CN: 'Kernel Test CA'} - Issuer: self - include_header: false - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: - critical: true - CA: true - -- name: 'server.pem' - description: >- - Certificate with OCSP for the mongodb server. - Subject: - CN: 'localhost' - C: US - ST: NY - L: OCSP-1 - Issuer: 'ca.pem' - include_header: false - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: {CA: false} - subjectAltName: - DNS: localhost - IP: 127.0.0.1 - authorityInfoAccess: 'OCSP;URI:http://localhost:9001/power/level,OCSP;URI:http://localhost:8100/status' - subjectKeyIdentifier: hash - keyUsage: [digitalSignature, keyEncipherment] - extendedKeyUsage: [serverAuth, clientAuth] - -- name: 'server-mustStaple.pem' - description: >- - Certificate with Must Staple OCSP for the mongodb server. - Subject: - CN: 'localhost' - C: US - ST: NY - L: OCSP-1 - Issuer: 'ca.pem' - include_header: false - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: {CA: false} - subjectAltName: - DNS: localhost - IP: 127.0.0.1 - authorityInfoAccess: 'OCSP;URI:http://localhost:9001/power/level,OCSP;URI:http://localhost:8100/status' - mustStaple: true - subjectKeyIdentifier: hash - keyUsage: [digitalSignature, keyEncipherment] - extendedKeyUsage: [serverAuth, clientAuth] - -- name: 'server-singleEndpoint.pem' - description: >- - Certificate with a single OCSP endpoint for the mongodb server. - Subject: - CN: 'localhost' - C: US - ST: NY - L: OCSP-1 - Issuer: 'ca.pem' - include_header: false - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: {CA: false} - subjectAltName: - DNS: localhost - IP: 127.0.0.1 - authorityInfoAccess: 'OCSP;URI:http://localhost:8100/status' - subjectKeyIdentifier: hash - keyUsage: [digitalSignature, keyEncipherment] - extendedKeyUsage: [serverAuth, clientAuth] - -- name: 'server-mustStaple-singleEndpoint.pem' - description: >- - Certificate with Must Staple OCSP and one OCSP endpoint for the mongodb server. - Subject: - CN: 'localhost' - C: US - ST: NY - L: OCSP-1 - Issuer: 'ca.pem' - include_header: false - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: {CA: false} - subjectAltName: - DNS: localhost - IP: 127.0.0.1 - authorityInfoAccess: 'OCSP;URI:http://localhost:8100/status' - mustStaple: true - subjectKeyIdentifier: hash - keyUsage: [digitalSignature, keyEncipherment] - extendedKeyUsage: [serverAuth, clientAuth] - -- name: 'ocsp-responder.crt' - description: Certificate and key for the OCSP responder - Subject: - CN: 'localhost' - C: US - ST: NY - L: OCSP-3 - Issuer: 'ca.pem' - include_header: false - keyfile: 'ocsp-responder.key' - output_path: '../drivers-evergreen-tools/.evergreen/ocsp/rsa' - extensions: - basicConstraints: {CA: false} - keyUsage: [nonRepudiation, digitalSignature, keyEncipherment] - extendedKeyUsage: [OCSPSigning] - #noCheck: true - -### -# ECDSA tree -### - -# These are all special cases handled internally by mkcert.py -# Do NOT change the names - -- name: 'ecdsa-ca-ocsp.pem' - description: Root of ECDSA tree for OCSP testing - Issuer: self - tags: [ecdsa] - -- name: 'ecdsa-server-ocsp.pem' - description: ECDSA server certificate w/OCSP - Issuer: 'ecdsa-ca-ocsp.pem' - tags: [ecdsa, ocsp] - -- name: 'ecdsa-server-ocsp-mustStaple.pem' - description: ECDSA server certificate w/OCSP + must-staple - Issuer: 'ecdsa-ca-ocsp.pem' - tags: [ecdsa, ocsp, must-staple] - -- name: 'ecdsa-ocsp-responder.crt' - description: ECDSA certificate and key for OCSP responder - Issuer: 'ecdsa-ca-ocsp.pem' - tags: [ecdsa, ocsp, responder ] - -- name: 'ecdsa-server-ocsp-singleEndpoint.pem' - description: ECDSA server certificate w/OCSP + one OCSP endpoint - Issuer: 'ecdsa-ca-ocsp.pem' - tags: [ecdsa, ocsp, single-ocsp-endpoint] - -- name: 'ecdsa-server-ocsp-mustStaple-singleEndpoint.pem' - description: ECDSA server certificate w/OCSP + must-staple + one OCSP endpoint - Issuer: 'ecdsa-ca-ocsp.pem' - tags: [ecdsa, ocsp, must-staple, single-ocsp-endpoint] diff --git a/.evergreen/ocsp/ecdsa/ca.crt b/.evergreen/ocsp/ecdsa/ca.crt deleted file mode 100644 index 623739ecb..000000000 --- a/.evergreen/ocsp/ecdsa/ca.crt +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB9jCCAZygAwIBAgIERIhZ3jAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwMzE3MTk0NjU5WhcNNDAwMzEyMTk0NjU5WjB6MQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UE -AwwUS2VybmVsIFRlc3QgRVNDREEgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AAT1rsrbhlZEQAubaPkS23tOfSEdWNd+u7N5kV4nxKQDNxPcScnSGrb41tBEINdG -LQ/SopWZx9O8UJSrh8sqaV1AoxAwDjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMC -A0gAMEUCIDEvg1FnzNQNnLDxyOthbOqpX58A0YfLjgGb8xAvvdr4AiEAtvF2jMt6 -/o4HVXXKdohjBJbETbr7XILEvnZ4Zt7QNl8= ------END CERTIFICATE----- diff --git a/.evergreen/ocsp/ecdsa/ca.key b/.evergreen/ocsp/ecdsa/ca.key deleted file mode 100644 index 05935962b..000000000 --- a/.evergreen/ocsp/ecdsa/ca.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgMzE6ziHkSWt+sE2O -RMFZ9wqjOg88cWTuMMYrKXXL1UWhRANCAAT1rsrbhlZEQAubaPkS23tOfSEdWNd+ -u7N5kV4nxKQDNxPcScnSGrb41tBEINdGLQ/SopWZx9O8UJSrh8sqaV1A ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/ca.pem b/.evergreen/ocsp/ecdsa/ca.pem deleted file mode 100644 index b5037745c..000000000 --- a/.evergreen/ocsp/ecdsa/ca.pem +++ /dev/null @@ -1,18 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIB9jCCAZygAwIBAgIERIhZ3jAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwMzE3MTk0NjU5WhcNNDAwMzEyMTk0NjU5WjB6MQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UE -AwwUS2VybmVsIFRlc3QgRVNDREEgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC -AAT1rsrbhlZEQAubaPkS23tOfSEdWNd+u7N5kV4nxKQDNxPcScnSGrb41tBEINdG -LQ/SopWZx9O8UJSrh8sqaV1AoxAwDjAMBgNVHRMEBTADAQH/MAoGCCqGSM49BAMC -A0gAMEUCIDEvg1FnzNQNnLDxyOthbOqpX58A0YfLjgGb8xAvvdr4AiEAtvF2jMt6 -/o4HVXXKdohjBJbETbr7XILEvnZ4Zt7QNl8= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgMzE6ziHkSWt+sE2O -RMFZ9wqjOg88cWTuMMYrKXXL1UWhRANCAAT1rsrbhlZEQAubaPkS23tOfSEdWNd+ -u7N5kV4nxKQDNxPcScnSGrb41tBEINdGLQ/SopWZx9O8UJSrh8sqaV1A ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/mock-delegate-revoked.sh b/.evergreen/ocsp/ecdsa/mock-delegate-revoked.sh deleted file mode 100755 index 1e40fba5a..000000000 --- a/.evergreen/ocsp/ecdsa/mock-delegate-revoked.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ocsp-responder.crt \ - --ocsp_responder_key ocsp-responder.key \ - -p 8100 \ - -v \ - --fault revoked diff --git a/.evergreen/ocsp/ecdsa/mock-delegate-valid.sh b/.evergreen/ocsp/ecdsa/mock-delegate-valid.sh deleted file mode 100755 index 5074a7eca..000000000 --- a/.evergreen/ocsp/ecdsa/mock-delegate-valid.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ocsp-responder.crt \ - --ocsp_responder_key ocsp-responder.key \ - -p 8100 \ - -v diff --git a/.evergreen/ocsp/ecdsa/mock-revoked.sh b/.evergreen/ocsp/ecdsa/mock-revoked.sh deleted file mode 100755 index a6bf2ef02..000000000 --- a/.evergreen/ocsp/ecdsa/mock-revoked.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env sh -# Use the CA as the OCSP responder -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ca.crt \ - --ocsp_responder_key ca.key \ - -p 8100 \ - -v \ - --fault revoked - diff --git a/.evergreen/ocsp/ecdsa/mock-valid.sh b/.evergreen/ocsp/ecdsa/mock-valid.sh deleted file mode 100755 index c89ce9e95..000000000 --- a/.evergreen/ocsp/ecdsa/mock-valid.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ca.crt \ - --ocsp_responder_key ca.key \ - -p 8100 \ - -v diff --git a/.evergreen/ocsp/ecdsa/ocsp-responder.crt b/.evergreen/ocsp/ecdsa/ocsp-responder.crt deleted file mode 100644 index 4d3f3e929..000000000 --- a/.evergreen/ocsp/ecdsa/ocsp-responder.crt +++ /dev/null @@ -1,15 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICVTCCAfygAwIBAgIEfpRhITAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwMzE3MTk0NzAwWhcNNDAwMzEyMTk0NzAwWjBsMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UE -AwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAERca9Bv0PDLkCULyx -axwx8nyPqonFF88MQiZpY7wK7atBfWkpZ9B/ukq5p+xVDXxS49huEIQUWOZ5xosF -frma96N+MHwwCQYDVR0TBAIwADAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEw -HQYDVR0OBBYEFNQUc8MKrQDR4wAFZZ2o9PNLAiUHMAsGA1UdDwQEAwIF4DAnBgNV -HSUEIDAeBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMJMAoGCCqGSM49BAMC -A0cAMEQCIBQs56OofXC3Io6DjP4ccgpkX8cLHpMRb3jfZ6MxulniAiBVLoXo8K23 -YmpwoWKLFBKBdtGU+WDdD01Mb8X4iQ1gYg== ------END CERTIFICATE----- diff --git a/.evergreen/ocsp/ecdsa/ocsp-responder.key b/.evergreen/ocsp/ecdsa/ocsp-responder.key deleted file mode 100644 index 9e7eaa64e..000000000 --- a/.evergreen/ocsp/ecdsa/ocsp-responder.key +++ /dev/null @@ -1,5 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgxxFxGTsPETczP0SW -69vnqYXZIgk+qG61j6JKElHa6duhRANCAARFxr0G/Q8MuQJQvLFrHDHyfI+qicUX -zwxCJmljvArtq0F9aSln0H+6Srmn7FUNfFLj2G4QhBRY5nnGiwV+uZr3 ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/rename.sh b/.evergreen/ocsp/ecdsa/rename.sh deleted file mode 100755 index cf72559c0..000000000 --- a/.evergreen/ocsp/ecdsa/rename.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env bash -[ ! -f ecdsa-ca-ocsp.pem ] || mv ecdsa-ca-ocsp.pem ca.pem -[ ! -f ecdsa-ca-ocsp.crt ] || mv ecdsa-ca-ocsp.crt ca.crt -[ ! -f ecdsa-ca-ocsp.key ] || mv ecdsa-ca-ocsp.key ca.key -[ ! -f ecdsa-server-ocsp.pem ] || mv ecdsa-server-ocsp.pem server.pem -[ ! -f ecdsa-server-ocsp-mustStaple.pem ] || mv ecdsa-server-ocsp-mustStaple.pem server-mustStaple.pem -[ ! -f ecdsa-server-ocsp-singleEndpoint.pem ] || mv ecdsa-server-ocsp-singleEndpoint.pem server-singleEndpoint.pem -[ ! -f ecdsa-server-ocsp-mustStaple-singleEndpoint.pem ] || mv ecdsa-server-ocsp-mustStaple-singleEndpoint.pem server-mustStaple-singleEndpoint.pem -[ ! -f ecdsa-ocsp-responder.crt ] || mv ecdsa-ocsp-responder.crt ocsp-responder.crt -[ ! -f ecdsa-ocsp-responder.key ] || mv ecdsa-ocsp-responder.key ocsp-responder.key diff --git a/.evergreen/ocsp/ecdsa/server-mustStaple-singleEndpoint.pem b/.evergreen/ocsp/ecdsa/server-mustStaple-singleEndpoint.pem deleted file mode 100644 index c2d3caa3d..000000000 --- a/.evergreen/ocsp/ecdsa/server-mustStaple-singleEndpoint.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICmzCCAkGgAwIBAgIEK6+qITAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwNDE3MjIwNzM4WhcNNDAwNDEyMjIwNzM4WjBsMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UE -AwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQp0AXlVttI8EFDhm -YZZTGT0W9XZvUwk+HCVvTyRruyFI/VRW6PvLuCrMpFiXrM6kSoDQDDwcIH4jBv6u -y5mhYaOBwjCBvzAJBgNVHRMEAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA -ATAdBgNVHQ4EFgQUHnyVeKPYHhZOYzAfQW+C48W+mQowCwYDVR0PBAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA4BggrBgEFBQcBAQQsMCowKAYI -KwYBBQUHMAGGHGh0dHA6Ly9sb2NhbGhvc3Q6ODEwMC9zdGF0dXMwEQYIKwYBBQUH -ARgEBTADAgEFMAoGCCqGSM49BAMCA0gAMEUCIHiAly+9pDK3z4shFjqQZILGcvaP -/71l3WSdKAjfKd1LAiEA9CpCiaGR1a5D8qSvr518WZtqOVB+YsEk63aJs/2PtM0= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgniP9x8ixUXWk16LR -EKiL5dqh2aH/ON6EmULoaReDLTKhRANCAARCnQBeVW20jwQUOGZhllMZPRb1dm9T -CT4cJW9PJGu7IUj9VFbo+8u4KsykWJeszqRKgNAMPBwgfiMG/q7LmaFh ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/server-mustStaple.pem b/.evergreen/ocsp/ecdsa/server-mustStaple.pem deleted file mode 100644 index b539779ef..000000000 --- a/.evergreen/ocsp/ecdsa/server-mustStaple.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICyjCCAnCgAwIBAgIEA54uVTAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwMzI2MTU1NzU1WhcNNDAwMzIxMTU1NzU1WjBsMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UE -AwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJjAN/Hd2R/RRBoAu -YouPhTbS/y2DiD47YQaUu1TlnrvABcvIgkMKYfbeNIhBfu44KzF2sKsmKrG6T6rs -NdJ3pqOB8TCB7jAJBgNVHRMEAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA -ATAdBgNVHQ4EFgQUvHVMhH4zuedQN+9sQJ8LN7jvy3owCwYDVR0PBAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBnBggrBgEFBQcBAQRbMFkwLQYI -KwYBBQUHMAGGIWh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS9wb3dlci9sZXZlbDAoBggr -BgEFBQcwAYYcaHR0cDovL2xvY2FsaG9zdDo4MTAwL3N0YXR1czARBggrBgEFBQcB -GAQFMAMCAQUwCgYIKoZIzj0EAwIDSAAwRQIgDiL8zqWkCR5Rc/YoAgV81qryUMrK -BQoP7fb1M0KKarECIQDPa5q1pFu+5UZ8gn7CP4/9xDcBiG6tQYK5N0FHAZXzEg== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg1IHezsqNUk0tfGOS -E2RcM7R00ue1/E8/pBBUGSt7RW2hRANCAAQmMA38d3ZH9FEGgC5ii4+FNtL/LYOI -PjthBpS7VOWeu8AFy8iCQwph9t40iEF+7jgrMXawqyYqsbpPquw10nem ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/server-singleEndpoint.pem b/.evergreen/ocsp/ecdsa/server-singleEndpoint.pem deleted file mode 100644 index fb2cfc596..000000000 --- a/.evergreen/ocsp/ecdsa/server-singleEndpoint.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICiTCCAi6gAwIBAgIELzCNWTAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwNDE3MjIwNzQ0WhcNNDAwNDEyMjIwNzQ0WjBsMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UE -AwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESvwx4QUCP0f5Dr8N -MMfO40epXIcain4+XEVy8hcAtR0nYD0QpnFJSX7E4b5eY7A/Lr7UEKx64Qg3qYEl -FgbezaOBrzCBrDAJBgNVHRMEAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA -ATAdBgNVHQ4EFgQUfOg4eUnUTje/rTmAHnZ3XzdyStIwCwYDVR0PBAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjA4BggrBgEFBQcBAQQsMCowKAYI -KwYBBQUHMAGGHGh0dHA6Ly9sb2NhbGhvc3Q6ODEwMC9zdGF0dXMwCgYIKoZIzj0E -AwIDSQAwRgIhAKT+d/zTlhzZnOeU05Gi6hJAC0W9Fq4K2Sh04Cdys9kgAiEAyEla -DrZl0P+kGIJN49CUTHBiXN1t6nSRflNrkFiPFmI= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgqD1jXcZlgcRjdj1l -i2i0L0+hE4YmhdetvKwZ8REk8jqhRANCAARK/DHhBQI/R/kOvw0wx87jR6lchxqK -fj5cRXLyFwC1HSdgPRCmcUlJfsThvl5jsD8uvtQQrHrhCDepgSUWBt7N ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/ecdsa/server.pem b/.evergreen/ocsp/ecdsa/server.pem deleted file mode 100644 index d120e1852..000000000 --- a/.evergreen/ocsp/ecdsa/server.pem +++ /dev/null @@ -1,22 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICtzCCAl2gAwIBAgIEP6OYOTAKBggqhkjOPQQDAjB6MQswCQYDVQQGEwJVUzER -MA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAOBgNV -BAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEdMBsGA1UEAwwUS2VybmVsIFRl -c3QgRVNDREEgQ0EwHhcNMjAwMzI2MTU1ODA2WhcNNDAwMzIxMTU1ODA2WjBsMQsw -CQYDVQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3Jr -IENpdHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEPMA0GA1UE -AwwGc2VydmVyMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEK4FR+soHPeGhF5c+ -bPBX9/+gm+RimTqlXQAkHQHopLETOVexyt0eAVJe/euPAdKx3JvQ2fx2YOaBZK2U -D98UoKOB3jCB2zAJBgNVHRMEAjAAMBoGA1UdEQQTMBGCCWxvY2FsaG9zdIcEfwAA -ATAdBgNVHQ4EFgQU2JCna5G/Yd+Hd9hkAoWXxSjQ7acwCwYDVR0PBAQDAgWgMB0G -A1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjBnBggrBgEFBQcBAQRbMFkwLQYI -KwYBBQUHMAGGIWh0dHA6Ly9sb2NhbGhvc3Q6OTAwMS9wb3dlci9sZXZlbDAoBggr -BgEFBQcwAYYcaHR0cDovL2xvY2FsaG9zdDo4MTAwL3N0YXR1czAKBggqhkjOPQQD -AgNIADBFAiEA3F6MCGLS+gBDMl3+GTAVxYYuxLbhW92CQLwh/FbDozYCIHQzJ2G/ -ht6PGW9nKueW0yDfppBVlxBmlKody9ugpcpO ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgp33qfUjflX1C7ROa -e5F/RNyIhLE9hnxg4eFQQTqdxUqhRANCAAQrgVH6ygc94aEXlz5s8Ff3/6Cb5GKZ -OqVdACQdAeiksRM5V7HK3R4BUl79648B0rHcm9DZ/HZg5oFkrZQP3xSg ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/mock-ocsp-responder-requirements.txt b/.evergreen/ocsp/mock-ocsp-responder-requirements.txt deleted file mode 100644 index 0344252b6..000000000 --- a/.evergreen/ocsp/mock-ocsp-responder-requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -asn1crypto==1.3.0 -flask==1.1.1 -oscrypto==1.2.0 diff --git a/.evergreen/ocsp/mock_ocsp_responder.py b/.evergreen/ocsp/mock_ocsp_responder.py deleted file mode 100755 index 6274e97ac..000000000 --- a/.evergreen/ocsp/mock_ocsp_responder.py +++ /dev/null @@ -1,614 +0,0 @@ -# -# This file has been modified in 2019 by MongoDB Inc. -# - -# OCSPBuilder is derived from https://github.com/wbond/ocspbuilder -# OCSPResponder is derived from https://github.com/threema-ch/ocspresponder - -# Copyright (c) 2015-2018 Will Bond - -# Permission is hereby granted, free of charge, to any person obtaining a copy of -# this software and associated documentation files (the "Software"), to deal in -# the Software without restriction, including without limitation the rights to -# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -# of the Software, and to permit persons to whom the Software is furnished to do -# so, subject to the following conditions: - -# The above copyright notice and this permission notice shall be included in all -# copies or substantial portions of the Software. - -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. - -# Copyright 2016 Threema GmbH - -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at - -# http://www.apache.org/licenses/LICENSE-2.0 - -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from __future__ import unicode_literals, division, absolute_import, print_function - -import logging -import base64 -import inspect -import re -import enum -import sys -import textwrap -from datetime import datetime, timezone, timedelta -from typing import Callable, Tuple, Optional - -from asn1crypto import x509, keys, core, ocsp -from asn1crypto.ocsp import OCSPRequest, OCSPResponse -from oscrypto import asymmetric -from flask import Flask, request, Response - -__version__ = '0.10.2' -__version_info__ = (0, 10, 2) - -logger = logging.getLogger(__name__) - -if sys.version_info < (3,): - byte_cls = str -else: - byte_cls = bytes - -def _pretty_message(string, *params): - """ - Takes a multi-line string and does the following: - - dedents - - converts newlines with text before and after into a single line - - strips leading and trailing whitespace - :param string: - The string to format - :param *params: - Params to interpolate into the string - :return: - The formatted string - """ - - output = textwrap.dedent(string) - - # Unwrap lines, taking into account bulleted lists, ordered lists and - # underlines consisting of = signs - if output.find('\n') != -1: - output = re.sub('(?<=\\S)\n(?=[^ \n\t\\d\\*\\-=])', ' ', output) - - if params: - output = output % params - - output = output.strip() - - return output - - -def _type_name(value): - """ - :param value: - A value to get the object name of - :return: - A unicode string of the object name - """ - - if inspect.isclass(value): - cls = value - else: - cls = value.__class__ - if cls.__module__ in set(['builtins', '__builtin__']): - return cls.__name__ - return '%s.%s' % (cls.__module__, cls.__name__) - -def _writer(func): - """ - Decorator for a custom writer, but a default reader - """ - - name = func.__name__ - return property(fget=lambda self: getattr(self, '_%s' % name), fset=func) - - -class OCSPResponseBuilder(object): - - _response_status = None - _certificate = None - _certificate_status = None - _revocation_date = None - _certificate_issuer = None - _hash_algo = None - _key_hash_algo = None - _nonce = None - _this_update = None - _next_update = None - _response_data_extensions = None - _single_response_extensions = None - - def __init__(self, response_status, certificate_status_list=[], revocation_date=None): - """ - Unless changed, responses will use SHA-256 for the signature, - and will be valid from the moment created for one week. - :param response_status: - A unicode string of OCSP response type: - - "successful" - when the response includes information about the certificate - - "malformed_request" - when the request could not be understood - - "internal_error" - when an internal error occured with the OCSP responder - - "try_later" - when the OCSP responder is temporarily unavailable - - "sign_required" - when the OCSP request must be signed - - "unauthorized" - when the responder is not the correct responder for the certificate - :param certificate_list: - A list of tuples with certificate serial number and certificate status objects. - certificate_status: - A unicode string of the status of the certificate. Only required if - the response_status is "successful". - - "good" - when the certificate is in good standing - - "revoked" - when the certificate is revoked without a reason code - - "key_compromise" - when a private key is compromised - - "ca_compromise" - when the CA issuing the certificate is compromised - - "affiliation_changed" - when the certificate subject name changed - - "superseded" - when the certificate was replaced with a new one - - "cessation_of_operation" - when the certificate is no longer needed - - "certificate_hold" - when the certificate is temporarily invalid - - "remove_from_crl" - only delta CRLs - when temporary hold is removed - - "privilege_withdrawn" - one of the usages for a certificate was removed - - "unknown" - the responder doesn't know about the certificate being requested - :param revocation_date: - A datetime.datetime object of when the certificate was revoked, if - the response_status is "successful" and the certificate status is - not "good" or "unknown". - """ - self._response_status = response_status - self._certificate_status_list = certificate_status_list - self._revocation_date = revocation_date - - self._key_hash_algo = 'sha1' - self._hash_algo = 'sha256' - self._response_data_extensions = {} - self._single_response_extensions = {} - - @_writer - def nonce(self, value): - """ - The nonce that was provided during the request. - """ - - if not isinstance(value, byte_cls): - raise TypeError(_pretty_message( - ''' - nonce must be a byte string, not %s - ''', - _type_name(value) - )) - - self._nonce = value - - @_writer - def certificate_issuer(self, value): - """ - An asn1crypto.x509.Certificate object of the issuer of the certificate. - This should only be set if the OCSP responder is not the issuer of - the certificate, but instead a special certificate only for OCSP - responses. - """ - - if value is not None: - is_oscrypto = isinstance(value, asymmetric.Certificate) - if not is_oscrypto and not isinstance(value, x509.Certificate): - raise TypeError(_pretty_message( - ''' - certificate_issuer must be an instance of - asn1crypto.x509.Certificate or - oscrypto.asymmetric.Certificate, not %s - ''', - _type_name(value) - )) - - if is_oscrypto: - value = value.asn1 - - self._certificate_issuer = value - - @_writer - def next_update(self, value): - """ - A datetime.datetime object of when the response may next change. This - should only be set if responses are cached. If responses are generated - fresh on every request, this should not be set. - """ - - if not isinstance(value, datetime): - raise TypeError(_pretty_message( - ''' - next_update must be an instance of datetime.datetime, not %s - ''', - _type_name(value) - )) - - self._next_update = value - - def build(self, responder_private_key=None, responder_certificate=None): - """ - Validates the request information, constructs the ASN.1 structure and - signs it. - The responder_private_key and responder_certificate parameters are onlystr - required if the response_status is "successful". - :param responder_private_key: - An asn1crypto.keys.PrivateKeyInfo or oscrypto.asymmetric.PrivateKey - object for the private key to sign the response with - :param responder_certificate: - An asn1crypto.x509.Certificate or oscrypto.asymmetric.Certificate - object of the certificate associated with the private key - :return: - An asn1crypto.ocsp.OCSPResponse object of the response - """ - if self._response_status != 'successful': - return ocsp.OCSPResponse({ - 'response_status': self._response_status - }) - - is_oscrypto = isinstance(responder_private_key, asymmetric.PrivateKey) - if not isinstance(responder_private_key, keys.PrivateKeyInfo) and not is_oscrypto: - raise TypeError(_pretty_message( - ''' - responder_private_key must be an instance ofthe c - asn1crypto.keys.PrivateKeyInfo or - oscrypto.asymmetric.PrivateKey, not %s - ''', - _type_name(responder_private_key) - )) - - cert_is_oscrypto = isinstance(responder_certificate, asymmetric.Certificate) - if not isinstance(responder_certificate, x509.Certificate) and not cert_is_oscrypto: - raise TypeError(_pretty_message( - ''' - responder_certificate must be an instance of - asn1crypto.x509.Certificate or - oscrypto.asymmetric.Certificate, not %s - ''', - _type_name(responder_certificate) - )) - - if cert_is_oscrypto: - responder_certificate = responder_certificate.asn1 - - if self._certificate_status_list is None: - raise ValueError(_pretty_message( - ''' - certificate_status_list must be set if the response_status is - "successful" - ''' - )) - - def _make_extension(name, value): - return { - 'extn_id': name, - 'critical': False, - 'extn_value': value - } - - responses = [] - for serial, status in self._certificate_status_list: - response_data_extensions = [] - single_response_extensions = [] - for name, value in self._response_data_extensions.items(): - response_data_extensions.append(_make_extension(name, value)) - if self._nonce: - response_data_extensions.append( - _make_extension('nonce', self._nonce) - ) - - if not response_data_extensions: - response_data_extensions = None - - for name, value in self._single_response_extensions.items(): - single_response_extensions.append(_make_extension(name, value)) - - if self._certificate_issuer: - single_response_extensions.append( - _make_extension( - 'certificate_issuer', - [ - x509.GeneralName( - name='directory_name', - value=self._certificate_issuer.subject - ) - ] - ) - ) - - if not single_response_extensions: - single_response_extensions = None - - responder_key_hash = getattr(responder_certificate.public_key, self._key_hash_algo) - - if status == 'good': - cert_status = ocsp.CertStatus( - name='good', - value=core.Null() - ) - elif status == 'unknown': - cert_status = ocsp.CertStatus( - name='unknown', - value=core.Null() - ) - else: - reason = status if status != 'revoked' else 'unspecified' - cert_status = ocsp.CertStatus( - name='revoked', - value={ - 'revocation_time': self._revocation_date, - 'revocation_reason': reason, - } - ) - - issuer = self._certificate_issuer if self._certificate_issuer else responder_certificate - - produced_at = datetime.now(timezone.utc).replace(microsecond=0) - - if self._this_update is None: - self._this_update = produced_at - - if self._next_update is None: - self._next_update = (self._this_update + timedelta(days=7)).replace(microsecond=0) - - response = { - 'cert_id': { - 'hash_algorithm': { - 'algorithm': self._key_hash_algo - }, - 'issuer_name_hash': getattr(issuer.subject, self._key_hash_algo), - 'issuer_key_hash': getattr(issuer.public_key, self._key_hash_algo), - 'serial_number': serial, - }, - 'cert_status': cert_status, - 'this_update': self._this_update, - 'next_update': self._next_update, - 'single_extensions': single_response_extensions - } - responses.append(response) - - response_data = ocsp.ResponseData({ - 'responder_id': ocsp.ResponderId(name='by_key', value=responder_key_hash), - 'produced_at': produced_at, - 'responses': responses, - 'response_extensions': response_data_extensions - }) - - signature_algo = responder_private_key.algorithm - if signature_algo == 'ec': - signature_algo = 'ecdsa' - - signature_algorithm_id = '%s_%s' % (self._hash_algo, signature_algo) - - if responder_private_key.algorithm == 'rsa': - sign_func = asymmetric.rsa_pkcs1v15_sign - elif responder_private_key.algorithm == 'dsa': - sign_func = asymmetric.dsa_sign - elif responder_private_key.algorithm == 'ec': - sign_func = asymmetric.ecdsa_sign - - if not is_oscrypto: - responder_private_key = asymmetric.load_private_key(responder_private_key) - signature_bytes = sign_func(responder_private_key, response_data.dump(), self._hash_algo) - - certs = None - if self._certificate_issuer and getattr(self._certificate_issuer.public_key, self._key_hash_algo) != responder_key_hash: - certs = [responder_certificate] - - return ocsp.OCSPResponse({ - 'response_status': self._response_status, - 'response_bytes': { - 'response_type': 'basic_ocsp_response', - 'response': { - 'tbs_response_data': response_data, - 'signature_algorithm': {'algorithm': signature_algorithm_id}, - 'signature': signature_bytes, - 'certs': certs, - } - } - }) - -# Enums - -class ResponseStatus(enum.Enum): - successful = 'successful' - malformed_request = 'malformed_request' - internal_error = 'internal_error' - try_later = 'try_later' - sign_required = 'sign_required' - unauthorized = 'unauthorized' - - -class CertificateStatus(enum.Enum): - good = 'good' - revoked = 'revoked' - key_compromise = 'key_compromise' - ca_compromise = 'ca_compromise' - affiliation_changed = 'affiliation_changed' - superseded = 'superseded' - cessation_of_operation = 'cessation_of_operation' - certificate_hold = 'certificate_hold' - remove_from_crl = 'remove_from_crl' - privilege_withdrawn = 'privilege_withdrawn' - unknown = 'unknown' - - -# API endpoints -FAULT_REVOKED = "revoked" -FAULT_UNKNOWN = "unknown" - -app = Flask(__name__) -class OCSPResponder: - - def __init__(self, issuer_cert: str, responder_cert: str, responder_key: str, - fault: str, next_update_seconds: int): - """ - Create a new OCSPResponder instance. - - :param issuer_cert: Path to the issuer certificate. - :param responder_cert: Path to the certificate of the OCSP responder - with the `OCSP Signing` extension. - :param responder_key: Path to the private key belonging to the - responder cert. - :param validate_func: A function that - given a certificate serial - - will return the appropriate :class:`CertificateStatus` and - - depending on the status - a revocation datetime. - :param cert_retrieve_func: A function that - given a certificate serial - - will return the corresponding certificate as a string. - :param next_update_seconds: The ``nextUpdate`` value that will be written - into the response. Default: 9 hours. - - """ - # Certs and keys - self._issuer_cert = asymmetric.load_certificate(issuer_cert) - self._responder_cert = asymmetric.load_certificate(responder_cert) - self._responder_key = asymmetric.load_private_key(responder_key) - - # Next update - self._next_update_seconds = next_update_seconds - - self._fault = fault - - def _fail(self, status: ResponseStatus) -> OCSPResponse: - builder = OCSPResponseBuilder(response_status=status.value) - return builder.build() - - def parse_ocsp_request(self, request_der: bytes) -> OCSPRequest: - """ - Parse the request bytes, return an ``OCSPRequest`` instance. - """ - return OCSPRequest.load(request_der) - - def validate(self): - time = datetime(2018, 1, 1, 1, 00, 00, 00, timezone.utc) - if self._fault == FAULT_REVOKED: - return (CertificateStatus.revoked, time) - elif self._fault == FAULT_UNKNOWN: - return (CertificateStatus.unknown, None) - elif self._fault != None: - raise NotImplemented('Fault type could not be found') - return (CertificateStatus.good, time) - - def _build_ocsp_response(self, ocsp_request: OCSPRequest) -> OCSPResponse: - """ - Create and return an OCSP response from an OCSP request. - """ - # Get the certificate serial - tbs_request = ocsp_request['tbs_request'] - request_list = tbs_request['request_list'] - if len(request_list) < 1: - logger.warning('Received OCSP request with no requests') - raise NotImplemented('Empty requests not supported') - - single_request = request_list[0] # TODO: Support more than one request - req_cert = single_request['req_cert'] - serial = req_cert['serial_number'].native - - # Check certificate status - try: - certificate_status, revocation_date = self.validate() - except Exception as e: - logger.exception('Could not determine certificate status: %s', e) - return self._fail(ResponseStatus.internal_error) - - certificate_status_list = [(serial, certificate_status.value)] - - # Build the response - builder = OCSPResponseBuilder(**{ - 'response_status': ResponseStatus.successful.value, - 'certificate_status_list': certificate_status_list, - 'revocation_date': revocation_date, - }) - - # Parse extensions - for extension in tbs_request['request_extensions']: - extn_id = extension['extn_id'].native - critical = extension['critical'].native - value = extension['extn_value'].parsed - - # This variable tracks whether any unknown extensions were encountered - unknown = False - - # Handle nonce extension - if extn_id == 'nonce': - builder.nonce = value.native - - # That's all we know - else: - unknown = True - - # If an unknown critical extension is encountered (which should not - # usually happen, according to RFC 6960 4.1.2), we should throw our - # hands up in despair and run. - if unknown is True and critical is True: - logger.warning('Could not parse unknown critical extension: %r', - dict(extension.native)) - return self._fail(ResponseStatus.internal_error) - - # If it's an unknown non-critical extension, we can safely ignore it. - elif unknown is True: - logger.info('Ignored unknown non-critical extension: %r', dict(extension.native)) - - # Set certificate issuer - builder.certificate_issuer = self._issuer_cert - - # Set next update date - now = datetime.now(timezone.utc) - builder.next_update = (now + timedelta(seconds=self._next_update_seconds)).replace(microsecond=0) - - return builder.build(self._responder_key, self._responder_cert) - - def build_http_response(self, request_der: bytes) -> Response: - global app - response_der = self._build_ocsp_response(request_der).dump() - resp = app.make_response((response_der, 200)) - resp.headers['content_type'] = 'application/ocsp-response' - return resp - - -responder = None - -def init_responder(issuer_cert: str, responder_cert: str, responder_key: str, fault: str, next_update_seconds: int): - global responder - responder = OCSPResponder(issuer_cert=issuer_cert, responder_cert=responder_cert, responder_key=responder_key, fault=fault, next_update_seconds=next_update_seconds) - -def init(port=8080, debug=False): - logger.info('Launching %sserver on port %d', 'debug' if debug else '', port) - app.run(port=port, debug=debug) - -@app.route('/', methods=['GET']) -def _handle_root(): - return 'ocsp-responder' - -@app.route('/status/', defaults={'u_path': ''}, methods=['GET']) -@app.route('/status/', methods=['GET']) -def _handle_get(u_path): - global responder - """ - An OCSP GET request contains the DER-in-base64 encoded OCSP request in the - HTTP request URL. - """ - der = base64.b64decode(u_path) - ocsp_request = responder.parse_ocsp_request(der) - return responder.build_http_response(ocsp_request) - -@app.route('/status', methods=['POST']) -def _handle_post(): - global responder - """ - An OCSP POST request contains the DER encoded OCSP request in the HTTP - request body. - """ - ocsp_request = responder.parse_ocsp_request(request.data) - return responder.build_http_response(ocsp_request) diff --git a/.evergreen/ocsp/ocsp_mock.py b/.evergreen/ocsp/ocsp_mock.py deleted file mode 100755 index 04963b385..000000000 --- a/.evergreen/ocsp/ocsp_mock.py +++ /dev/null @@ -1,48 +0,0 @@ -#! /usr/bin/env python3 -""" -Python script to interface as a mock OCSP responder. -""" - -import argparse -import logging -import sys -import os - -sys.path.append(os.path.join(os.getcwd() ,'src', 'third_party', 'mock_ocsp_responder')) - -import mock_ocsp_responder - -def main(): - """Main entry point""" - parser = argparse.ArgumentParser(description="MongoDB Mock OCSP Responder.") - - parser.add_argument('-p', '--port', type=int, default=8080, help="Port to listen on") - - parser.add_argument('--ca_file', type=str, required=True, help="CA file for OCSP responder") - - parser.add_argument('-v', '--verbose', action='count', help="Enable verbose tracing") - - parser.add_argument('--ocsp_responder_cert', type=str, required=True, help="OCSP Responder Certificate") - - parser.add_argument('--ocsp_responder_key', type=str, required=True, help="OCSP Responder Keyfile") - - parser.add_argument('--fault', choices=[mock_ocsp_responder.FAULT_REVOKED, mock_ocsp_responder.FAULT_UNKNOWN, None], default=None, type=str, help="Specify a specific fault to test") - - parser.add_argument('--next_update_seconds', type=int, default=32400, help="Specify how long the OCSP response should be valid for") - - args = parser.parse_args() - if args.verbose: - logging.basicConfig(level=logging.DEBUG) - - print('Initializing OCSP Responder') - mock_ocsp_responder.init_responder(issuer_cert=args.ca_file, responder_cert=args.ocsp_responder_cert, responder_key=args.ocsp_responder_key, fault=args.fault, next_update_seconds=args.next_update_seconds) - - if args.verbose: - mock_ocsp_responder.init(args.port, debug=True) - else: - mock_ocsp_responder.init(args.port) - - print('Mock OCSP Responder is running on port %s' % (str(args.port))) - -if __name__ == '__main__': - main() diff --git a/.evergreen/ocsp/rsa/ca.crt b/.evergreen/ocsp/rsa/ca.crt deleted file mode 100644 index ee6dc5a65..000000000 --- a/.evergreen/ocsp/rsa/ca.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDeTCCAmGgAwIBAgIEZLtwgzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwMjA2MjAxMzExWhcNNDAwMjA4MjAxMzExWjB0MQswCQYD -VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp -dHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwO -S2VybmVsIFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0 -D1mnIrh7RRrCUEocNYLMZ2azo6c6NUTqSAMQyDDvRUsezil2NCqKo0ptMRtmb8Ws -yuaRUkjFhh9M69kiuj89GKRALXxExHjWX7e8iS1NTGL+Uakc1J23Z5FvlUyVLucC -fcAZ6MvcC7n6qpzUxkqz1u/27Ze9nv2mleLYBVWbGpjSHAUDuZzMCBs5Q/QrUwL7 -4cIxNsS0iHpYI3aee67cmFoK4guN9LBOtviyXUTP22kJLXe41HDjdWh01+FxcuwH -rGmeGQwiSlw48wkdoC0M51SwpHEq+K91BqGsTboC5mshqKA88OPf5JK9ied/OsNX -+K6p5v3RVHn89VaWiTorAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggEBAAd1jj1GECUEJMH00IX3VFgb2RpJ4Qi8TKAZgMMHdE7Cyv4M -p4w/zvQC1F6i54n+TWucq3I+c33lEj63ybFdJO5HOWoGzC/f5qO7z0gYdP2Ltdxg -My2uVZNQS+B8hF9MhGUeFnOpzAbKW2If3KN1fn/m2NDYGEK/Z2t7ZkpOcpEW5Lib -vX+BBG/s4DeyhRXy+grs0ASU/z8VOhZYSJpgdbvXsY4RXXloTDcWIlNqra5K6+3T -nVEkBDm0Qw97Y6FsqBVxk4kgWC6xNxQ4Sp+Sg4wthMQ70iFGlMin0kYRo7kAIUF9 -M+v2vMwTFWkcl0BT5LobE39kWVbQKEVPH7nkItE= ------END CERTIFICATE----- diff --git a/.evergreen/ocsp/rsa/ca.key b/.evergreen/ocsp/rsa/ca.key deleted file mode 100644 index 9d10cb2db..000000000 --- a/.evergreen/ocsp/rsa/ca.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0D1mnIrh7RRrC -UEocNYLMZ2azo6c6NUTqSAMQyDDvRUsezil2NCqKo0ptMRtmb8WsyuaRUkjFhh9M -69kiuj89GKRALXxExHjWX7e8iS1NTGL+Uakc1J23Z5FvlUyVLucCfcAZ6MvcC7n6 -qpzUxkqz1u/27Ze9nv2mleLYBVWbGpjSHAUDuZzMCBs5Q/QrUwL74cIxNsS0iHpY -I3aee67cmFoK4guN9LBOtviyXUTP22kJLXe41HDjdWh01+FxcuwHrGmeGQwiSlw4 -8wkdoC0M51SwpHEq+K91BqGsTboC5mshqKA88OPf5JK9ied/OsNX+K6p5v3RVHn8 -9VaWiTorAgMBAAECggEBAJ7umazMGdg80/TGF9Q0a2JutplDj5zyXgUJUSNkAMWB -/V+Qi8pZG1/J6CzfVpche3McmU2WOsOWslQcLUnY6W7NLFW1kGXGof5e+HgDASik -jxB6FfJrvVagpR+/wZxAjQmG46Q69o4hD6SxKcMpz9BTnPXxG6n1B2EeFd+lPb2r -zf/C4uXBczWn5rFXkj0DZGq81ZXewcnUNnxjQnccVCuYW+hqYxznSxqWTCD6hsvg -sGceqv0Ppp6TqMSECCIIJ+kVlbiAC2i6mnoertheFVrNUdwDb8nRn6fs8T+F0ShW -PdxIfSvAaBKqvseJqqueVpuwVcdSl+moJYlCdMb4cUECgYEA30AIHvMQq/s33ipV -62xOKXcEZ7tKaJrAbJvG4cx934wNiQ0tLwRNlonGbuTjsUaPRvagVeJND/UPIsfH -ZwoY1Uw25fZNaveoQtU8LQBAG53R5yaMiUH48JWVvKRdfG09zr6EFCM/k2loHS1W -/CiDlaIl59B8REnihyn0wvkiaIsCgYEAznlZRhlruk+n2sWklierav4M8GEK22+/ -A/UP1eUnlcHgSaFZoM0sukSrisZnj6zu/BAfFEVN5czra3ARrLClLQteFREr2BMF -9XymrjNG99QkBAall7BGpfkDW/D2DFZa4G5R6AMG+pYZHCU84U4QT5ZKyfdhTUbQ -uTYx2F31COECgYAIUm+7D56AerXjbzqSsw/a1dfxMfcdHR+tLMVmJ2RNz/+1KyuT -BBsMUIh4G8otEo9GuuzRJsVuodj1l/Lj8WlpkhS9z8elBCRekWpT1x2Mqf5oGnTE -rRPli/3v8USW3c+fBFUSFxpImXZLGCSU88Gr80ZsdMYdGY/7L+Iy3myc7wKBgQC1 -uHeqCpWV1KWXFnxU63UjJZWdussjdqZXhUf6qUS9uXT9WNTZgbrr9aRE73oWKc3s -awPvg0+cAU7xsCDeLFoz2t1jDUnZUmTcOmk4yEidtkg8gt0bNDn5ucALG3hyQ06Y -WIAeAwwRYCmZa+y5H0ubwFryhpdMvBbX66rTE16mAQKBgC5PJd9zLEzyLj/jUfZ0 -xOwXubu9GejOuCiVwKMTn73nvdi57zFBOrDxSl9yVCRhve61L5fcJixRDiwx8qtd -VGclRMxbVPKVfKpAyKjpsmZXk3IPHjXjJb3fYLXAnzRHk6v+yjVn4fy2Z93pW/cF -wBgQNqXLNTGrBzrFi469oc1s ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/ca.pem b/.evergreen/ocsp/rsa/ca.pem deleted file mode 100644 index afa468f04..000000000 --- a/.evergreen/ocsp/rsa/ca.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDeTCCAmGgAwIBAgIEZLtwgzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwMjA2MjAxMzExWhcNNDAwMjA4MjAxMzExWjB0MQswCQYD -VQQGEwJVUzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENp -dHkxEDAOBgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwO -S2VybmVsIFRlc3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0 -D1mnIrh7RRrCUEocNYLMZ2azo6c6NUTqSAMQyDDvRUsezil2NCqKo0ptMRtmb8Ws -yuaRUkjFhh9M69kiuj89GKRALXxExHjWX7e8iS1NTGL+Uakc1J23Z5FvlUyVLucC -fcAZ6MvcC7n6qpzUxkqz1u/27Ze9nv2mleLYBVWbGpjSHAUDuZzMCBs5Q/QrUwL7 -4cIxNsS0iHpYI3aee67cmFoK4guN9LBOtviyXUTP22kJLXe41HDjdWh01+FxcuwH -rGmeGQwiSlw48wkdoC0M51SwpHEq+K91BqGsTboC5mshqKA88OPf5JK9ied/OsNX -+K6p5v3RVHn89VaWiTorAgMBAAGjEzARMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI -hvcNAQELBQADggEBAAd1jj1GECUEJMH00IX3VFgb2RpJ4Qi8TKAZgMMHdE7Cyv4M -p4w/zvQC1F6i54n+TWucq3I+c33lEj63ybFdJO5HOWoGzC/f5qO7z0gYdP2Ltdxg -My2uVZNQS+B8hF9MhGUeFnOpzAbKW2If3KN1fn/m2NDYGEK/Z2t7ZkpOcpEW5Lib -vX+BBG/s4DeyhRXy+grs0ASU/z8VOhZYSJpgdbvXsY4RXXloTDcWIlNqra5K6+3T -nVEkBDm0Qw97Y6FsqBVxk4kgWC6xNxQ4Sp+Sg4wthMQ70iFGlMin0kYRo7kAIUF9 -M+v2vMwTFWkcl0BT5LobE39kWVbQKEVPH7nkItE= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0D1mnIrh7RRrC -UEocNYLMZ2azo6c6NUTqSAMQyDDvRUsezil2NCqKo0ptMRtmb8WsyuaRUkjFhh9M -69kiuj89GKRALXxExHjWX7e8iS1NTGL+Uakc1J23Z5FvlUyVLucCfcAZ6MvcC7n6 -qpzUxkqz1u/27Ze9nv2mleLYBVWbGpjSHAUDuZzMCBs5Q/QrUwL74cIxNsS0iHpY -I3aee67cmFoK4guN9LBOtviyXUTP22kJLXe41HDjdWh01+FxcuwHrGmeGQwiSlw4 -8wkdoC0M51SwpHEq+K91BqGsTboC5mshqKA88OPf5JK9ied/OsNX+K6p5v3RVHn8 -9VaWiTorAgMBAAECggEBAJ7umazMGdg80/TGF9Q0a2JutplDj5zyXgUJUSNkAMWB -/V+Qi8pZG1/J6CzfVpche3McmU2WOsOWslQcLUnY6W7NLFW1kGXGof5e+HgDASik -jxB6FfJrvVagpR+/wZxAjQmG46Q69o4hD6SxKcMpz9BTnPXxG6n1B2EeFd+lPb2r -zf/C4uXBczWn5rFXkj0DZGq81ZXewcnUNnxjQnccVCuYW+hqYxznSxqWTCD6hsvg -sGceqv0Ppp6TqMSECCIIJ+kVlbiAC2i6mnoertheFVrNUdwDb8nRn6fs8T+F0ShW -PdxIfSvAaBKqvseJqqueVpuwVcdSl+moJYlCdMb4cUECgYEA30AIHvMQq/s33ipV -62xOKXcEZ7tKaJrAbJvG4cx934wNiQ0tLwRNlonGbuTjsUaPRvagVeJND/UPIsfH -ZwoY1Uw25fZNaveoQtU8LQBAG53R5yaMiUH48JWVvKRdfG09zr6EFCM/k2loHS1W -/CiDlaIl59B8REnihyn0wvkiaIsCgYEAznlZRhlruk+n2sWklierav4M8GEK22+/ -A/UP1eUnlcHgSaFZoM0sukSrisZnj6zu/BAfFEVN5czra3ARrLClLQteFREr2BMF -9XymrjNG99QkBAall7BGpfkDW/D2DFZa4G5R6AMG+pYZHCU84U4QT5ZKyfdhTUbQ -uTYx2F31COECgYAIUm+7D56AerXjbzqSsw/a1dfxMfcdHR+tLMVmJ2RNz/+1KyuT -BBsMUIh4G8otEo9GuuzRJsVuodj1l/Lj8WlpkhS9z8elBCRekWpT1x2Mqf5oGnTE -rRPli/3v8USW3c+fBFUSFxpImXZLGCSU88Gr80ZsdMYdGY/7L+Iy3myc7wKBgQC1 -uHeqCpWV1KWXFnxU63UjJZWdussjdqZXhUf6qUS9uXT9WNTZgbrr9aRE73oWKc3s -awPvg0+cAU7xsCDeLFoz2t1jDUnZUmTcOmk4yEidtkg8gt0bNDn5ucALG3hyQ06Y -WIAeAwwRYCmZa+y5H0ubwFryhpdMvBbX66rTE16mAQKBgC5PJd9zLEzyLj/jUfZ0 -xOwXubu9GejOuCiVwKMTn73nvdi57zFBOrDxSl9yVCRhve61L5fcJixRDiwx8qtd -VGclRMxbVPKVfKpAyKjpsmZXk3IPHjXjJb3fYLXAnzRHk6v+yjVn4fy2Z93pW/cF -wBgQNqXLNTGrBzrFi469oc1s ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/mock-delegate-revoked.sh b/.evergreen/ocsp/rsa/mock-delegate-revoked.sh deleted file mode 100755 index adf026ce1..000000000 --- a/.evergreen/ocsp/rsa/mock-delegate-revoked.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ocsp_responder.crt \ - --ocsp_responder_key ocsp_responder.key \ - -p 8100 \ - -v \ - --fault revoked diff --git a/.evergreen/ocsp/rsa/mock-delegate-valid.sh b/.evergreen/ocsp/rsa/mock-delegate-valid.sh deleted file mode 100755 index 5074a7eca..000000000 --- a/.evergreen/ocsp/rsa/mock-delegate-valid.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ocsp-responder.crt \ - --ocsp_responder_key ocsp-responder.key \ - -p 8100 \ - -v diff --git a/.evergreen/ocsp/rsa/mock-revoked.sh b/.evergreen/ocsp/rsa/mock-revoked.sh deleted file mode 100755 index 4a17926b9..000000000 --- a/.evergreen/ocsp/rsa/mock-revoked.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ca.crt \ - --ocsp_responder_key ca.key \ - -p 8100 \ - -v \ - --fault revoked diff --git a/.evergreen/ocsp/rsa/mock-valid.sh b/.evergreen/ocsp/rsa/mock-valid.sh deleted file mode 100755 index c89ce9e95..000000000 --- a/.evergreen/ocsp/rsa/mock-valid.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env sh -python3 ../ocsp_mock.py \ - --ca_file ca.pem \ - --ocsp_responder_cert ca.crt \ - --ocsp_responder_key ca.key \ - -p 8100 \ - -v diff --git a/.evergreen/ocsp/rsa/ocsp-responder.crt b/.evergreen/ocsp/rsa/ocsp-responder.crt deleted file mode 100644 index 58caba358..000000000 --- a/.evergreen/ocsp/rsa/ocsp-responder.crt +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIEA0v5yzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwMjA2MjMyMjU4WhcNNDAwMjA4MjMyMjU4WjBiMRAwDgYD -VQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxEjAQBgNVBAMMCWxvY2FsaG9z -dDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQHDAZPQ1NQLTMwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDiHYXGCSOK3gxlEmNSLepoFJbv -hfYxxaqAWEceiTQdRpN97YRr/ywPm0+932EsE6/gIjqVs8IOtsiFKK1lQ9sL/9f+ -ckS5gj9AR+Cta+FLDRP5plE+aao5no0kA8qMx2HHd47XFnuxKtUztRmgmTBNYbYh -PdY1kjBSRyuXXBn1V6TRaYhk6dsK56Zvhgo6Y3YqpjpldePa4E0XpUlBhY020QXt -K3iWFauEYKcKR2JI2oVjY0tR60zf3GHkMLCe7SdbofCdwkBHcCctLSp4xYb44JGb -JX1npM1mhxR4pnp80tbEXNvXQ4S3kmd7/QFUYE4IdXVkXNhkK6PtIdDKbLa9AgMB -AAGjLzAtMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgXgMBMGA1UdJQQMMAoGCCsGAQUF -BwMJMA0GCSqGSIb3DQEBCwUAA4IBAQB5igUUQSzxzWvL+28TDYFuNnTB0hvqTnd7 -ZVyk8RVBiUkudxEmt5uFRWT6GOc7Y1H6w4igtuhhqxAeG9bUob+VQkCyc4GxaHSO -oBtl/Zu+ts+0gUUlm+Bs6wFnFsGhM0awV/vqigDADZT2jbqbHBm2lP99eq8fsi6L -kpohhbuTVWjLuViARYIOJLoBnNRpVXqwD5A8uNqwZI2OVGh1cQYNZcmfLJ1u2j5C -ycohoa+o8NGgkxEhG2QETdVodfHT2dUgzPDvO42CVa3MK7J0sovBU5DeuIDPV/hh -j+v5A8L8gMiNpkLClqt2TEiFH2GItWDNQjTgrLq9iFUgJnbwuj4F ------END CERTIFICATE----- diff --git a/.evergreen/ocsp/rsa/ocsp-responder.key b/.evergreen/ocsp/rsa/ocsp-responder.key deleted file mode 100644 index ab3001e7f..000000000 --- a/.evergreen/ocsp/rsa/ocsp-responder.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDiHYXGCSOK3gxl -EmNSLepoFJbvhfYxxaqAWEceiTQdRpN97YRr/ywPm0+932EsE6/gIjqVs8IOtsiF -KK1lQ9sL/9f+ckS5gj9AR+Cta+FLDRP5plE+aao5no0kA8qMx2HHd47XFnuxKtUz -tRmgmTBNYbYhPdY1kjBSRyuXXBn1V6TRaYhk6dsK56Zvhgo6Y3YqpjpldePa4E0X -pUlBhY020QXtK3iWFauEYKcKR2JI2oVjY0tR60zf3GHkMLCe7SdbofCdwkBHcCct -LSp4xYb44JGbJX1npM1mhxR4pnp80tbEXNvXQ4S3kmd7/QFUYE4IdXVkXNhkK6Pt -IdDKbLa9AgMBAAECggEBAMMYOe4OwI323LbwUKX9W/0Flt1/tlZneJ9Yi7R7KW4B -EQ1cPB96gafNl9X5wLvpGJzIq8ey28MaTpUl7cYr7/nAe7rdGRL+oFh0LBU1uaOp -2wxSRlMVlHw2owzqAH/LIECclbBbg8nvbRk6Lqx0wEpj/mNcGVELm4nCQohMPVGC -9/8GZ63r+tS35jry9SBG0X4R5jYKsNzgNgcjR+lgMv/2FfpuZDryk9TWIP9ApQoc -7/DpTfC6P34f/ermfo4f2GEmRJsTACphA0kkpQX/n88r35cUSGeO5M9jYICUeCFw -IK4L6KNQcTRVOknFYeVJembVrj0RYKtWT+oU84a4XPkCgYEA+k7fcXhU2K+NX8RN -7HUPbxBE/TfLTNHdLTuWCUI77j+J3LUPNQ4BUyue+pUaFxI7Huc6x1zvvD27EqJ8 -0ge5MkFNflTUdUotuU/FKg7GKOU7rfdEvthzU2MbAZrHc0SeF+9/YrpvWZ+ZMKQ5 -IBQhiloFLsVGpGFzzF/MjpFdYo8CgYEA50HQxDDmfzmvNnURRZZV0lQ203m9I4KF -DbL2x59q0DaJkUpFr3uyvghAoz4y/OD5vNIwbzHWbmDQEA06v7iFoJ6BcJFG1syc -7A7KTB3PNQK4+ASG6pC3tYJ78mWtJwK130hFpuVkS/VPhQZJ/21EcWj9V153SZpA -RUqv/L+lx/MCgYEAs7E7p3IDNyuQClgauM2wrsK3RDFxuUxPw9Eq/KqX64mhptg0 -epn7SYHfN3Uirb1gw+arw8NsN275hX8wrHbu9Kz8vNyZSTpfaNFjcbX5fBJUrab9 -qyQoZoyXLqe214FDHVvJz06X8Xcpukmq2OSaz3+giNsGw6tSPj3n09F3gPECgYBI -1NGK+FufdetYm0X1RIOC2kLqF00aAeElj1dpRyu8p3Br8ZhAzBRfBPpWbyBfw/rj -HM9kNa3y1Uqxw3jdKJ/tFf5uFVLaE1bYgU/06O55I4Jdmg9jkHBLGe0vShZeUtw0 -le5ZwaT0xy1kF7b2WtNTZF1lRrsK0ymqqPsD/teXQQKBgBTyYVxPEHKr86kEQqL5 -/OKByVpqAxA7LQ1lTLNV9lXMRawp848flv/Uc8pj43MitAIiYazfXkpeeC6gGntJ -kkRT9jraOzy51tVAIh2KXm3l6KY/gnYTO3UXrxZOZU4IA7OttP3BG7xKq/9HP+kV -5P1bAkqo+n3XNxKoSSeJteCd ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/server-mustStaple-singleEndpoint.pem b/.evergreen/ocsp/rsa/server-mustStaple-singleEndpoint.pem deleted file mode 100644 index 47112c02b..000000000 --- a/.evergreen/ocsp/rsa/server-mustStaple-singleEndpoint.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEFzCCAv+gAwIBAgIETUEXPjANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwNDIxMTkxNDA3WhcNNDAwNDIzMTkxNDA3WjBiMRAwDgYD -VQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxEjAQBgNVBAMMCWxvY2FsaG9z -dDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQHDAZPQ1NQLTEwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDEWuLtsdzYxDK//wc9VXyyQPlS -AmkRHrLrTH0OSSPBvXK0NSkHtOjh3gX4jzTN8jTpEVkbfYt1EInZnucWOcX7mRRP -LRp0Fcq7j1pCPJ15uNSZDqDnfEA8kiY2Qg9n9oAIR2yk3FFj/8raBB13EnzOHeq4 -27BXH7oOgOgvd8PyuOB1OmNKjCLf5laaRbB+/lyrGfPFwmNcgH2lxtkfeBhTM5kS -vDkbAFIX6KqeWtvaV+WRPcyooa0FvNXTfCiS26qtw4rMZnWNODG13pgJCPckDZt7 -kX9qM+cS4L4oj6Hm3NrWkTpJzOFOQwZMily0X6ee1IH9m0yaLS7vq3pKlr67AgMB -AAGjgcIwgb8wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB -BQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBQX4EjmQUUFCdz2ZKGKMHEPkkGHCDA4 -BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6Ly9sb2NhbGhvc3Q6ODEw -MC9zdGF0dXMwEQYIKwYBBQUHARgEBTADAgEFMBoGA1UdEQQTMBGCCWxvY2FsaG9z -dIcEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAkZd/uV401ejVjqMaQ5ogkdo97Isz -Rjrx6dDY1Ll+5LzViqdRlXiAAc/bUq8NhYQkbjUC7b931meksIRRdtJUZx9zLt43 -npjjGKDdWEilLKKwT1IvKaAb2A7hmrT4WkwDtHZODvvpE+wvmEQ2LwthHDs+FwqN -2YDTuxdhO8mMePDXfK0Ch4WJQaJV/PT0sI34sYoeF7KC0TACWKwG08+qI9vawujq -qWw5fRwNTqxAj9X66wp6RdE6bJ3mWOrPmUppaDww3yRGVxdsWKCC8WoH3etNl8Km -iwDcp+WF+DmoOt2VAcvzoQsvsoUGdaMHYQ1MTJb5YsURr3BuGmcEUQI/yA== ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDEWuLtsdzYxDK/ -/wc9VXyyQPlSAmkRHrLrTH0OSSPBvXK0NSkHtOjh3gX4jzTN8jTpEVkbfYt1EInZ -nucWOcX7mRRPLRp0Fcq7j1pCPJ15uNSZDqDnfEA8kiY2Qg9n9oAIR2yk3FFj/8ra -BB13EnzOHeq427BXH7oOgOgvd8PyuOB1OmNKjCLf5laaRbB+/lyrGfPFwmNcgH2l -xtkfeBhTM5kSvDkbAFIX6KqeWtvaV+WRPcyooa0FvNXTfCiS26qtw4rMZnWNODG1 -3pgJCPckDZt7kX9qM+cS4L4oj6Hm3NrWkTpJzOFOQwZMily0X6ee1IH9m0yaLS7v -q3pKlr67AgMBAAECggEBAJqjLUafJdt9IK6+TVhLZAoKS4//n/lAoQ3YTkCa71Mc -PSKZHzgXjLSdIzyuo5px3qOS6wdQZy0JmlbN4xZI55gO5cS5M7UqmGAANMgnbqm3 -G49yytujqf9J5lgizHlG02wxu+lWLa9AeuQaC46D+9BkFUACnCzxKplTgggoHSSg -fTm/AKPRg/ZxejoorqveHK3IGjwVxk/2b6aqcCsr0GCuR5ons7hCQ66clvAqR/AH -ejz77lM/Nn6jq29Dgq/KhX22uabjML1yHFxZW0gF58chpJWTP8Rn3FEDw2mgMdao -C5C9Im9WWHquy05GQZRP/V5bhPuAgg5E4X+nCyn4eTECgYEA7eXTp+zLsGYe6l4a -MvXohDKMCouDF8w40hyIvJ9lF5ikQEhnJRQLPzbM7qx1AeeQwZevtyNBchX0nVwJ -VRd9c5qsSFkar489vBvhjEJ4B57B7KNoE5BHaR0+tfzWsWwK6BxHl9PmeGSn59i/ -7UwBhIzaC6dyJpTowCu/Scv8LhMCgYEA00vR/qeC0L7YPSG+VjHwerFhzUCTfnbd -wFpJM+N6PMRZA4GRWQLLxGmzPohjSfzwWMgUCXjopWiWOnxTEUlvTQgCWLAceMlk -rbTnHBtlXPPpSHvlmgVEG0+U/CpqONY7upEYrbt1xPMxNponS/7Yl0BXB2Qx8Je4 -pXs2H7wTIbkCgYEAgFOxUKwTVBxCIPqR91tfCbCaijWniXbIT87Ek7sHtSrJr0Nf -IEknp/nPog+1LknTdBp21rtV2kytnxS+lAAP1ARjWsN1+a2zB32itR5F0RZ6VUPw -KF1zp+f2pAS3aw109LAMjoHnmJnzWMU7Aq41Q2MXW6H/mYBJ7R+sGArJBbECgYBY -Y1Qx+bLATcU5NV9gwT0+pesqqEPK2ECFEX+jxBnDR8OQsuexW3kP7cN8eiNGtRd5 -nCC9oaV4ZBrL1mwNRDHaAGqy3ODcKisCezVeTZuGWcYRezqdxmwqHI1POxL6Oav8 -rGutaUinna/Njoi3wqCqDNEbF2/InD8ygisu9UbviQKBgQCS4Mxw+uOl5WvXjrze -z6B8L69MkT4fidgGkqimfpjk+N5Yt49otfvlvFPjkrVqR3lMqrqjV2r0W4fTeoSo -SDE3vZFZC9mi6ptUbgrW+aYqLHYQGYsJQXmj48Nkm/9uhkN1YEE9o04uSau1yVg+ -fDqxV7pLZwfnUbvGnYGjBShkMQ== ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/server-mustStaple.pem b/.evergreen/ocsp/rsa/server-mustStaple.pem deleted file mode 100644 index 5c80602e4..000000000 --- a/.evergreen/ocsp/rsa/server-mustStaple.pem +++ /dev/null @@ -1,53 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIERjCCAy6gAwIBAgIEJ++lZzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwMzE5MTU1NjIyWhcNNDAwMzIxMTU1NjIyWjBiMRAwDgYD -VQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxEjAQBgNVBAMMCWxvY2FsaG9z -dDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQHDAZPQ1NQLTEwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDykV3fTFJgaqjfAgbAC7TGPk9V -VVsRYRgLF8Zjh9GDRU/TQ6pGZG7qo64D11oQurW0WT2Zv/lqhXW4mWNFv8+qoS5L -9z2Dtmxr8CZbb6YftA0e22KPUuDCQ5nYhOY21A6SYFwqEZ6ZsrZAMkgfhx+TY1kZ -0jZM/jgkvRtpG9I8BbddHyF8eFATCJ41DnLOzjfNukd5zKSIdVxY6r+ZBOr29kii -dcNHkCAck7+WXl9/KSqH7jF5asU0S3x/68G2R/qdKAxki9b2fe70N3XGZE0P2WHi -lq2aJeE0eqjAv+hBGiEb4iJl0s8iheardrHFeL4EMbiiVfVdVCHKkp58wjB9AgMB -AAGjgfEwge4wCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB -BQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTOLiS9HKGWpiVKx81nNuRlK+HAITBn -BggrBgEFBQcBAQRbMFkwLQYIKwYBBQUHMAGGIWh0dHA6Ly9sb2NhbGhvc3Q6OTAw -MS9wb3dlci9sZXZlbDAoBggrBgEFBQcwAYYcaHR0cDovL2xvY2FsaG9zdDo4MTAw -L3N0YXR1czARBggrBgEFBQcBGAQFMAMCAQUwGgYDVR0RBBMwEYIJbG9jYWxob3N0 -hwR/AAABMA0GCSqGSIb3DQEBCwUAA4IBAQCg3NfTO8eCdhtmdDVF5WwP4/lXMYJY -5wn7PhYKMyUQI3rjUpQRIQwCVerHAJAiiflpgefxB8PD5spHPFq6RqAvH9SKpP5x -nyhiRdo51BmijCIl7VNdqyM5ZgDAN2fm2m56mDxpo9xqeTWg83YK8YY1xvBHl3jl -vQC+bBJzhaTp6SYXMc/70qIPcln0IElbuLN8vL4aG6xULkivtjiv7qBSZrNrBMSf -QJan9En4wcNGFt5ozrgJthZHTTX9pXOGVZe4LXbPCQSrBxZiBD9bITUyhtbeYhYR -4yfXjr7IeuoX+0g6+EEtxqrbWfIkJ3D7UaxAorZEsCt18GC7fap9/fzv ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDykV3fTFJgaqjf -AgbAC7TGPk9VVVsRYRgLF8Zjh9GDRU/TQ6pGZG7qo64D11oQurW0WT2Zv/lqhXW4 -mWNFv8+qoS5L9z2Dtmxr8CZbb6YftA0e22KPUuDCQ5nYhOY21A6SYFwqEZ6ZsrZA -Mkgfhx+TY1kZ0jZM/jgkvRtpG9I8BbddHyF8eFATCJ41DnLOzjfNukd5zKSIdVxY -6r+ZBOr29kiidcNHkCAck7+WXl9/KSqH7jF5asU0S3x/68G2R/qdKAxki9b2fe70 -N3XGZE0P2WHilq2aJeE0eqjAv+hBGiEb4iJl0s8iheardrHFeL4EMbiiVfVdVCHK -kp58wjB9AgMBAAECggEATA91Bf3insUTKspx32pMRxVmvvVC1xJA/cl4teDyu1zS -iQZgsC3x8bVdbWrrnO9O5rxM6pcd2F786OOAE3Dv5ysfX0apjVF4cegdvvIlfy9w -JcrY/uQYAhI8fX4+ydZ4s0Fv5OkdeEhniX26y9gM+KRgXg5iZIYaiLqbi7vjkloE -NBIDWGj8PCNKUVc2PSbZFVMMTc+7qZeUR0WRKr9CsaXBiEkWKfuw4MH1YUL0HJOs -uLd/oYg0l0eHPluUkKQW+KVq1GKsmr2sSc8NOcGtVTsUygSgX4hw36V7Vw3MfQRv -sNIgKp3RDEyynoXRoG3laHrib1GdYwDKRsHB2znKQQKBgQD+NAOOqoEx0lmlg/Wf -sNImv+3da0owE1TqTMHBWXriGo+DwqT+d9S+M5x3JMpmgH9vTEDlLOM2+qF8M3B3 -TLlu1k7F8D1G7YCdIZwMLUNCekCSHsqQcU9HMHlQqXd2cxFqWbyATk9tvJzj7xC9 -zMhaKGKvIS/EF0Ld8kIvrINmGQKBgQD0SExjk4yshv/DvWknxfJr8OupgQrriLHA -Hrk+n84Iv/4vzupgKsXJQE6VN0xM6e/ANhGATuxiaA3UE4p6K9wJNryHrw/wdnyf -I9AR0Cea9F4pa26BBCdLtQuyRqgl7dBZA1n3il7vKX6wB0MLoy/uYWYCedk4w+9d -acqh7S0CBQKBgBl8x5qHV/rR13E0AO2pAfkmp0fbGQ4m8g2n8olbWmnPNfKFEpv9 -EdScQiTkCHMskRpsr9kKniGGEajtU2pyw+jsDevkwZAaAho/I3FJHIRO06iS88Z1 -xfgiUReYVkUHFojuRGss7uPW1Hg6IRiWrsPzZqmejzZ/CpJMVvyGtIoJAoGAXmo7 -LBlxO5WJ8SuaIwc85T9etkrr35EbsnetfWjihzs9kVjV+YlOnLRAKygOU4PvaEj9 -hqv6bSZugdNzqDifeOgxAfhFntkM3a1H1DqxtBBS/ItLUI48aeR1utfYUaCS8HR9 -J1HR03okPwDvhuXxtp7qgHZ74JbKQz6KVP+Ib8kCgYEA7w0NnuOoZ0la17XuyQzA -UeTZZavgm0tNqqT4JcPiUV9zkR8WJsFQE704KQ8BjDyeYMWwe8EpfJaqsGqdJKGo -RnnxwNuwT4uSNb78MxXXVRG0fN/2iu70lNySKOl/DmA8siRc/weQj5JPsGbyZkjZ -IsaTqaZQUdtbZ7vRukyPo8Q= ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/server-singleEndpoint.pem b/.evergreen/ocsp/rsa/server-singleEndpoint.pem deleted file mode 100644 index 66849f535..000000000 --- a/.evergreen/ocsp/rsa/server-singleEndpoint.pem +++ /dev/null @@ -1,52 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEBDCCAuygAwIBAgIEZ0Q/vTANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwNDEwMjA1NDEzWhcNNDAwNDEyMjA1NDEzWjBiMRAwDgYD -VQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxEjAQBgNVBAMMCWxvY2FsaG9z -dDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQHDAZPQ1NQLTEwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDH3pCRRVSRghw0+55xvfRRWQx/ -5BO/M7XGtiLwAUU+R42FyQYdu8rgZQavtDLU/KQaws6xoIBvl0YezBGRTbEa4pM/ -ATeGSTz9Xdo5Zp9oQgb41yimdjVCxTrdMUAtocHi5UurkmuBJcyZ6UHLvQ11whgL -tZfGFO3drhLm8A/mDFr4o+9LX4q+9qh+cDFEWnTx5j16ZN2pWNR8lFF5pu/wsqPL -CJEC/dq95EuwQJoupjF+bC/faGx+b1/CLx2HyCR0pDSvNq9AlK9W0qw5br01qIhT -+mvv6+nPs8zzdixrguNlzHsEZN/pPnFZ3xBZii88F4xfxfPoPE+rfPO5M6DfAgMB -AAGjga8wgawwCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB -BQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBRWLvOTPv7G74DYdxd+Lv/7DzLabDA4 -BggrBgEFBQcBAQQsMCowKAYIKwYBBQUHMAGGHGh0dHA6Ly9sb2NhbGhvc3Q6ODEw -MC9zdGF0dXMwGgYDVR0RBBMwEYIJbG9jYWxob3N0hwR/AAABMA0GCSqGSIb3DQEB -CwUAA4IBAQAJvPmDizmNplKTdBu4YsF2E7EsAfJREuN7TKAbLsiYtyAMuIu5BIv6 -Ma4pcxeJUvYML5czHoPwjXNC9+M7aTsb18q8ZRAJzY2kVhvzhT2lVH5YFC8vhJ92 -aeX8GTpoa+lslXuvVe8os+tGcQzqMtiVF2xZHbAYOiAno+fVQey9VSjU+pXIcKUT -7nF/b0rRHHo8ziPsfI+h3kKWttywB+iQ60Zlt3ajlfWgTuL1fdbt9GEFl68Rhhsy -6s1h8oXSSM0VIBzJKqrubJgziXH2kVN9p1XtQcCwW2lrZ3z0GQq5nLvIsgTQHwx6 -FsuONP/eS2esZIn7LwT2nSNa/Hfh9pq/ ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDH3pCRRVSRghw0 -+55xvfRRWQx/5BO/M7XGtiLwAUU+R42FyQYdu8rgZQavtDLU/KQaws6xoIBvl0Ye -zBGRTbEa4pM/ATeGSTz9Xdo5Zp9oQgb41yimdjVCxTrdMUAtocHi5UurkmuBJcyZ -6UHLvQ11whgLtZfGFO3drhLm8A/mDFr4o+9LX4q+9qh+cDFEWnTx5j16ZN2pWNR8 -lFF5pu/wsqPLCJEC/dq95EuwQJoupjF+bC/faGx+b1/CLx2HyCR0pDSvNq9AlK9W -0qw5br01qIhT+mvv6+nPs8zzdixrguNlzHsEZN/pPnFZ3xBZii88F4xfxfPoPE+r -fPO5M6DfAgMBAAECggEAR6QUR64FMR7lA2zJj1WaNGpp25GiLl/XoUF55nNeIYO+ -S50Rryi4AJTVv7cknUltfRYkxnCUeOtNPA7DoUSq3csnImdKQr0PunWgmgCZ1OIN -47YjoP8v+h3+Cnjz2ydm+vBbnkUeea1V2DlO1zuNjo8i1Vei7mJkHJift92GpVtY -DW5GrTZfntPJXHQQjz6nGn5mQxTlEi1WafPPyDoqykwAIonehIyhYd7UCDw/e62D -XMWk8Bo7YmX0Y3utQF2tuu1ih2zz5+NXwviycBqE9GL4eoHZgdKJrPBA/nRBsy5J -SqorCKxLODvl77EIdqPUDZsyGzvWlmoEyDtthsvsiQKBgQDtFaIQW+DizGqibfuT -6/z5+4G8ZAp+FVJU/0Z/SmOX/ro2LzYhlV0l71OWvMVKxCfp30zlYaYWNo+R8h40 -O6zSsKcSE7JLFNh53euPz49Ium+N5OFZ6Yez7HBD/5sjWEt+iGDUZAr9SlqMZAGN -PhUSu51QvFj1kqqVbXxmA3TkiwKBgQDX0NOCri8J4jqBk8TuY4AZS3zq/2lVmFYh -81itma43zXRG3z8hFFct/CBqxObGwi1MQAGZAG7EeOvnH6FPV/85Tej1Wd0VtV8f -ryWgjSvZDt3dATZBKVcVibTfazdkfeqze2wtYRjFqNPlAitSF7HN8iOC8B02dIMq -ec6UM9w7fQKBgA03CHqK9IUPyd3V7ZD4NXilqTycAu22OImeVQqhVd3SCAUfKpBC -qBeGOI2NZh3dwy/JD5s1jzFrxyLmcQKOVPrFd/qM+IIw3kQkt42jjyQJqFArctg1 -KShBRJy1sasNr9+UsHkGPoqRy2xJ4sBBtqD9ri4i4X6Gt1Vu7eEtziUzAoGAafd0 -Uz8Zg53cIlGfKXobpM/m9zAP1WJmMGdfDGZgH7A2vrHROnnVUJPyitpBgihHu5/V -6P1IZhoFosdqGh5YCBgUIZxNLOKQYWtLa2jFtd9R2rlEnXwh8UZbVDQ9z47wFc6t -UB7T3gHGgTSudrGBsWCKRTmG7n0JBmsmnqhUI7UCgYBb4nBED+kaMGFdzRdpq8Dv -+KgShSjWa+4U2S4QZ3MYtb+rIMsAoRO4K8S3VMqIsun3S7T5szyp72jBqAQTHiHA -eGlRTrirc9dR8x6CO66UUf5tGMG05P7qo23Qoip+t1/rcCgrBH7er68AhMIZbxfK -2dj9RqANXyIWWI320Y+VkA== ------END PRIVATE KEY----- diff --git a/.evergreen/ocsp/rsa/server.pem b/.evergreen/ocsp/rsa/server.pem deleted file mode 100644 index abf978ef8..000000000 --- a/.evergreen/ocsp/rsa/server.pem +++ /dev/null @@ -1,53 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIEMzCCAxugAwIBAgIET11AjzANBgkqhkiG9w0BAQsFADB0MQswCQYDVQQGEwJV -UzERMA8GA1UECAwITmV3IFlvcmsxFjAUBgNVBAcMDU5ldyBZb3JrIENpdHkxEDAO -BgNVBAoMB01vbmdvREIxDzANBgNVBAsMBktlcm5lbDEXMBUGA1UEAwwOS2VybmVs -IFRlc3QgQ0EwHhcNMjAwMzE5MTU1NjI1WhcNNDAwMzIxMTU1NjI1WjBiMRAwDgYD -VQQKDAdNb25nb0RCMQ8wDQYDVQQLDAZLZXJuZWwxEjAQBgNVBAMMCWxvY2FsaG9z -dDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAk5ZMQ8wDQYDVQQHDAZPQ1NQLTEwggEi -MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0usokl3+yXDtLeYquHQyAkXIw -lY4tukv8nEgLtUlK0kt9Q7P8EdZigzFUeJtL7piFCTIaLuv6e4UqLLDXbIANxD/J -NXXQPtBasOSzdgZ2ToUj5ANPv0QegsFubpYGq5LXsMdKTRE8uTB91PJBvRzxY2Nx -O1kdQcIrYpSYXqKsNgq/8iAPrmAdZ3y+S7OBuNyvlQJZqWoB1Y0ZWuR1QrcLMgdm -q2SdBzZT/3P+r/dbHMKdDZ5JdJ9Nm4ylOG7mhZkfb38JfdvWedzXDMu6TzS2W67o -yM90Cj9Lt+UyHLJ2jlcsZSZp4km6Oj5RBNVhd95SFckvPJxLzSyFlpjOIXsNAgMB -AAGjgd4wgdswCQYDVR0TBAIwADALBgNVHQ8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB -BQUHAwEGCCsGAQUFBwMCMB0GA1UdDgQWBBTe7IMKaO1aQILcpoj5wLFgIRuPHzBn -BggrBgEFBQcBAQRbMFkwLQYIKwYBBQUHMAGGIWh0dHA6Ly9sb2NhbGhvc3Q6OTAw -MS9wb3dlci9sZXZlbDAoBggrBgEFBQcwAYYcaHR0cDovL2xvY2FsaG9zdDo4MTAw -L3N0YXR1czAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwDQYJKoZIhvcNAQEL -BQADggEBAFMVds6y5Qy7DlFsca0u8WE+ckoONa427bWBqNx8b/Hwaj3N3C58XQx/ -EZRNt9XVy/LoEHr+NmOWsCl69fINeVpx8Ftot8XPbFG9YxL/xbJ3lvWesPR6bwpm -PZqGiwfl1VrZvuobXADz0Rfru7B7LPkurpSxDiNBf/9JuLPYe9ffZwdFWQoehw07 -b9FKVaJ7mSHno/5f4Z/uKau91sL0kiKKG9Lo2JEIEmpp8HJ3OKCFh7DFkeDlRCDl -WyYxF4g/PfvJQm2Hd89cu8m3RX84rLa9jn1RGL/8bmxE0dxk4Di/t9gl5KGWIH9Q -LBeVRSQmH9GbI/WmldMLkGkvARYYTp8= ------END CERTIFICATE----- ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC0usokl3+yXDtL -eYquHQyAkXIwlY4tukv8nEgLtUlK0kt9Q7P8EdZigzFUeJtL7piFCTIaLuv6e4Uq -LLDXbIANxD/JNXXQPtBasOSzdgZ2ToUj5ANPv0QegsFubpYGq5LXsMdKTRE8uTB9 -1PJBvRzxY2NxO1kdQcIrYpSYXqKsNgq/8iAPrmAdZ3y+S7OBuNyvlQJZqWoB1Y0Z -WuR1QrcLMgdmq2SdBzZT/3P+r/dbHMKdDZ5JdJ9Nm4ylOG7mhZkfb38JfdvWedzX -DMu6TzS2W67oyM90Cj9Lt+UyHLJ2jlcsZSZp4km6Oj5RBNVhd95SFckvPJxLzSyF -lpjOIXsNAgMBAAECggEAIjNe4YHR5nzRs7yyY7SXkxTzGQKUP08L5ifk8mJCFmip -ZHEVdFQjz8yn3yZbrQjfz/0ngBD1Exeg4ZRHetzLds92iqsVOm1InIDxJozlOCov -w9T4U3UMfQGdfTpsJaL+TNblP8hJxMX+yTEtDwesnHmEbf8fJAw3pGIpYJQ4EIJv -1uPzyB8EsrTjj23a5NPF/FGdzzO+HP5fhNNIUmP83pqonXLUSy0v5rsRFNxNMBn3 -SPRWq+Z779eLQXnRjW/6hKssSBFg6zAOi3Gc4oDbrDa2WEbZ0BEU+JW3XduN91bU -SsO3yQ+VL+CQn5wvXGIsc4EHH6wO8Bs0vXfD7zeLgQKBgQDrHOzPymI0p0PnxL2+ -8LrSU1x0WdedPZJugwwfUYMfn7sjKx+FyVLvM+7wuJ8zsMOAab2AHv3S0Nxkovhb -aa4lH9SUAHILcU+nb7M6E+mwSr65AemGspvGz4ZC6L52CGVzRfIcoBDD0T8OZGH0 -4IeiqOluqtvgCoW4UV1dyw0nPQKBgQDEyQwcim5ghEQ7V2eDefE5yxNlkNEnSVnG -DNubM8KURR8jehpDWkIlxQ4p2tLBWGB0YeOCG9NmwfLnQUStvSFE6/XjP5bBJlov -jT66T98NgFRfUeVkcCAiVT/LlDzXWXXPLyZSY+bxtn8UA1NYNu0pLCLDR9TlH1dK -FKwiomdgEQKBgEimcHqo4/23LeGBRsyooGH7hlchp+GbtBLYBbfrvSPZfL8aRSxX -EHx/xLa3peIYHeEhS4A6k15AUcn7HdlJZ5lrI4n0NUlZ4y4u8ufgXVavUg3jDGEl -8cLWP3uPZcMdRxP+qhi0UVng36Y32JkNhHv7y935h+XL+pQA+GPSKadVAoGAPPvp -SvcDmdmjo5hEthQWU8jBbBpjFv++WIgnjoON65E4QzBV70WLdlUJPKNZ6R1QVwD3 -Fp00+IVml5A8jnMsWkWd4B0WxSjzjgUByY9zGqYIf7nLk0LEUp+Es7xu1nYc8mY0 -RBg9u+7IlxUowQ/Uk4vgAhDCw3bhAE5Dwj/+NWECgYBWnBz5l+Uar9oT1ErRSbJW -RVYx3iIsqka1lox/zw5nme1Q/dv2uTQy6uZRn9U9ikqGwePuMEexQTR1csnMqeMM -4i3pDFpGBfTZXJAvH790ak6eZ0eBXqzJlTyEjll4r4zXHk+slm/tAgpIg0Ps3J9j -Sd+bTtG47gpb4sRbqEtQFQ== ------END PRIVATE KEY----- diff --git a/.evergreen/orchestration/configs/sharded_clusters/basic.json b/.evergreen/orchestration/configs/sharded_clusters/basic.json deleted file mode 100644 index fd03f5686..000000000 --- a/.evergreen/orchestration/configs/sharded_clusters/basic.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "id": "shard_cluster_1", - "shards": [ - { - "id": "sh01", - "shardParams": { - "members": [ - { - "procParams": { - "ipv6": true, - "bind_ip": "127.0.0.1,::1", - "shardsvr": true, - "port": 27217 - } - }, - { - "procParams": { - "ipv6": true, - "bind_ip": "127.0.0.1,::1", - "shardsvr": true, - "port": 27218 - } - }, - { - "procParams": { - "ipv6": true, - "bind_ip": "127.0.0.1,::1", - "shardsvr": true, - "port": 27219 - } - } - ] - } - } - ], - "routers": [ - { - "ipv6": true, - "bind_ip": "127.0.0.1,::1", - "port": 27017 - }, - { - "ipv6": true, - "bind_ip": "127.0.0.1,::1", - "port": 27018 - } - ] -} diff --git a/.evergreen/run-tests.sh b/.evergreen/run-tests.sh index c56e86b66..b76b97004 100755 --- a/.evergreen/run-tests.sh +++ b/.evergreen/run-tests.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/usr/bin/env bash set -o errexit # Exit the script with error if any of the commands fail # Supported environment variables @@ -7,8 +7,9 @@ CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH:-}" # Optional path to crypt_shar DRIVER_MONGODB_VERSION=${DRIVER_MONGODB_VERSION:-} # Required if IS_MATRIX_TESTING is "true" IS_MATRIX_TESTING=${IS_MATRIX_TESTING:-} # Specify "true" to enable matrix testing. Defaults to empty string. If "true", DRIVER_MONGODB_VERSION and MONGODB_VERSION will also be checked. MONGODB_URI=${MONGODB_URI:-} # Connection string (including credentials and topology info) +MONGODB_SINGLE_MONGOS_LB_URI=${MONGODB_SINGLE_MONGOS_LB_URI:-} # Single-mongos LB connection string +MONGODB_MULTI_MONGOS_LB_URI=${MONGODB_MULTI_MONGOS_LB_URI:-} # Multi-mongos LB connection string MONGODB_VERSION=${MONGODB_VERSION:-} # Required if IS_MATRIX_TESTING is "true" -SKIP_CRYPT_SHARED="${SKIP_CRYPT_SHARED:-no}" # Specify "yes" to ignore CRYPT_SHARED_LIB_PATH. Defaults to "no" SSL=${SSL:-no} # Specify "yes" to enable SSL. Defaults to "no" TESTS=${TESTS:-} # Optional test group. Defaults to all tests @@ -42,16 +43,27 @@ if [ "${IS_MATRIX_TESTING}" = "true" ]; then fi # Enable verbose output to see skipped and incomplete tests -PHPUNIT_OPTS="${PHPUNIT_OPTS} -v --configuration phpunit.evergreen.xml" - -# Determine if MONGODB_URI already has a query string -SUFFIX=$(echo "$MONGODB_URI" | grep -Eo "\?(.*)" | cat) +PHPUNIT_OPTS="${PHPUNIT_OPTS} --configuration phpunit.evergreen.xml" if [ "$SSL" = "yes" ]; then + SSL_OPTS="ssl=true&sslallowinvalidcertificates=true" + + # Determine if MONGODB_URI already has a query string + SUFFIX=$(echo "$MONGODB_URI" | grep -Eo "\?(.*)" | cat) + if [ -z "$SUFFIX" ]; then - MONGODB_URI="${MONGODB_URI}/?ssl=true&sslallowinvalidcertificates=true" + MONGODB_URI="${MONGODB_URI}/?${SSL_OPTS}" else - MONGODB_URI="${MONGODB_URI}&ssl=true&sslallowinvalidcertificates=true" + MONGODB_URI="${MONGODB_URI}&${SSL_OPTS}" + fi + + # Assume LB URIs already have a query string (e.g. "?loadBalanced=true") + if [ -n "${MONGODB_SINGLE_MONGOS_LB_URI}" ]; then + MONGODB_SINGLE_MONGOS_LB_URI="${MONGODB_SINGLE_MONGOS_LB_URI}&${SSL_OPTS}" + fi + + if [ -n "${MONGODB_MULTI_MONGOS_LB_URI}" ]; then + MONGODB_MULTI_MONGOS_LB_URI="${MONGODB_MULTI_MONGOS_LB_URI}&${SSL_OPTS}" fi fi @@ -65,26 +77,28 @@ export SYMFONY_DEPRECATIONS_HELPER=999999 export API_VERSION="${API_VERSION}" export CRYPT_SHARED_LIB_PATH="${CRYPT_SHARED_LIB_PATH}" export MONGODB_URI="${MONGODB_URI}" +export MONGODB_SINGLE_MONGOS_LB_URI="${MONGODB_SINGLE_MONGOS_LB_URI}" +export MONGODB_MULTI_MONGOS_LB_URI="${MONGODB_MULTI_MONGOS_LB_URI}" # Run the tests, and store the results in a junit result file case "$TESTS" in - atlas-data-lake*) - php vendor/bin/simple-phpunit $PHPUNIT_OPTS --testsuite "Atlas Data Lake Test Suite" + atlas) + php vendor/bin/phpunit $PHPUNIT_OPTS --group atlas ;; csfle) - php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group csfle + php vendor/bin/phpunit $PHPUNIT_OPTS --group csfle ;; - versioned-api) - php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group versioned-api + csfle-without-aws-creds) + php vendor/bin/phpunit $PHPUNIT_OPTS --group csfle-without-aws-creds ;; - serverless) - php vendor/bin/simple-phpunit $PHPUNIT_OPTS --group serverless + versioned-api) + php vendor/bin/phpunit $PHPUNIT_OPTS --group versioned-api ;; *) - php vendor/bin/simple-phpunit $PHPUNIT_OPTS + php vendor/bin/phpunit $PHPUNIT_OPTS ;; esac diff --git a/.evergreen/x509gen/82e9b7a6.0 b/.evergreen/x509gen/82e9b7a6.0 deleted file mode 100644 index 6ac86cfcc..000000000 --- a/.evergreen/x509gen/82e9b7a6.0 +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfzCCAmegAwIBAgIDB1MGMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIwMjMxMVoXDTM5MDUyMjIwMjMxMVoweTEb -MBkGA1UEAxMSRHJpdmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAw -DgYDVQQKEwdNb25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQI -EwhOZXcgWW9yazELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCl7VN+WsQfHlwapcOpTLZVoeMAl1LTbWTFuXSAavIyy0W1Ytky1UP/ -bxCSW0mSWwCgqoJ5aXbAvrNRp6ArWu3LsTQIEcD3pEdrFIVQhYzWUs9fXqPyI9k+ -QNNQ+MRFKeGteTPYwF2eVEtPzUHU5ws3+OKp1m6MCLkwAG3RBFUAfddUnLvGoZiT -pd8/eNabhgHvdrCw+tYFCWvSjz7SluEVievpQehrSEPKe8DxJq/IM3tSl3tdylzT -zeiKNO7c7LuQrgjAfrZl7n2SriHIlNmqiDR/kdd8+TxBuxjFlcf2WyHCO3lIcIgH -KXTlhUCg50KfHaxHu05Qw0x8869yIzqbAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQELBQADggEBAEHuhTL8KQZcKCTSJbYA9MgZj7U32arMGBbc1hiq -VBREwvdVz4+9tIyWMzN9R/YCKmUTnCq8z3wTlC8kBtxYn/l4Tj8nJYcgLJjQ0Fwe -gT564CmvkUat8uXPz6olOCdwkMpJ9Sj62i0mpgXJdBfxKQ6TZ9yGz6m3jannjZpN -LchB7xSAEWtqUgvNusq0dApJsf4n7jZ+oBZVaQw2+tzaMfaLqHgMwcu1FzA8UKCD -sxCgIsZUs8DdxaD418Ot6nPfheOTqe24n+TTa+Z6O0W0QtnofJBx7tmAo1aEc57i -77s89pfwIJetpIlhzNSMKurCAocFCJMJLAASJFuu6dyDvPo= ------END CERTIFICATE----- \ No newline at end of file diff --git a/.evergreen/x509gen/altname.pem b/.evergreen/x509gen/altname.pem deleted file mode 100644 index ff0fd61e6..000000000 --- a/.evergreen/x509gen/altname.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAgyOELJgWP2akoidBdtchvdRF8gZrR8rwORDmST1tmH5aKiH2 -e/lkWf+pxmXnvmLXoOKk3HHGgyZ7v1sDDB0z7/rAimECqxnqJ90GFq8rGR60jCL/ -hs+30m0U9CNAvjzD5yFruaisPeCZuZXEA06QbTbTaSD4u/n7fgZVGSnj2m+DFel5 -S7dgL4Pa7Vh2nua8QPfczLLQI/uP9Ma5ZXjk2C2V+QBkmK64OanGY6yXn8+m5Lp1 -cKhhQUiXVVO1BgFHw65FapTrhG2zgyuaqvb5F062V+XGIwZhWhDz4cTgCx0dFKU+ -WQGXuEDDY3EzaOd6Ds3h6WkCRDs9cn2i0j4taQIDAQABAoIBAHeCTXkKXPQIia6Q -0dMIuWIy6k9nVCtIIWYQJZ3HUnJva6IL84IFxFNUcBczVV+m2lVvVsjjEwMAdjPs -MDnA/00LGp7BS9o8Mq2DeoH/vuoUlntDhdUIxcAJ0teurNjxraKcTX0T32xAnDeJ -6ekNlwdAuKeM+cDtTykJglH9X/324eOT8sEkpohkTJaszs3PEqgN9jrHttVatmft -KGT06aANBrEH61xr/nfBehd3R7WyVsIUmlihlIIBwbxyycdMSxHIiE1Qno252Ek7 -GJp/dPqO2pwIH47cop48SsZLFVosqaZs3jkEIDkQkyd7tvmVG69aFBPz5+PTvdRv -fufuvXUCgYEA1gTnvln9/PmC9mKFTDGdKLhFIypyOhKl1lUoDgcmCencjwu28yTA -+A2fKZQFupiHYvSg5kbvmr7FGVtKLNPJWocvr7jqPvrVLCzvs6l94LhGCTVyOmgn -e09xyDx3xQTuJmpg+4LD1jImL3OLO3fplbslwisip2CWzHZR6h3QRVMCgYEAnNy5 -F81xbimMVcubQve6LPzZq1pYaUM5ppkejNdBoEKR28+GP0NQ7YbN6iu2LXlbpWk/ -IrAyUmDUpnXFsiRDDWnPol6JzYTovzeZG+NCMJWkaQEOzm8BpUsC2UBvsX55ddxt -WM4CkLOxo7KXfQwYAMKc/H8tFE7DXloH82U7jtMCgYB+PuiBFc7IYlrJgjZFSuL8 -+S33X3uAHC3tL9Bv7fGXWXd8fhmOdfjKmiZwPVvfxUffrJQZInEGpE/Z9EreBJQ7 -LZGIo5iyS/5hj6RaI7oYTDssBXX7VCMuDx/8UQcJli3xRUEuO+XPvUdfKFZSXxrP -81SDpDRN7aEmvQj3BF0t9wKBgCgX5ptl4HtG1V7MhufMB+Md0ckRc42cKC0j8AIR -tu1udneXiHm9C/9aOGGFQLBI15rk1sVYAdS6eT/+1EQfLqBMDk0zGsfUE+VkIZdW -NAHVDcvlAFLVXrdP/+9ln+bfK85rQ+ux5Ef2Fg6ARGYq5Cu1koibPPt20krYejXF -Bz8PAoGBAKbCmptnjdu4QF+rGLfYyVnrtyUuRgN+Q0MCIag1dBTag6rC17xDYJ6g -3Txzzb9xAZ35pSHroB7TSr32vRUQVrAcfldW4mousr9A0pDoc/E2axtE1YmzSYwk -jqgu3PeWrtwBthUEoRXbQAed97bKW+gUU677u9IFRCS2YIfwDV5R ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDnTCCAoWgAwIBAgIDCRkUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIyMzQzNloXDTM5MDUyMjIyMzQzNlowcDES -MBAGA1UEAxMJbG9jYWxob3N0MRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdN -b25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9y -azELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCD -I4QsmBY/ZqSiJ0F21yG91EXyBmtHyvA5EOZJPW2YfloqIfZ7+WRZ/6nGZee+Yteg -4qTcccaDJnu/WwMMHTPv+sCKYQKrGeon3QYWrysZHrSMIv+Gz7fSbRT0I0C+PMPn -IWu5qKw94Jm5lcQDTpBtNtNpIPi7+ft+BlUZKePab4MV6XlLt2Avg9rtWHae5rxA -99zMstAj+4/0xrlleOTYLZX5AGSYrrg5qcZjrJefz6bkunVwqGFBSJdVU7UGAUfD -rkVqlOuEbbODK5qq9vkXTrZX5cYjBmFaEPPhxOALHR0UpT5ZAZe4QMNjcTNo53oO -zeHpaQJEOz1yfaLSPi1pAgMBAAGjNzA1MDMGA1UdEQQsMCqCCWxvY2FsaG9zdIcE -fwAAAYIXYWx0ZXJuYXRpdmUubW9uZ29kYi5jb20wDQYJKoZIhvcNAQELBQADggEB -AADOro10g1QReF0QVX2w+yVwCWy8FUzuksX0RI0RCFRJPo79SH7o2IZFGbLlBL8K -MMsgSrzRW/HcyE91fv0R2b7kvqfD3Eo1W1ocufjVg+3e4uuwm9k9SLjSI6mE4hEf -H6BeFoZhUdbrq9l/ez+NK+3ToHAl1bGLkipfnB522gRO1CjkpiY2knaaNQtjd/a9 -7QXqUs+KMJx42yqjBbVE6MdA2ypNMMIc8AgI5kRKEBGHpS4Z6VNZN4Pus1atGlRW -OwkjHK5pnT1TAKSODjfFw5VlXGztGTPKuJhM2/X7Qi0bO8b7NmH7cjDBATmZF5O8 -FAxIQ8+3qUPMXYkb1ipLOdQ= ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/ca.pem b/.evergreen/x509gen/ca.pem deleted file mode 100644 index 6ac86cfcc..000000000 --- a/.evergreen/x509gen/ca.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfzCCAmegAwIBAgIDB1MGMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIwMjMxMVoXDTM5MDUyMjIwMjMxMVoweTEb -MBkGA1UEAxMSRHJpdmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAw -DgYDVQQKEwdNb25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQI -EwhOZXcgWW9yazELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCl7VN+WsQfHlwapcOpTLZVoeMAl1LTbWTFuXSAavIyy0W1Ytky1UP/ -bxCSW0mSWwCgqoJ5aXbAvrNRp6ArWu3LsTQIEcD3pEdrFIVQhYzWUs9fXqPyI9k+ -QNNQ+MRFKeGteTPYwF2eVEtPzUHU5ws3+OKp1m6MCLkwAG3RBFUAfddUnLvGoZiT -pd8/eNabhgHvdrCw+tYFCWvSjz7SluEVievpQehrSEPKe8DxJq/IM3tSl3tdylzT -zeiKNO7c7LuQrgjAfrZl7n2SriHIlNmqiDR/kdd8+TxBuxjFlcf2WyHCO3lIcIgH -KXTlhUCg50KfHaxHu05Qw0x8869yIzqbAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQELBQADggEBAEHuhTL8KQZcKCTSJbYA9MgZj7U32arMGBbc1hiq -VBREwvdVz4+9tIyWMzN9R/YCKmUTnCq8z3wTlC8kBtxYn/l4Tj8nJYcgLJjQ0Fwe -gT564CmvkUat8uXPz6olOCdwkMpJ9Sj62i0mpgXJdBfxKQ6TZ9yGz6m3jannjZpN -LchB7xSAEWtqUgvNusq0dApJsf4n7jZ+oBZVaQw2+tzaMfaLqHgMwcu1FzA8UKCD -sxCgIsZUs8DdxaD418Ot6nPfheOTqe24n+TTa+Z6O0W0QtnofJBx7tmAo1aEc57i -77s89pfwIJetpIlhzNSMKurCAocFCJMJLAASJFuu6dyDvPo= ------END CERTIFICATE----- \ No newline at end of file diff --git a/.evergreen/x509gen/client-private.pem b/.evergreen/x509gen/client-private.pem deleted file mode 100644 index 551a43a75..000000000 --- a/.evergreen/x509gen/client-private.pem +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsNS8UEuin7/K29jXfIOLpIoh1jEyWVqxiie2Onx7uJJKcoKo -khA3XeUnVN0k6X5MwYWcN52xcns7LYtyt06nRpTG2/emoV44w9uKTuHsvUbiOwSV -m/ToKQQ4FUFZoqorXH+ZmJuIpJNfoW+3CkE1vEDCIecIq6BNg5ySsPtvSuSJHGjp -mc7/5ZUDvFE2aJ8QbJU3Ws0HXiEb6ymi048LlzEL2VKX3w6mqqh+7dcZGAy7qYk2 -5FZ9ktKvCeQau7mTyU1hsPrKFiKtMN8Q2ZAItX13asw5/IeSTq2LgLFHlbj5Kpq4 -GmLdNCshzH5X7Ew3IYM8EHmsX8dmD6mhv7vpVwIDAQABAoIBABOdpb4qhcG+3twA -c/cGCKmaASLnljQ/UU6IFTjrsjXJVKTbRaPeVKX/05sgZQXZ0t3s2mV5AsQ2U1w8 -Cd+3w+qaemzQThW8hAOGCROzEDX29QWi/o2sX0ydgTMqaq0Wv3SlWv6I0mGfT45y -/BURIsrdTCvCmz2erLqa1dL4MWJXRFjT9UTs5twlecIOM2IHKoGGagFhymRK4kDe -wTRC9fpfoAgyfus3pCO/wi/F8yKGPDEwY+zgkhrJQ+kSeki7oKdGD1H540vB8gRt -EIqssE0Y6rEYf97WssQlxJgvoJBDSftOijS6mwvoasDUwfFqyyPiirawXWWhHXkc -DjIi/XECgYEA5xfjilw9YyM2UGQNESbNNunPcj7gDZbN347xJwmYmi9AUdPLt9xN -3XaMqqR22k1DUOxC/5hH0uiXir7mDfqmC+XS/ic/VOsa3CDWejkEnyGLiwSHY502 -wD/xWgHwUiGVAG9HY64vnDGm6L3KGXA2oqxanL4V0+0+Ht49pZ16i8sCgYEAw+Ox -CHGtpkzjCP/z8xr+1VTSdpc/4CP2HONnYopcn48KfQnf7Nale69/1kZpypJlvQSG -eeA3jMGigNJEkb8/kaVoRLCisXcwLc0XIfCTeiK6FS0Ka30D/84Qm8UsHxRdpGkM -kYITAa2r64tgRL8as4/ukeXBKE+oOhX43LeEfyUCgYBkf7IX2Ndlhsm3GlvIarxy -NipeP9PGdR/hKlPbq0OvQf9R1q7QrcE7H7Q6/b0mYNV2mtjkOQB7S2WkFDMOP0P5 -BqDEoKLdNkV/F9TOYH+PCNKbyYNrodJOt0Ap6Y/u1+Xpw3sjcXwJDFrO+sKqX2+T -PStG4S+y84jBedsLbDoAEwKBgQCTz7/KC11o2yOFqv09N+WKvBKDgeWlD/2qFr3w -UU9K5viXGVhqshz0k5z25vL09Drowf1nAZVpFMO2SPOMtq8VC6b+Dfr1xmYIaXVH -Gu1tf77CM9Zk/VSDNc66e7GrUgbHBK2DLo+A+Ld9aRIfTcSsMbNnS+LQtCrQibvb -cG7+MQKBgQCY11oMT2dUekoZEyW4no7W5D74lR8ztMjp/fWWTDo/AZGPBY6cZoZF -IICrzYtDT/5BzB0Jh1f4O9ZQkm5+OvlFbmoZoSbMzHL3oJCBOY5K0/kdGXL46WWh -IRJSYakNU6VIS7SjDpKgm9D8befQqZeoSggSjIIULIiAtYgS80vmGA== ------END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/.evergreen/x509gen/client-public.pem b/.evergreen/x509gen/client-public.pem deleted file mode 100644 index 53e4e034f..000000000 --- a/.evergreen/x509gen/client-public.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDAxOUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIzNTU1NFoXDTM5MDUyMjIzNTU1NFowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALDUvFBLop+/ -ytvY13yDi6SKIdYxMllasYontjp8e7iSSnKCqJIQN13lJ1TdJOl+TMGFnDedsXJ7 -Oy2LcrdOp0aUxtv3pqFeOMPbik7h7L1G4jsElZv06CkEOBVBWaKqK1x/mZibiKST -X6FvtwpBNbxAwiHnCKugTYOckrD7b0rkiRxo6ZnO/+WVA7xRNmifEGyVN1rNB14h -G+spotOPC5cxC9lSl98Opqqofu3XGRgMu6mJNuRWfZLSrwnkGru5k8lNYbD6yhYi -rTDfENmQCLV9d2rMOfyHkk6ti4CxR5W4+SqauBpi3TQrIcx+V+xMNyGDPBB5rF/H -Zg+pob+76VcCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAqRcLAGvYMaGYOV4HJTzNotT2qE0I9THNQ -wOV1fBg69x6SrUQTQLjJEptpOA288Wue6Jt3H+p5qAGV5GbXjzN/yjCoItggSKxG -Xg7279nz6/C5faoIKRjpS9R+MsJGlttP9nUzdSxrHvvqm62OuSVFjjETxD39DupE -YPFQoHOxdFTtBQlc/zIKxVdd20rs1xJeeU2/L7jtRBSPuR/Sk8zot7G2/dQHX49y -kHrq8qz12kj1T6XDXf8KZawFywXaz0/Ur+fUYKmkVk1T0JZaNtF4sKqDeNE4zcns -p3xLVDSl1Q5Gwj7bgph9o4Hxs9izPwiqjmNaSjPimGYZ399zcurY ------END CERTIFICATE----- \ No newline at end of file diff --git a/.evergreen/x509gen/client.pem b/.evergreen/x509gen/client.pem deleted file mode 100644 index 5b0700109..000000000 --- a/.evergreen/x509gen/client.pem +++ /dev/null @@ -1,48 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsNS8UEuin7/K29jXfIOLpIoh1jEyWVqxiie2Onx7uJJKcoKo -khA3XeUnVN0k6X5MwYWcN52xcns7LYtyt06nRpTG2/emoV44w9uKTuHsvUbiOwSV -m/ToKQQ4FUFZoqorXH+ZmJuIpJNfoW+3CkE1vEDCIecIq6BNg5ySsPtvSuSJHGjp -mc7/5ZUDvFE2aJ8QbJU3Ws0HXiEb6ymi048LlzEL2VKX3w6mqqh+7dcZGAy7qYk2 -5FZ9ktKvCeQau7mTyU1hsPrKFiKtMN8Q2ZAItX13asw5/IeSTq2LgLFHlbj5Kpq4 -GmLdNCshzH5X7Ew3IYM8EHmsX8dmD6mhv7vpVwIDAQABAoIBABOdpb4qhcG+3twA -c/cGCKmaASLnljQ/UU6IFTjrsjXJVKTbRaPeVKX/05sgZQXZ0t3s2mV5AsQ2U1w8 -Cd+3w+qaemzQThW8hAOGCROzEDX29QWi/o2sX0ydgTMqaq0Wv3SlWv6I0mGfT45y -/BURIsrdTCvCmz2erLqa1dL4MWJXRFjT9UTs5twlecIOM2IHKoGGagFhymRK4kDe -wTRC9fpfoAgyfus3pCO/wi/F8yKGPDEwY+zgkhrJQ+kSeki7oKdGD1H540vB8gRt -EIqssE0Y6rEYf97WssQlxJgvoJBDSftOijS6mwvoasDUwfFqyyPiirawXWWhHXkc -DjIi/XECgYEA5xfjilw9YyM2UGQNESbNNunPcj7gDZbN347xJwmYmi9AUdPLt9xN -3XaMqqR22k1DUOxC/5hH0uiXir7mDfqmC+XS/ic/VOsa3CDWejkEnyGLiwSHY502 -wD/xWgHwUiGVAG9HY64vnDGm6L3KGXA2oqxanL4V0+0+Ht49pZ16i8sCgYEAw+Ox -CHGtpkzjCP/z8xr+1VTSdpc/4CP2HONnYopcn48KfQnf7Nale69/1kZpypJlvQSG -eeA3jMGigNJEkb8/kaVoRLCisXcwLc0XIfCTeiK6FS0Ka30D/84Qm8UsHxRdpGkM -kYITAa2r64tgRL8as4/ukeXBKE+oOhX43LeEfyUCgYBkf7IX2Ndlhsm3GlvIarxy -NipeP9PGdR/hKlPbq0OvQf9R1q7QrcE7H7Q6/b0mYNV2mtjkOQB7S2WkFDMOP0P5 -BqDEoKLdNkV/F9TOYH+PCNKbyYNrodJOt0Ap6Y/u1+Xpw3sjcXwJDFrO+sKqX2+T -PStG4S+y84jBedsLbDoAEwKBgQCTz7/KC11o2yOFqv09N+WKvBKDgeWlD/2qFr3w -UU9K5viXGVhqshz0k5z25vL09Drowf1nAZVpFMO2SPOMtq8VC6b+Dfr1xmYIaXVH -Gu1tf77CM9Zk/VSDNc66e7GrUgbHBK2DLo+A+Ld9aRIfTcSsMbNnS+LQtCrQibvb -cG7+MQKBgQCY11oMT2dUekoZEyW4no7W5D74lR8ztMjp/fWWTDo/AZGPBY6cZoZF -IICrzYtDT/5BzB0Jh1f4O9ZQkm5+OvlFbmoZoSbMzHL3oJCBOY5K0/kdGXL46WWh -IRJSYakNU6VIS7SjDpKgm9D8befQqZeoSggSjIIULIiAtYgS80vmGA== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDAxOUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIzNTU1NFoXDTM5MDUyMjIzNTU1NFowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALDUvFBLop+/ -ytvY13yDi6SKIdYxMllasYontjp8e7iSSnKCqJIQN13lJ1TdJOl+TMGFnDedsXJ7 -Oy2LcrdOp0aUxtv3pqFeOMPbik7h7L1G4jsElZv06CkEOBVBWaKqK1x/mZibiKST -X6FvtwpBNbxAwiHnCKugTYOckrD7b0rkiRxo6ZnO/+WVA7xRNmifEGyVN1rNB14h -G+spotOPC5cxC9lSl98Opqqofu3XGRgMu6mJNuRWfZLSrwnkGru5k8lNYbD6yhYi -rTDfENmQCLV9d2rMOfyHkk6ti4CxR5W4+SqauBpi3TQrIcx+V+xMNyGDPBB5rF/H -Zg+pob+76VcCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAqRcLAGvYMaGYOV4HJTzNotT2qE0I9THNQ -wOV1fBg69x6SrUQTQLjJEptpOA288Wue6Jt3H+p5qAGV5GbXjzN/yjCoItggSKxG -Xg7279nz6/C5faoIKRjpS9R+MsJGlttP9nUzdSxrHvvqm62OuSVFjjETxD39DupE -YPFQoHOxdFTtBQlc/zIKxVdd20rs1xJeeU2/L7jtRBSPuR/Sk8zot7G2/dQHX49y -kHrq8qz12kj1T6XDXf8KZawFywXaz0/Ur+fUYKmkVk1T0JZaNtF4sKqDeNE4zcns -p3xLVDSl1Q5Gwj7bgph9o4Hxs9izPwiqjmNaSjPimGYZ399zcurY ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/commonName.pem b/.evergreen/x509gen/commonName.pem deleted file mode 100644 index e8ebd4953..000000000 --- a/.evergreen/x509gen/commonName.pem +++ /dev/null @@ -1,48 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEArS94Vnpx+C+LwiCIyfZH45uwDbiEqCKr0hfWPVlJdoOQgaDO -av9nbxRvx+CnsiZ1yE6Kj1NYIX3FqOzO9YizwKtPaxCqFMjDzl1HZCJ8LTZZzMic -01K38wGfacsBwno/sNZn9jgnT+9JOasD6854IAs5T7dRCFH/nxV+RuZ4ueTWIcfH -jXzAZv9+wtu0sVmQKHV0J3S6ZdPqqDaYRdhOCyShBTO4RbUW1myIjooIqqy/xceV -TmXGWycqZjyDDronT1kj/yx6znqudOeDzj1PaEdnsqdXxQlI7MVdRf3nXdDXTpw5 -gPhqxqYc47vL6RvMxqief0BJnlc6PoZWoyTRPwIDAQABAoIBAQCYNMYwSsDrfO35 -mRpfVYHs+iGKjYaZNo+Hv8dcd6Jm9E4Gf0urIfjH2VA8fKclnUOa3dxNBtTH6n/T -bPyfMpu4U1cjI6w3RBNCxRw/V0eHfOMDZbTezS459k0ib3aGc2aShn0sGkICsKzM -cA6sKfPNRdACzXv8MgTUzdEDgv7LcGwNUKYzz/XWZxOX+XpeAGNSdXxv6ASvZNJ7 -u3Ba6LbOSAjxnKK24qdBDwCfuxRvD6ovenvI3+qIDSZSrEs/ofGhEEdKlQiyUAgS -m40kWqtoq9sC4/6cGxCLw9scuwXhwE0NNP19QRjh6Hsmr6qmu8LJAKugJi+5WyLg -1oHLs91xAoGBAO4oy6cdc57UdL7A2UbFDWJkBlySw0ChCK4I49Sfq/IISpd3mOfH -SxpZoh5IEnKTEYSqMi/kUUt8J/kQhjdAhqyA33GuNekfGPumUxyB8nKtowNNevyv -Ou6Y9FmzwEektvTLoku/4GxVbrgE262YEu/U1bMA700YK88knCtRWrtFAoGBALoo -qdUpb9s0NK0K4pGo8NYdtqVraOkXPAhKCCOY+hnl0yJERU7LLM9pYCMmR9m/TPcA -pXZTETPWcB6SDJoH3nCmje1Bt3xTxnSvt9P8lXYfvgVpKz8zBrvvnZqUDbMUjWe+ -vz9/jRKrarKgzG6KLnLgFV9sNbuSoOER4/h7MmCzAoGARP2qaUHd4Y/4Nd4V0yt4 -Qh1pvl2BlHJR2mCW51xN6jI+sXwi3lncRsjabt1AAtLZy02mdjs01aIkzkDcMJtP -qB85G2x1D5BDo3q+Ls7yFgh45ZcHXrXAY6gJeQbaV6a+nVF0NW9jKt7g0QwPO02H -htRoB4/owrOS1VHsr5vEpeUCgYAsWg/MZ2js8s0yBQvh5Dws5ztiwepmzlBRMUIr -KQE9NlJNMbLJiQKOD+8FsNMhf8BYgODrBfNtREPGJMm30PQgJq5dvnB2wIbhuhOz -/9OkJv/gziOtlPyfvgDwmSGCbv0ZoIp0GHGF5y0ujbznASj72YN+DovmupJ1zQth -YgionQKBgDGtSfvf3VpJxoabJ52tC0vJFDzkqdbOT0imuLjRHmUH4pSKuMvanvVk -kYcHXeQcfLOPjH18UUqTIgK5vXXjJraduq2bGyvdLcbd3xmj5guzfim3FP83Lh/U -OMAbRgBdq3rlylRqcZh0NqV05L0kJ0Wt1XIaV/eknpuFz5nD7O+y ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDcTCCAlmgAwIBAgIDB5VBMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIxMDUxNVoXDTM5MDUyMjIxMDUxNVowfTEf -MB0GA1UEAxMWY29tbW9uTmFtZS5tb25nb2RiLm9yZzEQMA4GA1UECxMHRHJpdmVy -czEQMA4GA1UEChMHTW9uZ29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8G -A1UECBMITmV3IFlvcmsxCzAJBgNVBAYTAlVTMIIBIjANBgkqhkiG9w0BAQEFAAOC -AQ8AMIIBCgKCAQEArS94Vnpx+C+LwiCIyfZH45uwDbiEqCKr0hfWPVlJdoOQgaDO -av9nbxRvx+CnsiZ1yE6Kj1NYIX3FqOzO9YizwKtPaxCqFMjDzl1HZCJ8LTZZzMic -01K38wGfacsBwno/sNZn9jgnT+9JOasD6854IAs5T7dRCFH/nxV+RuZ4ueTWIcfH -jXzAZv9+wtu0sVmQKHV0J3S6ZdPqqDaYRdhOCyShBTO4RbUW1myIjooIqqy/xceV -TmXGWycqZjyDDronT1kj/yx6znqudOeDzj1PaEdnsqdXxQlI7MVdRf3nXdDXTpw5 -gPhqxqYc47vL6RvMxqief0BJnlc6PoZWoyTRPwIDAQABMA0GCSqGSIb3DQEBCwUA -A4IBAQA34DUMfx0YaxsXnNlCmbkncwgb69VfwWTqtON2MabOlw9fQ0Z5YlwduBSD -DxkRosVURdqV+EcGxei6opnPkdoJ+1mkCDo360q+R/bJUFqjj7djB7GCwwK/Eud4 -Jjn//eLBChU+DlTjO1yL8haEQR70LyVz37sh28oIRqoTS3Nk2SZg7Gnor1qHwd6j -OljaM1WiTJfq6XCSZ9/3C5Ix0Vr7xZaP9Dn5lgQ86du6N6tmaKqVobCw3vjITmnr -eZTC7dKU4/O52d6lHZ1vv8GyvqrRCeiolTVzhW47GvO/n+snC0NMkXvoo7Rzv1S/ -FxHvlhiH5wCbaGnBx4uF5/boedV+ ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/crl.pem b/.evergreen/x509gen/crl.pem deleted file mode 100644 index 733a0acdc..000000000 --- a/.evergreen/x509gen/crl.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN X509 CRL----- -MIIB6jCB0wIBATANBgkqhkiG9w0BAQsFADB5MRswGQYDVQQDExJEcml2ZXJzIFRl -c3RpbmcgQ0ExEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01vbmdvREIxFjAU -BgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYDVQQG -EwJVUxcNMTkwNTIyMjI0NTUzWhcNMTkwNjIxMjI0NTUzWjAVMBMCAncVFw0xOTA1 -MjIyMjQ1MzJaoA8wDTALBgNVHRQEBAICEAAwDQYJKoZIhvcNAQELBQADggEBACwQ -W9OF6ExJSzzYbpCRroznkfdLG7ghNSxIpBQUGtcnYbkP4em6TdtAj5K3yBjcKn4a -hnUoa5EJGr2Xgg0QascV/1GuWEJC9rsYYB9boVi95l1CrkS0pseaunM086iItZ4a -hRVza8qEMBc3rdsracA7hElYMKdFTRLpIGciJehXzv40yT5XFBHGy/HIT0CD50O7 -BDOHzA+rCFCvxX8UY9myDfb1r1zUW7Gzjn241VT7bcIJmhFE9oV0popzDyqr6GvP -qB2t5VmFpbnSwkuc4ie8Jizip1P8Hg73lut3oVAHACFGPpfaNIAp4GcSH61zJmff -9UBe3CJ1INwqyiuqGeA= ------END X509 CRL----- diff --git a/.evergreen/x509gen/expired.pem b/.evergreen/x509gen/expired.pem deleted file mode 100644 index 2d92be01a..000000000 --- a/.evergreen/x509gen/expired.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAw06nN1BoINnY/WJXvi+r0taDMphWQNoyeM85A6NIYKo+vWtN -fvf6f2JTpg/Q4NJ3txiZE/F6yZaMFC78l1KMoz+zIEPLpSJoIezCaXyl2+AQih8A -WmAOFAoiWYTiNfWoVM7t0Qzy6yS+rXifuET5Dg1mtWpA6xRFHZEqQTdKX4QzbT5G -RenoFIBT9wG7xUV+FG1/9s5nx4f5gZDbwMKA7mNq/Jr+rZZQV4lReeGtoYNx1I/Z -4yd4xswI3RfuB7QDZMNHWZazFxW1N6EP5NJBDZJkYLEkMX36r4Orr/73z4EA5GBx -zqdSKH9qHLRiBwesfZf7u8xb120u1S1X1J8a5QIDAQABAoIBAQC+Y9swWerYM2WL -RKYCWZhndQP6e3SBzfMrv951hGQXD38Pyh2Gq5h/O0wN8xcNQz6+t3TqcxnekCrH -tjI4FZnRvlQRHOXVeeAHSjUO/hr1Z8zXyHbgowi2Ula/64FVVr+cxQgiJTxdK7nR -g2g4Csy6/SdlrEnSoDTsKMoHPy36Q0GaLDBnthpKIc1Prhntf6vBCgQAHXVfLk6E -NwddYloL+mfEZESa3Qf2ZYeX/Ovq9agbuQ3cRE7M5FunSo9E7eXt+D+Ntk0usTKV -BaUEHLRYXV827fMDGc1vBN6WFVfthhYviIEgDdkALwOw4lfIiA2WM3fhCF6Ow9hJ -as3dpEHBAoGBAO+l4PdUXypWBYQNZKggH79kAFuAOWtLsMqEBO0ZaXXdvFdwdzhR -jbL7mhRrghgGYpXWIUaNkcbX0XPlkWl2dRzYQqRNjUSEGFabVAxdGZPfiYoupXVl -Lz/FIG3P6BnEYmczh9MxRpJyk4wlUCKppYPiBrR0Ei/qcbGvciOwLq5VAoGBANCi -PWG2izO2HuBFgZTuVIvnl7ixQXgG/tvbiEmYvDNYy1E+w1MWY10Ve/JtIncBIVHk -fEgJPL3hvipAez5ir9Qa1D4PlWxsIrbjuNcLaj+IsRhWBDjMKwRWgmTvvsimcyF5 -39Vs4FujR8cgXy8UnZhYDVRC13PyxmYfJrp4QCpRAoGAKV8nsUsdir+DAEMXp3a0 -RGRNM361avKMOMoF17DVZgW7qBTAYDakEcwh03ij4uXnSxrGb9ms2vkTLcDqE5zh -pvMmvhqtUrDDSuBR6DiCW+bxZaub4OJw/79WU97aoOgoXMymnC0bk9i35C/k37cN -3fC9W5XWNfNxYU16lPKrfGkCgYA14hD0UY72Fg03YvwqmLshPvkCbFU6SKQ96B70 -0wuYP1CTdSBBL0EOY2QVonYKQjJ20gn/GNOlPs48X1b1L8u1fhBezuuKiwsULRAq -Cfqw2f7TCDQi7ygVALrAkuK1M7f8Z1uV5X60bCE3nna21B43oFYg8vpuKb9v1I/O -DQyVYQKBgQCH/Kxq+7Or/5ciq15Vy6z+PJdsGV9FV9S7zkQOZqJ4PXJn0wG9PXnp -ugjvmU1iLx0bXs5llByRx792Q/QmdWnwMCohs6bkWaBCf36JJfTkDTzzbez43cCK -HcYi6gtbiBznWiLWekudRkWdhIFEGU6cSjimy1i4yvwIw85PlEQt/Q== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDljCCAn6gAwIBAgIDAYZJMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMDIyMzYzNVoXDTE5MDUyMTIyMzYzNVowcDES -MBAGA1UEAxMJbG9jYWxob3N0MRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdN -b25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9y -azELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDD -Tqc3UGgg2dj9Yle+L6vS1oMymFZA2jJ4zzkDo0hgqj69a01+9/p/YlOmD9Dg0ne3 -GJkT8XrJlowULvyXUoyjP7MgQ8ulImgh7MJpfKXb4BCKHwBaYA4UCiJZhOI19ahU -zu3RDPLrJL6teJ+4RPkODWa1akDrFEUdkSpBN0pfhDNtPkZF6egUgFP3AbvFRX4U -bX/2zmfHh/mBkNvAwoDuY2r8mv6tllBXiVF54a2hg3HUj9njJ3jGzAjdF+4HtANk -w0dZlrMXFbU3oQ/k0kENkmRgsSQxffqvg6uv/vfPgQDkYHHOp1Iof2octGIHB6x9 -l/u7zFvXbS7VLVfUnxrlAgMBAAGjMDAuMCwGA1UdEQQlMCOCCWxvY2FsaG9zdIcE -fwAAAYcQAAAAAAAAAAAAAAAAAAAAATANBgkqhkiG9w0BAQsFAAOCAQEAgPh9C6Yi -6ykJYfaOETPEkggI9LlLQyQ0VhSJGrcw8DXGuPEkyd2xtczYoh0ijtYD3nlTQnh1 -u+5mEEP05nuMMURzG+v7WzZG8Qfz/SDBY1Lfvb/waI3w3RT/dcZ6jwz39jQhV+rU -o2F1vr37Hnh1Ehoa2igjKL1w1LmWdoFgHb0p09qQDAGtkP0gxl0t7iujDDRStLQn -OpWwfOpCaYhtzWwONJn/JIG+JCE/szcRbmc4XKw8t06ffS0mKR/yZBCoekZinnPD -XRVWAH/UF5XPs0mUlrvhFcT/vjgXSZvpi+UuVv3XL56xwPmXAgKsYUpqLlgbrVxv -jY93LTJ1azg+Sw== ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/password_protected.pem b/.evergreen/x509gen/password_protected.pem deleted file mode 100644 index cc9e12470..000000000 --- a/.evergreen/x509gen/password_protected.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN ENCRYPTED PRIVATE KEY----- -MIIFHzBJBgkqhkiG9w0BBQ0wPDAbBgkqhkiG9w0BBQwwDgQIC8as6PDVhwECAggA -MB0GCWCGSAFlAwQBAgQQTYOgCJcRqUI7dsgqNojv/ASCBNCG9fiu642V4AuFK34c -Q42lvy/cR0CIXLq/rDXN1L685kdeKex7AfDuRtnjY2+7CLJiJimgQNJXDJPHab/k -MBHbwbBs38fg6eSYX8V08/IyyTege5EJMhYxmieHDC3DXKt0gyHk6hA/r5+Mr49h -HeVGwqBLJEQ3gVIeHaOleZYspsXXWqOPHnFiqnk/biaJS0+LkDDEiQgTLEYSnOjP -lexxUc4BV/TN0Z920tZCMfwx7IXD/C+0AkV/Iqq4LALmT702EccB3indaIJ8biGR -radqDLR32Q+vT9uZHgT8EFiUsISMqhob2mnyTfFV/s9ghWwogjSz0HrRcq6fxdg7 -oeyT9K0ET53AGTGmV0206byPu6qCj1eNvtn+t1Ob+d5hecaTugRMVheWPlc5frsz -AcewDNa0pv4pZItjAGMqOPJHfzEDnzTJXpLqGYhg044H1+OCY8+1YK7U0u8dO+/3 -f5AoDMq18ipDVTFTooJURej4/Wjbrfad3ZFjp86nxfHPeWM1YjC9+IlLtK1wr0/U -V8TjGqCkw8yHayz01A86iA8X53YQBg+tyMGjxmivo6LgFGKa9mXGvDkN+B+0+OcA -PqldAuH/TJhnkqzja767e4n9kcr+TmV19Hn1hcJPTDrRU8+sSqQFsWN4pvHazAYB -UdWie+EXI0eU2Av9JFgrVcpRipXjB48BaPwuBw8hm+VStCH7ynF4lJy6/3esjYwk -Mx+NUf8+pp1DRzpzuJa2vAutzqia5r58+zloQMxkgTZtJkQU6OCRoUhHGVk7WNb1 -nxsibOSzyVSP9ZNbHIHAn43vICFGrPubRs200Kc4CdXsOSEWoP0XYebhiNJgGtQs -KoISsV4dFRLwhaJhIlayTBQz6w6Ph87WbtuiAqoLiuqdXhUGz/79j/6JZqCH8t/H -eZs4Dhu+HdD/wZKJDYAS+JBsiwYWnI3y/EowZYgLdOMI4u6xYDejhxwEw20LW445 -qjJ7pV/iX2uavazHgC91Bfd4zodfXIQ1IDyTmb51UFwx0ARzG6enntduO6xtcYU9 -MXwfrEpuZ/MkWTLkR0PHPbIPcR1MiVwPKdvrLk42Bzj/urtXYrAFUckMFMzEh+uv -0lix2hbq/Xwj4dXcY4w9hnC6QQDCJTf9S6MU6OisrZHKk0qZ2Vb4aU/eBcBsHBwo -X/QGcDHneHxlrrs2eLX26Vh8Odc5h8haeIxnfaa1t+Yv56OKHuAztPMnJOUL7KtQ -A556LxT0b5IGx0RcfUcbG8XbxEHseACptoDOoguh9923IBI0uXmpi8q0P815LPUu -0AsE47ATDMGPnXbopejRDicfgMGjykJn8vKO8r/Ia3Fpnomx4iJNCXGqomL+GMpZ -IhQbKNrRG6XZMlx5kVCT0Qr1nOWMiOTSDCQ5vrG3c1Viu+0bctvidEvs+LCm98tb -7ty8F0uOno0rYGNQz18OEE1Tj+E19Vauz1U35Z5SsgJJ/GfzhSJ79Srmdg2PsAzk -AUNTKXux1GLf1cMjTiiU5g+tCEtUL9Me7lsv3L6aFdrCyRbhXUQfJh4NAG8+3Pvh -EaprThBzKsVvbOfU81mOaH9YMmUgmxG86vxDiNtaWd4v6c1k+HGspJr/q49pcXZP -ltBMuS9AihstZ1sHJsyQCmNXkA== ------END ENCRYPTED PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDBXUHMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMzAwMDEyOVoXDTM5MDUyMzAwMDEyOVowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAOqCb0Lo4XsV -W327Wlnqc5rwWa5Elw0rFuehSfViRIcYfuFWAPXoOj3fIDsYz6d41G8hp6tkF88p -swlbzDF8Fc7mXDhauwwl2F/NrWYUXwCT8fKju4DtGd2JlDMi1TRDeofkYCGVPp70 -vNqd0H8iDWWs8OmiNrdBLJwNiGaf9y15ena4ImQGitXLFn+qNSXYJ1Rs8p7Y2PTr -L+dff5gJCVbANwGII1rjMAsrMACPVmr8c1Lxoq4fSdJiLweosrv2Lk0WWGsO0Seg -ZY71dNHEyNjItE+VtFEtslJ5L261i3BfF/FqNnH2UmKXzShwfwxyHT8o84gSAltQ -5/lVJ4QQKosCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQBOAlKxIMFcTZ+4k8NJv97RSf+zOb5Wu2ct -uxSZxzgKTxLFUuEM8XQiEz1iHQ3XG+uV1fzA74YLQiKjjLrU0mx54eM1vaRtOXvF -sJlzZU8Z2+523FVPx4HBPyObQrfXmIoAiHoQ4VUeepkPRpXxpifgWd/OCWhLDr2/ -0Kgcb0ybaGVDpA0UD9uVIwgFjRu6id7wG+lVcdRxJYskTOOaN2o1hMdAKkrpFQbd -zNRfEoBPUYR3QAmAKP2HBjpgp4ktOHoOKMlfeAuuMCUocSnmPKc3xJaH/6O7rHcf -/Rm0X411RH8JfoXYsSiPsd601kZefhuWvJH0sJLibRDvT7zs8C1v ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/server.pem b/.evergreen/x509gen/server.pem deleted file mode 100644 index 7480f9644..000000000 --- a/.evergreen/x509gen/server.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAhNrB0E6GY/kFSd8/vNpu/t952tbnOsD5drV0XPvmuy7SgKDY -a/S+xb/jPnlZKKehdBnH7qP/gYbv34ZykzcDFZscjPLiGc2cRGP+NQCSFK0d2/7d -y15zSD3zhj14G8+MkpAejTU+0/qFNZMc5neDvGanTe0+8aWa0DXssM0MuTxIv7j6 -CtsMWeqLLofN7a1Kw2UvmieCHfHMuA/08pJwRnV/+5T9WONBPJja2ZQRrG1BjpI4 -81zSPUZesIqi8yDlExdvgNaRZIEHi/njREqwVgJOZomUY57zmKypiMzbz48dDTsV -gUStxrEqbaP+BEjQYPX5+QQk4GdMjkLf52LR6QIDAQABAoIBAHSs+hHLJNOf2zkp -S3y8CUblVMsQeTpsR6otaehPgi9Zy50TpX4KD5D0GMrBH8BIl86y5Zd7h+VlcDzK -gs0vPxI2izhuBovKuzaE6rf5rFFkSBjxGDCG3o/PeJOoYFdsS3RcBbjVzju0hFCs -xnDQ/Wz0anJRrTnjyraY5SnQqx/xuhLXkj/lwWoWjP2bUqDprnuLOj16soNu60Um -JziWbmWx9ty0wohkI/8DPBl9FjSniEEUi9pnZXPElFN6kwPkgdfT5rY/TkMH4lsu -ozOUc5xgwlkT6kVjXHcs3fleuT/mOfVXLPgNms85JKLucfd6KiV7jYZkT/bXIjQ+ -7CZEn0ECgYEA5QiKZgsfJjWvZpt21V/i7dPje2xdwHtZ8F9NjX7ZUFA7mUPxUlwe -GiXxmy6RGzNdnLOto4SF0/7ebuF3koO77oLup5a2etL+y/AnNAufbu4S5D72sbiz -wdLzr3d5JQ12xeaEH6kQNk2SD5/ShctdS6GmTgQPiJIgH0MIdi9F3v0CgYEAlH84 -hMWcC+5b4hHUEexeNkT8kCXwHVcUjGRaYFdSHgovvWllApZDHSWZ+vRcMBdlhNPu -09Btxo99cjOZwGYJyt20QQLGc/ZyiOF4ximQzabTeFgLkTH3Ox6Mh2Rx9yIruYoX -nE3UfMDkYELanEJUv0zenKpZHw7tTt5yXXSlEF0CgYBSsEOvVcKYO/eoluZPYQAA -F2jgzZ4HeUFebDoGpM52lZD+463Dq2hezmYtPaG77U6V3bUJ/TWH9VN/Or290vvN -v83ECcC2FWlSXdD5lFyqYx/E8gqE3YdgqfW62uqM+xBvoKsA9zvYLydVpsEN9v8m -6CSvs/2btA4O21e5u5WBTQKBgGtAb6vFpe0gHRDs24SOeYUs0lWycPhf+qFjobrP -lqnHpa9iPeheat7UV6BfeW3qmBIVl/s4IPE2ld4z0qqZiB0Tf6ssu/TpXNPsNXS6 -dLFz+myC+ufFdNEoQUtQitd5wKbjTCZCOGRaVRgJcSdG6Tq55Fa22mOKPm+mTmed -ZdKpAoGAFsTYBAHPxs8nzkCJCl7KLa4/zgbgywO6EcQgA7tfelB8bc8vcAMG5o+8 -YqAfwxrzhVSVbJx0fibTARXROmbh2pn010l2wj3+qUajM8NiskCPFbSjGy7HSUze -P8Kt1uMDJdj55gATzn44au31QBioZY2zXleorxF21cr+BZCJgfA= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDlTCCAn2gAwIBAgICdxUwDQYJKoZIhvcNAQELBQAweTEbMBkGA1UEAxMSRHJp -dmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdNb25n -b0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9yazEL -MAkGA1UEBhMCVVMwHhcNMTkwNTIyMjIzMjU2WhcNMzkwNTIyMjIzMjU2WjBwMRIw -EAYDVQQDEwlsb2NhbGhvc3QxEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01v -bmdvREIxFjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3Jr -MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAITa -wdBOhmP5BUnfP7zabv7fedrW5zrA+Xa1dFz75rsu0oCg2Gv0vsW/4z55WSinoXQZ -x+6j/4GG79+GcpM3AxWbHIzy4hnNnERj/jUAkhStHdv+3ctec0g984Y9eBvPjJKQ -Ho01PtP6hTWTHOZ3g7xmp03tPvGlmtA17LDNDLk8SL+4+grbDFnqiy6Hze2tSsNl -L5ongh3xzLgP9PKScEZ1f/uU/VjjQTyY2tmUEaxtQY6SOPNc0j1GXrCKovMg5RMX -b4DWkWSBB4v540RKsFYCTmaJlGOe85isqYjM28+PHQ07FYFErcaxKm2j/gRI0GD1 -+fkEJOBnTI5C3+di0ekCAwEAAaMwMC4wLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/ -AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBol8+YH7MA -HwnIh7KcJ8h87GkCWsjOJCDJWiYBJArQ0MmgDO0qdx+QEtvLMn3XNtP05ZfK0WyX -or4cWllAkMFYaFbyB2hYazlD1UAAG+22Rku0UP6pJMLbWe6pnqzx+RL68FYdbZhN -fCW2xiiKsdPoo2VEY7eeZKrNr/0RFE5EKXgzmobpTBQT1Dl3Ve4aWLoTy9INlQ/g -z40qS7oq1PjjPLgxINhf4ncJqfmRXugYTOnyFiVXLZTys5Pb9SMKdToGl3NTYWLL -2AZdjr6bKtT+WtXyHqO0cQ8CkAW0M6VOlMluACllcJxfrtdlQS2S4lUIj76QKBdZ -khBHXq/b8MFX ------END CERTIFICATE----- diff --git a/.evergreen/x509gen/wild.pem b/.evergreen/x509gen/wild.pem deleted file mode 100644 index d41800748..000000000 --- a/.evergreen/x509gen/wild.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEowIBAAKCAQEAlenyliMpkLM9aR51iOO7hdLS66pwgafsJlIbtsKAy6WxlcKA -yecs0yCQfw5z5j3BFgv88dzAFEF+jFG6o/EmAzqmK5uRCQX1EJbl2p8detbzToj9 -Ys1Z1peWE8FkJtZMKUdLdlRQQ57v2VUr0kwtFEUGlSNyVwf4pJ5coyqpukmoUdko -zrKeclshjydDVo44Ln6WYvN6odz/CZT808fHZ0CXcIEKyDV8zXIcHGX2OUL/ajtZ -+C2pIbAx64nin1BLtHGvDT0Pan1xKDiMCkOdc7va0gLh0qtPjGLsI4vc8iByviGJ -Kw7hVaj7ym0r2DFzeqghfvNNNHisGXSf+6EcPQIDAQABAoIBAGq/PVefDhfVKaNS -ZwrkbkDqT/ozUQ1hzwuyZ72JXkCkaYFkEGS0Ufy8MWfnmKuXyYezXZezQqqpwDyW -bboTGqgt+OkQSwQL0+bOLDmyF0HDEVkYvqS96HyfT+QdTv1AltbFx3woqUadQ9iT -hzKlv2uxgvBrXx2NtYUypnAhDt5wQQ4n1w46Kl1USb983qWDWyFtHfIQo6vF1JK/ -s6I6oA2tmORPTD3A7E2xT98UMM8B1c/v1F+owAiD+KNmgAN4oWSWBfRGEKg59fZA -aGWjQrwoWmQQJnMnTsHZc+2hT7waKnyOwOFq1NPXyfCw+4cSeI3B3rPxPyShBM4O -ZKfajIECgYEAz555nPHhk5GmMpXprryCODy66ChHWulA3fM9r0k/ObBgKqTirAOA -y0PmA8OxR8acV0V2lZImdwF5Lvnj+c8+fTFSnPKSQHpcZ/lbxo+m2rYwv7+BxUP9 -GJAWzA6xqBde6hNPULml8cNOqT7jwRnLt/DkwY+94Oeh3H5CRYb90Y0CgYEAuNkR -EieGwCn+TjgatkhMLhYqr234544p3ofL82CFlCcsOXtWqCma6POOi038eBlZiHV9 -EPBq4qQHCZMAPeApTZbiZ+Z8ezC3IxjGSX0jP5QK+gBrkk7nbp24nRMlHOrwizsL -/Sxu4Y6puZk5aTUZVufPLXokY6Iez0Kd07vyUXECgYBqWHFQi7EQ5nzr0lAVOee1 -qJ3QRrlt/qZESdCh1XH2Obq4fSbCFzVEaK4L5ZQMANaZ+TGpoWfkczPAdS1qCtam -R7paPAHf1w04EMkKpxA/XS0ROqXdBltA1qVmtmwXfokWeveYkM9IS9Mh6927TlxE -BrcV0mvfJKaLC30koeWnDQKBgEn1oBzxb7sHklbdn+J7Pu/Zsq6Kg+KyQRJmpzXz -0r6ahdlh/iQ+sVqvyML4KyIqkmZFDAtxBnM0ShSMmrYnMJ941ZHY6Mmpjj0etofE -6AuSQmoRLPlXVMYvmSRP+rN9VU2ADKX510usd0BpjE0KD99z1LNPgavTvBwVfWyw -cJ4hAoGBALgyVPMBPv1d8irbM1WHFe/I3vxjb4DWOY9xclbRWjkW69oZmkouGP07 -52ehzfBtBC87VPLwTEr/ERZqfICBqZvXYFypd2ydGhbDKjDswiUd6nACNKAx5ZPo -OVwQjVfjGqkKNThoHhvE1YU//+WtCe0YVUGqMA9dyZe1QO3HcqI8 ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDkzCCAnugAwIBAgIDCRU4MA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIyMzgxOVoXDTM5MDUyMjIyMzgxOVowcDES -MBAGA1UEAxMJbG9jYWxob3N0MRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdN -b25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9y -azELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCV -6fKWIymQsz1pHnWI47uF0tLrqnCBp+wmUhu2woDLpbGVwoDJ5yzTIJB/DnPmPcEW -C/zx3MAUQX6MUbqj8SYDOqYrm5EJBfUQluXanx161vNOiP1izVnWl5YTwWQm1kwp -R0t2VFBDnu/ZVSvSTC0URQaVI3JXB/iknlyjKqm6SahR2SjOsp5yWyGPJ0NWjjgu -fpZi83qh3P8JlPzTx8dnQJdwgQrINXzNchwcZfY5Qv9qO1n4LakhsDHrieKfUEu0 -ca8NPQ9qfXEoOIwKQ51zu9rSAuHSq0+MYuwji9zyIHK+IYkrDuFVqPvKbSvYMXN6 -qCF+8000eKwZdJ/7oRw9AgMBAAGjLTArMCkGA1UdEQQiMCCCCWxvY2FsaG9zdIcE -fwAAAYINKi5tb25nb2RiLm9yZzANBgkqhkiG9w0BAQsFAAOCAQEAMCENVK+w+wP7 -T1XBytsScn7+Bh33sn+A+c7H/6BNOEdTxCQ67L3zBc0XrBFYtiHcAppNBKvvM8cV -ERWjXlU2nZ+A0WKOZE2nXYQL5lBnnXoIMwcdtJuTJuWw8r3MlVXDcP6bK8tNSQMG -WYK7PHQ3RNiWNABZejJV9GVP25nO6Wr2gt2xnEwYvUXTnCJtT+NsTE/fU4MlGuUL -a93Cec86Ij0XTMTcnj4nfZhct30nuqiU4wWBPHCN7BXxRQzIHu68aVHBpwDEAf6j -PAOKhucGY6DW+dyrW/1BjW6+ZOmJWxJ7GB+x0gjprQbGH67gIvRvTa9wW7NqWyS3 -Go/qT7H6FQ== ------END CERTIFICATE----- diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..7309afd20 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,27 @@ +.* export-ignore +*.md export-ignore +tests export-ignore +benchmark export-ignore +docs export-ignore +examples export-ignore +generator export-ignore +mongo-orchestration export-ignore +stubs export-ignore +tools export-ignore +Makefile export-ignore +phpbench.json.dist export-ignore +phpcs.xml.dist export-ignore +phpunit.evergreen.xml export-ignore +phpunit.xml.dist export-ignore +psalm.xml.dist export-ignore +psalm-baseline.xml export-ignore +rector.php export-ignore + +# Prevent generated build files from showing diffs in pull requests +.evergreen/config/generated/** linguist-generated=true +/src/Builder/Accumulator/*.php linguist-generated=true +/src/Builder/Expression/*.php linguist-generated=true +/src/Builder/Query/*.php linguist-generated=true +/src/Builder/Search/*.php linguist-generated=true +/src/Builder/Stage/*.php linguist-generated=true +/tests/Builder/*/Pipelines.php linguist-generated=true diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..067d4a1b3 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1 @@ +* @mongodb/dbx-php diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml new file mode 100644 index 000000000..f85a72de2 --- /dev/null +++ b/.github/actions/setup/action.yml @@ -0,0 +1,57 @@ +name: Setup +description: Sets up the build environment +inputs: + php-version: + description: "PHP version to install" + required: true + driver-version: + description: "MongoDB extension version to install" + required: true + php-ini-values: + description: "INI values to pass along to setup-php action" + required: false + default: "" + working-directory: + description: "The directory where composer.json is located, if it is not in the repository root." + required: false + ignore-platform-req: + description: "Whether to ignore platform requirements when installing dependencies with Composer." + required: false + default: "false" + +runs: + using: composite + steps: + - name: Setup cache environment + id: extcache + uses: shivammathur/cache-extensions@v1 + with: + php-version: ${{ inputs.php-version }} + extensions: "mongodb-${{ inputs.driver-version }}" + key: "extcache-${{ inputs.driver-version }}" + + - name: Cache extensions + uses: actions/cache@v4 + with: + path: ${{ steps.extcache.outputs.dir }} + key: ${{ steps.extcache.outputs.key }} + restore-keys: ${{ steps.extcache.outputs.key }} + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + coverage: xdebug + extensions: "mongodb-${{ inputs.driver-version }}" + php-version: "${{ inputs.php-version }}" + tools: cs2pr + ini-values: "${{ inputs.php-ini-values }}" + + - name: Show driver information + run: "php --ri mongodb" + shell: bash + + - name: Install dependencies with Composer + uses: ramsey/composer-install@3.0.0 + with: + composer-options: "--no-suggest ${{ inputs.ignore-platform-req == 'true' && '--ignore-platform-req=php+' || '' }}" + working-directory: "${{ inputs.working-directory }}" diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..d771b123a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,10 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" + - package-ecosystem: "gitsubmodule" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/release.yml b/.github/release.yml new file mode 100644 index 000000000..34064499c --- /dev/null +++ b/.github/release.yml @@ -0,0 +1,5 @@ +changelog: + exclude: + authors: + - mongodb-php-bot + - dependabot diff --git a/.github/workflows/coding-standards.yml b/.github/workflows/coding-standards.yml index 3476e5344..22e97cb1c 100644 --- a/.github/workflows/coding-standards.yml +++ b/.github/workflows/coding-standards.yml @@ -1,73 +1,54 @@ name: "Coding Standards" on: + merge_group: pull_request: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" push: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" + +env: + PHP_VERSION: "8.2" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: phpcs: name: "phpcs" - runs-on: "ubuntu-20.04" - - strategy: - matrix: - php-version: - - "7.4" - driver-version: - - "mongodb/mongo-php-driver@master" + runs-on: "ubuntu-24.04" steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v5" - - name: Setup cache environment - id: extcache - uses: shivammathur/cache-extensions@v1 + - name: "Setup" + uses: "./.github/actions/setup" with: - php-version: ${{ matrix.php-version }} - extensions: "mongodb-${{ matrix.driver-version }}" - key: "extcache-v1" + php-version: ${{ env.PHP_VERSION }} + driver-version: ${{ env.DRIVER_VERSION }} - - name: Cache extensions - uses: actions/cache@v2 - with: - path: ${{ steps.extcache.outputs.dir }} - key: ${{ steps.extcache.outputs.key }} - restore-keys: ${{ steps.extcache.outputs.key }} + # The -q option is required until phpcs v4 is released + - name: "Run PHP_CodeSniffer" + run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" - with: - coverage: "none" - extensions: "mongodb-${{ matrix.driver-version }}" - php-version: "${{ matrix.php-version }}" - tools: "cs2pr" + rector: + name: "Rector" + runs-on: "ubuntu-24.04" - - name: "Show driver information" - run: "php --ri mongodb" + steps: + - name: "Checkout" + uses: "actions/checkout@v5" - - name: "Cache dependencies installed with Composer" - uses: "actions/cache@v2" + - name: "Setup" + uses: "./.github/actions/setup" with: - path: "~/.composer/cache" - key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" - restore-keys: "php-${{ matrix.php-version }}-composer-locked-" - - - name: "Install dependencies with Composer" - run: "composer install --no-interaction --no-progress --no-suggest" + php-version: ${{ env.PHP_VERSION }} + driver-version: ${{ env.DRIVER_VERSION }} - # The -q option is required until phpcs v4 is released - - name: "Run PHP_CodeSniffer" - run: "vendor/bin/phpcs -q --no-colors --report=checkstyle | cs2pr" + - name: "Run Rector" + run: "vendor/bin/rector --ansi --dry-run" diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml deleted file mode 100644 index a7a1413a8..000000000 --- a/.github/workflows/docs.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: "Docs" - -on: - pull_request: - branches: - - "v*.*" - - "master" - - "feature/*" - paths: - - "docs/**" - push: - branches: - - "v*.*" - - "master" - - "feature/*" - paths: - - "docs/**" - -jobs: - giza: - name: "Build Docs" - runs-on: "ubuntu-20.04" - - steps: - - name: "Checkout library" - uses: "actions/checkout@v3" - with: - path: library - fetch-depth: 2 - - - name: "Checkout docs" - uses: "actions/checkout@v3" - with: - repository: mongodb/docs-php-library - path: docs - fetch-depth: 2 - - - name: "Setup python" - uses: actions/setup-python@v4 - with: - python-version: '2.7' - - # The requirements file installs urllib3 with an incompatible version; we replace it with one that works - - name: "Install giza" - run: | - pip install -r https://raw.githubusercontent.com/mongodb/docs-tools/master/giza/requirements.txt - pip install urllib3==1.25.2 - - - name: "Sync documentation" - run: "rsync -a library/docs/ docs/source/" - - - name: "Run Giza" - run: "giza make publish" - working-directory: docs - - - name: "Upload built documentation" - uses: actions/upload-artifact@v3 - with: - name: php-library-docs.tar.gz - path: docs/build/public/master/php-library.tar.gz diff --git a/.github/workflows/generator.yml b/.github/workflows/generator.yml new file mode 100644 index 000000000..f9650fc1f --- /dev/null +++ b/.github/workflows/generator.yml @@ -0,0 +1,40 @@ +name: "Generator" + +on: + merge_group: + pull_request: + branches: + - "v*.*" + - "feature/*" + push: + branches: + - "v*.*" + - "feature/*" + +env: + PHP_VERSION: "8.2" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" + +jobs: + diff: + name: "Diff check" + runs-on: "ubuntu-24.04" + + steps: + - name: "Checkout" + uses: "actions/checkout@v5" + + - name: "Setup" + uses: "./.github/actions/setup" + with: + php-version: ${{ env.PHP_VERSION }} + driver-version: ${{ env.DRIVER_VERSION }} + working-directory: "generator" + + - name: "Run Generator" + run: "generator/generate" + + - name: "Check file diff" + run: git add . -N && git diff --exit-code diff --git a/.github/workflows/merge-up.yml b/.github/workflows/merge-up.yml new file mode 100644 index 000000000..f8de9e676 --- /dev/null +++ b/.github/workflows/merge-up.yml @@ -0,0 +1,33 @@ +name: Merge up + +on: + push: + branches: + - "v[0-9]+.[0-9x]+" + +env: + GH_TOKEN: ${{ secrets.MERGE_UP_TOKEN }} + +jobs: + merge-up: + name: Create merge up pull request + runs-on: ubuntu-latest + + steps: + - name: Checkout + id: checkout + uses: actions/checkout@v5 + with: + # fetch-depth 0 is required to fetch all branches, not just the branch being built + fetch-depth: 0 + token: ${{ secrets.MERGE_UP_TOKEN }} + + - name: Create pull request + id: create-pull-request + uses: alcaeus/automatic-merge-up-action@1.0.1 + with: + ref: ${{ github.ref_name }} + branchNamePattern: 'v.' + devBranchNamePattern: 'v.x' + ignoredBranches: ${{ vars.IGNORED_MERGE_UP_BRANCHES }} + enableAutoMerge: true diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 000000000..37bb2ed3f --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,185 @@ +name: "Release New Version" +run-name: "Release ${{ inputs.version }}" + +on: + workflow_dispatch: + inputs: + version: + description: "The version to be released. This is checked for consistency with the branch name and configuration" + required: true + type: "string" + jira-version-number: + description: "JIRA version ID (e.g. 54321)" + required: true + type: "string" + +env: + default-release-message: | + The PHP team is happy to announce that version {0} of the MongoDB PHP library is now available. + + **Release Highlights** + + TODO: one or more paragraphs describing important changes in this release + + A complete list of resolved issues in this release may be found in [JIRA](https://jira.mongodb.org/secure/ReleaseNote.jspa?version={1}&projectId=12483). + + **Documentation** + + Documentation for this library may be found in the [PHP Library Manual](https://mongodb.com/docs/php-library/current/). + + **Installation** + + This library may be installed or upgraded with: + + composer require mongodb/mongodb:{0} + + Installation instructions for the `mongodb` extension may be found in the [PHP.net documentation](https://php.net/manual/en/mongodb.installation.php). + +jobs: + prepare-release: + environment: release + name: "Prepare release" + runs-on: ubuntu-latest + permissions: + id-token: write + contents: write + + steps: + - name: "Create release output" + run: echo '🎬 Release process for version ${{ inputs.version }} started by @${{ github.triggering_actor }}' >> $GITHUB_STEP_SUMMARY + + - name: "Generate token and checkout repository" + uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 + with: + app_id: ${{ vars.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + + - name: "Store version numbers in env variables" + run: | + echo RELEASE_VERSION=${{ inputs.version }} >> $GITHUB_ENV + echo RELEASE_VERSION_WITHOUT_STABILITY=$(echo ${{ inputs.version }} | awk -F- '{print $1}') >> $GITHUB_ENV + echo RELEASE_BRANCH=v$(echo ${{ inputs.version }} | cut -d '.' -f-2) >> $GITHUB_ENV + echo DEV_BRANCH=v$(echo ${{ inputs.version }} | cut -d '.' -f-1).x >> $GITHUB_ENV + + - name: "Ensure release tag does not already exist" + run: | + if [[ $(git tag -l ${RELEASE_VERSION}) == ${RELEASE_VERSION} ]]; then + echo '❌ Release failed: tag for version ${{ inputs.version }} already exists' >> $GITHUB_STEP_SUMMARY + exit 1 + fi + + # For patch releases (A.B.C where C != 0), we expect the release to be + # triggered from the A.B maintenance branch + - name: "Fail if patch release is created from wrong release branch" + if: ${{ !endsWith(env.RELEASE_VERSION_WITHOUT_STABILITY, '.0') && env.RELEASE_BRANCH != github.ref_name }} + run: | + echo '❌ Release failed due to branch mismatch: expected ${{ inputs.version }} to be released from ${{ env.RELEASE_BRANCH }}, got ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY + exit 1 + + # For non-patch releases (A.B.C where C == 0), we expect the release to + # be triggered from the A.B maintenance branch or A.x development branch + - name: "Fail if non-patch release is created from wrong release branch" + if: ${{ endsWith(env.RELEASE_VERSION_WITHOUT_STABILITY, '.0') && env.RELEASE_BRANCH != github.ref_name && env.DEV_BRANCH != github.ref_name }} + run: | + echo '❌ Release failed due to branch mismatch: expected ${{ inputs.version }} to be released from ${{ env.RELEASE_BRANCH }} or ${{ env.DEV_BRANCH }}, got ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY + exit 1 + + # If a non-patch release is created from its A.x development branch, + # create the A.B maintenance branch from the current one and push it + - name: "Create and push new release branch for non-patch release" + if: ${{ endsWith(env.RELEASE_VERSION_WITHOUT_STABILITY, '.0') && env.DEV_BRANCH == github.ref_name }} + run: | + echo '🆕 Creating new release branch ${{ env.RELEASE_BRANCH }} from ${{ github.ref_name }}' >> $GITHUB_STEP_SUMMARY + git checkout -b ${RELEASE_BRANCH} + git push origin ${RELEASE_BRANCH} + + # + # Preliminary checks done - commence the release process + # + + - name: "Set up drivers-github-tools" + uses: mongodb-labs/drivers-github-tools/setup@v3 + with: + aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} + aws_region_name: ${{ vars.AWS_REGION_NAME }} + aws_secret_id: ${{ secrets.AWS_SECRET_ID }} + + - name: "Prepare release message" + run: | + cat > release-message <<'EOL' + ${{ format(env.default-release-message, inputs.version, inputs.jira-version-number) }} + EOL + + - name: "Create draft release" + run: echo "RELEASE_URL=$(gh release create ${{ inputs.version }} --target ${{ env.RELEASE_BRANCH }} --title "${{ inputs.version }}" --notes-file release-message --draft)" >> "$GITHUB_ENV" + + - name: "Create release tag" + uses: mongodb-labs/drivers-github-tools/tag-version@v3 + with: + version: ${{ inputs.version }} + tag_message_template: 'Release ${VERSION}' + + # TODO: Manually merge using ours strategy. This avoids merge-up pull requests being created + # Process is: + # 1. switch to next branch (according to merge-up action) + # 2. merge release branch using --strategy=ours + # 3. push next branch + # 4. switch back to release branch, then push + + - name: "Set summary" + run: | + echo '🚀 Created tag and drafted release for version [${{ inputs.version }}](${{ env.RELEASE_URL }})' >> $GITHUB_STEP_SUMMARY + echo '✍️ You may now update the release notes and publish the release when ready' >> $GITHUB_STEP_SUMMARY + + static-analysis: + needs: prepare-release + name: "Run Static Analysis" + uses: ./.github/workflows/static-analysis.yml + with: + ref: refs/tags/${{ inputs.version }} + permissions: + security-events: write + id-token: write + + publish-ssdlc-assets: + needs: static-analysis + environment: release + name: "Publish SSDLC Assets" + runs-on: ubuntu-latest + permissions: + security-events: read + id-token: write + contents: write + + steps: + - name: "Generate token and checkout repository" + uses: mongodb-labs/drivers-github-tools/secure-checkout@v3 + with: + app_id: ${{ vars.APP_ID }} + private_key: ${{ secrets.APP_PRIVATE_KEY }} + ref: refs/tags/${{ inputs.version }} + + # Sets the S3_ASSETS environment variable used later + - name: "Set up drivers-github-tools" + uses: mongodb-labs/drivers-github-tools/setup@v3 + with: + aws_role_arn: ${{ secrets.AWS_ROLE_ARN }} + aws_region_name: ${{ vars.AWS_REGION_NAME }} + aws_secret_id: ${{ secrets.AWS_SECRET_ID }} + + - name: "Generate SSDLC Reports" + uses: mongodb-labs/drivers-github-tools/full-report@v3 + with: + product_name: "MongoDB PHP Driver (library)" + release_version: ${{ inputs.version }} + silk_asset_group: mongodb-php-driver-library + + - name: "Upload SBOM as release artifact" + run: gh release upload ${{ inputs.version }} ${{ env.S3_ASSETS }}/cyclonedx.sbom.json + continue-on-error: true + + - name: Upload S3 assets + uses: mongodb-labs/drivers-github-tools/upload-s3-assets@v3 + with: + version: ${{ inputs.version }} + product_name: mongo-php-library diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index fac516d73..440d1d6c4 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -1,72 +1,63 @@ name: "Static Analysis" on: + merge_group: pull_request: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" push: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" + workflow_call: + inputs: + ref: + description: "The git ref to check" + type: string + required: true + +env: + PHP_VERSION: "8.2" + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: psalm: name: "Psalm" - runs-on: "ubuntu-20.04" - - strategy: - matrix: - php-version: - - "7.4" - driver-version: - - "mongodb/mongo-php-driver@master" + runs-on: "ubuntu-24.04" steps: - name: "Checkout" - uses: "actions/checkout@v3" - - - name: Setup cache environment - id: extcache - uses: shivammathur/cache-extensions@v1 + uses: "actions/checkout@v5" with: - php-version: ${{ matrix.php-version }} - extensions: "mongodb-${{ matrix.driver-version }}" - key: "extcache-v1" + ref: ${{ github.event_name == 'workflow_dispatch' && inputs.ref || github.ref }} - - name: Cache extensions - uses: actions/cache@v2 - with: - path: ${{ steps.extcache.outputs.dir }} - key: ${{ steps.extcache.outputs.key }} - restore-keys: ${{ steps.extcache.outputs.key }} + - name: "Get SHA hash of checked out ref" + if: ${{ github.event_name == 'workflow_dispatch' }} + run: | + echo CHECKED_OUT_SHA=$(git rev-parse HEAD) >> $GITHUB_ENV - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" + - name: "Setup" + uses: "./.github/actions/setup" with: - coverage: "none" - extensions: "mongodb-${{ matrix.driver-version }}" - php-version: "${{ matrix.php-version }}" - tools: "cs2pr" + php-version: ${{ env.PHP_VERSION }} + driver-version: ${{ env.DRIVER_VERSION }} - - name: "Show driver information" - run: "php --ri mongodb" + - name: "Run Psalm" + run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc) --report=psalm.sarif" - - name: "Cache dependencies installed with Composer" - uses: "actions/cache@v2" + - name: "Upload SARIF report" + if: ${{ github.event_name != 'workflow_dispatch' }} + uses: "github/codeql-action/upload-sarif@v4" with: - path: "~/.composer/cache" - key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" - restore-keys: "php-${{ matrix.php-version }}-composer-locked-" + sarif_file: psalm.sarif - - name: "Install dependencies with Composer" - run: "composer install --no-interaction --no-progress --no-suggest" - - - name: "Run Psalm" - run: "vendor/bin/psalm --show-info=false --stats --output-format=github --threads=$(nproc)" + - name: "Upload SARIF report" + if: ${{ github.event_name == 'workflow_dispatch' }} + uses: "github/codeql-action/upload-sarif@v4" + with: + sarif_file: psalm.sarif + ref: ${{ inputs.ref }} + sha: ${{ env.CHECKED_OUT_SHA }} diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 4a67ff4ae..9b6ac37d9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,20 +1,20 @@ name: "Tests" on: + merge_group: pull_request: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" push: branches: - "v*.*" - - "master" - "feature/*" - paths-ignore: - - "docs/**" + +env: + # TODO: change to "stable" once 2.0.0 is released + # DRIVER_VERSION: "stable" + DRIVER_VERSION: "mongodb/mongo-php-driver@v2.x" jobs: phpunit: @@ -25,96 +25,85 @@ jobs: fail-fast: true matrix: os: - - "ubuntu-20.04" + - "ubuntu-24.04" php-version: - - "7.4" - - "8.0" - "8.1" - "8.2" + - "8.3" + - "8.4" + - "8.5" mongodb-version: - - "4.4" - driver-version: - - "stable" + - "8.0" topology: - - "server" + - "replica_set" include: - - os: "ubuntu-20.04" - php-version: "8.0" - mongodb-version: "6.0" - driver-version: "stable" - topology: "replica_set" - - os: "ubuntu-20.04" - php-version: "8.0" - mongodb-version: "6.0" - driver-version: "stable" + # Test additional topologies for MongoDB 8.0 + - os: "ubuntu-24.04" + php-version: "8.4" + mongodb-version: "8.0" + topology: "server" + - os: "ubuntu-24.04" + php-version: "8.4" + mongodb-version: "8.0" topology: "sharded_cluster" - - os: "ubuntu-20.04" - php-version: "8.0" - mongodb-version: "5.0" - driver-version: "stable" + # Test lowest server/php versions + - os: "ubuntu-22.04" + php-version: "8.1" + mongodb-version: "6.0" topology: "server" - - os: "ubuntu-20.04" - php-version: "8.0" - mongodb-version: "4.4" - driver-version: "stable" + - os: "ubuntu-22.04" + php-version: "8.1" + mongodb-version: "6.0" topology: "replica_set" - - os: "ubuntu-20.04" - php-version: "8.0" - mongodb-version: "4.4" - driver-version: "stable" + - os: "ubuntu-22.04" + php-version: "8.1" + mongodb-version: "6.0" topology: "sharded_cluster" steps: - name: "Checkout" - uses: "actions/checkout@v3" + uses: "actions/checkout@v5" with: fetch-depth: 2 + submodules: true + + - uses: actions/setup-python@v6 + with: + python-version: '3.13' - - id: setup-mongodb - uses: mongodb-labs/drivers-evergreen-tools@master + - name: "Set up MongoDB" + id: setup-mongodb + uses: ./tests/drivers-evergreen-tools with: version: ${{ matrix.mongodb-version }} topology: ${{ matrix.topology }} - - name: Setup cache environment - id: extcache - uses: shivammathur/cache-extensions@v1 + - name: "Setup PHP" + uses: "./.github/actions/setup" with: php-version: ${{ matrix.php-version }} - extensions: "mongodb-${{ matrix.driver-version }}" - key: "extcache-v1" + driver-version: ${{ env.DRIVER_VERSION }} + php-ini-values: "zend.assertions=1" + ignore-platform-req: ${{ matrix.php-version == '8.5' && 'true' || 'false' }} - - name: Cache extensions - uses: actions/cache@v2 - with: - path: ${{ steps.extcache.outputs.dir }} - key: ${{ steps.extcache.outputs.key }} - restore-keys: ${{ steps.extcache.outputs.key }} + - name: "Run PHPUnit" + run: "vendor/bin/phpunit --configuration phpunit.evergreen.xml --coverage-clover coverage.xml" + env: + XDEBUG_MODE: "coverage" + MONGODB_URI: ${{ steps.setup-mongodb.outputs.cluster-uri }} - - name: "Install PHP" - uses: "shivammathur/setup-php@v2" + - name: "Upload coverage report" + uses: codecov/codecov-action@v5 with: - php-version: "${{ matrix.php-version }}" - tools: "pecl" - extensions: "mongodb-${{ matrix.driver-version }}" - coverage: "none" - ini-values: "zend.assertions=1" - - - name: "Show driver information" - run: "php --ri mongodb" + disable_search: true + files: coverage.xml + flags: "${{ matrix.mongodb-version }}-${{ matrix.topology }}" + token: ${{ secrets.CODECOV_TOKEN }} - - name: "Cache dependencies installed with composer" - uses: "actions/cache@v2" + - name: Upload test results to Codecov + uses: codecov/test-results-action@v1 with: - path: "~/.composer/cache" - key: "php-${{ matrix.php-version }}-composer-locked-${{ hashFiles('composer.lock') }}" - restore-keys: "php-${{ matrix.php-version }}-composer-locked-" - - - name: "Install dependencies with composer" - run: "composer update --no-interaction --no-progress" - - - name: "Run PHPUnit" - run: "vendor/bin/simple-phpunit -v" - env: - SYMFONY_DEPRECATIONS_HELPER: 999999 - MONGODB_URI: ${{ steps.setup-mongodb.outputs.cluster-uri }} + disable_search: true + files: test-results.xml + flags: "${{ matrix.mongodb-version }}-${{ matrix.topology }}" + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.gitignore b/.gitignore index e8e8c841b..9a0a717e5 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,11 @@ phpcs.xml # psalm psalm.xml +# phpbench +.phpbench/ +phpbench.json + +# bref +.serverless/ + mongocryptd.pid diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..9aaaaeb89 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,6 @@ +[submodule "drivers-evergreen-tools"] + path = tests/drivers-evergreen-tools + url = https://github.com/mongodb-labs/drivers-evergreen-tools +[submodule "specifications"] + path = tests/specifications + url = https://github.com/mongodb/specifications diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c43f249d6..1aa330118 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,6 +14,7 @@ $ composer update In addition to installing project dependencies, Composer will check that the required extension version is installed. Directions for installing the extension may be found [here](https://php.net/manual/en/mongodb.installation.php). +Composer will also install the submodule required for running spec tests. Installation directions for Composer may be found in its [Getting Started](https://getcomposer.org/doc/00-intro.md) guide. @@ -26,16 +27,15 @@ Composer. The test suite may be executed with: -``` -$ vendor/bin/simple-phpunit +```console +$ composer run test ``` The `phpunit.xml.dist` file is used as the default configuration file for the test suite. In addition to various PHPUnit options, it defines environment variables such as `MONGODB_URI` and `MONGODB_DATABASE`. You may customize this configuration by creating your own `phpunit.xml` file based on the -`phpunit.xml.dist` file we provide. To run the tests in serverless mode, set the -`MONGODB_IS_SERVERLESS` environment variable to `on`. +`phpunit.xml.dist` file we provide. To run tests against a cluster that requires authentication, either include the credentials in the connection string (i.e. `MONGODB_URI`) or set the @@ -43,14 +43,6 @@ credentials in the connection string (i.e. `MONGODB_URI`) or set the Note that `MONGODB_USERNAME` and `MONGODB_PASSWORD` will override any credentials present in the connection string. -By default, the `simple-phpunit` binary chooses the correct PHPUnit version for -the PHP version you are running. To run tests against a specific PHPUnit -version, use the `SYMFONY_PHPUNIT_VERSION` environment variable: - -``` -$ SYMFONY_PHPUNIT_VERSION=7.5 vendor/bin/simple-phpunit -``` - ### Environment Variables The test suite references the following environment variables: @@ -66,30 +58,27 @@ The test suite references the following environment variables: `username` URI option for clients constructed by the test suite, which will override any credentials in the connection string itself. -The following environment variable is used for [stable API testing](https://github.com/mongodb/specifications/blob/master/source/versioned-api/tests/README.rst): +The following environment variable is used for [stable API testing](https://github.com/mongodb/specifications/blob/master/source/versioned-api/tests/README.md): * `API_VERSION`: If defined, this value will be used to construct a [`MongoDB\Driver\ServerApi`](https://www.php.net/manual/en/mongodb-driver-serverapi.construct.php), which will then be specified as the `serverApi` driver option for clients created by the test suite. -The following environment variable is used for [serverless testing](https://github.com/mongodb/specifications/blob/master/source/serverless-testing/README.rst): - - * `MONGODB_IS_SERVERLESS`: Specify a true boolean string - (see: [`FILTER_VALIDATE_BOOLEAN`](https://www.php.net/manual/en/filter.filters.validate.php)) - if `MONGODB_URI` points to a serverless instance. Defaults to false. - -The following environment variables are used for [load balancer testing](https://github.com/mongodb/specifications/blob/master/source/load-balancers/tests/README.rst): +The following environment variables are used for [load balancer testing](https://github.com/mongodb/specifications/blob/master/source/load-balancers/tests/README.md): * `MONGODB_SINGLE_MONGOS_LB_URI`: Connection string to a load balancer backed by a single mongos host. * `MONGODB_MULTI_MONGOS_LB_URI`: Connection string to a load balancer backed by multiple mongos hosts. -The following environment variables are used for [CSFLE testing](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst): +The following environment variables are used for [CSFLE testing](https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md): * `AWS_ACCESS_KEY_ID` * `AWS_SECRET_ACCESS_KEY` + * `AWS_TEMP_ACCESS_KEY_ID` + * `AWS_TEMP_SECRET_ACCESS_KEY` + * `AWS_TEMP_SESSION_TOKEN` * `AZURE_TENANT_ID` * `AZURE_CLIENT_ID` * `AZURE_CLIENT_SECRET` @@ -105,38 +94,84 @@ The following environment variables are used for [CSFLE testing](https://github. * `KMS_TLS_CA_FILE` * `KMS_TLS_CERTIFICATE_KEY_FILE` -## Checking coding standards +### Updating spec tests + +Tests from the MongoDB Specifications repository are included through a +submodule and updated automatically through Dependabot. To update tests +manually, switch to the `tests/specifications` directory and update the +repository to the appropriate commit. Remember to commit this change to the +library repository. + +#### Handling test failures on updates + +Failures on updates can occur for multiple reasons, and the remedy to this will +depend on the type of failure. Note that only tests for implemented +specifications are run in the test runner. + + * If a specification is not fully implemented (e.g. a recent change to the spec + has not been applied yet), skip the test in question with a reference to the + ticket that covers the change + * If a test fails because it uses features not yet implemented in the unified + test runner, skip the corresponding test with a reference to the ticket that + covers implementing the new features + * If the test failure points to a bug in the spec, consider the effort required + to fix the failure. If it's a small change, commit and push the fix directly + to the pull request. Otherwise, skip the test with a reference to a ticket to + fix the failing test. + +The goal is that the library passes tests with the latest spec version at all +times, either by implementing small changes quickly, or by skipping tests as +necessary. + +## Backward compatibility + +When submitting a PR, be mindful of our backward compatibility guarantees. Our +BC policy follows [Symfony's Backward Compatibility Promise](https://symfony.com/doc/current/contributing/code/bc.html). + +In short, this means we use semantic versioning and guarantee backward +compatibility on all minor releases. For a more detailed definition, refer to +the Symfony docs linked above. + +## Code quality + +Before submitting a pull request, please ensure that your code adheres to the +coding standards and passes static analysis checks. + +```console +$ composer run checks +``` + +### Coding standards The library's code is checked using [PHP_CodeSniffer](https://github.com/squizlabs/PHP_CodeSniffer), which is installed as a development dependency by Composer. To check the code for style errors, run the `phpcs` binary: - -``` +```console $ vendor/bin/phpcs ``` To automatically fix all fixable errors, use the `phpcbf` binary: -``` +```console $ vendor/bin/phpcbf ``` -## Running static analysis +### Static analysis The library uses [psalm](https://psalm.dev) to run static analysis on the code and ensure an additional level of type safety. New code is expected to adhere to level 1, with a baseline covering existing issues. To run static analysis checks, run the `psalm` binary: -``` +```console $ vendor/bin/psalm ``` To remove fixed errors from the baseline, you can use the `update-baseline` command-line argument: -``` +```console $ vendor/bin/psalm --update-baseline ``` @@ -144,182 +179,28 @@ Note that this will not add new errors to the baseline. New errors should be fixed instead of being added to the technical debt, but in case this isn't possible it can be added to the baseline using `set-baseline`: -``` +```console $ vendor/bin/psalm --set-baseline=psalm-baseline.xml ``` -## Documentation +### Refactoring -Documentation for the library lives in the `docs/` directory and is built with -tools in the related -[mongodb/docs-php-library](https://github.com/mongodb/docs-php-library) -repository. The tools repository is already configured to reference our sources. - -That said, any changes to the documentation should be tested locally before -committing. Follow the following steps to build the docs locally with the tools -repository: - - * Clone the - [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library) tools - repository. - * Install [giza](https://pypi.python.org/pypi/giza/), as noted in the tools - README. - * Sync your working copy of the documentation to the `source/` directory with - `rsync -a --delete /path/to/mongo-php-library/docs/ source/`. - * Build the documentation with `giza make publish`. You can suppress - informational log messages with the `--level warning` option. - * Generated documentation may be found in the `build/master/html` directory. - -## Creating a maintenance branch and updating master branch alias - -After releasing a new major or minor version (e.g. 1.9.0), a maintenance branch -(e.g. v1.9) should be created. Any development towards a patch release (e.g. -1.9.1) would then be done within that branch and any development for the next -major or minor release can continue in master. - -After creating a maintenance branch, the `extra.branch-alias.dev-master` field -in the master branch's `composer.json` file should be updated. For example, -after branching v1.9, `composer.json` in the master branch may still read: +The library uses [rector](https://getrector.com/) to refactor the code for new features. +To run automatic refactoring, use the `rector` command: -``` -"branch-alias": { - "dev-master": "1.9.x-dev" -} +```console +$ vendor/bin/rector ``` -The above would be changed to: +New rules can be added to the `rector.php` configuration file. -``` -"branch-alias": { - "dev-master": "1.10.x-dev" -} -``` +## Documentation -Commit this change: - -``` -$ git commit -m "Master is now 1.10-dev" composer.json -``` +Documentation for the library is maintained by the MongoDB docs team and is kept +in the [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library) +repository. ## Releasing -The follow steps outline the release process for a maintenance branch (e.g. -releasing the `vX.Y` branch as X.Y.Z). - -### Ensure PHP version compatibility - -Ensure that the library test suite completes on supported versions of PHP. - -### Transition JIRA issues and version - -All issues associated with the release version should be in the "Closed" state -and have a resolution of "Fixed". Issues with other resolutions (e.g. -"Duplicate", "Works as Designed") should be removed from the release version so -that they do not appear in the release notes. - -Check the corresponding ".x" fix version to see if it contains any issues that -are resolved as "Fixed" and should be included in this release version. - -Update the version's release date and status from the -[Manage Versions](https://jira.mongodb.org/plugins/servlet/project-config/PHPLIB/versions) -page. - -### Update version info - -The PHP library uses [semantic versioning](https://semver.org/). Do not break -backwards compatibility in a non-major release or your users will kill you. - -Before proceeding, ensure that the `master` branch is up-to-date with all code -changes in this maintenance branch. This is important because we will later -merge the ensuing release commits up to master with `--strategy=ours`, which -will ignore changes from the merged commits. - -### Tag release - -The maintenance branch's HEAD will be the target for our release tag: - -``` -$ git tag -a -m "Release X.Y.Z" X.Y.Z -``` - -### Push tags - -``` -$ git push --tags -``` - -### Merge the maintenance branch up to master - -``` -$ git checkout master -$ git merge vX.Y --strategy=ours -$ git push -``` - -The `--strategy=ours` option ensures that all changes from the merged commits -will be ignored. - -### Publish release notes - -The following template should be used for creating GitHub release notes via -[this form](https://github.com/mongodb/mongo-php-library/releases/new). - -``` -The PHP team is happy to announce that version X.Y.Z of the MongoDB PHP library is now available. - -**Release Highlights** - - - -A complete list of resolved issues in this release may be found at: -$JIRA_URL - -**Documentation** - -Documentation for this library may be found at: -https://mongodb.com/docs/php-library/current/ - -**Installation** - -This library may be installed or upgraded with: - - composer require mongodb/mongodb:X.Y.Z - -Installation instructions for the `mongodb` extension may be found in the [PHP.net documentation](https://php.net/manual/en/mongodb.installation.php). -``` - -The URL for the list of resolved JIRA issues will need to be updated with each -release. You may obtain the list from -[this form](https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=12483). - -If commits from community contributors were included in this release, append the -following section: - -``` -**Thanks** - -Thanks for our community contributors for this release: - - * [$CONTRIBUTOR_NAME](https://github.com/$GITHUB_USERNAME) -``` - -Release announcements should also be posted in the [MongoDB Product & Driver Announcements: Driver Releases](https://mongodb.com/community/forums/tags/c/announcements/driver-releases/110/php) forum and shared on Twitter. - -### Documentation Updates for New Major and Minor Versions - -New major and minor releases will also require documentation updates to other -projects: - - * Create a DOCSP ticket to add the new version to PHP's server and language - [compatibility tables](https://mongodb.com/docs/drivers/php/#compatibility) - in the driver docs portal. See - [mongodb/docs-ecosystem#642](https://github.com/mongodb/docs-ecosystem/pull/642) - for an example. - - * Create a DOCSP ticket to update the "current" and "upcoming" navigation links - in the library's [documentation](https://mongodb.com/docs/php-library/current). This - will require updating - [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library). - -These tasks can be initiated prior to tagging a new release to ensure that the -updated content becomes accessible soon after the release is published. +The releases are created by the maintainers of the library. The process is documented in +the [RELEASING.md](RELEASING.md) file. diff --git a/Makefile b/Makefile deleted file mode 100644 index a1d31b68f..000000000 --- a/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -.PHONY: composer test - -COMPOSER_ARGS=update --no-interaction --prefer-source - -composer: - @command -v composer >/dev/null 2>&1; \ - if test $$? -eq 0; then \ - composer $(COMPOSER_ARGS); \ - elif test -r composer.phar; then \ - php composer.phar $(COMPOSER_ARGS); \ - else \ - echo >&2 "Cannot find composer; aborting."; \ - false; \ - fi - -test: composer - vendor/bin/phpunit diff --git a/README.md b/README.md index 8b0fbf3da..643e539e2 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # MongoDB PHP Library -![Tests](https://github.com/mongodb/mongo-php-library/workflows/Tests/badge.svg) -![Coding Standards](https://github.com/mongodb/mongo-php-library/workflows/Coding%20Standards/badge.svg) +![Tests](https://github.com/mongodb/mongo-php-library/actions/workflows/tests.yml/badge.svg) +![Coding Standards](https://github.com/mongodb/mongo-php-library/actions/workflows/coding-standards.yml/badge.svg) This library provides a high-level abstraction around the lower-level [PHP driver](https://github.com/mongodb/mongo-php-driver) (`mongodb` extension). @@ -21,8 +21,8 @@ extension may be found in ## Documentation - - https://mongodb.com/docs/php-library/current - - https://mongodb.com/docs/ecosystem/drivers/php/ + - https://www.mongodb.com/docs/php-library/current/ + - https://www.mongodb.com/docs/drivers/php-drivers/ ## Installation @@ -44,6 +44,26 @@ that the `mongodb` extension be installed: Additional installation instructions for the extension may be found in its [PHP.net documentation](https://php.net/manual/en/mongodb.installation.php). +## Release Integrity + +Releases are created automatically and the resulting release tag is signed using +the [PHP team's GPG key](https://pgp.mongodb.com/php-driver.asc). To verify the +tag signature, download the key and import it using `gpg`: + +```shell +gpg --import php-driver.asc +``` + +Then, in a local clone, verify the signature of a given tag (e.g. `1.19.0`): + +```shell +git show --show-signature 1.19.0 +``` + +> [!NOTE] +> Composer does not support verifying signatures as part of its installation +> process. + ## Reporting Issues Issues pertaining to the library should be reported in the diff --git a/RELEASING.md b/RELEASING.md new file mode 100644 index 000000000..19506007a --- /dev/null +++ b/RELEASING.md @@ -0,0 +1,206 @@ +# Releasing + +The following steps outline the release process for both new minor versions (e.g. +releasing the `master` branch as X.Y.0) and patch versions (e.g. releasing the +`vX.Y` branch as X.Y.Z). + +The command examples below assume that the canonical "mongodb" repository has +the remote name "mongodb". You may need to adjust these commands if you've given +the remote another name (e.g. "upstream"). The "origin" remote name was not used +as it likely refers to your personal fork. + +It helps to keep your own fork in sync with the "mongodb" repository (i.e. any +branches and tags on the main repository should also exist in your fork). This +is left as an exercise to the reader. + +## Ensure PHP version compatibility + +Ensure that the library test suite completes on supported versions of PHP. + +## Transition JIRA issues and version + +All issues associated with the release version should be in the "Closed" state +and have a resolution of "Fixed". Issues with other resolutions (e.g. +"Duplicate", "Works as Designed") should be removed from the release version so +that they do not appear in the release notes. + +Check the corresponding ".x" fix version to see if it contains any issues that +are resolved as "Fixed" and should be included in this release version. + +Update the version's release date and status from the +[Manage Versions](https://jira.mongodb.org/plugins/servlet/project-config/PHPLIB/versions) +page. + +## Update version info + +The PHP library uses [semantic versioning](https://semver.org/). Do not break +backwards compatibility in a non-major release or your users will kill you. + +Before proceeding, ensure that the `master` branch is up-to-date with all code +changes in this maintenance branch. This is important because we will later +merge the ensuing release commits up to master with `--strategy=ours`, which +will ignore changes from the merged commits. + +## Update extension requirement + +In `composer.json`, ensure that the version of `ext-mongodb` is correct for +the library version being released. + +## Update CI matrices + +This is especially important before releasing a new minor version. + +If this is the first release of a minor version for the library, it is likely +following an extension release. The `vars` for calling `compile extension` from +`build-extension.yml` in the Evergreen configuration must be updated: + + * The `stable` task should specify no vars. + * The `lowest` task should specify `EXTENSION_VERSION` with the version that + was just released. + * The `next-stable` task should specify `EXTENSION_BRANCH` with the branch that + was just created. + * The `next-minor` task should specify `EXTENSION_BRANCH: master`. + +The `DRIVER_VERSION` environment variable for any GitHub Actions should also be +set to `stable`. + +After regenerating the Evergreen configuration, stage any modified files, +commit, and push: + +```console +$ git commit -m "Update composer.json and CI matrices for X.Y.Z" +$ git push mongodb +``` + +## Tag the release + +Create a tag for the release and push: + +```console +$ git tag -a -m "Release X.Y.Z" X.Y.Z +$ git push mongodb --tags +``` + +## Branch management + +### Creating a maintenance branch and updating master branch alias + +After releasing a new major or minor version (e.g. 1.9.0), a maintenance branch +(e.g. v1.9) should be created. Any development towards a patch release (e.g. +1.9.1) would then be done within that branch and any development for the next +major or minor release can continue in master. + +When work begins on a major new version, create a maintenance branch for the +last minor version and update the `extra.branch-alias.dev-master` field +in the master branch's `composer.json` file. For example, after branching v1.99, +`composer.json` in the master branch may still read: + +``` +"branch-alias": { + "dev-master": "1.x-dev" +} +``` + +The above would be changed to: + +``` +"branch-alias": { + "dev-master": "2.x-dev" +} +``` + +Commit this change: + +```console +$ git commit -m "Master is now 1.10-dev" composer.json +``` + +### After releasing a new minor version + +After a new minor version is released (i.e. `master` was tagged), a maintenance +branch should be created for future patch releases: + +```console +$ git checkout -b vX.Y +$ git push mongodb vX.Y +``` + +### After releasing a patch version + +If this was a patch release, the maintenance branch must be merged up to master: + +```console +$ git checkout master +$ git pull mongodb master +$ git merge vX.Y --strategy=ours +$ git push mongodb +``` + +The `--strategy=ours` option ensures that all changes from the merged commits +will be ignored. This is OK because we previously ensured that the `master` +branch was up-to-date with all code changes in this maintenance branch before +tagging. + + +## Publish release notes + +The following template should be used for creating GitHub release notes via +[this form](https://github.com/mongodb/mongo-php-library/releases/new). + +```markdown +The PHP team is happy to announce that version X.Y.Z of the MongoDB PHP library is now available. + +**Release Highlights** + + + +A complete list of resolved issues in this release may be found in [JIRA]($JIRA_URL). + +**Documentation** + +Documentation for this library may be found in the [PHP Library Manual](https://mongodb.com/docs/php-library/current/). + +**Installation** + +This library may be installed or upgraded with: + + composer require mongodb/mongodb:X.Y.Z + +Installation instructions for the `mongodb` extension may be found in the [PHP.net documentation](https://php.net/manual/en/mongodb.installation.php). +``` + +The URL for the list of resolved JIRA issues will need to be updated with each +release. You may obtain the list from +[this form](https://jira.mongodb.org/secure/ReleaseNote.jspa?projectId=12483). + +If commits from community contributors were included in this release, append the +following section: + +```markdown +**Thanks** + +Thanks for our community contributors for this release: + + * [$CONTRIBUTOR_NAME](https://github.com/$GITHUB_USERNAME) +``` + +Release announcements should also be posted in the [MongoDB Product & Driver Announcements: Driver Releases](https://mongodb.com/community/forums/tags/c/announcements/driver-releases/110/php) forum and shared on Twitter. + +## Documentation Updates for New Major and Minor Versions + +New major and minor releases will also require documentation updates to other +projects: + + * Create a DOCSP ticket to add the new version to PHP's server and language + [compatibility tables](https://www.mongodb.com/docs/drivers/php-drivers/#compatibility) + in the driver docs portal. See + [mongodb/docs-ecosystem#642](https://github.com/mongodb/docs-ecosystem/pull/642) + for an example. + + * Create a DOCSP ticket to update the "current" and "upcoming" navigation links + in the library's [documentation](https://mongodb.com/docs/php-library/current). This + will require updating + [mongodb/docs-php-library](https://github.com/mongodb/docs-php-library). + +These tasks can be initiated prior to tagging a new release to ensure that the +updated content becomes accessible soon after the release is published. diff --git a/UPGRADE-2.0.md b/UPGRADE-2.0.md new file mode 100644 index 000000000..516b57929 --- /dev/null +++ b/UPGRADE-2.0.md @@ -0,0 +1,83 @@ +UPGRADE FROM 1.x to 2.0 +======================== + + * Classes in the namespace `MongoDB\Operation\` are `final`. + * All methods in interfaces and classes now define a return type. + * The `MongoDB\ChangeStream::CURSOR_NOT_FOUND` constant is now private. + * The `MongoDB\Operation\Watch::FULL_DOCUMENT_DEFAULT` constant has been + removed. + * The `getNamespace` and `isGeoHaystack` methods have been removed from the + `MongoDB\Model\IndexInfo` class. + * The `maxScan`, `modifiers`, `oplogReplay`, and `snapshot` options for `find` + and `findOne` operations have been removed. + * The `MongoDB\Collection::mapReduce` method has been removed. Use + [aggregation pipeline](https://www.mongodb.com/docs/manual/reference/map-reduce-to-aggregation-pipeline/) + instead. + * The following classes and interfaces have been removed without replacement: + * `MongoDB\MapReduceResult` + * `MongoDB\Model\CollectionInfoCommandIterator` + * `MongoDB\Model\CollectionInfoIterator` + * `MongoDB\Model\DatabaseInfoIterator` + * `MongoDB\Model\DatabaseInfoLegacyIterator` + * `MongoDB\Model\IndexInfoIterator` + * `MongoDB\Model\IndexInfoIteratorIterator` + * `MongoDB\Operation\Executable` + * The `flags` and `autoIndexId` options for + `MongoDB\Database::createCollection()` have been removed. Additionally, the + `USE_POWER_OF_2_SIZES` and `NO_PADDING` constants in + `MongoDB\Operation\CreateCollection` have been removed. + +Operations with no result +------------------------- + +The following operations no longer return the raw command result. The return +type changed to `void`. In case of an error, an exception is thrown. + + * `MongoDB\Client`: `dropDatabase` + * `MongoDB\Collection`: `drop`, `dropIndex`, `dropIndexes`, `dropSearchIndex`, `rename` + * `MongoDB\Database`: `createCollection`, `drop`, `dropCollection`, `renameCollection` + * `MongoDB\Database::createEncryptedCollection()` returns the list of encrypted fields + +If you still need to access the raw command result, you can use a +[`CommandSubscriber`](https://www.php.net/manual/en/class.mongodb-driver-monitoring-commandsubscriber.php). + +GridFS +------ + + * The `md5` is no longer calculated when a file is uploaded to GridFS. + Applications that require a file digest should implement it outside GridFS + and store in metadata. + + ```php + $hash = hash_file('sha256', $filename); + $bucket->openUploadStream($fileId, ['metadata' => ['hash' => $hash]]); + ``` + + * The fields `contentType` and `aliases` are no longer stored in the `files` + collection. Applications that require this information should store it in + metadata. + + **Before:** + ```php + $bucket->openUploadStream($fileId, ['contentType' => 'image/png']); + ``` + + **After:** + ```php + $bucket->openUploadStream($fileId, ['metadata' => ['contentType' => 'image/png']]); + ``` + +UnsupportedException method removals +------------------------------------ + +The following methods have been removed from the +`MongoDB\Exception\UnsupportedException` class: + * `allowDiskUseNotSupported` + * `arrayFiltersNotSupported` + * `collationNotSupported` + * `explainNotSupported` + * `readConcernNotSupported` + * `writeConcernNotSupported` + +The remaining methods have been marked as internal and may be removed in a +future minor version. Only the class itself is covered by the BC promise. diff --git a/benchmark/composer.json b/benchmark/composer.json new file mode 100644 index 000000000..17fda3ef0 --- /dev/null +++ b/benchmark/composer.json @@ -0,0 +1,33 @@ +{ + "name": "mongodb/mongodb-benchmark", + "type": "project", + "repositories": [ + { + "type": "path", + "url": "../", + "symlink": true + } + ], + "replace": { + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*" + }, + "require": { + "php": ">=8.1", + "ext-pcntl": "*", + "amphp/parallel": "^2.2", + "mongodb/mongodb": "@dev", + "phpbench/phpbench": "^1.2" + }, + "autoload": { + "psr-4": { + "MongoDB\\Benchmark\\": "src/" + } + }, + "scripts": { + "benchmark": "phpbench run --report=aggregate" + }, + "config": { + "sort-packages": true + } +} diff --git a/benchmark/phpbench.json.dist b/benchmark/phpbench.json.dist new file mode 100644 index 000000000..8d00a7db5 --- /dev/null +++ b/benchmark/phpbench.json.dist @@ -0,0 +1,10 @@ +{ + "$schema": "./vendor/phpbench/phpbench/phpbench.schema.json", + "core.extensions": ["MongoDB\\Benchmark\\Extension\\MongoDBExtension"], + "runner.env_enabled_providers": ["mongodb","sampler","git","opcache","php","uname","unix_sysload"], + "runner.bootstrap": "vendor/autoload.php", + "runner.file_pattern": "*Bench.php", + "runner.path": "src", + "runner.php_config": { "memory_limit": "1G" }, + "runner.iterations": 3 +} diff --git a/benchmark/src/BSON/DocumentBench.php b/benchmark/src/BSON/DocumentBench.php new file mode 100644 index 000000000..1991cd614 --- /dev/null +++ b/benchmark/src/BSON/DocumentBench.php @@ -0,0 +1,91 @@ +has('qx3MigjubFSm'); + } + + public function benchCheckLast(): void + { + self::$document->has('Zz2MOlCxDhLl'); + } + + public function benchAccessFirst(): void + { + self::$document->get('qx3MigjubFSm'); + } + + public function benchAccessLast(): void + { + self::$document->get('Zz2MOlCxDhLl'); + } + + public function benchIteratorToArray(): void + { + iterator_to_array(self::$document); + } + + public function benchToPHPObject(): void + { + self::$document->toPHP(); + } + + public function benchToPHPObjectViaIteration(): void + { + $object = new stdClass(); + + foreach (self::$document as $key => $value) { + $object->$key = $value; + } + } + + public function benchToPHPArray(): void + { + self::$document->toPHP(['root' => 'array']); + } + + public function benchIteration(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (self::$document as $key => $value); + } + + public function benchIterationAsArray(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (self::$document->toPHP(['root' => 'array']) as $key => $value); + } + + public function benchIterationAsObject(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (self::$document->toPHP() as $key => $value); + } +} diff --git a/benchmark/src/BSON/PackedArrayBench.php b/benchmark/src/BSON/PackedArrayBench.php new file mode 100644 index 000000000..5f8bd6284 --- /dev/null +++ b/benchmark/src/BSON/PackedArrayBench.php @@ -0,0 +1,87 @@ +has(0); + } + + public function benchCheckLast(): void + { + self::$array->has(94354); + } + + public function benchAccessFirst(): void + { + self::$array->get(0); + } + + public function benchAccessLast(): void + { + self::$array->get(94354); + } + + public function benchIteratorToArray(): void + { + iterator_to_array(self::$array); + } + + public function benchToPHPArray(): void + { + self::$array->toPHP(); + } + + public function benchToPHPArrayViaIteration(): void + { + $array = []; + + foreach (self::$array as $key => $value) { + $array[$key] = $value; + } + } + + public function benchIteration(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (self::$array as $key => $value); + } + + public function benchIterationAfterIteratorToArray(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (iterator_to_array(self::$array) as $key => $value); + } + + public function benchIterationAsArray(): void + { + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + foreach (self::$array->toPHP() as $key => $value); + } +} diff --git a/benchmark/src/DriverBench/Amp/ExportFileTask.php b/benchmark/src/DriverBench/Amp/ExportFileTask.php new file mode 100644 index 000000000..8600cd671 --- /dev/null +++ b/benchmark/src/DriverBench/Amp/ExportFileTask.php @@ -0,0 +1,25 @@ +files, $this->filter, $this->options); + + return $this->files; + } +} diff --git a/benchmark/src/DriverBench/Amp/ImportFileTask.php b/benchmark/src/DriverBench/Amp/ImportFileTask.php new file mode 100644 index 000000000..8db174409 --- /dev/null +++ b/benchmark/src/DriverBench/Amp/ImportFileTask.php @@ -0,0 +1,23 @@ +files); + + return $this->files; + } +} diff --git a/benchmark/src/DriverBench/BSONMicroBench.php b/benchmark/src/DriverBench/BSONMicroBench.php new file mode 100644 index 000000000..a1840c925 --- /dev/null +++ b/benchmark/src/DriverBench/BSONMicroBench.php @@ -0,0 +1,54 @@ +__toString(); + } + } + + /** @param array{bson:string} $params */ + #[ParamProviders('provideParams')] + public function benchDecoding(array $params): void + { + $bson = $params['bson']; + for ($i = 0; $i < 10_000; $i++) { + Document::fromBSON($bson); + } + } + + public static function provideParams(): Generator + { + $cases = [ + 'flat' => Data::FLAT_BSON_PATH, + 'deep' => Data::DEEP_BSON_PATH, + 'full' => Data::FULL_BSON_PATH, + ]; + + foreach ($cases as $name => $path) { + yield $name => [ + 'document' => $document = Document::fromJSON(file_get_contents($path)), + 'bson' => (string) $document, + ]; + } + } +} diff --git a/benchmark/src/DriverBench/GridFSBench.php b/benchmark/src/DriverBench/GridFSBench.php new file mode 100644 index 000000000..646f26f39 --- /dev/null +++ b/benchmark/src/DriverBench/GridFSBench.php @@ -0,0 +1,67 @@ +bucket->uploadFromStream('test', $this->stream); + } + + public function beforeUpload(): void + { + $database = Utils::getDatabase(); + $database->drop(); + + $this->bucket = $database->selectGridFSBucket(); + // Init the GridFS bucket + $this->bucket->uploadFromStream('init', Data::getStream(1)); + // Prepare the 50MB stream to upload + $this->stream = Data::getStream(50 * 1024 * 1024); + } + + /** @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#gridfs-download */ + #[BeforeMethods('beforeDownload')] + public function benchDownload(): void + { + $this->bucket->downloadToStream($this->id, $this->stream); + } + + public function beforeDownload(): void + { + $database = Utils::getDatabase(); + $database->drop(); + + $this->bucket = $database->selectGridFSBucket(); + // Upload a 50MB file + $this->id = $this->bucket->uploadFromStream('init', Data::getStream(50 * 1024 * 1024)); + // Prepare the stream to receive the download + $this->stream = Data::getStream(0); + } + + public function afterAll(): void + { + unset($this->bucket, $this->stream, $this->id); + Utils::getDatabase()->drop(); + } +} diff --git a/benchmark/src/DriverBench/MultiDocBench.php b/benchmark/src/DriverBench/MultiDocBench.php new file mode 100644 index 000000000..b569f9ca5 --- /dev/null +++ b/benchmark/src/DriverBench/MultiDocBench.php @@ -0,0 +1,99 @@ +find([], $params['options']) as $document); + } + + public function beforeFindMany(): void + { + $collection = Utils::getCollection(); + $collection->drop(); + + $tweet = Data::readJsonFile(Data::TWEET_FILE_PATH); + $documents = array_fill(0, 9_999, $tweet); + $collection->insertMany($documents); + } + + public static function provideFindManyParams(): Generator + { + yield 'Driver default typemap' => [ + 'options' => [], + ]; + + yield 'Raw BSON' => [ + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + /** + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#small-doc-bulk-insert + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#large-doc-bulk-insert + * @param array{documents: array} $params + */ + #[BeforeMethods('beforeBulkInsert')] + #[ParamProviders('provideBulkInsertParams')] + public function benchBulkInsert(array $params): void + { + $collection = Utils::getCollection(); + + // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedForeach + // phpcs:ignore Generic.ControlStructures.InlineControlStructure.NotAllowed + $collection->insertMany($params['documents']); + } + + public function beforeBulkInsert(): void + { + $database = Utils::getDatabase(); + $database->dropCollection(Utils::getCollectionName()); + $database->createCollection(Utils::getCollectionName()); + } + + public static function provideBulkInsertParams(): Generator + { + yield 'Small doc' => [ + 'documents' => array_fill(0, 9_999, Data::readJsonFile(Data::SMALL_FILE_PATH)), + ]; + + yield 'Small BSON doc' => [ + 'documents' => array_fill(0, 9_999, Document::fromJSON(file_get_contents(Data::SMALL_FILE_PATH))), + ]; + + yield 'Large doc' => [ + 'documents' => array_fill(0, 9, Data::readJsonFile(Data::LARGE_FILE_PATH)), + ]; + + yield 'Large BSON doc' => [ + 'documents' => array_fill(0, 9, Document::fromJSON(file_get_contents(Data::LARGE_FILE_PATH))), + ]; + } +} diff --git a/benchmark/src/DriverBench/ParallelGridFSBench.php b/benchmark/src/DriverBench/ParallelGridFSBench.php new file mode 100644 index 000000000..ba0dc8713 --- /dev/null +++ b/benchmark/src/DriverBench/ParallelGridFSBench.php @@ -0,0 +1,142 @@ +drop(); + + foreach (self::getFileNames() as $file) { + unlink($file); + } + } + + /** + * GridFS multi-file upload + * + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#gridfs-multi-file-upload + */ + #[BeforeMethods('beforeUpload')] + public function benchUpload(): void + { + $pids = []; + foreach (self::getFileNames() as $file) { + $pid = pcntl_fork(); + if ($pid === 0) { + Utils::getDatabase()->selectGridFSBucket()->uploadFromStream(basename($file), fopen($file, 'r')); + + // Exit the child process + exit(0); + } + + if ($pid === -1) { + throw new RuntimeException('Failed to fork'); + } + + // Keep the forked process id to wait for it later + $pids[$pid] = true; + } + + // Wait for all child processes to finish + while ($pids !== []) { + $pid = pcntl_waitpid(-1, $status); + unset($pids[$pid]); + } + } + + public function beforeUpload(): void + { + foreach (self::getFileNames() as $file) { + stream_copy_to_stream(Data::getStream(5 * 1024 * 1024), fopen($file, 'w')); + } + + $database = Utils::getDatabase(); + $database->drop(); + + $bucket = $database->selectGridFSBucket(); + $bucket->uploadFromStream('init', Data::getStream(1)); + + Utils::reset(); + } + + /** + * GridFS multi-file download + * + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#gridfs-multi-file-download + */ + #[BeforeMethods('beforeDownload')] + public function benchDownload(): void + { + $pids = []; + foreach (self::getFileNames() as $file) { + $pid = pcntl_fork(); + if ($pid === 0) { + $stream = Utils::getDatabase() + ->selectGridFSBucket() + ->openDownloadStreamByName(basename($file)); + stream_copy_to_stream($stream, fopen($file, 'w')); + + // Exit the child process + exit(0); + } + + if ($pid === -1) { + throw new RuntimeException('Failed to fork'); + } + + // Keep the forked process id to wait for it later + $pids[$pid] = true; + } + + // Wait for all child processes to finish + while ($pids !== []) { + $pid = pcntl_waitpid(-1, $status); + unset($pids[$pid]); + } + } + + public function beforeDownload(): void + { + // Initialize the GridFS bucket with the files + $this->beforeUpload(); + $this->benchUpload(); + } + + private static function getFileNames(): array + { + $tempDir = sys_get_temp_dir() . '/mongodb-php-benchmark'; + if (! is_dir($tempDir)) { + mkdir($tempDir); + } + + return array_map( + static fn (int $i) => sprintf('%s/file%02d.txt', $tempDir, $i), + range(0, 49), + ); + } +} diff --git a/benchmark/src/DriverBench/ParallelMultiFileExportBench.php b/benchmark/src/DriverBench/ParallelMultiFileExportBench.php new file mode 100644 index 000000000..fb02c2535 --- /dev/null +++ b/benchmark/src/DriverBench/ParallelMultiFileExportBench.php @@ -0,0 +1,219 @@ +drop(); + + $doc = Document::fromJSON(file_get_contents(Data::LDJSON_FILE_PATH)); + Utils::getCollection()->insertMany(array_fill(0, 500_000, $doc)); + } + + public static function afterClass(): void + { + Utils::getDatabase()->drop(); + } + + public function afterIteration(): void + { + foreach (self::getFileNames() as $file) { + if (file_exists($file)) { + unlink($file); + } + } + } + + /** + * Using a single thread to export multiple files. + * By executing a single Find command for multiple files, we can reduce the number of roundtrips to the server. + * + * @param array{chunkSize:int} $params + */ + #[ParamProviders(['provideChunkParams'])] + public function benchSequential(array $params): void + { + foreach (array_chunk(self::getFileNames(), $params['chunkSize']) as $i => $files) { + self::exportFile($files, [], [ + 'limit' => 5_000 * $params['chunkSize'], + 'skip' => 5_000 * $params['chunkSize'] * $i, + ]); + } + } + + /** + * Using multiple forked threads + * + * @param array{chunk:int} $params + */ + #[ParamProviders(['provideChunkParams'])] + public function benchFork(array $params): void + { + $pids = []; + + // Reset to ensure that the existing libmongoc client (via the Manager) is not re-used by the child + // process. When the child process constructs a new Manager, the differing PID will result in creation + // of a new libmongoc client. + Utils::reset(); + + // Create a child process for each chunk of files + foreach (array_chunk(self::getFileNames(), $params['chunkSize']) as $i => $files) { + $pid = pcntl_fork(); + if ($pid === 0) { + self::exportFile($files, [], [ + 'limit' => 5_000 * $params['chunkSize'], + 'skip' => 5_000 * $params['chunkSize'] * $i, + ]); + + // Exit the child process + exit(0); + } + + if ($pid === -1) { + throw new RuntimeException('Failed to fork'); + } + + // Keep the forked process id to wait for it later + $pids[$pid] = true; + } + + // Wait for all child processes to finish + while ($pids !== []) { + $pid = pcntl_waitpid(-1, $status); + unset($pids[$pid]); + } + } + + /** + * Using amphp/parallel with worker pool + * + * @param array{chunkSize:int} $params + */ + #[ParamProviders(['provideChunkParams'])] + public function benchAmpWorkers(array $params): void + { + $workerPool = new ContextWorkerPool(ceil(100 / $params['chunkSize']), new ContextWorkerFactory()); + + $futures = []; + foreach (array_chunk(self::getFileNames(), $params['chunkSize']) as $i => $files) { + $futures[] = $workerPool->submit( + new ExportFileTask( + files: $files, + options: [ + 'limit' => 5_000 * $params['chunkSize'], + 'skip' => 5_000 * $params['chunkSize'] * $i, + ], + ), + )->getFuture(); + } + + foreach (Future::iterate($futures) as $future) { + $future->await(); + } + } + + public static function provideChunkParams(): Generator + { + yield '100 chunks' => ['chunkSize' => 1]; + yield '25 chunks' => ['chunkSize' => 4]; + yield '10 chunks' => ['chunkSize' => 10]; + } + + /** + * Export a query to a file + */ + public static function exportFile(array|string $files, array $filter = [], array $options = []): void + { + $options += [ + // bson typemap is faster on query result, but slower to JSON encode + 'typeMap' => ['root' => 'array'], + // Excludes _id field to be identical to fixtures data + 'projection' => ['_id' => 0], + 'sort' => ['_id' => 1], + ]; + $cursor = Utils::getCollection()->find($filter, $options); + $cursor->rewind(); + + foreach ((array) $files as $file) { + // Aggregate file in memory to reduce filesystem operations + $data = ''; + for ($i = 0; $i < 5_000; $i++) { + $document = $cursor->current(); + // Cursor exhausted + if (! $document) { + break; + } + + // We don't use MongoDB\BSON\Document::toCanonicalExtendedJSON() because + // it is slower than json_encode() on an array. + $data .= json_encode($document) . "\n"; + $cursor->next(); + } + + // Write file in a single operation + file_put_contents($file, $data); + } + } + + /** + * Using a method to regenerate the file names because we cannot cache the result of the method in a static + * property. The benchmark runner will call the method in a different process, so the static property will not be + * populated. + */ + private static function getFileNames(): array + { + $tempDir = sys_get_temp_dir() . '/mongodb-php-benchmark'; + if (! is_dir($tempDir)) { + mkdir($tempDir); + } + + return array_map( + static fn (int $i) => sprintf('%s/%03d.txt', $tempDir, $i), + range(0, 99), + ); + } +} diff --git a/benchmark/src/DriverBench/ParallelMultiFileImportBench.php b/benchmark/src/DriverBench/ParallelMultiFileImportBench.php new file mode 100644 index 000000000..2e2159f8b --- /dev/null +++ b/benchmark/src/DriverBench/ParallelMultiFileImportBench.php @@ -0,0 +1,211 @@ +drop(); + $database->createCollection(Utils::getCollectionName()); + } + + /** + * Using library's Collection::insertMany in a single thread + */ + public function benchInsertMany(): void + { + $collection = Utils::getCollection(); + foreach (self::getFileNames() as $file) { + $docs = []; + // Read file contents into BSON documents + $fh = fopen($file, 'r'); + try { + while (($line = fgets($fh)) !== false) { + if ($line !== '') { + $docs[] = Document::fromJSON($line); + } + } + } finally { + fclose($fh); + } + + // Insert documents in bulk + $collection->insertMany($docs); + } + } + + /** + * Using multiple forked threads. The number of threads is controlled by the "chunk" parameter, + * which is the number of files to import in each thread. + * + * @param array{chunkSize:int} $params + */ + #[ParamProviders(['provideChunkParams'])] + public function benchFork(array $params): void + { + $pids = []; + + // Reset to ensure that the existing libmongoc client (via the Manager) is not re-used by the child + // process. When the child process constructs a new Manager, the differing PID will result in creation + // of a new libmongoc client. + Utils::reset(); + + foreach (array_chunk(self::getFileNames(), $params['chunkSize']) as $files) { + $pid = pcntl_fork(); + if ($pid === 0) { + self::importFile($files); + + // Exit the child process + exit(0); + } + + if ($pid === -1) { + throw new RuntimeException('Failed to fork'); + } + + // Keep the forked process id to wait for it later + $pids[$pid] = true; + } + + // Wait for all child processes to finish + while ($pids !== []) { + $pid = pcntl_waitpid(-1, $status); + unset($pids[$pid]); + } + } + + /** + * Using amphp/parallel with worker pool + * + * @param array{chunkSize:int} $params + */ + #[ParamProviders(['provideChunkParams'])] + public function benchAmpWorkers(array $params): void + { + $workerPool = new ContextWorkerPool(ceil(100 / $params['chunkSize']), new ContextWorkerFactory()); + + $futures = array_map( + fn ($files) => $workerPool->submit(new ImportFileTask($files))->getFuture(), + array_chunk(self::getFileNames(), $params['chunkSize']), + ); + + foreach (Future::iterate($futures) as $future) { + $future->await(); + } + + $workerPool->shutdown(); + } + + public function provideChunkParams(): Generator + { + yield '100 chunks' => ['chunkSize' => 1]; + yield '25 chunks' => ['chunkSize' => 4]; + yield '10 chunks' => ['chunkSize' => 10]; + } + + /** + * We benchmarked the following solutions to read a file line by line: + * - file + * - SplFileObject + * - fgets + * - stream_get_line 🏆 + */ + public static function importFile(string|array $files): void + { + $namespace = sprintf('%s.%s', Utils::getDatabaseName(), Utils::getCollectionName()); + + $bulkWrite = new BulkWrite(); + foreach ((array) $files as $file) { + $fh = fopen($file, 'r'); + try { + while (($line = stream_get_line($fh, 10_000, "\n")) !== false) { + $bulkWrite->insert(Document::fromJSON($line)); + } + } finally { + fclose($fh); + } + } + + Utils::getClient()->getManager()->executeBulkWrite($namespace, $bulkWrite); + } + + /** + * Using a method to regenerate the file names because we cannot cache the result of the method in a static + * property. The benchmark runner will call the method in a different process, so the static property will not be + * populated. + */ + private static function getFileNames(): array + { + $tempDir = sys_get_temp_dir() . '/mongodb-php-benchmark'; + if (! is_dir($tempDir)) { + mkdir($tempDir); + } + + return array_map( + static fn (int $i) => sprintf('%s/%03d.txt', $tempDir, $i), + range(0, 99), + ); + } +} diff --git a/benchmark/src/DriverBench/SingleDocBench.php b/benchmark/src/DriverBench/SingleDocBench.php new file mode 100644 index 000000000..ee49eb002 --- /dev/null +++ b/benchmark/src/DriverBench/SingleDocBench.php @@ -0,0 +1,111 @@ +drop(); + } + + /** @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#run-command */ + public function benchRunCommand(): void + { + $manager = Utils::getClient()->getManager(); + $database = Utils::getDatabase(); + + for ($i = 0; $i < 10_000; $i++) { + $manager->executeCommand($database, new Command(['hello' => true])); + } + } + + /** + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#find-one-by-id + * @param array{options: array} $params + */ + #[BeforeMethods('beforeFindOneById')] + #[ParamProviders('provideFindOneByIdParams')] + public function benchFindOneById(array $params): void + { + $collection = Utils::getCollection(); + + for ($id = 1; $id <= 10_000; $id++) { + $collection->findOne(['_id' => $id], $params['options']); + } + } + + public function beforeFindOneById(): void + { + $tweet = Data::readJsonFile(Data::TWEET_FILE_PATH); + $docs = array_map(fn ($id) => $tweet + ['_id' => $id], range(1, 10_000)); + Utils::getCollection()->insertMany($docs); + } + + public static function provideFindOneByIdParams(): Generator + { + yield 'Driver default typemap' => [ + 'options' => [], + ]; + + yield 'Raw BSON' => [ + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + /** + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#small-doc-insertone + * @see https://github.com/mongodb/specifications/blob/ddfc8b583d49aaf8c4c19fa01255afb66b36b92e/source/benchmarking/benchmarking.rst#large-doc-bulk-insert + * @param array{document: object|array, repeat: int, options?: array} $params + */ + #[ParamProviders('provideInsertOneParams')] + public function benchInsertOne(array $params): void + { + $collection = Utils::getCollection(); + + for ($i = $params['repeat']; $i > 0; $i--) { + $collection->insertOne($params['document']); + } + } + + public static function provideInsertOneParams(): Generator + { + yield 'Small doc' => [ + 'document' => Data::readJsonFile(Data::SMALL_FILE_PATH), + 'repeat' => 10_000, + ]; + + yield 'Small BSON doc' => [ + 'document' => Document::fromJSON(file_get_contents(Data::SMALL_FILE_PATH)), + 'repeat' => 10_000, + ]; + + yield 'Large doc' => [ + 'document' => Data::readJsonFile(Data::LARGE_FILE_PATH), + 'repeat' => 10, + ]; + + yield 'Large BSON doc' => [ + 'document' => Document::fromJSON(file_get_contents(Data::LARGE_FILE_PATH)), + 'repeat' => 10, + ]; + } +} diff --git a/benchmark/src/Extension/EnvironmentProvider.php b/benchmark/src/Extension/EnvironmentProvider.php new file mode 100644 index 000000000..c313fab4d --- /dev/null +++ b/benchmark/src/Extension/EnvironmentProvider.php @@ -0,0 +1,73 @@ +getManager(); + + return new Information('mongodb', array_merge( + $this->getUri(), + $this->getServerInfo($manager), + $this->getBuildInfo($manager), + )); + } + + private function getUri(): array + { + $uri = Utils::getUri(); + // Obfuscate the password in the URI + $uri = str_replace(':' . parse_url($uri, PHP_URL_PASS) . '@', ':***@', $uri); + + return ['uri' => $uri]; + } + + private function getServerInfo(Manager $manager): array + { + $topology = match ($manager->selectServer()->getType()) { + Server::TYPE_STANDALONE => 'standalone', + Server::TYPE_LOAD_BALANCER => 'load-balanced', + Server::TYPE_RS_PRIMARY => 'replica-set', + Server::TYPE_MONGOS => 'sharded', + default => 'unknown', + }; + + return ['topology' => $topology]; + } + + private function getBuildInfo(Manager $manager): array + { + $buildInfo = $manager->executeCommand( + Utils::getDatabaseName(), + new Command(['buildInfo' => 1]), + ['readPreference' => new ReadPreference(ReadPreference::PRIMARY)], + )->toArray()[0]; + + return [ + 'version' => $buildInfo->version ?? 'unknown server version', + 'modules' => implode(', ', $buildInfo->modules ?? []), + ]; + } +} diff --git a/benchmark/src/Extension/EvergreenReport.php b/benchmark/src/Extension/EvergreenReport.php new file mode 100644 index 000000000..40ea06113 --- /dev/null +++ b/benchmark/src/Extension/EvergreenReport.php @@ -0,0 +1,112 @@ +setDefaults([self::PARAM_PATH => '.phpbench/results.json']); + $options->setAllowedTypes(self::PARAM_PATH, ['string']); + + SymfonyOptionsResolverCompat::setInfos($options, [self::PARAM_PATH => 'Path to output file']); + } + + public function generate(SuiteCollection $collection, Config $config): Reports + { + $tests = []; + + foreach ($collection as $suite) { + assert($suite instanceof Suite); + foreach ($suite as $benchmark) { + foreach ($benchmark as $subject) { + foreach ($subject->getVariants() as $variant) { + $stats = $variant->getStats()->getStats(); + $name = sprintf('%s::%s', $benchmark->getName(), $subject->getName()); + if ($variant->getParameterSet()->getName()) { + $name .= '#' . $variant->getParameterSet()->getName(); + } + + $tests[] = [ + 'info' => ['test_name' => $name], + 'created_at' => date(DATE_ATOM), + 'completed_at' => date(DATE_ATOM), + 'metrics' => [ + [ + 'name' => 'Avg. Time', + 'type' => 'MEAN', + 'value' => $stats['mean'], + ], + [ + 'name' => 'Min. Time', + 'type' => 'MIN', + 'value' => $stats['min'], + ], + [ + 'name' => 'Max. Time', + 'type' => 'MAX', + 'value' => $stats['max'], + ], + [ + 'name' => 'Std. Deviation', + 'type' => 'STANDARD_DEVIATION', + 'value' => $stats['stdev'], + ], + ], + ]; + } + } + } + } + + $outputPath = Path::makeAbsolute($config[self::PARAM_PATH], $this->cwd); + $outputDir = dirname($outputPath); + + if (! file_exists($outputDir)) { + if (! @mkdir($outputDir, 0777, true)) { + throw new RuntimeException(sprintf( + 'Could not create directory "%s"', + $outputDir, + )); + } + } + + if (false === file_put_contents($outputPath, json_encode($tests, JSON_PRETTY_PRINT) . "\n")) { + throw new RuntimeException(sprintf( + 'Could not write report to file "%s"', + $outputPath, + )); + } + + // Return an empty report to not confuse the report renderer + return Reports::empty(); + } +} diff --git a/benchmark/src/Extension/MongoDBExtension.php b/benchmark/src/Extension/MongoDBExtension.php new file mode 100644 index 000000000..ddddb627c --- /dev/null +++ b/benchmark/src/Extension/MongoDBExtension.php @@ -0,0 +1,35 @@ +register( + EnvironmentProvider::class, + fn (Container $container) => new EnvironmentProvider(), + [RunnerExtension::TAG_ENV_PROVIDER => ['name' => 'mongodb']], + ); + + $container->register( + EvergreenReport::class, + fn (Container $container) => new EvergreenReport( + $container->getParameter(CoreExtension::PARAM_WORKING_DIR), + ), + [ReportExtension::TAG_REPORT_GENERATOR => ['name' => 'evergreen']], + ); + } + + public function configure(OptionsResolver $resolver): void + { + // No config + } +} diff --git a/benchmark/src/Fixtures/Data.php b/benchmark/src/Fixtures/Data.php new file mode 100644 index 000000000..42a0fb72f --- /dev/null +++ b/benchmark/src/Fixtures/Data.php @@ -0,0 +1,42 @@ +toPHP(['root' => 'stdClass', 'array' => 'array', 'document' => 'stdClass']); + } + + public function encode(mixed $value): Document + { + if (! is_object($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return Document::fromPHP($value); + } +} diff --git a/benchmark/src/Fixtures/data/deep_bson.json b/benchmark/src/Fixtures/data/deep_bson.json new file mode 100644 index 000000000..c7c45d09b --- /dev/null +++ b/benchmark/src/Fixtures/data/deep_bson.json @@ -0,0 +1 @@ +{"right": {"right": {"right": {"right": {"right": {"right": "EIXQykWD", "left": "VRVcZnIk"}, "left": {"right": "oRbShlgw", "left": "ojDbdtPA"}}, "left": {"right": {"right": "CzHfouDM", "left": "GQXwTsrM"}, "left": {"right": "JEQOwZLQ", "left": "LgWNfbhn"}}}, "left": {"right": {"right": {"right": "khozCROb", "left": "rTMhLQtj"}, "left": {"right": "iJBmstFH", "left": "LvtNlLgv"}}, "left": {"right": {"right": "UYcmaFMh", "left": "tuanWIAx"}, "left": {"right": "WwptcESl", "left": "ixOtlORA"}}}}, "left": {"right": {"right": {"right": {"right": "dicfQcdX", "left": "xYMiAHwv"}, "left": {"right": "WbMXdChI", "left": "oWLWuHVi"}}, "left": {"right": {"right": "wRodFaZo", "left": "aTcaLCnp"}, "left": {"right": "ijcnduCw", "left": "KFbXjDfX"}}}, "left": {"right": {"right": {"right": "zzEcxhSZ", "left": "bWSAvFzm"}, "left": {"right": "mQjffcog", "left": "hDbObPsH"}}, "left": {"right": {"right": "LOxUOaEy", "left": "DzfmhHAL"}, "left": {"right": "oGVrSHkQ", "left": "GGTRCnke"}}}}}, "left": {"right": {"right": {"right": {"right": {"right": "PHHXnUgq", "left": "xkZzQsMR"}, "left": {"right": "bSpBOtXU", "left": "lmRMwRFf"}}, "left": {"right": {"right": "ReWinBoZ", "left": "TOlxDiMX"}, "left": {"right": "KuopgxIC", "left": "iWKMSGCV"}}}, "left": {"right": {"right": {"right": "pydwIHaq", "left": "KMJNfqEW"}, "left": {"right": "JtXAjgEq", "left": "YmMYBRkX"}}, "left": {"right": {"right": "wmdxiSSk", "left": "sABNQDEX"}, "left": {"right": "YnINlnHU", "left": "qMdmLtPq"}}}}, "left": {"right": {"right": {"right": {"right": "rHcvRSjR", "left": "uNIIxBLx"}, "left": {"right": "atKAMwgJ", "left": "XWFVhrTV"}}, "left": {"right": {"right": "HIQHjrLx", "left": "YughZJOY"}, "left": {"right": "qYeyvcvK", "left": "mzHqtLHA"}}}, "left": {"right": {"right": {"right": "BlifVvAC", "left": "BvZTPsYB"}, "left": {"right": "XPHqYTOu", "left": "qUNMEFgS"}}, "left": {"right": {"right": "zkxbAuKr", "left": "YpAkENEL"}, "left": {"right": "wuBwgsDI", "left": "ONIZsGFD"}}}}}} diff --git a/benchmark/src/Fixtures/data/flat_bson.json b/benchmark/src/Fixtures/data/flat_bson.json new file mode 100644 index 000000000..6b0fe3fc4 --- /dev/null +++ b/benchmark/src/Fixtures/data/flat_bson.json @@ -0,0 +1 @@ +{"pfZSRHnn":{"$numberDouble":"4837384839313709000"},"XeRkAyCq":{"$numberInt":"12"},"oRWMNJTE":"pbJrsZrkbKNGqDNwPPrxpeWVBRWgREWtfaZZCrLkBUmdAXdyaPcrMwhDzBXCpZtXjloYMgNgfIfEyrvq","hnVgYIQi":"RwMsHQcqgXvkmNpPIndeDusPOwRfluOOvXnTxwpiZvzkMWQaEQXjyfjoSMcqpeGmULGQlhWvHxmxFNqe","cxOHMeDJ":"RTMMHBNebriQZeGrExTKSJsMRbeKcIzaEqpMwOlVfgNreVIHDkLFBjDWJMubctZtjlozdPqZWyhillEw","MeUYSkPS":"sunCadRBarhKilkQEMRPVYQULkReALfyzmotkXUeDMcEiRvxgqyBiTQhmuoFXQxMotjJFjYTMrEyZFEM","IsorvnMR":true,"vvUeXASH":true,"zEgGhhZf":false,"SUWXijHT":{"$numberInt":"13"},"HicJbMpj":true,"ijwXMKqI":{"$numberInt":"30"},"RemSsnnR":false,"McpOBmaR":"fKGDZnXeXYTKnMITrDZrwVjNojiGCkNHtnWSxYwAiOtmIaVzDzQfifoorkYKjESnrauTyfClKjyczlPG","ddVenEkK":{"$numberInt":"41"},"CqCssWxW":"iXTkPsEpuEDVFxDIJsZVLuzUHCNfIiCQXYFjFBNHabJBENLnGhIsjUaLMWUaUqUiUiHRcPycHhHfrNdm","taoNnQYY":{"$numberDouble":"6412916066386615000"},"MXMxLVBk":{"$numberInt":"-83"},"yeTUgNrU":false,"vkEDWgmN":"HMobufHXBFQvVXzYiYqKpWMTqviPqPJtGEEVsebUiwVSbvYExShHJSVrwBEcovhlVhhAJKERrsmaipwn","TgSwBbgp":{"$numberDouble":"1912191031177068500"},"FDYGeSiR":{"$numberLong":"487277598556628711"},"xrzGnsEK":"UgqXkyWUceZzifQuDdMLiOtOwnPoolsdcjfsWkbQfWfOzgPpXEbefKjgRCxjCLgDRqpHypvyclTHDoxD","VVvwKVRG":{"$numberInt":"-81"},"TmUnYUrv":{"$numberLong":"7726451032369080086"},"OCsIhHxq":"VxBUZZSjnrXwRplwxRSJGwkFBFVpGvLFpWTFrmSJNJcgQQUFmlEYUcFnXOdJrOFQCaIlmBMzoihXdofI","pKjOghFa":true,"VCSKFCoE":{"$numberLong":"434237187180930726"},"yKfZnGKG":{"$numberDouble":"6813471041107657000"},"UKwbAKGw":{"$numberInt":"-32"},"iFFGfTXc":"hfiYFaVdPKSVRQlUccqrBdSxbXlRNDDaUCQsFmuXkUSJLPshjiFbjnmWlfVsiXXflAioxjJUFQhBWxTk","RwAVVKHM":"mgrXmDmkEyXOcafRFdcgQWPnDJERlFruGXatARowzYEoXCjeGzUmNFqnFAOxlTPRIuAEEEIFluDgqPHz","AtWNZJXa":{"$numberLong":"3508212275855267880"},"XxvXmHiQ":{"$numberDouble":"7674526623550603000"},"wmDLUkXt":{"$numberLong":"6862757006749435415"},"zSYvADVf":{"$numberLong":"4604914032121925983"},"VcCSqSmp":true,"jmglLvAS":"VRgSVSHCZypXPQVtNMQhBVncwgFlzwuDUGqtlEiPZekhblDroihrDBKGomJreRVvxEsbEntNMXMZWogD","ZmtEJFSO":"OSFQgOFQgWjYqkdWfrCsigaqKtrlvhMXRPzZkIMUoNQTyzksczZAxCUpugucyVtOsAnCZOXPWOsRrxiK","MqfkBZJF":{"$numberInt":"-74"},"doshbrpF":{"$numberLong":"6122240408803684263"},"jWaFvVAz":{"$numberDouble":"4715021990214510000"},"LVNIFCYm":false,"VplFgewF":{"$numberInt":"34"},"eRTIdIJR":"XbGmdGmGWEGFksccUGkSQDfTCpsgwkFncUsKPMGDoAnqsYcOmKhhFpNEbCcoGxcRvgMLXPXPtGyWTizz","zswQbWEI":{"$numberLong":"6244091767194659669"},"obcwwqWZ":{"$numberDouble":"1896750106775623700"},"vlSZaxCV":"OtIbYgSoPwVCZLkuJTaYGycvxNQLQISxykLwSPAfuqutkkOsBaIfZkCWBrFphwtREOcbguXIfyfolWFC","QobifTeZ":"DYnMUSYAFqdovehITtzUczCWFCsGIZohIDsXlcwXRUWRBdsbUZUhbYmRhQlNMOWpIaXBehDXJFbGcdOM","XXKbyIXG":{"$numberLong":"2072366708468029955"},"dNSuxlSU":"QnxFGQAgRGNTNDoexinDfHbjtzzBgHOuDgdjrtzGEIaqPsgyNrmiWYyKAUyXVYQGLkKEQDhYDwgrIFDQ","HrUPbFHD":{"$numberDouble":"325761556852724740"},"XEBqaXkB":{"$numberDouble":"4019504911325976600"},"xWpeGNjl":{"$numberLong":"7449003994039447076"},"aicoMxZq":false,"LngvlnTV":{"$numberDouble":"1970843135463072800"},"WYJdGJLu":"phaJTVYjUODRlTcpQmsfpOmPvbCeGxLvJiAUQFUMHoYxPzvbLxTyqvmXtRDHjZtYmGzxihwoWDhVoeZY","TRpgnInA":false,"JhImQOkw":true,"KMKBtlov":{"$numberLong":"860986396812465750"},"gySFZeAE":{"$numberDouble":"3015363760891914000"},"RPsQhgRD":"lMIstKuqhARucceUyKpUkdSXwIAOeOhfJJyJaifJYnQRHWauhGovMmgwCqkKwphGEQnHishVfNZprBJl","rmzUAgmk":"srpWYNgASRjyQzUTVMpyTqQdAwGrIduuOzolvbmKNcNRDksZAoJKKDwhyqSSkGvSmpMOgaeslHWzaoce","Ibrdrtgg":"RqbUIwdCfDmzWikDAgKfWuQfilTKSeEyPIxyBaIqZzjbTTxSPkbLbstWFhGcWSnAumROlwADbnmUCRjN","WmMOvgFc":false,"KnhgtAOJ":"IyuKFgZwvnAGBNCoemNXfwQjMfUlvWOYyuwmSOWMNxxVSgraCIKQPiRNuAiOkuErAHgDwIHEOhSxPWlf","qrJASGzU":false,"egxZaSsw":false,"cVjWCrlu":{"$numberLong":"2748531972151590948"},"OfTmCvDx":"IoaWOFbeGbFAgmTDdgRVQZBocrhtcEbUkPuIGpRpewYLHwhzcWtAVcqmgQcxcjTUjNUQLYqDyzdGWIOv","MNuWZMLP":true,"JrJzKiIx":"GkDzDJPJFYxRGoyzkQNPIpNPkwIXdaWQhIyTpYROuBxIZUZsImnCWfNmoTgYOuYODuxAeOqrKNVMgoTV","JzgaUWVG":{"$numberDouble":"5703784872945009000"},"WHSQVLKG":true,"SYtZkQbC":"atHWUNSMsYTkOhIVdOXfTfunoQQIuRWzdxPQDOIOagxKUOewsHOWHPZWnvtIWGtJYkbkYclyPGHjhjqo","nBKWWUWk":{"$numberInt":"41"},"Rbxpznea":{"$numberInt":"34"},"tIJEYSYM":"jGphtvhjtsZDtmacwidBiDdGLohKmbCYHanoFpxyqhIDaUGhxLdZwjyXSxmXClUycMWCNBVmCHNQFPjI","dHsYhRbV":"MpSSMTdVdDdUljTFtgNEQLzCUWneoIDMyZzOvlAczEdVhFNPokYsjnKPwTjcSPBJhoqZEiyElvTKoBZK","iwfbMdcv":{"$numberDouble":"8139611435673170000"},"dCLfYqqM":{"$numberInt":"23"},"ahFCBmqT":{"$numberLong":"7114997365939674931"},"TkXMwZlU":{"$numberLong":"3854513878611629146"},"LUPqMOHS":"SKNaboMrDhvPGbymhONwDrWPbhIgKmFmIAqaNTiSEHGxqCdyWPXClLhOmIxzJXyJwpduAvdCrebmyWVv","hwHOTmmW":{"$numberDouble":"5160167887703515000"},"kfvcFmKw":false,"bkuaZWRT":{"$numberLong":"977724985626757127"},"nKhiSITP":{"$numberInt":"59"},"CYhSCkWB":"uQZqBkRYVaYFubIssZEPDMoJwZNUXoHWdutFeuwnbzwlwskOxqJEhulXzctrrhuXAduwqpxYRXVzvEyF","vSLTtfDF":"XgPsrVgaBzFFzulnuoYLRQDLjFOfsOCbAHiajSHeGSYdMbxlWWIFaPxOsnNqYbLRLVEeWOCEKFYIlupp","CEtYKsdd":"ULtshGrugvoWEbLeIuwpnMjYAQfxthdAAMbXQLNKlkqTZlrczjoKrdFFTVWVsFiNsPLfrAOOMrjBKUpt","dVkWIafN":{"$numberDouble":"6092406401066639000"},"HQiykral":{"$numberDouble":"6997084378132750000"},"YDHWnEXV":"glMkrFRyjzyKXLrYKXVuCzbjygQgzGfPnZaGrYPJZJczkDtRKyQFiIxIMVFpfiegBAgWylCPKUnXHNyt","pacTBmxE":{"$numberDouble":"5976360937339736000"},"ddPdLgGg":{"$numberInt":"-11"},"xWUlYggc":"AUOduJdhWBfjzZAjbtISwIbBJDekDcGkFKpkuPuSEKvyJovAOsbnBUSkgJPCozLVFtqKWlcbkbLMDAUe","dpbwfSRb":"LYdHhOzEWLioOBePsqKpSTanmJsqGmJGmBhAGmgaUZWOUfppmzGbzBjYBnyqNhAszSAyKVIFspeBvvFF","AgYYbYPr":{"$numberInt":"72"},"wjfyueDC":true,"fEheUtop":"QXACxLaREdGLHJLPLMfCEwHgEDoMiwbTBWMsKXsPgtAQGUVHNnshCipscgmQDEFeCZijffBNQYBDflvF","pOMEwSod":{"$numberInt":"-96"},"BwTXiovJ":{"$numberInt":"-23"},"TDUzNJiH":"RZZTBkdVIIydZfwHaVrFJGsIUMIObjlUHiyCIZFbjRHrhgyCIXJQNROinoekjzrStNhmVONXpFTuhZCu","KyxOoCqS":{"$numberInt":"-81"},"zMCFzcWY":"cptNXXHxnUyOVkDCHgaFBvzTrXkyngxRpUhorRpZTwetLlbemqxvYDaIkzPVPdQhLsyokFFrsCpsIcQF","FpduyhQP":true,"JXMyYkfb":{"$numberDouble":"3513302241369264000"},"yXSBbPeT":"lYVEceJTwIsxYgeHfyhPyUHilJBbFbYbbmptwtzKvXhITbtiicRyRyaxtaLmXqeHTgDsIHHNCZsDewWM","wjAWaOog":"oVhUaqzlBGrSveZHuRAGDENonbZFPQWWcCMQVnIOnBTBsYfetnRUfIkgqrMTQaCtVRMHFeLvjyoeAojc","cepcgozk":"HWWMoqFPswvQGTohBvAOnpfsjOgPmvuWfVODrpySXWabfjLQcAJfScmKBZeQgGNDhhHIQcWDYlFdTPOu","DQBQcQFj":{"$numberLong":"8199992792049175759"},"sYtnozSc":{"$numberLong":"7484353617351081136"},"BBqZInWV":"AzsVuMwTaJDIRJPdOoCtSchUgUSwaCIiDpsXVpbGxxHeYpNsCSKtiHJlWAAVFlFEAhVpJeWXpiRXlhhd","dtywOLeD":"LyYVrOxlWLyBfldYXiwvEKzkDxnxVoeKGZrvDybrJkuEyUAfmaxIjLENDWMyZlDrsWFEOGrriEtDQCLg","WoFGfdvb":"QKphEuwokwrAKuiBoyUQaMRRYHETLBbVIvIjYudcCiFwwwngoDmjqGuHAnQGFNuPPmzNKsuRcpMkKDYR","zDzSGNnW":true,"ReOZakjB":{"$numberDouble":"66287689751350270"},"uMDWqLMf":"ThdZGzzcvZOfAAZuYxcxhmbyOELCwOWjzaRPMIfkxsEtmQKHLAnBNgNouVFzcmMnxSqWKvHftNUdFGry","GiAHzFII":{"$numberInt":"69"},"UtbwOKLt":{"$numberLong":"2280370138792319183"},"DJsnHZIC":{"$numberInt":"-40"},"SqNvlUZF":{"$numberLong":"5994784905659127787"},"PvfnpsMV":{"$numberInt":"-65"},"UpdMADoN":true,"jbUymqiB":"RstgOATQyfadznEncFuQxWiXZDnBlNSVTnZyyDukKwUtfwFAIslMLeZMReWmOupCOShgfpsLjvdQsTrH","qHzOMXeT":{"$numberInt":"-56"},"XGxlHrXf":true,"sGWJTAcT":false,"VtzeOlCT":"YCXdGpHeDrPjQllqOZbXdGDzIjFCDBIbqUwoVAUkcdfuPvMLsgYFJCQfDVHxxIbToFRJANYYfZGCOpni","mlfZVfVT":{"$numberDouble":"1362707589677182000"},"HVHyetUM":{"$numberLong":"2679099034338649685"},"gErhgZTh":"hbAkaEVuyVtmEMjRmvnKugRdszFExBLKpFeSczaRQBvqAWLqBNKRPisGtlCbgtbcpFMrvIkQwycqzQGj","omnwvBbA":{"$numberInt":"-44"},"AgSNVyBb":{"$numberLong":"5022992695992337622"},"CDIGOuIZ":{"$numberDouble":"4277641814126091300"},"CFujXoob":{"$numberInt":"-92"},"reiKnuza":{"$numberLong":"6584276617939036140"},"PjKiuWnQ":{"$numberDouble":"5348924571675368000"},"pQyCJaEd":{"$numberDouble":"4291392859207246000"},"xZOksssj":{"$numberLong":"6571311739487813793"},"AMQrGQmu":{"$numberLong":"8087840999310375327"},"HQeCoswW":{"$numberDouble":"6409675101451264000"},"pPtPsgRl":"XVgpEfMdrVZdLjSeJbYCLQaXfYDqkGpjUrEwVLNyRvDLIxonippcArEpnTMdooUauoGmguuGNhxisdFq","_id":{"$oid":"568176370279243c4c57a495"}} \ No newline at end of file diff --git a/benchmark/src/Fixtures/data/full_bson.json b/benchmark/src/Fixtures/data/full_bson.json new file mode 100644 index 000000000..42ba9e0f6 --- /dev/null +++ b/benchmark/src/Fixtures/data/full_bson.json @@ -0,0 +1 @@ +{"KpnXZaDQ":{"$numberInt":"94"},"BOQAeydE":{"$binary":{"base64":"RWNVVkhUUmVsWEhzcnhxV25WdnVQTWFERUJMRFRrYmlOVUFGYmh3QWZzRGtTRkVHT1lrTWFKR2twUUFIZVVGWkJ1Y1RlYlpNTGF2VG51Vk8=","subType":"00"}},"kVDldkCH":{"$numberLong":"7791511468172299508"},"olUSRZtj":{},"ayrwiTMT":{"$maxKey":1},"nyYyaFrV":{"$numberInt":"8"},"zlVZQePF":true,"iRVlyXVm":{"$numberLong":"2167305701202279400"},"lyWwkZGg":{"$code":"ynoqoVfRjpUAZooFnnxIjIwFuYZtGrYKmntfyvBQOPsLMGcJGsaFeTvcTweCJIGGtFnFOnYfezldSuSy","$scope":{}},"DfzEYsYW":false,"VFKRjaPW":{"$minKey":1},"pDeYcUIu":{"$date":{"$numberLong":"776822400000"}},"kqKTGXUm":{"$binary":{"base64":"c2VhUHJCUEZwZ3lSekhDVWhMQ1VSdExiTENReGx4SnBzaXhPanlreFluWnhjQXhGYWlFS0xhaFJZVGZhaktVdnREeUFxZ2hIZlpDZWpoTk0=","subType":"00"}},"iNbJUZuj":{"$code":"QEUjFSxUAHTQGjJRGuawHllxxbITZGkBplrnbHyjiXVhZEnFmauehFkYOaIaYPbtyAsBMdRyonmwrflg","$scope":{"fKyfBXJY":{"$numberInt":"-39"},"UflrpcEy":{"$numberInt":"-99"},"MFzjzCYI":{"$numberInt":"-72"},"eTohHXcM":{"$numberInt":"56"},"LcgVuFFV":{"$numberInt":"-68"}}},"buEAkIge":{"$date":{"$numberLong":"1449360000000"}},"NAKOhrML":{"$numberInt":"-30"},"CAWdqfmI":{"$numberLong":"5403474030514800337"},"hAcOFtfN":{"$binary":{"base64":"Y1RHZ01abE9oaXd2ZFJSZUxLaFliS2t6Q2xsVFNucHBXb1hTSFZSSVJOSUd6amtSTlNmb05jRnZrWWxoQ2pVa0lDWVZ6ZWh5ZmZqTFNXYlc=","subType":"00"}},"qPDeTZWq":{"$numberLong":"9055278099777449039"},"ARdpUnvr":{"$code":"UubGxpjznChawiBpCJjWgjYaHAjdqHFbOBQyDkpBtMMtyZEIetHzpZshLLNChAQObxpMHdqsLkNoyFtl","$scope":{"oGlfmjDq":{"$numberInt":"79"},"SJqmyxSJ":{"$numberInt":"90"},"KGCCxetX":{"$numberInt":"-84"},"OMDIwkuc":{"$numberInt":"-81"},"mJtyLYxY":{"$numberInt":"66"},"mYPcOozX":{"$numberInt":"44"},"dsIvQCvi":{"$numberInt":"52"}}},"geaenijV":{"$timestamp":{"t":1,"i":1027123200}},"EuKkhmHw":{"$minKey":1},"kcVXzXHn":{"$code":"rcFbGNwyjskyGgevDrFrsDOstYxGvADYxnEuRepZGlnGUHtyPBoDsvyEHQzfwNLCSiaElUWgQmBRUWyE","$scope":{}},"dSMWIFLD":{"$numberLong":"7306035949033000157"},"UuDsAOGk":{"$code":"umFRKcVcQPMdvBGLMEhgNIiDjlxBzzeqlGJeJHhTUPCDeCxnnCMGkxbKnSxstvURPRlRoGMYMejQNULY","$scope":{}},"KUVCyPFv":"oQqKPHpzjHknSfdFdEymIJOZxYOQotZKAFlanBTLUBUFCPCpgXEdqXoNVVReXxJkzCpwLQqNVKvPGJJW","kFaSNVoS":{"$maxKey":1},"SQIAGqNE":{"$numberInt":"-58"},"MsFvjFIM":{"$code":"jYbFSnAjtwHgLOKGmENJthmsPIKxbQIulkOVicMfFRnxmkIkIPOcZyPyhMFrweORAcDwwJpWuSwxYqFv","$scope":{}},"ZZbaMgQp":{"$regularExpression":{"pattern":"uWxyRVYi","options":""}},"GqcHZeLf":{"$numberLong":"8240785954293476870"},"xCNVQDhK":{"$timestamp":{"t":1,"i":1438819200}},"jMeFYftg":{"$numberLong":"455743854322593800"},"yltfrVvf":{"$code":"WWoPrkbAWYFjTUsIgIpaCkPcymjMGotwFvITafFXXzvrAYSoGcfDkdlIIGYKqejFJwPJElNrShMdgJOZ","$scope":{"TKLtwaMQ":{"$numberInt":"-41"},"GniljOvp":{"$numberInt":"-74"},"PMKRGIPz":{"$numberInt":"-98"}}},"NzsNfcyY":[{"$numberInt":"5"},{"$numberInt":"2"},{"$numberInt":"5"},{"$numberInt":"10"},{"$numberInt":"6"},{"$numberInt":"8"},{"$numberInt":"3"}],"hhVNsfZZ":{},"OagyDZLm":true,"VIJGlGfX":[{"$numberInt":"8"},{"$numberInt":"2"},{"$numberInt":"9"},{"$numberInt":"8"}],"RBrgnptQ":true,"ebfJeJwS":"RCFBSqorwgFNcBwCWsgxjnSvWQQTUeUcTiDliSmuEldTlhzBFuLyysIKwaiSwZCdCnqRyiRfFNnKXVXE","LQygaTou":{"$code":"pVShLZkivMkARMdSFjHSaBcZPreJuYBsSpxIzQSVteWGQSXfFesEmCYzZyHhjpSLuuBEcEefRdyphvWw","$scope":{"Pnilqgzk":{"$numberInt":"43"}}},"uNvbuffj":{"$date":{"$numberLong":"1229817600000"}},"SHvLWKjg":{"$binary":{"base64":"SE51VlZGWXdBYkpqaVhIRlBJR3hDd2JUb2plVnBrQWZIZUN1U2Rab2ZudVZPZmpKR01QaFpxTUdPWHdiRnpibGx1c0ZnY0NscFpZRmdqb0Q=","subType":"00"}},"wKCwNLSh":{"$minKey":1},"giVmVwzU":{"$code":"JSgaICRscEgxNZjwPeEocTpfhdMNhAqbmOzwtuRMdhyTxwAKwLdplIJjWXzZYxIeLlofqqQPfTxJmoZX","$scope":{}},"BGAqpNkE":{"$minKey":1},"ZmCYgzpS":{"$timestamp":{"t":923788800,"i":1}},"PGiZAWYN":{"$date":{"$numberLong":"1270339200000"}},"wShiBppY":true,"tuTaYuPG":"aLfBnCaptjatvIvMUHkijCexLohaUYmmugMsQpyedSBfAFgvSktBNYTGBmmLUIUGsmhQCCSerSEHRMpZ","mUXXRWFX":false,"xoLWHvAD":{"$code":"VcWVKVTUfFgthzjgmctPeijKmNDrlOWddmBcMGulYjhiWyRpBjlUjJjTBXCbWAyYNZUGiqSlMjrZShPZ","$scope":{}},"WqNFzMgH":false,"JQbxXYUE":"NtsOrOgfmcvAXehXybWnzADxfildzokfBvdPWmvEShmKsNGuZkZmdUbuUmGLWuRwvkIksIGAVJtHeWaK","AZbAtRCC":{"$code":"AUKJNhEvuziRiApNRrScrKlADiLovaxVLOxyGodLKWuQJRwMMwOnqLwTLVDglnKBYOLyOvoYyRwEJCKH","$scope":{"IJpzzYpT":{"$numberInt":"52"},"CtVYmtxF":{"$numberInt":"-75"},"ELcEqqir":{"$numberInt":"-83"},"zMJPPwpk":{"$numberInt":"-30"}}},"JlhVrQmD":"TdJBbKVLawqzBqmqzuRpVVetrdgAbyHNMCGWuCKMUPJJQxDVpBuBjLhkRjilTxYmdkYNkflsBRdCBGHp","vSvdWAnJ":{"$binary":{"base64":"R1BaektHUlhZZ3J4S1BYUWVFTGd4Y2lkQVNNblRzeHNObmJ4Rm1hbExVRUNGd2NydVR6VVJCelpNdllIRHhUUk1NVEhWd21wZndOcHZza0w=","subType":"00"}},"JEVgGziE":{"$numberLong":"2044627925402213400"},"LCHLPrcd":{"$maxKey":1},"fWBoraHq":{"$timestamp":{"t":1,"i":984268800}},"bxrxTsho":[{"$numberInt":"8"},{"$numberInt":"10"},{"$numberInt":"10"},{"$numberInt":"2"},{"$numberInt":"9"},{"$numberInt":"5"},{"$numberInt":"2"},{"$numberInt":"1"},{"$numberInt":"7"},{"$numberInt":"7"}],"uhmyCSEv":{"$maxKey":1},"rclBQefx":{"$numberLong":"5827556087171336561"},"iEoENLRz":{},"yPKwWWxb":false,"fvLXcTQB":false,"DQuSYbZR":{"$binary":{"base64":"anhGWHB5d05uT0JLbmhEc05ncWdNaVJMTVlCSmJSbXZ0a3RWZnh1cnZLY2JaS3NjQlB2RHV6WVhFQnJ3eWZ0eEpIdGF0eXduYUtJbWlhc3U=","subType":"00"}},"uSbYKmdX":{"$maxKey":1},"nLsRKLJQ":"HPzjoSuhGkNSwsULyFwEIlRZlGEkxutczLMBOeeOvaWCAZRKZoTUFpcLNHQBGCVAskBBJGRvBLyRkRXn","kQhDjXIO":false,"duzmrLJI":{"$code":"tyYTwBCQgIUaLlAFjjJsrDdCczsyCJXvMDWdfnArhrEQnrBxxXpmBIxukADpKExQuXKpPSnBdWmEgXoV","$scope":{"EXKjeucO":{"$numberInt":"49"},"lsZZCuLP":{"$numberInt":"-43"},"giussUAr":{"$numberInt":"11"},"asWnYFAn":{"$numberInt":"-5"},"KsNFIwWL":{"$numberInt":"-94"},"avUNuoZK":{"$numberInt":"-68"},"ursJhljA":{"$numberInt":"-18"}}},"zqpnKCOL":{"$minKey":1},"SQOAGVaT":{"$date":{"$numberLong":"905817600000"}},"WboSdRaB":true,"bRaWfHwf":{"$numberInt":"-70"},"QdkSZoFx":{"$minKey":1},"EAYSerbF":{"$numberLong":"941789166999923700"},"IsYFQBkP":{"$maxKey":1},"BZogAEUM":{"$regularExpression":{"pattern":"luJBIsSO","options":""}},"duTaTKGF":{"$timestamp":{"t":1,"i":1252972800}},"IJOIvDcw":{"$date":{"$numberLong":"775958400000"}},"TtYjxGJH":{"$numberLong":"8268697854246446000"},"caPGDEGj":{},"dNVHpBIF":[{"$numberInt":"7"},{"$numberInt":"6"},{"$numberInt":"2"},{"$numberInt":"7"}],"hUhWCZbY":[{"$numberInt":"5"},{"$numberInt":"6"},{"$numberInt":"8"},{"$numberInt":"5"},{"$numberInt":"9"},{"$numberInt":"1"},{"$numberInt":"1"}],"hjSPiBiC":true,"UwZlNrHv":[{"$numberInt":"7"},{"$numberInt":"3"},{"$numberInt":"3"},{"$numberInt":"5"},{"$numberInt":"9"},{"$numberInt":"7"},{"$numberInt":"5"},{"$numberInt":"4"}],"juiYRtal":{"$numberLong":"6906562979624261000"},"RFhPJzgh":{"$numberInt":"-7"},"_id":{"$oid":"568176370279243c4c57a496"},"LNpbbRfA":{"$timestamp":{"t":808444800,"i":1}}} diff --git a/benchmark/src/Fixtures/data/large_doc.json b/benchmark/src/Fixtures/data/large_doc.json new file mode 100644 index 000000000..2d681d94e --- /dev/null +++ b/benchmark/src/Fixtures/data/large_doc.json @@ -0,0 +1 @@ +{"qx3MigjubFSm":"UAwV7PoNUVtv","oR6znloB9lnh":"6ESv2GcwPI8D","aDzkKYzkWpyx":"P48wdJLjNDyR","hoULFflvyyer":"Rr2zGMwh5fNT","tQwApU3WlKsX":"f5NkqXcGl2bz","yZ23g4TVk5hf":"MnrOjPtKygh2","XkQxmgddCEmf":"KhrT73wWHxyR","Ivt76k5awwmh":"cCFjcDYFKEQx","kqBw1bzxRnCr":"ISMWyq3rACDq","a184Ri7EhfIs":"fMjJ9LpiJeTQ","uyESiH6WYDXQ":"GGDVIjg8F9oG","l7AT1gV0vEy8":"brfNDpgWh9dM","r293s8OsaTCD":"3Re6Fl0gcPdI","LrIhoD22ZqZO":"vhyddT0KY4eG","bERV5xsSlWDp":"FciOS2RwxpMI","cffHC4WeIX85":"ss4HZvdXYD9z","qIPZHtFwDeCe":"DGmITT7mqtwz","KZJywXRRajbX":"SruVHkjxMvGF","69oin0s1iqOt":"UFHYuhuWw7v9","o5aOhVhgRV22":"EDjVp9UFz4z9","LTuz6BEjW7xk":"jNj2wnuMVnAX","qRXZm4ygEVFA":"NFqhPvYyCFLg","gSmDpX5eVFhw":"yvlBYLYcNzOD","Q0BcqCzCYXCg":"gMjWICrmMh0F","2qPwUfnJcr0Q":"liIk0NsVWz1t","mmj3YNjqFIGE":"uNqxRA83MDJA","ubhTD9jSYw4L":"BxBtp48izsGd","1x7sHvI4m6nu":"a3L1MN0Zlzh8","2jMM6uE2gjCf":"tLUZ46Ulratr","pMToyP6AYg3o":"Ionq04vJnXyF","Dqb9LVqRrnrx":"0o6KJqHwHGu2","DIyNepMGi6jK":"u6ICz1GoMgNm","ro1FMWmWbIdp":"ygMkt7ISe7MM","J0xPn052mJov":"hjhsPFbfc1gr","X6KcouZWISAC":"suG41ZsY87Yh","nkTM0jSoPJDw":"JzxKXmaZcGyM","rxn78gS0esPv":"PvT47oDLmuYE","XdbKopfDx1S5":"Rpcm6zaj1bfF","w9tMDSSSFFmr":"BkMAV5Pqcbxg","O7IEeOMtgQkl":"cETVZgXvbmgV","9w3FVeBPfu9X":"k61RJTWjQsc3","2TUibCbaXJt2":"Wfpln5rzW92u","324DugFZZdBD":"K9Syp3L3VqAD","KYuA1NqpdLTc":"k6owtO1kQATS","N2usyk9XoEOY":"UUZHKHq2wnzm","scPEyr5A4Uh6":"YYh0Icthdh23","wKNMOBddbz0V":"dlrfAhBJeh4G","ZHYcknuIudKG":"VCLzGWJINAwI","noLE3moWoc4j":"xHkOdS30eY9H","bcaYbXnsXH4G":"PIBg7yCZTjFq","plOBAbgHZ19y":"pqEjfldOXbxE","VcUPXadOpIax":"aIGNQro9pUVU","yuucWrwXWhQ4":"SuF9l4R90ws7","FTu14p3qBe4g":"luHyW4KWoqsW","MAwJVWOFGtLs":"okNWMgg9yAXw","xnihBT3dXr44":"8jhE8qfALzft","xfPXchTCajvx":"7am7qm9rDCo7","1ij6XrkZnKNa":"Zd0kqd5XBv9k","fbYzfGRThZ9l":"aCuwaqltdEzI","gTIwPOYTGBI5":"AzXg3JhhBlnM","EGTqnBrD3ruA":"8TmaIMjhxRL4","ZOFiJGYdD2Wx":"ZwNa9LmprGwS","ciC4TjYuUePf":"5KflES5G0E8u","DNsVdpXSrn97":"ongAv1k9Ixfm","l9YsQu3X4tSd":"SNkEbOpBiVcd","VIiVQ8IJAEue":"YmZN0x1a3XG1","veJi19iSkMDL":"ODrLbqRgL2kA","dR2Oz3T8jfEl":"dpFPonGpQA56","iCknr0F3fUNV":"4Sf2oMYOcuMk","T3uoVCvKXsDk":"9PUh5XUNg95P","N7QbM3cMhm7L":"FYWTsgmSROGC","O4nB0iiOkDcO":"ncYq1i6r72vB","jM245HIeU17e":"LADx9AWxbizJ","mQalncqZAN0Q":"0u5ylCjXE07I","lupbivGsuhOP":"bIFrXPl69sns","XCoxMbPOqXr3":"12C9zOYGpzyn","StCPEaz6T3PT":"lMDGHbsgKmsm","5IuaYvvd04fQ":"oP41vkqL6YVk","W5Y8rmx9Do7r":"2RWAO3ONhLvL","ptyr2WPM1dJs":"dnuhASs3LKdd","mKbQL9SsJKEu":"IoPRN8kEzvIP","lHIKSe19hzAs":"pC4lnZ3d2c7R","D63bjzTCeSVk":"1KBhwB94zwwA","p9YRZldEutJ4":"d1uxLN89Ivrv","dnsuHDOxFTBv":"FtHttDnGOaip","t43k2pY05AJh":"kyccSHn3iyQL","vNcrjnYEvyu6":"PMJg7cMS17j8","3MYF8Er25kAN":"D1KRIhXauCUM","bGPYtchwsJ6A":"sRgqWPUJn6iS","wL3X7by0e4In":"tFGlCUOGXmz6","6tPmbbXuu2jJ":"TMOkIEFEvqZI","i4zFShxW8eNq":"hKQK1uyn4FXo","KRgqefAv6qkm":"L0MNS4jo7jAG","37BuLuqWD0Ab":"61QaXi2521OG","EPfAsfUEXM6J":"tM6KARq1r028","DENbXUP7CviB":"4DYjzJtTk0RC","0mi361unH7Ss":"dyD2FuDNvAA9","WCPbPCMskqYN":"juj5uVNJQAHK","lGY1qfXZunUf":"7WeFZttNnGNC","KspzTFMlGH7v":"mELVyrLtoWGm","H4KLcuK1mUIv":"CU1OsouETFWv","Ro6mId0bvYeb":"uOPvlFJpDRCI","LUDArMtv5FPq":"FmTc7NTym0Oj","01ITEo1j2k4g":"BWWal8b5M0Tv","Pze85Lhjs11X":"6AbKyrZzVeTV","2r7joKkHJNUc":"jBiIa0G7jI4s","AGrxbaFQJn5A":"76SIIY9htMRF","0BV6hP1IRX29":"bDdE6yPqqiSA","bU04fh5Nouvm":"pzvcgKhdnS7U","RDKZXxhXoOkS":"BNi8N1k2Mhxb","8hZXb78Ozsf2":"OCwYKuh9xDKB","U7lHzAhMGLxN":"dL2mdToFqOoL","8TNH4uZxV7tB":"oNjmMnmTeNAx","9seZMVGQ0mtR":"ndqNOkVi46lL","5P0K5RxCUSd9":"1l1sXCq4lgXY","mSas4K9Xc6uQ":"nWzrQKuzhjxL","BNjb2B3lKuuM":"fzXPpjp8JEGP","5czBDQlm0bwq":"llflEMZEbAfc","IxTj36FncvDZ":"gWkECT3OnNeB","KBzEClGnFhhw":"iGQu8oE9MsEl","D4p7vxw2enIt":"QaSiMfVB8JBf","FFwYCdLRyFjv":"0MqNJIgsLz4j","gGrdthxSnB34":"JxBFj0fPJ8Vn","atdtCLPQiDyK":"JedjD4ggoGRh","Kag6byPKobA6":"ixdfka2UqlBS","JHvtXGUCE1ip":"mJaHmIIiefEC","28YCxsFQS7cb":"Gl7kVebAjtOu","rkBtPbGL8GQd":"BVu548NiHB4o","XO3py9xeAXTs":"CdBK3YzAxSt0","bWypqbay3tOb":"ldNMgtruom4S","mwhLy5mwDbpA":"0M3BMPmNAqBL","K9LtyPFJECww":"Tn8lMRvqdy5W","VYPNrX1lMqhO":"IOouXtHCpTh7","VLs5hOeBrzd9":"M3wxzKMq7DFy","gtXQNN0Wh0hN":"GwsEsWJ4jnfp","jPGP0Jd2rTRW":"PDVcDA1YoRJW","L9rm8gfCTv1c":"djxDkt6N06od","dgzznKoXQ4BY":"IVT37mth59g9","cuC1kUvoddkr":"yBEDpHRvku4s","Of3f5jPycJ46":"rwnBpD7xSGAQ","WDuAJV06XSud":"LRKeQJd5bnwl","oI42HtXyiNBE":"DKivlwfgRYWS","BBXO6Ys61sEe":"497aohy0dC8f","4hidYxqnwfED":"pgCu6spUXwGN","MUHGnVndf6hb":"dN3xzgMvLz7p","rWUUuOKaE8QY":"In64jpA8J4vp","gBLrZp4Vcf8R":"jyNTvkZiRTPm","rHNzuQXw3jf9":"UlFZ5oJlTuyz","WdWezCG73vlh":"Mi1Jg9bKSXkR","aP4OoLq8Ckvm":"cbO3h51ibEwV","7Rt4QsykjqgF":"XsrB7QUOZrfI","jZvgMbMwduDU":"3bTPxH3krGid","zcVfXVrIxnHY":"uaVzTMGNyate","ic6jmRY54CA3":"bYlS09fTBzn7","C9GOqaxrZOKP":"3SgnBeD6R3We","kyEjob9VQLtu":"7Oux7egoEbd3","JnDlAPu0oxc4":"lWYKzQOH2e9h","Dm3JcfHqPXGd":"dmVFa6shuq8N","3K6mdklMIXCI":"3m2TWPWuorJG","iJszFwYxXerP":"kgCe7jvCL00O","pkjY0vJknUcS":"KC3CGM2cFfqW","i6jhTwGKru8j":"TUddOE5Zs7TP","MHld8W53lHaU":"WJqaGp1AzuZG","Da7GiJxR8yaj":"0bk3pZduarQp","IaEvxYEU2Oo3":"h0HgRJ78FTBd","ewGwVQwdMaWd":"jZMtM6FJHALe","wAInJIw4nKc8":"Oo8h2XbWuOeF","91JZLE0rOJ9p":"n6k4YG5gG1Yf","kKBeRi3nKe9Z":"GKoQDrnsBKJR","POHIWxbc6hhJ":"kpk21AKKKVmk","9nnwMo4GGvIU":"8yOWuANTgzSf","77q0x9IewJWM":"bX8uncNr0N69","6fxYXiEOfcMT":"2W8IcJ9HsOsb","qwhNr9WAipDF":"5KpJvBfHhQiA","GlcOjo0PUFrY":"4jmmCHrS6ERs","4x3Pqrvoc35i":"SejIrX1nWsDa","3jbg1hvHau1r":"evZkaAxxjyZ2","8kJTUNB1oTbK":"PUnr9WY27J6Q","DiHN8LIRwxQS":"lyEagEVZcCZ5","g4c8awjwayij":"ICMF7fv06EU0","2cnIgBEbQOzt":"Li9z7BHEbv0c","7r81TdxCaBhB":"HSJB0c4AWTaO","hZM3QrVJIhUh":"ZkRm58srSPix","vtZ0vd1D2uml":"C0BK6PGkByVx","mfhrWArDuqFh":"WRQGcvfjld0d","2tFWtOm3jSrg":"NIJPL6uaLmPr","DlFUuF4R9P6V":"5nF0mDXAYqvo","quID0a6aBbGY":"kWyIHeVrjl2Q","3bW4stO6YY9W":"4VYYYaGlN1tp","S8J6Dd6rpNxS":"S6vDHj1fSoOh","2jYw3RlcN1yY":"o2AJmXU7rQTo","N4cpg9sPlwub":"hBSsF0uV5h42","StWw5gTWUq2c":"znwCi7K5gDl8","RmrSWlYQ6wmj":"n9AiWHfS3qQF","qc4zzMBhy2cR":"080DB9MJMuUO","PfdG7DFcGbR1":"H0Kq1ga75d0W","9F2Zbd2dkcKl":"On2bznIAFyLw","C22XXRoLxykM":"flslel5BHWvd","xab4KCZY3Eq6":"7lu9GYnQLzH4","EPxCMUNZiOWA":"dvGYvfX6vuiO","XwdQLMrVxZaK":"cLDjfmfjzTDw","BmY1wkWSUw2Q":"tkvSYZlHlWVi","PMKC87wjz3Kq":"E5TP0bJVtDJS","FirgbFqEaGrT":"gjBIv7T0TAKF","YyKtip9bux0A":"eGJixZeGJeMV","GXYIgQrPkTYE":"pUHp7wBAPzzF","HVfXXtiTWJiL":"KJBNg4hzwa5S","o2FMItV0Gw62":"FESQGZHYVoPl","R3B1Tx3B9loA":"iY1AMqPPgBhT","RyvuWObWRzwn":"IJFRH0TvLYVo","Wph2O2Oug72d":"z90H5P6w5nLN","eYRxIJ3tjKOC":"XDyatxKmxEVQ","5bkRK7xivCpw":"eWkF0u6c5P9I","PbbPb37GJ4gx":"gXBPszHolDef","jjvTSUH8wovm":"mZ20DSEQHNw3","4RlPSbEzAzOC":"QDmuafKNJhsS","X2U1zVZu39pc":"72Nsk4IL8Y4X","FFP4pRFv60IB":"hhJ1yitRRaWj","TShOgO8R7VS7":"Tb3V75HFeBQz","JRFiTLBLfd8Y":"kBxnNz3G2xfQ","YVAfPXwiNTdw":"ErkPvq4yWIDk","IHHfNCCByf1j":"fmiLUhE5Dp8c","QmB3axKCjCqI":"ZTejE0GOs8dp","0vCqxMe0zyo9":"pUs8ayTIlEzu","6yttIrNuObjN":"7Wu1U0k708R3","wGKIOCLlePb3":"dpDfjloy9yld","AbH0f03SfrbB":"2P2xyHttbDXl","d6NXwEVTN8jl":"wgj0a9alT3oa","11hVXMs7puYc":"cqHuWnA4hvfV","VLtgSNNdc0mx":"Q3wWtPQFXJW9","bDGKPgS83xU3":"c5PPQ0xN7GDG","tRMMrHMrNuxx":"Pc0halYUvw4X","gw1Esmif0MFr":"M3yw045r4taJ","hgnTsvflzOuc":"xNowfBFT439S","4A5dc8NAWEdj":"LElxk3IN27vE","kzLjJ15D8GXK":"xXRK17qHvON0","j0sN46tUAJEZ":"7N4zGl4ACLLQ","AWZZL0Q3ikOU":"18R9vkywlpW1","18AfbW23jk0Z":"teLolpPXYYte","w64n0nWEki3A":"iNli1IfN3fDj","6n51xONnILVS":"JuS61bS6j9Lo","6h2eVXDQ4B0d":"8lIZ1PkL4xrk","EFvosHHnYRCR":"5dqIWPHXurpa","7XBcDbyKt85k":"dzD2efFOzemv","bvRHVwR2zHDY":"uSzBaGel9DuT","Avsv1uFndHyI":"UgEqUFU55yXr","iIq1eKYY807V":"uB9YFU40bQ4g","OFVhWyy4Dz86":"RopMISBWnk0o","nOPLnvOCDrfc":"HbEsDdAGH3w2","YYOFeodiYm2p":"SGMiTRhGZJrx","xWI3rdDBrirD":"H3mKn4DDFPmb","H9fyo9C9b5bc":"ggyGAlZKO5ZQ","pgQxdmk5xcZx":"PEi3STLwZMJa","qwQdmNmJeG1w":"GzGMVio9z8iE","4kGdWsErukOL":"3oz7bP4rOqnm","DOpdqSAkMTJx":"xIchdf6ZM7xg","nOi92gVOldKi":"qOJp7u2xoP88","Wq9Fe4Kshf8Q":"KuoXdDPfUyd9","sEnpTWr6CjAX":"ai1sUrNIvudA","RoLgvoYvlSdD":"oGoH7OniTXOO","6mSHupV29sYm":"PGiRZ15ZBfHP","Jiv8G1lxMVub":"chlo6syTHwRp","dzU1OSuir4k9":"zKGaWtaBrGlV","UhKR1RJjGhRZ":"jr9kO4K6JXwX","j8X69QYuq6zT":"cn1rMpfh6y28","koZStc063Haw":"Pd192pkElewG","H5beR2zvVUCd":"9h8vPUOoUupq","wVeWLtpg2UrN":"LjLcCgQFTcBT","MB4stuKsABN6":"zCaGcx0fNOQk","ve07OYVU1PhP":"4fRl8lV1KdWv","cMeHqL8wutb6":"P8R9jj2oL9yr","8VM7SUgTdaX9":"nPWZUKePSEYM","L12srgmh9jJD":"lTxrxKacb2Av","yPAJExzVgW7G":"brjUYVPCypjr","oE1JCup00mOb":"j1BFrGpEl8w4","BnYfTbFCCNHx":"AiTIiMipq5bq","I2AWqZCqlYAt":"edRxD2F6BmvP","ZiubJpEsoU2E":"TzTzzh1aGlv1","Grct40LLnvTq":"qWS9TrtD3MlI","4CzqvUNeLlia":"4fY4DOcyQnOZ","OYqQwm0BtsSR":"4gcJeiYLpo26","ketjQjm1Jwtp":"6eiVDVjhsfdx","U3JG6QjXFTFf":"afm7HMBuGegX","oLzRL0DnZFyc":"w8k6uaTMzVnI","nvObt8q0LzD1":"STw05shc861F","3j2IsD3uMgC4":"6uZ1vglGE8gl","i0NNhKN0WL63":"iKs8NjZAcjdu","nNsN5vTeNgH4":"hlcDqyjXSRZ0","ZbzzcTPbeYPO":"HyjZiratzhUj","tlKfyzNxPbCy":"t0zZyv6wMEmr","HT7RQcsmrq7L":"3ajgOtr8V9Yr","uhXeE074OgC1":"iN1bR01sFgZN","uSTIPSDABQjm":"HZQPv4MWB7S4","URPlmTCnZ4cA":"vBc8I9DjiNtX","8xc7rteEt0Wv":"1maIYvoJgiqv","AWrdsQA3Rowl":"m0HZPYwSgD1m","rxTPAcVxRDXq":"VD6ZJwOHQ8r4","krdUqiK9aFZg":"MkYeVrW1WLBV","RZzQzs7xhKBm":"TJ7Toql8MrdK","ZN8v4CpgY5zG":"INxwyOuRls5l","ALcjdOmQsEyg":"8wBHyD4WqHac","V6Yie9x6RqG7":"iQMzuLixLUzP","sr95M4XoG9Ir":"Q9gRpfHa4tB1","WtlvxamY4aee":"JqF0DBA2Limg","gknd3EQAT9GC":"xBAA7YISJMUU","6Y61eMVqgvSg":"6FWDv9tad5qC","fBzEzUoLDf9Z":"KmuGUYda4Itm","AEepgCLKoc5x":"uMjn7cJuGYVL","FddU6sP3QL7P":"zxYMMWSETzOR","2QDd5loCQllg":"nd8Ni3YYeoG6","LTWRNMi8QExP":"MEEHaVb7Ts57","iEqTDMpQ1tyb":"4mfflosyG7cU","MGHvltRd8OFi":"fo0rr8fPhbOs","b4OJwCIH459L":"YSpTUaFdj9Mq","S8zm3GXeL25U":"dsxypRhqR3jK","P92pBi3CJ6Lf":"Kbamjrn7GoKy","GwHpxnyC57qN":"p3OSyCFNfGa5","eqLDc95AKVuA":"zFgTma3fQEnw","JyRAIG5pi6Vl":"34HcYfccHTEn","SikSkuJ9nT4v":"DVLveIpYmKhx","Gio2SjPkTpXd":"CM0E7WeOkPJG","lHURWdnMvFOv":"rnShHsv5LrOT","39Zok2SdKzJQ":"w3sE9AUM0lxN","dSVAvTQXUFDK":"a10AtJ0OvYYG","GpXoby21xKgk":"KRO9TDFhZfJ6","FkoWCoBMBXiT":"iQ8YhL5Cb4HF","RGaJ2Captewt":"xoHxekdmpxSw","4pczb2t9NxyO":"ptGqhhidl6IB","MeWmjbpTVtwq":"4vVnL76sPj6s","p1J6Q2Cl3Ct7":"TfNzHvAJvCgK","TgcxDENJ2wGJ":"dJoGQ2k74LsY","cMhNEINCBNxG":"MLuEVUPs0E0e","Za16Q44MJSk2":"4LBq5TR1SMJK","CjHboGAQPFln":"CQJf10E43UNA","inieWH3ujFBG":"qKxjVZTAxxtv","APVO0miKUbLq":"L3YI4eIxBw7b","syp2FFv81vBn":"npD66nwswW1R","LLAa2et0ioHy":"c6rKdTUmLmtK","9EZBA1Ckt8vO":"uQVw0PY8Sc96","NkEzusOdoVqN":"GipoNsd1POiT","5AiyW7PNbP73":"CBXUoUE8GZeb","WP9nGBJY39vc":"m6Klt9jZKDtx","R7fG7cbiGEth":"x5kN0neN5h1F","5HHBPjLfECxH":"pSjICNFouZsB","llcVB7kvKnwV":"VygI3hojHHax","PzPfiCHXkEpY":"9IZq9Ga6rfSs","NsT6JnuZIJvo":"NSMRkLKH8kYV","hSnfrQPOcRKI":"qBEigxc26xk8","8Um3DNQjYCpM":"MmVpbzx88a5q","VhtC6yPAhv32":"yarRK59D1otJ","V1ohkPzn9wxI":"2muhOF3u4rjR","tsk1Ipp76W2a":"ex1t3EhbkHCM","esYjFaAGo3vL":"u20qu6Rd7nUd","YuKrPCptFZKx":"Xf2hu1wywlDf","hTrVyHIlrh1x":"rVtnMpzSCumB","siku4AH3RY5F":"7fl3gSGtqiBr","qkG4Rb7EfJOh":"RfCzRe5zoD4I","ilgrVfnmfD8J":"PFos1VVAlVSX","uqcL2pTCmT8i":"0zfn3jPzr08h","Ya7aTgLX3DDi":"4EeCWyYRZWua","uyonPbLYhVLn":"1JOZOJdZoHDB","4kOCqnwbTQob":"rfCTuCjxDx1N","ulfZzrEGvMb5":"WTGPz6ABWoPE","lNY2Yai3gqjT":"ykLAjDO9MydP","Wv10yww2Urm4":"NgCsQn4zxdrP","l5ENaetGEcD5":"WERhvs2QvQfV","TQBInCf7NorN":"kNutJrKHMctS","v37gsTBMsNkS":"PUl2p4Rz9prb","YJdUM9b0wV8L":"6yYlCS0Dzbpx","rqRMSIBFTrVU":"63G9QsxJuiSx","pfM7wrHOtRBb":"d4ATd0r5TfPl","dio2EOrIXpBo":"0clvHi7GqK1O","pddWdPs0difE":"cmyhYgMhXLAA","kgjsfwjkRXZK":"3MehmGCQBDp3","0bz5K6055E6e":"ceTVHIvMrQvP","4R74opTMwmPu":"ysLjWkizhGsN","YN5H4SkNT7Wz":"xGHGAzvb1feU","1La1TNRSxruP":"dpvY7rYIpEHQ","MYRMvhyH6wir":"de1iEoKVEeqe","Zl5WgwNt3y9F":"vqehnKRQ6Ysm","5x2vKZ4uESSX":"aKHPSmV6Mz5c","ATs6plIapRQm":"vlf6zt0nt4Xx","SYaKIudcF1Pg":"oIvrTKGUqPCY","Lpzirg4UhTwc":"NndljTIzBdgG","EoenN5pQ8AVs":"OIL60n0Tznqb","8kWl9EaE0Hs2":"a0DT9q1VtxoK","OxXQf5eMiIUF":"3GLqJmZDnyKn","3Y0awMADh2b5":"01Wc25l7GWtE","XgBAv1BM3zvE":"A7HU8X6Oycrb","JPscHvvV4Oom":"G5BgCr65I4ps","XWQ3chkVohkl":"D4BWsm2oyFOX","JRqjHx3XrpSu":"7rH73cQDUBL9","kW1s3P1LGLnB":"uu6FczcRZh0r","Z7mKjpzUPiU1":"GBSB308Wezuh","1AbIpHwpgQDY":"CXGUhhHR6RHh","pWqDtW7gj30x":"UkOF9uldRxoh","0rQ5Gijqtspu":"NA1wx9JMe3jt","DIewvbfqqrep":"km5gmnRcJ6c7","fDUzuTrgknuh":"XCXfjplDm0eb","CAhHAGT6MJrn":"pnb9FzmKusOc","p1lK1twzI8es":"Aq9S5ROfRWLz","COzU3UCAD7Q3":"YqM3gb4gJLXm","b0kkUZiY3zhr":"KCu7SP6ZUBPN","JYkqIuhoQTUz":"WtI6tWI8e7KQ","v7JxLtHOK7DZ":"HLxUkVREsJIA","A7c6RdKVssbC":"BZugcxMpqSrA","X1rrmrqOC5co":"KOfJFAPZffMJ","ZAdx1OGApLU0":"eI2gjKY72eaU","eARB5jYsItqj":"iw8UMeBObHKw","73grkSouIoyG":"oXg7VV1ET8gx","DXd0rKafxI3L":"Z3dWg8mgZQqd","9UU1LB05Wyjy":"1gGSNqN7u3E2","cTWd1BahiPhO":"RzQ3JinQ9foO","OEnAp0R8mnEj":"8ZB21mfGFUq8","5Vq3zlPYJvkK":"K6FcxcQFKKnS","Crl7K4oMHIl5":"a2PHD3wBUDcD","PsyO71yQ5XhE":"AQFpWxxkjD5q","KJOGlOjYGv3P":"CYzbUn0BkbCt","xBLVHrsieBUD":"V7zyZEzC92iL","HhhiYETQthD3":"hj0cZi32uelf","gLakhGVZaIgk":"9evUkCqC5tmy","uG77Apiviecy":"8BLpuzE1D1r2","S0hEiXpTGWUp":"hwYs81vemYuG","9znmJ9xFDLil":"HrqC7vVJsJlm","3YbpSJe7ZBRo":"7WD9nTuljmU3","1xScgJ85azFC":"mNPQgfUjtL0J","qlPP1NRjmVMO":"ekQsMphYxE1s","4cblrhQ32Xq5":"EjRwQed2CtOk","4ktvepAXHmBC":"epIL5xQek6xF","HR12TyDzGc8N":"e2UhDN240JAO","EjUVEkBJAuT7":"7gI68PYKJlpp","y3PMMIHEB5sl":"QnXdrxtZQviU","ugK1FubixtUx":"MXSDAabRSbUy","o5VV88VJ3tYO":"ttYhu2MGojNG","xJptr1sGtJiy":"ohLGy1XpR9lS","r8mXnFzpxPyy":"uAfLDNL9ksQl","DjFe5CynFtZK":"w9NhSWPkxJK2","UA1w4gyZkGUx":"nS2mhNsDiajE","2oakdtXC5waI":"lkpKullOi5af","UYyeqBwitYmN":"4V6DIXzHjcHg","3DTniDiRPfcR":"KNT2LeNEhbD4","LcqrZvovuqAh":"Kfa517FaUG7h","mXoq5ztcztgg":"1Wytxxrcho05","Iyb1uZ174uAa":"H1klnLA4KbU0","bA2PJ7iraRcJ":"y6prEbY2CtkR","qXmPVlPvDfYZ":"l7PfZOOAX86M","XhGtdw4X4as8":"yZ4eJpMxNcDi","O7QcLSP6xnon":"qmWjTY50VIdK","Sp8UBpYV3scn":"t3Gob9rke04S","8guiRRov4Qol":"lCGNMevBmShl","EMKU6ZVZPEz9":"agWNVKb8QYOW","Z1dmmArpF2un":"CRjy4ye42y5j","MCiJgBRjjlMS":"ojHRmENImiE7","A1pCWt1HlTM6":"ereciA8ErASb","BtG8wQoRlV9E":"gLYmtcNERh3x","dVZjlD9br2WE":"AFSn9zwBChot","xwvM6DXL2iX7":"xE0wrp4nPPgl","AVjsZRbL9E9E":"xMVYkyimx0hf","iaW8fEqfan54":"jhOB4LF9IUFD","gjxHxDXr6MM9":"GZnN0XLRRKnn","bAMtpJuN1B2q":"fUn0wAnLvo2k","k2zKqAPOK7Kd":"VjTB5OklNW8Q","qNTTd8u7kCcc":"gb3itpemA1j6","cALT0cf8dOZ9":"DkwRBqkIudZO","xtqdscV392SD":"eMfSp8vcnOfH","sSvSYlu7VFRw":"QhF7Ik0NLKyF","LwqMM5j9AsvK":"87xahWhBu6JZ","7OwHefsKOyqi":"bVKwwzgM1Fsl","T4GtUTO9f4L9":"kiGOeXTb4d2n","XujFpuTyp5ir":"3YC9Ej3lZE8C","w7pkG36rfS6z":"3et31mPnf7T7","kVqWlFw7KfDW":"SwPig70Fge3p","L7YQntbR0wu8":"7OJddZVVn5Bj","CWH5fJRmYLCK":"zmdQtPzL91Fm","Bqa5SBskTuN3":"nf80TMfZiq6w","1CpWNTq8FDdb":"bLc4lTVq0uRI","IZpYxeMqNl0j":"8JQhqesX5mf3","Wsi49BeCcaG2":"nVvt8Oee74R8","ZKfg6pix7Rmv":"1vAKcbPYMYgq","wnmMFoSCrJcK":"tfJepdiSKbqj","hL8q2B98oldP":"TZ3ndo23I4F3","214rB34toezp":"tTc8rQ4kuwqO","iOA0wH3x6z8o":"NE3sXoSX0HBO","k8Av4cBkKxXt":"o4Rkisbl332s","tu9XQkNRvawR":"DBxuwRz8bT9W","59DB5wuubXsE":"NDygnGuMNNwB","L301j4xNsnG8":"yRtB1JcyM8m2","1rV9hw7nBz54":"19kj1mjU7d3C","mgJCCGR7K4u1":"MkHZwjsjgu5k","1LueBDaocwsM":"iydHiJdpR0h2","wc36wR4cdAX8":"k3B2Lv0VDP3X","Am525c9Gyl2j":"uIGmITFPflx2","s8UrOO1bA6Xk":"5VvUY1mmItEe","NhxJFlNonqFf":"sbqPdXY9MKxq","JYvZ5bYmuRAv":"lnuiXxXAn2Fx","jjF8Pp2QKoiT":"eBoclH7fa06p","fAQpSiB8qlFP":"0CKpSLSLxzZQ","8vU0mFxNsQlk":"MtjaoLnHpcf2","70PR9dno42Jd":"z4sNTKExl3zC","D8BrgxVcMKRn":"9HliwWGzh1HO","hNxs1ZQHgPQI":"EsEoPmi199Ud","YsMvNyiGTFP8":"qyb0d5LpUoRZ","8dAh8runimui":"tbTgzmdxrY6c","RTtOmN1NarSi":"RpSm0GP9vAEh","k0pfjl89cECP":"rZoeO3QQfavG","ilra2KH5WuZE":"zWG0ohS8lfET","LgM3oET7L6zm":"TeLpCbYOu4yp","eh1J9KT8dsWM":"xaBI2zlfz4yH","liEHPFsIf5U8":"g2IMyqgaQjTe","UcdjtgvayGOp":"Er0bPI6Hox72","fZwisXzQgIaG":"d9j54NbqwkMl","Fcv201hul5vy":"yfdjYSUMckY2","PZM15pyVhXPH":"PNMuiGkKWTwo","00iil9SZnfG1":"c6AUpHKz2TCI","NGZ52xjwZ4RO":"EQtPsaDcOrWV","FasqRS1ORfZb":"MrWUGo6TGNew","aqYf03sSUPKE":"F0XccyIkPQVd","Uilk1X70a8AU":"4ZScZmcCeA7y","JwfdIKHSvM9G":"fNHghz6pCmdh","OaO5m5eNpWnu":"Akn3JFShHTt2","rLDSzgHRYIq2":"kk0ufKTrWnqR","ngNixW6Xszxk":"oKfmt2MK1t1G","F1Git7wBbmmA":"4YVil48rSUCb","bStAJRpTJM2B":"ZosjXAzkwOSH","8l0Y10jDOdft":"rBTeiZ9OvKSj","M2H8KXQwzpsV":"sTDBGeAyLzFi","FpEbuOsbAgUd":"fFgbXKLGUbfZ","gVgM3mPLtVbZ":"Sr9K8meFHoqd","eFY2ad8u9AXt":"KxEGghYT7FbM","uy5C2kFzk33H":"A0Bpho9Gvian","eO5xCW1qosYy":"Hrf3QBrAKLZh","ZL0vNxtRvQ20":"Df90xCUmMj2i","uLwwiyrSui4I":"6I0GPGUnzEQd","zrcfxAPzRFYE":"QyE1HbyNZw9u","3mK3nLeSwK6l":"7H3SA5tRkP7E","0RD6OVs6bGOL":"YolKgOw72TTO","DzaVkAHArG1Q":"L8PC0jHQmCA8","kFBikfw7Re6D":"VmD7TJRZOlCB","2MIkLm2zP5ic":"PevW7fcO69cX","lnZXDXp3w7LJ":"NCZeGIE8dfc5","ebgewFLQ3FVl":"ClB1V9D5R68l","QwEuVry3Z0OP":"jEXCdVLlk3Co","C5OWEJ5sWA20":"mkrL4TRGj9RN","UuMScI3tEYhm":"mcoaD9FM0DRU","Lg5fHmXQHjQb":"WcSK5n6T8dBA","JoKt5xObulyo":"q8X2beJvfYzB","M7hxC9iscdZ1":"yqNlA8NVo0GQ","4xOtBjCl6AUr":"IsNOdrOSESFV","iciBIM1o9bH2":"sY5OTq4vxvj9","0Gf8BvE0pQSR":"5CsX1vT2oT7V","KBe3NYLM0qdb":"KseoOZYtJmxZ","jlQtRW7mVNbR":"adj3pSZWElKY","ONf1R6m4QZDF":"tQVqZiKWsP4r","B834pGQGcUjv":"uXZhbXKYdLIg","tHTMXzEtNje3":"gW4zMR9Ihsg4","rMA9UedQ2dqc":"HIeVvH0nailq","uwgiTK0YYWSj":"jb4Z4XUgqGun","PjA5RwiTuLOO":"3UuXMxRMX6x6","HuxnMzfpLjAc":"6Uf4hNCjlPHL","HngAKkgeaKnM":"33jpeoOObFew","IPkY6xiAvDi4":"gxvV2sef7Rd0","okOLEZDB9mQM":"3hsYirelY0SB","X9eIqS2MMHXs":"VRnkEiDjnB0p","lZDE3BNlshAp":"SUjYvHxh5sjL","ZQS9EEHVeTmC":"RguRTpzcXWA4","U2evtxbcbaBW":"6nn14b8ADF7M","qlyqSqcC9VuB":"AHY6Y77fazhE","pvFuwGzrobAz":"pWLetxTbzR7U","pir9R4YpUC13":"5cPNg72faqwv","xHdjV164DsYs":"zkg42b4oQkWk","BuXiXMPlph13":"ctddflXuvYUo","HtvquqNG9ANI":"ZwT13X3tmEKZ","FD5bCuuz3PlB":"7QyDyDgMjgBg","FHwTQWrGx4JR":"qWqosypR72Nd","rAqrgJ3Q4u2u":"XY1pngpvkhg5","GDXeYeGm0HkR":"7TF8nrxo3HMF","mbZk115gqStJ":"CLdkWh9rZyMS","EIcl96NIc5cH":"kjn9Th8uGkiv","RwerySduQFfu":"s5RGKtTANY78","yaaKwCF4qmjB":"OE7cuDunfMqt","QCfvIaaY1Mmg":"9GxG98GL28v4","UE04byxV7h9r":"b8gVk1G4nmej","MMfQ9Y0Oyup5":"YL4nUjZavsb7","TW8mEVRiCcjv":"GGdJEIZjPuv5","O7PDXL7gKESp":"5f5j6f2CDnHh","S4dwoz6b5QGN":"fFyW6gh2agdS","o4qGk2OtX2gR":"cooKxweXsRsc","wB4pzEosrP12":"3yf7thGarRDB","tiwFL6GCKpHS":"nRPO47ALUEwC","KXOWsuDL5Z3W":"M1NKRT8wiOIt","wtdlixe4enLz":"l4Ca9owHBKRy","lR148mo2TKEn":"FV5l4kV92WQf","k2PxGuHj1Uom":"H2xSnjqoDFVw","i4gTwKbwtnxj":"gjjZX3YHjBt9","ySpcvLef4hdk":"rMjrJuwjOX8E","p0AQUafthGjC":"95xiBP7F9jIe","cqtFWRcWOxpl":"N8sAsxQ76EBw","vccHQKYattqd":"NHxAmAYvQ670","ZRrItDQPm1mX":"sK0jplapEQyV","W5vvPimy2pMt":"5CAO8vxANdfW","Na8HKk0Vn1Jr":"oJhDWJ5pdq4S","KQIdu8ympIlp":"cCJHezfsFwdG","UDRrQ6YMy595":"48x74Jbnpm7M","yhDQYMghKKDJ":"AJEh9vd07fe3","eTRWcMGGiOF0":"Cpsctj3Zle8H","jSl74APrzdva":"VueJ1HfHRDex","KlBmQHY6DOg8":"2d3Vy8O2FeNc","NCDYSBCUnkOK":"gusy8omnFBJa","7WkBH9p4tnxh":"gTYAEwZZ9pLZ","TD1JQUcdeOrG":"bkH8TQTdxpyT","0e71mYisrxJ9":"SPQekWveKKqA","KocXPApao16E":"Fa8bOopfpUrc","Zk459FVSrhLU":"9uhWYuxMFfDs","nGe1V9bLfnTK":"N318S865drDS","ccFREh82liT8":"Y9oIGRZTZkYy","pU9h1deDP2ro":"hxNhFu7LVgPk","R6nXgMnnPQoL":"AiQQzTgwG8bf","cRVaPZiQDMGL":"HKSQLJGyis1x","ebz8IyOW8gQQ":"X7N2W45TQx0F","xRLpW1yjiT3N":"3qnV9pymd5gZ","nVJwSX9biLP7":"McvOZulJggnN","ymD3WxRdQiB8":"nojG2Y5tCJ2y","Z83XHq3UeI9E":"XswU50z16JEk","fznzwlczdo5y":"9ZjZUroE5uio","ixbCiCVGa8Db":"ZveUm4ELEv5f","JVaxOfoDIiWG":"YCU2SLYMWIC4","sT7ftvts3Lci":"CQpWOKDoXgJf","zG5aM5clAF5M":"tVnLIF1OahG4","PEQUEYq0giWo":"ZZ81IT7xRlQf","xc5OnjBeotjl":"c9qDW5FD3Wf4","SUnZqOwwOR4n":"hKriU0UixYdf","rZWHAtu0BtxS":"i5p56qYEP3Oq","4NT1Yv2b2nOq":"ym5PLi5KXrVV","t5dpLA57Y8Hb":"hHCet4JRuvjo","K8NrJzTods6U":"BIKlfcZU3TqN","W1PGBV7ZpSqu":"XZ22lpYsGjHO","ODMPZok9NNdp":"i7RoUPk99I4m","8Tn57NVAG55i":"CZiGbaoSFETn","PfrJIVe1UVbJ":"kDD5xqt5Rite","T61hPObFcQ3u":"sWAieGuCBUZP","lyJczFmsOgwE":"cjpXJEvhu9nB","yC5EDu3FOWoz":"hBbVS2YZI1Hj","ilctDfnCrjlJ":"WMRbRMWqZa0m","U18DUzbwOdA9":"lhyaKpLzH0iy","Ly9xwcy68lEU":"xNMyWXcdUPwG","TLqDgr6DLZlp":"Xnc3DHeLDbil","2NVuIvcj3ipB":"HgnBs98QIfa7","bWOTX2Iv9gaL":"e3yJtBLUPcAG","m6JOrauQP9Xl":"G99hBmpYBpXc","s7qBinfZMsrT":"jGSO6AWcaLw4","Np8V9Xjvab00":"m5giDE8IM7Nw","1nC3WPcskk3Z":"GbJhRTDIHvol","IgY5yhkk2nsf":"HWV8bWlBrjIR","ekyHWVTG5Daq":"6ekObTGJ6vUc","0I0R6igxw2GV":"AVLsEuaefsA6","8yEXsenn7rFX":"ttNqkYLnE81X","S0Iktu7ID6no":"toHPfKxM8rrf","xsErtfVTpJh2":"5vfFbItCKoDb","RTctdvnJ5m3v":"eKvkcjQIlM1v","jsdTGLlj9pxD":"4nvtkjAcgxOA","uNdX63o4xAgf":"LpvMwmz0iOl2","N2xPf3nYwNAb":"vHqFPa7hCDtR","EqmM2IGV2zE3":"CiazYtQN97gA","vmPUItA2oi6z":"jlBAvV79jN2i","mqgBOOyJznjW":"kYKejxKyum4S","xcPtsIna1RSc":"srwnHB9H6SG7","4ah9gqCLzdOg":"EJAyJTvQ2HR8","xga09vUoo3BJ":"7uUhSDthCQMn","TqTQZnPRzWUa":"EBcxfbEW00IW","1d9RPLFYQIeK":"tdWkok49vnKp","1gkDerahVeVF":"VAF2yGTZXDPJ","21F3mWpyRk7M":"mYFQwSFVQZQS","5nk9EzsBlQGj":"eI3zps7d4iiD","b74jKDTgWHqJ":"5UaOTbYNNoUK","usjUlCTTVCsP":"upy1FZAxxcAb","yz8wA8IT9i2b":"KJNkS20lpd29","7gpGSs5QgYdk":"yBt8Ao8AnJps","7rZw6eNhO8O2":"EgPeTHQD9w2T","jMez0etWLGy1":"aEqn1TyGzQ9a","tuojkDrSxKdC":"tgTJObvVkQMS","nOoRqecpsv7k":"STTMQmTY1cIH","5TSEAovl71FP":"dXxsgR1AQPaJ","O4vFtoJYAUvX":"r7FOfnRzojdX","bs83rOjlXNON":"33CiJ7GGFpsJ","hPHdGXjPPvvj":"p4Ja04EP93TM","5QashTR7dRmR":"oPsa7uQGzHWf","0bue2DTdINtV":"HjRWUXHeevEB","T0KX7LoeeSJN":"Evg2P1NI4FZF","PGp8a5kf0xns":"U0LvnltKur7L","SsPFNykOsqKD":"UOraaJ1zKrLa","yAX4A7anevqu":"LSIfzsp4oUPw","QRFH48ADFDnw":"RSI3PkHbZZ6t","lof7fenwX0Oo":"vN63l6Qt74aM","KNCOrpoPWcsR":"PzbL6YP1XBuI","Bssgw3I4NnF2":"0PxPj7XyBn8P","KGtGY2M1wLOW":"SO9smZlbmhOu","85hvUTqlMflW":"VgaLlPzPq6Bb","AlZJre4h1zqW":"ppQ1ka6MLZtT","OmL6l6lukUzZ":"xwbC1LvuIvrb","twTB6SPRLOSu":"VPMclG4455k2","8I46LNlslXj8":"4C9kmWzv46fY","sL3cYD5lrgVp":"DviDuXXABKRC","VmurUYHrVeKn":"1FNk9tGdOOSV","OpvDthnqaudF":"DGdAf9UIPhJM","d1kYWqEgVZ4v":"kXqdevgldmox","j2nCiOedFrpH":"YjxnUWrtc0a0","oJ3A4rFrrBub":"H95WvKGATuE6","4CkAHk1XICmX":"1YpkhpJl4Yo1","PTvUjcIuTHgb":"LkKpUsD0UHDt","blbT2Chd8PSa":"sFuHbZFN0ufU","iFZ59P6WVDAU":"HHoj6yPzPHqg","KZ1e7YWucQja":"cvRNkhbY8E3V","EwIDUag11TMC":"OV6aP7nVC3IA","MLZBxbkLCpHu":"VyIMTVWZhbEY","xP9lCRe8IlP5":"gCFk1zsOnZ19","Jk5biXeIwF1p":"WDnGmXw446Xv","esMSn7ayNyOi":"vPRJtvKDrYcO","AzA6dU6qQ0FE":"scpwvtBLlUjU","0U0w4Dd6MONg":"aIS9VfGEFnTF","c8Jo5oiSNifI":"4GRUlqEx2deN","dGne60VozqqK":"ZvhhXVNAHJYN","TpM4cxTB4GRY":"VfAD54jBktOx","UCjip0qzGkmD":"uYKRpGLU3MTk","GGgED0EfP9mv":"G0HeHfR6XWYt","oia6E4vW1jR4":"Q9sn9hdxArY8","BGdk1GoIbIKD":"zHDYVEy2DOEq","9VvIqyAwXTxJ":"e21HyCKQKecy","QEnGV49MnVPh":"OjMNL3ES8fqD","Jrm8obQWVIji":"t9U1t7bOXRD4","dBPeLQPFJIdr":"0tR2PPAQVCzN","ty9qFlqAgZ9U":"XPUwIQcCznwY","BGvTHnAkBb7O":"JpeEbXDfzpGh","IAKoPriZwpEl":"WqOWjzYgTNUF","somfGi3JKqV3":"vIAuQoKyluz7","C9GduY2qYmzt":"uONSuvNsSvJt","9TA92eeGuv8l":"nwNBoz2qVEAz","kEP16zKzjBs8":"VYXid2GOxC9l","BLckySvszxYY":"sxNUaz0uzViy","EhT8TlrlODr6":"jijj3A4l9nGI","gcMSztCQyKIS":"zX5J82hmJ4AE","uGL0mSUNnE0U":"3KD43SKSk1wG","y8oUcIm9Nk73":"bsjYNR5mXPU4","2Srvj29voyAh":"Bi4rgjArewlo","HVh17UeUy4W2":"9LeQ1T8Is7DQ","2zvycrFnoY0w":"H40m2zEWxYv7","V48ANlwi8k29":"lfueleoE7vHK","T6eGUfmcvFki":"mfg8uR7m116C","myVUYx6PlgAi":"yIXOskSTAep3","EVVvje23cbul":"mM2gck56ts2O","LRQ8ywDoMuFE":"K0xcFoZXbUcs","PJjVpNT7jsel":"GTG3nsyy85Xf","aREBuh5dIYAG":"FhjSHxhSkgKg","iEF7So3kWCWv":"Lx8FRCJXEthi","hnTjPywe4Y2X":"0sooUkkiLVVK","jorMqha3F96W":"db0ds507zgLn","yBXHewYTUyNU":"GBjDB7GdHqu5","fhsZttT1Hzt3":"HQbDc8NGFhDI","5aEotuxheIp2":"smGRljeoJqCI","romQmtj4JRjh":"ivgiRYCO64DQ","0I932rRQUAzN":"7ho1kmL7ZXml","rXrlY7ZTgj56":"9WC3ecsokaQ2","DdJxYCZTp957":"PyQITSpRzTuF","YM9W6iHtiPL6":"AkId5ey2EBo1","5P21pftDSJY7":"cXwtHQUG7Pa1","q684d4sNQiGA":"pLHYRHPGsu4w","sEf5HzUv4QE3":"MBrHR7v58zWV","UNyfXhn1fUgN":"ZJfRmjDWF7Ez","o9In30mszBLt":"iGGH0eonXqpA","zMY8NibvJLyP":"Gmz0JLspEuzU","jdTO8tr1p9Ov":"MXjc5egbk5OY","gFyhOpTcGgGL":"t8gHPwgbO4Vx","ItpA8nfRPsWY":"3urVJ2OCR5jW","pc3yBVlYKUDJ":"tOVoO6MonDAl","sMZNUwsJwudl":"mQd1hAjAG9Mj","ymQdDzfKHkKA":"1EIFTK1F1h9i","YA4r5cds8FtJ":"gpujRfnlwes5","3g693LtcqhBB":"29dKdkZ9dCf2","AfAuJvdoWnMq":"8MZyNgeGDRSL","zSUlO8bbNAdI":"bjYTFHnUtFUl","CAGlGEOCMDYY":"8MkhFeXO3vw0","wldx4ndxeObE":"ciSBMkWUqUc2","6wwmDDswMCTX":"TwDAMXgdEIut","GKIgwidXqRvd":"i6119KCXyLeW","oDTnPQsNY3wF":"yzONfZRhkYrt","DIM3tl6jFu7Z":"XBv4y1NEHM1X","cFeWJpIc4rla":"oVfebOjULcox","vx4PzYY0yzLM":"gIaiXhiKStOP","wfzZsVt8kG20":"nsrLcWRu7DMC","dWXJzt2Q3K9F":"Xj86FhQKTvO7","JevnGKdnPBqE":"ko2eUwXMNTjw","Jk1lbJJrYHyf":"C4WC1GLjfPBE","dY8JZL0jgDja":"0rFxOIsMBiCO","rFlt1clxzPof":"spUGjuk1BrZQ","SFbe0a0ONMd1":"Iw2AZtmPK7wU","bcwlgiXvF4zC":"q5TqbJwya5Nu","NKEJjlCs5M3Y":"WZyag5Bihut8","yZmvsw9ZcbvG":"fGELkTzqYXxb","igRYU3g0i2GV":"1wjkevCKC7Oz","CyNpX76mcLgc":"B6YFsLRkXFAV","IuMLzQNPp65F":"y4ISbbAGpRHN","4n71YadXtPFf":"XkgDKrIEObGz","XoNqt9sI9IOg":"N5oWoKC3niZV","tQKlFuRYV0TJ":"QuLiZwnof0fO","dAnY5DrveOEh":"QsJQIdvNXSpW","x0fMLUEq4U6a":"I40Ph7oHebhY","Wll1xOlW2uTm":"1QG28bCPVi5X","3a7EmBdRcNcA":"7q7VyGqoXcvm","keth4pF7XgiE":"gW53fSPRkdQw","FHAQnEhxwWvQ":"OXqIARpHgrRi","6pVHvjkWd4ER":"FYf57ClBDaCR","8XgKpH4gVeDa":"oHZaNgCk7vLF","NNW3psVD1YdY":"0tiEPo1ngxig","pyjfQ0VXXnMC":"N1IBweEfN2je","jIaW9m5q7jS1":"IJgLm7dpS1xO","Y6gcyC7YdNYj":"Vngg3uvoh24H","rXg2XrB888pB":"GnQhEjw8nBXf","uvbg52GBLQwi":"KXEi3GjrtcYM","RhARSePX3ZgT":"7DkPCgOVhs1t","H4SmR7IcqwYp":"xqeKTWweXDTG","Wuxr8R56YTX4":"wUgDZo0Yppqe","e4gjczPh6RtY":"C47WX8PGhpsV","SivKmVN2X16a":"WFiDCatI0VZ5","5xo1W90MzBfE":"jR4b3t2QUphY","ub6fClUzjZ50":"QObK6s0trn0m","DlqcAmuTWlhK":"Rt44upEUmrU8","2VjEJEuWPkej":"S3JVq3QU9HgU","DqGWRerV4elX":"n80XzVnZMNeX","9PVFTsxrIA9T":"NxNGGK8mGS4a","bvD1PZzbTmnm":"KoeXsMlpPwby","7eyLvrfb5etp":"us4nHvmquYU9","vOdbluz2bqlQ":"6XXbNS79xKdo","Cfb8tz3gjpcA":"eiRZ3J8cX5jo","WNKAmvRzZCSb":"DU2vYRL6ffPQ","vFDbZ300tjHu":"ORbGsQzYEPM9","nXCIOx6xIFz9":"EnIH96iXnl8b","N0BdB8F7ntpZ":"o7puDCIZGNr6","GXGmA2mHYaEF":"XpIDJinOxtx3","WMm663wd3trR":"8TYlXYJ7WqHT","BxrbRlQ3flsL":"tgIeHM0tpi1z","0newfzLBLw30":"Vf7caFrrWyna","fLSJtvEHIes7":"KsrNrRheoTlM","ZavBU79hcqt4":"Tsph1UJhD47K","bl1eMJA9Wnp3":"JXYUOHkb8lL7","Vm1T6B2fN1YC":"dnwGZRWNCf9Q","V0Beu7cRj8JU":"vQ65heXD3EGF","DQ45qJLzupsj":"XMfj1WLuYFBt","9BE4p7bYPcX8":"94LLxKPWjDLZ","IT2mf3qQ17y3":"WMnIIJZVzFeS","x7kB73rdmqP8":"RFuPJPJnpwoA","oj53RUUihjHO":"VEfQ5KG6iOy3","m7nASfVI95b4":"iZ2nDjFi76cq","eeYkQkLroiQR":"M4B8gBauzpvu","KTPuprtUvMW0":"xuk7wEgHnUNx","k5mCOPMhHF3T":"KIaIIX7Aytv8","gCI4mwT4vS6z":"obJ3vvsO14Go","QnJZ5wRkPvT0":"KBH4jtNHODa3","JW09eGJGbn8z":"BC3ErnExYgtb","yezVcLikCSkn":"9NtXqnM26lFT","XeHAdTXgKsNx":"rU8CswBcjlCL","zuR9ej3l37xW":"vSwCJSBovtRF","3sDcO1I0AWou":"yzmgjbdf1Dkf","xPkFNZQjMtZp":"xqmkybrOH6Wd","NnuDokUzpR1J":"Q6Xtzu2om7s5","YaKJVU1KYnbc":"NczKv05zl0hA","8JjN6yXKonr1":"8njVorYiTmlx","rKFD1zmj2pMn":"eUYRONVZG2xd","XMbyiacVUA2q":"zeULU1DFkSed","nsWDiVt3tkvS":"rZzoqKVaLaGa","aHlMEUwmeXXF":"wpRMFnZKaOpG","XZtmAlXBEHrq":"Pazp7VaBjsw8","6G0HOSiDu8QE":"U3wBbutktcnG","76m0RFPRfkyj":"j3W3rFxdk9gS","21nckAdjfrmm":"1TpCMt8V5u7t","hvPQlVuZIJpl":"Ev4xQ2Biza5h","4aKq068k0sEO":"bb8a4DZxGTnz","bmCwg9IJI9z5":"rQnDRsbwyc9r","Vs2IDxQfwDWT":"N0RNwfUFHGQF","2U3h7MsHxRrv":"vIhLzjJ32086","6GwFqHWdB8JS":"U95r0ajCLaXd","fD5AX6mTQLob":"P4nNLo9t094l","JcDC3F35u9C6":"EJQSh2EV3lvb","DQAH2kh2AAdE":"6CuTG17LfoTE","HwxgVGLNcdSg":"vNhr1PgqH2N1","jwq7TvYtLq5J":"H2a6xUa94it9","hq2VKSd7V0Xo":"YdqBimq5GMpC","k8Gtdu171ZOB":"hg3hnzg01zBT","ZoAYu4U1XYYI":"vPqVLMeNQFyQ","48WxF2Lh3IFS":"qSEXe4x9TMdf","9No6506fbKjZ":"aSJtmwqirM4P","Txg5FqTlkoob":"hTKIRwgEulJJ","JAixVAsY54nd":"9oDCu6CqI5fk","a7oTZL4rvlHl":"FiNeuvhR7tmG","Kyc3blhXsMnB":"py9F5jq9HP27","iMY4pmnreAVH":"1e5hRMZELk9m","wTYAFsWs6hBr":"SCzaXnJLVrzy","d2wmKpKps5cB":"DZL1bXqiwBaq","B1SJ98L862v3":"8BzDAKM7FCJK","PlCq17H37ZPp":"P5hkcaYXTN7L","03cTuIw2P31J":"EkRd0GsSPZtB","syK3wCgHO51c":"LPrOHjrlLRfq","XdgmI7PHd2Lp":"bBGb9oPbZiUr","6gSfGezVIiwt":"NbTrOL6Hnaow","ylgJr3hzHGGU":"gBZb811apJDj","SbDmbcyzhnhK":"klOs4eMXoapg","pFG0tL6n2TA3":"fEEWdfUgkvkj","qLCYHsKolviA":"j8hzmq3uRmtV","VVL60tjo7JDE":"6lZQDiCigg6Z","l1cJBg8p5kyE":"BlPT5aTsXcb8","pmEh55LnR89i":"8Eh2ZgThfGPE","4cYEvr2wGVO6":"qBNtRPTOK4Sf","hl74WWI6RxK2":"jdBg0JRb4Gk7","3bpSMW8c4yli":"CXV3fdbaMs0O","PRcbWXQrsrTd":"CSjuDRd2mJYb","RCEgpoua6i2E":"gkJGXrFlTuBC","uoKztF2VRlef":"oWWtFpr2Kmrc","zOZQTlzXr3kB":"DStnRa1wfklc","7IE8ZWcMHDtk":"3eZzHSE3mEGB","AYHyM5cfkkYE":"xrncG39KpuC2","ugNdjAhWGaLf":"IMGNnQxchoUU","N6WR8V6DSZUj":"TKAQVBUluuiI","cr53fbpQTtBN":"Uh6GsYPls0CM","8j4RSiEujt9v":"QmJIoXH0LLEm","eQ9ITRVftgi1":"hGX0SCSX1lKV","iV5tPlME3Jv1":"0ZQyR3bx9hBn","ICp0fesXWfWk":"3DbaLZxc5kLC","OHvpGRYgqvmq":"2D0jW6dSTD3x","Jp55kTcy3WXq":"VbpUVbhPegFc","ORwvssWwOq45":"B0x9265rkyxP","z3uffJAXjlb5":"1LH6tiPbVF21","76x0uGtQuhDc":"Ks87mu2CNNlK","jTag2BXxRAhT":"KMydSHv8XOox","s8c94PjHSUhl":"uhbYtjgUa5S7","lyEXitFnkpIh":"BP9Hltzutw7j","xsfl9R24uOuX":"coZOlXEAeVwO","TriOEtw90xlA":"n8VQcaiC5cDh","sZBKAterXb6t":"ssYw1e9Cb58R","XfBPvXrxG6xz":"jKM4VQhrfdeH","Ji7mWZzkKH32":"IyaVonqoOgPT","Sm9rj0tnklnW":"FNT7jyfRdCIC","NX7dwIQX8oAc":"mV9qfkZbbrZl","vLkDT8UFWGFU":"6NmTcuove4fM","UBgZeAEwJCiQ":"7Vnz6ZXDHIIX","40xb4bY2ie9c":"tF1RwaPQYkwX","C5j7aPYNibBP":"My8XVTBg7IFx","LYN2R5kenxpK":"MCTo6VOTosbI","2EsAxA5RMXNS":"LifVRrCKbrf9","ERb1TMy3RHcH":"J7ou4dyFZLWa","nj2ZeJzZ2fgK":"tyGoQrAFQIuT","49wm4DsWkxMa":"G0Pi3caJ6QYN","IPcd60ny0dpy":"D6DxQirzFPYo","fKtWrXC1id1Q":"mWBKI9Zlg7uD","GkXUMU2rr4fS":"4OxLAe12HAFS","ncd6ahvkfp57":"fc7J9Epqu5qN","ZmNo8UJdz9Bi":"MqWIJt15IFMr","omqqRyVvMB0u":"NRHGbjgDmBrF","32bvTFVwJk8U":"FgfqtuaYAhwN","OtNlqphzDPkA":"wDAb4UOiSYi6","4UXZUMJLvgaL":"FjQG5mk5jXBq","myimFE3Nhkjl":"7rZ5iICSAhHz","azFBrwn5yz4f":"hGrhZHLUWWhh","w3pVkZBHYPXT":"nBILWECAbpFu","JDBLBucqVXLe":"PrCaueIpP1ls","hYLHsglcTNNN":"HnBXCvVb8Lai","CnFNIOagUNeF":"w43oZK5VqbbX","YTRyeKCzXLyE":"hoLBSOrG5Jrv","cCq8VHGElgF2":"3ex7UkNxSqdB","QA1EA1kkTQDx":"6PPtzFl6HFRR","ELsLDwctm0EU":"sKowHOWRXXFs","GuCdNUGDawgP":"mG5VQUkWyP7A","AKjTcKhwxhA2":"tFfAPW37cPyz","fdmCmTraxshf":"dq85pBBqrMs7","AkX6cCji9AvD":"OfL30HsMfoQu","D34TMjtOHUji":"y0sXAVE7dQoM","hEBJpExD6mxY":"UMUkYgmzp7k2","kS0PfPh4Qukh":"msnOcU6OxJpI","uF5XbUqS3zZj":"i358MKKPWPfa","Tr7YC7X3Zrjx":"KBPMyfJCwc7e","OthFCecwWb1N":"JyrxIXDUfhCG","gKEppcrV07yn":"hRqll7zaSNZL","Wik64WKwlNLX":"4csHjpRX0uxc","kCP5Vil0jYNa":"IJzm9wnoQXZI","TMnRgiqY85B1":"0G6xuQPSJT1s","wCYPvwPRCWYY":"d27q8h5lOM6J","lBDAGZOX6XZk":"jC8ZjY9Uc2K8","TzfgQAZeESol":"3RSTpIiTESlM","kisrfu3xBY0h":"xkMpgss6UsHi","j5WT6i8gCk7K":"uncs4erdj7jC","SPNbvINYT5RO":"ZjHp51HSVlUj","fWxJnnOpGDwc":"aGUIrcs25cHJ","4K5HTxj60nVj":"rhjZXKBl1ziK","lcXCEZo6ae38":"VV9n0tPHZ6Yx","Lg0q1IdErjOS":"wLuxtQ9Ju5ie","s3aOVZyqY09r":"cRxEmE7XdY4d","RJFxUfpvYHug":"dqIAMMNoD2ai","WZpSkcpuJwZS":"JaCiW3Hgt3s2","KM8P1tvWo0IX":"tDJMBLUA3NSX","GTy9v7yw5gMX":"5V2oqQQC6sct","6LChPTjy3Jq8":"tY9hhaPSpevZ","fauUwLxvZkUB":"qq6SStnKQ75K","QAHBQbNV4ksg":"lwTw437BPn9C","dJKL9XkZmvUa":"dJc2wZbMw88T","VYDnqzuigk1V":"zfrYrRPl9l4E","0OCN4O3Rj7SS":"FpLdEgxTYYOq","EEY4RZPduky9":"7jrcaemMbabk","dZ2S2JFIDcZa":"HWW2CQGVOs9H","zsUcTM5SwJan":"l2zgtPEKwxTw","2VK8rBxDCQ4D":"sMQVvmJ6Vapy","Qo115F8B5BrX":"xMsq2I1Ohv4N","60abMUWu3UXl":"6RdnfDHGJyvA","BI6pn7xjYEcP":"noYfiQOe8uIW","yaaVuP7KUWx8":"E4jXW99GFNzi","LYFk0ES6bVHO":"ZNA0q41KqEXm","WboG6mb1Bnn9":"TuG47UsEYeYl","mgTVSEPBOB5r":"zliJVJJYTB1b","QFw3WbPi27Iq":"wun2jWuupSfR","dSnBMf0yrpUF":"qmuFY2GIAn5Z","bm0Czk3XYuCQ":"bdvnO8c2nq88","lYYSmFQgFHN0":"hiCeQd7V50KE","ouhr8sxUzbbW":"bjccRteE6jAT","0Gxt0otlGq83":"iWRsdPUOTDiy","DRZav2wiBUym":"5FFEcC39M562","6a410SApuBcQ":"lO81YZR288t5","c4km8LzkmLYR":"VQTzco7fU9um","FypuMpRFWaRp":"sreihwwJOMyd","WE8hsAGeybyt":"PgezFHRXLX6b","Ke3uCUPsPD8m":"yDfnlRCouasp","zW1jjKHpFa9p":"Hk1FuIe3GK8i","N1ZXt4teQO6I":"0YPz9uhtWW3q","SMDm6KFInYOK":"l6ygVtRSnTWy","Ogb9Vc2Jv8J8":"wRI93OVMSn1Z","gbwprXH1KESc":"UuN4GU6geeXc","Ac0cJvbfilAG":"PIpDosOntGJk","RaSkSmxSALIw":"0qoscJDtRgBg","EqwtcMaatOhF":"WysOpadf1BdQ","Q0AWjoqgpWdw":"DL1QYhAA0H5a","JWzYt2MQ4Moq":"b149A5F306CR","RwzXdbeu9fIV":"Usyi7VUUxcqY","8RycNM3YddVC":"sPdEeukuKGMZ","E2XtMlQvRyPr":"09gh1apzN0H9","HelEwLU0NcGw":"naBwJUbeUIVe","PmlXcmO2MNyi":"L7SI70lpBcB8","mKoP8NqHbJC4":"NliEXei9NGCq","9KhzlNpodyaa":"LeFBpo9E4P9O","9DLZ00emS14p":"vlhN8ed43xFn","VWgprSdqIaLv":"AdvpSj6JiYwj","prGkZ62wL5CB":"JSFychnYyBhy","OK1dk1xZ6xEz":"W13vSdOHBKOH","8QFhWbEhTgRh":"G6LSpYhJ9O5p","vViNHnsPlVnd":"ycQmbEBFCAyP","S5CS1e6bI1DK":"BCntsBj4KoMV","trh60cmtZLLl":"JghwERnLVdMU","3UYbFy6eYJ82":"RD7F2uTockJb","djN49xE1ZweV":"JbNNHgj0EcDo","zAB98xvWX732":"BWv7T3GJiQQC","qaKGzwplnKwj":"OoM7E3EDbZkA","wdiDGOH5oMTq":"brqL1phDudZn","wiSef1cmmif6":"dFZAJZqrK3Ro","SqdiQCfEws4u":"anuyAbZmjj7x","8RYk6GezdKxW":"6uyKE3yAtw3k","AYKLUucE8wTo":"eUhsbyoGtdqq","gLhipyayqRuT":"8yoSb3IntOTm","xPZiV9G211rQ":"LnMTVAEU5Rix","c7RaddJuyRWF":"9n4TeovbaIpv","q6fYQrlRgcIk":"IbRRqOlGNd7T","Ko08214IPLJr":"LmIpEthltLbq","7egJyfSZeNlZ":"ayuHPNaAPfLl","GuI8lNHCxH4q":"fxMSIVC4Auoo","lUCfPsf4JOP0":"9XLMMjuNa7Z6","I44IdcFwK7BI":"aPj4UlZr6czs","PWYAUEO87afo":"I1TGDHHq2L9u","xvMhwXJVhUjT":"T2jET6751JDx","Wls78x3SKjQR":"IMqyOD9MByQp","H8FbBaCqxdNZ":"VTMESsTT6car","euhJgLOvygVB":"APKPgVNJSlq2","Ro3gWZI0c3Hf":"kCOQoaPT9Qcn","Ol47meqqvOx8":"beZ4glBsNRxd","nJwMfkbWupmV":"VAx7K8sVtptv","bTlJ53zP7PEK":"Lib4kABNNFtp","5i3nFtONXfW8":"yQUh65G6R0Ax","orcurU1WrzNd":"ZgLKuPCd5DgD","xEkQJtYP9ZqM":"in1HGffaGrqt","byMscxiwwtqo":"BrAPT0SRSvha","wcvxAENEooZ5":"tfIEns1tDoD6","3oaPeyWpJlsr":"XMJ2naJq3sdq","sKW7nmnUFzkX":"H33bA740lwLY","2ippQTSyUPRa":"ZQ5s8cuPdCsl","6Yo7gBWZHu0v":"8zlWLvOYcN0O","6wW1dOpufIt1":"t2lU4XSsI9QT","HvHCtOXQt8Vz":"hFCPmpHvC1PY","mCwO5V8vIFrn":"s0ReWaoKU9oG","vYlrCVVKpcV4":"cY9nxQRuskOt","UJnFw1HbHyDI":"05yEw5bfw06H","V6SerduepIW3":"w1dt95OLL3xF","Wb8jDrAjWqKY":"H8B7o38DOnrd","6lcgm1qviSx4":"WICkJ5EHJGij","HdObqYqNETEK":"AqKWypoOoKrA","AsztFR1uPr3d":"U7tJ9ixBtKQG","UAREgDMQxfQm":"8Xp7q7WLE4oQ","HKocF2f9wZbT":"NP5V0R3mK7Cg","DttKDRfp0Ngd":"kLdEHqbaFKhI","kUUiBGg2AzTH":"cgpFiL7esHrG","ofLYavtklfdQ":"gyOWBlkbDbCi","4sFuNMcYAtJ8":"VswX2Tl5B8Gf","1D6Gs2JQxkTX":"uilUfcf5jVw2","FNoThLhDOlQp":"kf9qinT7aYL9","uw3mUF4fiJSN":"5wA0UuTEpisF","08uR8EhjKgTq":"R6FfsBy0Y0zO","uIiKVKQxwwwp":"ovjp3BLNpCxE","8Bc0ycbmo4tU":"OUUOguPfXAVV","dfCSr0wTDYRK":"hCme8FRBA8aA","21kPo2Lk59QD":"SNuBueoQZxiF","QTEJiUNjy0qK":"p18UDz3fkEzY","zN4MefjA5ge8":"avWgdI7nIZN6","U1fanVs0D8HT":"9TP1ynDzIxm6","1JIqcprFxKjM":"swQOCXt1YLUt","309J6DiK9DP7":"el7XMNIZV9ck","3ALR1SpGJ21Q":"xtPsS7pKQAnO","bqQsCv4zRDpA":"3nnGPPetZEGV","GWMNvyEb86we":"HHFbuVQXnfpc","rg2OFkQnVOru":"gkYrhq9ztBqM","EEHzcBojrynr":"KyTPQpPeQLrD","6bas8ABQeTiM":"cT5mZWXOjMrH","iiOlBxFziI7V":"Y0vTgalE8JMb","YhWbKEBI2Ft9":"TvybPrmlbqWA","aDqJdXxI0Blp":"axyDY5mzoQIp","ofO7gN6qqCMI":"hS58NP6Aua33","jVh5XkWZM01N":"g84zz3nogvli","BU727N7tuTg9":"VL8Fyw6nzBO0","IvXjTPPJFUSn":"sUv0OqEw3BL0","TQNdLvH2D50i":"ZQWzcZ9DBlgT","05tHjl58H4V2":"3VdfmsjhUMS9","st0bobpjDbwI":"Qj6qx8G95Io6","IwNpFJhvpD5o":"rYvRXHqxQjez","tSixaD48uASu":"6uDRgBUf0kZJ","pICahw07oZ2A":"iL5EegmrUJO5","N8ydqTjrYzVP":"LqojsCs7IOBO","4yQfrVESUPVo":"TwAPHrowwcOX","wzZsX04aO4zE":"ShbqXXFGyzsF","PdpRIxqhTSer":"VflqyNbPIxq3","sK4cWPeqiCcN":"RmEA1brXroPj","0c5AU3Xwx2gr":"OHhwxm0XtVzc","73y9PCMIOl4f":"aQ2EGF9xAQ4p","rny6Zvw1kvWV":"9HoLePpxkLz5","H4B68cADnUZH":"AyzXp8fspqMQ","ETwawxa9d4fj":"IO86JYbJ0OOv","QvvzQRJf8fMq":"xRih5USyOQeV","TQfwEyubyu18":"GtDcQfMBE0lN","EiOLlPFkn5K8":"Ugs5EVqHm35c","M7a90GhgDeq6":"y3fYtffMkyBz","DyOoIvwnOp0g":"9T4I8bpngArk","8ndDkl4IHhXK":"uA2iLkD70ow5","AxDcfwXf2Elc":"MolUd2LAIQqt","e1M36JobzLlY":"8yuS2AZGnkto","Q2SptWI14TJJ":"wBR9jisvXTV2","OHiOsuMRRfcv":"c56v4LRQUV3O","jsJOJVVsVajT":"x5YJEouOtpDT","NTntvfdvdClN":"Ah0aGgOODbmH","fQRn48FTWodf":"3g1ofc7PHVJE","kxdDgErJpfDT":"kHjzeRxxVwNq","vOVExXz7bwq2":"aowYKNhuhzGn","Pm5cXSNSF6lr":"EGfm3Hiur2RO","VmFT1nzeZGnL":"TwdrY03zoLej","rgnMIUgWIohU":"FcRwyctNsSv6","zOOmEPdjPpUT":"QiwF3tn27xRW","eFa1UMi0OLKW":"sCh5vGeBHEJH","9BjSY6hf7W4L":"eDE4TlBxdFTE","3eoYmGPQpFeq":"kP1dexbMJr9X","ZRA1hIuwMBpB":"WzFIoBdDNLTc","25PxbTGSL1Wn":"sajyG1L41pck","pjK8w6S3b0Cs":"v8x07AD1y9rD","qWVscljf25Ja":"XTf7ctPVYGZl","ddthEqRDdl63":"CCufuQT5hiT3","iUMjmZzBd3y4":"HnuNQ1MRlgW2","P9l62WG7atfK":"EQLZ3giB3MD8","iO1GkdSe1Q7d":"Qj77d9pftcOQ","k4TXnNi9ogqv":"MlROCYxNMFQB","HEe78lipMZ7z":"qIVd1lOHz4SY","2Jit7w1zQNBp":"aQx4L6TxEAl8","vt0r2TSqecC1":"Kbl5yrhN2UcM","dmgVcZ3La3zf":"33akKScrvBFK","B0ZU2iDLeuGw":"EhHQPp1ZOHd1","cdfKaaRUcyhg":"QuKSc6d4YeDa","n0eGvd3BoxDs":"NQNcfmZLVT14","jWYgJDDyem6b":"zLP0v8I77hty","zV3Xm0G3OciN":"5JaxzRFYIevX","JNDgvkA6RmPR":"qxDOhZntPgN4","QFjTerlmeGti":"5cAf8DB0GTFp","9Sad6BGwYhwK":"8xlaiN5kZc3f","VgfvJfg2YOlL":"xRkTfHINiZdz","M8j4axUh0F3l":"o2MtHSNqSY2V","ZnmGRLW3SRod":"ijtnWI6bacbS","JjXB2ahtmK9L":"Cjs9OSDyjiUL","lFHvMJHL5fVi":"KRaL17c8SExy","5QnbMdoUzSX9":"2GwFv1vXjKHh","s5WQNx35flcM":"SnmgPBsspSXm","ggVFYJnad4Qx":"WgGug5zMDkvn","mXabWGIunJGk":"qTjxdGkxLuQK","Q1cWcZ2PjfWf":"SEJYsx12Q475","MYv0sLShNsky":"aXpQdoWsqmtA","ZwE4G9dZiWNO":"8dfscmwK01Ni","fT4aVSWRY9TD":"Yd3dsQIJMv1y","ZNu7LQajchUp":"l4dBjDY47wlU","zfLelB1xSWtd":"4xMrlH8r8v9H","dyyIQVekGDuW":"uS6PzdqVE9eo","1nPCLblyyb2q":"LjiqicKGROPa","LTffUEvq6Gmq":"AVAw0sGKiIWv","7qTnWkEe9Mnn":"hZq4HSLDvMJ8","TvXAB7o9jbMj":"vBPT09D0w9GW","CldwT1ewth7a":"l0ILUb12D32x","2K3iGGUrEMav":"emJh9PslidnE","KSAah0Mjyxig":"eDWEjJpHpJwm","l3OYBRhbpUIL":"lw7KDLkP9xYz","bC6ct4IecT7j":"Qj9kRZAKXmO3","zYCHsaSe95ZC":"MNUjASYt1ogH","0bglf1DP591D":"wM7EqlF2gFcq","vMOoQZOLMEb7":"uxn65lYCf7cv","faiVqo3MFbM4":"vz59ZNtDgZPp","ktAvycp1AwIR":"rheJeBQKJoF4","FLdjTyGkd5eF":"qBDJJzGdGOco","QHhV9gpl7CnX":"Hx7F0YejXOdg","dWLqKZF4PXCp":"Lmk5ht1bys9o","neR579AApHAV":"ycXJOjpGPYYY","L1FA7MXbV1VQ":"ByZsYUquFekz","IbJv3mUfok4u":"OHMXvTSJyyVf","hIlryvW0WkMT":"VPiDd3PmmHwh","J58WEFmxpdCP":"kzb8I8hn5CPy","HJD2QWDaMNJW":"ecyyh7CDc6VY","xU2Z1Raw29B8":"Lo8bA1l7bdyj","6hOTHe039KbF":"AzPw9A3MuVU7","0Ezo4rvBJxip":"2DWnxm22Z059","kJltUyQcgO2U":"ldaLmeezQWfF","U6J8a4RujJy5":"qM0TWsKnT4mz","cXDJR08IBZaU":"QWxGS0UN4fNr","1GpeIn9X121o":"3CJt0OneW0na","XyQDYi9yL06o":"T5zvjQ4bYm3K","XSoZir4I474J":"nqLkyTv9JH8D","1aYvBXIFe0w0":"VjOaT83FWnLS","rtD4TifN3MuJ":"pG2XYmTTiaZe","YXzTHgWxPTPN":"DXWksPIYGGxG","ZyYNZRPRmSp6":"peVHItaLs3eX","elYMsZiwvogq":"W6ioALnX7n9m","nGDxE3B6Arty":"uqN0ooq3cxmj","Yga1u35KywNT":"K7vqFHLisdQK","cnFpWYiTh65S":"gQE9QOasPCKp","xB8IcaItHIkM":"afDOhgx9Z76b","tRpeFxmkDQ6e":"T8c7X72QAuPt","Pltr0tLZH6Kd":"Q45uewXmWKBL","EOqzIyfbJSl7":"qQzM59mzqYnA","b5hPekPzcnFm":"JLgAUrvllizE","u6Kiz4vM4vCa":"w0UIP8akTEY0","pEgkyxF4spor":"EMwpPLVsQb8S","E9kyzCsMWkqo":"3hikS1e2oF45","0bLD3kvTwNE0":"hphTwDCF288d","Wp8bjkHlHoC4":"0n4PZmB9YRnx","OorePZCNFERL":"iVOTbX5Pd1UP","SJB0ymYNgSdI":"o8kiiZFuOLPD","qni2JqPHKjX0":"ggI0hFa0EqD7","Q92ogTiAxoxL":"Mc83CO9YSVkI","IACWwuCMjHIa":"wOBdFDPDKYUy","OyjAEXviFAYK":"6hb6lplF8bF9","jTrHJ3afytwj":"3t6oebIZIGVJ","cU1VBGABFEQk":"D6BnN558gl8O","oUJ27dsO4zhY":"9FKfUn3wLCEK","HvROYEESoxeX":"CXed4IpB9mW9","dKe4iG1yqhCy":"w2uecNjxAXlD","r46dpQKI1Qxh":"P4ZAW8pBpbzx","dUGFHpt6mSs1":"zYgSoZGwBWBI","oQLomhZtdOhR":"cXZkre619WgF","B7A2OK8usbnp":"8inrnIb6wVcl","TokuL5m3aUyf":"3k5FSJN1StVn","RDv3YmqU4YAr":"oMncmAkriGdQ","BOspKEV8juI4":"9B1WAjSfKRLL","Rtxboost614p":"YKWD0Pua4Akz","XWA7DkT4dOU2":"ADJI2Sn7Lmjt","br8WDTeqsHz1":"WuzdIc63oCH1","rDnPeIBSKRiU":"ELgUVfjPwhKf","9EMHKPiYsOXY":"pAAhY14hFtpQ","iAY4CEhXuADj":"YMaGnvCwSU2z","cendphTe6B25":"Es0fplP7ozJv","GKcLCCbNGxEg":"okCVsL06XfGA","9KkFlVDJinY7":"g92XWUt1vOfv","Jm540aU9wkoZ":"lhQXZCvAEee0","DTJzg2DRVEu9":"Ef7XEuN9tv25","nrbbMWb2g74j":"XrsEM7svw0Q8","R03Z4tOy4l66":"PYMhBHTqF8Hm","DCNtqEzHWykl":"9WXBfH3eQn0H","QMRi1umDdX6G":"HjLElcQ0akDA","B6Z19agpGHq1":"Di1dPqQtqHr9","Xfl5joMjGAKP":"KIc7v561QnTq","m5M5E6ZofBSO":"FzDNRQmpJBHo","n3KciO8OsYCj":"jMhvqaF4Vcz9","0tZxLkHk2A6w":"bq6eiqyzEv4e","CQ27VsvdLx7K":"DCntheysfXTW","zR17fXoR3Kab":"VXjJC8WQRl7k","TOu2iW47x5Wp":"3rPsHBwEBwYT","ZD5O1h1RZSZ8":"3gUPNcKDNTtv","0LnbdzyEKoLB":"JKgvtSTJwtzt","h1F4g3gHvhum":"D2jVd2vfcohZ","NtMmRBANN1n0":"lbozY6YoQemD","pGd12DGDBsdd":"sMnVOyQTbr00","PoH6EDrANFh2":"hg6qYLvY14NW","QjaxYrAHdzCd":"gDOrWhVIySxo","2VJMtM2CJ1zt":"2EOglLkaRjc1","HNi9j9kjDc24":"3TTWzenzOdCV","axEam15C3uRa":"wl3PvoMWIy4e","JHdEJqH1gMDI":"musLKUqTpjaO","978siRJyAir2":"qo8EatlSFzGu","Nmp41DQXA39n":"skh9bdiEwJRe","1gD7Jp2GZifz":"pzimgZTH9vuB","zKnzLNPjsfci":"ZVp44S8X4dOu","bbppgbc9O3sx":"4u6HlDTKkba4","jA6bO2cTaiIK":"T6Z88LIzgrIw","Oi53DDI0Z3V6":"fwBkmP54fzQM","mWuznt5hov4z":"M9KXeKcLTQeN","grSqlxrBUykN":"SiObZYYjIvaH","gQNoCYEudxUH":"wG9qMSnguNml","O9CYD3P9dxVC":"E7LRCZb142Og","fElXPlvysKtN":"bmd0vLplLHl8","z1mjX1nLIDN9":"JUHJxF87utIE","umEgnwwRICBz":"Iss9Blp0z0eW","EhSh6nlPNjYR":"Cqy4IzCh63HZ","72KNtwzkGQ3J":"IjFLV5Sqd7ZK","7IwACIrdhNNN":"9dFjPik3V1Du","3LzFkT7jsa86":"IaK38hnfcxsl","lAPLTEWJpcPD":"SdGZVjs2JMHP","vjtmZMVewXCn":"amwOdCjRT1fN","xxfWoh7VoIHR":"qWT48DJ0MhBF","BeraIpb4MKsc":"0C3OQsSvYZAC","ZfUbfvTtc4w4":"41zAkHu7dGms","oQvzTSw4EZVg":"N15mGOdQD5XB","fg6ByaTPEViT":"mVGTqJNg4frH","FLLdN3yB8Dt5":"OAfIRXWDUhHW","zaO3hQwEOz4e":"xYLI4rSjCjN0","TwadcJ5PnGre":"q1VTkUAXvXYp","KgNBt7EVNhFS":"YmdZ0orgNulL","FlHKlm0QMcJ2":"Pz2ucuSqliJ2","PTnPScEtxBUK":"w2l96OCRzStK","817thz0aCh5T":"fQti47UePDWA","GVh6DGjstNQh":"sUeFYnm9BGAP","Hp3RtE97YOWK":"tkCcGjrCFzjG","8LVVnPbRrPqb":"iBZDFMrNkBwh","yAmTXUvjhah6":"qXZEQd9VXloz","D0OUEXH1ROKx":"R8tXJyavWkb5","UXV43LmFLfDb":"IMDSiH0wHIgL","wen9CkfcVTrv":"kphOJ0FFv7dZ","SRw254w5Virr":"MUnjzSIiVTYp","ngUFsRKIFKQg":"l9PGZP6I9Ofe","qfc9MhtuBdP9":"spdko0gykx00","6Cvd0JQVl7Ah":"iW3Yy5AIIvEf","hyMitsHO7kpm":"Cb2McMSew0Az","DPVRWerYcfWD":"WGucdA8iL3gB","jhMMZlGPChEi":"bR4RjCboCmbr","WF1n6FWtynKH":"07Tv0qanHtIG","fwEGIEaR56Rn":"y7dliXaFhHYI","tvxOocaPgamA":"3C1QIuzxOIpc","nX79pSniohFw":"iImerfJFgeYx","U6XeuqO7MqZ5":"txPDs88YDUNS","lTyUmszgCMq0":"vQWKjsZm9G9S","VD0QZ1QenLPd":"61uH30ur6LGh","t0LD4nBaJf43":"3HGvWiCgAvBl","mFLewvnzdFiQ":"aZmfKO2LRR11","18l9c5OU5rSO":"PiQxs7FLlwIx","CAt8JSRFTtpb":"PC6ApXW36KzW","aPykgszs7fUX":"7Gz4fbBD5vm2","Xzqq0aW6yLWb":"VJOw76a72XmM","edrUp2EEDsDT":"J9An9nkBjXZO","fAUSksFhCT0U":"4Kz7xLhTBrj7","k8XhcxTxzEVl":"ybqnrtSy7Zy2","8VUYhZaKZiQ1":"3C4B7hD6O4z0","PbaqQ25hzm2b":"Vd05IQ04aqXu","xdd82QVor3Re":"KZx3dKqKEPGQ","nlQileAuiA9x":"5K1QtMibebOT","2XZG1BA2QEUS":"54YREZdifJ1K","Sivh2gU0HOqy":"dFLp5eYcIvaA","MNOQ9ZOWzVhb":"e4hWUYpkBpYY","eHcunsZkZrXL":"a0RUMxAxEKF8","8u6j1emYbjS3":"ZDvpPtjxzJYg","fCMKu0uhW28t":"Fzd194HyNdSa","sLkgmbh1I1id":"1hQ5icND33UP","InwnhKqpSF4L":"rsqeZ2U5cqbW","VscQnmJQnztg":"iidDpDr4wkcV","4vGCjVnwq6Ps":"LX3QPojGCPX7","bMG62pWyiZEJ":"Vsc9gUFhqk9b","K6sRIS9bXUwI":"NsVfPnaGAFui","qBEAZq9IFACY":"WHzGYiNR2797","kZ0R63lrxh7i":"3HDyRaPcDLym","i22yY74cBKXV":"oaKy3Ehjntmm","J56f3KY0p4pI":"XQNV4ZmlVsEL","TUwCh00z3bRC":"CRo6E6nRlLrX","udPPIXQQFZHS":"ajyp3L05QeO9","jeFQO5Y1D9ct":"xWJFGx7npran","YUqhMkcX4WhN":"wfabCitcKUrw","uPzeV08UD4zo":"HStaqs1LRUOW","kRUJHoonUJjJ":"yZ0esv7OmAdW","i2qACxL9MIog":"llwLF0plxeAO","RXK9RTOfUF6I":"8N8Ijb17xL0W","9oy5GAjU2He8":"6d53yhMr8vTL","VorT5u9WBAFY":"U39G0ECaf9km","Po08PxmVHul8":"QVmaNKmAMF1x","SaprLi5EhmqW":"9xTfQxCtlJnl","mfJUVLASCgH8":"t5DeYYfmuGbu","ihxQu9Sf82j4":"FJld8P5NTEIE","IrGuvx5O2u0y":"Ei6LjwveQWPX","M5T0U1uyPwMT":"Z1jM6FyvNGI1","m8Z5nsO3Zplc":"iKcaix0ZC9eT","yiDQspnht9KR":"supXwSWLNRTb","XtX622E6qrjs":"ShoIO5FwcE3B","6GJvDLjKmiN9":"I2ZTvZ3rO59M","AZqPClb05MXf":"bzXRKKbU0Koy","YBqrlvMJbHZR":"dbCYdB2vXpyF","R15JGU2kBg90":"UmOPkkckhmAb","qnspXsDPzWqJ":"i2lZSNoIqc2F","2hElMu076j6J":"82QEhsQyc3mj","G0iMc28yMRNg":"T7nslGQqJZhm","8jM767ci1mhf":"2A7jRZaWJPdE","eNKb6Vizxdp5":"EBUHLbL3fKJ6","LYs8C2JnY8OS":"D92nMrN0APQR","o0TBcrWgLTop":"i9SC7ljcC0yU","oheT73PXAuO7":"1960y7uzext0","ihNmXyArWC18":"i4BA6DlRwGKA","24L2io7OevUg":"NYp7aSfwJc9V","yKTRtrzm9Rah":"LDVRbpHhcvr8","aqFtKLEZ9On5":"H8gcECVAAC4f","p4gXZiQv2kZX":"0MJNYWRD5pi0","YbRgaC1P0Q5T":"0fWnLIsiGqu9","wd4ZSlAKu3Tc":"qZwpHFL8TrpN","akvoWocJmpuA":"UdWvIsSsZSkc","qGAkZSHfwSX8":"RAAfs7eA6kC9","GYfjrRa6cmLC":"RgEnCcoo8Z0s","7Fu1Ya0SPqbg":"x4owyxvvOfZY","P7icdtymQ4Il":"OV9ko9XnAu3h","i9Ttx9ErJVhd":"kA00Wg0v1zm2","3JC7yVNxivcw":"Ih24GUBUVhh8","N47r2dgEIzzA":"Dgt0FAVCLZtn","8MO6crozfzsm":"wMUtGIX5qjKf","0RJbHGuKs0iV":"ZUIugHvPSYI6","In17BrcE86x0":"GmrhjA9YeZAs","Uql4CUQ1F1rV":"BxFel9F4llVN","5bMdni9QCckx":"E7p24qTMb84S","nZzDq1BuAxdH":"NFBUFExWE7uo","ffKYR1nMWEcR":"QrIZV1a56i0I","qJw9kBxXb39w":"MwQ73pLwp3bu","umkpLRddbvGr":"ADdPimQueeRF","zF7Y0tSq3qGM":"tNG77EmYeGUr","u2ED89So3Fjt":"YqR6hLyy6c9q","DtaMjJ5gDOMv":"WOE4j5BfCRrz","BIyqIkmL2V3L":"lTV9oy096faQ","2Csoz18yueHn":"tgqsElkLQgDv","fcBENkJrXZzt":"y4i3CGPpW1eh","2tynoRN2PMKR":"uZHDqhzs0Pso","CfZWa7vo8XRQ":"1TuffFyZXpBk","f5nmp0yhN0sn":"W6VkdelNSS5o","3YTTZeeaSLEw":"erAaakkYKCK9","U0qKhFN0o0cN":"8q10ehFGiq3U","fENjpyyHGFEq":"x63AcIg9tPoq","vaOSJPA9tzao":"jqQ2ykT4wt4g","iLEyRrOFS269":"cmqnA9qXLIeh","n5it9c1UNVQg":"Imw3PHEiDFYP","vOk9Shq32DF8":"E4rVAIHztnNw","epj9ywheoPSD":"vPwkCmfptDho","e1BVwJ2ITLvc":"yqucdjMP03i8","JL7MPWTClKuj":"jJ0o0BDUdchn","gGwgEkB9U35i":"5FlZK3yeaCxM","SQbflb5F7c4h":"HJX3dTvLGuUM","XQrBPxDKfJmu":"9CabiQtJAXyD","RhDtwloiQC4m":"fpWeYfAn4Umm","SqLIgXbhMabJ":"STo5jFSDxUpY","tcJZZhdlIV7f":"T3g9EeStnlwK","Q88egzMqhGpf":"28CzMgp8djwp","iz4RZCSWVJQ4":"qqAjCRz4KPW9","0DrHh6iZLsQO":"cHcVYQRjlR2Q","aGqqNV4ODwfx":"LLahDgv2P9xo","WpB45bKf1qT6":"pBJD3IFUm2CG","T0Ms7Sf0UaDB":"xhVjLC7E2Hyo","9IeET0yoBkl0":"JoLTAhLAhGjp","zo5JFF9caafS":"L7rT5CYgAVFa","HWUZSejJ2NPI":"R0Y19RGWJpzx","Qj8M7ogccQTW":"wUXhupTaJR1D","v8U295MNYnUv":"jGrnuG8wnzpR","wolGvyjhYKz9":"gJd67KMRkhT0","V5GKGVARddnJ":"ED2EhP9SOARz","Oq1U81fyScWx":"nfiqc5AI8SET","RWiACQuUWxIP":"DtpQSModEYxO","FUBR2GSBXmu7":"kG20mQ9QBBh1","raFYrGV69b6t":"xe2mTRHiKqa3","Eaa5OgGeApnZ":"JaNJUAR8GKdY","lU7GQCHTJsWN":"CX7yePFHpqQN","1Zx7mTEng1ZO":"4QIjI45WB3sW","1SF3fwiCIEPU":"JHkL57s2AGuB","qoMnjJovkVhP":"Cxh2kjBLHkFG","uGhvmJeVS3gn":"gUo2t15SgUBQ","2JuvkySFXm7z":"1ENeQ4SqaK7Q","Dra0moPjuscR":"pRkLsWhxHx6F","fxjypncoZ7yK":"3GWHZ5Dh3Xn8","7NdTWk6TKiex":"e1IAzEZ7I0Wo","expVaGNqJgad":"oz96ENdAqB3D","qyKuz0uxcPa8":"9pMwJS0gRSdT","1bH2TCcsb2Kb":"BfpYeGAEPdOT","QvlrtJ6XZycK":"w5lwFYQipsgB","4yZstIXaAvGG":"8HawB1bUgLRM","IcFAliNIGTBf":"SLLqkauVvXGg","iPOzgNBPdBJO":"RFCi6YAAljuh","n3BfCeXLD933":"JJwaopJoz8SP","N3lyA8baMMXd":"E9CVWlV7Yhax","oLHBdc9IqIAN":"iAu03nlIrHq8","4QCWFI58OtGZ":"2hSl0zQxJkBb","UhiutwGFbcNb":"hSItGby8p83h","GJCal9rxXldR":"RDC1PuyEMIwB","ilN67uccrehB":"iERQh3hl04wz","LqcjawRj4e2i":"7vNLQcM0L5ml","x92c7yHwxPew":"UsFUQcRgubMH","SurTbFb1JpQW":"voiXpiuQOUV9","7HkwMybvFkpg":"fIL7ZgCgM4OR","d5TNScc6Wp19":"OSCJNiOrY22s","uKouMqq6bcC8":"mjVAWN7uJ3II","Jo9DmeWJSqwq":"pjQnqx2k5lK4","P4Dt1uhYVQJl":"KOJI5yK9tPwB","GhjiaIgflEVQ":"bgKw0vCGCmZz","JKktYIr8wKjy":"TqSS7RqAPB54","ERiZMtyOgzGk":"UEI1fUbMlBkK","EaqycQWqHyvN":"0yWlyZWc9o9f","FhwtggWM291B":"PsqxTEjpih8n","9AReBJgWamva":"wjywOOe8Hk1x","N63ttU8XOekh":"5Q97NHE7PhJ5","A07rcZ9S3Pax":"dUVYenaWZ36m","s2Z0Irhb4TCV":"K4xN82vw7B81","u4fNbmvWYpIc":"Hy3HhWEmIKIA","gNpEhYMDs5I7":"4EYvnMssqw2J","bdDyk77RQdxt":"BydqzldaqCz2","UchHjqvq7CPz":"BCtuRmen1qiC","StQkItFg1187":"YIo6sQLgfNFv","wVhR9HDTmUPQ":"qWW7xIKTA2vz","qxvD9vmNeHZc":"x7aqyigl7UHh","GhrlinZ2J2mK":"oj9CWBw0g6ZR","80btrjItGWiU":"4kk1WzAkjZ1E","swDIRcGwjIrU":"5KFHDhbqW9LS","qql4h0xOPD4F":"Ov1dcYLNTWXH","iLVJIg7a07XW":"qfm0T7DAZ2U9","bsucZ7AicMqy":"mB2uxZm8r2FE","V8dXWdnOCrgK":"my8ZBJqSe1R4","DZ7ovGirbICa":"KizBu3ERCjYL","WatCEk4mkPST":"cjacKo0MoIOZ","30yg8uhsJ0w5":"INgWXM1WzgxF","r996Y35zCZ8H":"ezF5qAiKQaBP","x1Q7RnWZX0QQ":"fqBSgGCtosVB","4FowxjaLoVaf":"TR7icnPYDp6z","YyZkv1yqzaFM":"jUYazn7fSszD","Z3r7tfpBmFiR":"kGOHHvNAJFVD","V5HzPr8os9yF":"0xNFyS2VnKW0","P9MrxGCc9UNm":"Kr877atQFS7C","d5HPT6hImIB0":"K1Ic43b3BI2I","bVdQNPMG7YTB":"VbVEkcq34rGn","JEuii4w7uOxd":"GEPlr3yMEjgL","tVxdJ8C5oExi":"fNaRkW706EpH","0lhRVoDB5miZ":"E0pCwHvnaHDx","7s0031XnsJHE":"7M1bT2DI68lS","qOHDjMJLlufg":"dxkRwRw7gczf","IkC8yi7IWRap":"M2tQsO7R7v2s","RT7Chx9RMkLN":"XpuitQHnrBf5","VtV3c6uDC2tA":"cPormSx4thG2","7462LHL7lFEh":"5eniJqMSHfiw","CXETbUPTF2S4":"s2cUNsS6jKWp","POR5g7ZPwfq2":"EX2cSEzcpHAZ","JMvuGlgebmPF":"dkMgoGPzCxZA","U831T63oPESm":"Y8bC88Vif4s9","zt3ptRvN3tin":"pwZoURXlbs1Q","e2WhBtmkFhmQ":"bDKuuwan3K8r","NHwxmWmZFtmd":"QxsxV7LSePY6","1VsIWQh6bxf1":"olZyHc7sOGXf","nXGrusbMSrhg":"fPYUzrfp7zCW","9xSbknkoHgXy":"bvymHqs6HUZH","LORSPWxrpJAd":"fKittJEUtaql","mv87MbRw4cgu":"uVaqBx7Kwr8r","zEECRTFIHsbC":"QZETxz04Tdqg","1vEmzqTsyHpl":"4Gri0svgQ1tr","vqavzTL0HBTx":"yXvm0huNL0uo","unCfnW31HgWK":"OD8EDJELTQzC","11u2PCn1bzfT":"WEZyGmFcu8jZ","THzpafniitvs":"QMiJWOytGCzw","jxRzUckRxSde":"UFxfgQUSBJH4","AHERW6weL89G":"ZAdrRhXfE3go","Er6mhu5V7RGW":"lNJNIK1wthDu","EiK6wgXIX2ji":"4cdaDmEFGqz5","eWqJkEaoNOLm":"OItMirRCOP5B","gpL2Bj093COV":"nzJYzVYm6pEE","fxZsSOjZ9mFi":"laKrRdBEg7Vn","pVKyMdGIHPWw":"rVImIVgrBor3","8mj7xGy4OkCn":"rr3XJrR8DQcE","roPKjMFf8cob":"xpuQIiT5J9U6","luF3UVAEBXCQ":"hgKVJLdLtxrI","3UaHsOR1RcQQ":"GSM3td5y3E8W","dEew9mFvGKaX":"NMJktAEKzOGw","iesnFGlFHdh3":"3D9R9vuEK4SS","X31RCtRL6I2b":"qDT3gzhX0IZ2","tONVgWJF2Fu0":"m4P1AJN4fJ8X","oWtbFGEiugjB":"IbzZOcEUakNc","tTOrkrL9cc5K":"qDfPwGTaV7Dw","kxdkB2NMnNsq":"FHwFxXxQCdgQ","Q1svS7pXVYYG":"epUL1S58slpA","Pl5gUUtOWYXh":"1CXWKl3tAjly","NNrSpvcV0PQs":"NrRMQSOfdrBE","ZPQsZFvFE8Gq":"Cumj5SzrDXHI","5Z9GRZvucYd7":"17hMWpzZuhtM","Lh5ejRORxwOV":"S2xTBN1FjFaV","QWq8d8P1S6g6":"t2GTRCwVNrPO","YfURx6XbNk0q":"mnzus6pM0Nkw","SpRCwBxb3B7Y":"YK0QwnCh7hoc","MmzV450NJIQv":"SCQ98DFQytjZ","4UzCwOskKvfo":"Hne7YtJvLhE8","fXPVBisv3qvp":"8KGfE3kPHbGD","h5tEw8lNXCQE":"KOPS7FLzLtMs","4arCS6TW7DJ6":"uEDcqGpURvxl","7ug7qALqka3z":"WuioXeUZD1uu","nP3yEacQHPKh":"rpkHCluQ44oJ","kmIP6P83AWHj":"YtYMi8EMeNJs","9zyvNfV737fx":"GiUqywdOR5bd","hOoqwRSkjhOb":"f7FdlTof39rS","sgakLe3N8oYY":"Wo4f6h97BZdB","7W5wl0hU6TOv":"UrKVWaDoLNGV","pF3fcr2DQ6im":"mhlzkPEf6kZN","9BP3okqG4VyU":"Zkhtpq0wU5uw","7mPKxTllcJCK":"eMr53fwCTaFB","k2YcnByR2FS1":"mgsOgJoyURry","9Bxg8Gr72L8R":"EXDC3sbgRa4u","RxNtjnWSLIlr":"UsGhNMZhyfyf","lH8rKe8hAgs6":"RPRxEdcchqGo","VxjVLRUkWPtW":"M0wUP24S4lsa","9DxXjFGb29wH":"CriUscH7Ke4x","wU34hQ6cZRCF":"UCCbeOBp0VuU","LwIbu6s7sRHe":"UbHA8zsJrdki","ZL6l7ehQseXo":"cFm1lRuOOkzJ","guriGPcfICL2":"VOzYvJj2AEag","Ihwvys61tMX7":"JjZIiuiuUCTh","ZjK5iay8gUWo":"JibqJv7vuuWg","dHheQbnd0dbT":"1uqg9nnBhpB6","TrWLH7WDIm2A":"7eTGC1VDLykb","sk7yhe9QY8Uc":"KYlpurQ5XhqY","pshE5Zhynnf6":"X2DwKuEYciBI","ZJIUBJwYDZWF":"WXyF8cxfcpGl","TFrHvV4fWhbt":"9zLZAsv8jErY","YXkdZhzcBjNY":"MLa2V1zpAJTN","3AWzU6GjlMrN":"gKiS7zS1TKU9","JlKewAaXJdqm":"J2D2KgavZPRL","nF0uNvTcIjrK":"tznKPA0WZzx8","jgleqsdyHPej":"UKxjXKqFH5or","Ovpd4hTK0tSb":"jED7qTu2OnNL","vQzV9eBpbQJv":"AuC9Tl2cnhis","fqyMd5eHTDhb":"32jp2pqMqvS8","HAeg3lDBVjpj":"AzSc1zRjgqHw","SaYvqnL6CR13":"6F6a8pSB7t4n","Rxnx5qXO8uDB":"oVkvfRQ47FYi","DaVsCrz83Zms":"zeojWvVIl3hd","sst1F4eHyVQE":"OE8jgBpJrMB1","jyjQwm9IeHAr":"78M60OR6C2hJ","NfnIPIfe3rFD":"qaNyqDbq7Qvl","sklkfyKHrHqp":"3leysJOSYJkP","eTW6RPrTb6go":"zif0gdSZSZW2","myM6IYpOY34L":"VJIpO2uOadWU","qY3GKidRHXKI":"ILOw4t7hnycZ","QZ2bFt1VV2VL":"jL3Gt97uEzWA","dMckw5srNUA4":"5WbeaAAm0DsK","LU2lunnguyA1":"WD0HR9Yx9XzS","6AGUS1d6ndDn":"nBebaapo0Bez","RIDTOrXKyT7M":"qmaTcdHvbxFV","g0yoxolfh08f":"T1jrh82WW0pZ","sUhu95qHyeFA":"CBXQtf5FZy8k","934UtALh1L58":"rgZ48CS5aIk7","EXWoT45TlggT":"owbsiSvSLQ3D","YVdt9SI4ufOi":"EkPBIX5vxY8G","1i2q6OxtR7Rm":"wQeIkEU2P1pz","ADggXWM2chOx":"m2XdqM3Q2NwX","i12kgVwNgEi9":"DGGTkObuzVNc","QtJ2Zf4akK9S":"f1blMHjUedbN","lCS9NKur1c7K":"2HczOZUh16Ye","5WzxS89fUq15":"Ow6RmNkxA03e","DJ1NFLQGhN9W":"sr4pD4HMlmOq","xdhdqUVoIaIP":"nIkC3vxvMdDh","G3W67VHBJr7X":"Q9AsmamSFtSs","mY38YMiCtJAy":"8L3SMusKVnKD","KpgEvlNINiEm":"M5sIGSYnzJuy","oR6mAOzecZm6":"GY52Vq6RHsG5","VDh5YjESNcVI":"9BsaV59q8AGr","6WiBzdqOPCud":"M537pk7I7KyD","8YEKluT8cRC9":"PIBjlQi78iR7","PdFMEseMsNGI":"064dqIsHwtJz","CARE1mTuukoP":"YqrWMV39ga1R","v5hrxOZxi3A3":"qjn0DaP2MgrC","i61BrDd6ZJty":"W6X7LIZ7PHVB","mNnYIUr36LCu":"5PgqRnzxicGI","nL5Ic2ZVmFzE":"RDbRntMMeWZJ","ccToU2hTtxPd":"Lp9YqKsKbHUu","YPwoghuneGIq":"bmIAtuoOhjhZ","tQy2rDY4WtrC":"97qMo7BJeEcr","8Hreuj8SAzEc":"601yDHUW9Svb","EE3wQFtWwk7E":"dpukoLi3Pgqj","mSJ3cw0x3vRI":"HWjdNufrgGdZ","S6k9amVBuCWo":"Rabo7X2oVCcN","Difjx2FO9T2X":"M1C1YcTqrDfX","rJRpFPjys8tz":"W64IZQxQMe4q","xUxGjM0PeiBA":"mgqHQXV4z0Wi","XRRjYFnyGIH0":"IWCuSnbIXe7k","BVo3L4IWMD7C":"xwYan88hPSoP","SVQSLreHuvHU":"VcULVKPKMHeB","PFw15EkHn4E4":"npEhYMWAlk2o","r6b6ZYZDWyjk":"LTZxBUlQKE0U","GDfHjIq7MonL":"Ou1CnXcTknUh","Pdqbcuczdckg":"iXSQSxKqC5m6","1fdAJlVDEy1B":"q6UilVUDFlL8","j2BkDCcKiME0":"PvwizYB2vMce","uRVblq2xzrak":"eV6cqphtjgSj","WJY8qHQBiDK1":"Em4nwDGZqpkL","aug2oqVVM5ZD":"7e2WmlRorr4r","6zyLt5vaG0PF":"G7XYDXNKeHlD","gABPNRgs8br6":"8zugUCvUVU2P","0nZSwNKNRIDc":"aiFwLGB68ttW","Gj4Fz1FNR0EA":"OxyqZ4EHavPG","pINXYB4Oe8gP":"lvrdshHEfQ13","nevxq2EfojZA":"cgvXjMx4EjKt","O7BT3fZS1qh1":"ARYwkzq9wXcB","RwbNdXAlRni6":"0UY8HaeUOh0B","eZBAJ5aHXEgo":"eJnfYlbtdZIi","868tUSsq3JMj":"o6TMbX2I2Hv6","huMtO08pLkv1":"haUIIexxwdUb","z3EwaWEkvCkI":"W6bvg1gSOVyM","ni6Qpge5rba7":"wu7Y9vrfEWvE","CVXCJn0mX1ul":"hQiFnZMFvvgF","wEx7n1qTrAGB":"apUq4Nyu40Uv","f3nKT4Mr6QFD":"3mvYPwgIHiPi","7ASrgnux4ubw":"gVQxyKVbkjDU","dHuwr1lzK7eg":"qeb8wPurrbVY","KX0cXsqa7UWf":"lQs9tqOu799Y","9gnYodkiVW0M":"jcmcqPUSlK4n","3PfK1SRbqPs5":"nqyDRpUoKVg5","PTcn5qfGjpzg":"ho7JXoQymXUS","4TQy5gIOu6IQ":"combWHwDjWF3","NCbLsgK4FYgk":"rn1ZClHu91YI","oIx83C2fDYY1":"aE7jrm5pcuWY","xlGyCD3KlNPD":"RC91vKqcPjAp","WfCGwAukxgCm":"7HP7xR7DfsGY","OYs3t3hhRdOg":"2CMEwQTAiYLU","GkiNJIzT89NJ":"dlVapOJ1MAEl","nCvgGbuWlcff":"FrUsYWL5svl7","Jod1eSNbpMbo":"imZFmhx6kcpi","zTiA27yc2gg9":"pr5o7bsm805v","FjRmKNxJ7f0E":"AVi2p8q1VFxw","GIJMS1TZGhZ3":"p4kZY2OWvopF","1A1eEmP8Frcw":"sH2CxVAFFiGH","aRu1nJs8bvgR":"Fmqx72HQnopA","71oyUQtQIFUH":"EZ1l9ZQDkX3k","NXqbrPbfspvX":"3Kt9VMZ9sjld","LCIjCC1Tp1FK":"MLrdfy5t1ArD","lhP4GGDDeYCp":"TAEymSVTpJ5e","Du6PB2pPBE83":"FnLHiQYva7dR","83eOPjuVpzXR":"7c77k9PhPdDb","BtlLenQOn8UA":"RGhK1HTeUJhT","op3TXr8js7yE":"sZVKhWFvdR3L","DQyd3aQmNaUo":"dvYaKaafN8Gq","jafcA7pVXxiM":"YQ0BOA0Rdl0I","2EyH4DuZwWvq":"wdLVVHp1DVXO","hZQjr88kA66m":"6JHGmgr0hmuw","PzJ9RAVQIKFf":"2wVduLN34sNY","SvqdqmCUr83u":"EE8VJW80Vgi2","2PVTMlp9QezO":"Wc50X0h8d4xm","xPixoh82w6aY":"eiKgYeaKBXgh","upKM0JCFqRPe":"gLT2vterXhJT","q7Iqc1UheKrm":"pw7HKHFTaZ5e","eIK2y0P0S5Zg":"tGv4z2ueDXri","AWAGv0gkLG19":"8wjHogf69Erl","3KeJSvsBcPYl":"EROcZRDTZSUN","JN6xzeSXSPSB":"TuHzbPZfeUOL","4Z1xO35FVVJx":"JebnEmmozvjy","0UleGxgeVGUx":"hD4663OhzQXX","9F8ShWnZkmRP":"87OErwkXXNVY","1L1i7VV0vqsu":"SPPTOLS66jEI","cJZP9iQIh1N8":"SQ08njPN1mJl","Pnqgfo2wr7bP":"pf46dCAte4IM","uJD8louSopPC":"iNf6FLLqJhT5","GV0XRHkagDwg":"eGaUa0W1Jysy","c8BoCrlxJiWn":"0Rw2OcMJFPNB","mYOp9esJWAaO":"1wVIeRt3FFlY","7y8HZdrRsHX5":"Im2yGasHRxmK","86WMfTZBbE0K":"I8jeyUHwaXBk","6IPXV0o2ZkZj":"w0rKiopTSjJ2","j33rVlMkrlHn":"KZXqCiAfZyHK","QaHZN0PcvVan":"uBqGXje6Tafy","Zne2dpj7r5JH":"Vk3gdsiPkLMK","Wmm6W670AYHB":"3RXFzi3pvrwU","HCPqOGbluAlz":"jBGAiSOhhPab","QZrJ55dgmsZV":"S3SNBSA6GOuQ","Mw9OQL8W1rMT":"0bsAe6iWvUAg","qDAQC2E0CXJm":"cuLVuMzEkjtr","0ykVXe47QC8g":"FPlf0XJxXNZo","a0hPJDIEs4xq":"4LFUed5xHxzP","Olnjn25Erx5O":"XgK38wQ0rfsW","YUDu3g3MC7m4":"E7KG0E4GTdti","R59RdPvMEmQj":"bfJJzwuksAcA","symWOf6f5AY4":"cg97UiqA5KlU","i1e3ijE72CsR":"b3n80QvmrCBr","Toe2QvCw7WAi":"C5cz47jWf9Ij","afN2Wmkkr1To":"4qalBNUfe1jv","RrZkUPXmcM2I":"W8boyZbB5TbO","rRjUaGMOLq9u":"VYs0k3cjko6V","AsYoWE86Iaf9":"WC2F7UbmOKtm","XnFh9VOUToIl":"L63VFZRIh0qS","FntnmxFyh2Qh":"qSfZSwGR2XEg","9FWhda08uJwt":"cFwzjPPjzWKt","hW4zQokuTlrM":"UK98oKzsMK4s","dYSPljeroOrB":"UPZ9zc6fXQ1v","9i8NUCublfHV":"Or0Omm7rbO0J","wqCvOFU6Tc07":"DUUyvHuj2BUs","k0sW7WumB6F2":"QrZFrdfyXHHM","f6j9ZRTw1skh":"H4uqfFSF28j3","dzz3Q6xo2OBd":"KduVFLghHpWi","Nw27nf5IYrGJ":"rCwnSkIdfD1F","QZawE9xz7RbB":"1xEWTSS0Ou8g","GprRoY5wxkvZ":"kWAKsRJB9rwM","dm5iKL68o17j":"bWJUguSnfjk7","e1KNNjX6yFXI":"L0LlG8xPntKb","CQOFGyCGSuk8":"9c8HcqDSQA1z","IWjkJsbwxio1":"z1rUJqI81fDh","97XrOvqSQHQW":"c9CDqxssdTwH","6UoCDr652xIz":"wKCHAUzMstY8","I5SHufvxUDIL":"GZyc5v8cbqM6","AW8F3DbwwVyp":"xzSrueIcF284","q1NJAnQIj190":"LFfNuyKby0Zs","D3gFlvdQhsmA":"aZjmayi0EWgE","lm0uFb5Bree5":"cnVYMZifF1bZ","pC5RWCTDJntm":"KBoaw4NvgbWs","8KealJCB3Uz1":"jJEtXZT1ZNNR","EwoQzjxpE5f5":"Vqj6gi8QQy8L","FFJoIo4sCCOE":"FQzxlv0YucfQ","9zO2yfGzmtBp":"ipRVpAkTHaB2","k2MI6Q0t7pah":"TEBcAkUleRDQ","2h1VtwNWXBey":"jTDGpiKV0iIF","kuicAwl76N4R":"QC9PNdBGVeR2","T4m0HtG7wguf":"SzYS0HWRup9S","Mpv6XA0vblXo":"otVwo8g2qNgw","YlIaCGLqVf2E":"zW8MUOURawsa","bUB6MHACQALl":"ZpmlJnJJ8U0R","Apje6gblIpxn":"tNvBrXleqWBV","LuNPMsYShFAF":"cZET4Pr7sGaI","DhqrRABAkviy":"wBF3bhMLGGen","4ozo4VN3HbSs":"hrZnIaH5WpzL","wKeSOBNfnsyO":"aNLnT5fTeRU4","By6ntvYRQEN7":"XcmZ1BjhRSSM","hQJK9hVtgn2c":"PCNBasBvAlBq","C2gFvZWHJvNu":"9CQv0X6BOMpe","KaqJu5YY28BQ":"fWaLbcd9sUqX","1NROkRweMkrw":"Tym0mf2Fkd26","iBg0Lcb5VS54":"pKRqqcR4ZaJH","rSX8t0dfY3cU":"tDjxtwhzPlN1","NasJ7vtShLHQ":"j11heCHGuOsX","HRLnfsO7jPqy":"WlCtQ6hTgTTq","9g2ApLnNyMMb":"g5QSz0xkGd6E","lpAiNI5YfR6l":"y9ylb8rom3Zq","BVQOeg9fv62R":"F1SUlKcMyci5","K0IwBWA6lj3Q":"doKYWPVIUE9h","sCHRlJvACOXt":"scNsiiUuyB0O","Pp1ylxQoGEio":"iIRPiW6j9jsC","tEkaPwgEt38J":"rsDZrejpaNXP","0n44dKw0eeMr":"1EcV0WcWgtgQ","tlBXMfQ3iDhm":"c0TY8MAoMnOl","wlSrPQC3P9XV":"TcyJQNeflK0c","oKUXVHzysvoG":"3UjBm1jhSBHa","X9YQ2pqnhe3r":"Hk8N8AKsEbqx","aab4MfuNXYh7":"nKAyo6hzEEhW","gb7vATVVN0ME":"AYvjMUp6uWil","071qmgJ5u6BA":"clIolrwzNHa0","tD0vGyK9ItUj":"AvasD8pm01te","ZGZRPnQ5NJWD":"koAdcViwAEHe","R54qnWT5PMd3":"sAx5VGtExCjl","TutowDDEqAZF":"pdeJig9O5HHf","jMxo8biV6bkh":"BWWi7ThHHHLk","goYRQDUskqHo":"mR5vDB5QY2vk","iyYJFHjL0t8C":"9wpIWze6cacc","6nwlYrD4G89N":"enYag8Nb2919","XoCzVsGz99NR":"MSkwnj0KqY9i","uJaxH2F4ZA8x":"Y4Jp0tWrQkas","sF5E8PlhS104":"397zeIVh91B7","qeEyUZStOEwo":"MJ6Kf1hdyOEt","63UVy0HR9mc6":"A6eJ9UM2Nsif","SVRlkU54Kjf5":"ePnAguxmr5Xj","vHEmqSKZq0xM":"nhsAi3RjQYJz","UQxhFXKI5E5T":"7Ysnb5J39iOG","ZeJ2tHI2aMqz":"vI5VQFLOLQo7","OP8cjUNgaB7h":"EE7b7XeNHeqj","pSD56Bq2PDnv":"bHuoORGLlWie","O81OWhWdY8jR":"OF1KHm7Lz09v","FksW77RmfGY1":"DiXJeagw8tEj","5UFknuBgftcH":"LuXCtlo16o9k","Oxjj81AWRROO":"uZmo39lMD3bM","wHm3kWUptZ57":"SYQ3BGVgOKKV","W6o1uIGTfvjn":"udfhacFR0dSl","Tc957sPiU3u7":"tYIBTEKhmft9","kOCE4pHwT1ne":"sX1N6deuliiI","ZutAUmaOgHaJ":"lczNGNZkFYnu","bQyTW9YwJpXh":"i348v8jBd3oa","PDJRRrRT9X0c":"v6dh8xqlnOfC","9e9MLiGavChF":"XHDSSjhf00yH","9PHFwu8K9gRR":"UjGLJZqkLQDx","FmsaUwSavQzA":"FREMaCfm0vFa","mMF6CDvRkzSb":"3BIXTpJx0KmD","thOyiURiqe8Y":"ae5nmVPyS23R","4EkHLyLz6ejs":"fDcXa7U10cQs","i1iDHNXIB6kT":"NTgyFfgb2vxn","CpH6PW0ppxPJ":"D8Ck1BKwtQqX","cKqktkq5Hrk7":"uodK2xJdeVAz","AHk9QyX73wF6":"IRwtdIVC8GE3","fCUecuzQyEAO":"RDDimWQxCzgo","rKuFm4Yc3nLx":"kiyoMIDPb0cD","oFXXpy4uMLog":"epV3MY4J48j6","trbKce3mYRpJ":"a1LNvggKoZfc","GKmZqoA6enPl":"MM6REXyjLRO2","EUOVrpcDWe64":"W1uqRCrTN8yM","qoTVYZ3umcq8":"CwjHZqETMGOe","innjW8ovycAF":"GIDBofuqUP2h","R5mZtrAiv6rw":"c2nKhKmpVL5g","iRQIoZC997hb":"pMKb102RL3Wb","3RXQiLlQfahJ":"zJSU3zbv9pir","vqFTF2tmBBua":"ziwxewsgvRqN","mQ9Y1DhVSJ0g":"Sv71PO815wLd","MyymX1sjlD2n":"RI7geaQAOUIu","uXueLDCUuluk":"LbjAMf7uwxj4","zsdiRswjcdtr":"ka5wnxuHeVE8","Ddqqc3bTQoU7":"eeBy4ENwF0q2","TO0bQzYbrOXr":"hRpICrOwrS6i","zLHrCLxzz3e6":"3Sx11AFE8LMA","GQg7TqzCa7aj":"7v00msKtoaj0","QkMBPNsUOEKf":"YHRQsKBqiVDM","Z5MkkLKBYKIX":"yvkHwQr6FnAW","bRc3OilIhQLq":"9mZuPI5lACmb","Ul40fyoPWu3H":"NQIqjIQEqKOM","6KQiOL2wKGmt":"gwmZ3Bqc4nzm","V2PzRvImdslS":"ywawnIoTo5ir","fMbviWuehX0I":"7Apz1CXVhDn7","PmbSk9aCVKlm":"IV3Nk5iz3vA4","imCTPBB1TChU":"IwzcYlAxOaAi","pDYHn76x25jw":"FqhggZtfKgqv","TsDiQvalo2JU":"Ca7wJwOH01CJ","TZF92OG2OBGh":"caJp1JdglvOv","jIBIRO3KTMFg":"1vRq8qtHL1pq","3pBVFLUOsE4Y":"4PWdAaF9sfkj","6MqZWZughF8W":"gVsJYwQKAF9b","iWb6fdAY3iAq":"JyGrONdlvYNE","qpqEInE3XJuk":"OSRpRyHB2Qh7","hJaGnazhhImI":"njD9a9luFiJn","GpZTF5GIWORD":"zBGhWsvriWAH","7OQl62KEDcBq":"EcJVinZiJ1Ua","PpKales9bD7O":"eNxMPrwX2xSr","yvjk8FrpEO7T":"xyzShDXKN7YF","62H0Dl0gidxD":"0cLV4yta0Gfx","v4FOsCSVwhqj":"bsEFbNdTgDj3","UKA77WYQrod3":"SyfeabkNBd50","96F1I75IOlzJ":"7E1RdwIkzZkw","KN744da7d9kC":"Bmk4MS8ubrQQ","szMkFYQMHqM2":"uZqxh1gQA64R","Z41WAoysN6AB":"YE8O1yLBYcsN","AvsDPMo1bLvx":"rIhyL5DdCv2m","KMX2wOOz3rAd":"W3SK7ansU5MX","TJbluf32H9Ts":"Pd6s70Qp8zgo","xLQc98BOTsvg":"YLQAz7kSX0yP","fEoSMSe2RUE6":"jT6Vt0HMN5u8","Uax1rm30fZjQ":"xSiP9hIX3nLm","yGkj8qtFAmwJ":"G1oJqoB2lAyG","10wxeDEg8ABm":"Fg29w4uRB7kB","VC6EJQO3dWwS":"4rug9WKQTZWJ","TWMq7ZBfUoVs":"IRjvUgrTPPfg","b4UnSwphc6MR":"NzLIVyiOzkLR","ul1WESb9NkEf":"I32QnCSMvrK0","5Nxhwd9tgZHS":"sFCLGlsrtPn3","0XmPlllQT0Ko":"E7QVzDqCC3O9","YQXKnIYTcVSY":"M2wNN2pt746z","aksYORyHoxLn":"gGxonKAlsv2E","lcEeQGXMjl6x":"xWPjLR9lmLGO","uDpkrPvFjvrs":"OTuZfYHIrTTu","JtfP68qd8I9e":"Ac9h5ixvjvU8","rFoXAILNa8xU":"R7uip4CbtHzg","asyi5TlQ7aLN":"DqSfNc6clt57","Q97W4PGb92gg":"zXHlb5zUPQkI","vjF62D79cuKz":"5Sit2gV2oDIx","DbAzusWk7NRa":"D4JqOdyBi3xI","eQqjRFObIIsg":"WhAWx1gls12g","wehtUxtYrcmP":"cHTQ3GIRx4Tz","WteCeTKwYHu8":"M1YKK7z24wNG","0EIxgity2oZw":"Y2tBTznwryyJ","dfgIkycJE6q0":"EiO0WghSuZOY","cq0yQC3gkcQN":"rGwR45DycTgW","1zUFRYERl6DI":"pIAnZg22kXtj","fqywUTREV6hq":"1r6JYVaRiBGO","D3HO21S1KIfV":"mxeK3jWI4m3K","WmFnXAHBrf9c":"GrNvjFxOdDEM","QISVj4yaYn3l":"gN3EBCF91lB1","xIGymx8kvVkS":"KHrbvz1E29vP","uvrGR3j54KbC":"LrHzZRRdsQBq","Xe9lmhB206ZM":"3r7rZTOjHc2j","iHyLXQYWlG1Y":"fm87liDg0QYM","IT7HAjUzM9Y0":"1eqWFgAk5DGM","BrX2KGYkNAKw":"8LemX4EXMFCy","Bm9FOkcDKp52":"Sf5oy8xQloAa","Ae5bxtEcqNr4":"MM4CpBqiarjR","ii616Qg1xHb3":"Xzgwf0a2qM3a","CYVq4TM8EyWs":"zTJ7GmIXY6YY","MRlR641ORf0m":"62bNNfQNGT6t","KigRGyygvHka":"HxYDoMBIh8lW","4qep4ASQ85UZ":"hN5frPTW5gmO","gbzdtC8cLFbl":"VXW1L6YmiGbL","FZurFM2v3fFR":"xy05Uy7BjB49","xhZGMZqMEKFJ":"e965HpGWxmBm","BMFQ52h2romo":"iZ4Ruvci552b","xLlCVSGReW3p":"iPo04RzM1rWX","JGBDIDYvcThG":"xe0BZQxC908f","VRAHXhUlr8XK":"2xp3dVTrqMQm","aZTE3Qedqp37":"MCz7rLfVhBPg","ACVooNGrM7Cw":"gTkKf9dOk6AO","hzxgQmS9OjWS":"zRIqNIpnVTnT","yt7cGi0VIa4H":"mxgwnqn5oWfo","lA4ytfDrAHcM":"jJCtocL3VT5L","BwK9nLWIa2op":"XWKtI2Q8TxrH","8kAwcS0b8GR3":"w2SxJsXhIfvH","NC5KrpJEiyX8":"YBxHFfZD2Hg8","I34e3HoiaEIe":"1TQcDLnYprbl","qou2xzgaVvrn":"sSXJGwOzvrBp","PQE3HwNA9XBL":"9rbldwhNLHB2","n4Xe7dginkHb":"J8Vm0dtQBpXp","VGeLvo7S9gOh":"gOoUwint7twL","qyTaqJxaKMA4":"O4TH8l3HU8Qg","GTZLLbxuExVn":"TjDi6m0QjQ2g","G7mZVwDFRJFd":"L3e6upSkVT1Q","b3mTL9YgBKiB":"OacXMQGReIRO","p4DngO3LKgnR":"AEK57Aj04vGl","2eaHipDgpNic":"5kclVls1giqq","uhBvjXwzpfQ8":"shrD4yMFataO","OyM51G49e2WU":"ZN6zksJHMGeh","595wbmnslhhF":"a0xtJq5fxxEz","PFXCSfiKKBLy":"sWI9eYjbapeg","IDQ5ho5wVLwe":"mR5SIZJYNJ6U","GeMLQNNSqtgg":"oHESgbEPVUUy","Y4c8OROrunjZ":"LOXStLScmpVH","xkumLo3KGa6U":"aHX6mbxQibLK","upg8psbkvngp":"mZtOP8UYysrk","VO82oguJpA7P":"iJCXs9fakUzR","S37AMZimEV45":"zVnrmhlAPHto","2pKKD57Q40hi":"etFOXB3d8EqU","E0W4icEerRPc":"gjn1rAzfJlsF","WMRgH86CxA6k":"RUh6vlUpyOHh","5gZz3snd2eie":"orcCeTjNm2A2","03Z1nvyUadtH":"Xlqq7oPMstFF","rxSj77QDodYJ":"2nCX7usdEDDq","4pjth9t9jO74":"zga6j22XQRGO","kI1HaezzXThE":"LJhHLxmgKKrf","r1PzQ4rLNc7P":"I1JefbRRX4YV","hWZ7ediwKN0C":"i2tJF7VpWf5z","H3y7u9WjeyXd":"ja6aMN1DbkdJ","teH8qvVgKz88":"QClf0cRHaf7y","D78J4XNKHvLY":"dvSb7yRJ7VT9","FsHqsiw8iDZs":"fAlmGmFkCrV1","0tMGd1BFH5GV":"yAuGmR8GPRmG","qG6qpsVggvUG":"cUnqJ6ti3cNQ","BJMatlQVQfkX":"KThM2Gs0f4mD","0sD6uO4j1q8O":"aiynU8iW5PK7","PZAPSolxTfSy":"9KlkWJoQMD5t","4BlZG7bVIm3Y":"3bw1V7OgZqi2","m7MQs12kbN20":"Loga1zutTy4j","C4EKxBZKfEDX":"Tox2ZakEHbye","QcmVw4KmRL5q":"LEbtqI1tDEn7","o4Se0nht2lAV":"TDG9Q06RIgjH","LMSeoFEePElv":"JlYZpu6EYHn2","smBfmPNj9RfP":"WMKp2m19btF6","WM748M4Z3IAX":"X7PlzuOrGaIW","PtrzaNxt0Afv":"UmdJMLnXd9Q0","TDOwR9R2tRNK":"UtwOtAmBjcaE","fW1TgsOZja1T":"dDw6rdZdsZ3g","aJZazAxMSojh":"JIeGzOAMCaQR","smYiklrHxbnv":"epIPupRHG1wT","nluf7ocNhoCm":"KqBKkRt5wKBB","87cP8zMjPNnv":"eMLLIRkgFQPa","rJ3NqYoZ9ogU":"mGmmP7iVpPdu","XjuLl0MGuohR":"kZY10VdEVB3Y","aZBCbHJdAzbI":"jZWPbQCqT1EW","Crrc9X24HihR":"MY5jJOKOUGod","rJP4pUgEVa4a":"7CNHIEE8CSQK","1np5Pf2gEVbA":"RRnzAQHH6oUF","fLOLV3vjvKKP":"GVoBm49H5LDO","J9VJPukCRe0R":"LccqkPSgPy1n","ibrZKroqiO1V":"nyQ7ZVvajep0","Ife3cKHYjfZJ":"It5jfWSjxe2r","oU8oZw0bH7c1":"NG5Qe2U7FpU3","xO77NJZHz7MT":"ibxhOt0RKW9n","iOgfahILDuBw":"KysVYbfaTZdb","mNb2Ci7EAjkn":"pGnVT0vE4AaM","ULh0DuXqhRw3":"5uUPqzmPB71U","CIJwpI47cyA7":"y5qNdLIRW2XS","SW610qPXS9hi":"8sHkfkWT4tsM","WSI5OFFeKRsX":"r6teCBodPoQm","j6R0KDG7JvVB":"pM6M1L3OkNzK","ShB1fy7ojGpn":"mq0CKTUuFNAc","yGGH3wJe47Dt":"H63EzWnMxjhe","mN0owGuvHITT":"xtq1UkRLYqQr","XUcjiXHh0mDY":"LW7ZafWpZHC7","zvWiiAKylLBK":"ISS3M6XJIAu7","YCEEGqLZIwoH":"wKItXiuO5hIr","rXDeUjWl6gz6":"g8b6y8TZzmVA","KrMeMbxQeoVc":"xh8sDwlMMuos","GsGRjdTKAz9L":"ubUuKkP3q7As","mBJ4ABpRwUQf":"1deSdmvtV645","owCka5ASAXha":"ZGtK01eDuImj","tnA5VdftlFSm":"gqJRVLLHnihi","1nPFZYz2aA4d":"fuSaacgjmEDe","hs31ODIz6sIT":"Pw25timr54eT","7T7oAWwuvMT1":"lseMHHS26OZv","rBmYbvoIVH4J":"KF4veLFp3Tzi","LcVvty6xn9uy":"l7tGjDuCFasd","sXtg3Eoqk2rn":"woOwuZXTTdqd","ZXLnaTNDUIB4":"uAKHXl3M8VGu","JtUZD9a4ZODm":"UjWGK7wvYntn","4bLN6BTMJF5j":"dNLk0pJilfcY","nOVmk34JpAzs":"f1vngoy2dhYZ","z6gk0sGBgZ0l":"oRBKz84cjxid","SVeqsba5rSsy":"Kr6K5NrVZBMM","IstPddOLSZBi":"KknIcRDe5isB","bVtwhki6oBkE":"rgkqTBU35tpG","bHf5Y7B1T7wu":"afQlthjQTDM0","UKFPnaKMmZSb":"9FHxbNBVj3Qr","J10TwhWeY7Qa":"1wfe0dYwP6oZ","4m5LmhMB3MOW":"eAVvqs2TtxXh","pYzfeyhnSobr":"0zfcJQdzbPbG","xlsamc7MRVkK":"vawbcKXV1oeh","GQ3Go8TXNjYG":"ePNylEZWwwPF","SMrQrmud8i7q":"nH1ByVeREnDi","eBeiqHjWXX3H":"xxC4ctASDsKF","KVW0uWq1WPTF":"fm33uUChT1EO","lgh8HJJB0mwE":"KA4MWUzGoHgB","FizvmcmYT58f":"HYFyk5e6XI43","cMj94PM4ETXV":"AnAbQ7NEgZb7","nPIlkk0jhjI0":"k85ooiqtUr7a","JQIM27knRzLZ":"Dc3WPr3QtZhH","X1yr2HUZrxwa":"HzEtP8TNxo8N","QeFZsAL5ghdS":"UHmP3DdDKR82","73KPRkI08RAm":"HOdfxZzR7USL","7OtBvFEHQ1Q3":"TpW67WzIjgTz","x0A60bzPEvWd":"pD6oVSvb3ofK","YsEWNdJLMdE3":"L1VYFJTmqYZo","r7Y26oCWaRKd":"IbqgZwXb0Qco","n6S249EtTO2G":"0UlFGM0YSyEU","XtH31t2nYL4Q":"o7o1qsLvU66b","tCJT0Ildanc7":"tvZRYrVxyzdy","jsCHk3DkeMp3":"igsJv8HLWFrD","5BU7vkNFro0u":"nDp8sUm7a1JI","swP6IzF4mcna":"6edIgt6ZeP20","Vw8cilL44XY3":"pWmmXhxbemRI","xFffFzZZlJ78":"R8OQBwdIFrUw","2IiJt5g3Si2C":"32y05qgl6sut","CeUDDVIMIxGl":"eOfHED0qDh7m","5cWLaRlFQtl3":"6QZ0MHn5mXef","ZJjdxvxXUOCt":"UEZiHTZpXkO8","S7CZjIU9YGcg":"B3wYM8W0kXj6","lNFrGo8tJK8V":"v483yl2gk1Vi","jHpXobkm5TQ6":"0ZU8F4nHMvAM","ErF6rGfphLRm":"VjluGz5iZVz9","wvKt3xkJooCy":"ZVO1Ed5QXW90","ipn53sUcBQhj":"TYa7wPebb7tR","RpOq9nmTt8tf":"Tgah1ZeJHhlV","rzZOZJMMZvIt":"t8aNqNkDgLLc","FGlJMQa9d42W":"2SdCzJPbTRP1","w49CmeuakiJs":"KP71ZZ4FRJYn","royomndXMvvz":"TI7qe8KLZmNg","oQkeRBkkFLmj":"ypHB7FynAwzl","qG1kcHbf7cn8":"GlcgUJsH4UgH","cscJqcpmtQrM":"rvTAghxOo4xb","GsqqXJwXHPEO":"24pQ3uuWSbQ3","A3YpByXGJP6f":"nmkVHgFSRbuT","eyRFX97MM1Pp":"PbWBmqAkOVA2","aFG4wDjepa3s":"irWzIxRT7CFc","gzVZYGp6suFL":"JJw55bAvKmAR","q4iq8Y5gYHuy":"GuwuGTu5N1V9","zMroZqCdFtRh":"MnDJDkaP9d7I","toY2nyjAd3da":"AmGmDieLrX2r","VVCocDErr2Qv":"Y2JNVSlagLdX","7poLFG0cQ5JZ":"SiK7iN2YkhQA","5GfxWTouWrIJ":"swomCMZ4eiNX","OMxCTyDmBlM0":"xiR1yVP7bRps","xBJQsxZJVmZB":"ZjNxFKeP7GNb","SHuVArW8BTS1":"g3jjqUslaTWv","ppnu25nMsdco":"qOvSOrSDcvu4","btDgHj2Zhlng":"yUAGhqW8A8Bz","s962gmakXaI1":"dt6yEz3cxLgG","oUViUVaIl2zk":"6VaIAl7Z8ZK1","JsfJ5nGvdmq0":"As3qKh5YkHNS","N1jGOx3R31wM":"ysfTulvNhB56","pZbuOtOo1U3R":"ff55xVhSLgjV","YwtDoANWjBzg":"YVWsh9qiJJfU","QbVuvRAtme7r":"kI0ohzTmoaxl","xlvRowMJHHc5":"xKJZ8iTxaWuX","OG2QFjEb5MSD":"qglB6g6VcnZH","Xwj77WDlUjmw":"2kEbqlqm1HJZ","zxAPYsDYGUTg":"of9j0nter7tN","R0zRxEW3OJMa":"8taAoYenyLda","MbcAMdaDJvhX":"huCazWMxgtNl","j9zmN1CIe4JU":"ICY640djaxic","7KjR7ackaNxZ":"FUk90fwPfg28","rn4isJJTPE7w":"61589HorK1gk","eesKEHDHcPkD":"RJg9oSkBDMjS","MALa6p3khAli":"UX4lOfoGkjjb","QkPmhNGlb42O":"w7r69MBIhR4p","iBhr3VfoTp4A":"PpWVmNPHChvx","goORrRU0G72D":"QjtsPdh0djB4","YUd0NUs0XrIU":"y7qEKeZmh3XM","KS89vd7dRLEa":"xsZ4eFKVFaxN","jMLMuC0G0MVq":"CyIFLRCSnbjP","kkyFGCBN6sTi":"DzY1DBvTJLt8","UYwjde6zIWhF":"IPN211VzEcM2","ujmt8dMZceTi":"infI4wZYIUKu","lzTGlxtfh6dK":"QOq8SyIvr7T4","yM11YSa0659v":"C2dLqPJrXr0Q","FmgzTtdvUe3E":"vplySNjIPrB3","t4a1FYvACa9M":"YJKfBZWvNk6Z","qTMGFxxD3nM1":"ejLNKaTcXJxv","BwI871bPLFFn":"Bj6DhQuAIKWU","K4mrpa5FdoP3":"JhyrG7txnRa1","tpA5VPdMgCtn":"ZpZZgAfKLkCk","Nsdu5L8XO38u":"MgI4d4uThTYU","WIy2FlKpIW3x":"AysvYeYqBxC1","VKjYt07r537S":"Ox7YgNPEyomJ","h4VopRI4u4Fe":"NyBsZyKBGGqC","HeJUdP28EwVd":"6fo0ctNQ2HkI","3A8In4nMBhAC":"0hZVDxcwrLSt","i2qoUoJgkwGT":"VyfSKHGzncK4","1dfjDt7v2xyW":"RWWXxPDIENvf","TuJd3vkJvo4q":"xCrtyeTHtTJe","PmmDjQ1n4aIN":"swur1sUbV6RS","dn5k5KBWPxma":"GBlQ2xyY2twm","8tuXGf9YBJKX":"nInpRam10YLa","1L2ce1eHzxJW":"wwxiuPTroZiy","9DjmRpaidKnd":"K1smXYHX1d5D","zP0HHStbB1wu":"D2t6R30orhsS","gXRp3uIrkHKP":"RmWx9D6GxDaB","eI6L5VUMIhIb":"oeVnUOK0vgsO","3K05hGyE96z8":"7wrzJSwyQcnL","dwfgD4QWRz9A":"SnrY5krYFWqW","J8hLH78MYWQ7":"k1Y7pkE7aGIY","PkbgzkXSCJGC":"Dic8H5Qpzn9Q","vgrZesJpDzK9":"HyU30vP80qtN","qw6Gw1zJ3hTy":"sbTxbXOL4bKT","syhgUnvunQIM":"2phWt0OCcAXV","hVL09eTD82Sg":"HbLJu1QaKHIn","MMdORIq0oHR2":"uFgWYEHuOgSj","FtKuYUNRqcED":"7MtqQHJyABr9","effa09f5ztbO":"4wt5hbtYLvHD","MxJ0iIuaEnih":"jhe5LbaZCPpp","L0eeNjA2X1a5":"GEljaHwP1IWH","wtUWLQdQpqym":"AHdMvyVRKUbp","SRSgOllsDZ3g":"zJtMdErIwA40","CNsKKJD96BCm":"9Qn0Q5fzjWnV","6HO1JO68lejP":"7DI8dS5KLOmG","NJchkmmTRCIQ":"V3aYOpC6r6HE","aIgBt7U9To8e":"0z3UsUGn8K0L","6ro6azEBSmhp":"H7OLa1NSRF7a","idQI58skuTGS":"gn0va8dNi1r1","FY4omfIil1Q8":"jGlPqvsaN3iw","KCVupWajdofO":"Py4XOHqRy6FI","WyDQNQGA66nS":"xhoiejWkp7mx","odHMlyxrQS5Z":"cNydHwwHFmJk","VUVl9kq9lwAf":"uE475inTot5P","6JDHIwhQji2T":"asXuZHwQxWLE","AewWupwQU5c4":"QC7JlMzYvKJd","VcVK6D5B7UrP":"MNUVMDMuf1vb","jo239BsDkCLc":"QxelHfFEbXk0","HD8dTMtcigCm":"hNHtWdSCHZL5","UPH5NRtaa2IR":"CSDbnNGGovpk","pgUVMi4s484w":"nYbhRsB2Dbvx","Qjrrn1y36Ybx":"HYcSERDfqmRi","oIUHVAmDKTBW":"PWm5u5m8XCM9","hvbCBPyKa2X3":"cVH7FDtYkHww","X3wOIIwjhsO8":"jt2xNxNvurHc","bKzOfq0S0tS1":"rC1WsGQEo2rA","LCJmaYuuEEXi":"3DDByfwj1Q2R","4TwUVY0h2sfN":"y5sXui2aL9VF","Z9LG1INGAHFm":"LYdgPhWOspdN","bqumRPXAvb2t":"5d2Lg9SEa9t9","PpST2vsNuPis":"7Nvp7S8cpWbS","sRp8vWmMsrbz":"PdHOBHAADWEz","AuRHdIzN1lyB":"HIcb2qjDuCt1","ggDjUc5RnqX7":"j2X9fsSaJXOM","QbXC53gnDkiL":"1aAQ370koYiz","BbWR3EMGCt4v":"1ECFkLYhb3sD","GeKN6Rg7CkeO":"wSsgXGKcPRWD","b5MEKuF9cneF":"2xlMrpjwYLau","RP0uih3DVf3L":"l4lYhukWhePi","qJ6Fqs7zwwqe":"r91wKPHTb7bA","euhjoYarGIZv":"QximWt0xRLot","P69XBgdD446O":"w672nk7TdpFa","dCeZIFLNh6tV":"yWve8wGQnW66","wXbu0zmX3Qf1":"8QerrgzQTQX0","nl9lUghLnX4c":"eLYAgbsgHEd8","LQSpLKyDG8Ku":"UBspX6IAVQh3","r5QaYWWzMVP5":"QnBLhHquzPRc","BI8qN1fZy4Ju":"QBfuvYeAhsIn","RO7ngTTOoOc6":"Vrsr765CV09f","xgTSJ04ccdhS":"kScOcw55Vvyh","wimREiCTSKyx":"lSGjxfvn1x4H","53mOJFREsDwK":"M52JMn8m8QTF","VwPfzt9WIXiV":"wmxw39eLES0g","CDy0nSdOP7lH":"lTstzj7ZVKkO","LXValnfRoIiA":"9tpOqh00Cpmw","Z0U5UDO6T3Ao":"K11lH49PBiIg","f5d77a92nbZm":"KroxvJGzgJne","rkVSgJdnnhyi":"kIfHMDd9de4g","fKMSa7sZk1nD":"cnTRoJdBvh3x","taFYCFu38TZq":"cOfDRFVQ6hYM","HJTLlzdo59Qf":"G6fpukmiOIN0","pTxhjMulUBe3":"GqV304XqLflr","Jiwc9KLzSGrq":"ltNzGy5F2U8P","vG8CoUrbJNwB":"5c6IEo3Kdrgd","OPPoFvvv82TM":"AbFY3EuXMytW","prSFkFkFdlBz":"BtLGLHdoNh3F","jWLvQFMOK8u7":"QfXLyROBqGlh","fHZ1qcNfPYhm":"QEualMVVdJdQ","sjjV8zFXpySY":"wAyLhcnBSb2x","PNyA6GWcjvtO":"YdBCMlrTTjVQ","kV7UXSxChpRe":"YoHAA83j8Xth","qCV9BmBYQP6S":"Lqu8m19yPljc","gocKvyPwZc0r":"zcmYCmciMKcQ","yZ8wH9Q1TB7X":"qvLCYCss5TTi","lBNtXXqUeI4n":"H6SXI8Dd66Fd","2sjASQt1MlkG":"gufk1NYpZLPL","zsWAwD37LVEu":"0criAO6llBjG","rBnLVIEJHjMk":"kcWgjyM8Elyq","iycOTwKt3DxJ":"NeRwR5CBtUzC","hh12jjEjDbG1":"abhTKM0y28le","SCVeRtC3hFtC":"Ygn5QFxVMpGL","YypoMBJKj7F1":"iOz7ujlK2eRw","rP9IBLmp3x9c":"0lVJYIwmdZBn","RQyVBzjet1wT":"FW7pyVCgipuA","YjvpYVhNVIn0":"Emsl3J9czvqT","dGwsZkqqAva1":"08NguoyqVT2i","ZRMBxgRRQ67H":"LVBF6iuvSq5Y","gsBZrHBYIBG1":"xHjFURBWZEEh","bwgGwlLjRkfV":"HfLaQHdgOIOs","4hM6NOBJsxjW":"XbRcTS4GC9oo","mbVBDvTKNAwU":"8BVX2Q2vHSMc","rlK2qe8Dy27C":"PIyz8DtMR7qs","lhcI5yqzLcz2":"bKSzeWIpk1l9","wLwq8mnn3dFK":"JrkQNiQMqmT7","ESVY7py2ebD8":"x0VXR59XB6s5","EEoaQ08qFb0F":"2lPKsKtFKEXF","hnSYN1XSNDfw":"oJgJITaLcvJV","8j0BxRmc8dvv":"tgCNSzZmiSFt","P6G3ylkqHQ2K":"IW9pbKJVMDp6","seL21H8aN1BE":"o2xjnjsLOQL4","Z3FTzVyfQ5PX":"PHRxi0WT5glx","7lc0bprmFLJq":"aWOhJTMVXgv9","qDv4qX9MWS6x":"tlyIMq4jPGjb","g1cWUbKFZy38":"BPW6Oq32904n","w7igfCwpLUNf":"rFe5cODLltyF","lnTdryfcsMki":"kkGsTl3tBMuc","APJigWmdiSop":"h0rxSu5xkUeJ","kGnul2XAwLvZ":"4RXfEChADO3z","GAl2iwEns0xt":"JZmYfPLU39wO","AI0QNrnrVQCy":"lKlqRYU7FuyL","eIgUlGXq7lbW":"rlNXX8q5MOaH","gTxLO0j0gWGz":"MVpEMDI7Llk9","PZZitCs4KSRz":"kjH6TVxyLspu","EtAMRIiN8TNW":"iv74IMxlVrhT","Bqwb2vRYJZV7":"nlp4oanvArCp","3EmHA3zV9oQb":"S5slDgTJg2jm","kYdonSCATsqy":"FDyhfIZUCyP7","8VW6VclK2MzD":"mNWOdPpN8RGT","FxSScFvSMXQz":"Z8CULvb0CK9v","OtoHFbjJxHGt":"eWMoZq4biYcC","b6uCgwpt4PnP":"k68iwMotBUqT","CZ6JWv8yWRsP":"B8MUWgWI1cfs","tACWEMDSetie":"S3wx0MqH7oHf","FH98HTyiLlXF":"NwS1x19JenhK","w3HKcJuiXOnW":"akogevjnPaEa","k2VWPT1jHvZh":"K9bjyPYfUwFq","j5lCIgdzXKie":"HcEahmNmy3Uk","ny2epZS5763L":"ldMp8uO09pRT","0T3M6SWP5Fxf":"cU689h9zCsdS","zw98XatgDJkr":"DTPxNTxYCJ6c","O8BmFEu5Ghfy":"DZYZzmyt86hL","KlTJXvDM3pmu":"VRw7DehFTknr","6Vl3dE7IhQpz":"PzAhEX02JqzC","M7VBPe5IKm2k":"HjZB86m79chj","rB7UfHDWqdUr":"Lv2QxelMOmxz","PDkcsjnZbMCJ":"3NhRTGRxZdqq","zC4ttRDL3jQk":"CnHYaxSc0UQ6","hTvc3U1GKnQZ":"1XdTfOUoAz45","kFhKKqhkye0N":"9DAUwyIZs0zl","4nGUmsNUkA2j":"L5usUvHEoWk5","U6rNUPMQFXwk":"Vy74bz9ZObsN","NSKfUU0fSuo1":"mOnmOqgJ2stZ","7DzMsBB8kYaA":"xERMUa1xe9nW","LQWF8Lhfdljs":"6Ao14zE9kzUh","7aHTXfftQpbx":"OFpXl5dhRwHS","GY6q1Ehvdvis":"Tl0QpsrDG9WF","Dat1zGyq9v7J":"h5K90RXjXrYp","nTydWWHygwZu":"d8B4VyDxOvXH","sgIyEvtAHnIa":"wGdawhCzBxne","IQ1yX0mVzOC9":"hHmu2MhGEHEl","j7EX8lJNSLBk":"QnQqOhTySMAi","0XvsauUxIzeI":"WBJcipRlCVgh","52znpcCCAn2v":"CWb0d8sXE4kh","7QXdH890n67P":"0uQm5LBATNW1","YcavEwxnZfSC":"OSh3OJqsF2jM","YUzFTh4kFzb4":"SpZU09YW3Q2O","SWEl1xyLMpRU":"68ku07D5LRBd","TWMHDq3NiZ9y":"znFUjFqHwGJE","x15K6hCrTs6d":"AvjtD8xJsVl9","ruiBpLaKDQFu":"FECMXdQ7dm1Q","VYeS5UyzWFDr":"nAwA6DEakVJV","IxXE0t6ZOyVL":"NW2gLgXGLGej","56EHH9wygsTC":"Y5ICU5LRWqf4","IlqiD3W58LbW":"PU3aIok82kiW","W6zurrebVEYT":"J3xGZLIRAPQr","tuBGCTzuJKQG":"rwTqCMyYkGNJ","uxrzhwZTReLI":"EVAugdC9U4yB","ZnHOV5XruMWS":"ZtuGpTGan0EC","yXCkEknYtqWk":"Euny3mUR207F","HEgGCSNb42NI":"PESxns36CKaN","gp1Q2gQ9yzTP":"6ypxC89PpNgq","9IXr0eTihg5s":"jDRhuj5cyPyc","SYetln12sB95":"bkia5iH33ytw","Jh6nz2MAp1Nf":"o4vjlUKH5D41","TjxkDjeJjwbE":"H6jylwtMt8lm","Q0uSgCZ7NFsE":"RHLP9GFkCNsL","MvXhJ117icHB":"5b7j2JbZsbKB","4D3l9xjb4xYQ":"KHrY8vDRtRVU","9UXOyYb4JsV0":"dsD28pC8cjHb","MScgg0aJwKby":"lH1kkNkWWOrh","WibbnHqKVtLL":"RkoaUZidApfM","4WP312EG8Bp3":"CSqqxA49a8Vb","NjyagEKx5X1l":"fjZuwxMrQ8AX","bHA01ZNx5sZb":"FD7AuwnDKzYq","PxtnZkhhpAyA":"wh0gny2RwNnj","vWuzuMrGauiK":"8kaCcsaf3ejn","NtM7J0VP99cJ":"mvDsuVYkhR70","jso2U2iKfFOq":"hAEqBzL7GnMx","W7vfUhJ7IrgF":"MpKXLVD81j4l","NHi3CpJjHrRr":"CYrijzmPhU5Y","HkdFVrdRRpec":"w5s1Z9Zf7C4C","Gtc6uW0npEjJ":"Hj0mUsTQuD0w","yxMpf1ezLZ2M":"0EwZCZvjE0pi","D0OuPl5JNoE6":"5soWm8lSnVTa","ab1BoJ6GKshg":"XgKNInFpTxAN","879nJsTKikJA":"dCkdLpesDLnB","4evTgHbyluH9":"Gn6kSFJCxcUJ","fXDkH1AYYr7E":"uoGjoXx2rYyB","7pntIsXm3Jlq":"LpkJSMKvCWqV","sjoNukWf6N96":"LzIhxPcYnnn4","eWB1o2Z8wI3r":"GeJLxBQBpgyg","e78KzFbBGGCg":"Uw6UNbefCpOt","8mn6nh7OND8H":"Qy8hSVCU5gVv","eYgFX6IJbuj0":"uaM6Mqwx0rMc","mUUopGQitoWP":"R8awuCbSTGdt","J0W7GG5AC4W5":"Gn1z1k3v9koJ","dLsSuILdPyQJ":"7wRtWyjjvTQ7","5EvQRutEmwuc":"hzvXyAkMkIb2","7t7SyL3ZmnIW":"H7KJgOSJiV5V","M15f9g6DvfUj":"cSbnN0CLnXpF","5bD17rVcUgko":"vIuCqiKgXXgw","kR2fgb3BGgBT":"bYE6JVhsnlAP","LhMoT4LZ2PQ7":"XHApWxquDCvV","1r3e8gHTxvXR":"jdgPF3vUzrEg","iP3oZZlPzeWM":"bvUVslfnhC1U","ZmgwpCjEFUaR":"32IRRrMjYFlH","bFUU9FyDvhqm":"33FTRJ6SuYuI","sS8JOvuB3dic":"fdM66TUtXRvU","JrLKmRCPGFxW":"WPPb7NZCmM1q","dALRDd6SN3WW":"gg6jTIKq2hmV","X3fDvFIWVjOn":"5xJuqICHfk12","OATWSw5fs8J6":"Xr6DbzqGUDqR","8EdEroAStA9e":"Nsm99Z4jLof2","G6YlwM0PJz1E":"rRdkwLyyvLg7","IuEIegoVcZ0A":"owQnrlNUb9oW","aZjoUHOHvey6":"BwBtuAk47uL4","mGmVyOWHTmw3":"aM8KbHniBmEE","AnJwfNYcwQXi":"sam4grLl6ZoH","11rtW7WKFYFK":"6qNdvY4BbfKG","z3OfEIntlLsZ":"fHsyZW6ZjZYB","9aznwc9yJTVy":"BdW7dlQTrfjB","3MtWp8rHQ6Ca":"jr1c5lM5pHFb","16yaMvMvcYJz":"Zmq9rEnbTrTV","5l90sK5F56lK":"Guj4bPR3uJ0W","ukWB2dtKz0Hm":"mt1WLLquXgg5","CH1meGX6DCxE":"4VXyGPLhpTFb","f4Rtfy4JeWDO":"dYT1RQng1H9Z","WDWZsisTEY9z":"LNAmWg1SQfrQ","QL9jivoPDXbo":"XVy6t0UEUz75","VDfM0nlgAppM":"RfrfJ3uK6I5P","tVpiBs4XM4Y5":"8tIDyBHHW5Yq","QVRK8T9Eytj9":"zBlxrVlC8gPC","Iq1szD1mdVRg":"KiI4NhGQnJcG","gKmSd1TgSNeh":"1H0q69tIfhkd","x4oCGeMZwqao":"EnLmOExitX3O","vHmC0llaRlyP":"TuOrekgNu0XR","T2SKS5DL9c6v":"Fhz1OJWXs6NF","lzi0NaJVrSqq":"IN31gLkZSVnL","ePovZMoDhHRc":"T2rDK1aNAS25","qXKTQ9GThj3L":"Pl4WIbiP5AAO","ICAijfyJUN85":"gpLgczj8wDmY","KTBk7Biax2vy":"f3HVOxlCxBPB","SwRxI9ceOr6m":"89ok28HoXHT7","jYxb5ytNKUma":"bQ1Ee9Goa2Vi","zamlyBQ0W5cQ":"vslYwp6NgZhO","egRnx8LCggCa":"W9FIMPM54EKU","4TSFzZXeqXXj":"JNbr2JC89pCw","ukJvBVftERsj":"89M3mu7mo0qC","pEDBGMAiorWi":"8blj98VQYZwg","yXA2J3tizn3j":"aJxUAZVih1Cj","pobDM03YqUq1":"q4ffhP5Rh3Wd","JsJ4FGi1LVJ5":"L9IXHMxFKtEl","9imMkBK8WmWt":"TMmyylKxJwaT","S2n6ikZJ38Dd":"hTxhA2aYA9Cb","VgpeJoMwWmwX":"q2N83DzAJCO3","kTJ0kdBGoByd":"U3SPNo1QsYKR","5xpSsf6tKNEg":"2CIDKRWLuij1","xISnQfvzo6Sw":"YGOkrRGD17pb","L3HRoKorOQQ0":"D9lOEJLwCOlU","cLKDWFdYELtx":"TTMU1GNDt0Ob","H1iD9dJ739sb":"TPaXZrJHHbef","pTZqIeODlSV6":"zwJBnbplzsBW","HlGaSOUKOpWz":"MpqHkOrMFiNr","heQtFYwjqkob":"FAAT3dSv1TNG","VbghvGBou9Ji":"bVZImk8S3L20","biyOvASHzFj1":"8dtphFlL9i3O","8SeWZDHEMm4Z":"QvBkYjNhiqMg","faK0x4euUiR5":"R5nBel71ygY1","FjjJiTfRnMr1":"I19IAWisxIAn","ZDgUlktUZ9bA":"u0pwazfPf4C3","k8pLJTyX8IH8":"fYRFNl9S8RPi","UzusQ3kmEarh":"vrfFyMcMuyt0","rHtWfn0C1Zmo":"5E1JDLY5urgM","bsrABYGkxfRv":"iCauaGa7n5qr","3dYp2KKj7YXm":"NPidTEulYgG8","hi0qVShrM6up":"UgOAGdBq8Zvx","I69mJ8ciqlsI":"cxIxuRgiMdtv","0cGPInYChmPI":"vL31mnk5GOkW","r2xt9OZz9gHy":"uXfoInNThQgz","YVMzxsfeDe22":"1CRTNj5C4Y0Y","8glxiMJc01CB":"PNbL40qAjfHR","IC8Yud7lmZy2":"08gA8KLx535X","vn7ZJadDVeZi":"3eVdDs1XLOH7","ciTsS8qxgo3S":"m2BjNQHdshig","XJ6B8eeHBhnj":"a33OE9vDjTAM","x7yWVH9xlgh7":"T4u3DHEQcaC2","A305Jv3LVep4":"wcrXhJiycvCO","FKsIRDtsRZYe":"NSpMmRtOqb7Z","2ZWkfDHqygCA":"Ra59KI8amAPP","V8miG1SKAFa7":"r2i8i7vcg7Uz","y9h2Ijd4zr2f":"KtMWOgxgkhUB","trN6eJx9Mc3N":"6UbD0p1ar0J7","dLM9qAGFTjPh":"RvMxaIIJ9Niy","eCF5GlarenBR":"QuNWYiL9aAEi","DuEyjrrst9ht":"NqnqBasAgEwS","Nsjtxmnp8TGk":"FytycNQELcKH","2iA4GM6Prbkq":"ddn6Bq0mSVfo","0O0PN4CKR87F":"1VCg4HUKYB3O","45AlGvArC2y0":"650oijyOKvti","LhemBEgrRMQl":"aeUgJZlDh242","1EQMLVFnARQP":"4GOQmjrtJFGm","LY2VaI87P39d":"QSvYDSxcmwP9","4kO9prSe8EDf":"Uex2At8fMSlt","bXlQI6Rg1SUW":"P7YZl9LiHdRr","7tlccuLMa4Hi":"dPtwet8RU2eW","kaiEcU31M6LQ":"7baDmasjUXdZ","Lfd0pL6bZGqn":"24LCxs1Cdgfo","6U1i0LhJpaBR":"tQPJmdKzRqzH","6WWFfShpY5fx":"JX0utKAsoGfG","7zO5OJvRKzdX":"jf4NYpnlY0bw","6A8HhGcjdf6i":"gnV5bsF803Qb","ktcfQjKgnfhH":"e5Rgn8yIa0de","lSywNpo7P3R9":"uTNeSZzZ7oFz","9oZObjaa9MoF":"OY6xhPrqyuBV","WN38coQ2KtKW":"v6yz7T2TF5HP","7ReQniqRu75E":"fumpSdeswKTt","98tcE6wLwgoe":"9O8lGTpbJh27","rVhXR5uEVTGU":"mdsSL7iL4atn","RrpTzSAzPjZo":"Ht6vKdc6yyJw","BDSjtRLGJ9cu":"kAgzdjhTEyax","WlAgVFlg8dH8":"UISdaHAPrDNd","hSOiIyhFkIJ1":"d8WcKl72yZwJ","FWbftF413HyN":"kKviGxGiVso9","HBDmgkSZMaD2":"zb8jsMCyAp5p","g3YIRtYII1xV":"ZcODWQ2g932s","qDKt4SDtRnom":"avSDcpIXKSWk","qi2dKdArkQWX":"Mc9bJeefSPgw","1QRCVtn1LrNj":"50EefwUpAVm6","oBR3vMho0ns4":"3hfL3OJcpMDM","eqwAe7v1p2xo":"yDc6Y0LtZ4LI","n2Fv8HOny0Q2":"j7dJfR0wN1Sy","KBUY6HuHHvVp":"xXbJYlh5LJ88","rsYjusVUMImI":"uk5Blyvp4Fuh","1K1ZF84fXxlK":"e58agPji9Esn","HFwkaYXZmY16":"Qi4eDuSptmpL","4ttjlUJc8eyD":"kAkThbWKKbV9","VRD7y5c1mxfy":"LO8RCfCaK3hH","YIBsvMtHKM5f":"uqQTaD3Ar5hr","etpbpi5sewfu":"DKpI3LGrAcj2","U0novHiC7Id4":"NFX0VIBm4G9G","tJAyoSZV7o6n":"b7Sy0oD2UyxB","gttx4wylmRqZ":"45Pc9v7OsSib","ZDGbEdP5iUpo":"tWvVhgTDWy2G","DmjyfSdaEu9j":"cJs1wIqQnlP6","HhLp3TbmwjZN":"hLHOHbVrT90V","UnM55npuaT79":"a6gtTdUeAXB9","H8seJxSIMh8x":"FmWXUP4U3HVg","cR5IkMZER04D":"AfQjJIMYlah9","7X5J0aNCOr83":"uLUUrSlL5q3U","PiyS2M4Kuuas":"LjKoZlFFPjCF","EaNaK4pL2mbb":"Xl9oWXshdd9h","MaOAGYmESOnf":"QUFLVV6Xf34y","YRjUBu1ZS04I":"paqInklFYNlv","G2YqNzLgBfzB":"rRRTMyzmlDOh","xzLvEAVBc15y":"CpbAoh5i4TtU","H0W5ll4OaG84":"bN783ZZJyU9M","xsR5VQcErQWq":"tqLQ3DQ3VFl5","Wfnthl49zCBD":"gDlhOShxrL5h","f94jdkKnZGMj":"QaoINDhfpZSw","6Jzd7EBTAgw0":"3i30bseCofvt","vvgmpyJTUnuS":"yps24R34fsUL","YqjAqH2M11dQ":"9pLExyPExIiS","wh5wDMHGR8p4":"Kp7xVL7jrObR","EO0pLeDOSLqd":"QTiqs21pBqmO","8EBAUSWOgtA8":"NIPHLdxowIGC","9JfWwkKwkk3l":"pIfb2XFN9GQP","OEioztUWzznY":"RxhGB5IxwB7G","OAH1zVfqHlPE":"k3eb7RqDmWw9","5SqbSpAd12D7":"OS54omRtec9s","CcPg67TvY60w":"1SZPBRNE6y7u","rcmDrtlnHo1O":"Z5CMpBfmkwYU","w9RiwjmrgoIO":"FjLuTjBJScdQ","36fYFWJOC2T0":"Ncszy3WeSEUm","M9s8EMQ8Uu7L":"mVf0obge87Jn","iI7N61rOwet9":"xQzUF46QxECr","w4eTv3SEuW3b":"QUN6YWPM3DHK","mUMckhrNmn5M":"smRgkg0mCoI2","1bF3gT1kyGC5":"dYhZmdwzfp09","eCcIQRA0wvxy":"WJaCa8JJze6D","ktBSxRH6mRmb":"7CuWBkwdGgZd","OuFclYV23Cjk":"IhwAGryrn2ea","jf0HpZGowxqB":"sAYCZG8LWvC3","oFEzwFFSyh7U":"9FL8GprEaWdG","B0UYFTymmk1U":"lE76pJ7A1ABT","b8bNHP26mrj7":"7v8ps2Uhasg9","w3C47aiKuEIf":"kKnYdRVOj3Qj","OLbppOtq9SOz":"uQmGHjKDfyyU","Y2Pc8xJyY4US":"UeHLahnNTTf5","MKPfny1VH1PS":"ZqsyEpFr6TdF","z5dfg3StC7mC":"bU90v0DWOMYd","BmQyhK3Hta0Z":"YChLddvZs6K2","02HLHXeS8bEx":"rqqpPsUJh3Hh","H2pPY4mPXdwI":"Etc11gUdFqLo","v8CxOEyKzJfs":"G2w2jdOE0cC1","DfHlyGGscYDK":"WfgYrsNwGARL","KB7irgGSMcBx":"MzN4RqWv9boG","OK1Bp6dBYEuf":"KSMcRmwFyRv6","d5eFIC9BlSzd":"sTUXiJPRqfa9","lAegfHrIpQ0a":"vMYkbssvsgyV","y5cFZdc7C7k0":"R2hU9Vw7Blw9","4rieNE72EA65":"3gQ0UqgGniO5","7i8c7hX86QnA":"QGPzzU2uTVDm","ogo3l1vpLOjt":"WL12OZiZnShD","RaqjsJwTos0n":"312p3WALTQyD","GaZGDIZUZteY":"sX1rjDxXohrX","WEdXDDYmM7xn":"3GiyL5sekgOv","b3Cq0t3LpAsg":"tUIJn6EBjEkP","L535rCH3d9cU":"X4UJBYF0eOkd","RFBsiQ5XQlpV":"UTG2SdcLdjMJ","wd5BjcpST98h":"jaR2hzezzDZo","28YO5r5BVOAx":"an1IxodAWTEv","AHOcAmWNdaPk":"k1ayB5YSh6Mr","9sZlv0g1XHSu":"RuXVMaVCbHVf","vtCEfvn8U1k1":"d27LIBFxmN1y","jyOXJOYzY1G1":"8eARGJ7d2aW0","j9rc6VLrA1EU":"OZDke8YZfgTU","53EacKG1mtS9":"Y1j6htanSzG1","sI7isBRHQlCp":"KRTcmaRaMFaA","BYUHuxWjdP6u":"8STz5o7e5aYP","wE64W6Wqhk1E":"TjZ3uLW2oYab","yASh2Z7i3PgU":"smpTS0jbA5aE","pD6jdmFYKo2x":"rOc5vAjCkc0m","i3ZeN0SgsDHP":"uKMXJUkb6oAt","TAO38vcvD8JE":"n0oayNkfd5J9","bz3cGEXma44X":"YGAol0TKxnko","DpjwTU5BpQq1":"SqRfTB6wduMr","bqUUzHo9bDlb":"PSxkR7OqU0Iz","MkK824A6E1vo":"TP6XBQ8qRYkc","wktGkYPYcOpF":"0wEWINxcQ6b9","sMxlDkmKVTCr":"sZAY170WxChL","ucpNdz7oNasm":"aSrram7JDQno","gBsBGFOB0ruN":"GgBGsS1LHmo2","1sZkSkiyXBOs":"ilIepIFMa0K1","2L0KuMV6bPp5":"OOBuTin1i4mG","F1tvhYjn57ti":"O50PFrA1W5Wj","KOGBz870SXbr":"W2jSdixWcfhU","0xIDVhWFLjJq":"sDxci77lWuvI","5B8ic8Zr5vpO":"NqkCyH5TY48a","0OXOTwhnlITi":"rPoLDu9ffPst","wLAT09ctWwfD":"moItC3LvT4uH","BxjGj171BoT9":"lS4NQYGX5s9S","beOLF177DaXG":"jLDAeVpC6ccf","9cXctKUevAWn":"6uqOq87wIwTe","cRmwLyhMXL5I":"yWDfnr862LwW","W2KuBMEnbufI":"SMVlPARmMlUx","6rEgYDIKHVq9":"xOEE6KcJInXi","Ij05UJcsHR36":"1aPtiQ87Ecr4","gDCa51zhS5rZ":"eniSBgg1oU5W","hBJ4RzdLxxDl":"MSh8u0gR2GE8","9Y6otgDk4bGy":"GfV3fI7ZTBMC","LcueXhiS7USM":"pXaedyaYLWgv","DvxaW4BqXyZu":"XRP9ZHwzbreI","FZiigXDpVF9R":"vQ6daNZO57k2","bvTJTUlAsRqS":"Z5tC1adGXMCp","D2Oaai6umiL0":"CK5XpG29FXj4","SvpXCZq1e6g8":"zEPKPaVAqdOp","zYxBxWmtrwRE":"jsxYROosOYw6","Jby9Y3qGvvvt":"dqLzb8vOphoJ","GFwfUCxm21E4":"Blj4aW1ViwHN","pkKuPrH7InPs":"ofrELnfWu1nc","iCgYKCtJDajj":"842nEjmKiN36","zzXU6cnNRiu6":"Sh7RKWzDFtTF","keXR83Z7bmgj":"U1Qn8H4w6N0j","HLPB6FHMcQ7i":"srZBueJ6OQGc","3LaleJLaZTSa":"CdoYuQVP7Zux","lHigkZ6xqvKs":"5fhXiTZ6JKxT","PCMvTEc240lc":"KiYBzHV2eTRH","PSVyIZp93g7L":"E3zYeR3NfQdJ","5DABkKivfcax":"4OtMpLm7lv3Q","EHGuUeOFpD5h":"Oc1SQ494qWiP","5NJMgxMVqjJR":"FDBezZ9sIrnO","TKPA6x9OPxit":"knFbgtQhlgdU","2bxEVrcasU3O":"AOddnL7rGWlB","9fI2gozkzFUh":"dpJV15tFi3r1","KhtEJbiihQGo":"JylPd75oEEBy","8N9xt4AadwLl":"rsRNNQTAv65w","rVEOWDkddOnq":"sxSN9jlRk25u","q4Wddlu0242p":"Vt985tT24kfU","NaPtBaNOBOJR":"0M3uE4a27Cgu","aRwAelmFIepz":"QBBIsDfYHnSc","VY6Gpyo3G7I7":"nzO4VisfJWUK","tJHqEHpEDDzj":"S308YnwT0AR6","kQtbIPmYZ99a":"GkyseLtbyDn3","fhfv32wlKemF":"vK4Dm7Dya5GI","hBw2FnL5bSFq":"503maqesIEWZ","8iwSMNKZqSJ5":"Z2btD9741d8H","4KVyOSJI7hJD":"z2lLJfrhOASS","EEHL3wOSdUTh":"HPLowucQqqf3","uNs6g9nRP8Pm":"04coSZT1EANS","xdXOtxF6quk0":"FZ0zwj2T9RpY","t1MlfeT8AeDN":"RjdNe0P8qcww","GY3ViKnEWOiy":"MR57YVOOiUtb","IDiwgiwP5Zza":"C9nDsAXjlNya","4PH8AIfmigRC":"llr41GqFCWcc","HsTKrjW0s3Bi":"6Ws1UUxC5m69","7FV9HOBeQMG2":"r8CMJgo6vWfr","eYQadw23CbkH":"0XhUnOytIiSt","2LUbpHh0p9sX":"vijVBW4lREXE","k4Qa3ZtawTKn":"PD2niNgJp1ux","eyZnkDHIR4B8":"pUSgtBaZpYsh","z3LQ4mUCM5B7":"akOXnk2ot62y","xHcHOvw3yKo2":"YDyRdwADvjjZ","dGsg9XxQV7AH":"udK1PblYy57j","jrVzNaiL1HOf":"AX02YyeYN97U","QSoj5lACOHLN":"2CaaYQCHLomo","XhIBgKPxo8fO":"6SrfH44mUzZp","fdOoR5y74Wbh":"5OpyhY0mkOLv","STXdX018hsGh":"VGBc3XgjKoCM","ptpQsyAyeny4":"XNUyX6RMFCf6","tqoyrICcYBqm":"weXhwqvc60ep","9wfQ00nnZda9":"Pa1wz2TVrimG","3MnZPaSOfHHa":"mtuvSW7mT46B","Ov46KD8jiJer":"Iieq9WjWpgDZ","IBsRfX0JMHj4":"XtkmPZLIRZUc","bvjIQ0mvWJr7":"HVSfCqGK1t3q","MgHb0klqDWDo":"yOGVWJFWvrWJ","XRxtnPj52cbB":"yfrg6ER058Fn","PY3fyISpLb2v":"iA43ielnt6xe","RPzuHVXgmbLj":"05FVGQOtMIdt","qGBuukkSGPqY":"gfym1wdXYDms","9aHyXvEFkprN":"o4z6EzXaQnV5","Ol8r8cY0M1Gm":"E7iEZlvxZIqx","F87DDrCvyupD":"taYTg4aHJbLn","x64fNtKhadG4":"F7dQrHxHON88","0v5TIfz8fIMB":"lqBZqxq6Y0bb","WHwLGmp0Ezlm":"HSBc14siHdHt","dq6yFA48Dx0o":"3cqmGBU2XhwE","hPV9uJr9FO3A":"AHlW4d9vWGBD","ywF8rXKt1Uiq":"NIXWQzwEdIa9","kDW5KXAVrjru":"FmUfgEDHrqbt","WXECIUcSC9kF":"oyo4veeAdQB4","uKP1LqxoBsMj":"tGfHTaPfXaVv","JIyEyq07hgha":"74MGuRrl0yEy","De9oxo4mMBgZ":"05WJwAh4oSjC","f1veKWfKSC0C":"iUGp9PF8PY9X","AJ3eZuVDmo8W":"vVTlbz4xR6W8","Maw1c7Qo6w2A":"ksRoO8OY7euE","9zE4RQWzIlJc":"4sndbi0sjpY0","xrWbhXKcu0vH":"ZVqHp1qfLRSp","yHYe3McyPnyF":"fdlzPwtL8BEp","fmXuRRagIQtU":"7TKoTtVN0S2y","FWa83x2McX7B":"59gDhuuV0U8N","s68g40jHdWc0":"yiuacLOc4vU6","BEEuqPt9RJcM":"AQUUdXueHlcz","z6M8Xhod9MjN":"odp25KMn1DZE","9FINj6uvHrG6":"iYMEqMPDRerd","EqYIdAQxkNLl":"plTqQEdZIda6","QqAK9p4sPdgD":"ON6HywtL5UzM","17CgSWssefxL":"9bIF6xUA6YCh","fxYzbFEWIqht":"MzeGsrirGI6P","J5RjcMvSUsjU":"uEyAA54kxhCi","5ERmgp6C59mO":"aIRXgEpY4lcM","Y6bnZD6Mpi2U":"lvz5yGH8AAor","6kwsTSBZkSWT":"xGsgiIEbd0ly","B6p9ZL2OkW80":"WTMvb2k7XkMD","WtTU60a2SRwu":"ULvD6E4flFAI","vELqLfNQ58Bv":"i3bMFZGskfAh","9tOslGtz6x6z":"KkVCAie2OwlZ","IAlZUmRRZWHe":"pp6qkgya77TS","o3xhteVLovEo":"KbH8ngS78PZn","4ZZvnSWJhHFs":"1XxWlEYqrCWj","P248pBiiufzL":"v7hI6o0lnkjN","j1StnbamPbex":"wMY6gY7afCRV","wsroCVCDiAsS":"4tRsBEkWz04d","nYgJwlPf7gSj":"YwsRwC5VJPq1","wo9zJzuE4mIy":"jFsiV4GMKbDT","cMadgVekFtY1":"YjGQIM9EalJ4","p27anWYbXsBM":"E0eqkajoR83M","OMSMRgpgNG8k":"PqLVhe3OsUHs","4o5pVdiUGXin":"UMMjOgn0c0m0","w4Weg20R1I7F":"gBtY3Z3dmsEs","RhFPX02WrIjx":"HirDpJrjPPGN","Ysa3NkbUk17v":"khdvxgk8aZaM","rzH5idhLrIVt":"FXwXedJORiNi","5sWLRiCa5pT1":"QqCF05MphntT","W0wbHUAJGOqk":"jGLdnQ0zdDXg","0wWTlKCd98Ey":"Cql5fQFjkADx","uH67e9wdQzxY":"7oywTJ20XIhN","aevA3BPI45rT":"WGVdVESsjNG2","J7LCnQgePGB3":"5ti29ixxbNp4","D9YJVoi4vSio":"V1noO8Dzv1po","E2RL1sawa9Gi":"4sLcXM6FWLS7","C4LR0Lz0d3P7":"UhlQlDGYniCp","lZLrYi5SXpMB":"frTbvDWGVGFK","d0UPgtjugRL3":"u5y59IqLDDNi","oSkcvdOxGbRf":"JJFicoMJvfAA","EzT7GdeG4Nsk":"7sfRvrQKqCb0","VsZ3dtTt1Q4s":"qfUEYYXqVScA","jBbrjNaRh4QZ":"3bB2v4GWS6Fa","Bg1gCGCAfKKE":"5JT1dvFfGiDU","Z9te6I27Rzit":"p9qaY0FXE681","9c4ieEU7dr1x":"YkZisWU6Jqxm","dnv3BnYKmRUV":"7SKkmWWs69Rz","AlLET678GHAQ":"5vmhnyfiXb6o","ebVULN1pW4VM":"8heNZGcny877","OQ74dm8d9SCH":"RswuVluuJ9hG","cIk3X7e7Ykor":"EiYyFsEHBdfP","36mcVpYgo4JQ":"jMbbEVVve6Vr","1nd3PWDdzYQ8":"t6CMFzQbvMpB","CjWvsbXoWIXf":"IuTCVuAiIPzA","qQPMCICffplZ":"LmbHWOa5N3dz","1M6Qz37WzLf5":"Y93wH7Bxdsfr","KGVA1nP19H1u":"k2Dsf4oUCIZ7","FGZWxgu0nhyH":"INZGHXTR2nU4","QkYRPrReGwDC":"IzSNbFIOuZNT","v1BzcNO0bRKq":"rduDGpzSCEtx","awM4Jsw6AdDR":"6FzIYSgTFnzZ","BykNs7DxKAvB":"APlH9YhKWc5X","2cCDOriQLmUg":"9FB73WEJhd6G","q3T0u4Cq0dyF":"01TWCx49eh9w","TS7rDWnVE3xY":"301GVtIu5nLa","OPxhU1XiUR1k":"4e0AvAfNW0YG","X0f2WzlPH8sC":"ytjRoDrr0Q6c","C0rozmpqek4X":"Yv45gg4JyxQ5","LjVc09VNZLlD":"DAxYqummlsDo","glT9VR7QrklP":"Xj1zO9RzUbLN","MJhAwUsfCXCe":"5mj92Lu6FUfl","f9pAy2b1WsFD":"7eep1oWOtq2T","8ChRXAijhWpy":"xyVq9RKUEFmX","hGXrc0omuldS":"V9DEsgTbUSDF","8cJLRrG9wT3H":"qiGQv1degTdp","DFm7MRTzJu0i":"6ScMIfir2oHG","hCdHIHovIjnH":"O0Rr3d6U3tAb","8PCeRzrTQZDf":"ObPS7W5VQpuX","vJCRMpbSZ6IX":"6SKgC3MDPs8k","vHpmbOQXX3oD":"6VouxDB4816l","dSCqe8yka4Rj":"ONatOIw6HKlW","MucxQwYhVe1x":"lJ767FEwVqPP","iJfq6W9iTNSf":"fxWSX0McJRIg","laiPGWotdUva":"Y2ORuh9i6pVL","iYMCNYSmEdum":"pJmJ6CdDKDxb","jY2LwsA6AsEG":"QiaL2XHq05VL","pKI09JLMZiJY":"uhTwIFmW0weu","KZQh2DjD7Rr3":"YNM3RuTAODi4","9966A5LIMsoO":"Jkce4musgLwz","p3MQDWnN1RbV":"J5CpaFcYk6ig","ZYmwITRWOKjQ":"3tvGmDKeszaU","bzAa07EfpH0m":"Am201rUY6dA8","8jbkGnemCBAq":"upWruaqZ9k38","XvU2ciB9MWCU":"fnpwUYmlc61N","vifjlohSbPoE":"lOCZkIwEIuDq","R6VFznJDsbcH":"w3CyXrv4grGJ","5q8Cn3VbRcca":"dC4wheuddC6k","WsKvmg0mSf71":"eDluU4z3Bw5R","FAaltyMRkGaq":"KGgvWL45gU1F","0nMQYv5CUfPY":"OE627SKToCVd","4cFfMxHi6czb":"IJBoN73vipEz","pCvivupFCuwK":"LDBF7mq2X5WB","Uvm2xgGt4SyW":"ZuKdE96nlOvz","XsCgwBgLvPFp":"klxUvH9oGdiP","ovJKpfQp2xqZ":"iLs5MSZxXYb3","iJmuzTznJ89d":"UQ4Fadzw24ge","n6oDNZp3Rkq6":"ZIrnTyJg4mWX","nB1Cm3L2xhDF":"j2al06Xphrnt","WVvMSoHhjufb":"m91nRB6124ba","ab3O9Rp3lWl3":"p4xl0FJm6e3L","pmGBuAmQjNVq":"PAIY3ci09xlT","MG5ES2wBUHzI":"x01w0wOAGEmA","6KVkAPTt46dM":"d84a862o8btj","mmdkhzcp8nKO":"J4Fm7Hu0a0O4","GI7fjfRsl2FR":"Dhotc7Ps61rz","rJcyLEtLs9AS":"86jOyESy66p7","XkLiw737W61q":"pzMBRpQDM5mp","YkfVOLWyBBhO":"n3hRGsxjI7NE","Hb4djH9vVZMn":"BSQWHddmCyiw","NEArk4ymCxeS":"z2yKvyb7DgaY","QeBEtbJ42gcN":"ISxCncTsEGEz","PqKhwOmvpjVp":"SwCmlKquUTzI","afQnZBJaWzoh":"KYcwnRhs1tVr","ALuPkVRNmypG":"XhM7VvAsUbcI","Q6BZObx9axbK":"oHVUFO64snwA","G1Vq8X6rq06l":"S1KDUnu1E8sg","BAtD0AYhEq2x":"XOh7yScJHLgL","p8HoZpj5fg3Z":"9TIxmf4KyRPl","Ajhtc7y9XEb4":"zKsK7ng3Whq8","tIKMGBOlexkl":"MAmlryCZHM7B","K2DKqdAHSWJ9":"JpsgmxlIygrW","YhPihpTba0wk":"MOkh4sTBwpDX","A37NFcDXJ80z":"ClpCIRbsiu3p","AmAylOkRmczb":"9cWrBkdWlSmc","qHMlhR1Q5Iyn":"5LJDdHecs0ur","BYmABhJANJy0":"LiD70JxinaiN","WAWjCsbXb6u9":"EiySmqpN69l8","zJYhjcjQY8y1":"iVS7pWBThVTM","Ki08oRSnaupT":"m2YR7qK1b5ab","oy82juGYVkEp":"nTBK0XycXxzh","c5J4w3LaBzat":"mtsaKvXzrgeB","kyoGDiAVd774":"FXUIO64mW5Rz","L2LlU2Jzpadj":"0ceVflublutc","GqT9rRrSwD21":"fgXtfeXiriZ1","aryLXlI5Rfxu":"MxLO2HttXC7l","r0Z2xchdf6Kf":"PzfQUcgZvuo5","PUgr4xCf7rM0":"U9hwhKosaYb4","eO1gY9JFxohO":"kxYWR5WQLVOi","cX5Hs09IeLLZ":"Q5Pb8ac5xVdA","qMe3JNTviEb2":"w4pQBa9xHd79","LWuNYaS5GdDk":"cqS9Bc3wyA3t","zYiQWsuUQEd8":"9wesndNmFT8x","iqbRa7wK30Y4":"I3aaCHpaIzGJ","XfAC1qtgqLmt":"g9ElKXAJIVAK","xqpKlddVPRlY":"gquYMcHZ2sS5","iEr57lZ2BuBO":"vOFS9rAn5s3i","cIF57lDIbALY":"W75UEScaIaTp","gvn4YJdtjrAz":"JjEfWBI7WBPj","hMDJpGHfd3lb":"XrbNSMFqy4XB","5DI7VsGu1vOZ":"7FVmXP1vBa9W","irecEYH9aEpd":"IFzDfFhqxKpz","mq0iGMeHg1NY":"FQVOcDyfIE1P","gScwHfUPGCj9":"MEvkmjrMUdtX","dwdy1l0qiVrk":"ZqMk51GZe66c","Vi3W9fjo5wTR":"wnkyUYhozDNS","1kXcUGTakHec":"Ec478VLk2bSV","B0lCIsUPktXH":"blJZVQ6SIqu6","SBQxx7OMI6YL":"JLvFulT5FFPr","Abr7WBWtIYgp":"eZcTUP5RdMMC","0Fwy4LkicWxy":"yqVqLwTIKHDd","NJOssYF1IbX9":"RlIZqgd1Je4y","oxNNcErPxYCx":"47799RvBfQtB","MzCJkwcbcBrp":"cPj3AqvVr3Bd","Ivrwf2JgysIp":"aKzzAcGYhiNl","cbA4umd8UKQ8":"K4CvAeEE1Zfg","UYzKvn1eNuny":"ADkLHlW4AUv8","AtxdEecChUiq":"cPufVFFvEr35","ZKS7X911aAkM":"GJdTrKcMEOXJ","02cXLU9Zv1Gj":"oji7HXK4TtbC","OkWYpEq9xE9J":"Kyy6T7F570jL","6vXbZ1sE6ZWj":"Ey4HnIar6TBs","fxzZooUxRcGT":"1KdWl77s7qNm","AoH70xPp1Y00":"yRLpnDOFQ0uD","PsWLQUxDDveM":"DukhKrxhRqOy","y2NrqLaHHSy5":"MMw9ea6KHnfz","ysXMHSLokJ4j":"xTwqNL7mdwk4","6hwBifJxpuj8":"mwfeQUqPxYmC","JaK9I0mR2v41":"C9C5KVmHIIJV","11OD8S8wIwPw":"RbWVgZrersp5","O3CWTKXvthzK":"Tedla7k2Hkp6","4wMQcFV78S4p":"B2Mi4PokhMFK","KJbLLPJwwEFB":"6a7ljAxTynbv","TaSQgx21mE6e":"q3Wqx1hF8xag","SL2utM4kY4Ur":"kho74hd44GUH","mxWMWMWVOuW9":"aMkdCQVUzhi6","OPo4qdTFIvOI":"9Sd7K4McIwmR","bVgDcZMJ0eSe":"9i3qUeR9Ru1d","GZpdfqGCJUGA":"sSEmmr7q8xVW","PTukeN2QmbHm":"jVKiZ4nGsoqb","GTsz8XeFiV5i":"bm2BHpkxwVfc","GTWD4dR3b0O4":"HhMEXvdV9UK5","amLBppoD5j22":"wQ7mEZtxQs7D","WQWNPhtIEeKD":"24cJxDYNQpx4","f7LA3WjfoxFy":"T4IGTC5UxOsm","6YKEwnLEiE02":"NmEnNgdvY5ea","HmpsCMbxgaJ0":"Pl1dzDkuhKjg","VqLxNukyTAKo":"JBpk5ipGZxGc","lkzQJgdPgZUp":"Q4nEh5JPhr3p","ddppKHohIjKX":"2PcOhjvth9SO","xogdYPnaI2D8":"mRhVo9iHiMrp","GaKSnVn5JnzT":"ZTJiqC6znRJJ","J7UKmDIcCOcX":"CqtVWFJzL07t","z4OVNSHDfMu8":"yLpUAbmkMvfG","0epN7wBmdGaB":"8pIkjR9YyWKY","qI0NgO0hOw98":"p7oQpzmX38j5","OVW1b029wQ6y":"aBFhTMJTecq2","kDTmXkoNc75f":"3VkKhVoHt9cG","dpvWOpyTnnEQ":"RJKSWKeVNx9E","zaKgvxco2bYJ":"NWI1v3GKb197","JVUKBUAKZuCM":"ZgkVf0In3Ixd","2134uOC4m3k5":"c9SYhUvyfYgU","45Wk73olCTqG":"PRX8IDwh9jzR","g0dbNINICQaN":"OXFxnYJKw43p","LtVhCj5zRnZd":"esQG1oOdbbiz","aFQw0V7koN6b":"36OD1NKUohSp","pTndfOIMWiYS":"aFla6w0gOi59","2fHYHPd5sEgd":"g9HgO9gPGtPT","XqzO3AarsCFn":"AahRbB6KNrmL","9sUSpIqUXDrd":"BWZi4CjDyaIx","cxSgNt5yNqgm":"Zegf8tYbSW9O","X9cFmAz8gtR0":"3x6tqwcY2oYX","PxbOYIit8yM9":"9RiFhwYh3A44","SlUt4w7cBWFz":"fkUqZPsj6oLx","8hSlL4TMILSy":"sa71vLuYesOQ","u67nc3UHEMzD":"eAvkp5U6cMqK","kErSnx895Vuc":"3CGefyqto6mA","Usttg4TnKUSd":"eSZldqD1ul6E","atZiw1kXtXEi":"Vd50FL9PMO5U","kVBCRsO4MGNo":"1VZtEmYwzCob","A7OUlMzcyINU":"mNDaqA6qfRCL","SjPVndEDFgvO":"ncAvxfyEKbXz","RvdNCofTdcSQ":"OsuSVgG7ZFjF","sos2F0G32uBl":"aePmhf8EdkCM","q6JiJxclFfYY":"ZiTQldTdLHBX","ninzmPlW6SBN":"Eymrb6KOUEZ6","l14BVNSRVYWP":"MCLbn4HX2LgR","KTwhs7HLEmU9":"pgNFxkorxa9X","N8WnMEQyU00D":"wHA43Iy1G1n3","Jh88DU0Jl4bE":"rZUzOgeCkPor","T3h4vZoLWOuI":"oVZmqdeHVNhN","0bix0jmXTQqK":"Iv6dolgzkhke","C0PnSHj2MD7b":"qobMWIrtbUCf","leUG9ZogGTCG":"OUmd9TYOx5wZ","yz1vNABQLNaF":"FfYSjGRw6lH7","KVoJ9foCiLMy":"L3ScrSj4N73K","fA94NU9xq3OQ":"jKOn8bXDkbUy","BUhLHzUyPXJB":"8HYxTkrw8nlm","5HESo26e75PS":"6elI6C6aXxP3","GhGkEqsJSqC5":"JJiK0CuKbEcc","44Oj3BO7vbEJ":"V7YwvHoVeUE4","VgRTQctfJ7ml":"S7e7oZvdGEaR","4pAyjeAkMdKh":"Td0u3tPoHHHy","XjC4HQAnoSsQ":"eN3RkQRIljWv","BC4B34eUDz1E":"PJMVsKPksr0h","MHLmdUYdc7F2":"7figjl6aW0aQ","7Hpd4t65OYGL":"Dk5vw6E4WFRa","QiC2k8tjAfMd":"jIocWM4tm6XO","Lg1TGosMKitu":"z4dDUDmKPnnK","yFW1PEAe1ycN":"XYQL6dRCZe7j","hjLP7VwA9NUO":"w73o0WZRGWSC","oaeJUT2xbsrH":"A5alXulBwuHt","r2hSwjAtszSU":"fIjbUptP3dDD","YyrZBkAC7ru3":"A83DBqpxDiZu","OegoTfVbk0li":"N8aplMi1Al1J","DOsbMBUhmXhi":"H98UZYN89n5Z","i4VG9wSiHG9e":"2r101s3v0H4V","Bd5cSGUZzVsw":"DHuaNTWTtjAN","TUYb3aeivtGw":"oLTX0LvqoGRC","wVFo6mOsTzP3":"MdY8gmf1iPBn","ftPc0UfMHlkR":"xzJc6ponxL6R","OHQpa3SYk2x5":"IL1arGTgCHcX","ITuLjk7iBnNZ":"SPqu4w67JQ6k","y5Smbj3Ri7xF":"9f0AUsX0ll3H","eCWKKaeYpCxM":"co3CN2G6Ekdn","lmAxPYlpMBeG":"LGMqNfGb9wXP","5Zhu457LuflS":"gWGRemvmJPbf","0T4NSkZxeMCa":"OHv4vrtseyAj","mktgYXZBiyMY":"aLLHNwGMY7DI","nATb2s8BbqVH":"JzlpCIHrRI6r","9TM7XxX4zHHj":"Y9gmkmrwgx8x","XMFqQswGqEer":"dtURTav3YbpA","XgMXIr1UciGI":"4pQ20kIHv0pk","kwpKalc7g34B":"TgRN82T7bENV","8JtzkUZtqseW":"dfh5w4KGdWPl","QhbOywKgxxwC":"fjMbX9WULCpj","93yW8wQThKrH":"bOtaAzb3AxhN","4CDKahUPVzyI":"EQBgWWgAGYT5","F4LgJB3DkxVr":"FfG60yM5cFMd","XTFPsCsRIy1T":"mKDId168BBb7","fIP0t1GJ5JFW":"rhfU87j5pyHY","Pz0wSC3ErKG4":"pta0vqUc2de3","KVY2bK2Eulfj":"cGfcE2IYeN4b","BTy81rJCsmI5":"2HhGbchKcEH5","RxEzeCcRuAuD":"2dinZfzPJmTD","85V1jfHqj99h":"aj9MHFtAFLvy","pev5UX82ZIvm":"uhvXmXlZzeE5","ACicw6BrnwVt":"bSUg9GnURhc5","kEYCvkAvRfUv":"q2z8YqsSjB2q","Datn5GpYGoVm":"hhTg0yjAjj9B","7DKSN4rlOzTO":"fJRyBpOaXGM5","PzA41tItdqyl":"S4Z8sB26uUdl","6ZSS37ovPDiu":"qhnnL7SBw5Bu","xt3J26g3eVFP":"Ik4FyvsXfX9Z","Gv8dTwNQSxgs":"5qpwhToS3vqQ","0k4cGMq8Udwg":"JQsz24qalrWZ","2KP8A3VqXxE9":"I1Se1BdDjR6w","DSyASIFnS538":"LrLesOosCLzX","Y0z9Hi7Am0Kx":"lAcbch0J54R5","gr3LPuH6MXGX":"Yop4NNKpOWDw","jejVTbpCV9sm":"TVEPd145PA0L","pl4G34xPthVw":"z6Xihl65EkDK","r5gYNOr92dUe":"P95subGC4UCS","Vg3Fl3ftdDIE":"E30QhfRbIPqa","oM1jzs1kpxx2":"wtkM4Yl865Yf","tmFjY7CWXdaN":"qUnphkB3bjef","ZGd673R9wt5C":"oVjHuqonBR8a","jtRcVBdJhhsn":"hXVac1Pp57Ai","KtoQ2HTYHnLG":"KGTEYCTHpJRc","gTzHJUtsVtaE":"1iUEEBlSTDoU","yAYKT26U5Hey":"u9IA7Nu6kx6M","TmxhKNaBVpKk":"qj04m4Ua3rLu","zAmXbnpDD8MH":"UxFyj1YeKI2P","5EDWzFr6QzNP":"tez28DurTEVG","myYVjB3oOVsv":"ZyKeZrlPyemB","FobtjlMpicjD":"iIISnxe8iO7T","Vm5wqPUDunhR":"Ted1xeSuX4pR","OVdLOmec4GaD":"rbt14DM56uI2","ZRfdvbZBW6V5":"4guA1165hPdR","pS6m3W93BenK":"YqBYzsuDa2fn","2ERmQGXprog4":"PC8c1BoK6HZE","zkXpozvwx8Of":"PRvZBO96RnID","w8tnq79i8z4O":"O2CByVL3kJrM","Tijj2r7KeqSN":"mogCG44KFsGL","KImsCPsRAePX":"EWmEXPk5jRvC","BXwXRO2JAapK":"U9i1axWzBQq4","R4m0Ue45xwUM":"rTqroUpZaxzv","b0px9FcF4kwl":"GZgRVQcgvZ8F","s9pj8pbqSb5y":"m5zxz1fbjdqo","dxuHFDUYoQdI":"3rRJaImSRl7i","nuCVaVkfbqKu":"Lgzr6UXPezc8","HF3lfqH6xQKU":"EN0QkwARUerp","Y8Mk84pWOsqd":"I4tnA9oPJPGs","Ege68AQyjpiT":"OX3sgRtlqryh","cWLKznLmYCPN":"eLLDWmRtbMPX","86JuCQFYA76f":"fhrevIbG1Sf1","ACvTEKWgIh8F":"OuWg9dDW2lsQ","h7RJ1S5CF722":"XHgYbIdePZ8y","jLbgsqAC9qja":"zBErRbIvm8ZP","H1mXYdyUaejX":"6BAQHAFUhNFm","yce5na5iqEwP":"TDnjPkMQG1cL","EGa47OKt5ZpJ":"RojPQmNepkr5","vnyVkWKmKPyw":"E72vZcLSEPhx","LZqCdfYLZnA8":"WOl1MCwr8nPq","RjHhnOHx60Wp":"2ETi9FNFrdKH","vgI1RKp7lLxQ":"3TsVaD5o2xyn","1TzY3iiTClxr":"o5SEp7ow6jFG","nL2RD8y1cEk6":"ZoyEbsEUH9Ap","XZQxXAAH9GW6":"WNv8Ar9r5Vr1","mXgK0bLbPG70":"YBjhikrhGIS9","lTtmPFSGX2zP":"iUbB6EJarsTR","sTiHrC4ZZxYd":"AcLBnHH3RV3f","WdwpaftF8W7r":"YWPrGc4IGbuJ","bDgoPtkmbdD7":"dDJXlW03y6TV","wf8jjYAD4wKF":"yxqQmiWImSEt","lFaYmVsYzFhj":"IPw2msRWUBtW","scsV45KUhfZm":"y7rj3imoK7ae","pJjMwRmR01xO":"mYdkNwmacORI","B6Bktnsr9eFn":"eLPv5sVVYm9b","bLS8oybNyqp1":"XlV6HVAhLa8b","zx6xDufcPyb8":"87UgITjqwh6I","qLwHen0zY2Oy":"ccbflHAyboyw","CGYZEhiggS7B":"AqTRa45zsrng","mf3entx00g1A":"qbzAh8KkDDDK","OylskbNhpAVk":"EyT2soRn0xxc","z1iharCJz8Q8":"k6R769IV3z0i","iqeepygjhpiB":"EaWzDWGgtDmP","Bbko6yTTRirS":"MoGX27UgOPYO","82g9UUiIUiuU":"wvwQxQDc33fK","0SaeHveTKMeP":"EjMTsklnpQbh","pKYqrLvwO4j0":"RcL4lC4LPNFe","iCtpfDWDzKKh":"SJBS5wyWBT4K","XCpHfTtv8PZH":"7a5nW3mlZ10x","PQL9oZwgKm0q":"f2dWAGcRX2PV","vFcwDffbD5xk":"2aNAOiYKJqhX","cDRwnre8K5xF":"uBSwuFkCZVin","9q0HnBdlDX0o":"uPnDaSDnuXin","9XJ2zIIIZ2XU":"5ObQvxxUYr77","Wdu4zn63VfaH":"bavcMK3jFIlX","mcqOGEnFxOxy":"d9rbHqPAesfl","FyinbZgj6RBj":"pxBuCB8Izewi","kEuVO48NdjXg":"lHkvvFXJBGkS","CwVjfhpARISW":"NNVC0L0jiadw","0RwqLGdJefIY":"JPpl5JWJ9fEP","TzxeFqPKUhmB":"bX98d1DPI42N","Ber129dUKw9I":"qrTYtP38RPG2","A6cBtlGOu2xQ":"x5XnadyCswtH","Y90ddJpduLps":"Y3vnnvQdrD4p","iw0gtHdAw2O3":"SJdlQosvg2fQ","CBLpoBaC904J":"3bSn9GV0EHiN","CayfvZj1pOpD":"FfI6XeaAbuAm","3Nrci7P1GyKj":"bzyVTwDpuGNM","Mq4ZHzp4uTrC":"oTx6qgb0knHT","nBiYVZoZJZSZ":"Nm4T5SVmAikM","9KebuIUMCky4":"3Ue4y46578pr","X6JyZbte9CEy":"PD0qnqnY01TA","kPJretqC3PXv":"P5YhNIwuommK","8v4c2WfHFSbw":"S8RopxkrikGU","qpksGmnKVdWr":"P7NfS6kXbouR","zZAUiw2ZdwET":"kZvmNyvshhUg","0yS9C4naouwV":"DqPGtccS1Uwi","THOkyagdoGb2":"sJuTAbtdExJs","JXD0ZXRgetpc":"BnFv7UImN6Wt","zccqhcje0pHW":"n6Tko8hWs69K","7RXMbn0Y9Blb":"6IfxNS2uyDfH","wdKdBpfg5Nde":"Vpspu6myJmFp","2VjFFMLKshuw":"K90tN2pybnAe","1yhJGA2v97e7":"gPAG0lxhODze","AB2x1MZVyjEj":"UcNXRTUxGVUY","4O0TrERpSSRg":"oK8DHHU3GP6W","m8TMRNDfD0c2":"9eM7peYiV1Lm","BNPSeChdtKF1":"fhjK9VQSZjCN","74JsaPqQaG2n":"q84JBSMMkk2W","CpY49RrWj2MH":"dnGLDuIbpG8D","XGZGXtY8Dw56":"iWAxIfPGJ2R5","UHefZMvKcEEI":"YH759KBchfpr","Pahsqi0KDMgb":"mxanp8LtEOKm","PRCel38VqqqR":"D7obJrQo6SFI","ZmRhf4soClVS":"TlpeCSLCkgra","6fa3W93xt53K":"7LDHS0XsKEwW","l0ofBryIjJCu":"cU3nj6ry8hbS","RSN4WlAtAPiI":"ybcME1mN9GnC","QAjhKxLk5iKs":"DMlnUPpFQJ53","AIqbskg2lInL":"Z2pIcDIy7mMc","iY1nXPI69yZr":"e3CEuTXTinRw","XmDFnaDTW4QO":"sU2wdosrKxg7","uTo3BUmlLoVK":"bGUZIEYCfGRu","1NHXaMMJiZPm":"BAh8WYbTKDlt","201sd6m7D8uq":"eK4UAbm0LhbX","NL0RJDgghtKH":"lTspWV98WpYw","zTfmYLLJD0UF":"1pMkOBSTGwmn","ykQs2qEFIJCA":"18u8IhO5Q8cv","AUizzHrKmiJh":"MdahZts0m88Z","yszDPDxMQ5Ya":"DcWULH8B9qpk","XhN7gRxFrZnx":"49W74XIUbfwF","yYYYWI4a6LUi":"68FBEs6ixQqv","A699bJHdVBN5":"O6xzMYVQyO2k","Z1HeUU8t919F":"awCvOOocdJvS","FfYDjnVpcDs9":"MEYcwdyAa1HN","mFRLWfeVkNti":"MtEoncygopvf","5qlWeq0Gg3y3":"Wtkpmbg1HR3y","FKGcdL0l5LWE":"XKb5DLDa69mT","a1DPI7FlcZoq":"7siBX2uqjjXP","6UIE3Vuuj2RH":"QB7EyHe7UlEf","vTDUPU9GseGF":"VUUwuWLPg9l9","D3zPwQOT6c0u":"RwcpJs6LVTwa","877pwv9yvGVL":"WSoEvGahzpHD","LA4ldxbEGj5N":"NidWk5gEZZ4Z","9vytPW6Q92hP":"YJFmhMMFn90M","afAn01Z8TUcC":"dnWgpISzXywV","awcXmTQ7USbT":"xbyg8vbqJNWY","TBtAsarB9bze":"Q3yMRniAM0lO","vhOssxasHDpN":"egf2KrL8mjAq","P5jKtKQAvChS":"0yRWk6rlAvK7","xtS3tonl42IU":"xL2fKMwRmu4O","29iMBjwDwf1R":"hWYStHsmc2W1","nFNbRiIQrlFf":"ADr12QI058HP","mKGqRWQR248f":"C5UTndioM3mD","U2Ba2OgX5g1h":"9IPxH2lcK3Wa","JGEKBK5MxZ5Q":"qDrCt7EX7EjC","Ezs7g4t7Q39b":"utpXIWEnBBxo","cYDC4UQKtW7q":"rVEP7voFmVHJ","HSX9FkRux4l8":"zt2zUnF7lVJ6","syVdRo5VEIN0":"KAwytzvALlkW","n6O3ai6FYI5O":"ZTyINclzN76f","H2P5mapGqt8D":"BBCuAihNyKpy","5IS6qNe2iLRV":"WIPHHGKGbDDg","n5cqa9oUKLKv":"lLU0Vk4ftXFZ","5myt3HVHmAfn":"33M72XpeTq0L","1sFgxKXuRiCc":"TsGVb2mHqNm1","B1IRWRUzwsci":"5nByaJ3cUG4U","3yjQBcHjKCz1":"wOvEdHFvQBwI","Vvp95OXgIZD9":"Rg4zfWwbmncI","gPbdYovJmA6R":"fWzuuRxheDRO","47oP9UeGsY29":"jrFn7TgohJkD","Bim6KSu8Ixwi":"a6Gm9A3BLcuW","pz7y2ZTHOxRw":"koLaA2ANItLx","LQDAfVTpul3U":"PqRbUM0zuzhR","nS8dEVHpTvGf":"FMnptaaKzMi2","MM8nNRmOi9fP":"pd02I0p9QwyR","HCeFOJjTOuuR":"7LcvK8miHKGR","IPyfGXku5YKg":"FgZO3hPst9hY","mez4YyvycQSJ":"uF85JcDYAyXo","30bzfO94iti2":"FwBg2SdEOGrd","vqSHTiJQ3ZtW":"6gadoqJR91G4","ijjcZC5ErOE1":"eMtqvP6MgG0I","zUdINiGQcA4x":"hjD3tFDibgla","pDv4BhC1Qrs1":"2fGNZ9vIeNQy","0F9vyE63eekk":"FLLTrQotzT7v","uVfgw2zxKjBE":"IwNzmhhEmQIK","Fmbdq2uYgGEh":"Vkm3dtTRqHhC","TUQ8TIASF27U":"Uy5V82Dh8OKW","5c34VcB2foet":"jNir5b9DjHYO","25giQHJxrw1V":"dLqUVUPw2Nbn","LuryvOYnyu8F":"HLhEtPTSrzkh","4mOqxhosxYzf":"uO1035OuFRDx","lmGIhOE18ERY":"gucNmL1VKghn","SfShA1NKrAF3":"Dwvga7OyUjwt","4w6aQA3qn9eW":"90mYm79vUffn","lM0fFRPhCy2V":"xsJac41nIIzs","ctEsP9JRTeZ7":"YareuweRN4Kd","otw7Vdz7QHDP":"RWuAKjELGD7u","pNWcAM5tXfFq":"Odi45NtSxRtS","jSweqLzME2qG":"xobbBUUgNzKz","XyxvrKknHoDh":"A01oIF7wbNT6","fm82bJX1uq0M":"GuWuupqzry2Y","ri1pQLhhPNdP":"6RXgidqUV2BM","6T7aT3biFLV2":"LhPt5nikY5BM","giIEIOatBHxN":"VgJCgm6BrrHA","Kzz1LkWTD6kr":"z9UnSxxYgmMW","rxnNrkEXPhwl":"16ypEkjXOkzb","iHt69oYpLfVw":"f27wnSCvPW0P","ZgHdjnr8pXmu":"UkWtXanrjjfB","pYFxVMJDCMVn":"1nqTp9RpXMNf","S0nJmruXZzAR":"TfaF1giwvK2j","f4VrrHacaaUZ":"fU23rPyMQ6na","wV2xYD1HYPck":"U94J3fQhAd2u","uzXagUQjnsBs":"nEf1OfYy15Rd","rwg8bffQjVvB":"7xuVU5baCQ5A","wfMlrdtWF79V":"vIcPgnTXLcpQ","PWavJWyuNUe5":"T0MJ943z9IFN","DF42YlWC0Xpd":"lN295E8237xg","Xdu1fCrnuw93":"SPUA42dbgqQp","d5uvSYzVNDhx":"CHRfT0t6c2t9","GoqO5MZGZjx7":"7ZhIanUXT7rp","WA58rdqx4dPQ":"VQjLHMoe4y2z","xGtkon3FuC81":"ZCk5kLckXpjd","c2C8GUTV7UGp":"F5uMTRizXNIb","ogt2in7ieKfh":"luB5o496pgux","UsdvuF6YBgVq":"QYjBF3XYOxUe","HBAML4kIv0c9":"E7zS8svMTPYm","4yxKNnwINSrY":"GGQDx0Og4fWH","twoKlucNuUa2":"oH5mnjBx7VJC","7EgUiYGFkJAg":"zmLVUUmHOvKw","lJllVGUd7ZWh":"Zm0j16tALYlP","Plif1ISWYPkE":"eA9kNTRLN2Nm","1e4vmASAkdin":"LUkINnpJpMSB","iA4U73gfjSXx":"arWMnQEM6UGu","McoLcZxyHA0m":"9wpx6XkYKSnt","grYYNH3LVTLk":"WElXFQGQYIzd","H4jyt5wfkBuY":"fScqLMJA03H5","OwWiSWFNoKYX":"eKf0OUq4c2h0","wr5jsnuSDTJG":"aB7DLNMdtNSL","dx9Wudbkh8xp":"Tn2U8qp9SnMQ","VB9JPXvuZxC2":"q2gpMO0l0to2","NrJUIW6HUEA3":"pGuNpkDnx7dg","SHQSPBZXK4kg":"pp2ZzK4mVRVy","dKJJVDP4ufOM":"MUDcTRxG1jTb","JaocrM1CHjrw":"YaFhpAe0uK4u","XuEMjBSXxVNj":"phhAVRwYtz4b","E8Y9ZrQeC7DO":"5w1XTy5He48g","av3HDkIKuHdl":"MkPErkQLwMAM","cyAD01HFbdLe":"m6EoIDb4MHcf","hkiDDgsclJdW":"VFbB1gfa6Nb8","ZdiZX52qUnIc":"zu7B3xvmC4kv","VVrrkvzkAAcc":"ZjKxlDS5IAC7","LIib5fl1pONX":"AwdJ811TOP9B","mSSiDZR5rGwH":"2OWuRvPzOtLk","oxgRadSCOuyz":"5f9SiWuCs4oe","08DukBeNUN5U":"XedfW2gkdcCP","mMJS0SCQc4ni":"ihW3XS2Zk92S","AL8Qz1UsBNZ0":"2V59GAhy3QeN","1jcO60QPyqNC":"x2tnE8iaXhqR","ypYwQmQtvBi1":"C0TD3guqjKJr","ceQnQtr5gqVy":"GYbqa2ENWaqA","zxDxXJia0Q2S":"oyETlIdxrezJ","ilp9Qo9VrhVJ":"uSm7S4mCfBYS","2D2sdhtJAX7G":"sE5FKPmBcOo7","jcMUYuvcaCsm":"enfVM0Tu14fR","0CGG8fXD8S2U":"JwduZEB6PJNH","nLCUJsmt8otM":"48DB7BqVK1lA","ZfjfILK4dT2F":"AmseiMXbJJ1x","JgYt53D33Po5":"c5YhWowvfvKc","ttTtRlGAIHDR":"RQtcnyFzzQjU","IFw6MHhvxpYT":"Hvmx2FRPpKXH","tImsu2JzreOQ":"wC6I0Mz8IdAR","fyENGhXf2Osv":"nTwzMyBvpssk","aeysu6d37dyZ":"EHFUxJ44xH3Y","vRDFZtDxNUpy":"fjaRY7p52qq3","e5ApPcCnrOJ8":"EiqsdO1rHumx","O10m9xl67D78":"qcH3T4XqrnJY","G3pcp8HoX0rs":"Lx3np44e8197","llogWviCqoMg":"kF29koppPs1l","Nh23YRv72FO6":"2qwONiBwg3Yw","39KRVqCVo8NB":"YoXGVQy1CLNF","VB4On3KMxAis":"1vjzkxKwoGt2","hHPe0EyrcnGH":"qPjNWAjWaO84","t0yjzxkPw3lm":"jfB0RnUA10eF","QfqJleUFxYRf":"1W1pvVvBeb23","pgDlqiNmbzAF":"hDBhMz4RUlAT","xtLQSjUyLIix":"OpE3WYE51AX5","Z4tMNrT08VMC":"wfpt8vOiODqO","p0w6wGqxf4Pk":"Rj8cuip8P3xm","OHml1Nk5Y7I0":"7Yhm1JWdq1Ys","GkrJ5SfkvAAX":"i1XNgjZeYRl8","lOGW4uPrgkVx":"72L10xuyUuUS","vjtynMtZTEai":"n9lADu4Kf1K0","JmWvynybu8QQ":"pgFVgyl8YhQN","tMO3vGkrqSxM":"f12BiimNWLVC","0oQEzT1QKWGf":"1kTOkSZbZAsF","PnltERmZcHut":"9LrSJC7dU0te","50w3AYc7nRQd":"hFPt0uNC47HH","OpejubPzdVff":"XCIKGsupJMSz","pWZY6VO9SJ81":"xICcPJIPHD2Z","PWh8NNFavlBx":"ynkPY8bMVTBr","suYD9nLlyTKU":"GuB07yewY24J","E8Bd5cX3HFHT":"WbmrYVDCVbGW","nOWBo5hnLWUt":"XlFcvJOCmMfk","Wu5Y4zpx398T":"EDS4xkPzghnZ","xJA7YQ6ldwMS":"lmIllu0gpZaL","QNlOcoH3HVc4":"ELpatWpxgbsz","4XkWBn5qDg2o":"R2pITSofSBWb","N8qe47EE1klI":"ZPaCb87APujn","e1yscOmPykPo":"a2mMGX4tjfaC","giZIhG84Lnm5":"lVIzIelJbb7s","nvEzWeROLyUU":"oCtsUgooR8xR","A91x6g4HxBsl":"OSKh0PaXryh0","b2xjOWBzS9LO":"msLMCc3TFdJj","v7FtGkpYkCdI":"fQ6igkW7bLxQ","jXf9pUQsV2Ca":"Gs6X8JP8i3AG","eE0H9IIMiQ7Z":"e14AX4QSyeJt","5p5c65OTy9Ve":"D1ToSY3kGz3p","CAIZwkFcDm2i":"mGRkSajCo6Ah","WTnc7HXUPMAY":"2FjXyIpnWHNz","iUDWtJOd5UNK":"UHe0w00I4wUm","ly0ulxl6LB3Z":"cbMU00kicDjM","N5eFZUhcfiQs":"p8u8sMC1xiGE","upWyR5aMZrD3":"4Z0kkDdvIPJ2","Z7VuIFmzczhe":"tYqIlDJem8Ad","pNZcBPDWTJqu":"wVhbZUrKakZR","SDdqTX2AnHRg":"NIQ2jswYKzsR","wppGdwQd2Bmw":"YmUihiYVFnGH","tFzRI89q72ai":"24tWjjNAMvqZ","cafgZTCJez4j":"EYMN8Mugf62g","mY9efCsN9pIy":"oA2GazahaYzA","Kq7fdzOZmUjo":"4OqFeNzP2VX4","1wmoiRXS3xt0":"ExiGI7RZMOjg","23nwwWIvHDE0":"nCTb6gYpcs8F","praCST7Emv9E":"Q9fLu7otD2zS","6lmSGaYkGto1":"qKccDiExIYdP","WTqPmf12ehiX":"bGnC1zfGAuly","odiuxIiS46nX":"F5p6Bp7Y09k5","65gZ2vXhoPIM":"qOMu003ztdrr","2uS9wAxDq503":"DaJp9kyHrwkk","ZliCgqcNVWar":"hcovk4Ptdi3J","n5GQXffOsUJY":"pOrksiQ5YVT6","VAW1Ol4bcB6s":"aCSJygDObgcF","BeNe5HxupzaV":"F1YJcU1fEAN6","0bhdgyFqxflA":"eGNvUuOYp0B5","oL5qK2wEl5j6":"yY963xldIOmn","JxEXVk4kBQ5X":"48rr4Gyy2zk2","zz9O5qHsyJd4":"O0AHcIadOkg2","31JqjcyQwoNF":"7cFGhQ6T1q1L","AYHXJsWHTaRC":"hqPb6fGqpmkp","0cmQMV8fVWzb":"vsMHugkzSaB9","C5GmbszfRyhn":"OwrS6FbnCfZQ","vMJbrO0Mnj1W":"33YO2JZGTb5l","6aVLrjrgqI8J":"asbR1akKxkm8","rKkNuDlZSYaX":"bOIMqdI6sbtX","sxvvQhJSKEZY":"JN307atRH8DV","FE3bG3GBQRkH":"bxuabaHwuoqn","SZR52Y1E59k4":"dAjuehzpBVTZ","GUzifc0hhWeg":"VHKH9dEWRSs9","H8SZ1z7dJbg6":"01mBjQY13DqK","Uu0C7xYfAFWB":"6R7J91rfWjFr","CDNyKDGZZcVg":"dNJdB5wsHANf","tGID9zlwulam":"px3egQ3DQtgH","3tt8r78wPSOt":"cqkchLHCQTrp","Zel5H5l8hPzs":"ikXp3dWZLbm4","YOFKQq545zAt":"uXblkoU0vozv","x2H5W4lypO0D":"p2RimfipMf32","7Kg1DTwSOpA8":"OFE7Uwpz7Owq","eOekl2klO40f":"ERa6wstgrJLH","j4uxUY7J0PAt":"u8c4eyn49696","x5maEdKFlBwX":"6jMizSAl3d0U","zcRAwf9DD9Gq":"CdLcLKZ7TS46","fwe4LYGdprQ5":"RFbOo6OmsALh","ilLVjiS5Unuy":"GGvDEof9UWVp","ARJ1sK5B2IIb":"OhrevyzE299W","ps4resfwJWrM":"G9XvLuqa2U7X","rVNilSKtJI3R":"JQemgnFp9cui","exbZjCFqSBut":"Am406U5EnFGn","kGgNumggN3aj":"KLV1UVkLSus8","Q1OeniFyYA3x":"8psV00M56naI","GYuFwX3EksIZ":"jEy13iFFombY","SY5gYIaHWd0y":"jDPGtwEoLPEo","SsDlf8ReiU35":"6ALxWDavgOlI","iTKFKJ47sSiW":"t7izOTH3kgHX","n0wq3JwLJ9n0":"sC8X1hVz3MFl","q9nEBg3qpuzy":"XlOtHFc5XaDW","VleIxKzzztPv":"CDwhPfSXbDMq","d2qGr9jCPKcZ":"jCzl9W5G3JH0","iFojCOPNeXjT":"F5xtnLRlXKmh","fwncfrrU9n3K":"PNd0RPTvbeS2","p3YeBAVBEhoT":"OSyRUKCkGMZA","qpzFZTWjToDg":"0sziQ4cvI8pb","NC01V8T13yWf":"OQDv0M5n9gdm","sjZEbBBMkfjH":"j5ie3xYfN0PT","MNkGHt8v1KMF":"glUcFUpfl5Y2","GRj3SITbRxyK":"rYVVKldOXsed","aIyILJyYu3my":"ZGL0EmgeIRkr","xRBI6OZ71H9i":"xiX1AAfirTUj","DwiBVrvaSmek":"7Dce9HY3Nkav","zveRfX5flwzr":"ZZQ7RtwBUqeU","47uNQdNjgvKi":"JmK23jWyoiY9","neamQSvxrrmM":"JG8oF6cGdaZp","G8bQhBdX3Gwf":"Lc8U7Q0mrTqf","PUUCKeHlt4T5":"oJFGNmjMe2ea","Ylz6jPfALqmp":"nXkW7jFwgDH5","oMcxeD63kP0R":"WBJnKdx5IPRc","9BtsHjTZThTK":"TUCtqImvR5nK","fdywCh4Ilw2v":"6gV7bMvMIvMp","vlPC1uXZj6z3":"R7uZoPfOeqIy","o2BID8dKKxqJ":"B1u3eSKThKpU","pl3M2rtptGGw":"aD4DoTlanb1I","sXgmOGo0G315":"OZ326QcPGnXo","PSp9dssq9fk5":"0l2dLApjWg1Y","1UthxOM3WIHn":"c5aG19xW0Fla","vXJwAVCdxj26":"u4yg9uJTT2L1","3Fg2mhsH1XTV":"4aeLMeQR7zrh","J5tUrOgIufr7":"OtEpiKSoDOfM","z6SvpYuUGwF6":"kTW0PjJk9tdW","tv9TZsamc8Ts":"Pg0M5G2YC6B8","NIrZe9pfH7c1":"ozxEp2f9CEuQ","9TZPP0yqtwhn":"NOYl86VlaTxd","gb2mIea5wiq8":"d4YgRsM9SRsn","b5tEeFUaydoL":"jb9vLl2CQ04W","EI3uQXeSQ7rQ":"iQGh12KSChRW","0YGHOYpp2o09":"m8ClpSKDWyi2","VwXCiFzAY8B6":"UfPXLwO0XaYq","bTbLaQTwTUPe":"dRZZ6Btw0Ytw","zGojRb2AAY2J":"h4LozIfDetWi","BNwkQ4HXmnhu":"NXuxvVm6TsfJ","0RmsSUsVkXEi":"dXJQQFzcgU51","Dl1zQ8xYcdnp":"x6OpUviQJzJd","Zea63ZNEr0PD":"hdpbjtd77gMC","FxpqGKEOt1hC":"oMZ5v31K2Y9i","SldkbCKVU5be":"bcQ2fd4Yy4d1","uUd4gAd03vzs":"UeU71PxfTQKd","IxkNs52KTXGK":"ItkJ9wvCfJRI","TEVF3lrKDeOC":"ayebEXUimUKt","gyFX2WcSzW6G":"DgLrxnecuqyx","BzXWgvJ6aqQq":"ipnIu785jQ9w","Pnlq7WjDMCZu":"wDRSd2VuWe4R","AvnR8qgJJPHs":"qEuOPP5opkN6","s94omhqqjGSe":"hlBvKZHfsTqk","EphKEZGZDFPT":"PDZrnwqMwcYG","AtrFJ9U7AnQn":"cpjfSoshnLv7","wEmjkBEddI38":"VsSGRyc1vPew","kiqEIGbl2nU3":"k2Navd5GA5pV","EH7gkQp3TwnB":"GHcVFO5jEIgK","lWxnSVKylRK0":"NTCu6OhtW7Qu","Ug3HGGW8xhMS":"OXaIyo5Cc7pk","AmznVlbFPZ1h":"VRqnLetjwjDU","ybUArCVqmsbc":"tRJlo63QbKT3","8lvbT2Uo8iHs":"ptEvqUUXkMwR","2U87z6n74re0":"EN0ZwA3wBXT4","vsrWgZrLehca":"5tufzgx6DY3f","3mEIk1fastMD":"BHxHX2fq5OPN","oakg1uERq2qs":"PwX4BhlnbDFL","khm8rcztssdS":"UBy0TKJ80Gfl","lxWhsYEuFKaa":"Z3tTdzLHf4rV","NPtmgue5PODF":"btgPj35e2EGI","BHAsE3lxs633":"ZqS0HasXHhw0","PP0exnU9LFAC":"1NKCJGhvMojH","zQs3MGrsNrFE":"jE7RXR3xBId5","VvJngJyjVT5C":"yq8DHw1OrwOK","y7WPqssbRl5g":"JsrwGb2Zo7mx","u6G9vls4fXxs":"r4lR0ANj6ebq","fWdDop9Exi6R":"zTLcgoGrf3q3","HObUlWNocmSm":"cr5LzRNPeGmF","LQOihYCz1CmD":"GA4IDKqdQzJW","Yderux6BK4ya":"S8I8hqkjLg0D","2jm8O7DHgxav":"HWVVbclEDJHp","XAZzzjoGXLbb":"pbi6UcQA2Q6W","z03NtqIInSJv":"ErCmtdBU59JQ","G8KfEICtQQNH":"GRVQHrLCRZXs","wRP33tQ7rLDm":"JxR3pwSkwwrh","nrNVOO9eCWWK":"HOmHbbuJMvpO","Prf9zR4H2seD":"IYiN5uHyf0ty","ZDveY256XEWK":"dG1p8M7IfFVm","Slh5GfQ8jcwy":"tESHnX9f5m6J","sB7UMYXZrmrx":"n0Sngu0IDVDz","VohHAjJf2DTj":"eMzcj0jcvJlp","uD5gxeHszgYo":"I8Q53MmaYwP6","bgzuZmIjjbek":"pJXQJm9LBi6n","7eEpkuOUhr6K":"eSjlKOEDR2v5","RccouqmfMmBd":"RYV6Lpkq1Vcc","Qm1V7hgb5JWT":"HUbqwjkxGh6H","eE6N0mecgu1d":"jZtK7v00y2of","Zy1PfL9YREDs":"RPHe2GOzYguU","h8WhKpsKyjWM":"rLuQkhMkCkVR","tjZc7V0oK2tD":"OKpX6OtaWhFj","DUPiUBSs5q7y":"Es907Y3HdvDa","JAvWuAFQebjT":"a3uaNQ7EEDBH","uN4uDVcUEqdC":"PR5McklgH8cv","mVXwibWuhFCb":"FHrsSaWAYR1U","AvgE2e0RrK2K":"64DLbg0eyn6j","kBOpUl9B1lvi":"USEUeZs4eCFi","nP5ycPD19AK6":"jAYnoXIZAGid","xK5488hogfil":"Fq4H6BdDEtCC","Li0CvKrQeFhy":"KfM70Cyi4Acq","SoNFJCVqgugp":"0bfF6I4vXmSN","1I2pmzZS05gF":"LVURlAb51CHt","zv7Wk5SvD6B7":"UHeAC3tP5mg4","2fUXgWTQugHF":"O6tbWj3i3Gos","PbEjGQAm3ut9":"FAJ17V87I2z8","z7RCiELaNAwu":"k6pIgLy8RjrW","kbhidGfenD0I":"WgLwApRlHsKH","Oziptof4mY28":"yaTRhUR61ErB","N897yzobnpQf":"mPJCgjY6eczW","vqNqwK4odIrI":"iy5CLuuhOYFZ","ROPXawEzorKi":"18Ajkw6oHuHD","bPDMS6NgqbGj":"DXDwZNBcTSUs","FyaRu0IvObUL":"70XWqYYKHrJd","wNdWkWBnxGR4":"eejIR37avekP","n0DX7CFBTYBP":"iTAynpzLE6iB","34Q1C6BVaLlL":"92XeFRxjvV2O","eOHmCIKYhl8i":"KKnKWBhkSYEE","jPEWYaS5bGHO":"8iNWV7IaGF3A","nY1p9JZqNoa9":"hKtk6lF7fvdt","oMoWkI6fsZv5":"Ru4HFo2uEPrh","bHrOHrxa5zCb":"vsk1KEeJqZii","r8GKUSiyBLmH":"R7fFKstJMv48","B8oKT1tXb9XY":"qHoUh4aiWTWn","M658h6jblgFY":"AQmEeXQiQhqP","2Rk2glfmh7kr":"ZJkuaWV9vLoq","LquTKzqCN96F":"X2zg7I7fsPWt","UgMbLHfbCp6d":"vb6omY44osnE","yrx7vZ3SZBYD":"gkgdyteVUucE","xvGjumtHPePP":"65EskmHXwZfw","k5PWBjeLJU5Z":"ai6jNvJxDW4B","co7ET10nSKJB":"4tORQVIqpxcJ","EajrFwSTHR5y":"nr6EOKZ3kvok","bBxrtGue4g7Q":"cp9YmwoiCaoH","2gR3iPItT9hz":"KYJPdv8Yi04B","cqCNgRn3pcPK":"sdF2PzQYB3mz","ifl1Vx7AinhZ":"WkE0TvDf7TSn","MMFAddAmVtcv":"YvcCaYjcn0l7","qUAiaB9HFsGr":"cPH8C1vcpE9l","igtZ2M5Qyuwd":"xfJIRqBQB48K","9a2g7Cn8QRtt":"REMcvvjPPGK1","eLlIFSksPemg":"LmSONSFlpENY","wwrNHf7TbOTt":"dQhEblnDoKXK","VaGseleowKhO":"ukClqAMK2h4N","at1P3JII215b":"1QHiZoSXvwCo","t1YJA7XdkzTH":"GvPF8NpWl5W4","ZxHUFl73WGCx":"9Usi05UU3Fka","1bIK4x71mlSt":"jjlQizne8iuv","iEcLZEZQB7aF":"mjGNaDFyDmAV","viDYPtufhzUC":"g4QIc7r8uUeB","9HfhCPMaig66":"cE1sv3jWIHkP","qYsZABFhcHa2":"vCbC8XI6qJwX","OW1pjrZP6b39":"bxZpdEBxMX9Y","qh1KGuccssXl":"tWRSldagzhxQ","eiO8GbyoVzD6":"8Z82VtHqmJVn","tqKX44VvrQd3":"vPAOKuAL5aQ1","uP7yY43cjKQ7":"6WYs2qM8n3ps","mRoYnJxRoSqD":"PTeXoYG7Nr74","RiDBeaBskjjX":"IgRCwMSF7HPW","GhAulrOcqjxb":"5i2xcTAqO8sQ","7SM6tTbfcBtt":"ObtqJOIvuZbY","bNKEwT4HSJaj":"JxZY2xNbzu0r","jVTRLZl9LWOV":"xOwdXcPNuhss","RfWIOelXb0yg":"rwZJffJNLAHA","JWkzPRCt272D":"ShrQvII8wARO","p90KyT2Sd1Q9":"X3vGccIxCyvS","oqezJLS8RIRY":"iGhx9dHF0D1w","YdhAk4vexvcn":"LagIyOVEg8co","k7HMppRQiJD7":"LhVX5AGdKZCv","DV1sR8IubPRb":"84gsxr2A6bya","bUXakMZgmG9D":"0BZ1FFqN5mMh","sO1MlrlTpnRA":"GtWSHFY5FiOW","zjWcx7snE8CO":"DbWQYmOtvvW4","KLsm1S8FJrjn":"fMmWVMGRZHok","1UfCVJkHPJ2f":"vMb89VYeNmtg","cx840Gb1OJn6":"uJFvwODEywJ3","Lwl50R23K0gL":"y1mKqjKqrIj3","IXziqo7ANokW":"a5i9ufvdjNrB","lZKgkKF7LHQH":"LDktD2sUu2C5","c5xsvBA0PkSt":"qubVr3CRBTOf","BAnQACI8k2HJ":"WMfgc7imhZ0c","wQIph31n1I14":"8AKd5CIaonS6","VKGew8Sz9ED1":"a4C9Yo8qqg4y","2y0oyUQQrIgV":"SaRDIJczwpbB","gXnDuYKMwB46":"3mug2fE7R5f5","Fg8HgbuXhH7H":"aKGcicyulps4","RzUZXnkqHd5r":"7DiZMqL1EHuD","IZKdcKW1oPPB":"iG91SBf7mXem","ZO0hHQjKMr0i":"IWndf77t5FvK","2x4Hcya8zax7":"PIk3WRWmZDYa","z7mVilMF9AjM":"1ka3ofn6xNqH","LbZhhgDtRK14":"TTxc2xyNCqP6","5KMGUThrC2NZ":"78e48KBzMF4v","NUYNtELrf5aQ":"XR85a4kLLS8u","wCD8YDWTgvUi":"piWHAJLsvlLu","gnP5Ssk51Z0b":"Doe6M8jHbGSW","q3P8K6D2CpxV":"DTd1tiook4U9","HqGHvFHrG4XF":"jc7DQuFV1u9A","9RbYu3eYhIEm":"3CjPS0qa1GCN","ucjCSZlbKTvZ":"INsPfPLcXdYX","abxtfLycTOrP":"z7bcep3YnNC0","T8KGEvB1wQjG":"mmcSLLcsHg73","HCDIyx7hZxgp":"TWuPr5kZw8ja","Bbp7cZ41eWqw":"99TiOa9TTIaU","hl3HvkbgBLYq":"nIecNOHPNP54","pCoiozO580uq":"DHR7jLFicqCv","VZQY2u5on6RZ":"BPR2mU05haUZ","VrXJccGpDxGG":"G05VTjPqUFH1","5tJM9UhaIYTQ":"4IvVTrOF791j","T8t8DQCZYMAj":"Xxzz8ShthAt5","YbyGinmWN7Ik":"ML2W6eWQIoqE","AZjKBLdwSSuj":"iRiJGkfLg4dv","lsDQjCeuNK5q":"iOHRbl9iUa40","oRoK4gqxglnE":"C1rmCffkpteq","ByB7yX2xTxi0":"rv5JmcoRjpWp","trvLMxLLSTFS":"3mpxR1zzBhU3","JPQH1yvQZEUa":"5CUPgXB5VgV0","R0ijGuf1j91m":"IGp5k8LgyViI","VGYRFqhlIFVz":"M1IhnWgZYtwb","PVPleeKONLGs":"suMJjJ0stmbb","tNhRRZ1Oj16s":"GtMcF4NtV7v4","L1ogY7HnEu0P":"ldHonymNMy1T","zH7J0cM2BqNM":"Rw4hy7HwQSts","jdGWj2sPqEw9":"xDBiH4ejsDD6","NDeyV8OE9dYJ":"n1kScTn9fJZg","ZvUfZFc0XReo":"6O06Gw4aA3Vy","iMNgY2pPVDt4":"SZnb11GPVy8G","UKcSKBCFkavS":"y32WuKKF8rE8","ZTSTTh5fI7NM":"KQ3mkzuvOyHD","qD0vgTETU0a6":"9z2EMLbgumHM","1mL4iiw7Yv1l":"Ac6dCLZFf3v0","o5VzR7DnTghx":"rVe44dBNE34d","z4VrEkuOzsEZ":"h6EVfsvx4wje","qqWvffGSb3jG":"IZ7Et5wbKQnM","IKRBrHkc1W9x":"WMAZSX1gzmbX","z8BU2iYA2H32":"76cmKF6ktNti","tmj4IN6Qwp2W":"2nhCqXbF6af6","ckdLCXDDkkC6":"5ldhK5I0XIGH","3wE11sJMyzc1":"V2JOUPKxiaQb","swGlQVGaBfhw":"5CnnODsovYDB","mQ2ftPSERZ0C":"DJTVVP9LPWU7","eG1iwdHqri1J":"9Ki9a7JT5iE5","RIu4rXCIIwM9":"ZPmROpNbrjVG","xdgomWlej9Y4":"2eihnVC7xFxd","Hu5veTfiFkTz":"Q7DcGlaB5jP0","dHeGwYSUf7pa":"lEQNlswnxwxr","mfvNUSTfnvq4":"tJ1jp7AHReBP","At2M9TX3QWAg":"SxYNwQC2pwB4","ywPcwhKNe8oL":"avqrscAJGjc1","UFKpoSiCL5Qz":"1ObPMAN2LqVb","7g43n4jGys9C":"Gpl7CSl7TjTV","NMQAnSPhdE9M":"0e7ux6nOhnJp","4V22hQEd18Z3":"8F9RD0PuJ5X5","XQl7LuP2lm5w":"IOt4jBfahGRa","DOZfPec15wJr":"3ftBrnEdTkAC","iszUuaJDvpRY":"DB6nQWYEc3GE","xOiPgiuDbOlT":"xqcgGKwzpNcE","7OFtfwLXk1aT":"yK19ZKuVKz1Y","3SNFdzUmfnRc":"0SZd6ZU05jhY","1vox2fSZqJgm":"vOiAgnT5Gpcm","IjKlwI0qKKPs":"G65HjuVQQ2bz","Romi6rRgvYz9":"xJ3W7Jyayb81","kfDmleecrkqy":"Bh0ofN2nackE","ENy2etykuddg":"zALwrPXHKHc3","gRSKlRqS95gL":"QWs0XOmzwsAb","H2qAjvg0IlGb":"mIE4yHVjdklI","IIAQ57kTpIV0":"VkWtoeaYzu6R","sUm0RvbGfXvj":"O3Cdproom45J","Q9rKgxZAArw9":"GZZ10o3r0R76","nkzbasCVMyG6":"uBY5A6pANVeZ","kSAnKOwdaypx":"jaNI6M3ax0Tf","fpHLPuF43Lq2":"wYsuWNJzVyYo","PRFq70vjvDDN":"VaanFfU1zD6c","BiEjReAxSJLy":"FxDkK0fvWyGa","I46lVUwGvVsA":"07N2KoHMqqFV","ginDkR9xvpK0":"R9OdEPdBRUdn","mXqHDFr5tuvL":"Q7A2UhAJGtWX","5378NtbOqFQe":"TNjJmlXsYV3p","uWSxv3YxxWnT":"IplgpFI3bEWA","WgyHvMIVV7Ji":"E389N7Ej4YGu","b52BwwMlb2kk":"x1ECsNMl9hOv","A0wGfODouSr3":"HpRr8A0GsOwi","beVQkw19bGgo":"98uzNK9mTR7U","nY3cFr4Sq1AF":"Yi38tA0V2seW","FC99T7TkSnPk":"TnFeq4lYVczC","ox54qP0bZKVP":"cVL7bdwWyHLM","do0NXUu7bxbe":"AsaboPHmSh1r","Xe2XqWHo5WzX":"wIXtrxKomC8t","XpTtQhYCBlxC":"5tJ4ngLjc99H","DncmIhnOjtYl":"VAJmoNqVeKkE","klNJcHy79Bd7":"SREfTtxuF4CT","nRKgYhDYiDt6":"j5GgjPbbeXHP","RcVHfbLHPkrM":"1FSilAUYCr9r","yO1l2PsVfQuJ":"I8fkfBEgXicY","TqlaDQl5a5ip":"huHc5kIp0m4K","cGpKpvCW18HC":"sZNKcsMAi3aS","TtIVrnKoC3JL":"HXDL0wN5Gk95","xwxALAV4Xe8y":"3Yxosu7DeUuQ","2EmQZPkpUuA2":"XGW0pPBrgziR","7mGyxbWpOACB":"lCe6UyWeASJ3","5GH0AkOSKKwc":"iTZeSaggc8GP","ZRA53UsXiAJm":"lMsQ0CvNWVUm","IOdpNyuCFLxm":"lTFMshqMBUtC","WdnoZIOOB0cy":"hGsKJr8wzwar","8pANhOF5dXGb":"bIHDqMW1P2K5","ZzvxVBe3NO5I":"unsfpX9Dh6fx","DN4cGFXQXDqv":"9adJRoRebbjp","F3M1u42PgeDb":"MMy9A7Ad3IVK","ETzaBHprivKM":"hKYfgPs9OS2B","daVyY9Uzx2Zc":"W5LBwOOjnnEs","QrOvQD431CZl":"iARZoitXH07m","xikKBDWqYUI4":"WhfOur7xvEEt","7KuGNL3jEvHd":"RJWxnf9emuOZ","Wt5CjYPL1KLV":"9sDqFdjGST17","rs5MssTDMaPw":"1SPSz7dAmC4n","yQ3GcI5ADjvT":"SnDwOG16stsG","E2MrF8ZdUi99":"nZqbql8jvqqo","3UlA81KKUxCq":"iaOzvjD1wxBA","70hyn9ndAIfw":"q8CjzijP1rBR","HVS2RYcTpfk1":"DqguNyj8qMAl","hhXWYqf6HEEr":"RZVOzehWMsTD","PHGbJUNE3miJ":"E34Mb9tckJPE","S4BC9ywgIyhj":"ALs7DlE9lOdx","dSN36X6nucaq":"Vu9vzKsJa3gS","fCrkuGpj9UI9":"JklYfZd2r1aT","pqcstaSxXEQ9":"9iDKaARdiuC2","rJUlqJvSmsFz":"fpDxtneLJfSp","fQgYl7uJZyVj":"kPDiD9xVntad","WFRzHYbgGvY3":"p9ZkRDIkjmwV","hQN09P2awgTr":"28NOQIw1xgy5","OWT7ChKt1zif":"HlFeiTgnytDr","ReW6mMtMxJyR":"jTbdX4cZaas3","VelFNZKOF0Of":"1FEjjBp9rIN8","v1vVf2cejxk6":"QwvqFmpVNjzW","jVQjaY7EykQj":"G7FC5JpB58AI","Esly0LQPDxLu":"kJLHZNFyrHFu","OhedPPrH1Wge":"OL8qgVS6b3Ms","exKh79F2qqW9":"yHgP1NePCoqL","3mIXvi4P0koS":"6eqywXdZCamN","gZUiFd4rTQHl":"sOFjOdGlkwRh","0wkA8kx5UzN3":"rwC2tYW7QsWX","qWcUc0dohCt5":"xkz4vnNkmxA0","kG5LBnPOZZTM":"9cwN84mtn8Rn","DofL140xK9iz":"amArmZr40Pkn","YfiBTNm15ORo":"2ZVkFbxRdKqh","F0OBDRUSHcdO":"fZZS8iycIFwf","0h0CL5b1W5WX":"Wnq8O6oUQl87","aIsyfFt3bC2O":"Geh1sqOHr27c","M9sdFL5jhZZD":"s4TUiYEBUzTQ","RiRvEOqt9aLw":"nnxFCyLQKn1s","BHfE8wic3or3":"uPuXMYPTSXNu","I82r5OUPZdLW":"Zhiois87wV92","00kYXnWxlWu3":"1Y3MTkg1bnVh","rF7uiUZc1bbV":"OgmNHrVw0u57","2VYwyY0hEikh":"VJ4wOB4ZYpWr","ltvgkCSiN564":"S0Y4RnlfK9BZ","iM06usyGmbCp":"LSsnssgo3FCj","Ogm3BIulxA8J":"76fVASR9Ptn5","5qicMsa0RkYC":"vB8VnJi4mbqb","zmR7EojtTcFR":"p1eibJLFWeJM","CSZt9T0cXNqF":"SqdbWcQ0hBZ6","3AAv1Bq4EFwp":"8bfz28wR8ns7","7uBdOBYVbHhu":"l4xiTEfzio6c","EaNkYbkCjai1":"0TfQlY7KEh9N","1w010wUCDoxy":"Ku8LXdolumJV","ulrCDJcXmpTH":"NSpw7hHMAFdA","XOChEASWgLYz":"pdPVB19VsVgY","4FqqaHrfUS0y":"tpczGyc5oASh","Roms9eN3UHRC":"py6SKWWjurxA","Z7ksz04V4kdZ":"92LUzzmjz93B","6IGl4Qdyup9e":"UUPpffCmMigS","YWzJ3OerNh3e":"5cTdHYn6TXqf","5I3hG65BKzX7":"LIAnjcBQbm66","A8neO4HeUH6C":"SlJdJOMiwu3Y","bMueQPk8dhN2":"Qf2ekILsdHfU","W36FdC0OXpkK":"ebAI6ZTuxGcb","LNRAIMy2fQr1":"ksQuqGU1A84U","D1rrpnokqzds":"Kyk9Z4NbobnV","54z2w4TLXe91":"aa0iw0wS6z3Z","n3Mg6oFIBT8U":"6Zi0VloYhcRy","alAWNDaGGVLn":"ScnhECh8qagK","aBPbRNCyTcic":"Yl1QQkqaVFMe","BpMBFS2tbpKF":"2AThk1VnrHmh","d1CNXyRnLkrV":"xyZY1rJtNQRu","C7mbf9Sz0nij":"yfxp1WdL2Pyg","6KxmRzfwDnYk":"0XjKZldU2MiK","URUMaVXdbwcP":"hV8kpg5v20sI","LfBeE3YWa0hd":"3WTPHnZhVL0z","g2YBpHaTDJqV":"4nMXyPk4LasA","VUZsEsS8v819":"KAABrQXGq56o","XT06lub7ZieK":"c3pAnR4FxSiS","C8QWbha8pCrE":"iYLb9Nx4l4rE","03uAICailuuH":"vXRbzzMZM3nJ","bnlMBesO6Ov3":"6gxi8m0hmagJ","KA3Fqcz54DWm":"ncZDuaabvxNc","cbJtglYhAD0f":"D8o5CTvReRjA","Xcpl19l6eaVN":"2z9m7l7vnLYk","96HfcoyBTOdA":"wb738r1Pdxkh","hFGSUHNb1kc6":"OIUuhOPzqC6D","oY64CUWFyCn8":"VvOWmeAFI8JH","C7M7j9seR4q0":"c3pukzGCR9TV","ugy49YHzo4uV":"JbptFNIjIq7J","0Ur03IZpkMjz":"Z6bSNOa1dnDf","BC4dygKsmK10":"Apsj1QPXTKjZ","pGSkjKPAnh7w":"VQAKTiUMROx1","F8QdghqRRAeA":"cImg9SMXBIjK","FqGYpibXzy03":"GysedBPAepgR","41V5sDTdGBW0":"bfLbyjZ2cHdK","R9L9CpGGi8Zz":"dCKVDtJcIeRs","7i3VG7V5pnR3":"8bV9g4WkRCcI","z9XZFTlidgdY":"7FL7w90Gw8zj","3von9nbmLy1y":"PHZFR14yhEzH","evuxNisiwnkH":"OJkRnMAWX2YL","sWAOMY0iJpOD":"oXAPl3ZLDHiS","NyKw7NtkXVXi":"wVWCUcM5xUQ7","ztE30dEocfoB":"AWojKmyHxKcX","29CqTUJSbDLH":"NsXLWxK1Swmi","GW319oZrcVEY":"8Cz2VZHgIdj8","PoBRQlkxCeLT":"nR1umeTB63Vf","5J8E1E1XS3tZ":"XNoytz5ql25F","QvfYiYhkvSPn":"3Ln0ERFGe4Ss","nk41B9elUrNm":"NqovLrJdThCB","x2cAXtB0b4iy":"75bdRAIyGAy0","CNvH6OPEfFsl":"xU6FccfrJcLR","yQyZR5UHBLNW":"AboKeQAtidBp","ehoGKPCYnZ6P":"45ZO1vXu1Gbl","Ic9N7bVhFn1P":"cbPRWYS9epck","gHRgF7vHhAEG":"YZHJhHZyxTzk","EpO3F6xOIphv":"4WHT2CxATVho","tqDD3UvlfxYN":"X8givRCCv0Sk","WS4s87Clczbo":"ENp2GmHYrkFp","bHfQ0YRgLaRU":"de3WBOjkyNT7","FxUrz2YI1xjX":"S0Wlra5DHSvt","kw9Xm5XIZeQS":"HsBUVMJw974I","e8bxJIT9ZE1I":"UJxA9yxFjWXr","z5Quk7KaVu6M":"mYgjGfpYmXt8","xHX6JjPQGGcA":"vuHrVVj94Qnc","I0DxdsWtEdba":"03B81VLeaF9n","cwGJvjAIVgrL":"WxzwFfDZZROg","1ovdlHYxiiQd":"VpbPVvpkkvpb","88uQ48aI4NNX":"PmFD5AQjtoCV","vo04CwB9owLb":"E5CjYSefMogf","agRUlTAryCa7":"X4Kwq4OQalWb","06HrtjUzV1gY":"cIjc8CLqAmON","QOIStdPoIL9y":"p9GStbZ7uJxV","u4j1A4yHQvSL":"ENzJkjhkd7R1","A9kB2Ix5e4EX":"eNIqElVcKULD","ombavpwylFWM":"047p0GlWb805","23232XdNXNch":"SpAOHgzUZh11","l1qO1rCx26lW":"XxzsUS6cugI0","cE2YiWtxOxK7":"nRZUUlfPJIhx","Qatpm7QhXGf7":"Sa9m7IvVmANc","yiBFQ3JTWS9z":"ccf3EGaDjd0b","V5P5ORIPul6n":"1hm5A7OV71X5","bY2oADzQFFwa":"q2Kuo6wekO0X","LaYBMN231N4l":"eDLKw91PhXyA","rRCq8Mhc1NS0":"S2hBf5abge4A","1uu7HFv4aZhq":"TmxXuNVBCpbE","wY1XrygLcDPA":"OTigqlxcRi47","1efrVq2IEVOV":"X3EVHW70RbG6","kAt9EB66bowr":"glzuUU3Tg5XL","K1Ousq3QQpzb":"fQu8TcBKTKFY","nVIlWi0U5T07":"ncoavy22DlI5","guQEsLMGR9We":"adcugEEYHVzu","Hq1vxD2JKjYY":"nMYdCLwIN0Fm","edxZnnIRZBsa":"WPuRG1TaKQNI","nvRcXjZBw0bI":"iVLF5OYSCFT5","GhnkbcyEhnk5":"0kyT23DS4lIv","UYT4QEvP88CV":"AjnimcVgfYEf","pa0oW3XGCFXn":"Ln2A77CN6lJG","Xgc8HksJ5sIV":"YNioIntJR2YN","uh8ribWfOdHq":"744zLtzw7967","rZK2hAmgqtMB":"rHRGOeu7z0k2","qhWsbtO3zKfr":"OyFTDdLquJBK","RmygnBXdwoOM":"DqB2NCS4ki0v","SGAihh1Fd2Za":"YzaA75gFBGkL","e4NwEwdqhvT0":"EwsmceqiXDWc","rvHzSZt6ZW0r":"etV35xgTZ0GT","faCLjt5BwSb7":"eTGlkkF90uqU","qrrTiUqTmwWr":"oHoTsSJ2zwBZ","EA8e0Yyxqj7j":"aN6n4xInotwZ","UfN9JOLBvT2J":"IjbVWOwak2S4","TBGED9lMKh7i":"Zb75D13gsEyd","aU8WfZgjHPzq":"r5WMaf7VcgIU","qFpl4tsgvvRr":"FcdK3UM87Swt","ByXWcS6HkWnW":"mX2rlyIa6eVz","qin0EfJ0nNA0":"4sD7YQlVDHhY","YkVWb02isBqn":"CJtcK2hTGPY3","sNdxKoEVw4OX":"bO5fyrCDYBx3","8G8yDa8f9w36":"OVuqbqdFiFth","8MoxmBcZIJBF":"0pf6oNyhXWyo","DRe1Hw6BBXSG":"nVvj7SCnaft4","hM57kufTFl02":"mCgi90NfnDeu","NgAA3V7ZYHFB":"fdcvAcnL5zcK","KNGduKQzPCoF":"SKxLi9nrQPt3","DxxjdczXv1Sa":"VsUCvJAxpZmg","BS8Nm6Xq4h25":"ZOTtDL2GdQZr","k9f3oHkTEvA9":"p60fvDalciqf","TQ7Up3rIEo7j":"o4xlcM18DfeZ","Nget8qtRwpPs":"sgAZNJstDEf1","fAGGl9d8JvAT":"ukpyFJgzoqqK","oN0IViMWs1lT":"UmAEywcBu3r9","oYsZmqqfDbT4":"OHxMnAaQWVsO","X8bLK9qcmv4r":"H4W9thNB06jU","LUpNmYMOXQ6C":"vTK0fiTqSqHX","0jfYttcJUr2X":"OPFqQH0EnDmS","kfoc23vCHAkd":"iGoGIpsjPMKG","Bntv9KtWuCYu":"GlKeBhdFbYAV","cFGCz72waaw6":"N6m85nWp9HIs","Dd0BsAkkF2es":"RAX6IFAVs3A8","wrjMZv2wev68":"Y9SZXCie5kMU","eovKOPsNBTEu":"2lhHSjpxD5On","VsHHFZry2VBV":"1pOGJburJGUy","pb9oeHCZZGSa":"UyUv57awhaWA","3Wono3Dq4NFG":"panYeZQcDuj2","dG068tl7Cffe":"2cqs6Q426SoM","0268QcAnkRjV":"rjOOKdUISms9","jV3raGkq5s95":"I5hcTws1Bmt3","inqkBwn9NOsv":"l8FSlYouGTIG","XWedNWXgD4lY":"q9ILVbt5nLuQ","6VbIz2SYfb1C":"RZE3yLG5X0SC","klD08TY0MKbe":"8IY1Dfm2nQH4","seGXLcvVGf2n":"1t5OhSe6KiGZ","8PSiQQRHdr1Z":"2JaXSm5cOEeW","vuuKYmeN32rE":"ZBkBNRUI4D5V","f4pCCbwprmXp":"IiwN1oHLogRm","EMrpWLK998CD":"MEvqGN9c4w1m","r9i2wEuUo2zR":"9ZeApFHCxKbV","cAewKrlLxLsC":"eMa6VVvRSAWo","kdMU7fXAxDPR":"6twPMKdieFyP","2bNTbrZ5w2pu":"tQw0QFoFINM6","uJQmtvyPoKtY":"8yknkpsm9OXO","zPtfIWpPXTKT":"bnwa8ewgsGG8","0CshaqDywvmI":"CSavb8vsHiRA","P5Htu0ohjHvk":"JPAYLAoQhwB9","dMQU8r2941Jx":"hRHw1GeArzsu","5acpnawIn9x3":"Bu8kRvJukr4w","pRprRd9tXL0H":"pv9Vk5Z9sTD2","9KjEHp29W3ns":"bZsypvi1ntjo","xZf1G6xezR3E":"7e0Io1wl0vvq","ulMEc1cro2KR":"Iq1gbIySaIZf","TTh8dEUBqwol":"4W3WMSp1tRDF","E7bAEXYCmp3U":"2bbqoqgeWgL4","d4czXOa9tH39":"DoKIYWdq5ZOp","qQqYROn8xtGc":"SukOPdJyMx1D","LIHcBbHnABBt":"yPAfMreNUV1c","XlbWXMBha9xu":"anDN1xrRgKoj","fBMFJ2CI75kR":"ojE7bskpTFat","qGSY1wXGWtkt":"QxSBukWHYz3h","0NqSu6nDj7g7":"s7SVqbsxEpGG","whIYxvVdoiy7":"SE3DAKOgsgoJ","EM8nKWYVY1bT":"3HtPyirFIvUN","1fnQUu0gKM8x":"K7MqBPgpCetp","w8A2E3x3wrsI":"BWe5LvfhOQ3x","4k0ZcX7NoIgU":"phtB35gsmAUy","pro6rzCzzbyg":"8eT2pbaf6gER","wH2KjN36iLD4":"zlsYVhV5bps4","BgZvND5mqj5u":"Ri2hZS9pvByX","lecT0rptCS33":"BmQ0DIUGoYH7","5ER1Skf8MRjh":"Zd4VUK4susoK","FevvHIviieGb":"GUiTveVN6oos","ZTY7n9i6zWHv":"iyftAho4pCtS","6nasNr6Zjdt9":"X1rL1biTqDYn","RCH4gi1KnlCV":"zzGc6Iy4iOyn","bs12lJK6QDws":"EW7XWsTKTf59","fpe8Md6gJCQs":"NzjfDmR22jcY","0WoGaFkNj5bw":"7I3Fyy6T7qgS","MNwO6fI8oVYs":"fA1XkyBoLU65","ZqnRzZhumwP9":"ZThujsyIe2iw","BdqGlrEmUtcz":"e0yvP4UEk8LK","no3FWt9LYA1Z":"Wh7PfcvEIvHd","QAumbSNjqQQw":"R02UAxRyplbc","a8435L9n65ti":"2JmlujQePHaJ","QmL2SipIBYcn":"NQgBHTP6aeVh","UMPgI4lsjs4F":"bxinl2GOpjOA","QbyoVljMipKG":"eRnkJno31jqJ","LOLDfedqtWLB":"IMFtffa023Vv","FON6PkVrDUJU":"6FVKOfPOsmPY","YGX9ECDrilTK":"Ws2gEQUd9KOR","jvYguusSe2LT":"snSB7xqnbxj3","TpX3wJ9tkvsH":"aYiEqigQkENp","lwkV4WZX9jFv":"KmVT6XQZlHEb","ULilPXnpLx94":"lpQdZWiZxCfF","1MNY4yZ8jUvg":"mZZ7ZcfHysWm","JI1xN0WGPvEI":"2CL848bDn6SZ","7vdaRFLEFTcK":"hzDOrq9Nc5UR","u43BOJ7CxCxm":"wtpsOP1P6CNk","jaEGTTMnTE93":"Xpved4TofrDd","c0Yw3Hl1hIfo":"P4wNM1VHRRKp","Ext1fhruJ5e2":"nkUjgHesPT1X","8Py6kfSfGNft":"diRRz3bAM3Us","WvSoEVbxcQyL":"uyImQkTFoeJn","8farroSPv5px":"lBcIlwiOUm63","wlFIbRe3ZRuL":"xetsqQjKegY1","AZ8q2fXU2mx1":"RDrwlNlPwk5J","w0YuIW2fjfm1":"y70IkBr40bIl","Bbpl8SZbdQAC":"hIWg584U6Rlp","WFnBjwUKwO6a":"5vIV8dzZp38h","53a98CRY7Xfa":"kHa9fGDWYCgf","h8AA57VK0r7l":"lGm0mHdL2r4Y","K8bQNoUq4Sn5":"lZCkcr8nkngV","7qmhz33vkacj":"vccEtyjmX0v3","cKFuD1xgzkDG":"8CyNm4F9EElr","FQg5NlUbyMwf":"HaAVIPZe234v","bL85P4U5rJnx":"vuBY0NtL1hTd","LEvfap45zwSm":"NFKQvIEkOyk9","wwOk1iJ08IDc":"k0UM5ZyVXwdr","kfgjy3LaKP7x":"08y9SCypBkFU","CeqMvYyzvssy":"61hMBBNw6dpl","Xch7hmtaGeTn":"Ml3GfgdUqw1D","V1qbYWQfEfQB":"KlafGk3KDqHW","Ia3Qgc2PMavB":"MJR9y6zhL4lv","PYGlmmPHvp8K":"UxIs0ZwqHaIP","bnimNlppSo9H":"c0iJ6sFvczqP","ovGZPOUqpFjx":"EGjGhtiueVP9","uU8i5RFVQx4Z":"CmxkIKVdFjCC","RkaDedeyc12l":"pQq3ATWITGKK","bQ1GvEEjwgzY":"UWBzMujam3fX","WywHrKyy0wHv":"nwasxxDwbcrt","0BBp1GcunEsF":"KmDBRGCAl8Xf","xGkBVNekGoSg":"bfD3c1fSXtCx","APtG27OX7AXg":"oovnd1N1OAq5","WZcPo0qko2BY":"7mBS9oJWiFf8","VprrWvEEQpyx":"rZwFP7V0hSEv","X0FfyAw670Is":"zvzujUzwpuqp","C4bNVkDPIDcJ":"Ac8D7wpqLasi","FzGnBN2L9iyV":"dl3ZKoxXVkGX","QL6KqbyiM39F":"Goa5aOLXQaCN","AM6l3bsCaTTP":"vqSWa89v9QJJ","peGruBOihVRe":"3pGBfuur1QbZ","UMa9mW3ji5al":"bhXEP5idImzq","Uxwm75sQlKhM":"Amo0RWNJrDza","znIJ6GUuYTDD":"E0GR3kbF5wsz","apP7VhnqupxS":"WsaBBTXbW0jX","rwyUcfsEg0jy":"6Kp6MhzBcE9y","RdxBaHdI5eSY":"gooQVkvZPqqL","C5RI1LVjg3Bo":"HWjK65heDGaQ","bzyAQYGPTsXZ":"32KYb2t5ISRH","BumlFGYpmohT":"MSJ24RvZywKS","3pyiCP0MmW04":"33vfYzlhvDcT","kiVSNd6gsqNu":"NqFMbwQWG5UE","MYeOshVC77as":"xe68Ai8J9Vc4","tq15IA1ZZSVb":"P5Klt7Wlo97P","P9TRTaU1nc4o":"hl1va6vbCVKX","RMDWeSwrhs8c":"Ay5vWCoCOaiq","eQlxKMTUs9QC":"QWDqVW1VO2G2","SFjx4rMK8DAt":"b6Q4Rr6fdu6Y","1U9t4mdrEayR":"iDNw41heVQ9f","1FoYJLIdFf8P":"n6CM5oBBOvhS","Nom1wY8iP21Y":"ehQ1ZkVdzi1k","933ep6pQQAzu":"lY12nOgR2bqW","jrTmtFwwpzzS":"AZeeR1x0mTcA","OU8CEii1DyzH":"gYiCcPpV2eyX","IntfXsZrdETs":"neQROpVIEvsg","etZvOAELLZhz":"gessIaBYQJfp","WNFjtalvkTlK":"EbFjLSnqOw8p","QRa7bEqJieqj":"7KJ7azwuo8NH","Z2rg0aiTqaHN":"pVL8cqgNGN7x","6cffZQVZEpUO":"n6eYXrHP2Q2Y","R7fMu55fx4wV":"juCSxnrcTTJK","nZhMqZy4l613":"qyAKzuS2DhAL","u7dXvCG91sc4":"KkrUOMm2UnGI","hgExMRJIUuoW":"5uMjZCqiU1ag","ZPpo46X39fvt":"IybAAv9ESc6q","MdffaJR0K1LO":"U9OIA9m5Vh21","z6NHHYqxBpe2":"pc80ID0NZhac","dyAmUAhspezs":"wlnvLlZYbhd8","xDUbuxumzw8m":"Q3Undnkse4BN","JXss3SVL2orT":"1SzrGFMrr61o","1A7pdkKgU9Ip":"uT6KSgMPcEvJ","oOS60uG80Ovf":"WuXf1d1ck1rF","zKc6v38o5vVH":"YwMIA6tx0Fcn","VoGog5zhf6Hk":"w6QnrHki1AjF","113SAdtAQpOV":"8G6yK40St5Fe","qlZa5aOPZukk":"uNKUPZeDuxSy","TZLfcHyU9Htj":"MQ3zjGwxOs2b","3VUobNACqkBI":"ARRlgOVALXOj","u0nR3jFIdJGH":"nPo2Z2mcKbsJ","Au3p22VZjW33":"NkvO6yNvpXpn","fohlqAInmiLc":"ViZFnCcdOy3Z","ke7DvtQoiy9l":"GW2uZNGZQNZa","pV8S0LPUN0UA":"O1OdCgkM2eBz","L8yzvxiWinlD":"kphdcWte6Eho","a2uHIQg2th8S":"3QpXPa4mpjHm","YEfnENAv5ZFk":"EE0KrqpzVMlc","UwmBJlT0jqWV":"eWOmZBToA345","rs4akIjLiS9M":"eyQcBpFgeTSg","LoNd6sF6ttMm":"ohbeptwaKVq5","gvoUJbsJ48y2":"tTJqSktzCHto","2GB8wfhOZDWR":"WyjsPiQZXBSl","I6M67XVSP8J3":"aTxOyr6tGDLo","brCAzqxxL8Fu":"S8LKa0ragEWG","gQIlnYpWLqOr":"r0yoUcsTZyjU","03LZqCaenHmH":"OPd6Juvr77JT","Qsr5iA2Z6REz":"LMN9qJ7h3jvR","5tgKgyv0AWGu":"fh6FkXUD19Hx","uhH4lfw1mx6s":"t7FpQtCFDgvi","g62tXqnfGnK0":"21mmLIiyaiit","hyRAU4bGFhzX":"46ToLWayAX9g","hwrx1BaYrjS2":"LH25fDDntH7E","jXPcqugG6oDv":"1RAmyDSSdEOc","qz3Tj8cVD0Yk":"KkSa8NMqLToF","Pj2gHZTK3Mx4":"0V1TxPyEo79H","IQAw4CQCYsrI":"skrXqKzCGDA4","2iJXXPXc8nK2":"IvKxasTPxAIM","w9qUwevbZFnU":"Paa0C2iAxJai","T2UZw2rAsEnK":"CnbdXVOVLH9x","q4vaEchoR80d":"topBaENm3Rgb","N7H5305BMCmj":"1xsBxpBUNwz7","GGlUNi7PzEJr":"dEGmmF4I8ZOd","n6NtOGdCAiHY":"IaqszPR37ZEp","1R16di8VMFQJ":"zcPzy8fgYyXh","0RbL8OXVki9S":"luVZKbOObWtB","Y85M8nNzj6RK":"MG4gZGPiWgQD","ihHXilnM3MGY":"piht1Ye4BUUg","MscIDs2ExOXH":"a7gjkutCbWYq","H1mTu7PnHlur":"HYHfIaQsmiCZ","SmmwJIzrJWlS":"0UPifaA4Vr4J","PY83Iy08M4hb":"CZQ4sp1y06UJ","AVHPvuJ3RXHD":"8arl3gGyDzs0","OXtf3XB7UZ2O":"5wbRImYpg9Ze","mnnD7hA7DA7S":"VFHCa1JU4vcx","8kgfFMbSapLg":"eXrytA1cvB26","mG0awUfdVb5d":"GjlxRQUg6FsO","1jXHChcOB6gt":"UR86uU7s7LAF","wMaovhcjNNuZ":"2knRlrlQjYvO","Q4c2Feibviv1":"SlgpnefJrkvX","AhkFylGy955w":"1CY3BIL0pQQt","UTZFnKzlIOIN":"h13HTl1zCgyE","Y4SSWkw7uZVA":"x72b7Xo5XWlA","xDymDvngm7tA":"K8Z32KrSE8lU","uCCCumzAIyZl":"Zwxp34cRB8oA","KiWaVku9tii0":"jJ062h9HEM8i","bAR7GnfdsVUc":"usX3DVdIso5G","SN0ocS9FybXT":"pIHOKPYzT75o","9GSSrsBUycj2":"i5OXpVFpWUtG","2FSrGFSzM56O":"IeBO3TB8zLop","nnYJYCSIqGSD":"omQJm4J30n5Q","jwlVAK9m7jxg":"FOov6xNNtvpk","2AP08NrgBlTR":"UOoba1uw5BPu","4R6XG2AYnzj8":"9GWVbuApJYHc","WH27Mxlc6Hht":"IT2OExfmhEHm","GdpYVLlX8S8p":"RVRYunzRsw8w","rk4CaQDcaRKF":"vLqTsYzdJf62","5zfd813228gp":"tyf69xO0d630","IFO60S8hPxnY":"Vcb27LbwgbX1","yUBk8UUIaT15":"KyhWfCfuUFwx","LTfdPzAWY2Nz":"7aGAh3AwdHWK","mA33Hfa8tRVz":"54qf9qqNwPqD","cb0qKGKEH9oo":"h5KcWxNylfiJ","ohAeqjsJCd9j":"bWOFRlThTfGK","HxVF9EhbqtK8":"6ynD7BSCl8Dg","MZYQwNLLv0OM":"PIpad3XADnjZ","wbB05i2dFAh5":"R6MZHQ9h43hy","rGxRvvq0jFw4":"CCj2fFuyy6YU","3coEKqknw3Dn":"sBBjuSswyx3d","1pv9CJxvz7LZ":"jwu5TF164TK6","9XSWgncHBvm6":"w86jjysbKAvm","JOnz6z2vvddJ":"7iEBw52KVwEa","K1qfqh6N01xp":"hsFaxtXh7fkT","kPSzM2om7Qpb":"25VW8mdWQUl3","05eSM3fuqpa6":"cvVGaXNTusGN","15lCx88WhXaI":"DB5zTD6eSaKp","OvwqTuU7ZHT2":"733XQACNMVFS","YT8Up0BJ483z":"zBSKg95Y09o4","PH43OnnMfYIc":"QqtopvVZglxF","KcW1GRI11Atf":"i7qYRS7WiGy0","gMXjw8SHhRgQ":"TVNiGwKTSUrc","a0PxPKjQkXfD":"p0cGbN4LjM6c","xXgdPkWlNhcG":"T4me6ORuuoZD","ruSssK1jrARR":"EqfzUsvwQmTz","mWg3jFGgixnD":"qJ5UWGVShzvR","uqQ6M7vEEV8j":"PzelO66kdLlR","KH7yEqpwVgF7":"CqcjCyhq5xut","TeZfP7LzaDR3":"iuVXqiCOLKvt","5EtKQdwDS9pq":"2flg2lYs5j6L","9upZw5GcPopd":"okCTolPwRgsC","X0NrgrKjzY9R":"XCcWYiF42x7j","TDdKxDDndpRN":"T5GvCbCjEHNq","CEmmWDyHJn4D":"PZWDUr32Gfbp","uW5eOD00Tf82":"ADm93KkRsHt9","PNFge0WqdvdD":"0wsB69xlhbMM","16FSAuK4IVvZ":"yvk9RFOqE4y0","2AUCVquu4WEE":"mmFPhjhXOdR5","slIpF9aAtdBG":"OU3XbzvLX7g9","UsHovfwa4hsc":"ocHZdxzE0X6B","Leo53Q0by8oK":"UX5Vhku4lRyJ","VFRVbpTVfEBr":"dpgo9vlJdvLh","hDpFAUa4h1uG":"CPcXEaMUMStY","goHpgLikx7uS":"L9wCAlkZfMJ7","haveaa9KC6Je":"r8kmYSGTdyjr","VfKkCOaecBDg":"opm2OLZLVaeD","ZIkwTLFcCvvy":"GPvT1OVxUace","nZWg7BOv3I0H":"kgi2HaNJz0JG","Q1RdlN8Q2kZj":"e9QLnIjL34dE","rrcPxKmgtpwL":"mWiUGwUuHWfK","gvvGd6UDlGUk":"afOiczKThGsK","4CPVP8LHGW2I":"OuwRPoz7zFoP","JNqPEYZxcMSo":"kd41VTtrO320","g0AtqcKHnHh5":"DgUIjOzObbxP","j6AAjtUyyiyF":"dY4R8gsSNMih","aGHrBrv6EuAH":"1wSYAqjV7yLC","RTZrbOFjoaz5":"y1xzIBzdxwM2","RTg0BYEjNTIv":"6MOER2375HDL","7aUWnb8HCy3S":"KBm43AWNzNgr","VhPmYtgLlOhz":"Y3xfsH89fZje","lqIukKnpwZc0":"ocRJ2PHwx5ZF","EsTOjdeIGBQL":"jrxoBwOmbkqR","MULek6QvTUzI":"JrgfiK0rzfYG","a7lrJq0LVXMz":"OcIRAzVPe7Af","tmPgA5QyNoEN":"5PmBm8i3OA9F","eZhsQRZoPCR4":"0oEFqnS48wH1","iKATove2ZHfV":"56lyCcdh5FVz","LICfyCoBtmlv":"V4ybPDGWyY2B","VxzKlmRkM1YH":"om5Ox3AB3Aue","NGuWnQSvym0i":"FpKH1iY6tvN7","U17cPJ0ypU3h":"BHVEfUlL8Pyc","wT3XJ0OmHYRw":"nqeCGtgV4nks","7jNPL9eye2iA":"dFEW4RPA2XJG","k2sfBZhcUbQA":"RzArJA3OlFlI","5Rrvd0R8dCX0":"O3D1hEMloEZ6","5bysfmgmsfrb":"40sQvOHHpq50","FcYxD8kJ673R":"eY81GhvswY6F","r10vTsGXwcuj":"lbl25hdbp2C8","jDsvgo8gXMZp":"FhcofTBt5TLY","lJxro7IO4Sbm":"OSUMLYW8RWWR","TQYOAjzbNkxS":"oKzB7nZY0vXK","LHztpe2PQHtG":"M393jE3RvA4X","6YZaWX3SNf4d":"VK0xjYjjyV61","yeKIPeSJ6CYt":"M7UZFnBU2WLK","s1YthvpTejmB":"8Yu5WvLTM6Qh","EA4Mxsx7THJu":"1C9tfdVFNNgt","scAecHbukZYb":"CfY7CT8FK8s1","QtEOI2akq0w0":"6sj0SpJzvHxT","3CBIWJr9NZxW":"8p7qRWqMVm1E","8APsFHjlJRyb":"cG3oRkF5zlL1","DXaoRzujNmyk":"iiQmvLy11e78","2uChBLIv3Q9Z":"6X0VKuCpnGiC","Su9YCYp8nd4K":"YVJp53o69VSq","ORbuyQ0Jo1vF":"f8saMjyZBxAJ","Jz8bys4sSMHy":"5OJR5HgRZpD9","pB6JdqRWfd45":"vnACy9wOcnH4","ri8wM1uOioue":"weusO2DVQUiz","VOPVlthNbHGd":"1KaEyG7dJFQv","V0sP477KQuwA":"7yimGhaVmxZR","z139fHUa0gHJ":"HduD6EbNtW39","8nkCOAXHLM3T":"lRPZDy0Yb0S3","WFHO5bspTMSi":"buO6aLJubkju","EbToCboiuJQB":"SC1fCPAesClN","9FkrL0zq7WCp":"HPlw08LaXx4L","f2EFm0DyLfzy":"kOKlc3N6N6J4","RWKWSxb3fs5a":"qit0pZQrLg8F","nCzlSGjAlolw":"zWn9v49PMkb6","ctFwD0bGTCi2":"CDVDp4grDjW2","kex10EILSa6b":"mn1Ru9QdSFOx","5FAyCMvolB46":"QrLbaGQRXBmS","LobSWAzVzb6p":"gQG5sAIuGuIf","5LwHclV7rIHh":"LsItMp4TPjUi","wzPGYuMS0KYM":"k0IvWVLvcq3q","MRxwBUzHm6xs":"NQwsA2gdjmEf","Wv6fsZa5rHFd":"ef7fa6Sbg9EJ","hUhip5JOEQRx":"zFKn9Y4yCJyS","Hl7keaBjYd6n":"u0PNeWovic4Q","2cYYAKkNE81j":"SX0QLS9tWsqH","LPfalOJf8zAa":"XD5UIS59uLoV","wEH1AXt9UQ4g":"YkoYzh34gdQO","sxj5AoNWnCV8":"O7bw69bbEm6j","lyBiYoKqKcx7":"iyIkpJlszFh9","2qVSDESPmBDm":"oDXtnpJqE4Oj","uKkOu7Sm2Prn":"b2iy6yKtqkTg","rOH0ISobNgnw":"Z35n7SbFk3De","6a9TdYqyvlm8":"KVnqg4u9jROE","HkLp9yoL30it":"Du4BdkoMX0Jn","xtdfNwloHuUI":"BYhCpHk4BpjP","knSrxO0VN4IH":"QOgK4yW6m1ZS","fit9TkZo1SFS":"S7l0LiAhRrqp","M3gvMl6Fblp8":"ofrXK4wtYQSE","VRLnG5tvYPFT":"QC0woxvMfkrb","ThVYWtnFvgQF":"rV8EsOzlyfdv","Vaqs9Kx8ba8m":"IOytXtX6K9M9","pfI8qOaKG8su":"2R0KJB0rL5Wy","sFWk0XkvwEvt":"rFmZgxFg8AZW","UWl1lM3adcCV":"wGfBkvguZrgP","7vgABLgzm6Y9":"vNAcZpMd7w0P","ZO13SJU70jHp":"PkBAlPBFPUGt","IbzUhkNy4mkF":"M113uj5muuaR","ERAZ845aO15q":"kM3KyAjXvRP6","mV5qMLCYn7AV":"lSksqDC3aVrT","il6PI9KtiSNG":"QleFhanF2BmY","Yi8PzUCoIB4l":"Pl35uEDhCCXz","Fp9dw0zOktrk":"SQzVz91jYuMT","w6ejfFkla3ws":"v1W4wmqRdTgC","6XhZvlUVPZgM":"rqAjmM0A7VJl","4ZHz8Ve09y5S":"nRxRi2izp1P2","YfgRQ7tUlbVZ":"HIYWVqvNmvxv","AEciIpnrY4zi":"UqxPjFZAuoKy","dAqPpzalGoPq":"fObY2JugKgr1","uVSXFiDo5EZt":"FDkTUR1SMacl","CWObCvw5gMqp":"MTwVxBpfQrXB","eHcK8HdhJMWq":"vn7pDOwvznXd","rAerENCHIcPQ":"6edU1yc9IWQY","yPd47JNbDAVl":"WycpCmJb0EVq","En7N3y81Pw1Y":"iNjIlE3yCQZr","K4eCQQT0l9W6":"NrJLsSAJjR7n","T4KHtF9xuUc1":"MjS5Sc9fZStt","jFydoUhAhZzP":"rtaeO6QPPIiN","YxNfk6lrbnm5":"RZxCPYZyIF5w","yFEJBG9FEnS7":"PrnkHB1CbNH2","elrwoGxNc3f9":"nPWhQNSKRrH1","sAVF9fs2tzc8":"pRybxcaWB1JS","9caDJW1wm3l9":"7WUU6Zg8R7Co","Pp9urjau6AUr":"6nJqyMyctGzv","QVxYy8hb1QP6":"vM96aA0MCU5G","jEEdiGnu9Zc0":"YU2Hhpz56JtJ","0MOlVNSJ31nm":"k4su7wvk8Zyq","CXPZBbw8Re3r":"kQHnOsMcdDTp","s8HpdxNcnVPa":"fwoM6ekfEAIC","zDGXywa1IgrJ":"RMieJF69SzEC","cGmaaPcKUOU2":"XRlMTBQTee8c","BiQI1dmODMAC":"GiSMLiuGdbwl","nDemiWwxrD9F":"nTugsjJVjROe","HKCCG2zxMJQn":"VQDavUnCIlUB","eudW12jsZoMk":"KX1FkW5p0pHb","5nZ6ejlGCgmi":"iF5cjZLguen3","QQyuXUFvIu7U":"o28mhMvVQckf","ojQZQpLIbh5J":"9b6OUvsxtmOM","3xL9YBziKw5X":"gfklkmtaShZv","whefrvNEjQAH":"pDuPNiN0qiuv","h0ArLInLV7h4":"FL24PjYoLFqT","oorZf5jCMFQq":"0SCBKFz6ZaBg","QUtEVOoyN3Hq":"mfn60Q61Uicn","5O6SA9I2XcZh":"SEwU7FNgSrTo","cEnVfJ8Jp3z1":"lLdQ5igXRq7Z","r1mE5EXoh609":"FWI8ERSY0Ss9","13ePcrQRssEc":"DsAziCeA1tEm","QP2VBPs3azZQ":"bWhAOIsDOlZv","dSMgtcMVj1GW":"TtIHDZhRQWLh","wlfloTdL9A6w":"qLVhdzsxfrQ1","ycjIUb6dVM5Z":"KIb0UW5bPjMT","I0Hg1p8KhEaP":"DFPA8Wuk1F7i","f56bGMecprB3":"j519Df2rv24i","iceyXSwl7Zbf":"nCQjhsYujuzA","T0CGOspXhF3N":"RUSWUKcsCweL","mjXsiyqpvLUp":"9NoFLnrATuCo","4il6LhfU7GWt":"Cqw6metLKPtX","MHiNiwnKYhr6":"o394NxPk2yAq","bHaR753D72We":"RhjG8WJRJCps","cLxDR2UhU8yr":"cVMm5BiwGkld","7tx7eWmuFV2r":"pgt1D2ltOjJe","ZC0Y8VMheBtY":"F1uAymuJaove","y7Kq64BBThCr":"4FmI2pqNaC75","JuxrHi6LXbaz":"lGe4b902ZZKz","qm2Ji6tzbYeG":"OyhmAznbwjT0","InVMXIzybeoV":"gsL521FidlwK","BD5kJAZy9QDP":"5dor45LpQg4C","YcQDV0CMNP0d":"UZuYyZECtHV5","Pm4C4Km7cNhm":"H4pjoUil97vD","jziAGk7jLxrr":"rDGWSbfN9aSL","nLF5TCN9gWHx":"zjmYPfFN5ho2","aNtVPFpOjbVv":"LfhJcUdXRco6","0fcf0x1sorCd":"dMns3dF1gfHS","l05NCVnUVTvY":"82wq3GAjvEK0","95fY6hXn8B8d":"WeKtrExnN08A","rWeZdbcv4CPz":"caMj0vs88iOC","6xQRD8lXrOcn":"VPlM3Gw3NyFZ","ujVWKddOLIQx":"DZUcT2YZzHkP","jL7syZzqeHO0":"Z8KY7RXb0wLA","NFIlpQacaNRl":"Q7fLAwueANlM","jcumkGdasHFH":"mRBS9Ku87yjH","ADWV3EWFDvg9":"PgTMPWUlSNT6","CICSXGgirVK7":"VBzZPvYSovwu","LGiDOKDytrzq":"R51IUULNYcpO","llcADYS41EIq":"HJeEFyF6ptNk","gyqZtsRbo0BY":"G1Qjab850bgU","MC2OvmmLFgCN":"LN65nfWMWqfe","VcQbE8USyOf4":"Wzb0awPyWFFx","pJVMkWgOGCrK":"gesnP8wJcEe9","unQs94vSnEFm":"NgixbOQNshQ4","2FXb8M6D752G":"v6MM7FNZxzLG","BQi8yRCv9zc0":"863d46l4v3Qu","g18z4suaZtT1":"8vHMc8UUu88x","re9NFuo2Az73":"Rh75gGpYZVm9","mggrUwHp3A0I":"QQl8wBTACDC4","Q8XTAggGFOgF":"I4ZKRXJldxjD","s0VpMi1md961":"Hdgo0MMaMMqi","NY1fdH6GQMcz":"mtBz9YfrVOET","vfi4WAch61Iv":"GkRC8R9qGihi","3p6tpyg0DuDa":"L3PPAxnnOlSu","BI2KqJ5WIwfc":"aavTOkHKHQli","ZDJuCxDTq5ht":"t8APg2GxUnFp","t7iCYIvWgwHr":"R1mq63V2UgxS","sbKic40s6XpU":"BZcvNWx4c2Mo","cOs5UQoY0SJb":"Y4OTcliKgrko","Xi5Wret7inOS":"oLxv0G4U0VKT","ORu0DsaE11Xk":"I2JBMYs8QA1m","y7tH2sNSGShT":"X9TY19jt1QAP","sqlYP7PTfM3o":"bJ6O0ZaLtKtH","5qw2u33GkHXB":"2kjqP5c90YHm","wz1ZTSxYXd4q":"kIMwIM1wm94Z","AQxaGVlTDD9A":"KFtWw4PRSLX5","TieYo6A5gwDj":"0Er2bHkZetDt","2cCQ04fLK8qg":"vBnD3lcZaSZI","NnRrLdiOM1mR":"4dIVUW6hn9vI","9LqxGreF0wVU":"V0ApZpC7aGXc","zAOkxRZLYrur":"HBrQUXP0znzc","h0GDlApbO2wg":"j6wKEWANPr7Z","1qxZbIb1XIzU":"nyGfTFc1q6iZ","BSnLQsofVBeW":"CwuR3AtbaUDd","lla7Ot8dQlhP":"Cn2UNPx9iCWj","GMz7kyaHSOuM":"aloYlivHn97E","NbDWOkV61sHo":"biLKRhys3mub","S2kAB6afPPDe":"RoYMjLT91jVQ","ggM8AcXFITYB":"oj8ce79adkg0","q0T9pCvVUIDV":"vpJCv12GZTW0","p1tOBvGQkorx":"zGXt5YRv40pr","ij4TOwZbfewm":"ng5gOMLkQTWB","kzqISUIBi7cJ":"FV46AmTgnU0Y","WB3C42eipi2S":"5GCdR7dtL5HI","mL0OFWcldvJU":"5JWNbNBTcDdx","nFaI4X7qqEHV":"CGnLLp1eaI3x","ByWlCqgqtbuo":"uYZW7S85Oxb0","FVXPjITWKYS2":"hNjRYbsrVBiz","60QPnq2T7g7H":"2zTCtJrU3Ckg","K2J3mcazvxgU":"8qVjhZO0eUUP","KtWp0yeu58No":"fjOho5ykxhpr","RUvD16xsdv9f":"l8vTH6DGKsBP","8ZOBWZhbmvOy":"828ZDRnc74Z2","yuSKQqVQaMeK":"HvEshu6Fedcm","ybxxrrpXtP4F":"G37aodIqr51B","jsddJtT7Ttog":"TzJFQwcqZa9C","LPOqyelOqgra":"lHkQbmFH6NZ3","Vbqb6pRsNIqG":"BDNLUurONCDy","tKAhdNNyL1Mw":"H2fPrpcpLiF7","n9wj6ZOyYDTR":"3BUvugQ9bxkJ","B6RBIkWFbqux":"pQo9C28YAh2j","Q1Y3shJw1Wci":"nYjRT2d8F8Rz","3201ywNIlTYv":"whbEap5HDdMO","80xN0yUvpNVZ":"ytHPe4oMlKqV","zIj46IQCW7oR":"PWImoTAOnlE2","gDYsP1ALGrOs":"7wDh7MVJFYrG","PLwcscRdciiT":"4tOt3nN5jBLO","zLtqobWErRfo":"7Dzg9F3P9HL6","PweafROjtHK0":"Y5FAfUDIjJo7","cwW4hi8RXfvC":"8suLTE7hM7OM","GQViTZCdbAHk":"uS9njBi66Jbj","Jvpi1uox3gtm":"nyx4RhuYrgUa","ygUNZrmFMprz":"B0FtrdebFMu6","8w8UvAgknt3K":"LnSOT0MGFSVw","uSxUcxCaOuVh":"MvwXuNk4WDt7","vCAYGXhcjSK9":"hx0zl5vr6DlA","WIP6zq9eMJkC":"LbCoNifcO4nG","AZX4QwblTfRq":"IuQPgWBR1uvL","1DdudJnA2MVo":"CqZO4sA3Dg3V","mILPjL5umZgM":"tnI6ZJc2MeMa","j6yaDZ59pze3":"lmqTlqsrk51c","AWNejm04fSts":"toVK31yZwF5I","pGQcyPoT9rxM":"Knwg1jTvOY9b","RIE1ciHUOCaB":"VXLjhb4DG1tM","E9qR6WNaWxVS":"IoOlfrqUbF3l","AUyGsOTtyH0L":"LzdqeNYEcMOF","hrYPtVJZG13N":"qI6rjLYPVJRs","EMGuGIcmOYPr":"F3mjRwEe7cRW","vIKlnlSj9UlL":"w1el4ljPI1Dy","Z1rRL13BoqqR":"Ubf1k9ohh8Cd","bMa0nADQ9h1S":"LvFW6hp3CJqu","NSYUV7TmmeJF":"Zm8EvojgkXQp","zyIluQZ21JOG":"lJgoidLPPal3","oldgAWOVko64":"abTlvFv2lF98","hyjlUZoKKSeV":"Gm4DnSHaIsKr","BilSt6Cys4a6":"msWOIKYTm8gQ","elshjlaG8TB7":"wHVBPnrfjjCu","FUGH2WoZeUix":"ErnqVJ9jVTux","xYvJrXm1gF8X":"HCoRefXtKuVr","283RgGzvPZUP":"iEXsJIrJ0qoJ","qp58ZLKcJqum":"e5t35c0M4SIN","5UibRCqPYlMh":"jCOkzopNiHMi","voNGaxllXwHN":"YdJPxpdox2mb","vnT5LiPWbamp":"JGRDoYTLaJIC","KRt2DXaLkORJ":"uL6EzGNsGbhn","fCmI3ZoiBkbK":"DQ4StGqjfpuf","WTxkmpELaCJd":"a93MdpJGdY4T","KvG42UMAMXiQ":"xdyVFft5Q0FR","9bA6JQmznRcR":"ZO4XTOUE5m7k","jcnGKowimOTi":"MwVnE4iSRUpk","XWqZojLuTdMG":"YcBNMwo8TwOZ","ikJ1xP3mDEUW":"K1TqkjU9m7E9","8vYrUoWWVyTD":"zEi5QMM53bav","pHPAWPLNbLtz":"t2nQXMVXckyM","C5SBD7fkc2ez":"kl2xNv5TskWU","A2QUBdBLtNtK":"lQqnqPuU52Q4","2SaK6jCVHCMK":"60eALeVt8ltI","DLAgcsZr2qLU":"1DNoaMNogF7B","7FACQFsEOCD5":"Iq8wxAFyVc9L","YygboCOnCAw1":"UttvS7R6Do7x","aBcvlVnydgKm":"JQ3vWSKAdvgY","ThCB9Fb9hdaV":"QecBGp8YdhvR","tEFEZWRjMIcJ":"GeoOhgvVDUm3","Bi0He9K3D3SY":"pUdpdjbmjbzK","T64jHMfCQJ0i":"9DQaovkPU2ZR","JdhPouz9VDTs":"RGNYc9jcxwzP","KqJimDbD9GMo":"PUyQOAJSTNHZ","zt6vTYJ7rq6f":"Y0dFXE3wJBmS","VOfmMCvwmSUl":"r0X61cHz8nOQ","38ktMXSJ5mzI":"571AiKbn34bd","SlOxbwVv6gur":"O144PJH1Hgf5","bp1vOGr1V4Yg":"6Oy1GGQn1YqE","mWTYlxK2o8ks":"B1uldkhcLc1O","cVVfQd4csC2c":"m1zxUH3c439l","4O6CuBqBqxVw":"V6b0lA4AjWHk","wymL8HhDPC1g":"NfBdCuriafLy","yJWE59PNKiZ4":"Sguze0KkoRCx","gNzSSR5ScMxp":"iVBtmsZxg7G4","uP8NtOSRfuwy":"4tHjvkpLsUQO","ThRcXq694nsS":"TU91S2Zi4w3X","a0j97s3aYEkF":"pvaT6vcvTud6","Utdo3g0dMKTv":"2T7gN4eQTCdm","uMEhuTeEl1ux":"R9dqQG0J8a04","vn08LqqFDGUj":"CzlsEM8Gft4y","xPIrGkSgX7QF":"G1RSouyCfnr6","qo4S6hvwPE31":"VPTJkTSweQ01","VYwRXllXUMyX":"jyQoW7vqhzUi","t9Hglcx8zSu6":"T6tKtpGCzJEk","sN3geK0JIpxz":"X6XFVJbXFht5","0b7HbyiDbFGl":"9GlTPjpncSYp","1HyTptQqhYFM":"Mm4FCQ7pwAYu","V5iTd1IsSCPa":"J3GD15FB1bGV","40Rka4zTxKr9":"3Efz0jBZyoJ2","hzK5Qw5zHnFB":"3QUt7kjGH3W0","qZeQEKsaxyqP":"J7CAl8D3pUhu","13Uk8adHMZH4":"b0CfBjzxtlN6","alWweYfhQcin":"UDIcZBAZDQMB","RzEo3JN3qqn3":"biXFR3DsQCc9","ZIggaNptAfvP":"6fZXUHKMjCSX","RiRmPuNzbq31":"piNK5a1M7E0c","coOQZVSbJpOA":"TUKZGRjJfMka","hMuRH9gDki9H":"MZzp77oDFshg","ctGP3iJxVtJO":"qMUjtXzSKNIf","yaqpOa3VvVWE":"y3Kh6BSMmfXm","9ht68n2RsVMf":"lsgG4O1yniXn","e1kG9BTsfCpd":"eN6ooeT0TOXV","fiUeVdtoPCS6":"nrfucPJIlDhM","og4TgMlMRHgK":"1jPBsTm97RMj","CAyupiX0JCOC":"pdOZaSfg9NrG","JFQabOjMOq6H":"8GisGyvQIbd3","ACulnLjYsDdy":"Zx1dZz9CoaSG","TzrJd9dh77xW":"DQ0Bpl1dQbM3","C7jfM7xSQC7i":"PIZ92UjEcoPy","uEdPRx2xLDl2":"NU9Ho3URYmq4","7E45HOfeUlQF":"8vX75C5SfTzX","rubrUgrn41Fe":"eloHfzefWvL5","R4QmS9fhZdBX":"mmAMa5XMIPC3","JOmwpMs6Mz94":"WFjYCkv56HkQ","UrFb5H277wZU":"cqKDDptqtrjK","u2QnM8AWBDqe":"0AyNuWOJm7rK","026vxPZTFlnY":"RnezQvWCar8K","GPPYvvowEwFg":"oB7BATr2fHgY","GDNdDDCubYj5":"bvYRzZlb4YNc","PREkUg9a5ffp":"AP8fSn9oAAd2","LwmITLvsmPPh":"ItXb6wwRzXgs","q5CFlvqorwrR":"jKcyPJbPjmvN","4ygJBvDJjwzm":"sQU7BKEEBEzj","YO3rZyxOUCQw":"waqucVp3zmJc","WY3NB59pRDYk":"ghLNbzSIbIV4","iWIxr7r0hJXo":"ZnatK1y6GKVH","9AFabU6wbrvW":"ruRq0wK1XZxf","mQ3is26sQV3E":"j5gcGtpvTAl9","LauLvG5jyuiW":"XLdWXfEHs8TV","KXcuCBzn9TuW":"mU6snRHNT1Se","vGoVJNrdZUIA":"33Xy1JESLb8Q","hBeNlMV7VQnk":"vnWD8aZ8GdDX","7b0TW5ROEwAi":"JUyQLbrGysZC","xz4GEtYehV61":"0V4Z1wxmgKuY","MFRWLrpESTid":"gf7ivYpAcxcO","7wl84PMIC1U7":"ku69lUb544Na","6hmAZfnqlYhO":"TERvbM0A5Fow","Q33kkST2f13b":"5naebFFObJJv","266j7UViXGqL":"ewCWbyMXEOKZ","yTn7I4q0XRzi":"DQ9zZXDAu4Pb","gY1dGY2U75kr":"x2BqBtXLTMy7","Tm2QU7uqvs3r":"X5YlUsuwlRcK","2rR0PDwJJ1dP":"JXhzmxlsfGDp","AmU5wT0FOeiU":"KiwDUYANtklB","K0KMwJVEU24F":"cHrCu4y63iSC","HiUXJIQUlTXz":"Fc57NmUh4I09","SXm4XJeMVty1":"e04Y07IdiVnW","mk775cuT8KX0":"0mgts4PpcUO8","GqXDWPFQEEyb":"UTRx9xRNC0tG","GztkDJ4bX93L":"J0HT7qMcjl1k","lcEdn33JmzZe":"0qFNKwC6QfMZ","zeinQbcpa08k":"zEummzwz2tEp","RrJYHl6Bhh78":"Tieeg1MgPlTI","kdUOZ82CyRjr":"miITC6N5jWqe","saRU0fbgZiKs":"A67jIGyxyEh2","lQYC8VGLJR9F":"10DkDpmPJzMS","pWYkbnTIrwxE":"BeL9Mzd0TCpI","KgkM11rK0RBT":"KZVeDcgAv9Ab","OT6RH72BDb3U":"tP2JA9BRAOPn","gmeS1Tx1XXAd":"aoGu3oz9Gg29","UckV2VPYXs4D":"JtaNuvgXpkjs","N63OsSFaOrKP":"pvmM0u85zYwq","T2OcVLfxdNcn":"IG6G6qfjh8ZU","rCAkuSCZAzeN":"Uqb7XfZxkcNP","tieuZrCFSYn1":"fv8KKHQ6thrd","5HoPQ5ZZr72j":"6S9M8OekcG56","K7DFxGUDDeY2":"HBY0GIoub24n","9TKceYfwiO1b":"6ROO2wzZteLC","3mQ9ZpbwXNO2":"VqMXSV4GMpDH","jirQTekr13ek":"otjAVyXSayhN","P7hrXM0xpgcT":"4wNsUZI36Id8","jVDhnTwC7plm":"fEtyHKtC6cjO","v9Jvr4IyNdno":"72k53gWMoVRd","4n5Pw0QyOyYW":"OLnTvLAJiAGJ","FzWrB1ll5MFw":"mjX30mQxkTRQ","kJk3BCVb8bmF":"UGuDXFPRIL1f","vfGA8mad31m1":"cozdlskgoYZO","RY37ZKrjlyQu":"YNF9kqLyicDc","EDF6h53FZWwd":"ObwNLtnNFbHt","XfKfXw9ccqcY":"SYMr5MS12pbP","0eDJJewkMdth":"SKJhI13aQU9S","S2LpEXYOj7Qy":"4O6vdifn9C3x","uKoWr5c69Sj0":"3SI5naaRGFq8","VmYeIQF10cpw":"vFnJn8pz8K9k","j3t5JZfJ8l36":"M9VsL0fcEOcO","4zdYzs7B4o9E":"NbCCsG2liopj","uUMP5TjNAe5j":"as0YWzdzBwek","OhY3QSi8D6No":"e6P3tKezgnSf","0GGAK40FuN05":"alWy6x3ZJwnt","oJrBU6Bijsdk":"Eh5x7w6BYXi0","nK6IHqjyyEkm":"8jGu6UOmIUNl","XeZnDfAa1oFV":"QuCqCegPlLWh","7fLWBQDWvknG":"nj4gAW2tvHsa","yPF6DUnykHut":"1U89ZGjk1OKp","qUxNwY7gdTQ9":"BjxWLOVnFXsE","on6EQBRjFCLM":"2LXZS2nNVSa9","r5z2ghLNTZ85":"666XlIIpXkEh","HmTQvaRzAgbV":"G6wdxdTyimjI","sF1Y5pDKGrV9":"UZfZgneiWWUM","2LrN9v4HbmFH":"RQhGyfdDr2du","abFri99KMed0":"0f6Q2jXN3fqq","WPkJH0nYbbGj":"cm5fC7JnruNR","Nxavr6QWwDHn":"wDTwLjeEtaFe","KFjkWGCDinnh":"woqh2USBcw1X","NRyV8h8soHTA":"hEzlKmmP8exO","GeQp5UkkaMnb":"MCyLfex4NvLc","1WJg9zkn7EGq":"yysHdnn1dPjt","TR9YiwEJPRy9":"0nsPSIvGiz24","RNHWnKXxYvuh":"FZFUrPDIhwN5","0vTgHdFTiFpc":"1ylpzeK2DXdg","JHrktNdAQKnM":"byRllYeIIviQ","VqlufqOJri4D":"t5F4dg1Aa6fI","z3prZofI00dG":"EgJZDje6yPut","1oEylGT7mK15":"JNIVhdjCb3RZ","hDt8BFsG3pJP":"xjpLwEHFNsrw","Wxh0DYSz6URF":"rdVWzKLRzMvS","0gzsha983IcB":"0SCo7U5WGTrV","7lRelS48phjZ":"XyxqRkNUAzzT","jwkKpAQvDchX":"jsonw2ZAE8g9","CUCKSRFdTzQ3":"UK052K4b0Osy","pdWxXshS0DSe":"7ppuS9ZLji93","zYJoTt9E1ZrS":"lkec95LXr6OI","g13mtlhFBHvq":"OvAUNeDJ4lfK","gaI2goDxynNW":"lzQom3AiYePN","R49dWni29KHr":"PErS5aFrDQr8","WMeCToQz55F0":"69waMJkGZysg","af7ObFlkD0an":"f9jDhexFYcp2","MroWlXOPEczo":"fEiQxQcF3opN","9cruBe6iCWLW":"b7D7QLv0wR3H","F85TzFQsv6kS":"C8DNnO74A8p9","cgjgJMLbT2rG":"F5xJdoOYHuo0","etJmn4I5m4WZ":"08Jpj3op7lxK","YjjvsuXws0uQ":"VBggUll4QayK","MPKXQV5rUr9o":"wJ1rrwSpFrpU","atLPfKI1UoOU":"gaAqrcA2LnZR","9DsO0DnbOfvz":"zNHV1cxFDXk8","l8BAA4Cygg2h":"wjthKx1Qo4Jh","zBwgAi5d9W6x":"2BPu5Ff8IS7m","TTquR5msUpCM":"kFSWFTE27d2k","30GrIk9qq6G4":"pHAbtnytkZVp","ZrYOMRLbDedS":"byH9PioBka5a","5DRTeH2LyCgg":"9mI3UJsYaxeQ","ciSxnWaKCfLi":"bO88yevmogps","23JroHszsm26":"ZHN76G4teDzJ","JNyWKXLw9i3S":"pn48GZcKLr07","wZlHhBxJ9z4e":"anvd2FeyQoTi","EwQdNlbAR5nL":"dRoRKiUAzu8M","gpdj5sF0kjab":"YmR4jgpJ86l3","c95XQMQaxyOz":"HLSBdZJWW6Lh","aBFZFjxxWYYu":"HFd15ut4Y3Xj","VSEMAX3Z0KPn":"863kakkKykpB","4HU966xzHtST":"eU69zjW3bKLT","oRBBquMV1d25":"3IhvrgSAd3WK","iDOcbW2Hy7cn":"BBfmDCfYI1ay","qEXo1arAtSNL":"Y1lI56mIaFzE","9rX5MagZoOQp":"PSBqOcfrCOfV","b8Qr42R83QSB":"Y4JyXKhVS3P8","XBhiAGIctA26":"ITZBfB1pWYnB","WG9UWw5qSIMr":"9IU3F7Xv2Xwe","CIyHmWWqSf24":"o4bI5XZwzjbA","iUFdOu3zQN6G":"9CPH2dWWIrTp","CH1CYx6kEe1C":"KPKjdAFo12lG","yOHOgVg2tOgo":"HBMpdoTLvceN","TtHamUQLlWjo":"Hr6Iv0uim3jX","mZELTncd4WIf":"GddOSPggdtur","E3o3AWJKe9xU":"WdaYYltfb7pS","ZgXvPZPOR87o":"QZ8pSU29YNOv","wwXmmh3xYocg":"nhdQGd0YgH7b","MzDwLBRLRjqB":"ah41CfoL36NU","mNA5fojkHaG4":"N2BgHMAx4FPf","ruNajzc6jNUO":"YPwpwoPEhlIt","aN1S8XHhkLTW":"mfIyjoxQvx51","0yiFwuU2JV53":"pb6r8rG9O2Jb","SfoiUUKtym8P":"ID0V8EZGe0M2","B6ludL9VXRxM":"4DbhgP7gCjFK","uO7pEs92pGv2":"C3QVquffGqXa","wEfa0ameZCLK":"RrCtQDFQRl4O","RbaC3r1JS4ZG":"OGodcf5O1K0B","o4tEk7A6ti9n":"H4KPVdrCQuqu","5e2JljsPuksC":"SX6TJoyLnLUJ","4Ad8Y6WvJvY9":"AX40aMdaKXob","r5sfGpehldgR":"M5QLBd5DmZu2","5n8kj14v02kf":"MkXXux7O0lfJ","OOzzhu9FI8LA":"6Tl5zHkRdZxZ","6sCQXuAQEUxW":"zSAQtN7oJjEy","c8sAALFDFAaV":"WVD0pveXuGsn","bc3MlxikdvZt":"FOBYXOZAPudi","C5sQmGNJfmDz":"h53BBHRCVGuM","lLVX0DgwTKDV":"zPTjwLcAloNP","jpsjinnxaz3s":"2ojIgb4v6UC5","huBzbfk4SUld":"lN4mjePiQ3cW","kgKgC8GZCpfv":"FhNDam2GuSrM","1i6MOOPMVXTm":"igKqEeRNaanV","nePMXeQJmgc7":"A4oKS7ZaYPaU","Amt1qVyVQG6w":"jbP6qmccDFaF","d6rakdAYKy8G":"yrm4Rgj43czs","g6q7PTMU5NUv":"T4FZJuo2pgHU","3lNBCDqASeig":"CDsO7wKKcJ7S","I8ry6yLgtBvq":"yunPfOKvKzwt","khoFKVLvD2Bc":"UoUwQcpezQSl","GqcARkkdMvXL":"YIjioeP8hk7k","NS4Fr6AdonrF":"G1tTGPkka0TS","WSI5cUfCKk89":"4LQd8x68Ntwb","Kl8PbqqqGYzl":"4AEI96eOCaI4","aMjfJWAVEp20":"oPtFXl58tLXO","VcUmvMMJ2bEv":"Qw4D8miin2rN","nRmzMuIOh5jz":"uV4XXYa2aSEB","vhUubfJPRuOT":"mLGEpm0LWka2","uyPVxY7e0jtE":"0tAGi5Dk3iN3","MD2velRTZAmM":"tpWNJhvrl1w6","2HCfTZw68OYV":"RLR704wbdoSW","mzlIujhDqbCm":"Q31d2L5bugiW","gkEIBOV32big":"7t0Mg1WyKRIy","TVwFpHhDbLI4":"tHbXE9P6dKsK","nXJLAsopJjbN":"XmlTacXrRCFH","QkaYU2QVLqMH":"1xPIVXIt8Zse","HcEPCV5eRyQr":"UJ1YZOYjEDgt","QTK0pq7NmWKs":"UiESf2goEmV8","XpxtqUNZxEXK":"IbRxhVOqakxE","hJf7vQXqjYL7":"W0QIRIt87X2I","9gyqPZ7590wJ":"PlBitHf3wEbg","Z8Z0MmbzTtUW":"bNr2ZrMHHVLO","RD9F9rmtWjPt":"QaC6n4uWzwtu","BG5mXzRJG7sF":"14Uyejr7laue","l1Slp7SlUg4s":"cHU3TTbAHDDq","4W2wjZa95ClL":"4erefevmMDc5","47Y60uiRYCp8":"RBAZYPL8k8P3","41HdLEyQGtL3":"OXhaVwHMDcRJ","ocMq0FHpExm6":"bS36569POL8N","L9O3S7zPFSFO":"7hkX54jtzoAr","O3m0q8ZoXOLd":"mIH9N7JchErO","UTFW34FfTYNI":"wOQEWXBcr9ef","C8SvTqN7s07l":"ylNiCb87SEMW","8cWI0aRVcyth":"TNgrLsZpKciE","2AZjZd1gmgt8":"G8XId7GCSP9N","v0lOIE4dte0C":"GanGUedThZ5M","j1ynAecylh9Q":"YU7Cp7focKS8","EzQq8JMpOFoa":"TjzTrq0vLe6H","oTFvsrAjzd6o":"m0tHjZ9mtU6d","J7UWcq1BDDmh":"EhIvmeQMrQnz","24WQoeoKAieI":"94NJPh4taG99","HgcI98zqMqwC":"grUw6NbmSHzI","zdvvWJQsGsME":"lh5CYTlkHCnu","Ov11LPkPMYnJ":"pTeSZYLCm0zC","QZjwuQLdRw1c":"0DMXeK5x99Gb","p4au5HnGlCBm":"Ii0RQ4TV6lh7","sP4UMNDj4BTY":"QErHrJVuyI8D","sXMURFFIAcz5":"ySD4ylARtqzd","2qSITgLkU2lj":"42Uu3lDjYK9L","PZC0gVvaKHPA":"pT8bkQyf4hnA","l67lGUqi8il1":"NDD2LE97zq5E","HXDeRtTQnfUu":"wzGun4nVBgWz","DqkcMhW9iZtD":"vtc0kvQcVGPV","3oKyAaZ6obKQ":"dVERI1kbJshb","SpeJ4pffZgia":"mPJDRugY6kAe","QabOaV2FvNuG":"mCrTMBTnwtdP","Gq5yDyr0qU8I":"0fI61qPT820N","INZgg4HuJ9yp":"EgCuUZx3fJdo","DD1SsQd0Pt6s":"jIxvHkJdwUEf","jEcWrTi87sbJ":"uGdMnkonCS25","3FhKtxSTHKVH":"VRqRekNIxpt2","DmmCUfxD8IAW":"B15W51QmX3Qq","syuCmsXOGk16":"ykqSuU0oJMIf","aO9S86bnJ0xy":"hXRpVQVMzF13","bH2w4ADQbBIs":"U6oWt2qoEDl6","hgFip3qwLWGA":"8dF0iYmdIC8E","aR8H0XeU0nK4":"CX02N66EjuY6","Gr1mFSSxBUhe":"le0nbVITjrCV","1FcmEmQlaeFc":"bk439ZL4ybsA","uAfyb85axMr6":"xSlejIp2Rm5k","wgvV4anqgrOZ":"pEGwrVXrw0Xv","BgdYM5yRp3Wy":"0bHrpD249epu","Dp7XFeJmDx2y":"aK10HS8yYM52","paFWzGiBBDzI":"9iNMaNRs8xva","E25HQAG11Fbz":"f8gh5EwxJDay","4aPfhJBqGdW4":"NJQABe9qbd5N","AJY0DebsHBXC":"PwlPK7dC7i6o","HANA61ZcYbHt":"hQbGIzZ2SlGA","Krt4dnryXuUW":"eVguwkM2nMLM","lm3BYrLzyViK":"f39wghqiFtU6","x8HBBMo1sttw":"ZvkHwu3mQMZY","zicyjRtGOEKF":"wFVXRc4m1Vht","kdCDqOuKAQGO":"EOq6UCdK8Fo1","6wnUy2C2CyAF":"W3Q36B4fDt9E","XK3AJPUMOq2q":"J99RyWZ8H79K","flbRYMbHR1Ug":"VKvg9XEBIwgd","KGonBM0qgAAv":"NoskLv41zPO7","E0qVCWg0b8zz":"MftgYAg8civI","cjcBiXTZ9R6F":"E2mf6x8uv7iS","Hby2gQItoqAx":"EGY3dcWkxPxY","B6Ny5mB0m36x":"rsZgC7Mg09IU","776LKqXsNkUh":"1nYrqUjrWCZc","Dg4UaVPdRN2f":"uYPKCVJOqmKd","UYD7fsmeR9bK":"0O5Yd9ETG36y","qjT89m1OnqyG":"LSkR11OVBUvd","KLV5g2OFYtN6":"vKKRnWNfFjRI","gAD6cudPst5J":"2iWlp4xtr1IP","4mQJmS3VeTgb":"DDDAOAtsTpHl","f8CZiaCrWs10":"ruf32GZi1v8X","lQM9RMBqdpcI":"U9F5fNdnei4f","FURIGsCtVZiZ":"p9bmOyfqQGbP","rjbwLoTWiIb9":"vEPyC7U7WRoj","0Gzfgfz4ljpv":"dcH1N91R5FFa","JYYD42TttQSz":"tJojU4Xb20xQ","X8PsuVBTt4Yf":"Epd7RZ2G95dT","73ypPrpAJw60":"S3ZNVukeB5Ce","RaK0NlgPOPti":"W89RwTKhxKrt","UAJExEWfrlEf":"a8zeOXmdvNMN","pv40p7NLWQjg":"iVIYHoS5Lf3c","FFgxR0cqBWTw":"ejEzgCaTs9Lz","cB8KWBSivMk6":"RJ2KnEZkBhRi","0WCu2E64zgmz":"SbsNy6w8p7Md","WsLrqw4WjTmt":"IxPQs1dYubPV","BpMMXdxgGTCz":"zhdJbIbHAQ73","wvrA2HnulzOx":"SWQ88FQft3We","CnmjQrGXUqlU":"ruJOvijtuphc","SUoDlOOYH26B":"Fu1hcrm72z4Y","kuwlBI4h4nT6":"NkjnlY2GdxO4","pQ7bg773WAS8":"dvmslwrKnSGI","pjHPs13732ri":"UI00aeitMAMx","lohPQiOSr0kf":"U9IonaQcJB89","gwx4TBvAtQi7":"HJ6lApj8L1VI","WXD2QixQeeIG":"WOXZOxemNlRS","A8LeHvq8sdzW":"MKWOmPrnky23","EQZuQms0UWKY":"CQoKDjacgzVI","nG1I2agHEnTM":"dR9cjV3i3ClR","a2eqS6mqXPjJ":"mMEfNKQdE8uQ","wVAAfUhM2jBB":"QM1w1HvAkA3D","7yFJ2WeYpLiL":"Nz6RRpW5ou9t","PL0tEIzYj2ET":"YF7dOfBwjOkg","MfRU0NN1tI96":"7d2V8WbFSdFW","ZARgNCWBAvD2":"xtqMrbPYrVhk","gQMXrG0EQvkF":"VCXIupTPSBIa","gTEDttwtgRkf":"dN5qpDXCnt4f","RCCE1FhoUSxe":"n43m6tJzMVxp","jIcf0obaAdSR":"gsGivhD2Hxem","A1UQcxAD5frD":"JyJ15XEN6qAa","F6MgxlKnwxBt":"i1sbTpPYwNjl","i29ZUVxUxjF4":"cKkpuKvxrZ1n","Bcxrr1aR8PRL":"jcsYIF3G2HIj","9SZ9Juj9h8XN":"77eJIWR0Eac9","oechtQEHlakh":"eL6ysCx1UdlG","FKOqvX7TmGvJ":"UHSqueeP4OpB","vKG58vP5CyO5":"JdSq5xPx4RZB","eBMMJiIsxgGL":"Cu5PEKSj7XjQ","AjUGRnjiQ8um":"IsAdtpS4pxnM","j7IKF16QcRY4":"ymrscimMV6Ng","lFFxLtHf5JGn":"bhn11OBhteo2","VJ7It9rtya0X":"ZNgAH5pJtIzq","WUWnZLSNfYno":"jjPeV6Pe4bbf","kJZaiUvokXrD":"xl54M6q5rrcU","x4hmi5vOZuta":"OjoeVYxh6lmo","FsQ7e68fJsoc":"LX6db6mIpUnZ","8p65EOrQTsQ7":"WQOp8L8Fqp3d","ycp42JEUhcNL":"YUI3RbcCZtTu","zoDcmISXcT8j":"9U80HFjSJq1c","dlIHPVFrf4YG":"RG9OnMPRiM43","NSxsbtMEZdvx":"WUcUrc5Yhnoe","ovZHSa7QMHdY":"D97JtuLOmjkN","rrJBiXqgZs41":"UpO5VCRPCI4l","PCJ15pjLFWJy":"uFnuyMGsiWxz","x3OGNqRuJs0x":"Qm0ntBXOu5tP","CiSuOt2ifgiD":"xPFfgF8hjrdv","12j7OxTBsOxH":"Xy5i5BoGemN7","O12zjpFTu85q":"wKLwJk9TAURf","cHwv9YAkDguC":"qOu2xsCT1I78","VpukuhWviEjd":"T4wxFV2RmeTY","sfMYL10okOUH":"sTv3fen0pITd","Q3us06MgbjXt":"cAEa0pPLKQXy","OiEFQAjrrbBk":"oDXaKhzBIefd","XeKM47srI1PS":"GqsGbZ6bRoAV","VH0btk3joupm":"dWHKnH0BhT3N","o9QnMvH9ctk6":"hOIkIrY0IMDd","OUBzfcq2uvXe":"EqRfxOnYkm8A","z8K7olEuYfem":"N5gBEdCzOEFU","JAn4K7Ck00xn":"B7XL06VQLKkH","Ic9lYwcHBp6Q":"biiO3ep6NoDB","tj840VXqNBKb":"EuhNU8NkStmL","tl2ooGskfTNa":"m8NYYhU4SveV","0kd5x1WyU3pX":"my4gEghaR1rx","U3K7xQP69kZu":"sqcYwoprMmL5","F2axK3DbEl4J":"KytzHuKtJgWT","R82mOHa8S91a":"fdCVUIhDKYY6","F0mvMIxRfTJX":"TT0YG7BGj7F6","imnSWGp2u00r":"ZkVrPhox65yW","Xw7A83eWMw2M":"dfFg5IIfpgxP","RWsVbZ99BAnf":"YtJMP71khU1h","JMKiJ7typLNt":"qgLO5tfOxXx3","6T5sFAnKPe2E":"cZ0Cv8lGln3P","gcCC8dgiyFRU":"bLLMijeMLzIx","nl3U3xZPOrKj":"puwtcjs2y9pU","AYbDSvnc7p2h":"WnVLGAPnxVBN","m7U4J1Ze9dOJ":"Jht0Bo6MGbfP","sHhfTDAadeNV":"MrnDhUUss1Z0","1pKIZ57438SB":"4TDmwPmrTsdV","bKpfgHVWyxHY":"26dwKAzNoUnv","w3QuUze2ObKr":"dILJy9LwEQ8M","ZnsE7Od0bATK":"8xWxeM2JIeK6","yYdaN0lyO9kp":"kHwBukau35tW","zgXQdnJnb7fm":"BK5HwluG8tqR","OdI1iP4pLyF1":"7KT8DTxglHeY","oEIL9maWNxMW":"Op1GH5C9kFdA","wlLbblLy9aHO":"3VV6TJQj6sA6","4d0r7N4upHRq":"h8ZU68T5tB00","nEHNJexdTYsE":"2o3yElZ1uzzJ","1WFcuB6SMFiJ":"oJjCpi8b5KGX","mxX4fldplM2A":"gNtE5LBnNPeB","j13DmhflnB9b":"E56FcZv85e6g","WZbLUgUkxkdk":"jHWHOzc1deCd","P4CCaT307vVm":"rpVJleBdNH9r","qepsiAf6ELhw":"EpRA0FrOc2Ac","vTBVoOZyUtBc":"HdnG8rS1CCZC","Zln8Gk4lj0Eo":"DSOZUqaR1qOx","XhT1JETCH4zz":"seBHZKW1o2CJ","M3gkskv85lmQ":"nwBmzq3XE2zJ","zWk9DxZoRIrh":"Ebow9ogXoeZw","A6D3KrgXGfmA":"i3VcTzkRmEr6","O1BLgurj0OXQ":"i5X56xq3lPXt","wgNuaZCqwA5U":"M720donNbP3U","cpkdQejrW9Tj":"c5ZuLLuan1Kl","GpmqvKAQiy62":"i8EurTGi1rzH","V0FTWI9W7Sz3":"5cau1RYDRKLR","N6iXCnEUcr7S":"jOQj9bJjFCff","QuysyltjHzO7":"vKmLsmmRdHjC","GzGpVUgKqLJd":"P2Gc3Y34mrhy","1PDfSBBE0Xkx":"1hBKrXdq1cqg","WfKj31xAMKAU":"IpdbRA32rafX","UenXJbboohzW":"v2O0ZTdgif7i","KGqYydZYf4zQ":"UqydXyZ9sLrk","07P9rjh93zG4":"d6HQiSgdEAcd","nOTuKcassw1m":"Knexzi0PNbn8","RE2PEDhfbTGV":"kAcMNY8Novb2","uFeSKzfkMHHd":"9yVFbZtYbu2l","YwIHdCQ7c05L":"hGFooT33p8jR","M5JkkFdJoJTs":"imr5JWaXSojb","LuBeAL5CpNnr":"FrhgVG4qdcIz","VVA0zVYDT73X":"10KsP6kwOeU2","Gu8Sq4btQFZZ":"1QeIsM3sMlQd","Hw59r5uiq1ZH":"DwSgaujwMnY9","1P8Ou1AYnlFU":"KQugpl8eE6r5","QLhF4bNMt2zR":"F1Owb3zRyKcL","OrNiIfQ6Nwdt":"zyexqmsFc6Vv","vUKrb8qRDJju":"qUsTeSSEY41i","WwzPI63CH4id":"Og4XB74c0HRt","t7Xdr6qTYvKM":"5JZPUxvTDY2y","oTj6dcNDK1qr":"Q3FeHBUcEESp","iHcjdpZFI0J5":"01kMjxFWfc8P","IljCvV0SSJcp":"BAr7fui9hxlT","o1Ei5engI3aY":"C7dDbVrIRoYx","uEos4UMwlbCe":"P1RszU3rKQ3X","zkJ45Ui3trFg":"1C2vMhnT2j6Q","YnllhG1Vl9Mt":"1Fo37HTchCCM","5Dh2jTVNrpHk":"vVJvvIrO126m","DRtKFdeMmwY1":"vXskCu9MGuQ3","awV0TgBH1Q9C":"9zn8dRf55p89","U6YnaTX66iNa":"WdyoM9p9VLiH","JnGKwstL0sxF":"cexMGEl7PtFl","lMFmwRJy45pg":"ZKNELnfeD1we","o4Nabzkamxmu":"kHyapDTS4s61","JSZoebT47idu":"0WJv32ZdPZQ0","QmoARpQfyjat":"L3yp206PSdYz","JqJiJBigcNAi":"1HeHBOwLg7k5","ZykisPyoFsEF":"8WQZDTrxFam2","5hPzEZbCE8aZ":"UNZpZlxTBSvg","WQRBrmBY8Jgi":"Y4yGFOLcYnTV","xoAp1sO1ZNov":"SJ9aGzJZfcaR","xaMPUcatbcVI":"zQQnO9LK5zNt","QdL1vTti4YEp":"r5txYaLo0BPv","saGXAoqSupsl":"EInKNirZaaQD","MSkR78ThNfxq":"sT3SibL3kW8R","XRbBTMk5KFSf":"A0fUz0Wh9Q8W","JCkRtwljct8r":"mNwSNXS0phR1","1QWL7unKwR2M":"Hg0VWdSpbIKD","yEY3NwyCMD8z":"V5ZsEMOa4gdl","GumEaKbFY6cg":"RHFbxE3wdagR","GEJy0zGYcbQt":"Bdnxm6LP0wtn","0RaLjCmOk0qE":"YfvJ5v40OUap","w42otoifcZEL":"BTzK7LmJv8Vn","E3Qoo85t2Nz5":"FRfmVlnvqE65","cOoQfzVFvZgy":"cWKMJCM3QXVI","URnoV32v5KOh":"bjLlWFqMci20","qyb5lCfkBKrj":"gE1SU8J7Q0cp","I6jxL3gqSaTZ":"tTFax0TsfX1Z","rIelQsM9NsHe":"lPF45I0FI0bv","zVBrCeT2qewV":"Mp36W4ukY8Cl","cMy5qLHm3Z5w":"jFQrM17MBusq","ZgGCYny7vwVx":"xdLUB3OuvcEc","fOW22s4jL6yh":"Hp7sncPWFvkH","9XeTvHMWRaEF":"m0cslB27fLZB","Ygr50guYmBXR":"YZbydjULWx0M","jrecXnKn7lnD":"JbxldcI8iscD","nxS3St33High":"GVweO2Hac6By","xyNlkaJhKA72":"uEe38uvHcy1B","yvseiobwxEyh":"NpMMf9vOCVoz","KBrzFmI7Eu1X":"80XUzy1zt0xc","tki7hxpMktcR":"6OsslQE5on3D","vyo8Si2U5QnR":"sOS4S3GI0A90","3L1wW01GSwY1":"q5MWmiF24NYq","RzwRGmn0iPeS":"LprWe5EqrFEi","zdnjTd3Fglkl":"CZzkFUsRqwbZ","88y5nbLMZNym":"DzXzNCwuIYhw","1gzv6SutCfu7":"ROmbSoxteJlv","ofv0zqCt19J7":"8mLNsFPN5xSY","TjDm5X4H3Yrt":"naJNab6Ie351","IdA0kHYkfQR9":"LVvV1PZAFiX3","5fWETDDisAzR":"4ruiMIvZHKQy","8Xhogwm8DT4q":"lZiK5JyOzcrt","eOm8UhGM9xCA":"rc3H951TR73f","1pZzs5btprLn":"mux3cqgDtVvb","VF6qlZjvck91":"IB9qid4rKzTJ","LEwCuVeDv0TO":"mpG0b02KDC1z","vRsO3mNcbCyk":"9K1c7gLwzunt","WliBvKst5HTG":"fgFOfQi2VUii","3cfOKF0tc6Rl":"CJotSt2jpuEX","XMIGPnN2JLNj":"TFgTDBfVumvl","Cd11KlOvW2hl":"kQa3SnFoWid1","7qwIj4dbQ9Hj":"J2Pty2PUENnO","mrCZqlA0hZ2m":"oUCN5iRLqnST","17JiE8jcRQuV":"CBP9I94ak2Mv","VOQW3WWsplgE":"qZgdYHpyfrL1","0DWKOXCviTOd":"Kj5VFNdzNHL6","AdZEzkk8A8Ec":"BzyU2oSYy1Ru","DhpXC063UaKZ":"qqse5zhrqNKM","6GtMUE0ri1qP":"zm8944Kq9f4u","JNvDs9SgCbcJ":"DQSKFCh3R3rN","Zdt8gsmWZ1EB":"B1wDpDnrVAce","zuTTtPazkaVa":"B6EZFU7Q0owP","q9eq53hrjsIv":"vU96G1O6lZDJ","ujmvfsdthgCA":"buvonA37J86o","TzyZV9duG5UL":"vPvPuh0pBipU","4UA8FuPoiNPr":"Tfp85rxi5CiC","OAAYJ1TNewzY":"zSuvSD3fFocK","VnH1YrbCfLXS":"GP4aZqZyXc1r","CmKmgjvCjp8g":"qSK14TAoD2ed","SlrbFJMlwfeP":"HTVpgI9fK3FS","e5jcBLU9c7US":"JhcB2Z0Ikpgr","jK4CNZRDpbVg":"VMn45Fcvor6O","QvSuwwRlkVfQ":"HvIJ7sypbJZF","edC0BclxHmWE":"NhNZIpsOMzpQ","jnhhWklZBGoN":"GaI1C8YGl6D4","dCUVsBIcGliH":"YASzgJi3eGhG","d8KhKGVh3f6Y":"0p6E0yxEfC7r","EWW10SC84h0U":"XTw8GHBUgmyf","sC9cn8sgEeyv":"n1DkbGl6P8hV","DcWDJ7KGmNSB":"qy71roRRAgnH","FwGxlPeDo2zF":"1rxAPm17nnoa","yScIghcksI8U":"Beqbfmw94MkP","zopsBgfFCEBC":"zwFzRoOCXHoN","3fSNyQ55dKau":"EmMK8JqMFt22","grBZx5g79sQL":"o8RN6mU606pk","k3asd9pQkQPy":"qmKVOVvA3QKI","G3NWQmZtLebC":"pOsgKljZ9XhN","e5vA2H7zT6XC":"gJlbasDEht3D","KsyLHcDMTcGt":"Az7bvyO1qlPi","j9pmKpsX60e0":"6uxqPqDtWlLC","RZBJ6dZf9GvW":"c5vlVTll2rQs","w5GN4dnSq9cL":"bsNe1jEezv5h","vAW8HNU7FK6w":"OK2M7igxMk7D","3PZfTrNBmaTT":"9zwHz1i8ZxQM","x1emHf1FAvsp":"MTwOvKzvw3He","Urf5drkOzlr0":"ODOrXPWwDgbv","kRsfo8t8dfsx":"kHmWNnEgzSYZ","Fsd14vJAZC30":"5IOJr0bMF1LF","4xthZkzpGgte":"nCHaUgvOXjHJ","nbjp3wU6L1L1":"znfYzFiU0sjP","h5Vj2xHY3eIW":"sqnGHTbjHxo8","DMmdm2gL4JQr":"0w6wznZwYgdG","83kmdsqARKuu":"wDnoWXO5BNN8","2sJIB2Gc7aHt":"tndCg17tfplr","dYzfRytW2EUi":"iDaWz1n63HAj","d7pP13aSBscr":"WUoUixVg7Occ","YQyBmrw05eOZ":"3weYcqTBXHjf","Xd9AlhQPfbQS":"7zzLGKTikwyb","QlnYqVuNffBw":"NmlBrBwZb52m","n2tf4SNWRZ3x":"FLTAXq5xnsuD","q3LCgcIKqHFL":"yv6VBYvTlbV3","9f1OTZzIdvbF":"35OVypBNGTFB","VCTPD3ZIIS0u":"vhBsgX2Mfq4Q","jOl4AHf6DjyB":"Y0K7EDas2RZ3","YlWpuFLLG1T1":"WTIR8XwaVfju","aRILcT02cjSv":"CnpHBx6rEWAO","eAESflj6mM2r":"5dB8GoN2wle3","lZNQsizObex5":"mp8eZTItVkYI","or1npuGeC1E4":"yjtArn1h5WEC","lAUTmvg2EaNu":"jwO9eacdFgbq","fJvn368VLx85":"HifNgZ3QPn6e","tfAwCjGN2B1Z":"595zXhXdFJFV","FD9Sn6K2QTnd":"7YLNP4z7jsHN","b01E2BsN7wu0":"FMN5qwXpdO3f","2U4QQWfoc6Gz":"l5uuA9hlRgLp","Ir5UCnoDEFBk":"y6kai5oOLvRk","8tOZJvU9DLzr":"SmhqZ3sxVuqb","wg7fHKuUz1BO":"U4JFCg7ABHSv","HD8cXuaqtzt8":"K12pKEdZkewg","Aqr7QxEudU9D":"IprKRGROfwGE","WwwoOfxE35eK":"r5CCiv77xTev","9n40w4WHh9ZM":"nOSObZC1hXku","S4QF6Z7h7VMM":"BBTK60eb1r6O","dyWV0ogi9eLX":"lUPFJlmLWgbu","uhdemZYtdagX":"JQL3iftbsiyG","jZtiCZvhUNqW":"IpsuuDRie6vA","49V7fhBMM5JH":"FCWvPGwbLqg6","oqudqRlyCRtX":"QZH8Ok1sKhda","jHXuG1aukfpc":"kkj1FUydG3nl","C1snLK3luO2d":"w5AEQkPSkM3L","t0fKGUUKdBIc":"Jd06UvEh2gaD","VIzAsq2wRDE9":"iYAzzJt49Fyz","doGMrixHSlDR":"xFMFxsZuQUzt","QE3SYyvaiLhY":"mgCKZ7jlFN4N","Y9GnGibj6BGK":"ZG3J18Lu0ei4","b0TF60cwj06F":"AMHLkjAMNMX0","kL6szHvF6Dek":"4NsWr3keOC9G","7h20Xm85rAqX":"xJpJd0vet8sT","e0e6qYI5rojg":"1pA55RHFy1Ew","2TAlp4W36qjs":"3uTeKKjQSebC","SXl25fZk15sd":"45YN3wJO8kFz","Kb8st4KwfRlj":"O7x1TaGMunE3","wCSbzrcDILLn":"dW9GVgeVaAW8","AEnpFuNSfqBU":"XJoyCgcr3rVG","Wc1GdMEfBXxB":"H9HRD2Fsi0MY","DEW5E40uHCKf":"GAtCntmjToII","kYhVjl8sVEwC":"fGRnfOv0x941","cEfdzFJ15DOz":"XFR8Ww59ymU0","MsxnYo8ItHHD":"4aCgCzpu5qXe","P0BWAl5wsX8N":"qbANMNR4RfzZ","p2D4KpInRqwI":"o2FYVLH4Nnas","e6ddZkSeZzwL":"C1rfRC58e0Sg","qiuNedrKMUPP":"fvbF3tX3rSMx","XPmNgqug0zRI":"rbhkfuz6F9IQ","skChu3YNC911":"A0JFuOo5AsDI","acQv0dVFbsT6":"Zb6cAXLws08P","pCmy5NrE7wh0":"VIrlgNonhi3L","RZHqILaJH4it":"bNrvV2pRIXgV","X6Tps86UiRQg":"8euTrIWID5V4","MoQkSt07Mhb2":"RNsy76cV9Tgo","qIXxhZtP8gKV":"OAJ6GSKIzpW5","Narfm996Oi1K":"O3kT2PeILnmH","0BV2tcuDg70L":"sD2Mpiuwgmao","RjkrYMDXBt5A":"ihFTriGcV0Ed","5COGcdU68xJ3":"vYiBLRbbgkIy","xHx47U1tWRuB":"QGgHEeVba8IU","BlrSoDeUtguH":"AHlXo7YBtVuz","wDxUKtIhSb3R":"38k57j3rbhIG","t6yeeHxaOkYe":"R56S7sWTPCl6","frG6euOWUkVV":"3h29QmPukeEd","nXbCNpjCF6tl":"LTfeYmXvkQ5s","xEmJ6TxP5aSW":"oRKpG6ThaR69","PcaJdBi1qLp2":"YIe9GneZKIEB","KGjR9giBuQt0":"5tuvfVqFo3O7","eLrsDZ3VtuZf":"93UtSah3j9mt","rtDBixKM6Jzf":"dzArvbHLbrwh","0wTIHd39wIx5":"MUzc7WxyOtVb","ardZNeNIrhxf":"T2S2yY6cCUO6","SssXthwIhNH5":"3j95Q7WHcDeE","FAwZKTkDyHcN":"GAKGt2SJZfFl","3uqIL1dl00SP":"lcCyuhTVrPWu","Zf5J2toDIYqx":"60X1pBFet6Cc","CZdM8LX7pIWl":"6asWYhw0dw09","hlEVJQesaWKB":"EX4Gs1qmE67x","zsDmZlTeOCN2":"hpkofii4fZpK","sEPTXL1rAGaS":"9YOSvgCqVuqx","DMNcPZPXNeHa":"v4rmjAX1KMPx","kqdnMBusdeJj":"dYzI0oFM2qwq","fosrLSArm5RU":"y4GivM63tEt4","SDQs96LR0R4X":"0uet2DSvQKUq","eJeoHHxLV9Mr":"a5V6vKbxkzTG","6dL48hRBXekw":"4WEES6oQ7bQc","doK6caFYBwfl":"mQcN03uV4s1J","HUVLkwd7pgqr":"KhLbjvKdIUNr","ZEc2OJEjvq1a":"QQAXJJQYJozR","HAlM7OZ0qAQc":"THGNcLAZOMxi","XXREJQya5jKl":"ZTYnb6VrcBJp","qxtJc76oSGCC":"vgja3BzVSyHb","crNqHs3J1Nu1":"hQmxKsh3v1Z4","C1slJ7zju3NX":"EggaB5I1N9PW","ZL98laJTCns9":"9RYhEV3h3Prl","74agsIaESYmO":"gnMl1fgPibRp","5TXc6gIrdCpD":"XcCkChXzjhAm","wJBubzXFTc70":"B8a6COSvIYoM","nvWGUDdZi43i":"UFGMH33Cv1yl","WdtgK0nRZy27":"XCsHeAZfuXfn","0js73XnmxC4m":"pbFFYM6Fm3tu","SaGDxcvqOjni":"rfTJyPV6WrSD","hHDpPcvYCbkZ":"sRapFT2MXsKM","kdYiErP4V2Td":"ognn2rWI6QTg","yEXdzS9hVRIA":"2FFa2wRWn9fp","LLpnWgAXk76B":"7BqQ7BgWyjYB","M5Td9fw02WMX":"OzvEu03GQgco","Pv5vQhJNSFKA":"LPm9EqlYnl2z","WnarJipVqP3i":"I1R2EtDaIILi","e7ZzryAiz1BB":"dm9AUlqeNfnf","MFOZDvWlJBrC":"oDxsJZbHStUP","VvqAJaw3Ncl7":"m6lPMGoK89SL","a5oeZYgDtXr5":"BKNIkjiUfG3A","jEmkwUL5Solg":"l7dSu49rLLaL","lyWPUoaoaxyF":"7gHHVq9UMv2s","fA0lYHxLtnBk":"DpUDZu7JDgYt","25vqeubmKjDD":"uKw7PRu1Hxoy","LTEwdrs5CqF4":"ZXK2E8x2Wdvy","UDh9OkTEWwCx":"XN6beG0ZOJjw","5ONmrFv5BLaL":"mZn2rS9rnque","WiLQH0416p2n":"jeFjziPZPZkh","DPf1pGDhM21p":"a0LdxxNngWe9","68ZYIrEkfQ4p":"NdR0PEkHgg2L","CZesLfgtPzT4":"NMDpL8dDiPU9","MKnmJHmzn1mI":"hG7KB8SbHDz1","mglp7XDKvKwP":"tQpXmfJ369a5","F38oSF5iH9H2":"bzfhZ0CQ69wz","AD1pdUvr9RIi":"8DsfWAzanU2X","RSfSylJcfy6x":"zgpPzNTBR9F0","fhgbx4ZUR8uN":"89GVYAoWbt6N","eEGzAqPL9zse":"0yBkED48WX4I","Ubu9eq8eOwLG":"xZtqE119N4Ac","O43zbgNxEWCn":"jMpJQFRXLmy8","4zHS6kB3GcBe":"n5h2aOtz7BlQ","jarbt5t1cGaw":"nGjfMXy79JfM","KblWfcAgSgMY":"e8E3dqvN23kr","Eyvs6CnQoUOj":"NmqMnSTTPOiP","kSFFg5zeevDR":"MK6DezACv1xy","4oaEfnSny5TY":"sH7CdnkCaeeS","lHRgJm83X4rH":"8GU0S2PKqehM","9aCHAoBMidm7":"0EcM9ki8Ln8G","CuNIgOOF0dY4":"12vgq6bcYFss","bTyKtcPGWoku":"GcjZ1m8MNCnQ","Poy0jBjKkfSl":"9yYbp6gEly9k","h5JuEGNqf1EW":"FpBMTFyzt7kx","xPiXNrrW6xN1":"X6D26bXdR3aB","W4QBBOFoJemU":"QLiK47RzbDWN","Q78dBE7bD2yb":"Zt1LVFz3wC60","UFDBdeWioXLo":"SCl8BwXsaHix","v8RUha1g1lrF":"86crj3EBFbGT","2Lr69RcGh3w1":"qEpESuch3ZCf","eyMwPQyDUQpJ":"SxxHoFEN4jmS","P3j8O57l4WaK":"j1vbcNOqhwfC","Dp3Bn3cLg9gx":"SksKrO57c7BS","rvaZOfl9uTxL":"Nk66BaALtAEV","BxOa2toYePMb":"u3bWJCsf0oyo","exk17FNRR3KL":"806Cr0RbgCoy","JLDgErrJbcwn":"RM8I7hdsMayS","cEPlNCBDHFYm":"yGS3ixEU5jAs","yYKhyffsmEkJ":"WB1skklnN9xJ","sruoUJ9id8WN":"rTQVwi1KQXC1","dtDINy7e5WtL":"SAWpIWAUbgPH","Cxlv4rPFDIoj":"EgJbwykfSsTP","C3ja5wbTfvHB":"2NSP2VayNZny","edV6OCJufnMk":"2QzkRWkmzaYN","VgeokUdasArw":"2NMWAXe30Cgu","YV5jO9IzhsaD":"wFNasT6gzkiO","mJs5ItaqNo6y":"K82u6alEt95l","AGBXyyF2DiK1":"7FBjoDsvvAZI","EKvXYQxJEEqG":"vL9NmgLIRUKK","X1hzoaDk0In1":"vhNZyZ97Mmef","lwiHNl47xzuD":"SYdovjcXuGTh","qhR49LKEcwvH":"UvM4EcDweecf","jet5CugFyeQN":"lWKsgGRsKtNA","5zQ8yisus0Iw":"NbVDySB7wbx4","VAAzI2iN5Cyp":"AlMUm1DHmxhV","zdP56ZEa4cxJ":"JcQIMsfoyzah","fRybXJBbs8QQ":"7Ir5lVBSPlpr","zJToAtHSJRru":"vmX6ZHurAcsD","pAwn5K50Z7hQ":"exPpi5C0kVRZ","09Wact65UcQS":"3kVJSfDPv9OC","8PtjHvOE5F0Y":"rkw5GZ572Nrg","rkTUIah0s7lk":"RdNyyFn3i6ai","rANcTPyLsa00":"NByGYYat8K0x","qZltr4XAJvLW":"ntKSo2k6nAEg","4tIm9tv1UO0i":"qlzN3uGRX40Z","53Pv5J0RMlUt":"U3q1OtB1DinI","rnqDFn7Urauf":"xg0LazvWK5Gw","EZmc4htLzYUx":"mkJ67ZTCJjXH","S7bLEXwDVJNI":"8jptBjNUc1WO","pzOAYzyJ4Rd2":"Hq1M9ffcLd5S","iDBt17WwAS4z":"RgX8qOro29do","u7W6x8HSRsIE":"0GeC7HXL8mYN","O1xQZiA1U0JD":"UuZMc5lsggYO","SIKIeoJkUZkV":"XdM63sbrQx57","nTus4L3AuBzK":"guyucIIw2LRU","eCZCcz0deBcR":"JYpENqpNdYgZ","hj9KTQRIdMfl":"kyLrxdlJd1yY","b6xOboMfxDgO":"AF04QQsoxfvn","IPuCIpVOOFfO":"JSEyIVwSrhU3","8iOPd4lFaknk":"wL9xE9zc0RjX","BJZmwofikcex":"a92jphizAauo","JYHN095VAqMw":"7srKNKK0IR8x","wxmhlb9C1UNZ":"IEF2Q0M6YWAM","Sv68MCoCr8SQ":"H6mCrS62taCK","hGRKQIVa6rL2":"0jqTYc0VBSpx","TXsJrpFPHEVt":"veRoFOElFrLi","HW5E2rLtluLn":"ah3VWzwTNJvY","kgxfQbXzdXgQ":"PeP8yXaWvJck","FouCKNFdxdwf":"Omz3U27ia5ob","XybpL8tJQATA":"lIOQhe6B4rcN","KNJLwHF0M3v4":"Vrkfi2lFUfY4","hEDHESUyCYz7":"L48q9pBZjvFi","g9P4MWQNFJJp":"xlO5y55yprXW","KQbaxVAPJ6eJ":"IkP9j0ha3aVc","4PKFNm6DpaBP":"mNS4PwxQnUuk","v7PefrlfbdzD":"B1SVLmnTLPSE","TGPbsegePwBz":"qsts6Q1rQDBN","XutBBskcfdBf":"OKyT0tFtryKV","b5aeDBSbsYi1":"EhxjNie29S7j","8mQd4QNNA7YF":"95gvPeeBhYR1","4gv7SD2J7EVa":"f10oRJZGucPb","nULFPvrxglBa":"lLKdGMm4fGSQ","54VuHdoYrD3e":"wyvwV0JMF38n","cHiwmtGx07Ya":"CUFZe1iTtU3v","E7qHbKzmJVvn":"IBZ0czLrKmP2","C4E4tWsjhfC5":"6ePIlCoozf95","Fi8y0FBTLf25":"muIKyKDdD3BN","Rf3DBfTjaYW2":"THVK8nhEihN4","dkjfxK9UoneX":"OnVbs3BDxDyi","Dnu0Mgzx3XMO":"ibxvpv4z4z6x","M4DwG0fYKMG9":"Mge2xq3CqHla","W6jdZtqZpMGc":"4H060iaBvKK4","f4SWEIJEpBcn":"1ViOAmH1idFk","jRNLi3YkpyWS":"BVul1ECchFfI","o2q8WcnGeCfd":"VVwEHaF9DW6f","GZqmZ8SJg5OF":"27czKFpKjtdn","JehtfMQCcTD5":"GCQIpyzgPPip","en9gsEXGQAnU":"TvVK97KdYNgR","DdTuc4KUiOSo":"VEwY4FZo9Dpp","mVSPoKT5NWJg":"GpafREE5lorw","mtCjI5lbC2MI":"EfEX334FNzhv","JBSDT4wH9TqA":"KPF9lMBbuxzp","x0pFsuezOHGW":"bqUIZQvbXdGO","cVia4O2aZ31Y":"Uv1I3nvsDkyG","giIw5vhnbadC":"EWWghMSFETQJ","ti88XzuU2oLv":"Ckgi6QbaJQEF","VSrfNdELIjxK":"NPjF3s6iDpbM","mT1TH2W9SXQg":"a0LSSce3l0BL","hc7pMGHMHFfp":"Ofkwgb9uZxP8","gkO8YSryNKKD":"LQ2YvtCnIjPK","XSlZQlupdPUH":"CWDeehzQ6Xxo","OBWnRQ0b7zuw":"rArW4p4muhPr","MnTYmMNHjYgk":"7mfPg3Tdmjwj","fujGL6chDi8S":"NhycC3yU4HI6","lmKAw24MKvC1":"qZoOkLeOzqC4","ok9Z9R70zUU1":"eE5wCacYCYq4","adN0ta3YS24h":"LrNBHm3FKHRl","2IUaf025Z8Ql":"Ze5dPqclDJho","PmNf1NCcxlHR":"N4rUEz36BiCu","yWk6ApTOo7po":"raDkWOxWlGdg","s5AJN4enDlz8":"gIWeWIqytGmB","QI4uN85YaprV":"w9uSHyc4H6bK","j54DlfQP77Mm":"46h4mmJx8pBj","ILu5Uq2jK3hQ":"rCISdXGzRLd5","HdtRkEmfmUkA":"I3iyM33EdBjL","VoE68z5etrK9":"GQxQJoWzN2mP","uIxXh5OvQUIp":"A1fkNqB3rPBv","gT2tW3wBqpvX":"GznZQeSGDY1D","2eXCuF5oK34R":"l1JvzEfhoYrQ","VQwmcs11La4X":"tP8O1pikAI30","PLY48X4Hmxhv":"cKIVupoEIhw1","iAk9a2jWF4gH":"NNtlaA1LAVDG","4KyxNIFyg8rS":"qOTNMt2liWC6","4ppQOvbUhIHs":"vWFAkacOBVWp","YrFAhqOQlcjm":"YTAsLIV0h9UY","miMeLEZ71uMx":"otcFR2CZXSRS","3aGYTePmcH8w":"ouwpBZ5t7O4e","BaIZuZAGDChT":"9mdNVunWczXh","oytwDyzCqtnt":"SMJ83A2x7Avk","YmUg3Jp544OC":"ilXurpAuC6v9","ThYJrXDLBlkv":"fS1RM50alPfD","f5g98MTuALJb":"uVqK8vigAJM5","n5uKWgjmAdEb":"NQTGmXRIK1pI","fYSLgdW0gMMP":"rIr6CHFg53BZ","82dMCozQZfi0":"FKIbjg1C4qVN","6FZKhvfnPKex":"Bp6CZlRytFZk","VkZQRr30xUnz":"oCrVKUgiiq8E","jbmo6aS0ddWO":"8H1oYZHgWOJP","kSgVr9TSduls":"7wCdXmk2tlor","e0zT5zD0c9Y0":"dDq2rdT4O13f","jneDMxndDR9D":"5YcJW8tgda7w","2rfag56wy6yf":"8EHTSuQCbrbv","fQuRgeLchYDg":"duXSJpYMKWsc","HhtK8PiiHDxI":"ufXK5CnlpXKQ","3aRlerx0j8DY":"nn3Gnnka0l19","RFDudClQwuMi":"e9yHfQv9dD1i","vHLqx3ev9ajq":"gu4N3DWWWOBD","Ux7fAznLnDnM":"pYMtYW6HuIlo","MZbWV2vg7KkN":"9gajTYHzhoVW","ka1OGKzXstMQ":"S1UezKDSutbI","jqgWttrjMxTG":"uWhnNsD449VA","rA6w7ynkJm9B":"4T0tYvBvVwQo","hEM4I793gKtV":"xa51fNxxDlW1","2xKv1Wxm12mk":"vclMkYKGA0XM","oXJodwM9UEyA":"UmwZ5Kv5qsLW","NffUqNLTWpDJ":"yCPm6ewm1aYl","p09G7epqgXZK":"wgrWgmduBAB7","LJ57acJSRwqv":"EuIXk71oTCbE","0Ll5rkGNLSyP":"OAnlo2x7YmAX","fJobGx8YMyLi":"iFNzV5UuescG","MlaBCYuCZd64":"PvAF1W3qx5m1","3uf7xpW21FV8":"wez111yNNSjG","hRASbjMU3zVH":"2LAB1S7F27td","zMpvMrQP1vtP":"BG7iBLHifSma","aNOLl3LkiZXQ":"qFGfLcnH9cRy","N99bFOa2N649":"78UCj4zStXnE","h7Kz2iAxnWoy":"POGjVj1N7HRH","JXCfU6DUqW7w":"W6TdUs5kc2Dg","VgtZMojC3V6X":"JthM4Sc6B59P","tQpx0pkR4Jn2":"uLrj0oc1XK0N","bvLPoafMc3vp":"7UxP2lf8NZDQ","haptkXyrc3EX":"XBa0NUlGrQMA","WeAFhAjJXgi7":"AUxDj9QOuerf","GjsXmKBQWObp":"wJtdg11eHmoy","9vPG248aUaJh":"25EymaxkqoYb","eGnMhQQTxwps":"NvWwvKOmGx4w","rAEEXso6GIXM":"raPJPCjm6HV2","3TGdT1tka7ds":"1IF3CFCjWeMb","9Xh2b4tb9ua5":"lcCXA2MI92LU","X4rkIWnhnhUy":"vTtpH3I22OT7","Er2PG5h8K7Lh":"JQr0FU5NVYV3","mTD05qwnwUf7":"pfQtwKjiQJ85","ISMsIjdIJ9hh":"MG8Z1PJdXuOs","kEZPLYilB0eh":"qqYIouzG65EB","ofGYzbKVw1fE":"RKH7sPQK6bs8","iLMPb5ylpdr7":"MEhRXHEkI1tB","A403nGjvgDEU":"zfLSiqMtUBoM","QikSqLiG7ri5":"FmMWSXxwdToK","M2a6RkUOUxSm":"4VmZdtn32kNE","jxQgsvrzSA0y":"Hk4uwtqvHozF","liVlaVrSTEXq":"gAClMWPYb2D0","v9B01lBoo6Gl":"arU14PJJOMN4","ELDFvYJAy9wm":"iWpHDknW2IOd","vXSld93SkzQ1":"W2ggEyyNDhb5","PNoF014WSRWK":"ER0m3C736CAc","o75QEHe4DR00":"4e2KZZTV76Hi","vV4jpJdbfQqv":"2zVyUvZgltOM","opCK48IBpmo0":"m02vkhujweKX","DYV2qh2lUaVi":"oPhalQSqjNwS","C8wQEqexsP5I":"77Yg3EZyee3W","NR5goH9CsuSQ":"9M40jGlIxnYc","VmNZiF4pbUQz":"jAB6v1NSI3PL","PgQ24ZHWnrwN":"grURG8VQr1bD","wdrp3gtPwZrl":"litnVSQnguPV","oKjkbzyMLILs":"Y4j5sXtTlVV0","XgQNJnK8u8bq":"9gXJbojIojpw","xlNiLdcXWdUe":"0MuSDKqKyyFi","TrYXbWpHHWgJ":"2LJraCWswzbq","wwPQDgfInTI4":"0JYQLhLjbjYy","iiu21AOjMjvF":"2YG9W94veQ07","kdLAr2H84q0W":"M65vIm98VzrV","aMeJmMTuxO9v":"ZI3ZwwXkYFw3","YMJCdQLVrVBv":"18a96LhOhfUO","BZ8k1SJh12qD":"dPZAvLQoJK4B","81FSTlYW3QYn":"uUvRnIS2KXqO","ek5vUg2DagOS":"NprK7XWyQqln","NR8bE1NcmVky":"ysqXGvbHyNwo","YUoWwdiJnmGr":"Inezi0PTzcct","D6ND9bhLWu40":"j9Scib8a5udl","JDfMbMw8pqHm":"IyQ5idhmfe3x","HKovBGfGGYkV":"6lpas7CNpdCS","zWjSCjMaiRKj":"hNJBwprIJOOL","fjxN17nBkCHO":"anpxYpw6LipD","XWStNMndsEGC":"V1xLqkhBRIHv","pv5x6DbrVrlY":"CpRZcmlVIaUC","CWo4Jh1DAKtL":"wGNcMrOlRAhg","3nh4kSIvVTLO":"P9XttgBebnkL","4k6ncJsJ0A2v":"zPgzsHR9OrRK","CcceRfOzSWJT":"1cw6s3dEYZ9X","1N9UZslYiCGy":"wn9jopXI6jzm","6wUSRmzeB7Kw":"sCzEFqanNRzo","FaMLh81iZHpc":"YARI3jbhqEB3","4KAZE0gv1jTy":"y9wmDscf1M4o","ILbQMVOPNh0m":"yhnK4D9FKmp6","XVa8CpfylRKf":"QLoff6jqYI6J","NmvIdcZdWIUq":"lykgYVVQSTdc","aN4H4Ube5zq0":"1MT6A4hq2FVD","MCWrjEte70lf":"XhokbK5kQ2og","h2DWeFzuE9Mx":"cvg7hN8AW0jN","1pwQMdA86fkd":"CwZ03bdzPbc5","9yMRgitzCq0f":"ZjD1yZSJaJYg","KXoSGNfI0jSu":"g5o6EZ67ThQG","SwmxNRUK4rOb":"lVdE1b8chSKK","yosKt5mzXTc9":"wU9XMqN7BGW8","SqLDpRm5wji4":"prozE93fEHV0","Q6fELjVUHYfd":"q5pg7kuR52GO","x52CSliWWNJG":"zV8s4IiYBTK9","NxkW8gVK2eD0":"yB1qN9OqsYhF","6CLU6GTMaXso":"IIUD5Mm6fel6","bKFpI3rIE0t3":"JjIftsFHP12J","AtSRwakoTj4i":"FPXFlsSUu8Fo","UYDLeFOv77lX":"UIYkog5taVZt","OkH3f70yqKdu":"4qeAYIT1NZxx","irbJgvRl9JAr":"vta7NlLMdeEx","7w2mc4QAtCYA":"FoYNXxIbzSNQ","HEP7ed5FUEcT":"pY5JRg7831vR","3mdwC2bm4rWF":"kERUM4ooYLtK","YR6DX9mYx7rl":"eEKtdH5eTibf","7nDLdkzB0GtN":"tm0ySJiFslGO","HKXAi6WpKZqp":"mNk5NhdKfTYz","jGvFWQtwMHe8":"QnOONqRaFAxc","iGm2UaAnoZIi":"93e0D4g6RpF0","KNSU0Vz3PaeF":"CgJu1kTr8TJM","KaUSRFT2NZCv":"fYLxJJGJjGKA","csylIx2dojpH":"y39itxkV7nRV","pMxOqSiGiRP8":"obys5oQvpOOw","klds4SiEcIwx":"ZPX3bUxOlezL","7TbzfrQvk5kQ":"4ohYcIzZmovq","qlNxkhTXvqG9":"P0k98NLTXU4U","CoGRKQWMK3r8":"SeofpOywb2KB","ZluDpxYUVDsP":"Nc76X74kyxZZ","8jJFgskJ6IrV":"IAz9hVX8qAy6","23cZmkzNPRIy":"i3Imn07JzaVB","RQxUcBGMfed0":"39ouaVrqxYTg","Ojx0DqCeT0eC":"jiEccEnF1TuE","g5WG1nLcwb4C":"F5Z5ZRbVSgrY","34oXgFL3Gdsi":"3X4XHA55txWt","w6BHTRHWGLE5":"tNDHSDqCGWDN","WhlT6nLOaDQM":"OrJZILLYeBsd","2kn7HAUmO2VL":"uhVRxaBh6aqM","fqXwFbSgGXxA":"lRR4E4eMjjcJ","Q7XNDUrzcv8b":"qQWpehv28w9D","FWst5JuAtz09":"GCA6nTQbLmpI","GjwmUwJUkWxy":"sPtzra88PMMU","zYPydiufazId":"HE5fOHDdpXdR","93HidYrT2KKI":"ifqbZeHw1izp","a8XW1Sk75P60":"9rdAfunXjWXq","4lkRYNm0tlZH":"g3U1yI2UUobY","J0F3SFncUYaw":"5seL4LkJlx1A","l61NqCWX4xn6":"UQR1XGam0fN7","ej2YeULqU7wZ":"lIm2G8Mko6oI","76EcbdzUOxRw":"4dMSGT6sy3e4","upbapFWd6k0u":"X0EH1kzBdD2K","BjbajANuUKaY":"fhf9ZGPY2Yq7","YrShcuVhdRj6":"U7By6yHs5sT5","aQDJz5Va62wB":"oPZjJfTKarif","VXYGlJCMTbpQ":"xdUxwafe2N7t","vy3B72k7soMO":"QOvtkSqorQs9","FAgZw2Urq5HH":"6HcWWZmKaeAI","dmO9PxSgXK17":"eeY5IgtCQbQI","Vz1R5gGwPgjU":"0mvsdsnhBnNa","c9YvIXlhEFjC":"revgaNH0g2ec","CJhwIH4qL9En":"7KGIm6rpE4Ur","BuTMLfMecsca":"bzFJev0ZqUwI","CVrRbUVywcRW":"k8eP4DJ86HOj","qram0tqjLePm":"JdjamZMP1xTW","CbqLZF9sOvJl":"RrdaQgvIA8Xh","USGV2Cf97whp":"KohayMi5WrAW","7obdpgzZY2dX":"g8xB871fUn4J","c4Opbdk0BVLg":"6uawZseHfLeB","F7Cs15ZzBJ77":"I58l1Pabyh2d","JnIMElCuJdHT":"xCllk0xt1wY4","HJ3OwUcRsLDQ":"Ns85L1fgfLCh","4VSrImPtPQHL":"8UOMdey0aQ6U","eXNBFGiV7neg":"LdTCqNFVQT6H","Vjhc157Z8byk":"b7mZqcMfchK7","hh4scGJHsPAn":"1vE6T5OqeNY6","KcFQdIabGpDR":"fsUCZODXYSDE","QOedJ3FawJuJ":"sHe7xT51NsMm","mzYP30NZL00s":"xRg5gbCEDoEL","Kzu2P7dFkABs":"nzszHGK3RoUQ","EDNf0pHjhB9v":"5XMmWiJc8dx8","fn5ik7U8jS5s":"Uw3NyhS9YwcR","4CXwQ1vT4WjA":"Ovm9JzOWZqD6","52Zs9Z6HhgDr":"POEYBYlU5WOO","yLekGgBhYG0P":"fH9d366erXhV","SeJnIeM0vPvs":"34eS9EVsT3ZR","zdXOu5vIMArF":"c2q0otgFc9nn","H2DlgwXywWLo":"RuccCN3zPFTS","JUjLy1XKbPLJ":"n378vUcjrpDb","IXtcsaIhTHpT":"DjmlEume2yeT","gftOMlOlovSo":"ZAiq1QDmHQhu","K7jNIZ8w6Kem":"ue6EmXW7nVJc","5K6DOKvxCKl6":"Cu1PhtG5ZvSb","mYlvVoGWGVL3":"leo90NECWMuE","kfzYVymlaTfN":"Z8r4G3dRCgu6","krvzpMt4ZHON":"bPnKPSgYpPXy","78bmyzIXS4YZ":"EcBmF195KIvY","WZ9BnmsoQx2k":"uS8378gSCpAU","sPGhD7UOixSt":"r5tfavTFw3eW","OFJc7ImUbPq2":"zvlogXs9aEVP","GxZtot5vOiku":"XhG86iXufdzq","pF0Hk9vudXV4":"3fnBfIHf7U6P","llYeMAFkgD9m":"Zeh57gJ1Ezen","Qze6xWloOwpE":"HUelXxWK8lid","iiAsfmjVIr21":"euX1YG4yeod0","rm1qjiC14xU3":"thA3QeuNZPSg","gHrsMwWAua4t":"luksMJ6PdDYU","dmCtkC7aXDcF":"GNtMdAxQXSY6","a01L62TcqrmH":"3uIqxNBvctlK","dCxV0EVGqIcH":"wJpYSE016j74","bsqsqoOawIcA":"6zg2XXe05LAB","wTlamwocas89":"4V43rb3tN8VN","JJjWcUHTtSH2":"bLCb44SjhDni","sQ36VZ65ZMQq":"F7JTzTy0lJBj","B976MDYmJ3J1":"ALamPLpkuZR6","6H9Kwc4XIrVh":"tlcMprkhuBPg","HrhZWdTt9bZP":"4IyD0DolqN6M","GCsAXqUQUSB9":"pGCp9I23sv74","GXi6YeY4blwX":"pJKvzHG0ChgF","4tmpPmdHvNit":"hbPjxSZ1vR3x","at3LIBBRuJPO":"G9kBZ0Pj73lp","gR8EEEthlMnv":"MEDxdbULLW2p","VdOmdIZeK344":"o4gdrLE8m7gb","Q5ZAhipCO8fq":"5VuR9n3WnKxM","nCguI9EPykSW":"FfBLu7pH5kSv","Mz5D6i6eALsS":"H7EM7bvBHfuo","riLfKEE2d3BU":"TmppDfIkwtkM","DJOnAG57xFqC":"COQL9eqDL3xu","l6qATCVrf0Si":"S6w96qRmqmXB","IKGP10kfmfMh":"Zwqs9ewS2TKO","UXt9qxp3AcPg":"ztZFDfopJKT2","SG10Qow3KdnZ":"EZrS6joN0izm","GJUQALr90u0Q":"Q9RFisEsvFB0","QUQZKkEiuk3F":"4SSemL5JLrO4","z6k0ZYBDvn53":"0r0SagRa0zC4","kF2L609g7w36":"MbNNIEM7KTyb","kI3mXELZFml8":"AzT0xtvgp3Rx","OihRSr6VsaH4":"Zu4UcIn7Zx9D","nlUQwfaGDHkW":"kwVlmOqGSLmw","hcHXnB7UOGBi":"Ys0yme71Ntyo","LkGLGQtiZ3jI":"0CNaZtk1cPny","y2BucXcFGyGg":"2LYRHWbllneI","fUkw14CbGzfX":"U5wRGveeS2Gg","85V0xn3BUFy0":"bhmeUwu6CYpB","7uycpgKhadIc":"v62ALuM5Lye1","WmDa2wNMx4HK":"4MXLxFV0e4wn","CDcXf5WvO0u4":"Irvezzl2ZwlL","GQNnxgrAJ3VQ":"3sKgNAZZbU63","nc5puKgSpVYB":"WgNJVNblaxND","FTnVDKaXamqL":"PqVIfMOopDgB","R46vkCN9m4fd":"JJjhJcaSC4lb","ZMZ3o1mN475p":"AYUSS5i1MF3m","HMCmGWzTr79u":"hWfoioJSIsWc","FiuoO7mW6zOM":"nFEiXOJp1B56","4E5jOzjopqLG":"hiRCeHmafJsA","2HfbGHeQGWde":"BOgsEGmLm5cL","KgOnPmFnfdp2":"EDtKspfHsvgi","TxC7IcnkNF6E":"RV9Wma79wZin","M49SujouZwha":"2aWWr4upGBNC","OoNLgImFzexi":"dO7g4yfQbJrx","WLu0aZb7KsMP":"i7WpclrVraNn","rUsyRiS1zLUx":"Rf5cJKCjjWlX","yySAfuKVlQRq":"z2JREz9xj4EZ","Z3qRUfAxYXJu":"KbTecUqD2ZHj","i9hrHcnzKrVd":"9XIkdpNooIZf","nhRvEdkNdQZ3":"sq2nErwqEG43","xcaWPfGYkLtm":"nat6Pw1EBVcH","Gtta5qz3T5iD":"9XXA2ARwJ3jB","gTGF58PM4EcS":"lADrnkKh2Efj","gmBjlIq9xxAO":"ckTViDDmrxTu","BLUAUxt5YZiB":"GGKTyRpsVIip","cIlwtP4XEe0l":"5iignqtyKR84","gLYLtrsSaWIB":"Iy0PqJM7WWuX","iNnUajz3ht3n":"hLG9YCsUpSCp","BZBMnTsYThdm":"5K1NNd7vpHfz","n3XQrnwy6hpO":"P1aQ2hi9XMHW","1AI1J9mGnaPu":"RDsaCA8V5o38","Oz7UztBe1q7Q":"6HacXVlzPeOs","cqs85Vyda5Lu":"lVpDMxGwX2mN","81PjVGwjcG2K":"ZnAMSNt8VB3i","DwLFtPCit8W4":"ytdQTIwcWetf","9F6AjSgeEKAy":"XdY7vNTLjdFf","Gq2vDLlUNaBi":"vXlgpe42z0qJ","mrFqUsSo7e4d":"JyFGtmipHufv","mmBLYrspCTO3":"6BQTSexJXVkK","ZijHGlxb7pU2":"IwEsEiDJMT5m","ni4LTnwc7o2l":"PqU1YEoXNGW1","acYxnVZgudlW":"oNoiDLmpEdqV","xRd8LTOEWYb2":"aJD5N7wFwsik","bfwFuKii1yGW":"tijIth3y9Kqj","qYA1FQVbVOnW":"FuBFy4kO30TW","BHpFjBnK90iq":"lP3pM4xs8Of1","tKOcCwSzxWQY":"HmbLFCfp3sEE","QialCoS4KiDJ":"j2tTmPJ4MfDs","jOysU9OZjbgk":"XBk2DVo2jNqQ","KhQM2KoGsjS9":"WPphEwotYWgr","vxhMdzZuRxaQ":"WvIA2G88weFa","HZOfsArYhdOC":"TBIxPxVDvXuq","XEanxMEDFrqn":"Qasy34z7C1fX","2yS0z8JaZnuC":"DRE2uPL7FhDe","x8nsWnf62pQQ":"KuWsc1VD1ur4","E6VhrG5jV7bB":"ayUFlLfbRtlk","uXOZQJL3kACm":"ayMGw4zxSN38","49liaIfhXYea":"7e0WTHDZ1JJT","XEYnJYlEymt5":"yNy8kDY5RWJk","DEaRKYJPjXkO":"AJqym7z4ayoL","dkLqAxo6RZSG":"88PA1vshVLTt","6d6MhYvU3rGB":"mYO6dX6IueVa","4YL0U4mVHPZu":"bmqP8oXviiuU","uorKiNIirdU1":"v7VORPHd4Gan","aQgOQbMBM2qq":"BJTx0zdbd12R","Xi9IOu5YvjlL":"eoEvErH2IIFc","ldxkjSnUErTh":"20UVaxqyBmGs","WFM46Q8izOA2":"oh07M0Xqmfz9","WyNLuVxbRa1p":"8R07ibAaOfjG","xEnmsUouDLgH":"bwwZR9FWFFTh","6EaztogO9Hse":"gANyVymQFLoX","c6ElDiGAs4kA":"0ohiDNFYzKkN","Rpicj7tprWls":"6c3X8CSgMrf0","aLx4llGzhvJS":"XZGC9ZQ0ATu4","3Esa7iFavghw":"pjXnaIEFTty2","VFULgF4XJLDk":"WRjczvHbIoz1","CVdbtvCIqDsA":"Hv7zdU5kF0Jk","EyVDuzg5Nw4c":"XiZ73uhLAxD9","EsYILx9T4lqn":"Y1MmLr1bJtgF","qNXeoXrCpRkY":"AZjrppOrqtJy","PgBPVQXIJ7bT":"Nk7F2Heb2WY6","IartgKNCaqr2":"XQbvxNd1AmSL","bgaikokAzY1F":"ERuePaOFte2Q","dGr6gxlz2vBF":"bQzA5QLy2hCB","Z3S5d3GjuvhE":"tZyMWIoA6kWh","tnFFsaR880M5":"BA3qZOXTXpqG","DeDZFkFhphMB":"NoB73zpz4rt9","WqLM3wfnOy3z":"SKTo8EliGBNi","MyWYw5DXggGS":"9wr5KYOVOjpw","rqNzxVBVpegW":"eDahcn5VMnwo","H8aRWU6Gt7fH":"PlNCKZ71sGuZ","9Msd8kmPRLhN":"lgmAOazmXnn8","dq2E1sOG2D8e":"wUOknv8oCxUJ","5h1mLDyPmeIJ":"A7igc69gYC9R","dmoPMg7yDqCh":"g4Enniaosozp","rfZ8ORVW4Ovj":"tNzXfFkAoAqW","dJuvfxhkQsvg":"eKl3PgRlJegt","yoTrQGtTrXI7":"7OhpivVn77Z9","VAs49wYDqfrd":"Fvsg0EfLb7YZ","tDkdYhxMIavh":"rHahH3Ek0krq","jHssQ0lC27Kw":"bpKm7x0PoDHf","Fyvl57qyOIy7":"grnyLBwwVbXm","u7YRslOTlBmk":"W2qqOiLISs7B","NaHrtEnkHIY2":"VXzu4nHmStkz","5sn4IThm3qLH":"4FXf57VoJul4","6nrtm2ziJo4h":"tl92JOjB4W9c","BdBileQJOydj":"uDyIByXItHbP","zQ4rPDeMdBFK":"WOnMfNWhSe9u","srL1pP9Lgm9C":"oxscW6DHQiPf","ugxsEbdgyhMo":"8KU4LRYIGg9w","Pr8zjhSJUKlc":"l19iSF724L43","6lFUly7iYb6c":"dr4kz8ZczWHe","x3MFigoQ8WmJ":"PwqKD64ieLJb","DBinZ7e5jvm1":"X9v1ziOfwYEW","PcVN5lgV8SPP":"6nZT8sCKUFEL","6oQaDlVljhiI":"rzz1PpmEHNUd","7urPWHiFFHLf":"XbPLV2LTzELP","JEHrbFNyFPRB":"JcZJSub1pQHU","xyx8J7iEeKJ3":"b87XWOSm98uf","a4dpS2hA5SLL":"zOK0Xl9LokvH","1NbUsWdtalXd":"2sUchJcvbzgS","3AHxAxtwMlut":"BnunQrqKq8Pj","sC95FEfqPpDI":"lArAR6msNYHn","WWRza70acOLh":"rUHJHEOLj5Hg","ncbYDAeumPAO":"a8FYXHiZ6V9N","s8r18GH90OTl":"ZSDrHDD84GWX","oRQ0nKxaHoQi":"Lckvrw9wGyM5","1e4yn5oPTn6y":"MR4Xjr3arSeB","gmeObxqVXoPc":"DFwGXH5ciXcr","OlIlmck5mcva":"vj5J22U9Wl7A","XmYTgiVpcTTY":"b9660ABA5cjS","Pb1zIpoQb9b4":"zwhEyqo4zo7H","PkMNu1nHdq0C":"ZNLZ1PtiSVPB","mDPr9vGGn55o":"NSFp2VP5HgZC","6aPuqlb6TVzz":"yMQ1nFPKzmkm","6G3LFwPsRdit":"8WVtztvINcFv","olD2qFGBlPyx":"aWtFmh0VhLZz","yR2StR1JzkRY":"H1A5B2d6MIAT","RnCG1ERNpwPZ":"31N79VkhZiJW","S4d4ANiMMM2u":"fJEqOA3ZX1hD","WBcYWLeZenRs":"lWlWRMX31dtq","mNGue6fg4rG5":"cbw5O3zGMy6V","3H4BFzB55Z2b":"Y9WkNQpW4c40","qfz8vXD7TsxB":"nfpG8s6tqs0x","gPqKAOYsjHJa":"8oZCRjDWY8n1","PmQfrlUuZNhr":"fvNDpAC6XCkM","FnVJzNLw68H2":"Xv7UJFlqj3t7","pxEsOjorydxm":"25fneb4KWdVM","ViOjGZqHKFlB":"ll01p3uMeEFN","RAhHAqlgOtPW":"jNEtyzAPDReb","OPF3BrGibvNp":"EnF06hCFhFoE","nP09Ty9V8KuD":"rgRMlADPQh2A","URh8CGs6zsUj":"RebSRAUmq7W8","q4YZ2FPjhm5x":"1UWlhyNUSFIM","LlTlxzkV56rQ":"zy7I6ZOm7Tlc","YBMoGRQ1PHQb":"uOy6nMAXBGVI","9ngAHtnp2ZL8":"KCQXhrVXl9ms","egVJtrIHmdMg":"RJHNfloGUQAG","HX8Nkc7S5NUx":"Kh15g8Lvqjxz","G4SAGoK4nxk4":"asJrhgACeb1L","GYD5fFOWzmqs":"0mv1K9ZXqxuo","U6taRngRFMCx":"MkvBZF8FHa7U","jprkGdtTK6Fp":"dtSGvzxVzLRh","ivSCkNYfCKXD":"7axG7O4ssUQL","PctE3Jw1Qeox":"rsQ6BFbJ966q","MwV0QsX9DbK5":"M8ozIYhT0lyq","e1u2GPOYsdPl":"g8rq5M3pBslp","DKdkL7WAkoMC":"vgw3feyAjmZx","EBsYycjbkGVB":"VkJvAPVOongH","oNn5Jcj1MH3a":"oMHUYHJ4bpz5","pMEVtkmCCV5x":"B0PwW0Pc0Zsw","Ux3oBF8Z6l1G":"IOfGGbPX2K5V","RFmcWLTfXAtr":"V5hOaRf1i8Fu","P2KMKYrVuKwz":"T7oWqq1erjUs","Aehwhggk8gku":"9Kg6LgMpzU6C","KyeuiG6hcq1C":"YVL0aCP4oSiw","9sfcAAgXFY33":"8uD6am8FbkUR","6Ev2VdLTY4WK":"5XtOJidRQzyE","AJ0pU9Ddqxdk":"KUL1dAzpKIS6","Vlwb6om018I0":"OE4IfCwiXmOq","iuUsMktFI2EO":"Zab75HcCfoaZ","ZV7ytTHxievF":"8vgdsdriG8gv","Prq4VXnMpnUo":"dsycxtGgD8Y2","nBWd1BITPGHl":"JX1EiiTxaN7K","Sr6meQYpj4DL":"sqT56D34Fdhr","4cY1acXgn7eF":"WHlDkojihS9u","srTEjLMQWzAX":"9R6x1dZLZ6jP","sIVDUZiqe02O":"9jshP8e8kS3b","o4wxKuaZAgc3":"xXlVw33VopJv","Y8NASw7vCpDb":"hMsCyUYT6D2H","b1VMJ7afGxI2":"WDVhjcvLht4G","IhPhjTDI3csT":"HAJvac6W0peJ","mSRgQX9Vce8C":"Bf5TaCbHQBVX","7Sx2lTK1eIbx":"fHwz7cDDWdKj","8tshiSkJB5zw":"TBnz8wvcpThK","U8UI2E03BQd4":"66UizDLSBh51","dewNVy3TGxPH":"ZpkLO96E1DUz","rq4Z6YWpcqlR":"ZOp0fKuxwxVg","xUPx7XXsZuq6":"Q5yd83wNiaAj","VZ5WIlF0OgiJ":"sRXz8QG5xTH7","j5hEIveZZ0Os":"HO9nLxiaxSLz","Uc6Q01aOnJnb":"sZwexnZ6GPep","kHGczmk3CTt2":"9McC3VjgRiyG","IdHCfflUjgTw":"vNsRtyo4gcqe","gB84Uwgsogys":"9x3YqGu1FcTA","RhpJnxhZ367C":"swpGhkuuPdhR","zcYEktD45HA1":"2dGB6FT5tstB","h3FlbhmOaS4G":"SzmF69YBzBb5","a90OJblj9t0o":"La2S1V5YpkuW","LJz0JmHX4TIg":"Csz01rJ2sGFU","09XLeOjG90m0":"3jPcoi9BQ9HF","avhBJR5pgqSK":"dimynURJI8ly","HNUGYHZVJnsO":"7ymollhSGhti","49ngxxy6HOIN":"Q5zOkgzOdjhL","AbBVMARTTu5D":"9iCZR4Uw7Qxm","hwkQ2fZ8Nxa7":"pKL2zKaxJiy0","bxWlE7IDvkcM":"eQH67FlfVJJE","7bJz9CIDvQrE":"grgX6gl8F5eV","Y8ZLeDJH9CH1":"dW4J7BCarQgW","f8ynMD5iyn53":"lDZiAYHmsBLX","VQpDQXK7SfUt":"apFBmoRTqa2R","WFxSBFnFJFTR":"r4an6XVwN70d","fEWRegPRYMso":"2iQCpBHojB84","TRM0gmRqJONq":"5huCn3m5RibF","YDV0DFzSDJx8":"xGZBRCBHGJ1t","JJ9FatqldPg2":"Tyl3X8zH2A5U","66zo6yia6c32":"h8TDwQtJCUIF","gcRx1ByFvj9K":"5L0ZvoXKePC3","q10R1K9L8hDt":"xGeu5BJFqsGU","vK9TEVDDxkqN":"UZoxNKQzaWl0","7WdzDg9zr6g9":"GpK6cByGs0AZ","bLp7x0kSXH21":"lQWP97zo1Buy","KV3uIyUeqGL7":"tukNB9MH3ib5","Lw8nvdIzOaWe":"9wxI7BP11fZF","O1VhFloDx9fR":"h9gQPpOEWXtW","JxwI8ftLFsAx":"pbclHN6e9pmG","7IlSeNmny95I":"DrDpuPiCBvFc","qKLUIcMiUmDu":"fOBaUzDSrF8D","LxD5BGc0YJ2Z":"08KnxDbhXyf3","XGWx562L3TrP":"L5KvgM0IDIhf","QBughBYqZLCI":"SIMyXv0Tm9xz","YvGb9jBxZpFI":"9Si5WB1i0Wqs","XiYXlsKv6Mai":"uioj6vtk3ycc","uj8rWq02poE1":"Pd8jKJ5NGRO7","8TJAI5IeIbkt":"5E4bHcY4DTV3","uzfxpKUR6gvc":"kwpj1vbdZrJ1","7zMKOREOmWa0":"tJg92n4JRFkd","vonKlyB3aLv4":"IxwfQWvTlGCi","F4fKfJYya5oy":"7WoeV59eGnlT","B2OWTn6GPLjG":"f8SP8njVWtJo","xJANjdCA56MQ":"OrQ1UZbt1FTN","9xH1EoSGWrAo":"EsD1dh9arqLd","2OhcnR4pbPlF":"jqiwH9fPfWbM","JTEsW9ovBtrk":"EM4Rhu72RmhG","pj0gvMs54Kx5":"jSomF5yIMNEK","lflY6iXY6RFZ":"3R9kMqyXsgc0","heMYM9V4PWA9":"Ku2BQtR4fgSN","tUh7Zrt5Gjpu":"TDCTtUEORnhs","wpARdiMwp3ku":"rRsaKmfSKwS3","fhPRF6DPf6UI":"164vrbnB9QSG","uV3DGu3b7CU0":"pRGyCryTDPeI","bcUXGbv7rU2S":"Wq0p7woUQfiN","C5GZFk376fYd":"HS415n8EP75j","jXIFt9OX2jNF":"Xamld8Z95C8z","4CGycC5CZHVO":"VvdRGPBBRg1D","J8aHK6KI5SXH":"Ea5DMIh3Lv25","3j2SyeiU2u8M":"1VEYAYik6TA6","0CJTYtUZZKFr":"HDLk27sSdO0g","tSdAa8Stn0mJ":"TU3MCpS5qj1V","RWXCxvcybNB9":"SEyk8LIXVtaz","qn3LmDZDqRXU":"ELEmGDXegcvA","WWft6lqSSJIp":"prg6UabH6BxO","jk87DIuMZAkv":"uuFGEj7hrksC","cVrosccJXdAC":"MEq7SxWB1KkG","hvUWLISAngli":"NA6De7bLrRP0","Xk3LNiQ8vcso":"YvTk1dKMbLjF","sxJr3LysZ9XJ":"G6DZnyhtg8Ff","TylKHX2KHrsb":"SdyYqTvr1E9I","r8kVESBdq4vS":"xM9PjGRdyVnO","Ow3oWYbnne3O":"wL4LeUueIZG6","d4hL0F7UC6f8":"e0wJROI8SCK9","Lls0NPKjs0aD":"hRNK3mRfz9qa","7S7cRHgmOTBt":"VRRQb0l8siy9","KUsuhjDySGWH":"GGcagdifqIR0","Lmz1feTbSg8n":"LYzoWsO00spJ","HgvtuM6cuSVL":"jy6YXU0ATD4N","aAyrhpWZg9ok":"Ve80SbolDQHn","D7SNLMn7cloR":"EC0uBybiWw6k","3CflSSsmTLoZ":"5jyoplxECgQF","sJM30l8NXCDs":"V8XrN10g0hQ2","DX2i1ifwIerr":"Pb6msMLw0FVi","9qSL1YQmf9eo":"sWwrljSt5wng","oelVqqPdS6h9":"AsrAaar2BTM1","LCGdRHotsuNo":"5wLr8UPxpDHx","z7XZ5Xzav73i":"O7k9OB94FteZ","utE3bLVh8voO":"xxu3tbOKsHgq","kCIZe8jDMXeO":"lTIDtyAqvzri","fpsopDj655O4":"F1ICp3A45QAV","Pvd2fzZNkyur":"SmhxNnY2A36X","QjBUATvK4v55":"adRViD9KEZNm","yUiuGbM5zjWz":"Ckb6QzsEMWw2","16Pw1wLBhIbq":"JdsQYDHtC3hF","IcMuYrcmPb5q":"Ppc6gGmSSg47","TncMuPFvcmfm":"Ew7L6XlZaUfV","hi9eyqVAKO4S":"zK5ohYQGvuRP","RgFJaT2Ibwab":"yz8zJnAipyEu","rtwdjtz6pxH9":"w6cQPj5lzUls","UQXdu6m2e9iG":"c8D2hD6U0HDk","oB9HmX4XZ5Ss":"hDPbWv82ITxT","p4dht9fUdHcT":"9nQPm0LL2uyL","gTCprTN7x8dp":"ddhhRU0bb9D4","XkBIboTMDM5g":"PLdurOery8Sx","m3FWO6ceo3bu":"P8eLf9QGhMCk","Zqo0Iwt2AgtO":"sNwdgidtoXoQ","VVpngRAqszw9":"aszpQgeRIhM9","6Vl3waWrGqza":"AlKCjs3tWtcw","XRyFp7RmFZbr":"zfZWWRbzfBGo","YTagGovGHFPt":"apEMlS48ZvMT","BjEHvUvhlRbi":"iFLmrbFcniJm","p8Fu2t3OR92i":"FKgGBAUOSnMZ","wO0rRcJiv4wc":"UkDjRMzWDWzl","BdXOy9CLUkKU":"h7UH474TBOIJ","f6luVDLeSRip":"PpDBxk3a9CRy","64Ag5hMBcqhK":"gfqCWY5T88Bx","t9LBjZIBGuwe":"NtGPyoV13LsI","pGvddoip2NH8":"yj8XRM2t3Q5Z","DFe2UxcdsaRj":"Hfkdp3skJLAb","TuR0LdmdxH7s":"Rlzwd8mFLspI","hP6cKtoIYwG1":"ZtZa6CG4LRYR","SeVFr7luhFxs":"qJkLQCK2Yw1L","Pn1JAsBFiHhD":"tXBjFF1ccLJS","xdinY6EyGlfT":"DPcWf0Jm52Zk","nFR0syq0I78w":"cFwrnlzP5U30","isJGYy7CiZGK":"V3ij5emyNT95","O2ir26adHWNw":"6wqGj6XUdkgH","ASfwJasAsxgF":"WmEbyQgmAP0a","5sBFzsm0SWKr":"cLQqdkRaDR8X","FHILITtAashK":"MBBvG7MgXAyi","isSAgRWiW88p":"pUv7bTQp5ubS","4o8I6gpG6PRC":"fJdOsJE5OCUO","HNuSQncYL75n":"IH8SRXiqRKab","jVWghAmvAWOg":"caW3FZwkNz3J","lit60cWpmqQf":"ub4p2Q0Fqdn0","nv0MfCAiF4Ja":"s5lPxGN2SEdU","W8I3xVNKx6Rp":"TBDjGsePsTkK","P9b4TwGwLhwz":"sKji9lomQTOQ","V7ZbOQQvV0gk":"dB9Gv9Tup9tf","AmpcgEpT2Lf4":"ftQ1apXSpDX4","wEuMzM3f4RGU":"KUETcDZ41UVS","W2GccSdUJGwg":"loYVt5adS5tY","qFVUsRjf6KUx":"Jyed4aHUNJht","hzqYKpFBGe7Y":"OP2aCe1kV9Lj","JCKmZLn7QdWf":"dnMVcZRy5tzd","3ZQFn6IOtq5b":"WRha7Z2cQ5S5","fxGwYeoDKS2t":"lcuCeEqG3Xg5","NJ3F7gZUSij1":"rZlIyEmauTTc","wJiGGaG342Hi":"y07HAVHUPkck","g3zFC8wAwI5x":"I8EyqfBFj8g1","cCoK6INm4BKj":"JbfnkcbQgrqp","5MFvioTJgiKt":"fdQh6qFIMVy8","ieKZ4PHuKxTN":"F9LrRGYmZwyW","9O9zEwEutpn5":"mbFOoTN8KfaL","YTzerpyQRR4A":"BRUkF8usarge","Dx55jLfnyOn1":"s7ngBviJoYk1","wWK70aaAYnI7":"suV2YW1ld4XW","6gC6TrY8CLjj":"fEI7mieyXjUI","wBNP9oZJAwfc":"WylBpXRVTHQN","ng5mTGCNEPvk":"4K22xwoSUvQe","RI7DWIwHAwxZ":"KcgjjvffzjTb","45cSArOStzmi":"wioXf2QKZjml","t67WfQGyie8B":"DB8gepKFINBb","SbHlIqKK9IpF":"ywWKPPMZLY9A","BTvDFZ9hESv4":"0XzFsrX6aSYl","0b2nZ3LK43ca":"fMmvrjV3J9vi","blcKMI0xrjfG":"syi7yqiwyT0v","nC6cNhl7cFEP":"sUg0wYcY2Xkr","H9yR9wqwr6ZD":"EHJIiMUlGhN8","iGWex8lV3d4P":"8m8HOeJPFCih","FCzK8EHOdwWp":"cXcX4ltG2dQ7","2iW03gCGFWKX":"QxDqI4YZ0aUO","u8I03d8pwluP":"T2MzePYJJSKb","M6kKi1o9oTZe":"x5HpZNraSINj","uqFn62NeEle6":"RYGftLj1Q3ta","SyxLB2IZXbMa":"xwmOHSxH7oGr","qA4b7VtNtYzR":"rMxhrAe2HOUw","CVtHdMYbFgwb":"Z5GL8AwDwMVa","7b8Ow3oIQE3o":"t58mpftjdmjy","yHijkdn7nEoe":"IjbK3MsnmEfs","gwNFdLSlheVi":"tIQ9yFI4mFOg","TNHvNtys9XDN":"0XNyDWinoOG4","1CVSeRU5wVy2":"tjKU3QqsUX7S","Hp8mORwLKyqn":"EQ6ZGx6xe2Y9","V42pTTQ2kKL8":"FBxG5eur5Enp","kBy6NQKOJwf6":"rUZcH4HxLlUN","d3yD8BT8uBvY":"xHSngORg6t40","bDdP2M8QccAE":"7Pg2HdYfgka1","DploDWdbvvlq":"nyQz5DkkrIDG","koNOW2qf0iys":"g5VqmlSnHXCN","xDYrbaedlF70":"i6tGriHwoAY5","uLizdFRj2M6G":"1Qwyj501vZXQ","yJlpLCXWSV5e":"Akr2Pdh1pPsX","rrru8o5u5Z2h":"QdcIT5gbDNr2","K6KvCJf7R6ML":"lsEHsd1spii8","elvv9Gimkamk":"XEMPqn7ZuwMh","plYuoPNRzkXg":"bsdN3bVDNrYW","ampvBl7gRkWZ":"MRbyqiQJYsvU","YYp739GLKRWc":"NHjhLg0Uf1wa","VsLpjWyDl8vg":"gx3MJBcYRv39","bFoI36ASf22J":"jvq9UCIEctRd","u68qEORSenoc":"fLocyFfkautN","xGYeFWoKteG7":"ta9auENc92E9","UwZFp0tAeVy9":"7j8AEhfpOKP1","7OugKHwuzdVY":"abPxkGeUUqaO","Ch18ugyvYhtS":"HVp2P6ddzs8h","J5FDhelmJpP3":"pEQ0TzefQEXb","v8bf5rBGiCut":"iulNIlOx2wBI","jpvcsPxuM2Gd":"k1o7FvTJdmIt","yaq59qxj9sWq":"9YgowumxQl7r","baAPuSQBHvWt":"sjWk0Xazheku","88v2eGX92FDi":"Tl3RcjZn5iUO","1tgLwh1BcSqw":"ncKp888HIrex","PVD7LtuCzjjU":"bEodpzvGrnqo","3x7fNswotVha":"JhSM81mUceWw","JyPO3pFQJ7tV":"nLOm2TXrUrZb","G40Rn1hTOFo1":"WRUtAqRdzWz2","XDz0SKnrkAPk":"SZ5ittIACGms","17s7g5B2dbfE":"wmD4C2uU4FNh","ZYQIvfifh7Kj":"fRYzo4gVRmR9","7UYV15aTtYjD":"vknI6vOcagTj","kkvCJLtE0JQR":"awzhiCFYXq0A","NhEuK8cvpfJ7":"j8XoUVeuTyjs","zpis3iS7dSiE":"lLoDf2cCewKG","m7VfO9eoQtcX":"lMPDfgB2hvFG","4DtFI3DAlhDl":"wJTJcodKNbDK","k9c57YsQZgIV":"49s1ZsAPpSAf","GJB68fwNiAgt":"Al6PMfFfTCMx","ACr2W4YNXxsv":"Q8GWZpuY9If4","WJRrLXGsr9QE":"ZOsT5oyXtudC","KiIr7QbND3BY":"xNu9EM1MCirv","KNm2xYTKbIcp":"XStxwufA09FG","F5q1TIdgEmZS":"2SQSkUFIU9Zw","Ql0DEfUXwIxk":"yUv9V6D3hgum","2PrA1IPGPWud":"N5H6zIg5SvKK","kS3KOBREGBfw":"2ERrV23RmPIR","6Uk7pd9rsuOZ":"MwyuBiFBA9Wk","j8VylKy3UhDT":"iwLQZF9yZcNX","zZ9c0lsYVspE":"UFRx1hWNxJEa","JWPawQlAVs9S":"Fydx4YDpbNp3","8vEh9fVC9CcN":"f8q1jyoRwDzu","TbF9rgNbnUA9":"ESHmdflhSoco","vGD85SiAJSJd":"uqtxNXhkii1c","8lc0YKWWCN4D":"C5GbCyygtkNI","4RARHSd4MgfB":"CxzQMxXk5BZw","lnNP7QTl6EGQ":"61yHaYusfzil","DQjKsh0UMpvM":"rtyDcTyvwVl9","bRp1XIAzKMx6":"7kIde4T2B3zZ","9gQXokdAIVyi":"WxuFrON186I9","rnNRvdAHZAZw":"CIfnZH22NuQq","1TSVl58wsvB9":"5icfbq3icWyY","gVBzk9oA044A":"qDEio1qvrqQ9","wpKN4IohteVy":"WhU0ZOI9BMd2","Yqdhi4b6B6aO":"ictAmIDme2KJ","xNvhHlCkItlE":"Kr1X5V8bF9Yz","yxWisUmdI2NZ":"P2FylJo35oBn","IO4ZtR82gi4F":"b6QEMhkl5WWt","JJfnrQc08eWD":"wulC6NpHRmSJ","LjZSRtRtpnQw":"rRHyBAzt5HRV","yqosNCqa8KDv":"cR0vIKby8A0N","f4bPLai11VdL":"4k1tGNbpE4zI","iqj6efZQVTfb":"dDijDdCo8Mbx","NKY9ZwX5uSYb":"TNf5qoG14GAI","iu4YstJGK64J":"I6yBKghJUUyS","CVOUd4bZy47x":"7pdw5tXj9p6A","2RoC7I1OW9I4":"WntTkzkasjiK","ZrURPUDYwRuI":"bH3krJ9hCa5N","xCZBArRgefcY":"koHcgzIlOdN5","2PwaYWjB9Di7":"HFePjOVLtXNc","PSQvzwqcEEuU":"a1iliK7w0Xqt","R2CQAQqEZMyT":"lBW4DtJ1jV4K","A43nV1fC0afY":"UvV691Csvf7A","q2AbEUbEFqYS":"yjDOdL62LwBh","N2BsM4zZZiIg":"AQyblxkR64aK","3jg3r3a3a9Eb":"7teUIco6gYdw","MXrTsPHVNIiL":"DxXfECGtryNu","UdgtSzHcne0J":"ZgoOOoNFviW0","YdFl2HesKnx5":"JkxO79RD5h5Y","W2lGdzqHB7it":"cWXlig4wWa0f","8ryPHt37Kzec":"gPVZCjvF7TT1","TmXhVbWPOKB4":"B9CqciL8ltf3","4oD4bvjBn4Wq":"A4mq6WjlnCz6","fifhT0U0y150":"ZAa3iGkjwKZH","BzFf4oe1YGXT":"YuCmkFDL80qY","ifCd4FeVg1VG":"Yh5BtlpefKrJ","juaFYLYrqMgD":"TJZ7n1YTarWv","ek8NjjGu8r97":"n9ZRf3PuUUbx","fvI3Mrk03bro":"G3x8PQKjUjJk","LUfItmR6k29d":"WzUBOkdsTAIo","sRC9zdDCFW8G":"n3pTvuNHAjkG","s55lJWTIejUV":"KGDLVo0ScE1X","xSWoOygutCMs":"DffdJulBeILZ","tBbqq5PIqIjF":"ZOz2nSPYXeiM","ueUXS5LB49ev":"fggLiJI7KTLh","md2L8WBE001E":"0JzmWSGB14IW","I0wVfy23cCAv":"EfORg5bCP3dH","oKrmzY7upBIX":"EBonRYxoaRAU","HAf0yNpdc7Me":"0xqwkmz17NuN","TjtZdUpIwVE8":"Unb5XHQ6F1KG","lIBYz7qBc2dk":"bBMAoVuWJJf4","FMdAIySLv44O":"sE7diooQHg0d","zjD2fCMoAdXN":"k08iGg8X8jOd","vDbMjrVApq6w":"HgAOOwVn19UN","RgkFgQld1PvC":"uNDIfaAbgHzE","IpqCO7Elp5l0":"r5GHHT3GBA2c","Gi0GLKSbJAlt":"TuPY13E7EQb9","6xnJcxOAOo1z":"l6U8UEhsIJxC","jnJaDXk0grPG":"FxwFZ4Jwv7EB","9nP0Qng7HfOr":"l9p5NqY7ySfO","wSYzkUcHiNc1":"nR0hjKrQv2Xk","aWggnb4e0tfh":"uJ3KRF9AqvJI","O7pIQcycrz4t":"PQ9zk6Nnco5g","WgK89VA8yxU0":"phyYUF8QqMRX","lhH4xMQd0Mei":"eaOcceqSBfBx","tZN4YdqLjzcI":"XvWQhrDi1uIe","UmZnYGNfZ5Si":"qXdB0ERhapFa","oAdhDqfYxH6P":"C2ObLXqIdra3","JAj7iXvcrlcJ":"HVtZkn7iw4Be","ls8N015gr3vt":"Vs4tQErmhxyH","s25pHzS2rlRg":"Yh616T7zfawY","I3jeExgH8GZJ":"yH8nN1xLvJdK","PJfYiqQTbVKh":"5EfQ7DGfrO5L","eZNe5KzMHKog":"TTJ032gkV6U4","d0zhd9xVvxdw":"PgaOfoJ7lyvF","LLeYXbpinfQg":"VOkRdQbIwTb0","pIVkUvlmxwvG":"AUrw7GHe8IqS","9MVi8pScjmn6":"6Rjf8pS5Y6Z2","wGEfdbIFz8z1":"qUfLBKKABolu","WWL2QKYUyEXq":"ANPUCh7WO6LF","zBjfxPre3eqc":"ZcVL4RG7GjJ7","PU9QNPafKnWM":"ZsdHQNNDNaXd","6jKANexdWqaY":"Y0qCqMyY9ASc","dymNTutYCXkj":"MEggthYFfvw1","V78ti8CgS29V":"lCFcj0SXsNlR","oW3a0VC5TfvR":"kIaMVIZlh9bc","sGgwhRGWu8Mx":"Ez4PTlLbfxHw","q3pIwfLXL2B2":"YkIC5LWlW1Nj","tqBzB3ksrUtH":"jAH2lRCUGA3T","t7AhTDO7HrBm":"nGiudcIDC92m","xbzmHiTdvoLS":"Ys0l3zKOlP57","1SAlRpCPAOCh":"TBRga9urISvg","KygIb6cik9uT":"vN5EQcZwxDCr","f8674lSdZR7f":"5rMDXtJUM077","fwgAjqHOEQD1":"5p2TkQOXueuo","e6IBmaGuXZXJ":"M2xKKcaasWCP","MuQkbwfTeRDT":"9DpvZAXeo05A","JRQe69h0do2P":"KRCyb9NAZMnr","GauhqOGDWySW":"8zy6KsQU68xY","aQiGf2ND1pAH":"0qEWIkbCNJMi","RbO4koSf7HlF":"exF41Cvrcuyc","sPTaPX5QT4Y0":"pO14Hhd1kwaN","whrmd83b6fOz":"kHWIiANAyRWR","HS0n4xg3f15a":"LeIlXaH86zUS","fKeZgdYYvm6M":"UpE4A6fQBZdd","YRC4XrvczeG0":"O9PK3SLHs6iG","vuOu0dWRoksp":"xMfKMo2816DV","hhSdTSmuX3fG":"mGHK8ECXKC54","ZKiHI6SLmDJn":"4bQEXy45vjqS","JRI4PQUEtG5U":"WW1ylnOygSyj","F7WuhrsApTSu":"ctMeqzkafMae","P1TNY7uu5RDt":"wbJZkdqhVqyy","afuLdI2Xb6Ut":"le1nua42uBO3","K1r9SqbixUg9":"EHlfy83xpOHg","WNVnr93sWDwA":"ptaelVpxrww7","kaCdcdNbWrlg":"CB8wAMAWODA7","ljePiD2oYL8o":"4acoRiNAkRa8","e0IeKlJeFcYF":"DgfrEQJWLQT1","Rnbw07W71sYU":"XJ0PYxsi0a2n","wxt5lZ5cBuiV":"Lb5OV3h0ft5I","uimDY399lVaV":"wG3HHDa51Wro","jkRMtCDgpV8S":"HlMdlJ8fKz7P","8qP9m0nU38Ds":"KpbgBWb6ybBr","umFpFssaSUN4":"qBF3nymvzEga","MoFCv8AgCJ2c":"AWuJLdoV2hZh","X7havQblqMjx":"fS9NX67GUQjq","5zxvZY4mimqq":"dVYg2jSJFRCM","fY9th4CylkOP":"v3wm2Yy4cFvG","tAH16S6i1eoW":"nsC0I8FtWFqt","j6Fk17NfODXC":"jVXRFsKIXiaG","BCObBo0GXRxF":"0PPgnFGYHUp6","9pjnYBlS4uVk":"yJ273BR3rA7u","1tx6PmF0BxJi":"0qt2Jww2I0Sd","k1piJ2Ssmrjy":"SSJUv87pG7OY","y2Ume7WhViXq":"f1mP4h2LZZCh","UAWRNkyrdQso":"eYg1LHOMDVFh","ppsgkmAvpULe":"MWpDzokUz9bF","nKbTlJwRWVLi":"k4ahRxbrbT6u","B8toAGlV4ZxZ":"uWezCQa24D8N","i0ox9OAWdfV5":"NM7S4JBe0eQf","F5eCJm3q8rDw":"eJuxZd4m9qDj","y1hxYK58h1bP":"Qh636aZpwJE4","zoOUTZkLAv5v":"4dDqCtrwcm2T","7zOa1OURT3VJ":"3i7BN9fgbdKs","DtflekiPoiaE":"QOjBpRvKes0R","sGQnaoxxOuE4":"BYKy8v6PQ0c0","6xW3lqAkJIhG":"rmBvdvuRhHls","wkrzlByCzcPd":"rcigCgnk2euy","c71iMivQWA0x":"1YjPoBVhR8Hd","mLwtWY92elgC":"iJUbfY2aw8Aw","lwZajmHxBjK6":"iNxxRGPPmSqA","ryErxeZF8YhU":"Yq3dKTwOJkZw","ofNLFMx5wKPp":"8LbOLiIQZodK","aKwUkf1QYlPS":"TNqyRnOZx7bW","Lqkke7nCKLma":"eBcyuett92My","Pj0JqNIku7qW":"XUd6NPU7HgXy","5C17maE0ehjY":"wGvFqXvBBEPc","3vLEaawuB2M4":"g513fZ0jSbdc","Isveco8aADna":"b5DH2k8A96jy","ms33wW8X54nT":"QKzBQKEGyjjW","8ivSFPLNqjfF":"01xYcfPIB230","rjx7sgE21Ecf":"ybnIp6eLwCG2","MmVMfNHrQqiP":"naP9ojgH6BJw","Om8yzfbsMVPx":"wA846BHKgL7g","q1PpjbJtOjZX":"hTdFTFw9V0RX","v3BT2h6yj96M":"i79oQvbQrwKh","HSZCByGQAdR0":"LxCGA5AsnF8x","dwKLVSBEOtng":"c6s1oC3XpolJ","3PCsA8bi9wdr":"kk7LQ7F75WWX","oTecuz2FsflA":"xfJVn7T29BZ6","Y1vvajoDQc0h":"PrTl1CmcTKlX","a72XwzaHZyI9":"2cduYX4ZdoU3","dVDZvAXwNuQy":"9Q1r3ehjCePA","nhZkDIwqdPqA":"A8gkBLz00Jkm","N3W8glAB8lye":"mJCdI1t2y0CM","iv6chUS6dFJj":"Al4KeI1RdRSG","w3uKWG6Ak6kp":"jOYc6qJCZ0E2","9llYvz7dWGUt":"RrhbkmwGkUxm","eFfmgUNJxaXw":"KZW2ZjFAjPX9","H3Whj8nTWNLd":"vlMr3Of1PwSG","Kz5yORhImufg":"6HlRw1fZAioh","IWI08cBLbpPi":"LCQKQ2N7pB1k","ILVFWWVxmZ7g":"nHD0S2Avzbv8","u3jThWwiEVlm":"FshKy2mFkCC7","fks5aEQPa0wS":"pmYUWVMI7AHM","DrFyH4CuSX4e":"KtX2ZUplTjZc","gTyg3axFt0JU":"31bP6yR1NMnC","iW86EKkat50v":"50nHvsOu8wzU","QcHvA0Vy4AWN":"mutC5jACh0P4","VnWpy5558vNR":"cobh8zrcm9Je","3LX4adSqI56M":"63FfbO9fHerl","9cHUZj9WAOAh":"yzwaABJ2OG28","MDxXKSq8lvbX":"T3bPHsKbs9lp","iFjgT0GM0j7S":"yEIKfnVMiU13","Tv7GeVcDTbbw":"1oOPAVmotE0b","mz2eQLUPQjih":"NPY86pvHq4tn","qpVu1MVIZkzl":"ctquY1Fp1GRg","z1vLNXfyzneV":"SOifcMf9ZYt0","24bdVWUheDVD":"z6YINHQzUZ1F","6V6T47eZFKna":"jxNLY1MXEAfl","Y7sGu980a4Pv":"6ZLmtzks1BcY","OUw6kZrNNFTI":"aJQ7UflNuOGN","sbbXNLX8YR6a":"8onCFQCUtW1D","8UEia103WEhN":"QgFkOI4L3XpJ","NXMZB77SQSCd":"9DTVkIwSFpaP","A7KtbpHTftlw":"A0PpNuRxxLCn","sHEizuNi7QOs":"zuw0UeQeVzSZ","h5OEz5a5JKMQ":"H4TQdvPu4PIc","cfLnGZYXvteg":"JGPXEkPK0UDU","Ece4s4oRZ0sb":"aT4t31p9NyXu","LaCMDJvbcB5S":"8TOMwxFOPXKd","WAgEN2HYiOp3":"QXvingcPRofs","nzC75zHlM4tE":"HFcZ6eGR8uP0","0b3MMu2re9t7":"S7alXC7KzAop","jmJw31rJlvdk":"u5dKoSAWJ75x","M01Q8PZF0auE":"mzNoYqkhnEDD","EsLY39ERP31k":"uBFl2lcGjUmh","TKeQo3PNBU3U":"tYjzKwo9j1jL","xys0KGHyUi1S":"gYS3pvzntYCT","LKxkTcjddHxw":"DjS3JuGuA0z4","vDTfHnjYBnga":"5mTjbrJM51Ld","u2nck9VC0fqZ":"lr8yiYqlFyDG","GlHOcNwvRzj5":"cHlORxxQx01D","OqG4zVNfBxf8":"tHL2aZIEyeHy","caIx813gLQAg":"aNF4AeIY6u52","t3JhXEnJ2oKw":"tlKtlGFnzIB0","yElswDBqWcuL":"gO6Il5kgYpXV","CdODEFzjJ7fM":"5TlMZ7v1NnRK","jfHXvfPQrFhk":"FJ5waLZ3b4rq","tlFdUpVLMXSV":"a1TIwD8y4b4F","mFbggaGWdFus":"qCqyOfoK5Ygl","lyW58TRk8k6o":"r0x9aYd73qii","lHsBMW21Jcjx":"zaAy9znhUJvJ","1hDLPEErLEh3":"lTMue2EuGlML","ZMgSLx0gzizS":"0YS9RK4OjI2x","pprvtcwK7WyA":"ZMjryBZQuXI1","cm3pUUuoo8An":"Rxv9TpLV1JE7","oeTUwyxhp8AH":"HlirLt8ituo6","WqrDJYF7wUs2":"UYro9ujopuqp","7dXhbsRDuPlr":"lUcZyM1B2QMI","Hus6yuhsjwNv":"ZOSjYbq9HZ8W","j8L6qhxrgbGN":"53kLMB85k2Ok","nB9dXL1jgd7B":"P5W4oqnkQqud","8uIg5Jpllr4w":"FFzaa8hJW6zu","DuFOElepTNtX":"vpCy5CucEntK","Q9HiaMfegQMu":"KUjEgnIOBaq6","aQ2BjKLFPGIO":"IntTmst2fCjx","tfwjcuesWpQA":"ajoSYtAhllkS","ZWWOnTngUHP2":"DYlTQPEx64QI","9CvuOELmfFB5":"7eRVhYb60F4m","wW44I8m1M6q7":"XEhlf7nd1cDy","OWHWMWkqUBK1":"SGCTN1djaZqb","3QegZSXyA2Ct":"3eL6YoNxiEGF","XPv3Mm78Uwpb":"EorVMhxPoRqv","nwLMqT3VBqF7":"jjMjUGj4aaUu","f3NBwl0EBZ2q":"7iQZvxM2NIUD","LoNB8PWl7se8":"fTjBgqEV55h4","sW68AF2ux5k2":"vgckYfARqoqe","GcBFDx9ODzqD":"QOsNS3mv2Hzh","tSgk8MqFaMq1":"zTDax8fKmyWq","WSwWH3DfRJqd":"l3zK8dRGFKaL","XWVRyu5tnd6B":"4MxtIa0zZTln","hsy1VW1uiIP5":"7ByzgQVo9rcU","5AymOqAZZAWI":"38eMI1l2uf7I","N8NsdU9tR9b5":"9SdcVBR5BlJR","nUYpNlK2V5cK":"mREpsYMfYg5Z","D4FkZs9fcH7I":"XWqB1Asv3CN1","QFdJ5aPAoz9A":"crLgxMwOKHsV","PoSur1oilIol":"brMb04oTgNK2","jaPKP9ayFaXi":"SBPPcwOICe4q","phKSLlYX3qRl":"boiPtqqNiRJh","EYRW8NX17Ftm":"6XH0YGbTpaCs","00ucnAu66TU5":"kgsk6rmTGbj1","Sk8fe5CZxJI5":"00XqJMfKxKbr","EB9JTGUQuMJF":"Eb1wZqoV37eK","Q4UwniHsNPAo":"lIdWLCIaX7L2","iBooHAgRW6Mh":"vqpxUS2d9z7f","tBpOFTvrl1LV":"vRRpEtwfVF8r","Kf3gq29Ssa1e":"Uqg2GbVjk4gS","Z5BMoKp8qCl2":"RF0B4vYPcutb","QJxRglMb7HPs":"1HxQNEnnOtgq","q9kcHfNh0Fpd":"oMiGXobfMxQa","AFnfbnPWDZir":"KliQlq4LpCb8","a2NQmVSVs9Gr":"mhs4pCCsxxVD","sB8Gfz1SNk9C":"X2Hyoq7XKQUH","WCg350e3MHOz":"h6NBQ6Bhamst","sr5UTQLk26mM":"67X3wIku1qpj","rqT5yn9SzSwb":"5RAoBsbbfYAz","Wjo8fjS4XdOS":"vphCVKPe3NX9","vDZ3N8XP81x2":"R9QPzAlWdDHp","OkhWzwXEh5AX":"j5JoywXiK5LQ","Wy0CrmBKd8U2":"rp89g6v6u1xm","Jc4ovjl7Iz6s":"T4GBJzsVXF5a","hUQ8kaG5QaA4":"oYDuerSX143H","ImCFUPdAKCAi":"JuvtsFiTfX9x","PQ8FW4ki4y8R":"c1RHKGQoSzMM","xlC4QWB4GZm4":"1uUOCq3CwMmG","Ftx8piVUDc1w":"VnD4SREJXA0Y","5vuSqv963rzX":"a7wJTszZnb7B","q79qF145Lipw":"CFmDMAEm21Y9","nW5o2RNk7V3E":"DbkgHn05lty3","jdE97tXgDdMP":"zsrROcvhjmJL","c7p3ZWnA66Rx":"rzRmePfff0Dn","4E6NfxdUkHKx":"eqAtFwAVaaeL","02bMjK78dUGj":"ikXGarYebhqD","ac6HeO6C331l":"sh96EsuoiSr9","qSZwfoPdurWs":"mlEYwc4wbJ3j","jPsJV9UB1yZB":"xKjYMZICZJH5","nuukPJwDTZ7K":"9saTvLdwjvNj","m6gfFhlA53zz":"0CakMHPGOoIj","qwk34ovStCC9":"myEVYvmuXP9E","DpornfKLi68a":"lAqtFx28l2jP","FwmPKP8VnKqS":"ciivFreD5BAi","5vAfBv8fo7wn":"VZeKrG3t7shS","bHGPFiAo0OjU":"I0DpkDpcrriA","k9b10vX1AnAw":"NDNvSqoedOQK","rJxzDZuHvMJ8":"C6dMcbOsAJhK","M7ffi3uwYYah":"ookhGNmMUgwK","CC1pxugFbwXG":"pUb1nnOz7yZT","KLVsiIup3jmO":"Rx7eW0VbyGlA","g5ZNGSNjAh6i":"MMCZoDEXvI0O","L7lCIWfyJtOf":"voNg2CXApz2n","O1NPHpODfFsF":"qpl2VNG8M0QK","7tKWwXshcGPX":"WbBI2aIhrG7x","h3tSoxIxHVGl":"7WThsBCNrwXO","V3pOQCkF7m6B":"ywVVMbMHVtEk","f4KcqFnoUEQu":"ks88ZEByM1SW","NEUR26i5XGfw":"KS9uphhJgUwK","tH3sFpykHAVY":"kmM2fEbK7aRG","hEiOsjGSKNuo":"rQZLxeaOJBDJ","OdDR8HpYjwRd":"MIUoEvPUFNV2","vzHRbDYFODXC":"yoDluyTPRYxg","QRs17tvCNUSs":"ywyyAzRVu9C6","q1SfCQ52bXri":"XFotx9ChTAnw","SS8Ds4tVJZny":"euRapy0yroix","P30vkNQvjVk7":"YyUF8qjOzd8L","yCIThf5NtPpc":"5TwheJlX3Asj","6hAqUPT8BO2s":"XYv0QMk6vpbs","lCMSPUjacxnT":"O6AzxA5I485M","yCsO7EEDinEn":"cYBgZ6QsG4Rt","UnPTD0B5lx0S":"doma8ZwUhHXB","qKW0ixZI1bpk":"keIeiNr11S5m","op84UhA404Ee":"daGMXYnLnO5z","PdTQ7cfJ80Yg":"M29MBaJpZISE","Y8p4DfOTdYqY":"iDXDMTDTr0z0","L65RIVxRNNNx":"5gy9304aQFU3","h8VhEpOitUq9":"XT2DShpmKePd","EC8QZfNqQhgH":"rTQGpOVuk5pu","CVCCBZhr6YOw":"VclRegaMk490","XRjnGfJyFqKd":"ypg1UcPtq5VD","APAQTROSbW4I":"UAsVIDB3kFJR","SRl6iYP2QAgP":"mulZFwBVLFBO","IbNQMhaUX8YU":"orSTBjrpEFqz","BZFd4BkqmLUa":"RAtYqYFv2lK4","dbAdbE4PQZc6":"RnOZALqwKDEj","0Id4aScl7KlH":"IljRrf03U6eo","nFHpNYjq6Cmw":"7e5t3etEqs6G","VyDIqpF2jLMB":"JNjfBSjXcDxC","5N4sjIienlbz":"z2szLYUL4m0z","NaoKvEilHmnI":"p8oMOrcoyWet","R4uyJScM1k30":"SfJchdPNxhG4","yYNcSRNZdr54":"gfevyKPDft0f","hJeyANj4dg1W":"t0jZW0jGvPSB","IvFh4vOdkevj":"kuEdut7bFLTm","n748aMk3eqbI":"VuYTJVFF7MvR","GQaIh37PUau0":"XXU1OjlUbda8","xy4l7ub904pg":"Y07O09iaUHFN","C3A62dnMEsKA":"95LzdzyiMGHl","4YH4MAkkN8wz":"U63jLS7V2gaP","3LoKsvZja5PI":"gOqTRcdLlKqh","bsymA1Oiehue":"xhgGaC0gpI8r","zi7Sbhsf1G8O":"Rvhy3FLI2ZHV","UVo66iSBpN7A":"4ngsJ0qX7aI5","6h2gHnvjkDwD":"kSU5jcsw0ue7","eQda7hvIRbBO":"p7gb5CGpkew5","xZRWN9lhbmrM":"AuKgfeTVzrPT","WpGBkQ4eGzgM":"8oxriXfGH3kN","O7ilUNoEECGU":"t5m5qFz3yi2o","Fm2g62xNxfhZ":"gDnTT997QPlH","8RrLJew8645b":"B8i5qOOlv24l","c6b46fSKmrSP":"ZhkuBzouDlIg","nQ0uutLWaH7e":"mAqclIsknKcs","CfIQIEtqEMqa":"TfsIeyEy5aVg","swdeIBYZTnQD":"Ek1l5nuAMIqy","cZ4OAinFyaPp":"2EnXEU2Eoqfx","veIY7CVRLBu1":"hvED3RzRZ7G6","HdVgmvkiTfcn":"RID161MUuS5t","Ef2abG3UUvrB":"GTgKmQco6O5P","gJn6ob0iXJyH":"UFX2cbpJSb77","tRGeo9smDEW6":"BH8RBBXIZEOU","j4gBEEOkJjwi":"dW53eU1a5mPS","Qry4lESecJgF":"oxjNhbfG3iwM","hcFZoTY43i66":"HBFHs4BqCz4k","oBBGtcEJ46Fu":"06L4kqEt860o","WvwBcEMnhP1y":"NmtKfxsb8ocU","dLUOTdrFuSgV":"4fyfkbIke8SJ","4cSUIL4YOAGg":"hbcB1oqh9ckx","MVoYEYnh0J28":"vWQpbCDOEBzI","c9WRZ3Ge2rPA":"WXT3yPUEDw1b","99mHB2rwsQIE":"YZWxHcYyvyE8","5ivOqkSaWnvM":"RTq6TqSltZ6c","KksV1pkuFEhg":"F2Nh8Bt8Kcry","slc70jnVAI0H":"M4en7vPqqeVb","ELybRTT6GS38":"A7kXfRhkkoS8","xb9Vjwl2fyEq":"GWnej7UF7W1V","mtod8p2DQwSY":"YmpXYqslFzqj","OloO74p4R5FA":"1QXeheltOadA","SvwGTfmsaS3U":"kBtn746m7sWx","sLeGoGpBbuss":"X0U21mXNtCWi","bpqVzL2N30bt":"pYsuPRiA7Swc","jHR0YrpGMulL":"Ll2nLKbCK9We","DOmNXf63Q422":"QuGtFvHh8aYc","paT8PjosucNG":"DZLnESJ2zMAc","qPzd5EDUZPNz":"IQtHowsaLfoF","G1dNmmW8Qzdy":"fIjjwUVNJOtX","kzrLW4JDQhVe":"snCMQ6cOTGyI","nimdEGtNT5tW":"jhhEaWLU0fFl","N70tFyB2AS9K":"HJyEb7FDGhem","LnUn0YdkH5mV":"g25uzFXnQT9U","6ZSJwXsMh22Q":"zvtppTCMb2Bw","SueBqoFY34GG":"H8nOu6zHOT5n","phmTjM5yIVD2":"u0kyPUkPhccp","41FWw9y51uxG":"eoZuH7Tr74wM","K8mTwgUOMeUx":"eCRZ3n4Ml7ej","G6TpjM0WKPw5":"v06D5TDBxqhO","NlkMQfoqH6Jx":"EVcGfsE5ZOV3","Qt1rB9GpxAeT":"8FpRsRNYgvnP","cWyzP90Uzmq9":"8h0RMj0OoEGR","52xXd3VPTsFE":"0troA2j5AbiT","OY1DbR3Mnjb9":"WMlUbRLImQ8g","Kd46NlrWleM3":"rfXUWOSWdhfh","TKNj7rFC7AXl":"UtUa6aZltElr","Drhq1Hoyaanc":"FKcjTv8B86Ue","ZMOyL08M0xeO":"5I9UbsXQ1TAk","joXdtkkaHqmP":"2iwKduyaE2KA","0m7PbZsLYuV6":"7eY0xLRYLdnU","npXxzhHZs1vB":"7Wv69gdnAKD5","FLHnAwWaj8mc":"gCDC7DZm9oSU","i1HTe5q4q8ol":"w3HKqTr3JJ6W","XdUYA0rFroMQ":"0BWtTIqWmf4d","mques0hj0ucb":"kvEjBW6CediS","jYuHlkYrlMut":"UGHEAQJcDBTi","IEF3XNxS9mGp":"O2bicescb9K2","QW19ujG89S7c":"Ivt58YnOxGof","7zYAOmessyAh":"rx9lC2P1OG7H","yM4RuTtaoCZl":"8QKUgH7QKdwW","d5isYXji5ZoO":"6KY6WaGzwmMx","fb58YNCvv5KZ":"uOr5liXUZKVx","pOhcya1odJT2":"32WZUjtKWbDL","ZVN7inGptAsL":"eIntgwxDmvWh","mv5dVaIenY7c":"FnJK3vHj2zQl","dAp5O178Nhvo":"BxiSOa6jYnf1","RC0AAHjofyYd":"wA0b6r6r6vNG","7cpS0SDFwhAX":"TPfeqqs63ZdK","YJuEbHrJKRz0":"SxfEQHc8wgxr","CMBuactFzHCB":"2HG2FKkFMUiO","SbVDps0QHot4":"Z3fLkWrDBed3","lQvQHRlW33kl":"XcMvxj5zb8jJ","GdLc3MLOmSmM":"tWBeiFNsXDqt","tWVkfG4h0nCe":"sfkPDyKmftbV","H8hhym5It5PU":"CRoSyHfq4NN8","vULdcmRBday6":"BANgAo5slowU","Xlh4xiCERmBc":"lxtTv0jacCY0","UqIHtfqN0nrG":"I67XzgGG6F9w","3g6uRoBjztfk":"MaTPsEksfU4t","gmtxxq6BFC2s":"HiwkJ6nvcFV1","hkVFw9zXsaWw":"VzifMb4meXTN","9YpFBrS7NVK1":"47otuVbpLJBD","ZCSYPh6HUEbK":"V3gZqLdDjH11","eFJvH6Tt6N0n":"aHzXAUOW2rWo","B4VW08mMiZud":"vqsOZ4uOIGLr","19BJVrdGh4hC":"EStfACobNH0w","kkVuu4eiWEjI":"LFcvQf7T7hOs","ZYKatDX8M94V":"qYnCIGjIb2Kf","mTbrUaSY3aJW":"CLKyxmcqMyxj","4aEIwzV9O68m":"hzh4QqxuM67T","FTZbhJC0OTlL":"MpsVSjEDIcmj","gCxdRWZ4Q3hb":"ebTFpahgIfoG","uPxPHFIbyipu":"dSHd35rwb068","hIfXA8dvSGUY":"ySw7icsyfjYC","mqfoMXYz7PFM":"prTpBvorwmN8","WyT4p3uMDLEE":"qarjnnOisU1J","1qqSuZn9scNd":"pN7TBk2o6Jxx","r0Qux8DfY3ri":"y3CJ3R8QbF7d","rgZdEYIpR66c":"shEa2MVhfAEh","ZQGamqHl9BMT":"P4rlwVZq9cSy","49FL9bWoQuo7":"LG6wpJVuUoGg","TgDf2IUwzw6c":"omUTKvuMje2d","BK8a87CH7jF8":"TOVVr0Dafd6c","BIB8rccRJBqK":"jJFgDnS5iIef","RReFe7R4hx5Y":"ZxnZBg7ALUZg","sGZj6kahigB3":"MWTcKbRWeqUm","eHtTfdonJ8LA":"hz1lt2JNVcra","ctbObHTW68iz":"qJAeD1Sso7ck","CG4axjF5ENd7":"3rARwGcrUQN4","yTNUqsILu284":"BziXoPbHt64U","f798WU7FZ01K":"gwxyAVxTO1Vr","QN0X4fAOShnA":"ZaIeBBgNvr3L","qEAPnQBZ9nEo":"wmVkrGW9u1K4","iPOOg7OL9gJM":"bl0LrQtNhlnq","bJfA1QlUZrE2":"Grqle4mSikv9","oZBUuxnBgNuu":"0UhVvvlcmj7p","HMQtskn134Jw":"eNxNNqIxSHfb","ctMgfLEmJHU8":"EDQ7xawrDzhS","6z24hO9VhwJ2":"Z6fTPlm9UpxF","MKVh8k6c2vrx":"ffn1OmjDOmb4","TTVWyqUoLLdr":"ybhCNt0MmtHy","Xhcl7RAfQDaI":"PeEbvlSigcoG","rQ5OxMH8UDPQ":"KapP4FSjkWXU","ogb6rjlr7YR3":"2T3uoWOArS2S","XwMIMvGnDEc6":"lo82tf1MY2Xf","4oLeBbBZVnCl":"QQToNh6gmSkm","5n2K1ieS4Kow":"aogxjf4vwzgR","K6OzUhoFPTRA":"gGPVpzkf5esd","7TR8Tamqa2IK":"q9VcikWKCm4o","FpV0sP0Md8ee":"4DaYx49DheM2","yDX2p6RCaEaG":"megrtRCvmOGl","3eSwS6edQrGw":"Qaa1OMXzW72q","z9d7Bq7RoOWZ":"e7B4bUv0D3L3","p80HRIgbkdB0":"O3avazZUBLLt","v7EHLbxo0rLq":"8GdfpCPQK7hK","Ma0c8fcsw3Aa":"XyhGthSDwxBB","qTWwx14DLUnU":"15tCpMMvgToM","y50m98PkVtVM":"4iU9gqmYUtUH","Adq9428zL6J8":"szlw5DrVcpvJ","9VbjYFdTKJqo":"9B1AeU80ixDs","VpY4TwFs3esj":"4rsQShGpZyyu","Dk0tFTjskcJn":"SUi3Dlnor04Y","we9fICWPULsu":"gFq3S9xYzyIy","DwUAIXJsGis9":"Iby5fGrS9QZD","IgfmTN3ObPCw":"mIXNhqEXr97Q","0tJzbh1CNXwp":"NAygJyu6TFjl","tW3PkITQaagg":"KtxFbfraCbYk","NZ0I7jr9X9xr":"f7Rbzzj6C80p","946At6voARXg":"D2mn7wHctZMI","slbuo0QjN56e":"cHsCooJNyJNp","G9V0C66jSjhd":"bkv8m12C0KfF","bS5u73QNhAjq":"QW2PtNHx2oYf","P4vCMraLpORF":"ejZNIZsQwTYs","hYuslYvzfaII":"g3KvlchZoEeI","uqaoRDgzHfkx":"LpGKT1TGhXSk","UwvPgFuISBxW":"Ttv4xDimMsqt","Mok3i8SYkRUT":"Ic4y94Jzh6SR","hYsggpkqhgrb":"gDepQ8RozHGk","ZqLgjD9xyUIM":"YUFAHnXqUEkh","j29frgaB2Br5":"XZtbGJ7IkTgF","IEF24DDWO8mP":"7j76dVNrrSHW","SA4QNGsQN0KT":"OFs0sMCJpmPL","HuZ7gFBRB1qG":"lJaBuNpnnNyd","cHA8fXb25P6X":"dj6zFXRAuRiI","xmpylMVHV0AT":"3tdtGZ9D2GhI","8DhM0cS0XOOi":"o3nbAcu7cxOc","IKH1k7pFsOua":"pHHDy6SZlunr","kjXu3lChGHJx":"t3JMXwYfbpEb","mDdAogr5K6Ea":"5cACY9mU7HYW","5WOxalzH1HKM":"y5E3zdt6PueM","fTL99EXo5Kuz":"hzWH9Jblsm0B","53QWKGcHDikQ":"WdiJewOxKFDy","5ojKp2xYRpof":"VvnPJ3Im7QyL","u7w5YESsSXhR":"VFhMigLXZtr4","SZ0pOEr0eIMU":"1y9uuTOwuOdv","ovVuuUZf6mk6":"Q5XQv2mFI4AU","5pKht0kaJiv6":"cp0IsSn5zYJ0","IjBLFYvQBVsG":"Be7PEZdzNRkX","7hIK1SUmrTOA":"TAWi7scRuusv","wswAkvENjdPP":"nA6hKdVJPDWj","g4kxi5SvKOxy":"7Yuupk5nf3Fl","SaFWtnKXk6Xb":"BYfzcYLggwei","k29BA7l6dUnE":"LpXV0SJPIUrU","xglOKdDhsdmS":"3hvRiHx85lzD","ZRZdLIdrm3sW":"SgOxeUaXDS5T","7pCdsAGHxqNH":"nM3c8NON3iZt","jY0KcDOKVyqd":"A7odEWvdAa1U","KBWp9IF3neKE":"5Cc8Cvg2txtl","ONE87eLI0fze":"rM1n4q6irpEW","P2xmvjU4TWfC":"WUv2QKB4qP3M","XuktRaWW0YCP":"62seTNcpNNiR","1cq7hDxxwtWp":"NEcdAnXyLCtF","pEYj2HD0dH4o":"y4aM4WXngT4y","1lW7glbvD4Eh":"iYFYggeUcov7","DhPhLQ9iV5RE":"BzNtaDO0tDeh","5tmVR36OLnKd":"LhephmCPpSzz","Y81alfP39aYo":"n7T3jZGY3DJp","j9dO0G641eZw":"EzVzKm0dnVso","560ixl8fbm0O":"LofAweiOPj7w","K1cql4GceqxE":"Bsvm34u8p4SV","5zAuA5E57CGV":"iEMGHNj5ebz5","JPJZvLKAvgQy":"ihxbHr7RurhJ","Vv9zOvfiGBW4":"ZYz1ajlcBF3q","kx5Jqav7Fqng":"JnrtivaSiMFL","EUqpHGaNGQr7":"V992qqLHXLlL","m1LBQoIR0lhV":"A1NAKDRaWx5G","lU1JiyZqwQKZ":"bFTWVDjabGBN","mDDg1mkHmQmT":"kwhvZVg82DEp","kS1fB9tQ2vO7":"q9XwzQXkLmbf","h7wURvig2qiZ":"d2hnwRFwyYAN","pTT2ETs8GR5j":"fFZsNVkF3dUY","0nz6RgVezrgZ":"rSWs1rrJqoJx","N5gFpAIcG45W":"yqknnoPbxxZA","OzZftTPyHsb7":"ud80PNHayQyI","m6TUenieT3J5":"RkXHBStYz3Iy","itRPGuYmhH5s":"Hh40Dl1GfW0d","qPX205DfcA2A":"RoqRCCqj42Jo","k2WYWwk2oG9N":"aywzItYb4X38","PsMJ3NzDqaO0":"JF80hzYO9eXD","gHNuFWvLV3fs":"fRLITbV0vz5E","nfUu1mfq9tlO":"NAC0wjT1TxPQ","oQE7B0ADjFeS":"kIGi1KC52BS8","KZk0kDbXtBsm":"CH1pmQlSbtqk","l29FQ5j8DErx":"nCicuDlKZkte","BaqjU9ii7Elz":"nPW1vwsDPFF5","LxG34i8zNmRC":"yFZN8hTOHyzo","SUM4PNNd9ast":"jEzTiUXUfcF3","qHVSXsGFzrzQ":"wDcNT8Uj2b8y","rOERSc3c5KDy":"hddaiN79Ewaz","rwapH7DBHj3h":"8zIhpXNEI07b","GCiODWLXH1zr":"Ml2hD34ExgN0","uqMptBfY4etg":"lOjTd0SVvSdf","PSs5pF4iUnJ5":"9KncYyBXHDW3","vtMPWrtezhre":"oISC3v4c45N9","tT0aTgAM68ox":"M4EHQeXha0gO","j9KK8YIiJzT4":"V5q9cGUZaUD8","0xdPVcWeWHI5":"eToiILOzhAQB","HnZnxzgrTy17":"hJlg6qyESSEW","LkofEZU9hAzD":"B9OuLBoFHl6I","gETIoE1yQTqI":"KcoXVjrzddhx","Zc2Hs0thoy2i":"8G2G6bAl8CQS","UtPk8BehA1oS":"r8DlOJFvEF4Q","4SOhEMRKCcPh":"axqXHNc9KOMi","jBBGhXJstiSn":"OZfEBN4l0wcH","3QqwmPRpefJO":"iniCtX36We8U","GgIXX6pl5Mdv":"a9Qp9eZDRRXU","TTzdwoFskW5p":"DvRZAPf4YcZt","cWHT8I1SSZZN":"iwrOOhRCEMks","wgE5pPJnDBpH":"LEGQxDsQLep8","zZ5xPDlzY1tT":"tGapEMKN9Tsx","D7UrXNRUmi1Y":"XFFElALWYnqy","uns1PcBB4lBE":"5YqYWH0uJ6y0","v6cxmfNrCClX":"II5pWFwnlYka","TipywkxrgBz5":"ewC6RtbIoIRf","UFbOyWQW4p9S":"QXvP0HVkC6V5","w9xkAzvj7vVb":"PTwbsx5iRjHB","D8ICIYSpZIt7":"VxI605dYhiTb","oXhE6aVWQ342":"IFOYiYiEmOk8","dkeaT1Oa9yvY":"n83NfDlv0q3r","Prrs0cqd99xT":"uINaHqrrHtyb","iwOwkLfuDBns":"vQzJNa7uDAX5","6EX5R1qc9tUJ":"PAsoGgLmlBcD","G9FtckL9DwUX":"rG5doWjUmzQm","aHyn8FNbcSOe":"CmjNJ9zGJi7J","Uk0JLoevf9K5":"pnpU9Zya6yUM","QbdGtDOjGJ9d":"Wla9KeHIXeGF","fnicQwX9VrQi":"YPOLVfJvalGK","YxhVLLn5nGER":"vH5viEzJYYsM","H8phvyiJq2gG":"siE0J4MeHEjX","ir3F1myHYagk":"E1KHhDjUDD4D","fdiMiHfzCfqK":"a7qMMsqq5V8h","36kYd4NnNJGU":"NUfZ6n2G9DJV","3qmxjSeM8SaB":"vcSbDoEIiUnh","KGFwuepA2Z1B":"DtLm9Q7qIxmh","8bCLS1Xj1JVR":"SpPmquOGhKnL","BGvfSRmGx9eK":"o1PDmP9sBX3H","dho0mVkDGTvy":"Sa3kRBpnfD7g","BnTl3e4uDfGL":"NKvolIvAkx4W","jtyV2OCucdac":"N1ZNh3OaGeDA","vQiHv7fSL08t":"WPrVRUrkkxjh","wgEk6r0L7v9O":"X2jTHGD5uVaj","dXTuDjy4dsuL":"sgSnYIGGQyyZ","lrB7x2z3jW10":"RFffVBgQlGb3","XApie9AGOH3C":"uG7JKWQjf6mG","Xi2PVZ8Wky8H":"LzVCJJREYkHO","nzVRBBYJepI3":"mQQRATeEa1ui","YpE00OMqO4yT":"vMn7rhuwVuO3","1uDlGESjTkPl":"k2fx6lkIuPFL","KRC8VCyCtg9Y":"X3EtLO8Livts","TdDOcExyLZo8":"BSQyl6UMEcy6","AwwY9cAww0M2":"O9Agu5IGPMjk","sTWmh8GX3g7c":"i9sCycZ57CUd","pnM6nu1cLtXb":"sGs8hoaKelrg","JiJL0qrsEI5R":"nm5hAlivnB2Q","YkZFid7MMidD":"V1Z6kXswxUt6","oL059069LwI7":"GUI0G6x0Glna","Wmi8YBETdBWO":"eOIlma6SL40T","ypz2QnLWgs5p":"43sGRUr0Tvz5","hLksFMI1zFGS":"v3K5VJsdHPbH","UCu2fxkyy7WR":"lu5ELFiThIrW","MfFIqAU2lcTb":"Zj0a8cBNRXPI","DbMTJEfT9ylR":"vn56dYmXXLhN","nEoMd6ThfQlt":"RubOpwYfB16r","jhy9xdUwS6aH":"SNMRuaL8RFnQ","WYheGTojYP9e":"qk57wAqe05p0","ac1H2hSMBM7h":"NgSSI5D8OrfC","JrmpFCx2w5fH":"9SzPcxvF0A3Y","bhnY0IBxNupY":"yC6Qt5qCHlKZ","oFXMmDO66g7V":"DvcUhLd0Ul7A","3xYipuPbWVNu":"9GNeMGvZtFfA","HJezaJd1Kx7Z":"5KkEZ5lBCR3Q","oRvqdftFsDgF":"4OPI1DoqXxjB","uvQ6gfEi6Fvv":"ZBD9WJe4tp8k","IPE8oFubKzx8":"Wr5h6Mq3axe2","5iloRUeR4SqY":"ULYS4D56F3to","7dscz7Rm4rIu":"GWf2Z7IXOMWi","X4knu454xRW1":"1GJOGVFdlNBQ","IYaED0rDNR4Q":"KJrcO8WU0QdB","nmFRyiBU0x1w":"CYeRDETJZBsV","qiiGG1RL2Ef4":"YFNKt8akRyYx","zUnhhEbpSRZ6":"JnLowcfTCCDW","UZxLQ1hh9imb":"n8o4zYocsa2b","R40Wr3ilHWHr":"e5WfSUSmh1gw","BTKkpWv0Tevh":"1A7ijJjGtMVW","ANdIoDoEu2i7":"0776qpv704BV","3Bw4AXvinN9h":"g9MYMImN18yN","E7X4aemAN9PC":"0v2JyEQOhhIZ","f5ppKFAbRdv4":"gg0ykfQ2SVfS","cUTS3tkdpB75":"YmW0e3rkvGDf","YPUP9ewRBlkA":"WtGlH6Cs91o1","mzxwMzMmtGdP":"YFq9P4aFFUoZ","PJThzOOWeKcb":"QUnWd0kPk9gA","3aAeufBVbqxN":"NBJpC0YD0U8N","z4Ms8L4Tj2ls":"3i6Vt1JEgEqE","1lcFf6LpNF1e":"rtTqO8hjb7Zw","k5O5wqZsMl7M":"sGZjB9ew79se","iSmt1jDGcITv":"5InMpwT3CE9u","7Jdxw8xTXab2":"9NZx7gKH42dI","KesvFm6TOsRV":"8WnctuvFvPY0","tfXCcw1Z1Zng":"x2Rk7h8aSDZa","yeMNh3m0xUEM":"NIXh9uW0fGDV","wgOxbBnQ4oZS":"l2Kg4021oVL4","qft5zGUPL996":"t3898xMLHmkH","HKGafqSSRisO":"n2IrCm45NETw","Km2bt1YPGP3I":"VTdxOEFrVEnf","uMMo6UH5xTDA":"m4YfXRz4HgEu","G0p1Fo1yHE8C":"2TgQsJhpy0uO","PpBzM4q5FNMc":"sHBeJoQVMw2X","wCTHzvQLUy1H":"4fM5EBrJ3bcZ","CoeG8gu0YjDR":"pDGogZTYneZ2","N8QoW6FpiCNe":"bOyCPbbimTD4","qLRt1F6saRGX":"Bszwj3ydhIF8","DrECpCoC4Fpf":"0FEOj2veppXV","KXlJGS6QtADD":"ptRzupkTI3aD","QfaSYNmqvoSo":"HsoWwo3CD5Rp","b6In7pb9FJ6V":"fTPqPGx2SRWA","UtgBVVjnNZNg":"JaZpcQca07Am","oriaUP0LrX2D":"hBrBHpB6GcbE","017pbIz3eb3P":"VQ3m7nixGO5w","QJii83Pbx1X7":"AkKJrwJ8SA6c","FNz34d79R5gp":"Sa8fdl22cSqi","7PZDSIUMhXlD":"AMwoLlXE0li0","7M09MqBADSjM":"NK3nWBM4vcY1","VeppGA7MQtRo":"ZQwSAiXbabC6","Ly0i3l8v92Al":"3mieWQVQSoUy","r2icEFy4QBuz":"hiDdgVLF0Ar8","MPq28Qrs8iDi":"yiBDRVw0wHU7","Xyvvgc1yd6F8":"jF6f2vy4igZD","2opxhZij4MNH":"tLHhS0264q0k","gLewZg7uLjTj":"3iLA3HQ62nmu","dDmp5g59VwXx":"ZklRdG9Qcd7p","gjPJdU2lHr3Y":"ogGfHpUJ9Fwz","dq66121WSe33":"AUWg45HhiEDM","r97cqYJ3DABc":"H6Zno60o0Yx0","Enc5sNNPk65x":"ihGN6KCurihS","Bim5V3vePCYw":"Hc7qptZeEObG","dk965V6yZu0X":"mdeA7RiClUgb","wbvDe26koETW":"gxBY49gBLFfG","ahBZjA0qyTFx":"fHdAuJnpdTjD","Wsc2yqw2flZU":"W4a878lQUpK2","GOhjVM3hEsN8":"boPyA2Lg7fj3","MFh2D7fbPwdr":"1DM1YEg7jcP1","Ew1OrPL2nFeV":"TDBG55iJcWTS","j6eTZQhgSjlw":"CLc3NSuHcGQZ","duxO1P4ZfOMg":"Y9S1Oc022mYV","pJOTnKMiRg54":"QNR3walldVq3","PuDoqAbeAI6K":"sP287kjdXevl","54HtrXENk8OG":"n8HBxblSuO6G","QAYXKswiSxS8":"NHx4jViBiYlE","gt1PgO8ghYMr":"S4jwTuUr1rKD","9GL1gQRTq7TN":"gaYwgOyjxuac","12uEGdYl4uCO":"Y6x8AZSjGNO0","2GsB5iA0kXYi":"UmmVAVR40DOj","5ignQZwMzTDB":"nNJZZvbVZ57B","ZDepCur66VvM":"DSZ6y7kdrhXO","k3Kvf2p1cv2Z":"ymmr1L2iIWJb","hffeF5N10Ffa":"YK11lc8U9kcr","8xRpr5eRpig0":"NocTszyZUfeQ","11owLXNQEoGi":"RL4w3Of0c6xI","fzXyrgEpkQgz":"3cYDV4WbupbF","RrhiBZtm8gGC":"O3oiSiCHzGfv","thkTBUxyjgsJ":"yRX7tk3vCBBb","Akdnb1rLvFB0":"kxfJiz3P7vL5","kdPxh0oSZ7qU":"73llRtONMDKk","VvXV6IhxnLMA":"TvxlHG3RxNUl","pFKSKAvNDIjI":"SYxoe1K5s8sq","WdfwMgpy20Xo":"NzLQHh8nsZHG","axDE8vbbLCgv":"msNEsij8Swr1","TAEPxrG2FnHL":"A40xCE4Eru9A","ZhRS7blPqxZr":"VWUFqBl8IZyj","XbVrArXvxPxG":"WRTjXtqbytDx","s9sszxa8Od9O":"Qyf553D4uNfH","3eBpUgyFXt1f":"n40Hh8pONjlD","m3P45s0wmEbx":"7l9jG1G4Ys4E","yYSlrjHpSJaO":"OHGQ5sr6NU9a","XLaMS9TAFLFN":"veCFYcSkNQ1n","juWBa2EJeCxC":"isdefwCOA396","xoBAVMI6JMxu":"ZpVipBfw7v4E","gIVHSLOLrhWJ":"20ju2BsJus1L","wYz2DqRRcePO":"C5n1bBezIiM3","g4fpYCKtnFym":"xyDKxa6Y3sLD","cusv33IeoOJP":"WlibQVz3eqvw","VELXqzDoQgCg":"T6gdghG5xasW","iu6movQC8IMm":"qGUhfGXPK5Wu","ZypYxeYcnwYw":"aJGGfTcaFTyI","r4hGLKYHxX8a":"1caNU4RQhHsX","b4dcINGNI8UM":"MjtGLbIIRZn4","SKtv7Le1taAI":"NCkZTuYR8syY","veOu8l1vHH2n":"XrU1bszZ3qN0","LYCOgYYQGru7":"8cmNC06ETUoM","DilDYd3akBmu":"zMCVQQScwvmd","apDz6VsLZXQG":"AZAFmOHrjJNE","CydU8VWX3afV":"2wlX57p87yK3","qEgITXkTGXOR":"o3sDtL7zckw3","xteMgYe3Xm4E":"IKXh5xNrt0qB","s56QNPQVmwsG":"YwXdTp9Ioe6W","1cQohFq9evz4":"Ra7wlyAMMXnT","VNaV7LtABIIs":"Fz7JofV50cKu","uBVirJwWVv9r":"O13oQtmVszno","91Z1qjib0Aob":"z7jy0ENsyAci","75ZS94Vb936Q":"GaymFtreLQMT","SWyqM5t9DQxX":"IRsoa93YxTm3","vpWN9JDgM4Cy":"pBfnlmUqxKRD","Zw4Og1puy4zK":"0nNSQqpNgrYJ","H1M403aPCYSw":"sVbKiuhA4QCW","e6bht1S626vG":"hUn1CIwnP8AC","C1Pn3iwENQNN":"y0QbT0wW9Bpq","XWWNamlOjWDp":"HumRv09akIg3","yjHpSRgWNQXn":"z7uojrVVbQ7m","5I0rTaNPLQ42":"CJb0IC5s3slR","S51rv8J1dV0W":"2OlDJOh7XBRs","TnA3H24mt5Ef":"SLrdyd83BETq","4uaYMnOuPHUR":"btfnIvRNukD0","QCQMOwy4F7eL":"zixRo4NBzhQ2","G6fGiC64yXlV":"7SWBtyjt9Uvf","olE8pdy19p4a":"ZTCqBiY22mxX","8wvYYXA5zEi4":"bXvhM8CXBVXp","X1iXtdq6Fmw7":"PQIsWxo8fR6R","DzYZf1yPnSfG":"t5rS5lfVqTX3","mCkBjzxsr1Zf":"Bs0A5vepAint","KmZ0U7GQJStz":"2w2wNziYJVwh","sG71HOJT6Al5":"BpKGf2sgopig","z8R1BgMy6DIx":"699gyvndHXzO","8zt9HB5SeiX2":"65EDOX8g83em","MAxgXHRu0kb1":"kN7BUj80KlDW","lHYwRY8hbbkt":"DBcBJ7cEnP0K","eUFjh1euHUcd":"4iIFkSRDrFJ6","wmpSnLxej1XA":"hXOraA8ReyFz","AkCiiDy7eWar":"5fj2ZvofgOG0","Zs7q7fmEWzZV":"ILt5YiL5jaT1","WfqwWINY5qgV":"28BtPX8lBs1Y","yM1LIe4Ysn5n":"CxfcjWIXJNbX","G3G8JpkZRhLR":"aOplqvJ99oAQ","2Z3lHUhWEDDs":"LjiUkXmIy6YW","eVEjw8eW0OXI":"ZyHRvOzxhsr5","lhiD306IcuSR":"RMeckGvHTGiT","ChDaV6pduc34":"Rz8HOitkezWm","8CkldekInkZu":"xe348YKG82lG","THipS3ZLnfhb":"dsZAvnaiBywm","eQZ4c8BQSw9S":"Xnx0fx0UuJXt","M9IoE5f3PqDg":"lbGwNV3WMJb2","wdmfKFA6yLsL":"gkRV72pYiiOa","fjsDzoE3Ph0e":"t2pOaEC8ySDC","0tVTKXOEYpJW":"88r0JOF5bS5h","nTgmB32iVqVb":"knNHnfqnyHVT","K8YInc6wRP5n":"rT7uxWOkPQuP","rw3tZfrSLouJ":"I7z3XKEv8bux","9NmOsm7DDump":"d41Q7xnV4nWN","2EdaLFQiKTOw":"TdgzpJ4Xt1SK","QovWF56spMRL":"3wpx6yK65Zdp","AX39Ghk6Ph2G":"9RMvnq4v1V08","Sht6Fw3QMaOG":"xHaElxWZn8yg","Wq6Em7QmxpTc":"YEi0KjVOcOvV","e4qtKvNEkWEg":"hTQjPvUGneER","Wt9IcxqEvGr0":"PGdXgrFHUYGx","fBOxwSDgAIXL":"47ni0iHI1zvb","gStoDHejKSAR":"WZqe4assXm98","P0AOypKjkMuO":"WTYdLSbJsq55","nYwVd5yUmPjK":"5SNhqRF7c9jW","teRNo8vDdzL7":"TUmY4NYtm3h9","TNfaYQG5TKEU":"b7IPvo7Ktdqz","mokoTulx08kc":"xUv8pcAiMpwd","lPDNgpsvGnLt":"ByT8KALFi6jL","OEk62R8Ixt85":"6babHkDwDive","eP8FqnN0Z8lh":"DwlnsFRagFUF","EfmmdXKeyF34":"TyWC6eLic2bD","rwI4fPnCN4A1":"rNQd82wPsHdb","sVM2m4y92xz5":"B9wHHbZe9BwR","L3twj2rSZPvM":"W2O9Fg9I4nXA","aS6njiY59NcH":"riQDjPtV4tMC","qCmvbTjBSNJB":"hXrNZIFhdBS1","myIiX7zxpmHp":"w4LIkYGj3Yqx","tecEFxxckFSl":"jPJ6RUoFaxvl","PwpWzELJEKSm":"WMPZYk8vBnyj","kDnPJMmhiJIT":"qG3OfCjSEkVT","6zBAZCQmu40a":"NrSGl44pDHoj","9NH214lVqdXS":"cZgA1OkgSyzo","nQ04Lj7yuqEw":"ZP9kX7X7im1r","bkJBe7iWbb0j":"kCUYcfMQUylr","RYwv0su6TpjQ":"8EgrfapGfjQp","IYuFlRyPjcYZ":"dAyLhDPgQ4hy","nDiNYxMZRrzj":"zu15eo2IFoR7","bGLBBoQ7dcGz":"2reDv3vCDLbb","4KEIIFcd3ctq":"RIrjDnWhby8W","yHJ4oGfiRjh3":"xJRqCG4ZfEFu","xdmw73WlmcuQ":"CfQl5yc5t6IV","rEGRmpnGqCBi":"nRfTRZCm2DeE","CWxyqohH66Ai":"LXcz3rQK922d","ioJtN2lCByfr":"Zj0S3wwRyrZ8","ZL5cwBaFSX3o":"4OIQFoDP7OpL","C5E4jAAlql5k":"IKPxjgtNZg2m","6GR3t7I9Ithu":"fDQk8u6lGbDw","Ux32pIBiMlvZ":"Sbe3Lm4H2nff","exbCXiCUmjGj":"sDSB7IeYkADI","p56eR9etqMpb":"QUMEk1HLKRNO","ieL0scvyHiG6":"jTlX8g23VPFX","lBTrYsKsC9Jv":"ouyjMyMmA6Be","FfnQFAwmXeym":"fvfJcayv7Fik","PfEtIc8UoT9F":"HcmXOiR6cCEw","R25ETGTYje7Z":"07tSpBl0GDCU","vf3w2PwweZMF":"0V9d8LEI7Y05","cegIFp9QbaIh":"D149Y207j31b","PGcwO0jzJVyQ":"ZuFWx7ZWEOQN","vdGaXJ8ASiFJ":"XsSvZT12jv4L","konUORzignIy":"Bmw8UoWXuzIe","Javq372sCdCn":"IdxgnQxT2SdT","FEaXytd17ag2":"UL1SIEjmy0s6","NN6aHxHS5Opu":"Bk3gq1gKQ6ol","egKdSq9QGqis":"0ACedROymQQ0","wybTvlOC1H7Y":"LvVEvSuowIIA","8icJnWKoehew":"rannYN7u56eh","TvzRQquR1NUp":"Txhlb6I8txaL","i80zlb0AdH1I":"YVnBTyufuj5E","S8nFIPybDxFs":"RHoY8FAaZa9X","VrVGHR9QVaZ3":"YXkPB0Anf1BI","hySZcQccHJJp":"u6aYKe8x6ziZ","6je5IuAUZm0h":"Dp5YbzOWEVA4","3vMYm5MQulPf":"GIUIwr5tNz5Z","daMf1bY831TS":"lCvyzt5SRw4T","iawS7BXeIkGu":"Mdfnd2u2sVeM","EZdWz31LFV64":"m6SvZDSrSw8K","AUW7dIPPDqiO":"FWhtrdIIhZzn","XPWHDXbXkJqM":"m2DM9NTsFroA","oSL8M1QFoL9f":"iuU4XL7dHxBH","LTYQuMc4vPI8":"bTgXnL6JH3RN","mUnvEaJFPkGb":"WH8u6oFkHFip","QdrDCglSsL52":"364wDP7gauas","C0pIpHEjhlcU":"sWhDK741WYkG","Khd5R8oUDRhh":"c82ZTRuI97Cl","wYh79xXHD01e":"bFjGiHoKdQoU","9tAcq0jdtwhX":"UHwMHshTE1hl","rHdmXuJZrYyE":"xheXKAUx1KY5","JFyYdebEaiTK":"hW2SJ5soM41q","NaxwivUpFWEx":"keSAlBgfqqnQ","GRKARbdWeqHo":"8q85pwW0EtLS","EL7lemZ6A2jZ":"wuyySJe1pL1A","j3g19vRp6skt":"DoeBCWOVB89V","46lIGfo9SDLt":"bh84B6I7LziR","OEUa3XS0OOQw":"Du20ZV5eAvP0","lEM8VrTsxBNJ":"5K1Bur7cMy3m","32b9PWwSq2Nr":"luePMMgTh7mm","t8OL5gB2kDPI":"rZ1tL5u4RwDJ","RHrqiyQajaJK":"MCCd9UmDletP","XWcuxqijLq6Z":"hiffRXlm83bK","Hok8cRm7jkMl":"rWMdlj05ceYh","ZztkhMLX2PgV":"87fKpXLdLU0L","4H1HC3d7stsI":"dyLio5MsLxCT","lZeSD73VTyo0":"cxmXWcAY7lYA","eCfFELw9DXDv":"EcSm9KdNAXmW","qe59x7bcj1Tf":"VGoIMkcbfFQc","jonhxZ3kkq6V":"DcB2wrDv0Cda","ONrfoAsd9Awi":"ACc3kxnbddDR","Rk1gWaKTzyKz":"dTKTWBLHc4sy","0yg3vNNQDHud":"qsmGncMS0GWE","5TOUAAaBenMo":"KlyBOnWDIAP0","8ieh7uaCO1lH":"iIRMim7I1Nt4","vsy1NuL4vyY9":"D065EdK17UFN","zYx8KmjsgCHJ":"gl50KE31SLjG","8OGSQNbHHG5r":"NcuQ7VGOheAl","hQtcOs8gDJ6B":"FClXZNKOdxP9","r3PUsHwLPvEP":"tfQDlColjj6m","WWXzEGGQAJ16":"gukJJdTIaJUD","roZ6AAaLqxxo":"jbdRsJzvAahe","lRVsjxsGTSPF":"aoqWYtqDAiqi","Hv2jZd2lZyt2":"kVp84aXdybvo","oxYIEoHAIQKn":"ssgZropacQWP","WxMVJvSDFPbm":"jm0HdRQvilxv","KDnCLXFMejL0":"e6t9Eml5CdWJ","2KZxV1QDO7MI":"tzPXMABYe7uU","xKqtMInYXBvH":"qFp2sA3GMcKL","d6zQajj8vOKR":"5EXJjv1RFdYR","Bv8aLy3pP8W0":"wcIvYYmjiZCa","wtEUb6dRKR6l":"aB19bxUU5obq","jcvqh9MnWmUq":"PcFhBmkGsjqF","6B3aD4455tB3":"VKEfcVJ6sMQD","a6Yv1U19so15":"tUEDeWGiPLfi","HKl32sI9XWjs":"ROm1w7ZeLMrs","jn3XaFgnntk7":"UFInnh33tB4a","jCPxDLGNmZRy":"DaXUzeZjtWzG","5vFErIZ5lsI0":"7F6w3A8VecsF","2BYki6nrh9yr":"Jb4F50JJEnX2","kc1TEhYhuYdM":"rOGP8h3Np9x2","LS4HWTLF5lR3":"m5saaO7rIa9F","RyY9pTI24VAZ":"iT81mfQDOx3n","MA4BZWCiV4qr":"XQHbQGgUV3P8","rSZq6VfNldmk":"IbfMW6QShDg3","OuwS3dr7uFlo":"sYgutczbKqpq","re6EP5yusH4c":"QuYQjinxcDE9","JJRGDDsToaCt":"fwxWuS0owmQk","bJktWUUlb7kN":"jqgAYqkZjgAc","VAIaBbDOU7qd":"OOdmMjqQJTna","Ma3xTyhFXPCl":"hZU9R7vFyX4x","lLwMC0tUyTor":"SfoNcrKl7Isg","GjsSP6dRP8aZ":"0YiXKMPKCvFK","nZtY6foacCkF":"GadfIlvEZC7c","PmhhjSm7zFyu":"MCjtIG3thzh7","Dh6kA2mxYXWJ":"3XB4OPUFjK2U","ERDD62vaA898":"nU6nRMmYmuBL","Oc85ig2JFLHV":"tltuVzPZuf54","s85OplaNn7UR":"R9PyquVL4q7P","2Nq31vO6UNQE":"k3kh2Ic13Kvr","T656i4rtm98z":"qNIpCgfIURac","sqMZq5rJEz1U":"oXpHzxuRyyij","s6rPQBw1rScf":"uCMOhMoYMfcr","b5RTv5fmgVxz":"bohimgJDRI8P","y7oIsxDJvRdH":"DWAAR7Z4OXG2","LoYNLumQIs54":"J1z2NT3R0CRS","KCgSXfu7Yazu":"RxNb6m3Bu1OK","6ZqXGSUnVHkO":"y5D3nJRvX9BJ","gXRcpybi2Rb6":"j6d52OnrJ5Jy","4aiYrSnBYfjh":"DhUFOyqiyHWz","INGLyMf5ohsV":"cGZTreK2jox0","67x35hBwUuXU":"PA4FEvBUNOsH","OUza1jq7t4m7":"YOzRuFY8E6qf","HaU066eFdoaz":"XYBxVxoGNwg1","ythi4ccjmWf7":"TuCyywIIn4JM","G2QuYo8uOCst":"37uj21SDbfoc","ijQDJPWB2NV1":"eQ8jvKMX966e","zBIy2uuKm4aT":"bsX3fXOb6wOQ","qVgsy9otXWIT":"6o6BvRARIUsh","9fBu5OnVpA5Q":"BTje4oQGmyI3","PpxpTuziurOh":"VtsZO685pjvh","TD785l5YRLQM":"1HAgXUZ8HOjb","AtVwLbrOpP3h":"uD2jiEHIhEyz","55Tav5rT2ZGf":"tfQwO5Cawq42","i5ooHnwvFiJ4":"3y23o2fBntOG","hV39I3nDTfyx":"22dp7QrGov29","mBEW3roS4Ob5":"EbPbTNkpIm8k","Q4xj4XXpMZO3":"HOsI0uR011Yi","JmCqPZf7xkla":"DMSpStkHcZvr","3qdItNxh2aRh":"vk96EZikBFyw","pBd5mGX2x9Gg":"XGnnLp1l6CCm","nzcyGybBVEiZ":"MhpvIFT6sGyf","X7ZTFxo1F1sA":"IyoUdanIWB3N","qdIyyT23SqXu":"XFPbqdJFBAlD","nrSZqWiBIb5P":"Rn0iDF2FYOdQ","hNzpxFmqRLHC":"AVEjFCaJgg1r","FO3Az4853r7M":"TOPk56j5ilxY","DktJOiThHZ01":"JNUw06BUPuj7","N1retIAB66fM":"1TBxOC07GiaM","ygH6ZDz8Lfio":"j3dXq2LKCLpC","jgajUpmvbO3G":"s7lDNFNKkIRi","kS4LxvYn1s6W":"xVTyS360ZHhs","i4Ocl8zkm1Hz":"1UXh4P7GBIST","Mj0rT4xbPNxL":"f9hxcNzZpYrq","ccKAlIBPeGrn":"auT37Au8R5yx","OrUtlSTbj1ov":"gEh9so05g1nk","bOzkHYPDHOp8":"SMIErQOCKBRA","N3DI3G2NR0cX":"kzO7Jip9Hgie","J6wyI0gcwWda":"DalBvtrnZiEK","HA14OwDqQLK2":"R4Iz2HfTQrKf","pe0gkbXhB4sK":"wVcIThUbYB7R","7WXEhesN1BQC":"O0HrMm5mvxnN","l3T3a7nAOsUH":"EwAX7D1QudHq","9I506KqOQEss":"dQ7mSMA43bBi","bcFoqUA29J7p":"rxiyMMkbUg8E","slSwBZbu69aK":"UzxRyP4IESrU","taRzDvPRIR6M":"P8qKauKUTcde","2OXjIEm4sTii":"JrKg1m1w255U","WLtL0LqgfHUh":"LZKgJIqFbYeg","rJslrDO9FoR8":"qEEXV610idmV","AHrXwyQHYVqh":"Rl8N4RmSlWef","C8EDCFHvBKWr":"jP3eZ8W6wjs8","CRzXQI2VqCdi":"yuBGc3jrRQU2","8PHtXIsLi1dt":"Cc5GZDCu29SB","Zz7zohk7VDJJ":"bDP6Exf4MiRS","GBCHssWSp4iX":"QHwTnWh3PWe0","kPDJHagFQglk":"5Epnyu2QFRd2","mVpG91AM0Ghl":"oUwwfOVBMDaL","6OutVzpHwwq9":"fFUnXOCdmbBS","XZORGvUICCdM":"H3OWe5SBClCs","PrWHx2VTS2Kp":"eI68nE9tjCA2","LBt8b9ulWTYW":"K9zxF6QZ0Hnp","gpHVxxLFxm0L":"ZIGiufFwTB2x","0woikHUUnMcw":"chVaoRcCKbej","dQjHvjrOedgm":"V6W0ok99RyQv","OQ05NMOuNAHs":"ygGC9WiErvda","hIjwPBCU8bHY":"zcU6CY2SifPR","pHLTThC0qli9":"xRxHEjiQpeQd","tJS3A9HoOwV7":"FfwayTku5Uzj","gOFTX69BMw0l":"TfmAyTzUvxhx","ucVI6av2Qa9g":"3gcdaxAm5Ajg","npYbwsMr0nrn":"Xkt6ohP5FNDd","9vwt3ExPKNex":"9xyZGv2wSzZL","pyqvTtAl7cz2":"fFR9CoguzQLd","IGahfW5yf9Vt":"hIBW0TGUEp9g","daRTWTw6sVK0":"OdtATS1nIi5F","Yadha3U5YQ0G":"iIzgZNZ3Oejc","hVTAWjBi57Ru":"DoVPxqI9pDCx","gBo9zsNe7Shk":"wCYPpHd2utCz","XnC8WaAEeh4W":"G1x51JPT8TWo","JK1H2OhLuFqC":"EZYbzJiuemBo","CNkQaQn7PTpf":"3RsU95Ttk6GA","6Lfv7vVlkflD":"ZqlFbLl0UCeN","NsAwRtIFYKEs":"ppFoFXKCMxFr","D6UD5AGJfCHh":"ubojAtlAg5kA","GzBeMcdjzFNL":"3EmT5AYq6mcV","s7RGA606MPXx":"1zPiIC6jZ98N","vB1muYuyf7L7":"8VfyE0yKC1Wy","vmqwTb6tDykU":"QzMh3W2GC0Qa","oHIsyWc91N7m":"4kfbVwrDRBpS","2Omf44WApQx5":"vmoK6dhq7NsH","7eaDvvxhKM7H":"e8i9CQUb5ZSz","x05XzDXPXoT4":"lfM0FpEGIYU3","WLPmK2p8ByCh":"ySUmN3HQSvLu","N69Pn6v9gZvR":"kNqEB24UhDm1","KScIEFoZjWUY":"PWABVg5xk8y2","XQZzk7RENHut":"LDSbeYcNgM7J","CUoryWmR7Rsg":"G99PNjKT6g8F","qvO5BPfL96Gr":"BVmUne2vMlWF","B7J5ID5IIS4S":"52WOZbTITgTL","VHbtd04GTljW":"pJTUnd9ZbLgK","nKyKRLYPgBsR":"pkmtQy5wDz80","IXkc6Zc8Il02":"8RWGgaCPGPJS","LP0uUSzJZt6A":"InD6yzhUB6wR","jnEo6tGlsga4":"hC9CLPlJBHFS","2BA5XeRlWv7d":"qco3AbbozRmv","s4KsT5R3xgCl":"3OgolQfy72sr","4rttXg6jYu4V":"c3RPaulbVbOd","gUlxFVrfu2sE":"9CUvNGQqHjcL","yWiaIJAnYhuw":"etd3KutXbz6P","RwDlDnLZ9AzI":"kwGme35fFQrL","mSG7LZ7NfOhh":"b1joENzERdVb","FHQtqyVg76mh":"lg4ZlYaYfYX7","gFKn5kyD4VcO":"GBYi5KFlnZZQ","GrDBp6VVOA33":"Wkn1OVel0vAp","FDK8ruuVIfpI":"uoBtYfch2lmZ","hhBOVgP3EtEL":"5jKGeFeQJNFK","UfxrTdv6SuMK":"R038yTX5Tjqo","Gb73L5DCYNI7":"u3kgLoVVJcyL","tgcS8xYiskwV":"lymhuPIwtDtE","N2fske395WTt":"qXcYaaTnIlV8","YGtbzbrh3Bq2":"orBdsBeaHMX4","nPBHHutwP5V6":"SVkMJ5SNzIiO","aorqDjbBC2KJ":"6QM293j3MHMN","cxO4uvMaed7C":"ofKW1tGZ833n","BdMmlbfzF9uk":"L7wXpSEUfjK4","OjniFBtwqFpl":"O6QIFiHBHAJB","YYpi7pyuJdYx":"9WVImgK7zTde","MP0FDwZCAY9J":"Ef23uNtkW0ui","drn0hpoTLHys":"43DuoKb9THTG","I8LuS6UozAcr":"Xu3TuBCMYzMu","L4rtr7vjdha8":"3tko52OtKd30","8PnU9Xldyj6f":"Ic7u7ZaLyfvi","ab9AwzPSF3fm":"o31qGWrvlhnt","YMOk46fa3FDk":"uvLiK5g8vbGx","YT185usQyapn":"nfHSipZjYWDY","YGOII8XED9Xv":"aF2SANbPg4r0","vJusdraS3z1a":"LbTA5V9d18nH","k9csC8wi1dqN":"2VVPd1kOQAqe","cLYrbli3qnMX":"NF4sUsBhimej","1F1SaxQvV5w1":"MKZodFGQj1XK","gK0ysevrtk1R":"TWNfFaXLgNcr","iPszjcmDDLrd":"Ipc7pWXFAZVM","zeyE54Dh1kgj":"q52DxSvXPZX1","jCXhGw8FyRdO":"O9AdfkIjpFQr","7e5hBKuFhbPG":"fkiWvhikkvaW","B6wdJpRLXClu":"ta2znZEKMDr4","62hLoDQuq6lt":"axJ41DwFReSv","XQDKj7eXYGwE":"JURNiCF9vMcy","G2XNSHVFoC6A":"B7wTlISZ6kbw","hbggvp6IgmDf":"HHTypgA0GtK8","nZhWp6tr51Wz":"rDSBSF6TvDcw","SB3QS237zdKD":"VruaELIRY1Xw","GGC3NK1Bmc9w":"EcbZqlbrGx2n","gYxsMJGwqovM":"55QpmSBoq3F0","SGucUh7NyReW":"1fPMkMqBsg9x","fcTpxMEvVqpf":"eJ7xXZ5cQheD","HjwCD0vJde9O":"9EcBYCz994LG","H76k1fHNe4Y2":"2k64trSXVHwD","2sTxUfi0xfWT":"H5QpNoBSxr2e","LXFQwH315miQ":"gslZyvjpp4At","nMNpiEqVm4TX":"zYW5V0wJCSFl","PzZx2fWLZTwe":"NgK8IUixM772","df2lLXtUOsOt":"w4M7fZyPbI5a","VfINwTorh58x":"EijbVvzI91ch","zeYipY06AsFQ":"VSyY8vaFCK1a","sgsfadGdJ30O":"DKIaB357Cq6J","mAvLaX92qUMj":"cTTxJ61TbnC6","AA7bilRpgcBR":"D8Uazk6eNWIp","RB95sOuPyjwj":"Um7CE2P71knZ","MysyKsOWl6Yz":"RDFxJkTTJMB0","UzG28tOXpcXA":"FOJL3l0SsAlS","ArtHmsEGjSX0":"4WOrWoaHCinf","hqilg57fS8s1":"vbkEPAb1h6yu","HnfEwhPMrQZF":"8n6h2FqneQVq","97HedUhnpkC2":"ilXOMyBXSOXG","EvvxvMKzWW7j":"SVRe8acb6IUh","sfG5wGkKafZU":"Ya7LKXStYSOv","pbR22d9Xcf6y":"jyoUG3Kn5PhU","iA9YtrqspAy0":"XPpSSYQsw3LU","rWEaeBOj11xm":"3IODpB0KSWgz","lG1lspcwwXUR":"FAr6G7CZ4qkj","f7QTEdjtNYKH":"W5DHVmgXc7sQ","muSaOMuBOGc9":"S9JiaWtumPeq","TlafQOg2UooS":"lNwerZvXzlYj","Be7BeAnBotmx":"G2QAU12Qhlrf","KBl2hLKNy4ax":"ox3JWWYPy9Fi","8zW9VI7xd7qi":"1reqqyUfHYJ5","N5gCmkqX1L6r":"OW52Mnfp8cEB","UeOQOi1ytV88":"3Hgd5RDYTENl","1GvLysK3hpTw":"baOwcVMcnNzP","IGbrFnBIWvSg":"NT3LGFEKrXfS","NZQA5EAk4gY7":"43X3P5KagIuR","RatiSKrj2HmB":"X4HnMzpafBJ6","L49vDUlkfOVz":"aaFKIsAsDPzP","3zwgsyFztBMw":"QSXIc19VUJju","NJdgzTpksThX":"jk8jQOlJqwwz","M70t9yPelal7":"GX0aF8YEAqKy","uGmRCA3c5XE1":"XpHd5fP9eUcg","Ty4qwzL3L2Z5":"vFgxrbCSWsSy","gFM2qgBgncKs":"yINSw8wyBqlb","C8WRRIFf40V4":"nZKKfVP8OBbG","jSz92YooCti3":"ACiu0NJbUbNu","oYPfa94wIGMb":"jxwhioEEbxyL","Efg5Y6ziJamU":"awlgSSxdH2PU","wasnBljEFXFW":"vAgBXhhdVsUw","Qwtx32lm5eyf":"TEVodjH4FkGv","RbYJ6erETEwU":"JyRKFKratvHn","2wL1RjkXxu8o":"ocjCkzxGu0dN","uzG2C7T5vW5v":"lVotb3tJmj7k","CoOEIS9JFnkX":"K7B8OeqHccrY","8QK2fc5F2pBo":"VzIskqyNA4BS","clMguY5KeVpJ":"o4A5bp5ugJVU","VYjCKqsRac2H":"uMGddFPCioIn","leArqL9cBnCc":"cTGpIzUsoY7W","uUZiq38rzt53":"YkHFi2h4lao8","WScwO1XXK3mU":"MMZvAnBdlNef","sjBFPvHOaCnf":"FpqYqfiPv9jz","x5nzCDQG42B0":"ATcnoO3D9A2z","VV1G4rv2LPoB":"uTB7v5YPxuQh","V3tFcZTD6Izg":"AN3d2O5nUQq7","JJLRvPScjSyR":"AfztHEMwjwbv","mHRxaBoRh8UB":"rXodRO71cOBv","CTYBGPRcHC9z":"sx81lEvTmDwt","kxNI9L25wmi3":"uj5FzXtqCufN","s2jviQiIuMO6":"d5jNFPGYpxT3","zGeu6UXVETsb":"lV0Ptebf5XD6","HjBL1f1KeiGx":"XrhoafqigOkb","e1wO0XXYqC9A":"PA1UHSdvO47l","qzxKBfRnE6av":"f3VKU4pgq7PQ","ny6i1dbKznZE":"oj9zn775SPIn","y700cdJgrFS9":"hkstxWki1xxQ","SRdlA2gw4WA6":"sgBRxB8dRnl1","G4xFasG4jRSB":"Rg5wcuwjSYqH","ZNBlqhc5ycyC":"jnXzphpixFpV","MR3NdHq76OEm":"Jvz0s5WicrK3","zEkn0CNpQNEi":"OzPdGO6nR4do","shUtde6hINXA":"OaKyY7c8pWzi","Vfd013dflcO0":"t7Zy9aOBhOB1","xTGLt9YSFGWd":"LrobwjCGmylp","bUPMCdVvgDkV":"sxO5gxzVJuIk","ZUAZYMgKVwfa":"Eb4i6jGHm7Rh","cjrdQ99DQVGU":"LL24WMnb78iU","yQdiosJEZUci":"ROUkeGgIlXXI","is2xxO1JEjuO":"0x2d7rjNPhmf","COr9L7zJ7agm":"rXFWsscqFmTp","nkXDHXMfbS6w":"HBRt70W5BRnG","ckVMwIByJBZI":"AGHWpi5zreu6","zqVFBFfBtPAu":"YH5ho92KvKqs","bEHRyqMbNK3S":"sPBLWcfLYbyZ","w8aAN5Vdibme":"edCTsN77ubko","JR53eApc9bGl":"8SBu29p3IAGH","lhNQbELtvGrs":"ASv83cLNJXxJ","8iqLjHfWpoQB":"2t3VzBzesF28","k24Vh8XpgSTN":"ZfjETggL46pm","Etoa0gCtTqqv":"fufkE2i8vxc2","L8miYdC9WPPX":"zVvxIiPfmiza","ZHNiDeI03ski":"k1Iqw8DzLt2D","ol1BuiSPjMH2":"ejofUSyAonLY","kzq8CPkmGWww":"dWe5uW0qnIU1","tyBL9fbTJrNx":"4WAqppYYXzQS","g51JbWz59dNd":"QuZcqdgsdvBo","xsbccav9Mq3K":"aiQrMUf1xDkc","fs097oFnhAyo":"5Lv9AHMW4Iw3","SrGcf9G9R5hu":"ErYh5z965Wv6","Itm8vDNwJgU7":"VQaanwDt6osF","A1VINDQr1VCU":"hg0oaEFl2XwE","In713ZQnmIJp":"G5piOFfhOkW5","ylBvU2dDyN07":"zrtEK2U4Zold","g9fU3wX71v7U":"9BDboHZ4Ax4h","AnkwL7hCPQBW":"SlLOO7JyVGnk","RBYz4HvxIj3V":"bapLN0szQiNV","knVTPuuejsiq":"ZnLtNWRZrtNM","ys21zXxucPQ3":"JHBckXvTWGvI","3scYs63EdqtH":"HarhoNzzAqVH","RcY7yRQynMrm":"GcoEUFHbKi35","xeXPm7sn2uiC":"AXRyW7TpiJ38","4RAZfnhKvyBu":"mGx57TEksXNf","5XIyTIafqRlY":"tGilrloTUTzo","mvDPhfZxTorK":"vP0EpOa30nhE","dSZu6NiVv18r":"pyptiASm0bTi","9w1tEdYlCJNl":"Zcog7vKfohIr","hnVsku01vNei":"G4sX2XrN7YEY","RydMsB5qsW0c":"C1zTxbQ0KF2H","Ev2AdObUOfT4":"ha7t3XEJEvL8","KPcMn9saP4n1":"KE3IxEcGwkOG","Dk254dA6GiCG":"4qiGqj0R4CaT","YAIfvmrOYyZi":"8bpz8uCFpp1R","9uPw3Zn02WXb":"MM1lOjqoOu4I","zGMhcNfY7IOt":"cXmHvbFWI95a","DZLkilfsG5U8":"BxGA20B19MKC","8zayBiYnkJ61":"63IAIurf8FDv","mqHMXVoGuRLB":"5m2MB5DusPyQ","hOR5NfeznM6w":"4Ld5RMWO8b7X","d4Kb88GtJUFv":"R6Qay3I2hzh1","dsjyXz62PbVW":"DygPflNWaIXC","h7X71vgdflWN":"etmHdz9xgOT6","Xf7LbBrJfOTQ":"tZna9ghzmaC3","SwnS2mdk26j9":"H6sXCGk6zXYU","TlxSv63WBHtQ":"QBFvDqFuhEg1","FKEEK4j18R2I":"7Kl2KqTvfd9J","x4k4SqKQloOo":"vkKwyxNV0huw","PjPOPAo2Li9P":"HmlTvU8U2BhW","iIRZ0VgQkIl5":"3EfADSRDtRUq","T3QIGOhWo71B":"3O1zWWdVMzmi","xjqxz5vBGcIt":"WeR3gB3rbwSY","iTlJLlmHCGRj":"4Gz3830LYF4a","AcFbLPMFpKl1":"zU6Bhpke2nJl","pC1sbuY7XylS":"Lo4e5wOtgm5R","wchK1DbBRsAp":"KlJaS3X9F861","55VVrVhxuCkz":"UVhx2S3pr4lB","lROz5IaaaTVi":"Dv1w2ZqcepuQ","iTgfxlzE7YGt":"w8TJKhBlBJoY","ClKKOq1JQTDp":"gGGpb7Zj5jOv","Vyj16h4D0TVq":"oBNJOhcHvtKE","1mHp8wHQz8lM":"7INaNTJpqQtl","Cc4CzCG8qlnk":"LB0wZWRjgLwS","pmSq0GFyVAYf":"3mky29XrG1Ul","nwCBJXJthgOK":"7v19q71diJBH","7so32XbggYxc":"XbFpujsVRs0e","gCbU3lC5FsgR":"lVtx4aeq5KBL","SgcH4G9d13tg":"DRlk35JplESR","uw42pMvXUykF":"QUyh3uIe0ZSa","6kclTLjstL2y":"RBrZgFkOTEGi","Zwi3NIjlHszK":"txCfKFTO7aY4","UpNfJjQE4LTd":"WXxlj8RGzVam","UrLYbKw32aeQ":"547HH1AdHCbq","q6IjOdM9r6Wv":"sIsuVuCnXz6j","AbXPD8Ld7vQH":"bR49J7gsYWIG","ePqcDbZXbhis":"Le1AgYxnmbCr","sDMNJe1K5Sg6":"KEFqFiwFZEoq","eO74xJdBJwgk":"vsUR067aQT1W","9o44kHUDdAz5":"HCliOdPDiQSY","nC7CofJFqct0":"Y293ZINBTATY","OyaVVuO9TJuh":"oG5n00Rd5Cbx","fVx8ASBM2C6y":"YqQyq32D0WN1","SJR0Hejv6ZEL":"AMTYRq9RAjRi","1cZmGUQWnQ2z":"hRkkoo5x6MZF","iL4J8fMDIMkA":"9IbA1mP6AokR","KJ2FOrgCRMDz":"2G9oDy2APdvT","Rod4AlFwvOWO":"KQDXPsKDohlu","AgNSIeduUUi9":"vGyC4muAP8qs","JegJsV9H4HQg":"sEAHMIcu5rph","CjkjUUj8DWhi":"7t6sMGqBwGGQ","qjjHSWHLSKey":"Mns0cjDn0qg8","BMTDtLrKsDS2":"cIcznrEf1UPg","JohVQrV4Ja1C":"9nnJMZ9zzbYK","ZeD4b9G6Di6e":"k2vjyazBkkO4","WQXADGi9O27h":"62gjBPFll8W5","OnWHxEX5y6Sr":"rG2VDcXGWp9P","QxRV1l87py09":"IMVTSLXiAuBs","s5lvH0PgYeNT":"QlsCn4ebeItM","Y2OIYJTBnO0p":"LJYJJbrcZX4H","QF2JyAlMA4jl":"FpmpInXabHPS","TEZV4Syyxzgo":"BhJMzK53GZ8Z","UZXBqhDCuyH6":"f7jAOCeaUXTU","v9MPZvKQ2l4J":"bdbF7aer28om","zIP7x9id0i5n":"9VLd304QAIhw","bPkET5ieA1ln":"AYxKlapZUo8t","XJe7SfyZ4O5M":"h97WYfRX9W3Y","rDa3XagkKVMQ":"nhbNSHqCGQ3y","VAq6aBEUx7RX":"GuosCO2PIl4I","isRYZbcqCy6I":"BzvZDeSpWb2A","X34FA1Z3sSgz":"x8GtO9lFMvJG","9PxvvQ11Pcm6":"L1PLfpAiarCW","YRHwj0NZGCvC":"hk9lGANHNz9D","okB5TBQcIJK1":"xJm44BiRKSFB","dZqktCevA3eI":"N6Fp4X1xsiJ1","UCWXgxPCgMBL":"KNjcmUw1UGIf","FbvTMNeIUlEv":"BY7GrCSFR8l3","itWfUamEzG6G":"QB4qEmIceFsf","fKFeIcbr2gyE":"29kzn3QkHp9L","LevUn8j11cTl":"fMEvLD6In5jJ","UMBdCW6HN2WJ":"TrK2WrEx05mn","2fyXqrZLDvT8":"cP7Jx2zY84U8","QGxItanPo2h5":"ar9zfr6SQN1n","5Wqyy196Gr2f":"8mYdWrOzJqCT","3qlifD1IUhb5":"GEUKJ5CTLwnO","mtsCPCHbvfpI":"VyTm43dWG5kV","ozB6HcGi01rc":"MV3a1aJuqGiw","5MOexF7Xoi4k":"D7Xz4qQB8pXx","21f0jZyKPrb3":"J8yT40xYuJj1","kq6ySHHcFDjq":"IleppsN4KsdN","TEOpbawwrkJB":"WJS8aUuXkpyU","XHCMwNU6BzFK":"oj1lg8T83cu9","JQLEKT1xeHBI":"9eqQQ4QXL3fK","62SFGx6A97C6":"WJSMijShvxuY","RZZ85wrrAGOT":"KiBUWFQvZkDG","onz3J8snuD7C":"CONkBub9RJlX","kaE6KLcG6vA1":"TVrHyOt0pB7z","i7Cg5vI5oWQW":"8kouDwnAskLU","qtTQ2z4Rm0vf":"q42ht0S4ETh2","sNuYQL1kC2FH":"Jx5IQs5evpDt","YYZyY3VPUADB":"DGq6GN8fqv9M","D9V4kubt9AS0":"MfD7iHpWebyU","ro2EHVEFCbRB":"1irHxaAtjkyP","exFY60jECEfX":"uIOqrEL8ePJg","lc0LUQRlrkRt":"mHnEf8EdTYIi","NPre5V6qT06Q":"cdLC5VqOvU4U","XTebU76cJ3nt":"Ow2sw5Tp1m8f","zzKj3Xi6yuiQ":"rzkyBCUO3A6i","EtI5wfM8mJJi":"TizTsoJB4zmh","aMQnbYwIGye5":"jeYznKoOxwhO","PAB3AquuDWZX":"x2cvmaJ6agiw","Sf8nijYMlhA5":"YJf35k6HYT0r","3VwroVySom05":"mxkmC4rZo6hr","CQoIhU9u1vuq":"C811WZMZ3KUy","uPsZsi4IDLQf":"yqW1h7Rm2eyF","BjbFoIv3QAcP":"nMBHhM5itRQW","bQvlraNtkQAU":"Hdeh7CwItgxr","4pNtMzNMBqYg":"VKET2ByGXrsi","p7Kx1h6JKO5K":"47unUSQ5Kyjq","iebkXvVy6uOj":"Ez8UBjVYJuV3","ONcv7ux6qbru":"1C0EdhEh3hww","4b4pRkPWAMmL":"vLbfi9KCia9O","lleeXQU9uOZL":"w8k2MiydpEE7","6Hq0l4H4hCRC":"jEHMvhtXPvy4","qCFrYJ0A0E3Z":"j8J0LSaPqSb6","qoD930A0BSB8":"bqirlRCFivgH","saTezo3MoxH4":"gV5u5pj2qRqs","4dnihkwgY2vn":"kiyeUyFbB8Py","jqN18vHyMxcv":"HXZoEnaWcwKD","owiesrha1lEI":"5AiDc1FsqI5S","Wkqy4c68lzUf":"o3Ix5iUnO8WL","QXsjxoO2QNXs":"JI4n7LhHFbop","3Hv7mwYZElue":"HUWUrx1ulbLd","KQY7m5e3jWK1":"hcwsbRcpDj8Q","zYT2mLdgMI2L":"xIVF75fZPWlN","o2pNFwEdqG3T":"vZG5FRzqTnSo","58j6uQchqo92":"3jhMzqYp8Hz7","yqzbvbz3RwLb":"KqWqBRpEi38a","1V6pZZEWEhUt":"vsfiuMXCPZTe","Nm9SmOQvOqJ6":"2ErwNQpNOXrN","sZajV7pdc6WN":"b8k6x332rnNf","KxrynqVZcbj6":"hopcPgqDi6uY","AsIB8Efhpfbz":"nYQOi4OcgVq9","8o9mi9HVRixi":"xRdLaC8ogZ4F","tHsPjDxcvRBa":"0FRWj8VfNspO","2xJazk6PXH37":"qmzHJTY5BbCD","1bBAM9sI8VnK":"bPCUIXM9j8Sd","neXJNJjmNBZu":"BLhsBCZnM8Dp","D3zeZ8QRrKLq":"y56hEL3nOPOp","0NHikXG8czIo":"85a5n4oKKzFw","kZrFlQGkM6dJ":"9bhxEvECDfyo","Io6zJBEldMz0":"mJqVZOYorz1z","De2squM8a4Lq":"CRhkjVBWxWac","TuSGUKy3r42z":"2CrI7ZFoJXVg","k66UfPL49VmD":"51liRLZrvhvN","iorzgivbSMPt":"TG42P2J8xLyb","Ei2Aou5otTFq":"bCLvz0MYAc3L","C2j0BQKKvelH":"ffbs4ZnqDX4g","56Hh9RpTwn46":"LUHNCzBP7qBJ","dkRCG1UD0GxW":"86bRYBEfH2b7","JuV5ZSQJ7T9s":"MQB0BecsQ8uf","Xk6cxE1OoA0m":"tv28smf2XbzD","QFcXqRFfQY63":"1HHYDkXibevM","n1WdYyZbAjUr":"W7sVpQeCHujq","oy6RHz2qs5Li":"V1WdYRKX6Zib","Tma4JG5aC8f2":"bAxgrDCtJNEb","50T320BVCiBZ":"sLhSG37WuyVx","2umNwgwcvB2X":"EgI9ErEtUvAh","LSDMjWpsGuGo":"nxvwiyXgefq4","JWxdIeqemvgN":"OffXshrhNEyT","EYYcZlmlXAiw":"f4Dx9pDfvQmc","czLMINZ0Fl2f":"iRxdZgHx4Gse","Fn1ZhEB1WYCW":"3auhlnQLt2ak","k58pf6Oc1gaG":"ptrrsxzBvokt","nFHqasAIsmwx":"LJ7Re7HnusA4","J3tvzXHg8OLy":"U24r38dHNc1y","sDvmn3TmAuPD":"ya9UeLB4d0V3","3PV6QTBMQZyi":"JE3P3h2GDULb","HyIY52p8jKVk":"WjmXinoMwoGf","avfIuflYrx77":"LGUQkq3mxylP","4n43ron6hvPb":"xz2hOsapwQRK","mVvynrw8RoCJ":"pEYw6wN7ePHp","nC7RL7ZTpVDN":"4eVwfb1ppm9W","1KdQIZJMyMv2":"SANujo41yQEg","SpcqCNYbIgX1":"ubAYdnrz1epk","7sTfOheH7gJL":"wolDCVDTytjK","5bUmsgyMArAV":"B7oQHoKBAulK","qFdtoT2GsJxP":"LBfFHjcXUi3e","CeZ7G1W1tLyI":"OyTdTu9TFWPg","YWE24Lk5Iw51":"FxDEHamlpeCd","7tsC5CGoQHkQ":"G4hQPQmA4050","ZzjlNblAdQ7m":"5UC0yyZp5tMV","2iGIRhtXp4kz":"kyY87SwhfKCz","rsFdcsgQnmXD":"Gcet4jW9SDq8","0lDdcurHPqH9":"B42tasFSs6h4","s0sZrwnHumPB":"0P7TXkoy1Q2W","rnz5Lcz8lZDX":"hVPcKwCpZtkB","ugDNt4OT448x":"pEQcld3VNVaF","yovmVIMkbmBg":"6gfIlIjme7Hj","AX2ZWdEsnSjS":"nk2PpQrMThYK","VcDaK8PjUccs":"XQtPipmibNsz","MoSsZPFFPXEL":"a4fNATJfMFvw","F7YiiW6KuKcz":"zgJ5V9tmbaBT","OxjOadt7f85y":"beBiZJTr8nDn","m0hlVUSeIaiw":"LZeGjt9UX0hl","9p2sIqenQgXT":"jveRRxhI5TKW","VeRDEkGoxSIR":"0P9l8VFXETIM","Xkhj2ea4dCZH":"Vrr2Zc0YsbGG","9vx3HIC30e3a":"6hXMyI7t1xXl","jiOzCnDKS208":"MNqNuZb5txZL","SQJDc5wY1zC0":"mWibs4yogPxV","zBSf9dygcLiS":"1Hkfjr9zyh10","EFwPxQLEByRd":"ZIQCikgCtGa3","gCPgq5cDLRYB":"H1niCBty8IFT","GrFxMVJWWhhJ":"t1Sw4yNKg625","YGUNNiV7Vp2g":"ta9yg2KNILCa","uU3nc2KDM2Sh":"igA8KjCpXBxl","HrJiFBZgYeMT":"Nek5zgiubHeJ","WnqXZsKWD4KQ":"zvQzYcAv6zS1","M5ZcH2XZ1GAl":"zfjw2GQOhlQY","ET6f9Q8m6sOu":"CQ52WRvYXk5Q","AwYUGFlDRJcC":"REunKGCAgb4X","unyRaLReqzof":"NJ3dwNRjpLk1","vsHIJJHOCIYd":"bmvEbE9JG9K8","U58St0NehVqv":"FpUEejWCXxP9","q8nWYow3LC5k":"UamteHLczRW3","6QGuhOhVUjIr":"EZqz7NShGXce","eZMQQGxsjyR3":"VxiOCUryy6Z4","D9lyjrOB1v2V":"7NxYRS60mQOB","DEztE8E6OwDk":"wNIhgrAiznjy","eYNA8Y57BQq6":"IpXSWYlQTj9u","nsujLYV4HEwJ":"QisrH4EaBQhx","m6BFVcyONqKL":"qKPYGKEHs3r7","A82L1QUR585T":"7xlLSpjbGxfw","LqNoHfkt9lkF":"N7MsedjrRdWv","FVT41UJhV181":"e5etFKT0cC9U","M8ZcbRDn6qSp":"zrUfdE6h3M9H","PdyQULWr2gh9":"HOujwcACgIHB","9DSpTDKK3PPF":"PlGrV3onUA3s","2Vj6HmfOFnHX":"2rQjbGWIA9El","hcyljNDauuGp":"0zR0UR6imjRC","FfuXHhdoR0zv":"rVysMvIKM5Id","g5veGWbKQyC2":"CisOLEoy281A","IuHIvqLtLeGm":"uLRotj0pHcPQ","U42h1OGSV64E":"kCdQ5iKYN6Yf","Fxc5V2pJtAXY":"o26GB4zQ8sAT","qWy1g7KahdgD":"qkzhxMvTAJHV","ptUhZNYUmOwU":"dTA8AYVXQU7B","y15XyDfJGACM":"JK0EckY6aqkZ","f6gAn5KIjY8i":"tP3yuOFRigJg","95Y49kFg8gom":"jIWdvJjNEqyH","cdkxkytH9Gsp":"MGeVFswWZpD8","5OrUDPYRDfyn":"VXYsOSGG3sKY","jcxEetl5TzRN":"wKFCnYkKePTp","uuhurveWtp55":"a6FRBISqlFWb","I198mINciRi9":"Ht9GaE9jmOg4","QVrIp89kbkMt":"AgvdY9f0POtD","0ejUeq1Uricj":"CDMpDJX4IPgH","EvakmIOaRnVJ":"R8ktAnAVTXuf","0rlBTVsJgXH3":"ixXvinmFRwZU","ZLsdmchSLcwL":"1pZVZeLSHR1S","aUh6TZsSStek":"VSK5x45H5VPA","hLqdTqez93ff":"MzOV0JXhLuHO","GZ7aOuceVyad":"hTSAycRHSOjp","xTwixttKDXFI":"FZTc3VsKcpL6","BL8QAsDIt3KF":"49eNjyBvH0qs","wHRD4jikozFz":"zB2kMzrrqHJc","45c2s2U2wPq4":"CAdvFNI6GKE1","prL1GAnREGX5":"FfHMZZJPAieK","OjQYv9HIB3mC":"Vo8DUVOE6JZU","1DG8GWxeG4Vk":"85NH9WkGBB21","Y7wJo6edWHEz":"c1euRUzxcIUB","j9ti8kxUz5vz":"2vUU7skJ6QJM","9wWxbv9SMoeF":"N1bVvNUNANAr","aSfhXZO0ujn9":"QchnN4EytBxy","5IB4xN66dZ9l":"M5F9HRXEd181","rk4sucRk8rJi":"U6VEDVGtwYio","fUBLfT30PdcG":"bfYLG6AF3Wlw","t581CskcZpu1":"W4shRbMYDeoj","2xBadZoWWLZf":"rGajM7tVa5vx","PLKGu7MLbUHJ":"PMJmrrJet7Kp","hBjVTXKPsLYZ":"qQW4tPKLStrd","Qs6WlS63Jbw3":"gAVc9rx47EAX","WUf5StpelkH1":"NYzNBelRkeH9","3oHFw23CrMPR":"8SlmkpWMYkKG","dLl2Z9G6ctAZ":"9Z1961lwqlth","2IdZl75i7Tnm":"FTKn3GYBTE70","VHAMJFqhYPTS":"UeRINoWIPcEn","H0INej5EnPrn":"IpVYXWDEwckj","ZQCeAsoCwCCF":"B2jRiEutmU1O","R0RgYjRVkaqm":"k0cSyh1eE98l","2n85eYa9PMEE":"WV9f571RDc6T","sdCQHx90AVYs":"kKZON2rt7Sqi","RXNeYg1DuMud":"DtQwIctjTEfK","IXe3GYQgee1E":"vTMgK4XX4hem","PP9KQ2xF3lb4":"UvaXwNPtWKT2","w0jm5hfoILS3":"xA2gr8Tz8JzE","wQWZEWAFFsn9":"mtnumsCmUoeW","8FI4mAilKLDo":"rguFUpWX4OCD","ZYySBQKYhNDb":"RTLYfStfpzjB","WI7expIEjNh7":"EmyWoP2OK86x","UDC0een8StmS":"vH3ttr7Lo9qm","C5jytxiVIabe":"TzFAmHAGAzxY","CfVJtSMtz6mv":"WHxLNH8lFhnp","sOPVraYPjpp3":"JOTxTzvkI0hd","iCIqMDidRtDl":"uTUjNVmGwypM","zb7WeL327X3O":"2b5xhK3jyUdC","2TbmAtC8Ni7W":"3dWgf5194rz3","CiwpcJXbSRjx":"9KC0Pmdfiphb","JeQI8Jt12NuT":"K8a2xdbugdBe","kPldZhQv8O2d":"byzLb7EFY83R","POI4M8el6WcR":"a3KNGj7N1IKo","x7Hg9KRb3AcI":"jH1iLeNXyOhb","uCfc7zKLRlaT":"LmTRLw8o1O9X","Aawo3yJHOEF0":"XQmqndig8iJK","OcPZwqUTQoA5":"32PKO5dqHRvb","NtTvjY0jxPu2":"IKYUEJmFOxWq","6C1vJ92eS3Z4":"WizFpayzxngf","2x6SXfFhtJkd":"OntJqHoodm3T","9Dw12nIXbk5X":"0GQBEUpBILpm","Z5ywYToWpcug":"u7scPDGZlwln","rvB36JRxgj3q":"7aTPfdUj9DVk","CThRfJuvO629":"iYLjj7zcdA6V","LYs1h1qVSG7x":"sgtKOKTsh0sh","2j8pQEIVcK3A":"luG8GHRr53F6","uCSV58GtaZEf":"77scqGliWDdC","kHWA5RNvvrqJ":"hCS9O8slo6WO","qOSjI0JDN8MC":"PefRd9sqmSMz","CX19SYuXTXFl":"a0vjxVVcVMCn","TXmBHTYQClK1":"nqFrKcpuXnQA","mI5Wqvy1DTyA":"TyqJjWrQlEt9","fSsVWVPnUqRW":"Mr7km6p8OOi6","1awG6DRNYJlt":"FHO9aAEyZkyg","mtpXwMnUhNn7":"38vBifm7xSVm","4YcAJwLEvmuS":"TRQIv7tabN0g","s9GIPrBkhsrx":"Uqr59dmKPXlb","80hZ0gSl3vQW":"pZDdFf7GkMYl","sbFOzkSiOp5H":"PWmvXiu1hZMf","zrcpRa7RRC4S":"RjC3lkkUbBnG","dq38JTQeZaB3":"dFWbg8Lv2Kz2","uwNrHMpGhh42":"G3sYnWbruHDm","XLLLYjr7S7In":"VkPiaNWUTGd3","Ncx2IeVWs0d7":"rJ4LEGEeAmDN","bARDMo0PuhyS":"uqUR8YtdkrAi","cQr4leCeNHGp":"AeTGYchWORhl","KPmsH5uLJSeM":"tqNxpmMwdakw","eYIeypHl4t7a":"dpZRo1ly5iBo","roqp3BhjBOn1":"FZr4M5qlRTc3","rflarBxePGBJ":"fX12iO5eyR0o","e0bMydxVKmtk":"iU4BovxvaXhn","Of7jnov752lN":"c2DkIwug2GqJ","fJZzg9cyIdDI":"oW4pHq2paUoh","GvvHeZHYTp4U":"d4Bwrd4rsK2F","E2TSpwjp1VEI":"t6T8ViWKPrzu","lJFeBVU9Mm7P":"dfY0P1YzD7ue","kf5QUy682Be4":"kUWv3yRh8dHs","NfHwI81v6T04":"Bv7fu3NirT76","Mak2OIC4Nr9N":"ObwWbZnQAp88","PIm6PePFqajP":"YWOEaYdKSnbX","gOXSEeGwk9PC":"BA1fewFwKLOF","hMovfS5clb3W":"MQaHZxwUrmbA","CBnAnmQTFsyM":"cZVUmo229u0g","Obwd7Cbe0lI1":"4OFn8lzR92Lb","cz6DMVmAUMwE":"Ik7JOFfCA52v","Ks6kUHK3MvUI":"9frGSAe1UpYy","djIcbN62Yqk3":"0pgqklHOzVLe","lwVDQbu1grnQ":"jKyyPEoFleqZ","w8YuaWt1NXYu":"oxi6MmgvQvIi","rxOTSTQr2FiA":"gDDPpPIlbsf1","8EWr0hBSLPNe":"HqtlvbG1dPt3","7r3mLXHDCeUH":"0b7mt0dbgmR9","Q5VBNfi5WP6g":"NlHigUKgzh4N","OFpKttOvmOAy":"hRB7KQZbnJYf","SILnnc3n4WV6":"Ks2q5nKvlXND","rrLy1hebWrcZ":"GuqiorKqP9Wq","OrtZmggGAvaf":"W09YsyZeyLWU","j21tpMISGaAX":"iBddR2CnfoZx","nTavsknwmOwh":"FD6UasuCyieL","s7FYbm8LEPFX":"tEypb4k5wG89","pK35t93gGsv4":"R9RIJv1ro7Ow","7MX2hbvG73Yj":"VWZIMZHQr7ys","NOI0qxVuNS5x":"B3B8qf0iHFmg","n3wTuWRcT2mn":"1BjOhUWWOfdT","OaiotFVOHJz6":"smd7lDks7lEa","aLSeFLaq0mLY":"JxHA8tm9LxyK","RS2Tmyww89aR":"tLLuCy7epBxh","0PXud6tlubVg":"mFxKb0xK1tTv","euCmZJW8jVXi":"irS0Xi1qE5db","crqAfJw80UCq":"eRI4LA0aLf62","Tz9gq0vhQRfr":"tqCBBbckAPk2","8XQpTgFsSFcn":"MlHRivgHaFzN","sYTzwJYrfCjQ":"V0lVXwjXG6Kx","6SLDhVKm7ugg":"nXX8eDYYmdgm","VuRELHiMpxoG":"r2ZtF6U4Psvd","06CAcyCYxVdD":"1m2eYaaHc2Fu","ifgwx2pv8yTI":"6bRf6dRxtAV2","kX3aPUBCvUOl":"uHJ3mFqrJkL3","PE4K0AniqQmM":"2WiT11fMUUNv","pn90LluUmJbj":"kM4E9ibCAPOI","gAcDXY00lEP0":"NCT4makcx0fM","8iYHAfuTbwQF":"o97a32l7rOYG","n85Nf45iMBME":"s1HYbKxcrT3w","t0yVE7qe1kIX":"xN5jhkvoEMjQ","ri4PH8VDjhw4":"3ZbhCEONf5ld","bCJIl2yWKPeI":"0jS8ltlbX7rx","NLAgnQfVMnZT":"seCTuGPG27AP","UUlg1p01Oht8":"Jqe9xFv7NwgC","OEMcXO9bGHnR":"oEYxO6pmiDzH","VEJD0wG7Q4tb":"WcLOblifyNEA","YBm36DFfcSly":"O1Uop0DsRSGQ","OBEOqjoPGcf9":"ms6LqyYp67va","FOuRXjvIbcyS":"3MtATEi9nuW1","THodYT61Q1MZ":"koDaAp17h4jk","6JGkkclXGFwS":"aIRBwqB3YrB2","EtTRiYJy9QZD":"ewhJuuMDbU9S","bYT9dGb0EToB":"04Ime4tig0wh","xmerk3pFHguj":"5woLegzojnMv","3bgLySK1UqHa":"OuwGSW3Olpab","b3mp2AHiyg9m":"caP6WA2XjAVO","HABM5bR3sNXE":"2T51fpYEWqJj","oaXAL1GHjdTq":"SF3EgjKlN4tI","YWJX85desJjD":"L71aFGZlJa6C","0kgnsJz8EuoU":"kmPYQFefr80u","OmRfXfvS8tZd":"LnLGZd0hBkOt","PPe95kVnhH8W":"wWr8yElvZCK0","zxN5ha6SAe90":"Awmjrw0bPlV5","4PBHUqOVNlaH":"3dKXOToejdWj","cTGbCuJiAaO4":"TO2ocaTfBC3W","IeZYEhU8ibZQ":"UJF9HoZSmIx1","JbuZ945kVmVO":"ftDxQmM4FRo4","agywt3gZNyKT":"bNZpx2YewuBq","Ip1UYP9GWika":"bK01Gwm9lfLJ","x8BHBJRyEcEl":"mNmol0Jz4ZXr","2uisyenGVP7q":"Mx5hk6xgsiKJ","qt63sfLZXbaS":"hF4KtTHt9jWh","u314c0tjf3uJ":"poaZDUOxcTxc","f7X8atI71DfH":"WwP9H09744Zr","LFgTxQ9jYxOe":"fhcrHI3i2YU1","CuWUTXCuRIEJ":"J2EfOfXd88LY","I69QVDG0HGkd":"Q2fPRBtRFlAb","yS2uGbmGHDO6":"7VRSmxOgcZmr","kTn7DEHxTfOM":"gUG3RlsXabJO","9gQOyj99FXKW":"F9bl1aOXLlYX","nroUHDN5gSHZ":"3r7bwWygtAea","UxorC4d1kkQU":"yYMyIoWCl9Wx","xSsQsgJJGa6J":"s2lZqRyRCVAq","bJO7THbdzQi8":"mWSGWdgGtoBv","F3HMqSoeRMiI":"8VVW7VhSZ4uF","HH2zBfuSrbEP":"mFMULhOdjC8W","UX5B1jG3wOId":"Lpc4XW4hA8YY","ZzxOkDZnr2eY":"6sm3yXYcqrk2","RY47xknzZD1N":"LNzO8uwhTOQd","MO6UWejP7AoA":"hog3jfhmnD6D","szMib9c3SpAC":"q82JfKIwqIp6","8NqFO2FeUljc":"Qraycgdo1enL","zjfHw1e20E7e":"L1q1GV7xelXe","Ofx5KsWrbStF":"GHjpxCB0TuMx","aj6tTomzPQPr":"m1V5exIh1oMH","tPNfc8wTovfP":"YqGWGGTEM0cI","Lwg4ZL65abv5":"mSjvKDTuCeGT","2lSXpYNkqlUi":"8MxRHdgoqIvr","eFjhAvrnvjbt":"L3V3pxF8EyPn","Dvca92TQtzJD":"LWX6SoaSb1e6","elNCWvZwLJ6R":"9ppdcQY5dWOW","CJvLDG6OZedm":"pplCRKtYaGfB","yBtZtjsJUpWD":"3TsABkb5GK88","IJaPBdSjrlhG":"ogYvRCq55e1V","3ayi19GHkciH":"f6erT7sz1mRi","w7HVpIO7mniv":"uvKgXK6LJmJ7","5EhiMPZbI5e7":"lILxBp7LSBmw","glSdutxgwmD8":"6U4C8sIk018H","DFfKEJKqPLDd":"1BFN6h2TcZvo","CofXbQdSPjCU":"VQRa63aE06ef","DWcueM0JyzxI":"KD5p0Blgt5Q7","QBaFmVpDgLrG":"yROiPwvA8itm","ZVQwRf9vkbVO":"l1egZODSqHSn","iREtp8CRr0e2":"YU4cpaQ6BNyE","qZBccf6X8U7D":"PF5GfoieXxkb","qTNIKESJ9q1w":"51iIcwAm0NEt","6ofWIDsOqFNN":"KHVnslHXFLIk","xVstTKmLrvYf":"gM33mICjknNj","7PHpqD30kz2A":"P18XUNS63zXn","XpIbOG84GZVR":"oue1x2rhxN4E","wxkpe30gtk6T":"E1IDVu1O5omQ","4mwCQ7TjMKZO":"LK9smX0IMF3R","69fsWJu61AJq":"UmYAG9STTOsa","ycYlaVTjL82l":"8RruIUC3mGbu","KAOQ0IiJ5AUL":"NbKlWRoLMTZa","dODHO2r47SQ1":"1MywW0knpGjy","aWTrPKG7dmQ3":"0BSVHZ2rbiMT","R4aGyT3o0eSE":"B6cMfAEg06Vm","RLEhMgqbNnqb":"E8ySONXtSGxL","wGwsPSBlUzO4":"JUR8NnQsXofX","Tafa1BIYhREt":"d3mThrZeUgCR","RYGuAhggvnxe":"JieeHqgRslN0","ukmmq0r3G5nn":"EgYgiPywKryK","KDLUeKFPEOI6":"Bj12Z09hNql7","L7sQp3YCwu7s":"tkqKHh4rcPHM","A6wMhImHvQe8":"xMjbPgyWBNlb","VZUIGFhI30ml":"nq5IuXZfLC6A","Xt1vepRpCfwo":"ExcO939iaP4N","i5ayoZsvolSR":"pSt0Tm3oWk66","KIHDyq0NzqoW":"3awOGVkkUn4V","EMT0dSyn0dTw":"nAi1I5DacQIs","OdipYwawObXH":"QfXJm9NmV5My","SJvVSd9slt9M":"MmLBLbunjd4m","H197IsEBOV4Z":"3AIO9ciEN1lR","TxUdjAYUeuE7":"8fiAsnXbsXea","scNAbm1Ohpm2":"GmQSs8l2Upen","I61bxroxrJwh":"0VZN8NgjDBF1","WB6a6gKr2MZl":"ohjEHXK11GrS","Mh1LYg5V6UVk":"HZN5dovWDiWe","QmkJXjdGfW7D":"LSd0LRAvK8C5","nT537UDif04y":"oFLHUxfycW8k","r5Lo8qhJzPCO":"glsL7WE3frEt","7g74qNN05pWs":"9N3I9hMFySbE","yBDsHYAxloxe":"LUZ9Idz6B7YL","SMcOIKuHOdA2":"VfszaZjWXJOz","KkTPBB7wBPbU":"pnjFhienOsUI","gW939p4KHLFi":"399XYN8Pr6ah","77MSmaxTTK3L":"3V2KYQq9PXuN","2on9gqqQiqZ0":"uBgqv1plLyE4","3MBTGTfE6I1p":"P42xmsRXEelx","ldru0ebr4zXo":"66bx1ioBD616","sfaHhnsEgL4s":"rKCmcmfR4hAQ","sq07EMOxlexo":"HOFPCsVueafp","9keh9iYIwJNG":"e7SSto3TOvZC","WdM8RhkdpXtp":"iXodP2MwHVI2","fW43TSNPTHUZ":"rDUERHLpjmsY","XygxDKFo8Zzw":"idxa8HH7Oc5h","OooVTubs6DUc":"rZoyKonMqlMt","EBKkkSw4i5T1":"PNPUW5BW4MwT","Ncj9f1xq7ts2":"ofiggciBfvGX","abx6PtWlg3sK":"G1QA67jsTqvK","DKg7hBMBUA1W":"GvXVjiiaH7LR","T2FO80XQtNZB":"j2ywEnsYoxMo","Qb1ch7ZcmwH0":"4rsyeHytRiN7","xpFzz9cbmpkH":"DAhnz8pUeAyM","mrTlcBSw4xe9":"yFmpvIupy4Gi","qOOvIi35jgCF":"q5RN64SjenFH","i0Q12CwHubbX":"5LK9QwWQVxvc","IOcJhGvOMqha":"g9zcUCCX43K9","pusSQM5xlvSv":"OYxcHJdI3lIu","XXg9EDg5mOsV":"PdN79zEmGqBj","kC5ftLwdvb4M":"LqILjn9iVWPZ","4UwHyqpZn7ax":"4AOUpKLXdqXg","oQ0jYKwL8iij":"mxcqrRI0J7gh","bxerq4ILuCNu":"m6IZJc7scJKL","Jxe5tQCHfXbm":"KRZ5lW8Rgb5O","HxrEOH7lsvb8":"GkjcrlYPTmq7","lHnfyf69Pf2u":"f6vLlNnypYwZ","hIaRsXQJVieZ":"gctmDtg46oHh","MlCHmAtIReAf":"vQ8MvLjmRYbo","KFE7d23lTY5G":"77HbzklIh2u4","gSgC0oBRBJKL":"Ml0vEGNXuURq","4FL7ftQaLXM3":"uPCoPzS0MBw8","g45KEM99pTV4":"Gbos7O5PXFuc","wCAaeAmE2F9c":"B2dAI6AN3orh","Xzv9GivocwhR":"KXwRXB6eI440","wPgZnw6MI5Jq":"HmVp2tenz6VY","4tEOSyom5n8T":"EFqKdNmI6Qr4","KGN9bZsn1UiU":"UwaiRdi0Qx2b","MQw8iS18oVzN":"2E8WxiO6XSJQ","TeZw3Is07qKX":"S9XkDIGI1g8f","Z4mYt8aED0BS":"Bzx1ED3p7ajn","iMZhAzcpedbo":"MTsHEOGT9Fet","IzrVGMEQ1uuW":"yg5UkEcL203G","rR12JY0vFxgq":"LVWQtQ36cx8L","Ar10V0Jd44xg":"w0tvBinevWnp","RpKHdDM42Tf8":"RXLyoFaErfE0","xV3o27LqdNpN":"k7sAKRV5ZRbc","GpO0Wxn8Yh5j":"zPiNQ2soHwei","vEwbZzFV8uET":"aTCptxCX71oS","FFJELrC53kup":"Suoh8Q5MkV1c","9kPW6w9fLW2v":"jQCkiGSDBYbl","hpet6rvYraDq":"gpcmNC6m4J8N","75fCDs4PG9i7":"wRJZsrjUN9rL","aMkQaeWeY6vt":"YzL6wTBmGyC5","TN73EImOK2At":"GefRoiu0ch4M","8QsgWT1XfeEV":"Yw8oZ4qJqnL4","n4xCjUvooVpt":"VkmtBdkek7sr","Rst9i6BXWrXq":"r2h8wNZg7NzS","HfAXW5mt0aKh":"zEjvxHr0Kg95","UFDNI6BGMu2N":"rxU3zic2AvTJ","7J42aZL1m57Z":"8N7XilrsJ7CR","sMwQPIkSVUdu":"gcZXLL4UGYnJ","rI6WMvYX3LED":"lioue2LD9IOW","glQDu3t4aNvG":"saym9OEGQPOo","mwj4ARxbGXRf":"RDrkUWDb9ydQ","RNnuyUvUsBkZ":"ZH8cMZzcHM6P","WAClnm5caOck":"kynu7wfbn6II","FaQ4OWzJgLjc":"CaAlGcUx1iie","VJTY0TidGr1m":"f5yoK3o0LvnC","tjaiitMPL3Wg":"znciO3Pfaezp","B1qxQO5F5MwZ":"Y3YZC4WTRZEy","aXVb4HJraJ6Q":"WeyFpCSfXNHf","Dx6xM7c8TSZK":"JA4rnzSBHwUk","PepMkOb0db4o":"nhoYCUggzRKZ","1GqYHVvS6UWH":"6E25ZL5qV90u","KPP0Fb84WdwR":"NKgk1Wu39AAj","z9J4ei7d6vnb":"4zlQ4vPUeSWn","VwWeWRUD7FrR":"AZo44dy3nzT1","soGaidTSiSlu":"C7ND4U8enIJ6","aMeaJdRtd4Z8":"EZwhEo8a4K4c","PPfAlNG10uq2":"TOzErRz6FCMI","LEkLjXVOTboQ":"bXC0NmSkyJ4e","ilzK036DKeyY":"rX039olugXu7","71yIWDgqufof":"HENOA8ljhXwG","fY6lvgjr79Ho":"SW0VMRixdWRe","lRPkCIijmOEE":"j625t5fTR927","K42vWWU3znwD":"E4HUBcrElzEd","lKavIyO8SuTd":"aKu5QaPZACF1","IGaSXDycglXG":"Na0fqIUJQQ7O","eIzwsboUBXEI":"SZ0xpri3nblS","hvcAbyhPzvr2":"VuMlKABNq6GC","sjvalNDIppTy":"BzXSCGZ3oSiW","HuEjkO1p5Q5z":"1IGSaQ7C5uUY","jn0qhE3rFbLL":"cAhh7Sma49hQ","RsydICf9bS67":"uMXTzoFidbZN","7xkWlTR3trBK":"RpUixG44K5Vl","eWcPOH4bXgTQ":"mzdpbtarbVea","G3RUf1E5OuMQ":"1o9PbPq4mX3k","s9I1SZwmT5Bd":"HxYFG9GUi7IG","ByTZUTTT2DBl":"HfTVkmnGspWH","U63Dqr0iwprc":"Idx1DPorVfxT","gL8t4CtgKDPS":"8J4Mo5QbBk2R","iuJWj64vJb2u":"B8CApmHc4ksD","RewFEpWgAl87":"xJlfqo5MaSco","m4OrmfWEi1da":"VWj76pHFyHKJ","dvPM8qPaoi7d":"uXEiu1zfcrUe","BaOVeeHegF44":"iNnhANbz784l","Fjt3Oxygy8W1":"9JY0UPmoRjCK","dShUigMxJQ54":"1XEIHu5JFdUZ","nOBTjth2U5e8":"07rE1Lt9iCth","AuMSkvzj15Qh":"lvuZFIMBf9p4","rLnnbhTkZKaU":"k6FDsXNasY6i","RWi0KZSa74FT":"R2XN2OP4C0dX","T0O7tIkgKjrL":"0RywgKNAd5iH","yPt34nQN5oXR":"9Mn4Py5TH3A1","UihlAlE23nts":"R3jHvp0cYvLA","xCwX3Hc1C1nn":"YOqrBqYlupX9","NMQJvjuGbIV1":"91PZQ9Y85H8q","BVjEifS4rPL9":"KSipeWiOOJzU","6N7Lm49SOrwX":"EueWJ0UODeQp","AW3qT3GKBcAJ":"otLf33jfFkuH","5ytlaymC8P8Y":"mPYm1ynjmdyb","EmhYUmS2B7W8":"rh5yu4QQzixR","ePNAtmEG4eEX":"l0TCVtjFkumG","tHuIVgubvIl5":"ennWZj1XBfOj","owMYmunmq5YS":"5b31owsmxMSf","TsvyBfGsuTvE":"VNNorw9mV8Q1","sadwrOan6r2T":"p7S2pMdFLvMy","QdvdO5JASysw":"LsA3Guu7Lb81","lfR1CRcV1faF":"d4gBHC6qFsLA","1qMb98vB68sO":"eVYbWKGNn2p2","xZHgpjPx7Mmv":"3ER44rtCe9VH","DlYJxVoYER4J":"pxd7EJzqPOX8","Bh6963FYBFXb":"OuGZDbT41P4C","Ltov2MNKtSP2":"V9WufEWewoml","jVlxXUEcFLMi":"jChmKdvmL3iq","FpsvqASyxZ0r":"p3wXPk70YXHL","HJxyZk3PJgml":"HDFDSm31qhMW","Ob91KBaLCfRp":"mivuYz2B4Hdd","U6xSpg5RNCds":"kLaQKQ9HOkaV","10vdqxqjbGux":"yVZCIBj0sANr","lZvvnM8jIPxW":"CubCARUlvT3x","9LIgjxmwlKuW":"l3qvuCzvItKP","YE8vLo8J5Enh":"jd6Eri9blhrK","nyD9E0miMNXz":"nQgNCSxAz9mV","BgUSWUwO60H5":"II8mRM69l3eq","lRFBQ3OWoVBn":"3ffoMlpRXv5L","x9rOIYC7G1An":"XM027poCqakK","dym5ydcG40JV":"WJ9KNRsre4hz","3iaacYsT3Qt7":"o0owxQqevbem","BmFdsoiVOpHN":"X04QhZCGZUgS","6zlBhD0SyBDO":"ABeC6ihVPhPl","Sv5hoqzLBaIj":"QeYx9MkULShE","MgN1CI6AGgjA":"2qr2QmDstAhS","LsP7QvJLna2j":"TgXI5dJDK9wW","Lq04GVynfkPQ":"TQA1M2EhsPPj","qj36Hmk8Wth3":"3Q5Xd37D7Kdo","Xa1XDZOCs0NE":"DC0ZrBr3bNjG","qhOrPJ38N26m":"PyjPtMgNNxeb","G44oxaSStuAz":"PEkgx7Pej8sm","UPLK0yllJyg7":"h2JsOWCqOTmm","Kze0qtCrmYYR":"xvJN7HkXypVY","vClxXIvwQIp2":"BNfYFouQp6vE","WXZ0dHQbD56Z":"lXbzsYri2Yzb","PRWz5KfbfM3k":"FwGq7xaivjMs","HULg4SsCtEV4":"e9jP0pjYCVrH","ZmgA06bh1NPh":"8Ur581qmiKUf","7Jiu1ZqWTYMT":"K2cYubfzCawY","xr5kFKFazFF1":"KUdXvTkJCTID","qBo2kJRSz1lz":"f5aR0fOAoZE4","4nASbYKmCU6j":"1jMjnats77cM","ttgX3wd96qM9":"6KSwCrjdWasM","O5XjNxnY7djn":"9Sv19hQdfUVa","FlWYzXQHoZv0":"WsHIWjGwMvJY","gpKz7tzvwsB9":"V2d9hWVaJlr1","qcP8Fuz2SU9R":"aMFdD0wmh7iW","KRPr63Qmi6kD":"DMi0XuXmFLsg","2m9GyrgMIDAo":"esQE67qn6Y74","FYUcRreujOVn":"NN4scRRLKOgq","aTg2itbptEcS":"0Bl0RUPXJgTZ","dYHdFcJU2QWS":"ZUq4FG9ZsTAw","6h8zsJMdLoFR":"OUfbcYMdgbeQ","oKYuYZrZhgKE":"jRCXgR2zaYhy","cvaEDMeSmCFc":"e42eospSTkFF","Bm0D0kXuFxwZ":"yw7aPyZ9Y0jD","rTfbPJqTVl1c":"8UarDz7npn1b","5wuuoXqRsXbt":"CYxOw6kw8Log","1nmvFnqWdFqd":"bB4Gry90bIFl","MDhNzGW6bR8b":"7ajDj3zhKRQO","8jz4pmATpkog":"P8uVvIOvudA1","wXujIzTeVY3s":"rCBEuFXOVAGT","OjTKsdF7ZzMC":"bCMCWL6K0YMo","noP6AKx6kAHH":"im5W1hkIs0p1","tPvmf5Qn64mB":"lWuO0gaKz8Zl","F2y7hJxV4GMP":"dNsjIG6CUzrk","KsgnVSWGxiVn":"JWHS15Lje1jm","b9ApvjenmWFo":"mmQw5SetqGuT","UD2BaMzuwKjv":"MZV7y7300Fgm","H3wZASuBg1Ko":"u596DN2tfWCV","8xox2vUEkL1W":"Xa7uJSinhZAH","hJbL8stDNKKb":"CpvhzQRgxmkg","HKQ6LS9gFj4t":"OsoL9Z1ZUife","Dkc4Z7fjdrk1":"JLee0J6nWBk5","5ivw86p0FvAG":"uvWnpfaXuz4z","lY5ISKeoz74D":"ipycm5Cyn75l","VNvYY23U5T3M":"sWfDVVWfVc3s","EgH63bQlyAiM":"1I52YZ6F5Ck2","DCxGiGGziNZU":"LQIcFsqvRR41","SRZ06JMZrLm7":"bMzLGMYDRsgy","1GE1a6GOeRXQ":"iHRHRbaH8oE6","nSlhX22P4WY8":"N6AK2oZOJ3kY","2G4STOcV14t8":"iSAYNS7rqQss","OxscWptxZNJn":"ucs9isDWOSWF","KGBYXr3bYfrV":"gGQ4w9ZFX38M","bu50ACKyYyg0":"XJeHezbbrXFU","pl4ytWd1rMKp":"5TSSyu5tjwHh","al19tp9j1Hic":"8lqLTUSHpajW","3rbSpSQlpqJs":"oN44MLGVssI5","CaWAfUUWFJZ3":"kwVm0GzTKSsW","rrhWIowJnZ1D":"N3FsZk0pG4Ss","CvCOonlT1qi8":"cbmMVlzRpIiq","fmOevraCX40M":"LlSP1ccBCYLo","pxRV5TcNggiG":"3XrTIGFQNSwW","gExagHwKdBTx":"FHUC8NuVsWW0","AvQAimSLSSYb":"NCkdUPsPgNo6","HwLruxRVRMo7":"ImxyNO2RA5qI","sJEMRdIyjjBL":"y43q77FYUPXb","b4fspjAgbxvP":"jw1mlNt9vAV9","yRk5H9drelBG":"uBy42kcFCc02","jKUPt5FYDlkx":"HqGR7IIlyInK","xYA6gBugJiOk":"57gq6QUIFSzz","kxMLKwoqcaaR":"CVSKWkDYvphg","7qxm5XUXidFk":"B40YgB5CnI69","uHimDcu1jupp":"8TouhGwaPToA","yc9EfExa4iK9":"IB5JLyFyGBSe","aYPpXQEPunNe":"iManHgfuVwmn","nTOLZZPQ6idF":"dpExP5NrxS4g","bgmN02E2Efjm":"BYVptkozT4Qg","FIR8l7I1w2bw":"NllNAjkHy077","Pzc9SA9P42LK":"5sbu7va7QxNF","2ovxR6bpUiuD":"PrdBeH8iYfCi","c02eKQMUq1fr":"b97lScuNKr1J","X63702hFZkz4":"TQVzHbwM54L3","9wsV4Ci5h9fg":"68uGkH3hkTSu","JpQ6BmVDR3v7":"P1JuAkzygSlM","xylAm7YA2j0I":"QWnX7Awlo8A5","n04T6rCqkDTC":"uHFdf2rBp9hS","yE5LZiSsY9X0":"eM2NKZVxZrxx","wMcdUgjhWlva":"rHv1YBvgMvGm","ZHb50BnidWAk":"FX1MVMFoQHuZ","csjADzkjSmMb":"3xpAqhstfa7p","rDcdbWuYiM6W":"sARpcRcwBBN5","6k8sjTECOFBi":"5ew7OCdYbJnT","mDVA7FthNtgx":"aqGfQQ1may3x","f5jyhAtgQoFq":"s3d1QF0w6cMg","b7oo5yXgVqQk":"o73NDDG9GTZL","cRH6D3hm32gH":"cv0ejnlnt3v0","MKR055pdrbWC":"QPAAszrDXoVt","EWhZwAeLxtr8":"ULQ486hN9POG","ZYEAZkEiLPl7":"GVzspMmjYUH1","iygA7PSZiLeq":"6G9GSWvtTMWu","xjU2r0CsvazZ":"LoZmEpsXDs9A","IkFdfqGDcZ9l":"floJBSSdUVjo","dnM7oLNmUmq7":"xh3vGWizR4oQ","F83W2UwXJsXA":"DqCoZroSIs3j","7WanBOJFaper":"jk9hzgPTglnW","akNz5GtyL1Qn":"UNo14txbpfeU","aMwj5P9lB1ms":"paFRjNfozoje","bM5ZNLM105ui":"l2gCg8tA3Ay6","3G4di9sk8i5w":"kU8ozyCoqyTG","d9tKtNVaQTR5":"YwE6MWfDs4Ty","Ws7C1bszQOaD":"4Wa2hcoaiTAF","nqx5LYrzfsVW":"g4Y5qZWsXp5W","cKakTSyqpTs0":"oAw8DdJwVtyJ","CEyFM5JRvVan":"327JKhbn8OYK","oEvUTZjcINib":"dj3XOrE2YmpG","jrieRbAmc8XZ":"kjzNCcz7RjLY","KDRRC3z13zmb":"WbPZ7FwXh7zp","RINSAW19D7nl":"XjwT4DIEwE9v","ePN4lJPBCrRv":"7kF8FkstkvRs","J9eNzXwpQ4he":"upF4UiCfRW95","Los0Wel2qFE8":"qdt2y4I9w4yX","OuwXwtKXtaUD":"dDPDZyBfc9O8","7DmWGLYJDJrh":"o7Vifh2MVmo6","hVvr8E1CpIve":"izl2ScHrfK75","CoYxOeOH0X8p":"1HGyQ4QVSsbm","439VJx96haQk":"eZM7yX7maZUG","gYLVSBXY5yXi":"5q2HqQgI67zD","YBwg854mWVCD":"dBlx5IfWLXLF","98099jTcFRJ1":"siJmZqABUNDv","5ZZr6DPE1993":"uy6HdBLcxpZB","9VlcZ3v3MCIJ":"cmP5VezsEjut","IcfdknEmQAlD":"93XhdquXlLqB","nkiQyzOB88Gd":"7J3FGjUxN2BX","kH4HDQOQz5lb":"x1LDAKoUbi3y","6gXvX3uLsfoI":"qSrffbbb6GR7","rFUmkfokdKJD":"UwdNwbaZAbxC","UxBwiCuVXOIb":"Gxlc77VRjZi9","vXLTjyFeqiKw":"1KFMNnPhgMh4","qcW2e2IGXo1u":"sEWuMKPLZ10m","7U1WSUA6WRvJ":"hdb762CSC8vR","oDxKk8CmGzX7":"oaVdjoJPEuLy","XNDk5J3gnsU3":"jnCHnRly7tx7","s0cp8g042fKj":"N4OPRwtexVDt","nOS2r8b74lxE":"ATpllr3mEO9c","9bxqmomXN377":"JiNwTUqlFypc","NxdQMhwzFRCX":"zjrF9QT14q3O","3Zny0Be9DFs9":"EV7AbVJOe8DC","BDVdyQ5mJvPa":"4mPmcz4FwBmZ","5BhYNRCaHFos":"sD7tkAIa5z3H","bZ8fAY8TvUaP":"vYLUIf8zO7LS","tgsPwOZwBgPc":"yQl41BSfd2nz","15WJbZ3Cd9Lq":"gJABt4jm4LdB","G6b0xq9yEBl4":"rIOTi4K9vQuQ","jF6zOa8c3x8m":"T0dBZGHaW2pg","FUO4mrmATO2a":"fTilNsjeeMFG","akwLET5w7x4Z":"ta5dtyO7jX9t","AsCDE5Oh1xGo":"wc9vAz0QXEMv","CyDf0kNQs7em":"lD3mNjOtSrB8","N6djFaQu3uYt":"NCd8P3kxun8M","YojKyvOtjrGy":"GoN12LYOfMAm","3cZ5z1iLqB1T":"Vdv8FnzDGGeM","Qrp7URZ9wIff":"yY4Y6ec41oBx","etrH4qS5OCSG":"DpCPZxqLr2l6","DlzIsaRzRxJK":"zWXfK4hyzIFQ","5YpjX8TvX5kX":"KSdMrVkUKgLe","yY7aVUXRXOz0":"XKDgpbRxNQgR","a8wWzKiCD9uv":"MXL77lVIHqWN","c06pcyWvJ4mz":"Qb3QVk8bdyoE","aCHMkpXtN1SM":"tv3KMrNBk522","DuLK8RlCj2VB":"njDafNiwBtUV","N3kYdOW2DaEi":"kCdOYkjFlgmv","LiTqZ1v4PzIa":"7HBtdaTk1nPH","WiHR0km1lkrE":"O9eUF9R5sKrX","Ul3bETrP0EHB":"vZWNeJNQc1Rg","MFtbhKUDuB09":"G1p9E8LkIMiT","WM10SBbBiNhH":"mRkbMZzazm6v","CeHZMJWfkpBO":"3WibrsQxGgkk","NOGBkzMOQoJY":"2uwMZ6YhGLJU","Q6PimWUHiKIS":"Yu93PIOeVw5i","SWwJRUfGT4VA":"ETzrro3tcmQS","Zp0nv0tlbuvW":"wZBKhSBFuKuM","7mYrTcB3qR9r":"uC6WXEHPyQuF","FO77f6rc4r3J":"rJ8yCEbudpFa","3bawqT9ProW2":"qwI1opZjhQMY","rH1PVfbPHvds":"mC8nwRa54gnw","KUYWRwb5G0X8":"sSCHWCpVLGxx","wRblEavOsyOF":"DHtPHAbd675S","PdIpv6iTiYiU":"GOUklCA31g8n","J1Z9i6apsp5W":"TrbQ3wNTgEmB","DLiAvaGe9rML":"uUwNcWkmGQDF","hAmu7I609oNf":"cAcBxifMjWXG","m7wd19vdDsMH":"IDNzKiEOm0DR","rHNSVUiKwR1u":"dRTIRgytAEzG","icZgMNGFX3jx":"8f1vvtmWfRya","7011vqhRdoTz":"5Gdiz6ur56Ew","zElOa4uJ9BbP":"QIMerfqjq8U8","TnZBHLL5dDr3":"0CMHMWFQ2HRU","4xPKopAFkl4U":"FDztpFNpeCHR","ikEjS0fm7VRK":"Vp6mmvhD84be","3MtkVZwG9Ztv":"CLXdCLDLpvOg","dOxBlUUOigep":"wAHHoXYIYkcU","wwcLE7FAz1By":"6KlcAZKhhyuC","A2G7yRceacZ8":"94wFlkHGRMCC","QONLt2YEGB0I":"u42yY4Y7pEPq","6d10JwIHExex":"fbAQ80AoyA9g","XADYWvp7IPBI":"OdsJuVOTIJMT","ZBb68mxO52Pt":"aB4QVX4imAXa","s1jcJoYgrt8g":"hA2m3fvPfXQb","LahahogHza91":"voVlIeqwNR9x","L3PGhMljygOR":"KYXJfRoYDdCw","bSozK22inOul":"NeAyOwWBJNj9","d8IR4vlRlvSB":"6ZmIYF5kgscy","uxYjJBzH06Y3":"nHUI4rzPSvUE","HesXgHHubfpK":"4E11VjzcE3Vd","JRIdgH1JL0ox":"asNgzpdPeE6D","NN8ubjpuzdzJ":"lGJ9IQcPLUlL","6WggCgqwcniL":"qb7zGJ4VzrJ3","adK6Mqj5KKwv":"BJYVXLA6F5pm","MiU5fw34MVGU":"fWdjEWlcxJae","cCnbbVmPqnM1":"g6RN1LjzvYvT","hTAj1O6uVcvW":"NeiEidD64Yct","ZbNCzdgUzaxb":"0MBc62TBPbJt","eyGCR1AIlg2v":"L0L2DadhkJg2","OnBbVfPUuEIP":"runEP42iMtPz","xJJAzfoo2c5j":"c4y2b1GOMT74","3cLqEP2O59Bx":"W2N4EchY9BEH","gbiyEKXvXBgN":"YwfOY5RjsIir","ziU8w2lR4D5t":"8kuUVag03R9b","JKaLapP6PW6o":"ScAVJL8rCkj3","wfh2TLIpPQfo":"dKCzA45uiZ9l","OstuWKgj0Zht":"eOo41Kf6bCQq","bzluFqhVKie9":"eIlDOGjArolh","gctO0p1J6LT3":"ir9Z8AYoM68Z","lXWu1Knpnjhy":"Eg07JaNJEpCC","eCboblLLAxmw":"x6GRda7ajIXs","O3LpGna8JNN0":"Rktez6g3KfAp","rPvpiQYmN2ph":"GZ1DCG5kyn4Z","wajkltnVyD5l":"qatzndeBaZau","irBUTJFr7W54":"afEsO1A6apf2","lyNtRJvhFfRG":"7rUVWpeCwB63","HDlEpOXvZ2Ff":"yToDphWUxsRH","4tWqnD0MNm59":"LSBLoUiMjVcD","2CquBrpEJdvG":"FKiywMXyEeY6","BRlsqhVeTSIz":"JxQhZjlzHZlG","ehCXnEbzFpA5":"FK9NHxtVsxPE","rmgPcvTz28MJ":"QJ7GKVvS75Bs","gJVsIOwYWycn":"bbm8Ykwfrnbd","ifCmdvMgqTiZ":"MT4sShlRGJiA","UpcfYQHbCqca":"jXlRQ1ulTmBF","s7sSnLYS6VkV":"E3U0L2bq5fKb","gsmdQXqtrgsK":"0PZ5ePw4s9ev","LzrQfbvfhw9f":"gaPodjWdsPW9","rsH6yquL5qKh":"bSYEBGWQLLD8","uecBcLjGt7cz":"0nj5xXYg9knV","oL5xITotZzSP":"3vOr5qHP4Gvx","AvGE9pJEYyrt":"nMjzHWN2PbyK","XEvJCD1DWqiq":"4Jv5apH3cacS","tYCmDzDg1lnX":"X5xpHBBGem1o","zeomy4Dw5Ijq":"9MxrzZQUJwiy","i2CnSHrJR2JA":"LMf14AsHMkel","jChvqskwu825":"WLCTLMsKhTxm","vQR4YgcCZUkT":"hmSd64znbsdo","KDwGVb80udDV":"J2sri3fnf9UV","7gt3n7fTQKur":"jhT341w036Q4","PzttYz9oyUP1":"CS3ZeAxQhVfn","DBi1DYf2KNV9":"EozVCPYhnU74","VENkHQtQ9LYl":"Q6J2aS1TqxNd","HinuETOhlsQN":"4raDraihQ7Eu","T6o7reUS8PYS":"X8SjbnBD7AdA","6nuCv1LaIuLZ":"7PaxAmFQogM3","aRYqDJptUs04":"MSRoNWC3B0kl","ECiv3H2YqMQm":"QWhbAvtrNmLJ","P4wifOFuTk0B":"yI1oYlCNiyq1","QyRLLMGpOspW":"xwY6NdrSUWU0","YdB8QpGZwnW6":"YvgulyMDMAqr","QCRs307BisSP":"WNVOw6Hj5u36","gorKvMThyH48":"tJhmA8pNEq4T","DBWNREoZHyUM":"ApmzzfTuIqNM","6fmKAmVY9hJg":"FpUJJuayXikb","iBlx15Sfb5LU":"ez9IS7IbEObC","E5NfmG3jA6G3":"y2TTs9fnIWW5","5IOMqDAGjVfQ":"7tzCMxwCyYFA","jZGa6SrGmD0h":"NCogjghsduvI","kg4LZKPT6dmq":"Xtsao7Nb2JC5","iNCXPGMMnhQm":"YeinpUAmeZPg","tmKC7i1HKdMu":"KdzpqnmFygVx","YIn9IPU9mELs":"r14HADHCXTyB","IM9dFHwcrJYt":"lqg1IliaPf8i","p7lQkinbqayT":"8jjnElG3jLba","LSbCeHrAl0hg":"AQlbdSCDtway","S7HzLySgbOpX":"8MTMll6ynD7V","AGSwR5sYgQ0G":"wXzH1DqOkjLt","l3VvaFmeC7jB":"mtfsWGvR3XYI","dYln2hai60ie":"LWsJjhWpzZly","q8LeSA8ObjJc":"39bKh9rQOcWd","QRMw3J576qnL":"03bxsorIC62Y","18fWczGJIf6S":"qOXXTbe2BCA4","bYKSlaKSiLcT":"0OKjfTGEsfkn","L8UUxX4XTG0M":"L6KcUU7ylsIe","U00c4Gtsu1rG":"bt81rjIdaBPY","B5SlwySphZiB":"CZzNVbCfRVsb","8QUv3aMY9tlx":"6V6memOzbecE","fFAisyJzQYIR":"LP1lIVSglA67","y2DSWiD14CGe":"6s5VeAQ25UM5","YLoJxwwUUerx":"jtoZdaON9MIV","ilH2NcH6ByDw":"XufOOXdRU7Jo","NcbJ3dSLvIPM":"CibGdKKE3OjL","7BrQfCUfMIEd":"cn29BXjXfx0I","OkC8IB7b4Mjc":"7fbZNpNP8KOi","BHe6SvBc0rrq":"R5QuODD7INBC","HZqirmBR0w15":"9H8UsR9m8xmF","PbCm330RdqIA":"OHRpP7Ki0SfP","KjV6uPvGwyG4":"8Qsw2BUMadNc","VBBbGNSp0jxr":"rtPzNqZKmISJ","Rb8gLBxLH7DV":"P0YROoYFpjNc","sEDzkbpCA4NN":"NArQCAGbyNez","dqS84UmlnwtU":"E1tK4uRLFmjB","mkLBTduacI9R":"nvWpFVeHrsNS","WS3W46ziIcwE":"1PeASlbgXUt4","qSxVsdFmH0YL":"B3b92O3tSfRb","5RMwwqCz2kbL":"ta5zszWcJlmV","SdAIDZoofjIJ":"jUmtqoyIheF0","Ijt0dsnQFH1b":"mNLKyFVaH1yX","6j9NAFQwsZsF":"N2idfX0MXpqz","iohDl8sS7ocd":"vPdcisLJltpU","fjrzEkc7cNHR":"gnGmelJRp7q4","7quLGHLeif8L":"MVHjLnAuFbwt","FintQj02qgFR":"P28KFaGBqw77","0Dcl4RxJJBZp":"XM0Y5KDUW0Jo","RlY2kT7hUclI":"j795irSejRBl","NpR7zAfuvHnL":"1HXrnMQ70rjs","Iia0fbpQVwUZ":"PduVabCF7ddF","UAXHgp35hEzH":"3OJQg9p0nBhN","jwOUiKMqCy8z":"p4TnzhrmJNIk","HlQK8Yeu6WFN":"XLRvXdMliPP8","oxUgmkPMX873":"xsxw0b3yzprJ","fZfYYqTDPqNm":"GfIR50aXk3Xx","qNdKuSHQ8qmP":"Urzn4z1icFPI","xzrJITNH8rDV":"yxSSfioH118J","hFG0Cltq7sTC":"UoqtEvgyDRkX","jXm2joiMxxN5":"RHPDSCkoAttI","2KtFqURanu9C":"ZGwSaUUDhvpw","qR6hsTM9K8Wf":"9E80BfCGBW1v","IyhPl89PXBZ2":"eGq9OurSiwGO","nSo77IQ1VPZj":"Dzvf1DhHSfDx","jnWxq1KexXG6":"LVlfg2saqbPa","zC7fk0as0OCV":"GBTYdNnLvtiZ","3lVvS1gGGr3G":"tmenI1JHRezu","WU1umAGNE4cR":"G2oDPcIUMh9y","ks6iYwFeCLW8":"dRGBsM7NbNMu","fK2Fta1cfYAJ":"jLoEeaxWaaGM","VuTv2CjoYuZB":"gKLjwEwgIA0b","SQFGIFJWWujs":"UgLlM43bCZ3u","2wv5fuy49fDR":"jCNG6WqNPG9Q","lumb6qm2yShh":"5T4fcEBw87GV","wHRifiAVvfZh":"uiZxXT7GJP9h","Ix4rNozOntf3":"3vpgcUwBkBEe","y3fGYU5IlYZA":"xCTGfFMf2enK","XSfEqkriMt6d":"D736FXeJNyfa","2uZPBqjhg2iP":"LuLnVfu4mtk3","IJPBRKPQxhYq":"rpH0sV1XkyiL","ITkeQqy22ohr":"b7SB6m8n9YuO","Swfhm6S7EUl5":"8c8KSUI8CbMt","gCE4E46ig6ML":"CKwuoY4vcEOo","yVQyE3TD05hP":"gHtU2tUw9Uig","LBiJnXnzHJMg":"tL7BRnQMHpt8","JE40BrEB0UJt":"17TysTaPeqBu","DNwUaC3ytu77":"va2vxEoRhtHr","TTnc8j4lGGUe":"WvxuwqCae8CK","ZQB7nMloAfIa":"OuolDaS6qw7o","Q9nP5bUw5ufI":"kFVsM7FKXI2A","ZH5cY4dhLIoa":"894of2lf6eyl","Br7rg5a7cIp9":"xezNz9ggJ2aC","ldJ5ZKzDJtWd":"WGX5SDxhAgES","eWjYCyK8M4V6":"Uc25wn7tHmvT","Wt4hjJ3QvxJ9":"hXA5oyg4AWcu","wQIdJyBlOSrq":"etOEMnLSeSBz","YP9CSWL2VIks":"wPiZy12SIROo","pZTjJc8WQxAz":"KvPsm3j8QDcU","oxp3fuSphz2Y":"Ls4lBug9Q2aL","cqe0WPqeVqf6":"DU3QOgc2m3n4","tgZV94v5choH":"RA3cSGoxdSw6","Kjj7LXKiYRso":"a7VMvkXyen2H","56gdr4UuMY0W":"r9XB08i4zlH7","JCBz5jE04LAd":"hJTdHJhDDmRu","itpNiYjBUoFl":"oZsDUY1EPPjq","YpJ2yMbNxrdO":"BJqBj0D35RGH","NB1V3zTu8YTJ":"Xe0ItcZc2zJo","FI6VmVu249uc":"Tmp791nwvvcJ","gP6OQnrlrBsD":"NQETZ4Tud7ZJ","TS3iLCO6xD7Y":"jqD46UbKwXtT","LY3OS4FysVoO":"7QvT5eTLeqtk","wruE0ciA2VRo":"fvNdk9mcv2pF","03rg5pJ4zfbi":"LtmPLhZbrYVc","twSCK7fBKmbg":"pzRhmr9zHVtJ","XYKjpp9NfK7k":"EYOJLml5FJuk","DKiJWonRo3PM":"pQohlx3SOHf4","iaX2b2gcrMub":"sD9dgfK1vpsT","6vdnmtyguQP0":"37a8lKpAUKHp","MttJFHiZh8it":"hMnUzoglrxXl","rKoqOoMG5hO3":"ebDYKXlh3I63","6l3c7ZON4yib":"D1D2beIGzhyr","sm4jsCEzpUmJ":"nA5fDfrkjym2","pNxNYaKDuseI":"FozrK61Ca1R4","byd7GvMe2eh6":"jSEYq8uPxDcg","JmICUHlgqAWy":"AjZkbiViFqsi","hlzgiEYKWGny":"m5RnP9pEKNsm","TT3Wz4SnhDkm":"IvPTVpdq6BeF","C6cn7iCN8JYj":"3UO5aGF6GDHE","1LFAf5AEs1FA":"bBkU1mXQBxWM","chF4r4XrYZMg":"Yh0NvPNan65e","DxYNQ3G4FEFX":"mdCblLd2CGgg","JkXBHOP0fYZB":"xKSqEf6wK2fn","jNn5kfbTEWH6":"ShlfO9YgKi6X","gjPHC4v5BSHH":"OelWQSI5nshM","tCmCRfUnWn0Y":"MghDzX9e5Upg","ISLSGg6NMmf1":"Ffby0RYOhFFp","8gqTJUfmGchT":"rKr0kF4YXMcP","IjkXYCNf27jh":"L9VMBVxxUqxc","vjEw49iAtisE":"F1lWmbhPfJWr","GB5cXSEPdCXa":"kTcPhZduWIew","d8pXKHfbXj1K":"x0SpYrqVrmzB","69CXM6YuSrmn":"hBijER8WjfsN","EZUpKD951OaF":"5PVYbjJEWiBo","28ufXp6iKeVd":"GCSbWBLFrhHT","Uz3kcFpo7Lqf":"6mEMkSjcE9dh","uwwf9rJ7nZwX":"7po535N4ebCj","GDwMFXAdCJRq":"FraFGI6EsjaT","lyCtSSSvqM5h":"UC8LA963c9Xv","nyWdJKSbTTNu":"hAfD5u7TXl1Y","KbLYuUKIe3RK":"FvaexFKHHJTV","wu3msWTI6V8m":"fyKBTEEVZujI","ElVG2goXtSk3":"cktDw4WNAgcb","QgISHgezNs4v":"mhaE90ey2EyV","J5xDQ8VSmyuL":"G3Ux4m5qn8K5","P6s5BfmvqNCe":"uC5yXvfpcyeV","nYYyIFerhzZ5":"8GgIrJVuGUCM","bB2ma8VubmnV":"SqfjaHHDizrT","4PtzcMgLPedr":"9gMZOnK55FjM","dA7W0StgQjr0":"gWHmZnEjGsPk","zF2pH1bbuprQ":"XnSwcQWhUd1n","jlcIaHBNXXfH":"pa4WdOrVBPAg","quddaQLKtO0X":"Nb73KCSZ1YAS","dXd9pkoWQkLK":"WFS8adI7Tr1h","9VronuzlVXML":"V4hUUdgI4Ksu","g0jiX3NebDHW":"ecw736OHV3Xu","YpAtWdPWRlE7":"fhBAPC7Jnceo","i1lDkPgilNmF":"4wXle1TCRTLR","jhCJnQzpbu7P":"T9anuNUYBwGt","EOHQZbdZBg99":"BhyXlpq9NOc3","SLeaowxwjr2M":"jAD32vypFfC3","S5XRtJjQGKYz":"vKMAC0N2ayjS","CVZ3x0H1blLQ":"oKOalF47gce8","y6ouaL4WtrNC":"nbBhfDL0PDnG","HeLyyjCKhnuc":"K1IJlen2zncV","GBx5zSEFT9mU":"tLnEXLLYB5Jr","qprDunqR5b8d":"nZ9ewHY43zCs","jrQ1J6TM9VjF":"Dmkk1HqUBfQR","BKkYjBJj26w7":"3j84qbrDWacG","zvx9MCTEOhHA":"KHc8KRBz1E1G","qnwfd6j2JmhV":"7SnFFhyIEaJn","uXKzYoQfJEcn":"QhgIrA47mDoX","WbFiEW9rov5O":"j92kojmZtWHn","hzj3pJE5iC58":"WPg66AdIgjd5","XplpN90sfVyy":"cIietwT2hKEk","btcCJa0NdNPw":"Sz8WEWctNUpF","fD5PgBLHL2MS":"Necj1aPE44er","VaYSI9okoVe4":"2KkkR4lKE1NM","Cmuh8Hp6C5Hi":"xou1k3wf5L8Q","xJRmEVVSZZI9":"8sninerupCJ3","HWLiZ4Pyz8Ni":"7rFfabiRU6nF","EGsP7JNDFsE3":"kEpdx51Z5nNd","AsUTR4duwuOH":"bBTXGDovqyEe","LHQVwqfSysvE":"Q7uaWNXCzbjn","wBlBmKuuzxk7":"dIx8U5llsl46","bbKk7pIjyKYC":"qaXmxt6YQt41","fKQkdFIaVRSl":"dOD3VZF4EvUP","W3BlSs3c90i4":"njUQ2DDoqzwp","tMehuHUdPZJt":"oFeiRYeJDMy8","MwIAVawu1khc":"jxIM4bepPHqX","MDUouTIM7p9x":"0wm26PuldHZo","dOyXSF3kUvYQ":"KDWiYWrZrJXz","Ogl2lkrDoa7e":"4fDozagdKFNf","YfR8cqCBRlF8":"LbY3CR3hHuCS","YuAjEya5reIf":"AfGjUFRkqjWt","VDfcgCnSwcab":"8MqMtJDHAoIs","WaGzMyI7pnUY":"idp65eUl1VAT","u9khdlu0gZEt":"yjmnqjTwtfw8","oV19b3WX90Cp":"4jLQTxqayEQ8","cyTcrLqtBMlC":"sTSis0lvG0cG","tTPuOOeTDtQL":"UwPOWEODzxsR","Llu9F26XsT0y":"FyOzAx9a6lc6","KtiHr2VZaHql":"Th6RglgL3eRk","NtVvidtTnZtu":"Ks4W5rpw09YG","NToegXu7ZEPg":"pfUshx7y08lc","AkF60DB8eUhX":"DJrnFbsoZa3j","WgmmGPNMrVVR":"0acrT8nnMtCC","g1EHx8M8hZVT":"J2TI4Ba2jbeT","r1tuyRdkB1hL":"lUX2qgGkKz0l","m6jtLBW5elVP":"qMFftxv9uaT2","Zobu7LUURG4E":"IHCdW4Fhps4f","vXd6MR3y3SSB":"d8oO8gqZtcyt","FqzNBOSKS8Hk":"zIa2fFYOPdJ3","XuKkFRt6wQBG":"2mPxRZ6AjRg6","lHnpjKcIE2bJ":"dXkruu1zsoIS","4dZS4BA3khTf":"h3Bcr6SJCrNW","DlsBJQjNhP3p":"oqd8LjCYRmua","J1oNX6KcFQm1":"ZjpeDXWO5DJr","wryFFYUpnP6o":"H4zEJUv1Iem8","WPtyGy6GRbyc":"uvUkHnEJSsVQ","HZtgcJUGwHOe":"m3p6fHvxF9ZG","hkxs87FfMlup":"xZ8NThDhjvLL","TrBS3FG6pd5f":"9bV0MXqvDiW6","Kc5q4dQFWSkU":"5dQDuSHnHw0T","ENqHhEOx0Vsy":"COWZfMrioQoD","lIoe0DpKkkgG":"wuKlBtB2YmNo","n8SbHFzKWrRj":"JdHGHxdSouQO","cRme92Yes7Y4":"n0OEKMxzcrXp","0JWlzhLfk5vx":"HqxkGY2kkqR9","mMhajSTK9UvD":"BrFviMzl6ccU","4G85VkDgcDoJ":"qYpcvbGKP4g9","5rGpQ6kNVW2s":"XwmjtujBGIIO","Y2iHg8gzW4RE":"0ftCYjdoQh6c","RbnZ7iHaxplj":"sAyEegPKfrMi","5nXX7QFuO54p":"ioX1oIOV4XmL","K8nvKUvFc1sp":"cmxN52Q1YOLE","om0s4nLvOSiB":"ukEJIDO2hjd4","1pr2hUgXziyB":"5HxWqCNagHFS","fzeRIMgVDQFD":"xqm0Qze87Zy2","GRquX29YzOB5":"L4FDijpCTt1y","VZlmmp5zLOea":"1ZdKvZPSf2zp","8igu4IvY4D7b":"aJcUI31IYauG","ISSQPbVJ4OqP":"2UgL9PwaaJ29","x7yvzpg80Jgh":"kHGhB6jziEWF","0VKZJcNu08M2":"9hBwgkWmqqE6","Qa0LLxs6gYq1":"yCV0mhyc9EYZ","ONkiYL48s6M2":"ZmifCrxHOWCG","D9w3U3wial9n":"w1NEOeQ7qESY","7GSxetueJMfd":"tIHz1NelaVUV","BWinkNUVoj5Y":"aDYpAZNGdJEA","COOux5uq5qtl":"PB6UzVCQ7eEf","yxlWASMU8Frk":"2J80MufqqdNS","I13fAbDbE8DO":"ZURo8RtWoib7","SzFWLh5wv54M":"XuMtIzHLGCgS","ilcFpSSzDFc7":"6Mf5QQ8kKsoO","8cAOMsYnnsth":"CzZ6NRkHsw59","5KTwTXHeUEsM":"Ebo4pCncxWxt","1oVuSlO4odAo":"3Vnennm3rqGG","R8vemFC24Tft":"TmUyPpyEGq1e","lJcXcSbESDaE":"fEYwG5HPzJIK","2KFPd4CAAKe2":"VgVZ0YOyfmGJ","NVYmgO8YQlWb":"XpP8sue85S36","6q5keqojV3Wv":"fKwGFUX3EcjL","Dr0ChXfuoNPj":"r5Vb6kxTvZma","f6KKnh8u3NAd":"KqWSx6ZYIEHa","UErA1CbFdihW":"m2DURCPccS3U","7fhb6nxKiXy1":"NA7QZwoP87bu","Fg92UWtw2lhd":"Y4JBagsAzk34","4aeo9lOo4UxS":"8hwWtO0MSaa5","RtRUc6CWRujh":"IHYJv6ambERD","hZ0GmOiSqCz4":"4eubCFbuslq4","WzkTuFnpfuAw":"pjqmyC3o6hay","w5hq6rmlFFyk":"uJMxHceT0zYv","YtOcyhxXAoqc":"Omv8Ot0uhbIw","EOKqRNncIXHP":"7qgQVqbL01f5","IfKV0Kv6wyqW":"F560I9oywcud","pbnVR9dQEhIY":"U9sb5MFKicZw","6eGFSCrYj2JI":"SDxIsqDb1xRW","kPXqNou1uGsz":"8L4TPdNQ6qM3","UAJAMBPKbLaS":"AJRxaI3ylDeb","NdB2uIR5FEnr":"ZJMZsoaaXj83","rMoy6pGZ340n":"M7GpUyQDvNpk","dgPNOkPRdqNw":"8yoMn7TVDUnq","McdR6YymRc5B":"FTJtj0I1tpEF","2Wk76U6yfB14":"rgnFZpEFrwYl","EmzigWn5igIw":"1eHhzQvKEmu2","rfix1eynnehX":"a5t5a2rTUsul","2jxZCEYhrqmE":"4Mdva87kvvvx","GEMEMWiSHzvk":"nbOur6umWaaj","ZQGW3oXWfpaK":"AV9IMxQE2SUM","9fcWhsPcoxG1":"J2IR6drCmM5L","olYz71QqiulP":"eMBXwUa6T8Hr","eqEu5zL0in84":"ek9Bwpm55HCJ","Q1S9P0uJREkn":"hWgIUjSUNL72","soMSCJpempdR":"UO0SVGkeiiau","7fov0SWHVKC0":"1jbntjnNY5zS","AEMRaaf1bc0A":"IzBCxPw2IYkJ","CeO4jJaoa5lz":"VYPpQwLuDKTQ","qjotFPt4g8OP":"VmzJiaE9l6te","UxwbIYqyLPS4":"w3KPHhsRC55q","1IXIQDR6kGtv":"hbusbxLnMquZ","1P1cJc99cdat":"pq8EhzKIkhvD","9K21MtSiLFKL":"8jTztazjriAG","oZY1Z0LY9Yf7":"ACiDdrRartD7","2FCwRe6UkkfV":"KnkzINaeqznh","NmtJ63TIslXu":"OKJyRZL3771Z","LrpU8QukrI5P":"meNW3A5Dlm4f","a0eyUi4hH21h":"ehi5L5HRDj0H","ETInSXaoMj4Q":"DVJvVLTaw6iu","DzDJh1Dn17Gv":"6bQNBcj2w6gQ","TEnVkicPK3Ll":"cqYnszEj0LcR","o1KZx8ak8adE":"Vox7rTuXSExh","5uoXFvmWHbGD":"0SXoUAbNfM31","NmxQgdbcDJui":"NBa6blxHq9hM","hVW5P5bfqWih":"jMYXjELBSyb2","P9OWOPGawgca":"4rtjcMCbdkUK","HyLLRCj1SEqX":"QOOMR9rkPVXg","l1KQ7pesM3u9":"BhqRwX30tj0A","pgbubkng2azm":"gQIjG4B4xFdk","1djzPLtjDnYl":"BjjqpSsBcriw","eSweaVJ4Ioh5":"HKxR6nUxNqgl","Q96UeHt8lTz2":"wZ44Wjao9IiZ","WuHbOt7rogRb":"iAtY5ri2wZBX","PkykdMPTP9wb":"fuFW2VE2SdAY","GSJzNo8GiwGp":"BQtm2T2fnMq9","I3UZpjVeevZD":"zvTUIs1B1xzA","nUns4IOGW2NC":"3TNj0DIayuH9","FDIHr39EdcKp":"HCL71RdoBLiY","D5nXZe89IcOG":"EK0GlAlAHlCP","KkAGu1c8Vk2q":"SYzhXpCqPpSC","67dl3nrJVtJ0":"VZXxs3A4f3lr","rBhJ7j1nKQAt":"BUJW2wkSI6HJ","tjgHnD4ihxX0":"QYG29lLe0Wl8","rsJbZAvo53Ni":"A3SfQQLlNN6r","39O4XQfHL7J7":"slP1SnaiFCIW","DzczwAjLZ3jO":"t69avdMES23Y","yOidt0MoaHo6":"v6E73JTPB0Hq","HyJgT7H5f5ri":"jiURn23CIxmq","WMcjrdyhzuMV":"tO7jQfE0oVZe","px0bllcC46WY":"Or4kgUFjx7so","pO0W1Q05MfpG":"Ew3Q4JEaxIO8","nmCQHqp2EKcz":"o9LftSqFlVC8","btfcKIzZDoDy":"DEDRZJfQUfpW","GLi0aoeozDEM":"0uE3uS6SamLC","UWfq3ojuKUT0":"U6kgA5IdnuHK","ub0Vfij7Jb8M":"Euy4GArfoAJ3","VQnhWKpn2KfZ":"qcQhznryxb3Q","zxApq4rGek4P":"dzu6WH1GPR8l","024C22LJdQ2J":"mLp4MVR7KIoP","jYj19M9xAjQF":"M0P99Jmn0hrB","TI7YK1aCNavh":"dKfhaBb6E8rH","urAYR2L8kBof":"EXsxHE3BQfrR","AydLy6sUkdHC":"9l3q66KUOYPP","870SYPA9JoU8":"VDNMXyPPWlDa","Jbh2GpqnHpH6":"7nAMikrN0u9R","60A7yLSp3qCA":"eXubpKUzpJIz","wVmLqmriUOA4":"ZjD19qFmRolr","uWSBbQeH6sTr":"nQFJuXd3t58v","8A19h7Twkcuh":"M3UyBIALQ6QH","SsnW7ehEiID8":"VkOnIJlGPxQf","SMtM8uJVO3e7":"ViLcuhxORvLL","EnRUmeRZph8E":"nZnB0ZC2jmMZ","7cSbOJXQu3pM":"1cl317jwz4AX","vXJZnWwPH9qM":"UxRTi0SepUbK","HsrX63YPsBi4":"cQJ5tKV5gZWV","8RhhkYgZY0Ue":"cO9IuKU9hl7o","4dlM3GOLANWl":"43A5ozN7jIlb","GCjgHKIlduQt":"GVhakQLvklxD","zC1CKUYkHykR":"aEDKqXqsXEle","NGGBVfn81RWD":"Xs0h0tJJpT05","RNpX3sP3vm5N":"jPjv1iwM3r2i","UcrprF7YE8KU":"UbHzNWFopsTm","rDjGadhqUOjq":"j7e1KDsqsDyg","n5d7yW6FDZ1H":"g6bV0kPgZnDA","kkKK14InyScJ":"BsFNzIr3QtgA","60GH8XdbEVuf":"LL4OjURs8uWo","rgPF4l4zwEaa":"xqEfEWRt27td","Dh3nRN4HBvsx":"AweEZgAXVTKT","sFvn8UV3t5BU":"foXpNbnvSKkm","ueZJH5bsDaXX":"kv6ToxCzEzQ3","MPqBScohvkLl":"JUOB9uSoSXbn","Ek8DmiLjKPBl":"nCVP8wRzDJBs","EpehyFx5JdRy":"L9OMxnfAb0oA","5NzGMHkAf1gv":"a3ao36lIi4a4","s6KTeAO82OTN":"7a7Ros3nmiYc","ya1SNGetsEc6":"nH4usgxHtMy5","wWebh3UyaDZx":"iTfQ0lmHK6Q5","qTSY2w1P7cE9":"rO19UT8IezrP","ljay7OzgsNo0":"LvbI96wY1Nqh","I0BEkjqN7KhN":"gDHzkV5E69LL","uXwtozuU1ZeZ":"hyCsEyz0U3WU","fLVl57LPY6CP":"8uHLpp0O9Ngl","5vqFWH2rsxyr":"gJUETQP1aSx4","jMBIGNwWrV4R":"CM6tpj8NwKBI","FchQKolOiIW6":"HE002DyQ2sgn","960h4nkWMuYY":"X8G86WpYtrYc","cfBLhMk2QG2k":"tZ8MbAgW6rEr","XdAXL5zpfjCI":"UQLoZcni4LwE","WY5rID02hAee":"iH3F1IomjBDM","5B2XpHNypPLk":"gRQz4DSTBVh1","xEPUJfFvupKM":"MKwLpPmsuRL8","84kEX7uh5m99":"pIk8Qflv6LLP","utin3NGT3kQz":"EebjzdZgo5mP","Ao7WwQ0zeUTV":"Q3LOLN1MgHAM","XgKncUwyEomF":"V71STGsGLcRJ","AzMwWIlFSlVv":"V0DWUiLl5gAF","a9wzSjBYsebl":"8IJ6Nd8U8sWk","zol6wOv0awSD":"VXLM2wRjomo8","ytRweA7uOZUM":"k7IKPd4rbw7j","u366ptO7ywAa":"ogJuKwgDmhft","iR9HU9kaFVqV":"9XYXpkIVdUWw","ViyOTBBzuqul":"gGbNUSb1v4xd","2PYirk4dRl3Z":"jWotmKgb6Leh","zQ27xCXwCPpB":"YZXJ4wBHPXsG","NCSqP2fUoV7O":"dmVFOQYkaJ0y","5Ip4eB3QvNES":"tsNvYcR1QZCc","CmTSDcb8L27v":"WDHQAwWBb9hF","ezfSGT3MNgVD":"25YsdqCoywCd","fsTQcGupUMLe":"aFYOrLtT3M9l","eHSCWlb3AV7L":"8t18eSW7Ej7i","XZXYYHQxJ51D":"qreaGuhW7wi6","6AjKMMwNQpgV":"9d5maNG68WO7","c3NyzoUhbjaU":"QF3X5XRvOEI8","taS4x5pKbff1":"ktU4xJevrcKf","3pwrlz7jCRol":"3vwXqEXP8cic","Ez6CWTsWZGjy":"JVI1StVn6T3Q","Vn12k21A0zBR":"9IUDMtpPigO1","wVMAxjxJkRay":"iZC9Z1U1akTA","1SIspHeo71kc":"Ivev3WSnHyYZ","RmabGwjYE8cm":"TCaPCqiiOFM6","rYpbHFfSc0Y7":"nOKngJV7kEV9","QwSXs0Lm7rzb":"aX4Jq7RWUYqZ","aho0Qs91yk6F":"VYpToXWk859r","FLNC15mwWIvP":"2pdDW32C39ts","eMmrluCa7Vqh":"CKbak2WVOG2F","NF8URd9L5cgU":"O6wJ3zO6Qi5y","CwShiQI4WGnh":"e7Eh8IAmF9dI","yWrDAATSqIvg":"Rokkc6WNSsBQ","Cbz6CYt7OFuA":"g2R6UY3J2xw7","lwhj7RHOunXH":"fm6NH7vzPYCF","ZHEm4BJFwvw3":"o65AVkWidFS8","xD8M8x6mH4c7":"pnnteVpG8JJl","EqXadzrbI3OX":"HXYv1q4O3uWZ","flPqdrdlLJKl":"YQzTgdenWFsf","uMKZWTjA8QzX":"T1UXXbswFvFc","U7qXetAYg7VH":"ktWFFwWZZhdW","10WonjWOhrJg":"SNQaswx8PTb9","JIT0f4xqfx0c":"MToWBwJXrNBn","YSixvwQXfMUp":"UTbbAWAnx5HV","8JGGWDEthwjX":"uXvI0jFAH9CR","Ix9B3B0WIjpW":"3miT2JVMDMUI","sqtP7A6AunR2":"pYJd3PDMEdq5","sP5m3TmOiNIj":"Qx2DIpIjUG0V","5yVJdI8EeX6S":"MRhJg0PIl4CN","bgkrhu45kzkI":"5zPY2mw8gSlG","V0N74Ph6TGrr":"gMJWtW2cudtk","tBfXyJtkSmxm":"AzKnEVIVlBVE","Vi2KBOOcHRPC":"Q6Jng5sKjPNl","RtgpnRwJd3Q5":"PmTsXTFSDBUi","7ym2K7Ffp0R2":"JTvaPuHYsMGL","6nxqFdlUTHTs":"FswFYlyD1yy9","SSqApyhjLSJH":"Qr6wQdKfSGcW","xE0GuaWds6YV":"yMQtKlsdOwZW","HZrUNC1vREYC":"tccpYOcV2Zmx","qlqms23aeojz":"m2HEPC0FpEzh","Lw38b2JSInCt":"TankdF0d5D14","j8rYwWSH80HI":"p1IRSnXYwHfU","6jrhSmqR1o90":"liTRV15b20j7","InykcGPlmLBP":"wNnw4nNVYjiz","O1PgKhp2PA5c":"18YhcWNQKkX8","aKjToR25upwz":"DMcmwuvwFEIk","cAyvMhOuurNK":"8jbrtVQRR5iC","JFoGDnAKiaal":"Dbnh9wOQvPsQ","cFqBQr77WXQR":"CVGl83Tv1igt","R99v4g4JI04W":"VAHOGTIAwCCM","Ey3MjRrCsxaY":"z0O8JZQDZCTj","Gu0t1Kf3Kt0j":"u1pG8jKSjdQM","N8rFd0EWDwHM":"dlNBYm4FZ8rC","IydmbbzZtVoF":"3hnScBaaCwFr","D0olXQKqD0aH":"eAXcQjOFyvfT","TCzWZU6Gx04m":"9pLRBLYLZFIs","JvHsAWHx4OWb":"5NMiHkJrDJoO","lwVbSBSvleUV":"hXaJ2CKUB0pi","wD2D0tVA3jI3":"t0m1gZzOHY8J","atfFWmDRnVEx":"Eqyjumaz1fFp","jGAEQQ9bcKmW":"3zVw9Qks1bMo","rUy6RlWcNizU":"58RdRIVwpWjU","IR7WK1smFPph":"TfwpKvYoPYZ1","TWkGTv05cKsf":"dfgeVRXH5DkY","Cj46fQtEGZir":"Tn0mYTv8uhsO","yK5fMOx8nJ8G":"K9H0eq0lNMZC","eOHgXPFmKEzy":"Ca50mXgWsc9Q","qkG3ZiCU66LE":"LbE0tA2FBf6x","6CfwoXRDweWh":"zjjNfwD6cAEa","scgojcEiwkfw":"WC8QRiVeY1yq","gc9P3Aqadlar":"T2ER1DJqhXrQ","XKQNsUKbAQrf":"mSJXi1zvWztA","whErDVqLsxzp":"oEyiSsYDy2BM","YlqvmrkWS2br":"01PMSVRJfv8f","sOvuGpaQ4qHO":"YQqkbew7aLzK","63rgFMensujG":"2EnyedrrTctr","blIwuXUhar7I":"9XWx7fsibmdl","T0i5fIaiAqzb":"usFcOC86ULCb","JUkGoqE26zJg":"TOOcGru0U7wv","sm5oaOb23a2F":"y8ZLWtNbePCi","7PuHiWzhmD23":"c38SDA7dzHu0","6vevfSJYDTBr":"7ftC204THJFh","gooBcokFZMIY":"CTRsJsy4d0Pu","eohCeu7veJ7w":"xqiZQdiIWsdt","rtzyLT1bBYdY":"GZGaxGK19VMP","Q6p1InXuZ9xN":"jAXGdYtMRnJW","ieKmcUpKHkqd":"taHuiV0v6vAy","Vp0qWxotUTu3":"CyNtLwMX2ESl","0O1JBtyqsYze":"6yBXrnlR7ooW","oHSPZyNqcl9R":"ckNIsd8OjUxG","WDZt5Y0JR9bs":"WwEtV8jEV0B6","QJ72FAtiNyvh":"gxuqEPPM7BZo","f5KBkMOZge5p":"uDGTIU1AmoPy","NViD8G8IEfMR":"IIQ2SBneKeVa","MMOy9akKkdYP":"oZDcjBmmsCRR","mRJvb5tSirVb":"67zSHtFSnH8n","493BaeYD7prm":"NbYHRxL1ZN4I","PyG6OEcQQLrw":"PkYmWml6ie6u","sho4Xb8KXUCB":"78OvIEGhQFHm","gXFk18lbJy57":"TLiNIpKcPMUY","4cTSjbHQgxp6":"nUZbDO3gceR0","ZEsYBszYVKM7":"EQAMWuvyt5JH","QgkZbJYiLWhr":"1ScTSEpizxX6","9Ykdcnzyahu1":"eoCNSGJCxt5O","9FSkObmv4Jph":"ufnPnUNBYW8f","sYYgt7RV3A1U":"FHtqXwoFH64g","sqx4Sex7iPmi":"l8l6jCwYH0O0","9vqNiK5W8d5u":"WWl3FfNevdGS","dQ4n6yVnfK3p":"YzgAGuZwGtxW","jCbACzZZP4y4":"ij9i7Ur33nu0","PpfT0AtLiche":"2LzLXtGpzjcM","NzyEI568a7cW":"H6w0873zvMRx","ZjJr0YavSn2C":"7vrEtfEG4DQm","AYIJgJCKcw8m":"YMLWZcZWZSl0","gik7U7Dy5J60":"SZggfMzdarOe","4TSRcMzN2kYg":"wgwcTcwknt09","YXnaFpHUxjV4":"urShtwrgkkA4","CktjdS0NFuTL":"LosLaupbd2hs","y2tBhwVb21EJ":"e03XRH2q52eH","DXdNDLiSiL9L":"obFEnr31N8l8","jnlOVdlqcudU":"li2mmYQLCB8t","BSbGYOV3kJFl":"Rkx1AMcSy79u","jUCtMet7MNTU":"CaP5MpQBoofA","Xz9l3tp0UuEv":"xhOPO3fCyjsQ","744nmeU4jUWj":"LxnS6KKuDb0E","4pq45aYluC3s":"M4SUXFDkpP2r","svqgxiIWpNub":"ISY7kZaA5W71","pwoutbkPo4k8":"qQvAed3EmLuM","cdl3ThUIGNNR":"qupXhGJZxrSY","8PkKTZMb2E2o":"dTXhCyyc0uAF","9IUgX9RdvgYH":"aeYIuMU5iCF7","YgYhMx1uL9ES":"PcEleinT86I5","yLDIXQTUB7vP":"ZnQUmT1Gmo7t","sGcIWXR7n3ef":"Ox1z5lN7Gziq","riI1zrjKkhec":"twlfEKrRaiNo","2x5VbrtAa6hc":"msvHBM0RtbEK","Vsfvpkm6b2Fq":"N8KAmimPj6rp","qOrsdAzGfgDq":"vnV0dUD0sebV","uOmMBtEMba9M":"8MzSBbJEAIp5","iiveQP0MUQdO":"BjzuxH8A60Fr","0RJxaUT9BHuA":"dCVEogrHBhF1","fqDNGWmvDPLL":"NOVe9ZYm0CQB","SHTGSdscU4Qe":"Yn27GoiOGFqg","knnTZcHoUMzb":"fpPZTfhw0uqd","Bh9Wpvo4OzOl":"xVZ34ejnA5bz","FDkhhXvKnztR":"UHy75A5q7pHI","TD4u591i3l13":"WvBdSiaUQR3v","SPIZsfSjvwQQ":"mk6jaW9s5uUS","V64cXh83ATSd":"tHZk364Kc5Ix","Qz9ZklUtA96H":"LKrXPTwvtgEe","lBC2MVUtj3uE":"Cp3VjMzvwMaG","Q0oHiR9OSJNB":"zPNCX8K2EGVo","TANDXd0QBszJ":"3xnjjVGK6Oyj","2gUdDLlFMjWH":"FSHhsJHR74dz","383we6So5Gds":"vDj4kBRcWayr","5UdlICVgjPHb":"eYPMratnPIwm","kKaLtRkMt5rc":"m9VgqBMyH1Ki","69qjn9kc3uXi":"rAUFRfFmQRrt","Y2HT0H8TbuWK":"NszpABI2w0YI","ozl2g7EpMApZ":"UDa8SOCnW99x","Mp7ww97p5gzd":"hppC2Nec42MC","M4bgDGRyMDji":"HQTdsKUCZkYl","Ebcsx0WDX0QA":"TCLvc1DdLjfH","09AUfOOKg9YT":"GclkNy261t6N","Wbp60TzFJy4a":"OktUr6MysYTa","nocpJSqcpp7C":"CprDuzJd8FTh","6IUyq6l403HO":"28Tz2AvgReUs","B2MqNNs3V0jk":"2yDeevBMHKuy","4ypGzGLgelf7":"4oVtfOEeaCgM","noJOORlZNOtX":"SMYhx2i1GMmc","qW5FUI5zfX1d":"QPa7S62GUjRU","6oESsXwduIMh":"H9Rvsv0LFKLZ","iouMUHAGWTWX":"VnWeoF4SdgoX","3EdFMnfemT8a":"gigxHT19KOOq","cQofWmvzAnH0":"iA9IqYHKuMX4","iiw3h0yjhsNH":"d9jbUQbprFhE","kmoccDSDbM8W":"duAgpeYebttv","387IevjrlDWt":"09ARVG9gaxB5","rZjhZM9a2xT1":"XdybIUr4G4GU","N0QduQ4XyA1c":"OrmL0iDH9nlr","UjAe6aaNDsgZ":"6mAOrHXKkUzi","BDVas592KV29":"4gwozrU58BnC","oFhcagKDE65y":"40aA1So1FFp1","LoxlDoJFfzrU":"VxGbf1ie46FS","D4t0ocEhDpXu":"vCIR8sNFrMr7","H4ZwzpgxrPGC":"feVsu8MQqH80","d96LWTzuZFMT":"Fg6ybqR6VbA1","uoE9VxtARnuY":"BGdeAYdZXe3s","EYat2XY6VT57":"3Y8LzZnNrDY8","qiMUkF35oQ4q":"lTMvPIkiHKOg","hE3Cj15GvktR":"k9s7CAHsEnVK","atHrfLo9WMyi":"zuS0SJfat8uD","JV2137TGlZXJ":"sZCJ1wc7bCjQ","n4Piy7vhgkIn":"k7OyRsbKrFCO","CEKn9alHIU61":"nEZ8FgJ75kGQ","AF6wTGzaDwyt":"2SdAkcVfR9Qc","G4lbMsY5zj33":"qB5gmWV2wbmS","TNtL3Edjjw9a":"OkOeYpUMw5lX","gKcv8NhnAFf5":"18YHu54ghUzU","vU4ZIXfJJECH":"A41l3pHmiZHa","KObhWhoxzLpO":"WSW3881pYOpT","1H3nY3LinQIy":"p0cmvPWpJWzC","C6rAwtD9eMZD":"2NfPckc21Rmp","XelgHazXCbjq":"eZeg0SlPgaQQ","Pl4V8cuFFhGd":"EPpQa38SPq23","JpOFTEssn77l":"4gH5ifJJBg4a","NNMVSNua2mZT":"1xTiv8XJy5bG","28HM0rPwycDS":"1i0jsBQBoYRa","BrvztNpbHNT1":"Z3m5AsHrbGWH","RLPHVOdIb8I7":"0gi2vCV1BNpv","EFkXpRArNo8n":"9DCFPkXDeBKW","eEmwAYD2L4or":"dJlVWtB7pamG","8JO9ZnR6cVtd":"lR1id03h7T0m","Olird7TW4GTK":"yCK7VpIhMsZO","GiivMIJJew2P":"JcNlT4nc50dA","dELijI33ffFN":"qBa3S0HYw8Bv","jVuRj35QJAE6":"ivnAxEFizXOb","V9VSOIP89lqp":"2sBADZuEgPeG","NKC6bkVc7a3B":"J3h9IPJ06Mwf","IPxXY3ei9mpN":"KVv7mJxeZtGN","Gg7YGBJwOuez":"7oudk9h131Fr","MIOK3QrHUmxc":"HFM4sJooyJts","WmlHMUb0INaH":"TQYxSrDV2Edb","VZnUC1F4TR5s":"SxKxObqaPMd9","eg5kRO03Gogr":"wG4FGNuX3vrs","AFX43P3SRZyF":"VRUdZ14LxsPE","ZM3i5rTQtXsA":"MP4Iae8R6MEa","o30ndcUpFwDk":"EmKlvF0QU6Rn","nCkmmUxU4C3E":"91LCVcNUFoBB","Pw8jpD96fYss":"fxbaG3Bz48A2","J9EztH0j8D6S":"RBMGxkkKjrta","ILnDBwiiQpfm":"oZy8yiCDbwMq","XlHCgzRjBq33":"KDOeA1H55afx","FndsgIxilqjE":"fFrKCBXbsAIB","G4W0WMxEuNZa":"wv4d1fKQPEzP","fPLvJ1aGVKAe":"5VZc2XsOKtuD","TmqoCagT2we6":"gKMbw0QBjJgo","K2bP2dDCNibu":"txApmBll0XQk","CSg7FiACf7Xa":"fU3UF7DdqwXl","dmjQ2Lii3SNw":"b5Nv23F4Zll3","eA1g5C8dFnrC":"WM6tovRuC7RE","bw7UCSJKDoqu":"4dSXugzNTsCC","w3FjqDSzJ8MJ":"RbEs50a2uYXa","zuu4aqC8lGlj":"C4BLiiABsX5l","1p38BUwAfBeZ":"Tbbq7BYl0pO0","tDahuc3BU2sQ":"gicZCOA0mjIX","Lk8lc5r4TEN2":"JMfWuR0ZLugG","eWlgCqDaWdFl":"PbUlt7Hayb8f","RuoK20EN8ypz":"ndOotIaztkQg","takXbgcXmDAK":"uitltQvLzd9M","NPwi3QNXvmuv":"wUS8wCBW0raM","QzIDtxbt5Cys":"vxJLAv4WbyyZ","gUlQIUmsEAlH":"Sc0Nd0sNkSB1","pEkeLpGbXodv":"FJN7kGH8KI2c","tptwuHOe5vJp":"t9OuyZFoRqHT","q2KUAPZLaibh":"JkXR2n6q9OCx","UWgq5HxPKPFl":"brL2KkpPJt8x","kULc7X1lbEmO":"QOu39shl93E0","lMPyTTQp3d1C":"XObnL603Lxfl","pbbYvuv11AVE":"r39w7TpRPD69","rd1mwn6xcIoJ":"JdYbhzZChREG","EOIwK8yP8TbZ":"BNZGJbok2dlc","DPFfiPzmfTZv":"nKnLtWtbh9QE","KRtoUB2TimIE":"zkzE9DptSusp","c4fmXwwgLsQl":"Cx0R4zkB6aY1","0YgtN1WOryu0":"iBvuId89JDXf","bEKQb7VD4oYg":"Jnm78mN5gGxv","SkMoiiEdYVH8":"bBqQFeHhokbu","4sHAzKC4ewOj":"kUyifuZRM7Ce","OolbkHk5ueSt":"OT4DH9pR0xvB","Z7wadOPb79yH":"G0kFU0d9FMdJ","vusLPCbnX4jq":"FZCgz6jCGWnX","E7SOwucwEsUw":"Q94XGAZehgUL","QwW4zK7Zk4qy":"vilBDraWd1Yf","HvBNuAHHEMOj":"biJRRJgrOD2T","jAkj8P0Xoz6J":"cCFQU6IhdifA","OU140v7Ef8VA":"AfOI2SXHpnsi","0ypB9vMAEuws":"qbTrEttJcqug","IMPvsZI3PCUB":"4hAFxAihrrTo","hkzYWvWW7Qlk":"uhS0RaRnQ9KC","E6FirripeYPw":"uSt9ytCDutwo","jVEYLPMr5oF2":"j7uK2aI2vYkG","KNW8RSmviC2Y":"71MNi9SpdQPz","6ZSPmIK7NgRP":"c0Z1GQy08tia","0QwJmChBuCG0":"T4kbIZpWsIQO","6KmqMoBL52tE":"ovYK93IPFwXp","nt8T52otcNP9":"S3chgddILjmY","MRxZFrbe3D0Z":"zB6rhQOpS23u","9wEcNzBqTSkM":"Q9E4S5Skg5fJ","cq8KOQbmt31v":"s3DoN5BPxuFy","4vYCInXL0NPE":"QI6tZFfzhyh3","I7mwiVq4uuOK":"n8X2WTqJ6vdT","4KZ4PP3JDYyZ":"9fZDxBA2k1wz","iEC6n7fSVtRp":"h1uV7eq2Sh8H","eNI2xlUy3SP6":"5iP8yyjptrxu","WBITs4z3y6Ap":"BTEGmQ6LNqtk","ip2P5JzHKUku":"hf4ZU3rMzlpR","NfZ9oM0blHY1":"ErZ7BqGtCi4t","vqKJ2k2BmVKw":"bAuuGEwALdEG","emGWnSadRvYS":"WI6qaLmYOT3f","WIfuaCudN0VL":"0KZ1peMaRegC","1gjUmj2T3mQE":"5adxZGRReM7D","eQ1XmVk1mJF5":"WciECbBKXXQJ","ZY7NssEqVEob":"Q5m6aOlrqRbi","wn24eWHSLrnS":"8GKL3oatKXMV","HPJLtC2OTVYI":"i4jvcQ4mRTEz","llAXTEkcUhia":"ZHNmw8DyWdnm","48z0qabz3hnL":"a3nN8XmKYreb","rELUAnY8rCwe":"YexC6xLQAtni","WO6Oebj5f9GC":"9gTBOWEo2B3x","npk8bR3vnAhq":"t1ggRv1OyKrh","57aQaFpVOGn1":"p4aczGZEvLd7","iLaG8wSrMCWM":"ksKzEJ55ir59","gnZbUgN1QY9V":"FMJwNqkPwW1B","AtzPdLgC9kua":"P1Yx6JZbBfW2","ZRxVFy0qSeko":"AdSnv0HiCXOv","t2G4IhUSQqrz":"g9ZhNb1p7Vnk","6hb61L85qvJr":"ykUYztqJXIXK","q8B0i4MVMu6j":"WtSe9Yvfz3gr","CPyYv2oXiDWg":"sUUgWYH5XmBe","dk51neVyp6zI":"XlBixW5uShuL","m3fATeQkvZcd":"xQYJgl6lzIEP","xrIBn6cz1qE2":"G1xp6GuiMCnP","fQiCWbhpQouo":"CN1wlQSteZKG","dGEx2jtcLJiX":"NMUt7cDUDBiM","YTUJcrnmIYqT":"upQU4zsV5FbX","rX33LCULV1Zm":"7lQagXqf7rCG","K2TDsf3bqBPZ":"PsQleTwxIjzz","bGw65a3ofnuz":"JOaXSthd9Ke0","wcBTrm79LxQV":"eqyV5zhL0Wyw","AzSvSA9H1Mis":"vtfyJ7uuYCTr","ZS2zIt9rXQin":"zPAbmmuHArit","sThkUmJLDe6I":"1MPJsciAU17h","UsmjXzoAwbDe":"RcvnR5uru9Uc","aDNJ7KoejMCH":"HBup7j1fp5GA","7wgNbphCNEhb":"RX5wjNsc4jz8","YGmjhTySyIsh":"HJKWLRQisfdZ","Zoz8UHJem5cr":"pB4Fe3h7Rh3U","ktuRg0goZbLc":"vnZNG15mm2Hr","xq4nA7tU5Pv0":"Vx3IRk0bM1Ll","79boEu41EBlk":"5M8CJVoGk6ce","7tEVg6omtmlo":"MSI3Q5nEto5j","tHAym2DYSLvA":"sXiMEM9iJLwL","tsnH3vKoEe1L":"s0997uKCudc7","69CfZs7gC3Xm":"NC3ANLfM04Uv","rFHkLa7tJzIr":"lqTfT1ihGf9A","KuZ2MAYP6FFV":"g093NFo8NUMM","SsXX5EQIsVWJ":"WZETz5xrn1Dn","MeqA4gqcozU0":"C4n6wOBgnVZw","il7AAqMi2cL0":"lG0phGIldhBo","zm9fuBhlHd6V":"dZ7j6ucixTGl","Y7dyXiC5386b":"m6CfXeCRDsNl","xWbT1sAQCELc":"PJY6RZH3b3hB","Ygj3EZT7LweN":"5JNxehPnS6D8","4KPT4SYSN9Rs":"d2MlCWHkXZlQ","aKJE86Sf5B0x":"YfCqkysvHJEr","WsmortWmXjNc":"W2kcNseLDoLM","9vQjHjuM3Hc4":"qTykR2oJ0OYA","4vR8McOivUYP":"SZCCxe65yFC9","R7S5W5jHYDkE":"PBlIqvyK1w2g","3nF801nxeMeU":"4RRg3P8EMpol","rQOxBZ8os80m":"ndxRW8ps1LOT","MSX0vKsMxeSg":"Yb5tbTXNCPt7","NbZgrBgbtbIC":"FG6KOri3gsJP","WnHyk2vMbJQj":"BSATgREgXktH","MpGJBIZ0zmZT":"eBvrV8PREqcj","LNGX8LpySdzt":"IrDdYKaqmegm","GEXUN6oHfuiA":"zXxikkG9I1Ij","2A3X4n4wmhmt":"8ypEkod5PFMn","uH4w7ovmZZM6":"tKgPOqflOCVq","NWFCOUMp6py9":"hqYZYjWYR5kF","udY8PG2NAQTD":"kd5gzvmoX2hM","qlIs9MuUU5nW":"xUZVPZxeyYx6","H7rMq1alCdJM":"IIkW8F716u79","87EQfQhGSFJq":"MYaZRopz6QCt","bTrvWMg5PtuF":"U7aBHGxZiT8b","4FxY6FnJBBij":"7KVolNVObR6L","2jEcmaj7o5zm":"wWFG5FiG3ENr","5fPzNqkXhwxT":"Xv0FGkD3YFXk","kAuB3vvFLtqQ":"alpDDVeywMf9","Q47QvBZshlPf":"8VHwqpHPoQRU","Qs5EEPQ6cO1X":"zuzDIbcoBkXU","SGZxNUQ6tb3d":"sMx52rYlWw7E","n4EMleCOp9OP":"ImYBLsw7q1O6","mcVxZllIFsrI":"NVb8HYVenPEW","jIljIaszfNxj":"1L8dzgJqJ2IU","n2TdmfU2OY2v":"FKCJQjzf6IMK","SmR1Vvgz8sXE":"r8AOXof0eboK","C7l0mKow5u8Q":"s1JITk5BAOhM","lZ5TKFe7bZDz":"OKTAerdVueu5","EYicaq9qIkRq":"4KvwrZzFiXw6","A0jnQyzuGvEk":"5bzjB0qXEkm3","tnixdtZWXvWo":"fwyx720UsJHE","J6I0ycSjWKQQ":"uoizg8WnR89a","C2Cc03dv9uuA":"D3tXKdyl12GF","C3odBjBVaoXA":"ZHXvbrR2ePuN","Mq29Sx9i4Lum":"4ojmnTCztFqJ","gwxdSzJzCOf8":"80xMbJ38J6lk","xL0C8SsEtVUU":"FTUeQEpnkbx4","KgC336VJ3su6":"jqMfpEviuQoe","VN1SeEoaq6KX":"sLE7s9ymoogx","0VgQo22ZBQxg":"LxzG9fGhdeeS","8NbooQRUFKSv":"onBNOGF0ISio","4eukAcp7bV1W":"q0YXFHZcwPIM","gnsMFQTAtTlS":"ruhRj1PznDO3","60dxV5MQhokr":"9sjnDZWBfXAC","piBzEv2za2e8":"EKfqmMC3k0cq","kyvI9AEsPIzE":"3C7VaV6gmGQk","rFkNigFayQRv":"MbPnisVgITcL","3Qqc8aLOtK64":"eamcYSIQjVdy","lWTYdIcrEU78":"zLfv2ZTlUqgO","t4t8gLyw2iMn":"rPeX9hxnOhAL","Dw4tIPyh6lvI":"ljJcxNLStyMc","U3Ki8cuiOwCs":"kZLHEDr9Kppo","5ZEE1oSXkEP7":"YnODjJOUYgLl","8zVyjrCJTtCw":"msczQY1Nu47V","Ogdj2q31hIsD":"nISe6GG0vAxV","C3PxZEKHTtjZ":"DoSkE06QadAN","bIgIN55tpWpm":"8KPBcYRg16Ci","BnfJqFxO05VR":"Orl5fOSGFq6J","C78xEhhgNxFQ":"I3BmoI0l32bp","0fSV3Csxlo1W":"1ZnQbCEMUa71","zBDzfR9Lf9Z1":"EFjwIEXCSrMS","RRK51mqlfbzX":"MKbJ4EfWhz42","QyWAi7tBFLbz":"q6Oi3BXgsGIF","tQUPyOEcjWt4":"CPPsol16rYdt","praoWFHt7HHP":"yRNmqE1VqSJA","EN8lmieV3hnS":"XHBVNEeJzY0l","WdmGp6Vw8XL9":"0NG7GUNi8zzc","xPMUsHsvrXsP":"NJmEmD0j0zS6","fYHl7W1R8gY0":"3zXCLzmQwsP8","ib2mIKUf4iRo":"An8nN82ERy8b","LLOa0kWswAMF":"cHWOLEwQplLk","SLTFltkqoZNb":"4eAT0eYwnhXo","0XOj8ddaMHiV":"0rZa7ajZPQIM","YUXbLY7Bkluo":"aomcZgfTiVyN","8XioL300z1q4":"FaN1YD7zjQc2","23XNaqOGIzms":"3ysyW90TIBR8","dq0p7aK3Wfz0":"TCwBvmsGnjng","GNIVotSPCLdE":"T499ie6dqmLm","9xw9gVOakoe4":"rK1VMWBLGf1W","OUP0PvlH5aYm":"0PtLqtLeKbgk","94vjNFDhKDF0":"beADnJygJroG","hUnubiGAhpRr":"KgLodiHXJl75","ylM4lVKO6rl8":"Ko4gLLtaXJ3r","rBpj2ZlH6lXP":"dRvfnaAj6dzs","SUUBtZu4vmh4":"zZtqzMBtRXPw","kNTIYWeko6mM":"4x7tfAZEP0Oo","lV20bw7eL2dK":"ey1bwPfMftXJ","AmHPmONw9txT":"KBMuhvc5Pinx","HVzXDRZZM4Vj":"mrtMUi392GgA","MuOuJke4ddQ5":"c7bsvYHjVVzC","IHg981QDlTQu":"HBArSs3Qew7D","rZcBJAVKI9rv":"l5T7oYOmdEBm","2iCRhtZtDMnU":"e6cc7o6i8eQ6","XIKWmm6IBZjV":"UEW9AuR9Ezzm","ajzxiHg9HGUF":"ej4T6sis8iTe","DijxS2tZlQIf":"jKM3tNvvcyjT","AzhhNGFBi5ZP":"VgSLExlBPRaP","LYTJazw5f1pi":"BbdgYZkSF65G","AFJj9Kw6UXqn":"UGSVrSn8vYEC","FhgnUxJYDEns":"XUfEMQZp0VZ2","UkmfwkIqqsj6":"1xuWbB5TcAKk","1Pr0i41gyZd4":"ruOdPamFvtAh","G365H64TylNJ":"WWDzRyrgtzjX","Im2FUlMYVpIU":"4o9ui9JanfvG","17vyEtcRxvTn":"9F00dDWW6DAu","cTsC8HfQZz4u":"Nk4ULyxPlSZW","RCBrMmITOUiW":"iB0QVoOYTGmU","ZUIqfRSgXVmx":"hYQGvqnXEz5l","wsN103GNSRkE":"NfIJS0U7i0Ou","6zmiDlsSG5I6":"IZXanRFGoDKl","sr6Jatcbl9Mz":"f4aGFDUNSCqN","z8Q8rNqANCaB":"i4RB0bbDBck3","FS5fwAITK2mX":"hO57YlJkE4sg","mXwTzRA3y7ru":"ko7eTcyiLEPj","o603HZdk4Du9":"6pFB6NYocrK1","Z6akGYUgG4dq":"1tCVyWvvUAg8","F9RZ0dLjCx8q":"LP8AjseZ746W","9Blb1ZFNiCwR":"pyN5Y4pKx09k","RLryvNMY6Jud":"FDn2GApdydWN","jkWMU4ZiGaxH":"0NlWZDaq4nDC","4mmN8V81EXnH":"I3ciNRXujCaD","xrjFCva7823A":"WCpUmwnIhhqO","JetaTHwnhIbv":"6eaK1EowX0t2","FTdShyPHn3Qc":"lkebzv5DH6fl","ednKT4frCsjn":"DnnAUEsPhlj9","KGVUy3pxomwJ":"diiZAEkjqkEL","VvwtgmdX3caw":"2ctKbBw4NZAe","pr8H9eg2o2if":"wQQC3xm1kGsw","bigZIaIrNJiK":"M9HNVjP5gzvZ","ImRtK09ohKJB":"FDJTQKD5RqfB","Hu97muWIVYVW":"XYOhQy9grZy9","3ZtsjoXjNE4f":"IHNMFOqwxRm1","Op2XwBWN9zPR":"n5qeGnIklCm3","9tzlu3CWBAeQ":"wPQ22gEaYMNk","7WyNoAqIOqNA":"yJZWM60NFbdb","Rxz0KhLNnRHb":"q9Sa1tVOR7XD","YEdqS1ThTZpx":"9hnrwC9DHhvO","0Re7Fv8hx5za":"I1TGuio4PM4W","N2HIQyte54kw":"OYLUAzL9aJvc","wPvI4H0hyjlv":"oGjV6CP8q5lj","Z3YRnViGT2L2":"EeD2Lsu6qYtj","pHa45OHgQNBc":"e2xuJIZ7w3Z8","HeFm5FMYc7EE":"UESly3vuCLkn","8h6t2M2hv6pE":"MaghVxt5vWsQ","BmBxW4L3MGQe":"NYvnIQ4rNeAZ","qvrsqY9qW6gM":"6jx0BjLCCJxI","55fe2e8HM7UU":"ABdnDvQKv8fk","EufaEXr114OU":"AWfFsb6YZpq7","Nf3WbGqBSSJZ":"qZX4INTo5apN","gy0yrOoDYsHe":"trF9AAScOqAU","C6DvzPgFiV0v":"3lITEEwqoklA","pgVe0KXAmQP0":"IfbVvXIsV2fd","o04M4gfEQnfl":"RAeZoaC7slv6","T6thnv9VvKVi":"xT0KmTTOP2Gt","1gnETSdi37qL":"ikaGgF04JH60","1mWhmHuUMrPj":"6J70eWnbdna0","2B5FuLhRsYSi":"RBQuA6BSDrPD","IJZ6yRB8ITHW":"IcqGsvbeATKE","4WUr6lPoDoHu":"7TF6b59htwNK","frVGmoUi1caZ":"WnXEfxM5907e","0x7Oqs3pNiT2":"4EiSwptgAJTE","HyjmF6zt4DJo":"im5wlFYb1LwQ","ijwTe9JBu37R":"N50yvGSrwX3J","P804lz9GWFhE":"2mdoJL1lrq93","SkiNjKAzk5RF":"PYxVHUek0UGE","id3DKsFNa6wm":"7tu2dvWxTsPw","jLftBokjdOBD":"sgFZHexfe2UJ","XHSuBPOGdbf5":"oP2xPixXQMrn","mfidYqIc3GO9":"DPnrswDSyTEN","IedJxCgHHojB":"Hk2ujz1tzAci","6CAwjD2XUb4v":"rMZRRtCapaXT","hOEPNN1Nt8lU":"yWI4axdt1sPD","U37GMghzrNJ7":"1NqnnbMdUEVe","Y3k9AYPUbby8":"3IGtVEIRUGIs","vot3pWg7k96V":"0zRoAdwRus66","ECGr5UHUv6yG":"QlJuJsegCfVQ","5SCEfmVmjMct":"W0QIjbUIjOiw","oZDCGqSvQN3m":"Mr1cBI7D4gAI","6xqxV0cydUwq":"6gflyGUHcc9q","S3Y19AXTHDOQ":"4xugbVWHl90u","F2MHLhuIqs36":"H8N2Vte95hN5","DP6rfNmbusNg":"8BUA1K6VnPEF","cfo5gZFISvs0":"aplu35n0pRZS","Eckgjon6JM7r":"lmo6chD55vtI","1ReEDBFL7AVN":"RiSuSfKlofiK","1cDqFhsN7ysl":"VxUOKTmifcsx","gXIvDyzsEMAL":"7K4R4XUEyXuH","CNoT0Asz5EIG":"m2zS7NMuGlNF","ugIYivLCcUoW":"ywtrGW6Gfd3J","wzxRRXmE8v4p":"BgwvjO5KThji","2e9PeELxQFKu":"eRmWhwWIHt6s","HurKWdYMPhK5":"VWBFFer8apdu","URqNmYQ4MQMU":"0cu9DR5Ir9dE","Gkix10t2tIIX":"xtHzRgLzW21L","37dkAyxb4N2W":"RTDL5ic7ezPj","PljRxLOnYWga":"lbnToUWvsQWz","uhtrhGscHxct":"AVF0JrbqetdO","vjRSDRghtYmT":"rtVOqa9zndwY","gPG0Q99qve6x":"OqT3FPFRgZ2w","fDAD0AT3FX4R":"ZG5oWTVbDIpS","Sbe4BugrzIyU":"R5w1d87vi7BN","CPEiBfYFfYhD":"920mBMpc878r","sVVwvRGDnB01":"ah06ZelQbBET","WPkqO4YhLULc":"TNg0ance92Ts","KR0nViecmCfN":"0OwiOeI0BFkN","e14vO3vo08jS":"jYRfOdlK3cc7","I2q4QRGqoS7x":"qeyXXb5wjGnR","B6gJokFLf3wp":"LAZ1A8h1aUaK","oj07OQJCfexr":"FS3eBlnbNpz5","jYQmO7p4FD4Q":"YlKHP9rK2KyP","ixsmBDKUxazu":"iqhbVJb61jE0","5UoegvhnHzpL":"eMtZQOBamsDC","NcRsOMNB9CzB":"rU3BksZ9QsRW","5LGS9W3nhFaI":"byIYAD1qM95b","ReTSeFXdpOkE":"btphI6BE3Gzj","uE7glGV84SgQ":"Q08ozK0CaTid","hzliBsOYuK4Y":"yDX3tNji5lvs","jzDfjYVEveli":"gOMUABZECBZs","fmXG1M1OP7C6":"Xx5gl8JpLYmL","Wo2WkSK7kNlb":"MfewENRDvlvb","EFrF2rdx4AjM":"f9JSlvpO575u","g0nlH3MhfoJ4":"endOUeTm8u9b","g6CU4Xhz7NbO":"Xvh8jKv52wGl","X5aGTHmIWeD4":"5b08PtOgVZlj","UZ6N9vMfZ9Ko":"1ZDd8L2TJeOL","lYDVBJva6P6u":"mPBtnFS2sF3C","6mBgsRjlhjQC":"gvXUEWqZZoPG","eck2kps3DaOc":"Su9DGlrqMRNg","ef0ymxehgde0":"kS47Gq0V5mnq","VtpgiG3tO0Fj":"TgXnXsMQ5lQW","DIfZBNcqHaQt":"70A6HTz0raFt","9Zj0U5hrxnzT":"VFOAfpey1D0c","vhrGSeDIZHiv":"ArpKpI3R6Yl0","xryO3Xkha0bU":"dbrcI89RHsk1","x0fJHICFuMac":"CzuiK1OGcVGt","I9HZtHAj0ObL":"xi61rlrnOwBU","f1udP7yQzjjJ":"ToJrZzaf73eO","Urj3Qs3PUFcl":"lmTZSKtNbto3","u0sJtlV1lhWy":"4en2K4An3L45","XBmlehfXG9Qr":"QQ9xyEiFahAv","0z7NYdFFvsBh":"gEnK01H6SCrH","V6zFjdCx8cT6":"ptJHcdTKmlXy","XIxmtyRT1FvN":"DNlGUHLy0SOm","3r2SeRy0phG6":"cHY9EWtTB9Z4","jomlVO2qsRAq":"TyqE7xc4IN4T","vqPgk6tZ6RjQ":"dAO3IDzX6QN8","n0ZOAowRKtuc":"gVWyFu9Y2I4O","3OhuclqVhFwH":"ElrOMfRb0oyy","WSQweq5K1CGy":"1Z2k0TeVDpB4","hY87L0aYEPep":"GZiwkuyLuPSC","JK0gVj2MjM9K":"qCyEXEmQ6xIy","iaUNymOWHUAH":"6gIqKjo7wnwU","eijd0SzKX8EN":"ithz2Iz4bpcH","2X9fm1ya0oJ0":"dVh1HhMZvkPg","iIRzilzOKQdH":"GR1F2240PjjE","gvGkD0FfyGlF":"EX4NsCZi0nHU","FslwRy76VKI8":"JbNR2DIC5AzC","zdrZYOyqDhZT":"n5lu4bXUU4us","1i03Dk7pOWpl":"1VjuOkzOCzAA","CPdV1FgQVPNb":"zVMoncvHCQzg","m1nKqzXdXxKh":"4vgov0eVqbMb","vGFCT0ptrlAb":"rd1R2oNGbw2D","x7l51rsk6W3g":"EydYXoGduPEJ","iJ5EwrgLiEBx":"TMgjqS7dLj4W","vRWVcQqCzgmW":"sosDt1k4fN45","ltkk04Cc2ZFX":"ixiRkStkcLnM","SvdPKkl3IpOQ":"bVo0DPK2AAHK","mKCoyHQi5ART":"QDYFP5anxDhQ","zENB3P4xeJFa":"n79oDdRfqN06","VhqtivycieLM":"1WDW4MmWSbtm","dkevwcstPFMa":"4s2hZvwcds7F","pVotEmcyDQfc":"RXzPq0cbGD0W","QfnyCPuyyvBj":"npWpgkkZistY","QygYHQEJWncL":"3loiRxjTt8Xn","97bpvTAzL7ya":"hAajVzSsJjdU","uIkQikS0irr0":"OpPy9GnNM5Hl","wle7xiPn27yU":"sHuOjMffpUsP","y4qNnMLSIXkD":"A8Dni66Tlztn","OgsxU5rbLE7P":"LkGI6u0muDOr","VwNmRCEcJeb8":"eqNFoMeZ9voc","k38tGrTdyTUx":"JlQc9cRGM1Cj","Ya2HrV0FCjgm":"dLrllzMGYa9T","DAKCXCZo88oN":"L7Mvrd4y61qa","9vYnu8uG3jQX":"HP8zWZFsHi0Q","Fs83ay5nyoh4":"T3sJwlqcNFke","1eOOeHLJhRsS":"DPf6RvnRtd7f","hLMldrpuOkIR":"NArF18swAEiU","m7ZGN0AHhdZa":"saYx7GxRr5QZ","u8cEKoo4rSce":"77KzJM9HC12J","KytFLIWJcb25":"9tKww5wHJno9","HebSVL8Lehsx":"hcBaconkR4zM","SuNM8AIIuiYU":"YVPJYLJye5Eu","B8zSlkbyrEtr":"49bMEhEQmIOP","FB7LXLtM9q77":"AViYYGlj1CNm","DQVsmHnHddNn":"rHPajgi55V1W","WMfuL5cJAiPk":"b93LxdBSbrOB","nQEoDuChBivI":"iSOCjx7QzbEK","8RqRtGZ53KPW":"xUXrPH1CFUqu","oKkj6nMUiSbF":"eBdsKbhKcAeI","IPoNXetYr7U8":"i6ZAh3y3YzNl","DE1gxUevxTT9":"VdVb3UDHLKFw","GRbUkNkWZejw":"w04Id5ulVvWA","jpAkYMBl3ji1":"EANSW0L8xyzH","7KRUMR5XQEoz":"0DLjg80uTrzF","wMjGILfy5Il6":"wsw7beRee9FW","rZSz2SHTJG1d":"lzL26ngpUYHt","aNZD9YRIPN6Y":"VETztPQDdluw","NRtpXc7Elmha":"husgf1MWTOWP","yjvj9pWSvbE3":"72e4M6XRuzBK","H4yLzTl6JcYN":"mx2aLD3FieST","XjFLLlQewFbP":"rSgfz0fMUNy4","iVLSwzf5q20V":"ytmnAOwURx6j","xDV9AZ8pVRsK":"wuBFKVQVNraO","JS8s6CN1BClh":"gsY4clf4Egak","skhUUAjqxTir":"dAhnNptTWiu1","idzw6ynFglfN":"wjCMRo0Dfuuc","pr2IjviAq701":"SmQXhdBL9MVU","78t2Gf6qgdw3":"elUhNYLsKsDe","joWWPKCzSU2q":"dTxKpjTLPHfL","tG7fDMB7vYtT":"pYCGi1b6VTrl","UBkPALFPpQ2k":"ExggNSeDFHwi","3bBkHfLHx8kw":"I1PXQj3hf6d1","2Dkpo222FPPj":"VnevqgQWwVx6","kTU4YXemxIaR":"tSLRAckvLZvY","jA1CG5wcFFqX":"tM1HLMkbgbxo","XLpTfgyccWuT":"mTGGVu0C1OGq","hhK5qeC2RgCR":"HP9qLo6oZtLY","TkvGRXjUUdQ0":"9WTSYGzPzHhb","7VkCGC2bn9vp":"bdCoompBXH7J","CGaKkEPS5KgU":"WFlzQX9T8GhF","YMEBXLvMHzEg":"tUwD1X5YF47G","lgi0gczUcolr":"cjX3qYamNuSD","9Ucyh2Hh1eVz":"5WN3btiBbiO4","62ULAw2DzYtX":"PPCTcnJmhyUR","2AsBqaiYgRku":"LVZM0AIQLMkT","mc4XKmYRoJ1d":"jCRZUEUxlEXP","C9ClmnNAywJE":"PxJXBwCXViQ8","9bQiqlHyDIOr":"4vl8WGwfRnuW","ln2Wjc3PCMpS":"eFVdGNJ7252N","FwCPlZjK3k1A":"BYzSAdVSJafy","O9UCQhBQ03Bx":"DxNV9NP4WLJF","dEfkno1ah6RS":"Ew7S3sNOvD3d","mwvjJv0gO2rc":"PaMQsx7AWY4h","kJZKJDdUYHbv":"ibsdW5wWqKA1","xCoZKudzvjkY":"0Qf99IV7ff63","mj18I7jf5XMO":"TF4bOs0oH5ki","suaDIeYSIjhh":"dgQBwQNB2fmS","GBhaUz0p2pXk":"ouHHPgfL8BKw","XaKzCcZg41WY":"HehnhrZvG1QB","8L7AbpGOuhss":"wFYBHKjS0kao","jox07UEebg9G":"fviOUWT8Oyiv","4Zu5V0DaYauA":"61piH56Kqnv4","3Zy87UiMM4GA":"a70S3xpwpD9H","uZ2lKBqUe6Yo":"TvdB2B5uH2Jo","9lveLZPXeLyG":"J0BucTMnd9G7","rjATX9Os1gcj":"fCdUqrNEK6dG","EUNi1FrFPYVZ":"iG05jzDuW5gQ","wZ0nxSdfIhKJ":"nuXtB2DKA81g","Mx9ePI5IAtos":"aZX2uyOtKoKR","um4SFVacLENv":"m9y4S4NV4qZO","pK2s2Fm1GIVR":"XaiQUNDdXxkv","eAwbTRSaVJCf":"oh0XGvRCPwie","zYSppFDp4j8R":"eNVKtzx7AjgY","LMV6ehSqKhBr":"1qmDR4sFpkfD","H85jGeEKHYOq":"3QHCxukW7ixP","uYvda1ufELJv":"K4Fay0tLny0n","TYx50F6FyLKP":"K4T3x6inSXgx","UWVP4CSW2Cp7":"yoWVRTSBWxcl","ubLx1Iliga2u":"sEcQ4Ol8W97q","nk0fJ9n7tRyO":"AGIXlmkAll3h","6OIeWGJt2nf6":"X86XlPLtLlLF","pwcS63eTCRkN":"NNPl4Dd4RPx3","X8wTQAdMweyh":"oG5tw80gdh4x","yWYtVEOo4Exr":"LFUqhNuTxY1r","VFyKdSKNxwZC":"KNooD0d8zpfb","oMuKYQfrLeyb":"j0OP51D73Qel","pjffU5hDC0UL":"IWtYeydtXXht","1NC8jIX2B7jd":"ZP3myQDiAXpx","kGP0DIaXpIRP":"wXrcVQdP0V0T","B23J8H9IxmHL":"KOPMvVWx5NIo","5qbyPqwQmhjZ":"5g8xfXDQZmpD","gajB7JmtOzgu":"KePdvVrrPsxe","zN0j2zn5ApW6":"LggOzRNc7TkB","6M4JPZMv5ye2":"UXvd35ohkTfh","MAIbTsJkdkTw":"oop1yzZZn5OA","lz8gnLu8Nwnz":"EVkWFTqWHTm1","U1W18SyxrgyE":"Xg57sN27BDvp","5GEM7EjaiS2O":"4QoDZqCGDgjk","grjgEx9gj0zn":"KHQQdmuXY9vQ","dEDKTckKvHyy":"6zir9N3507gV","m0f6YNTb0kys":"P7nFFDsldfoa","W1pFynsSq05M":"fR5CR8ZtZJho","lWjVp2ssmzd5":"485VyyYOXd9X","PK5fnylHKuwY":"dkdeVkd62ef5","mTcVxu17EsaD":"lfo84dnaz63F","5uFIa8jueOHt":"M0MA77EPkyz3","7DYjSkcrJbin":"LH7hVb4d2NUt","ddPhrmy46yIj":"mZlHqcN10bOf","emxtKsDZgtD5":"x22NSPYqb0LT","82bPmaYjyVu1":"PNs8IuZBObyE","2WHTHPSVED28":"0fczzdHSsU7E","eGybAQILeBLb":"4YzZARGgyqYZ","pw7hoFOTLMME":"FPEO987qzAMB","ZbchwJv5cHXg":"dfDgbUFUg44J","YJrD14AR32pT":"4FfhQ7SeEVPF","nWFlpL8Qw2AB":"ce7vhug5BMKr","QDlKj5b5FNY0":"Qwf4PcYiuVu9","hLBFyHbNO4TC":"HCZuTv8kKE4f","HShDZppKlkE7":"7bFUYIbDaLwe","vPp3vkMS59UZ":"6rerBHWXyWP7","yEWueraaksJf":"Op6UPm0zLcak","8LfeqoidECyE":"ClyI0293Uz6C","ypYTS3e6D12l":"7vnkc1JFrcr5","RT4p4yKAAfIY":"IrtgXyyc8utr","ztmz5KJlv40b":"v75BE35Ahief","8QNFv4F5WIUj":"ZDzyMvGsxS4t","VXwsD4sCsd98":"3hcz7O5j0BrI","mui9Od4aWrV4":"cu2At21kf0kZ","jmoK8rX1UjcE":"L8T61LjzUt2M","nQqXyKFQAw9p":"BBjmIZTgD5a7","zquUsDz98sLD":"4Q74A8cLAyeM","LsnFUiqU4uYq":"tox2RGIMqVLP","RjTupohtlOFk":"vPh1kvCuyMRs","a77UCus12IMu":"ZBKQLeoVl95i","iaBAg5byJD33":"rC5IxakF0S0R","Yjf78381E192":"zEpxYlrsaHHM","7VVjYxMprlnZ":"Wbznp3S4SVFt","iamScpjtugf5":"CpAo1sXNaP2N","mzugZQwHwaIH":"3dMgAGkbN4bY","Y0vvQr5jzdxA":"3BXG8IRr2tWc","t3Zog8i2gsGu":"VT7n9ronGiY8","TxXFrcdcTOZD":"Uum1PDkEVJHb","V44cVc8bcEXF":"aGfVkYduFet3","dKkhhrSvCXqc":"kDHhscBVXMUv","LsjTMnm32cIx":"yjyDn4QKk4Hx","LnMsIyrEJxZT":"31u3FJXvfbCB","WlVVtHQPh4N2":"dZAjGlj3AEMb","hP8bHzn7pOHK":"qiPvUZJ3CMgD","9qhoRl5xe1ZW":"sDRd3a3p6ziI","DNka6sOPiufl":"F9wz4cUS4Pwy","VUYV2LerllTm":"KOKz7zjHez68","abQW2uGhriXJ":"9vwLD5GJ44kN","1qnKqwRB8UnK":"zNSaRmjCIv1V","Xt7EgwA0OaHi":"ohIzVjVNLXHR","HqKF5LPKCBd2":"lH105R48Jn6o","E77zZqh2INTd":"CDoqTTKdwoAQ","4qK3OpRchtx2":"quUvG2Ktz9fQ","tc9VFyAFy0wv":"Lq0MQUual0sd","CyBt0C0EQKo5":"WP0uXfrTRQR4","VCkTeBwtX2gL":"6Pj7CD230UqS","hDIs1ZLXXGl5":"Dfswh7bZLVuz","WDkDu8vZ21yF":"93WtYvubfBvi","VqUQn0qffo4Z":"p4KXieh4Um3D","2g8ugdWuyq2C":"BH6ahMvfFqTY","tyVO0leQiwBW":"UkJNsqXu9R3t","6oKcligf88vZ":"Qg7Uxz5Z7dEI","oLuRqOGjKQ9q":"84pz8g1CmXSv","I6I7n09rh3Xc":"DJIHjOqpQ4uq","JBCrwnrqxJ1C":"PHWdkXjAepkA","Kqogo9XZOUHX":"e3AMgpyUyDEX","jqHBsiduhXX1":"UvolXCJLs35g","zh2AngykbPZy":"NlUIok61ey3n","0IwXV3e0BGrr":"dYoQM8oR0Rny","zMxOEK6zR0X1":"shiLOWyWmDMO","3lzLcn8UC8sN":"tCAgYZrE4tPG","WEFdGwZlkpa4":"bMdN4jrZNvQj","Hh7ZfnIBgw4K":"fgIyQejJCMm8","Dge0rUJfVM40":"3lLXao6Cca88","ybFl6KOgHkHO":"WVKi8UfidsJs","6lrAq0tBg5XG":"OvcaizPe75KS","tPdqensl7S3f":"7rdoHEa0fScu","giWLaRma8kcL":"O90yqEgih445","DvZTO40WXGy3":"NxC2akiwocld","3z4rdpc1EI0j":"h3hgfUs6m4xv","GvfXPrgOUeja":"FbzxLvOVIIIL","gN92nyUNPhGt":"gwdBYsP23kFx","tKyx3KP2Xo1M":"MWoBG9arXt5g","DKiZNb8HlkfG":"LPTsWrzVVLvj","1cberEsYkQHV":"0azJ2U3dvxIh","5466CrPeoAl8":"Kd1eavW4HJKz","0la9VzDONwLT":"U0GS555UVrsh","q6kHEB4k5UqQ":"siwZObcoFGlG","0Kgr1ONzWZOy":"bLH3K1YzoQEg","FS7LyrvUfk3q":"cZCdTHWv5OXA","NiCVKdPyp76L":"kFl9FSGzezm4","O9Eyrfmf0I3f":"SjlH26PjjOR8","32YzsZGLlg8G":"Fopr1xMO5B1G","ZS2K5G5pcDjK":"BaATh5Q3BKld","QBiU0KVLkSS6":"F1CVKgBVwUQs","nrDSwd56koRI":"9Vd9ZjibYMFe","F0Bvk824o5SJ":"5EmVK8q0rHoZ","mSl0bMaxK3Oo":"D7ZeH91raqjm","eq8oz87mSPlF":"UYOVZjfyQTty","WVQabOXc0oXo":"8NSxa7Dl8y3I","gPkRmqwGT4gy":"iXOjTl5wxQVC","p5RVsyFn9hGX":"lCZylU0IN8Sn","hPlNWQY1occf":"oGOvezBsvPdL","CGVl7ehS4XG0":"cNHQw2PC7Pls","AsWitTW2Lopz":"BXGsjs1NxFdf","OlETmA9zORwd":"P7CGAOzvYFQV","WA90KEAonLNe":"YVGPBfuw5uiU","YWMsnlWjQjKv":"LluVdWPIbyiY","g7ozsRswiIVO":"E4qyd2Z6JjMB","brY3Ieamihws":"wpfA35szTMpw","ERN9epOVk6pN":"NdK3DMliYTi3","JQyxSM9kJ58A":"NeyxnVJI2rXP","m7Pram1XOaLM":"GcDKtlLD6D1Z","F1UVHfBIqyLj":"1urFY839lAM2","tqjgg6P7XcDQ":"CzOug83pQR4e","FWq88SBDHQy1":"edbHzW8LzNFb","h0WS5dBJXlbq":"bl6XsZ1I443P","jChZo6Que4yj":"IpjFTDmEKisL","TpES3f3YXx8Q":"Zd4NkVZ3Vxa6","ymIPEjcH67w4":"LPUArcfu1WhA","aJOuLPCXkBIC":"p4m79LtegwdG","H2JCWAp3pIFH":"DaKcu5yWvoXr","3AqqXG7n54dB":"4105VJi4PJcl","cqSrN8P1Otnr":"FYLYssHrtMxz","In8u6OwJLabi":"0Tx9qtMl8Smi","E6GnNMjv9cNW":"NIHiqwKKUbdX","HvAiTdhZGcGQ":"sRcSHB8ZIDYP","3fIgYafmilw1":"cFRJ7SzpDype","nJRotxazLDyT":"J1PC0Ty1kqdR","iZ9CR1cBUpgR":"bpk2XXAmxVBg","MIItbkjBYWPE":"H7bCDeEoXKLm","V8g9QCviOoip":"ZwQGBRamTgLG","eZPGVwtKSuNB":"T8arCxFFy5hp","78bSDE9s76sj":"8s3jdvHFeXGm","E84GfboyEQWI":"RtslLtIfd6N0","LY6bYrdxIYya":"3y04N6dl8NjG","ANoZ8ofO5pCO":"HvvBRpIE3ooW","27fafhw283Be":"ElInLZoCdJ8c","T8eggXosdnmH":"m9UCDjioqu8u","JKJu6RaU9FYg":"iov4WIxkxWUV","M0pllZN7iT8h":"pKitdCiiZjKv","Aimk1kH5Fpea":"26mQOG6z8xqb","2Gc7qaxwmIDK":"npU1V2Txbg3Z","E8UsESHIXpXG":"fl6b2j0leTmI","HudZnNTZqL3k":"S6KqP7AkUuwi","uqumKmW9I1iY":"4HZg6B3DsmH5","AXFOTEeajh0e":"MtInXDPknjis","WsOsDcJBNNGe":"FtVG9p831XmN","A7BeUPPBhnM4":"wLtZTHsNa5Y1","0X1FrAzEt0eu":"0qpHgIegCfMO","kjScfFq9TLRh":"hP4l5XJg1pZo","NuyFIz6CAKcb":"XLWjd55YcMEf","9ePM99PGfZPc":"h4SEAQuZlD9z","sMW5GyNxkVeW":"0e4keajZ5Wp6","ABghwS8cT9Xe":"WV5MJmyVXW5v","idUxyPanenAr":"IY2avx98C6I2","vc0LC87VGRi1":"enEPTlgepuYa","tHbpMjeaID2T":"jGDF21tn2ODr","HzzU7CRDYNZl":"H7iKhc3VrDN0","yWtZYAEmGoVb":"RVP9JypNk1ge","ikd28LMG2VIR":"SJSBNBMB2m8f","d9u6mTTJ93bQ":"xapnLewFPPOM","RLcZUCS23i9X":"fMmBDWcjhnT7","UTbTa2k51bxL":"zyXQkusjKVp1","jgae0Qw1reqc":"FITm0JlE3Q30","XNdr9K2I0Oba":"ZBDFVsocuV8U","toWDToJOfCCD":"ItW36RRXn7JY","hlvdHrkRtCxX":"tzhRIvTxm6zD","OXwF7OgujTei":"wQwxumyUx6Vm","UDkOsMfVKnGN":"mRMrKRQvEdi1","k0Hb0pPC7rSL":"eqv04oO3nbWV","tW8R3vcTu076":"yyvMrJG7bAWe","ULcB5jfDS6xk":"EyO9PuodjG3x","VNvwzAOFiCgd":"Fkdx6ZcKWRbs","RvkY24C8oSl8":"vrHGMqiWDYKV","6zhkVpYIH2Ch":"zgHBnophGvSO","fNf3XXAauJVg":"rSuPVJvGNseB","AKyUlqGJyduD":"um9I22nPB7vT","QcUuai6qzN85":"RaxYjXMRdBbv","PCbfEZVR0LUl":"2es0tZSuK9jR","28kTwje9VYLz":"e5eweT6vBPF2","8ZDYG0yy4HkS":"p4HC39Di0xB1","m3aC11s6G4D3":"vjxlbZgKXsQl","d3U0ck0JKs9K":"6i8i8aS4tZUm","3H0OJ7Uk7lFQ":"nXu5Zv6taDo4","45qNQTgVdkqr":"gnPh13l0pd9w","zXNbMpWnDPLa":"sYq5dx5qjebT","9uwQ31XiKerQ":"ElDqOBlGBvPm","TyAwT7IHLu6u":"PAKE3VoKumQ4","ArHs9DGMxpGR":"z1iJGkFed8UY","uclBqWYd5781":"O4XrGstISy6B","iEPFEigcP2ou":"pmypyfOXxRC0","5fjpTWRjq8j0":"9oMssKNMDLvf","B3JslCzkLIlp":"Kiw2QPDsonHd","yc9cOIKXXheq":"IU4EDU8frCAc","i4M9L95wLG4y":"dFnpvEtV0XCv","VywcbaNpxEzc":"e49vLp83pLq6","AepRpDwUGAy9":"BNj8Oid3krt7","1p0A0PCHRgsn":"188w85TgAyOu","x2tbNI6F0ulJ":"cS55cpvikry3","ZEpl87HHxwl6":"uGW2Tf051HmC","PBygj5af3rKv":"NoOlZfuVH2HY","7UaVDFNE1uwT":"ywVeV36H3Xlf","P8uu1zVy5lEr":"IfQyQFQIYr2Y","7D8Os1PVJ2Kl":"KQbDfMwjh24T","gwLT2QMR3UBn":"idf5muN2t9Vq","yZb2huDT0gwa":"y4gUKMfwu8T8","gWhViFaVsa7t":"1R4c4TZ6xdQf","Py11Ga8L7rYs":"Jlm4LxmcrdFZ","kIDvq11asJA0":"7XoVyfuP6TMc","rzXscWLjLeMd":"WV4T4e2CTIVE","fOUjjvDGKeIf":"28epxOJ3OTwv","GoH81aA0GVEP":"3fMWWAuFFVFC","pgPWtGNpyXaP":"2CFOlyKf7CRq","7f2mSLmGu3QC":"gc8CiGBeZODi","x8AmhtfycRUQ":"a5rrYxttfExL","pDHO35Ltua7t":"VziuqHbdXCie","5tqDn9VZFFbL":"kuhWEzQl3EJZ","Baxzy5BtDPvO":"IpTKy2qTJ0Lc","goH6JLZGXddc":"UJ2vFy25ifSB","7S7t9oxftB40":"mjTWISP6GtYz","HTXC4uEpbuoi":"G1eRwtI6IXyC","nhwZdv223IMx":"YXG4sulxygUL","XYNSh55PoIOR":"zaB98iASxxMa","jwGlU2W7tsPR":"DZ8SbVH9XBtE","KKrlh1v2Yqia":"DQTUzj1RIeJF","JNWGnFwrb7Hn":"T7qriuWPA5DR","w8VQTj0dtOQw":"fWMbBWCn3CYu","aKam6s8ywEUX":"KNihx3MYBXzR","xnd2Nnfuko9c":"4x0LtPhM08Xx","d1JK9yuCyr8k":"2UrVribhVvXm","KthHERZKfH0S":"FNvTMSOqmqUg","GSgGkzimlvUw":"oH5whYc0mB1B","WfQ8YzwE2P5a":"6gZgHylVzCpE","Fs4bmn0ZtfMq":"gtNUyiEbfTKp","UwIYhTK8aswF":"6Tg0y1qPf0q2","FBA6OpfCKw4d":"UxmsPsWQbk4I","m1mjRYh3tEPY":"bY6QK9aLefor","3vqCo4sFYsYZ":"FNF8nykyohk1","9uPJrOyogBwm":"099d4ic2DtnO","EOkhDGXCTUi2":"eM8xbNFomXg1","Ba1hSsMa9x1O":"36mMnKfuSFet","OgLkOSTLCMjv":"LJ6qOLmRV43w","9QYoTQWvz9XJ":"w4NfWOiDqC1B","rEfrClvQ2rWL":"6MNfov0LgzXE","wITrTXKcM6p1":"tAvuFSsl81jz","s4UaNR6vQWta":"FGesfvok47Iw","k49EOiJdsaiT":"bVCiBpwwBYZJ","5lvhlXU8SCOe":"yOXdduIMiFpE","nTapDS15Xuh9":"YggUTC0IPt0c","jncvGio6fmn1":"YoLgv9vu8kCY","62t4vrcwupWH":"Bx1cHztMf79p","aUzN8gwiyZS7":"vD68aeKrkxFL","dolSyiUDRcXb":"sRCQXoYBDk5A","TdHjLKhO4F93":"ijB9mZjH9Oxo","Y7L9D3a9Fqfc":"8AykaGlKEHfa","48UieY7CBlfs":"xJtoWh7Eo7co","XePyG3Omk0GE":"woFTGXetXXfK","0xHnPCVgcykt":"xlf57JQYdLZL","lwjRlW8tpDR3":"wN9sP89AWlcs","YNqdozCwht1q":"Bt54LM1WB8T9","GLF9flcEu41v":"ZTEap1CuiS2l","zolXMiI9vPmJ":"bYbYexRZUxX8","pho5jpWdRpvp":"aGEfGrtJ5Cuh","P9nJYH7AQcfF":"VhIqVVaDTxhK","hegZn6LzOtHf":"zRM39fbpECT6","3OQIG06ZnkWX":"hbWrfGvJwY3f","eLxCd1lGzH2K":"Kg76cTLD3c1H","aVLfDtZv0XSH":"gm7ae42YNWiS","laXGyjru5q2p":"PDCKWK0EoV0a","VncksK5byam4":"6auCs0yYqJbW","75xSrlMOoPDZ":"GEYdlJPuCWOO","kKK0THCTmzBR":"piL19Y74NOwC","LOS8EgXSD3sK":"7kiTmys3ekv3","ZLewfHeAPkt7":"vn0GXGFdRfB7","KIn41lNL3Xyp":"CrKKju7dmu6h","NQjXxEpmECle":"LbdHJoaj9SVp","b2uOkWYQaIqY":"2ZiTbgIITozQ","rIYoHpxC8kFt":"uUvBIrEsdMj5","fE0TSJ0zeaE7":"SnodaSfpQLTe","ozGVLdFNJYWn":"qESjno4AfW7p","49rmoaOiuPK5":"9FkjefBDrMwW","WFT537lu8TLt":"o2eivyFETqGI","MvtS3MkPUYVS":"onR37YexlWNa","ObavCduq2EuB":"27K8sQbuzjBT","pYMhyH1FWopL":"iq4Vc7Qy7Hvf","1dwYPpbFYsKG":"CZQhpQadZZJQ","a2SP89mHyvVI":"E1GKdVLg2qDf","0lmC6w6hoNPo":"glgEQpqh0p3S","J8gBblrDbokD":"3IR0gHpfsvUZ","ONH8KO2YJgNE":"VuH282mGgn9I","4VniAS84fuJL":"LVe2RLlCQa4z","Rzk5CPfUBRM5":"SpJftew95MEu","pbE8o9OkOgIz":"FgaA3L241fZU","4cw3xOrRhAKp":"36Oq9nOOHR3h","2m7RfYD1t3ph":"ZVqaesPjvc2c","uvbYXJ9GRSgu":"oaGwhhF3jSco","NDAMgImNxDl9":"wsGULYnDM1Xb","1t6ybTq04s4g":"4Vky8puNEkPS","FuGXRVaGP9rV":"F0Apmu4mdIyJ","QtdWzR4ZtDRU":"zkeBwLKoYM5V","bSAzqIHiReXL":"jO4q5rmIgABS","JERnFK8ZLY3q":"iPvwzyQBfI71","5dnfVcB1JA9o":"w9d9qBqH6WR0","OIJ99KKNJPNx":"BeXAHlzhCto7","VQzDvBgH7rwn":"2eesnzwmyAk6","b97v6ylqqWS2":"6SlipOoRyIfo","oNWzRVKGPoF0":"YlrOZzd0TmBa","faP6jPL2zoiW":"dBmIg0TckQc8","3iI9AGQewp1O":"ad1mR7tWKD1F","rCbPWijKazL0":"8ueShir63ehD","yRu0oGR09xUm":"YOaYZ3RHftdA","AFIOvrpTsreM":"XMrrraqUmmr6","YtlrmYds2ejs":"o2jZ3uBjLNlF","PImeZP5EmSR8":"YYZ8JxW3Jrbj","EMPkwjZnK5NA":"Y5puBRSXzcnN","wtmKpm9FUxNk":"VrNr3rdrmqLx","0Qw0DvGgU2Qp":"4WWfhjFrtSww","o527RGd3c6aB":"GrFGPCnTcJLE","Q2QXuitJinMg":"zApmySFzBKct","NwKDpJs1OXGG":"fDk0mmUajH3B","rnIsRAmvaYEd":"Vi60GWyUnNhZ","8pQBBPSoXrQp":"vttd6kY4vXnL","q4OxCC7sj20W":"LrBtQbzXvAjD","2Cnq54t25x5X":"Pk1dBXOOC0JS","9zJDwc0nU6zH":"6GOCzuiPU0u9","401LVjEogTfp":"YuCIkhn4zVFl","3peu7CzxAJYa":"hc0d1fdmyEAt","Y3zA5oUDLbGq":"MFaivbyNuzev","omQnc4nhcBQr":"n5v1JAyqPXj8","gOd5GGPGfQ5j":"Tj7ZjOXDBq35","I1Qobko22WUV":"w0tnQDYgKdD8","VtoPghR3gfqD":"9kVDu3CjRwFe","WvkrWysLps1c":"6BQWRewrMgDX","pCHJHGtrCsiD":"K5gbQUR1OrAc","0OAxxiofGZ4U":"IQaFDxBL1Bcf","gFA6NuS15cfZ":"Cz8UKka7HohQ","KMy2ReTPBAX1":"nbTEw0d8dNPO","xrYVGKXjvMjd":"GCBdzzRB603N","W6tAp9NTA4HU":"Qd9J1bXaynQe","0ExC9Iy8c0H8":"sjKnQFYxXkE8","dqkDNEQ1suN8":"KTifAXjCoQVC","J2kYQvaWecSl":"PP5KQsmaSOi3","PoGH1AS6hkFl":"mMeDW2V6ZCEV","EKTzWoKcPzMi":"3UXCyYjRftGH","9DkJeEFJs29s":"yeagq6wPBKlI","byM4ZR3LgSfY":"RnkEfnh3IJSw","jCsWHtZBn1lX":"8PK6CNeJfVkv","qi7OZgwatJgG":"SLL9YsPzHBYY","8prINZJ2OCCU":"Fa9thXtQrsa3","ZvpOHdLBRJYi":"eHJF6g0yd160","soAC0Z5tgzZ3":"Sm9Ik0PBEWUw","LPVkEbT42j55":"X4sWOW5LLGDM","9AlZTaqgGWu0":"J8kts7RAA2eC","q6cwL3ZZUO7k":"R0BjX40MzlDc","6yy7Z3xW8tH4":"2XNVQvSQ9gdL","IIWITRZbVBSo":"DSLphr1dAWYM","mcJhUIdg61X1":"cdnrmuMXYFNN","MOmCR1n7L0tw":"4v37c9SQzdrG","Ghyes6HbwDBX":"pG8JvHTLB3hu","npfOQjvidBhH":"JjmVWhFwAgBP","RrwLDeGHiObm":"NjZXljgOxme7","GsbV80jX34cy":"qLuOsemFMzUz","6tNT9suGO6Hb":"hgU1OKU6gpNZ","YFDsZKQpKbfm":"VZrSshaKTlzc","URJNdDW9AwIA":"ScQQ6b4lKrTI","ToOJ193mvcpO":"lHJbYdMgWwKb","pmcarM63yyrZ":"6Sy4nJ1TrVeH","J6FVodHPY58w":"r2aO5YaTD4AC","lVUObmZzEyxJ":"16tdGLDU11SY","UFIKsSdJYG7q":"k37BH6QYbeGS","dRrzoPIMiHqi":"OS07cyG2xBZR","Djz9LGZkwejC":"sdNA2t7L3tHJ","IMUXGLe4Kmo6":"QNyR3KiRttro","ThTSZO0hyXqS":"DPFmiWY5D2Z6","m8sX2clQiJs2":"1TfOWd8BQnZq","8DuMlaBOjmBo":"joP76GfkoXqh","Ap0Pn1aCPEZI":"DBZArY2Z4CWL","OpjhdJJJOm9l":"BdNQtfoqKBay","lAEFzxImhRFZ":"0OJBMCF7kRS5","5rP2Hymxjj5y":"OBM80cy6EFV8","AbCft7mvQdG5":"T9EJvj6Jeqey","lDzb72tejRl1":"2iDprl2ZFgzG","yKO219PlYfVN":"LQ8A0LWzJY56","kbF77hYdBNlV":"TYH30IhvIKXU","fXUr5UG4GRqt":"mlxi2VildW5T","PDdcgfUyKCun":"Twmr97o1ZsYr","r6LKcElYAd67":"ESqQfdfilzOH","sFiwKqdvK3wO":"U7Wsqst4HZzA","1frqDqLR8Asl":"lATaOuXDVURs","NOuGWMee0VPF":"APkqJnVGxdlA","VD6GdJMxTGmb":"pueBbFn7oAuY","E6uEtowoOKKm":"acGa9AGijjjS","ikP7cvvapHrQ":"3KbVO88pwyhA","Y1Kcl3xWSwpD":"6mDx1gPg5XRM","tlTx504OjR6T":"705GGW9rcTIL","p5hivB4hh3dz":"qXEOUmcw0ShE","I2IEaFcR6Ntr":"VrDRpjBZ4ldY","VaqXqQdFGqWg":"jurCY6jwsLMY","C8qtkZ0S7xLl":"G8AQRXFpV1vF","oeFKkikI9iI0":"LIqwqFyJnlVU","v1BK9Bcy97k0":"oUHErFMw0s20","PMsU2ZpGZE0V":"dq3adhLI5fDy","iDOG1ND6kECR":"Qn841o7iGBvt","gEM8rW9SG7Ro":"3tGyVH890CX9","qCXhH7ysPfsF":"vrzpbwZTY3fd","Tdg3yLRCQjik":"AVEvfHAK1u9g","htu1Lt4O2fPU":"nxhqb1YeVrcA","qArSx48wMZWa":"vIZYxlrqzOby","bjwRLhBLYSmQ":"sFe60ZiOjM1I","R1ACWVAl8B37":"CSOzEXGKmb3O","8SNs0dr7FSbq":"JXeY2reeUsvD","L8yHNErysgT9":"B1qpwrAv4Gda","cwYVaSumcMS5":"43tZp63ry6Zq","SqUvuqMRHmqi":"JUOHcERujhEZ","yYg23WxhUHI9":"RO1zzNSA2WDj","oTsCqmWlTzyB":"VFJYbxc77LYp","4eOOODRnrDGX":"subGSbmvBQz5","lxGeSHZcGoCO":"U9R0CmkyjEEM","uhgrMEjxPg78":"smNneBDwT3zE","6A1egmDrsq9h":"VJMFDQcrjyy6","mYZFwQuBdBmj":"AGw40PO6aTYw","fkI8kZDUEPhO":"4IOjye0vF5n6","7gehCcYUXuaY":"d9qOLqDPF8iD","2fFsVPwt0CJr":"Tlc9t8DjTq85","xfAdau6Qfx4H":"PGp5pPnPUQyJ","N9Sfs3dJ8hCz":"qHN1HWhnUJkH","VkDoPLkYN9Tr":"nGTiHtmGxEIJ","QeYqyoHd83Je":"XdtngcusC09w","KpLrx4LEY6xg":"0o2bLSXzX9mi","eRAPbczN3Mbz":"dYdiq7gtUl5K","xD6T4cyrRHUr":"gIVAE8g4Zu7P","B2lWNyyk4eeY":"hux3zkwNq866","Eg2JbuMPndjx":"T6A446QGdlRz","hRz3FOdir3Uf":"7gN7alaTM1kv","Qq1FemwSZEbj":"j5EX3UXFqY17","8tsPbQjZ7yKn":"uMyMlwIVETpq","Wa72AKIH37Zs":"4IGiJCR4Pu8o","Dys8RPIU4oXk":"EW5UWVKsUhhu","v2V7vxwc9X1k":"xI9LlibBddq7","Ud4i9RC69XYv":"dxHvHG6IZ6uw","qnL64QbMiy9L":"5KW6ENhbMJe8","4L1SZh50PKrI":"kvhzNYwfx11m","jfuxUqYCWFaZ":"ln3ChzRI8eBK","UUazH0qiywlY":"UiqjMKzzAtbx","7avdnM8LIgqF":"UIzIWvHVIsDs","Z84x67OHzuuE":"hvlQtf9dBFZA","i3qqbPHox4uT":"IjULPH9IQd2t","rUaL3p4gTTiC":"diKNB6fuWi4F","0ocUlMPPAwEL":"OE7EIRmS2iBo","qKTGCAbHiI45":"nuKUP1v6ddee","Z970uVvXEEUK":"yeHaiZ6Rluoq","n9boAjNwgUvT":"IAkIy6G0K60l","lPGn7W8D16er":"Y4xVeKcun8Iy","pUlwxWgYbt13":"d6tj9JeY0cOS","Xg0hv3X3HD5v":"qsBZTWu1wxu2","E7Hw5R9lhXxU":"KeNwRWry8ooo","97Ta7bBq8b5E":"qlzy7XhGZGIE","jUunbUMvcP85":"gTAfQ2VP7yrR","4XKSRPv3r7h1":"sM4y6dNfXwVW","Q2M2KjGyBn0P":"WbNq0F657hSr","LImXUNuzRcRG":"O4GnOeONdE3b","SykNsBQnFu2x":"4aocZ2TBgNcD","XmwlLf4ggk3I":"w98Y25VZlkwW","FZGsEhmH9y57":"qnxg6FDqZot2","jcQHBzlKKbsS":"8sjmpjhJfKfm","WXid5yZK47yl":"ZT5K9A4tGWQ9","Zvwnww9jNmOs":"ZtBZLVUO8yyZ","yvY8bykjpiy0":"0SbcOG73Uds1","1H7WE3uOGRrT":"JJ2EOy0Wlb1G","fQc4QNVkLrsd":"2FxTuzJR0AM0","2BZyfFXkDplW":"pLN5muAtynrp","ji1hW8zPSuOP":"sxAYxQSuGZ4c","qhZFbdZcdPwa":"ZsqbgA9ywQ7J","gBWyl1vb9wBa":"rH8wOuArwkHj","lXlMLhgLkNHN":"EIOvzIRerNEk","OGt5SHSxDQqx":"2NquZY7MBT9Y","lCDjNCvvQXPn":"VkQI9xWrFA6K","S4HxC9TDiJvH":"jdYzECOEcNnJ","YReRYvOcKH1Y":"uPeYyRNLf0fm","3kRwshh9prOW":"NlQMBUyR7sXX","FzsF1B3E1yST":"Pok09pb6qrso","ru8DQ9xJcmcX":"G2LXv08MmYUh","tX4eedpXacOW":"74XEdYsXaiwv","S7s8P8b9snQh":"azZcXrJSgoXR","YnJMYc7DoqWD":"VlJCH77hTwkx","l27so0pHyhpz":"D7NztumCxqnH","cRGkjHbN1rS7":"izZ8XnQu9Hto","B8I9kYzaNYcH":"ewSqxC5pLK6p","PUY9ZGPB4WSW":"LXAD3C1681HO","EmgcYtti0GPL":"3zfs8ndy3eRU","VoSVAqgHFlCL":"q3QB0ZlHxrfK","3sOZLMfMYXS8":"KUFmICcjU37a","208HSjSz7oYl":"WzJGd9Byar5K","NtObBNqTqB1O":"6rbLnIZp4mg6","AGCEsvacxiQh":"bQRqy2Lhpj2t","d9bUc3sxvUgb":"V0RordvhJY4Y","hMaZdLvCxOpt":"uyZhpITdWpeK","cDW9UmOICc5p":"tVMzk5iqMblH","S6IXGvoB6qiE":"IyrVLRVyxndT","kslB5NNbCA9O":"mPvBcjVtxl5c","1qj0JVR3AXPX":"SEqZa23Uyi7b","3fmj4HjjDZS7":"bCaPCdPlTXrG","Z9HCdbidMnjB":"xkoGIhxXwwPw","MVPGgMBqDmKY":"VAkixMDqdBNs","DLZEDyF0TidX":"xnsXayaGuRFm","onAY4ze9LOPW":"nOFa6slyC1sq","JTQRkCS9ZEZb":"ahRuVFz3GqHY","T9VexSTy0MeT":"EqGR7vQXTYzQ","4kSt5IkOhfAu":"IQBkMjKaHXL6","zKaM3Obuhmyq":"RZM7SnFeqPSY","VR0900L6FuQb":"aDqytBlaXpTx","4VzmpFwFqxWL":"wgXVT04VsUJh","0vUdTKKgBSvL":"Fg7fPi7zInGq","CITMFDPrySbI":"Zr0Laux4ib0h","6I22qcGVAUve":"QefdsLHM9ilZ","xVApbZ6SYOAu":"WMZHUrZb9PmB","0ouGakazjoJg":"bvRhszBExJ5T","IflSvRvtfgpx":"Zz8cSeDVqOJH","5e8fu3kTbFAx":"s0QFCoAgu0hp","SNQhnNhY5HpW":"vKmw5eT8GIyd","GQjnLwlxnSa3":"Y4MTfZOh4UTf","Gjj4M45K2HZP":"TWprkIvYThT3","Fcu6wRdEqxbj":"Wy4nLKMpsWFE","Zc14ush29IW6":"RjdjifobBGfU","HZjir2UlgiMv":"zO7kuAhL6hi3","lF9gpfUklhLp":"WBsv9Uva0zl6","znEAl4UG8iMw":"wbE8TOVsth0G","OyKuGkRXFnpO":"lK6HP0Eqn1UC","J7PTP1a7CQmH":"bTT10P04pjRZ","01zB5A1Z9fK6":"1ut0756WOP0N","Hqo3uVoCU9bn":"bdG7zGtC7WfD","onNwUV6LIHWN":"5hRFwWk2WOSq","zM2h0EshSwCm":"1b3awoV5LYVi","LoWxg0lzxme6":"z0j4LP7t2niJ","eqMl1YmiM54U":"TMxMCki2dXOo","xzqwFzP8o2K8":"WqAOXzXie12J","4WqsEQK3w6ed":"F6cqdg8xUHgy","UvNN8Z3Hr6cN":"Gb7cTqUXdsaY","VAObuxX0v3cJ":"B32UqiRXQMPr","FUpzWThQsF15":"yOfHv4zkXPmj","ZbH0s1VuXxOR":"dIkLcbl3ezPt","tPvqhfk7IsCU":"u4HtZdftT5aU","8kYLKwwJSZHY":"of2UfsqKPygF","SFOcrxz8VPTd":"DMyx1eMw2LFu","NgsK8dvOOJxX":"ovzf7uChtX8m","TkZtmpu0zCvn":"K4la1pGHGVn3","ef2jg2en6cob":"pWRmQQpBxUHQ","a3OIS9cIKMEm":"1ii9CgGCjZ1T","35bjceTaRccf":"LuFBeQBqe6f5","oh88pKc0z68a":"LJx5Aj2lgUQz","qZ8LU4NGuFGV":"tdp2PKAR4ztr","UCc5jyR7uRCh":"tW8CfpCsrTEH","5Ea9EphUcybM":"fj9ZUPt1VYhj","9nHzf1cP3gZq":"HgqzmJzH7SYg","RzPub0Mt3S8l":"HrFWfV8NpAR5","uLZIZpGnCA0k":"AJxwK9GIi3Rq","iCryvNeMqOZ6":"0i938GnY2u6X","P3QVAXSIduQA":"x35j3Ucgiu2h","tYXtXLYLOErE":"s2NVhRaMKV4a","JqidZEvcm7dS":"00QjPQoqSdAa","ewagObI7Dha1":"qD3TN6rdWolG","ctqv7f0zSwns":"b9hcKxgCvxmM","v9Q8g7ptvZfH":"omoNEzhbQvZW","ZQrZb6jg0pq9":"o6RmCVtUIor2","TUAXHNUhV3Mi":"zMgzwiO1KJmK","oEAgxkak0Hw9":"6U7stM7mg2RV","rmgiSztXuGrj":"OYgrVCc7FdNm","oFJUGwMmASoF":"5bnbBml2sRnH","AwBkDirXVHoF":"6v2DAc3zItzU","KCDNa2dtX6xE":"K98dkSy7JVK9","rcyXvft0CVCD":"lqsyHnzj6beO","Az7hanrCDsQp":"Q0t1vJhVkHN3","sv7FRkeGUarD":"BgcKXfIflt3a","gywh4ALuJKdS":"RjXcXj2SSjB4","S4x1BeVqXC3W":"foj6rVdYesMn","EEsGdqDmJMSQ":"AwL2LhKadywn","vNo0TXGZ8Iuk":"RXD3Sbwj69tx","fd4W4TgwxbCL":"HTGcdCmVwAfF","iFRoYedtQS51":"6kvHqpafVQ29","Tg0ExOdcUaPF":"QRAjhxdYFlYk","JKkoPimGL3rj":"f0TKND5mmR3w","ccFyAetbDC1I":"RkFvr5LDwBUe","cZeH90Lh5hkr":"eT53IXvxECcf","40g2s5Ei4SvR":"SE6YJ2AZUVzj","5RSVlxO9zh8L":"YZyw7OBRRzf9","noZpOPnGPyPh":"hvM3z3W59v4t","VqvZ6RwxP0HI":"xIs0P0JviW7C","HC3Z56neymA1":"Bba0JuxAbdGk","L24WrQXXB6ll":"7jXx30fq8G65","EYpNZMsl8LoA":"zLvw37gk1bhK","c69oE5j77wZ1":"fwZt6JrjSXUR","sQgDR3DAIepN":"On96FofxJ0UA","7S63vS1O9Hp3":"Hdg87cG604sf","rlHJCbEWgvzQ":"mEjDjAu2jcu4","kBj496dghi7w":"Rpd5fgF6YWFK","n6pGv9sc4g4X":"H2ViUXSfMUeR","r2PFimrVPgPz":"dvB4FE6dgBAh","3ULC1UmJb6F2":"uBw9fs314DSF","XH4gfyl9JakT":"c6RSqsmBFaFy","V2ecjI5WhI5f":"r6htUsyodwD4","JlCXnMCJSg5U":"apsOWQ9zC7nz","je8vOWTGQ2mN":"xjGggIQAtxxu","AJZQBSCuyHF4":"mWjNfgxJOSOm","KXNGZKmrV3Uo":"79TqiDp1wsxC","9943Rqtn1hv4":"61vRnjM55peT","8RgRXv7ghAmE":"sQ6xdMFZlOok","xVgF7dhsm08S":"OjmVypCdBBfg","TxYu4Ju76Zak":"g6sAd1XHFpV9","xCyVgAyBTuVE":"LFePFHYeDX6O","gi99uujBMTIz":"8JsDogVYfKVD","O73xL0C7EZAY":"ncR8bxFw3REi","BBeYQxVuVIwD":"N4yGinH8sHUS","XNd0scGdjS8s":"L7mxIkkUKrF5","wqTqNFtErMCl":"Rd7Gb5OLUhCY","3X3UBodnH7oN":"ZOAcNvb7Xy6c","kc3vWeAUS3Il":"QCfAXIRwP0vO","T1AUqW2ruTrp":"Ghq7z18ZIRZz","SMN9mbwYFaVc":"45Nb9f73KmHT","qJJXVdgoe4Hn":"M5JpeVVeiApM","2NYxAfbVicgi":"8tniiW9h0NsA","VmQ1RoTSGQWN":"AO0P336dg5DK","kXmJRChR6x1Z":"rWocJteJM2Nv","n2zYX9rLQ320":"0bdPUZ1MpWIG","RUQLxbBxMdR7":"mA8ru7rlaxRg","gADP5V8GYDTH":"QTlq5iLV146l","chxWsyJdRn7x":"X5hngnRRmlZW","M6VW9L1FptjN":"LoAu4LRejIHY","ddWfzSFDxcgL":"rlcxV24zPGJ9","yVSUwdbWtCZK":"1GHW2vNo66I9","2x86UoVfCWr8":"cZ7xzW1gncNe","tg19yiauNlwB":"aoMkwe6IKDVT","sYVgvQGcU318":"RBCbMwcsOFPt","PQBlqGCEQcnR":"hAx3EzOPU2pk","CofJQl0eC7ll":"kgjTYXI8Rn8p","B5A92HzsGBnu":"FsGKdydyQF3g","Kcz4VhpkOrAH":"DmOCD9WZOfMI","j4DZtquafSfZ":"TszS93gYm4RP","cAgAU5HAz9x2":"BSclMK3ZSiHJ","6F4K8lnQefUT":"dY7oxDauz0bf","cbYb6Ey962up":"G0maHJmYls9c","gzeVqpL1elFB":"xFT9QpebehHw","ptFYwTWkeAof":"ofcO5gTdK13m","cSpVcCcift17":"7QDeXUhOaRJ4","DyS8T0duqgW0":"fGCFPdSMH9CW","LKW0O7DJ8o3N":"i9xZEwuGURyW","PSrStmeF2IOv":"uA8fs3yhofeU","FAHz29FCMa14":"Lk0mYPgJsOyG","Vd6vFmBkhs9i":"lB1P6xMT57ID","ScBl7OUP9PrB":"KGy7a3JXdhjU","TrypjfHbHyrm":"xKfoMUsCqdrl","HY2bYTu5nOjv":"oNSYfkhfPnsQ","ZzykfeHbJAgz":"gC3bJbcPRDLK","isfOJmQEAI35":"zFPYZaVFAXCe","W9NSMsA42Uhr":"r69oYyIILAJx","Ik0aY0LlkhDb":"Ulb8iLkEm0od","KBmI3qPASS7c":"iWNSs52mkXDs","BKLhEusc0LlO":"90tMYm2wj21p","V2pXrVpwwNed":"I2ykNnHDFL4m","1wOjcsiB7n8T":"KQhZAzU8eYus","BISZOTwLuygW":"gURL07V6SkzR","hRZO8XccALTN":"w7ngoK645XMh","qz0WChXHaSiF":"9OJP4zeUFBY6","4Y3THZBCI5Fz":"yiTIoBPoXOA7","92Fzyd3FIwaY":"gTE7VaMqNg0F","3MU9m0XyNj73":"BZb7YZJP8VZc","FBIEsDXSLRtU":"uI3LI3tBuWI8","Xa4eNaIN3Zye":"TrePc4IR4jZF","NSjUZaeRVjAo":"rhA5CnRxvBmJ","1saxNStLz4wG":"st0LcZiOFeoj","ZgB1ewVASTQp":"3jHxLVLwN6o6","Yr1FIgnvSwav":"jfuItsCcHmpD","48ef7iNpcDoO":"j7b1L2Y7GtPO","hMBswA1sRxVk":"wn5l4tj6a7Wu","2vI2hW33RBLt":"XEAnqPNNI9lr","jhDWpqYq2SdH":"eaBsr94FCfSh","vqbxfZtgizog":"2sEl5tEcD8DS","deujQb9x8Ar4":"k7zJbF7CDXLC","1FTCKpukJW8C":"QRfkJkosPLJa","3LGE6jvRI6EH":"wHkpO3yvvXK8","OEABNEQIPEqv":"DIncBE653mRI","Xbldcp7ITGUF":"wG4asNUuyx8S","gS7zSn1iqdIx":"eKpyxCzUJZ42","v1WsCMCiYKs8":"sXrxQtvVFVdZ","0moFP7bqaasQ":"LbkHcO8khmDI","zzlnbKey50zh":"ci1AfGXVQ5Qq","uaTK1u80sV5R":"sl4HgElUukrz","JiZdGW95M6ww":"5gQgB4rQs4Oz","sOqJ4BhDbAku":"Z7IatDbupZML","SY1K3ye3uAK0":"EL4l2rM1yC3a","RxQ9IJtae3pw":"9503hULHJeHL","DEauhhFZwIwE":"TJnwFo9EOcxB","twyqVWNqcc4y":"YOpmVqCGnR09","33P6aqK0icIJ":"S8Qg4Inv0cx1","KPET6ciFvO0U":"VI8vsxpOxeZS","i2QOkKvJ7ZUE":"bMwaPy4PzbZ8","H4qOidisKONg":"gi6NKUDRsbMT","Pu7hpscfO4a8":"vBmHzQ1Zcd5g","5GSOe74sPCoQ":"sBTsJbchZMpU","3s0SjgMgKeZ6":"keHoxbDr2RMh","DWkgFPYyJ9Qt":"yPxKtvwuarYC","uAv59kQcBPy4":"IiJKeazuUkTn","u9805uRsaDHb":"FE4MByHjoMU1","siFX3X1M1HsB":"WKUR6aIpCIDk","t63Se6P5Mvws":"wzIblNZ6QcIa","AFwL5C3gLT6D":"ttl5CTGmx5Jw","HvcI9aFvxTW6":"XvXd8Lk4GZie","EoSr5mxUyYOy":"HwfEGpPis14l","qDY2YrV4GWyh":"A8ZzDKiZy3hQ","KeFC1k126gNz":"YM89GThkml9a","gfWUGAU8jree":"xuWZ3qPwj2go","UIBPE3t83P9q":"k18SNterlsf2","LviZMuGhE5hQ":"cEo6q1YwkgMA","9fHJGEr30anP":"oyKiSN1VhTBL","rlmG11TLSFVS":"dYzTDq7AGULU","sanM68VtW0Ib":"IPctiMdmgzDb","rEJtUWtBzJ0v":"QWOfcadr4WkG","eFjl6XtxOvcD":"XnC2zwIAbwWj","CSAOsd9TegXg":"MvnRTqXfRBRY","4qESq1kbJR7U":"Hhe1aoKeHJ7m","kBWVDuxs3QsW":"bZr1h2Et6EHW","RHOu1G2bf19X":"BQujAVJoL9xw","xtheSecwnpsM":"wUVx1n00r0Ca","NKN21eSbP4Qs":"Qb3RPX2MbeIZ","IomyRLkOxj8x":"R25sMlf0odjh","WiUlVLEakNO4":"FTcLFvLBo5Uv","vV3OTuiRq9Jl":"fcMDYfvbDk3E","Hd4G7qnE1rPR":"8YKDfznNtF5g","5g0ZV3yEV3Uz":"dBznGR0vsHVc","tj9fK86Qu4wH":"CdThlNPgdTNT","nSrlbKbXiPOs":"zkE2R4ArCyj3","PUI6IHuuPXcP":"GUiDdnZ7TiaF","VCDQlOgmHfmH":"yqILyrfxCyZ1","VdXwbuJ65wZr":"GBQByat99k86","StA5KnGczYHx":"30XBqhGDWaBv","6s0WLZc7kkLR":"9m8TwOs0xutr","FCgSQ6Qdehss":"EqKZdm7SJrqC","baICXow4gcff":"4NO88bbamW6m","Eu6EDYc2MIf2":"486WlABzpDXq","rlZB0Ac8Draw":"vn8UNKYIFqfs","7hVbXsVQWIHO":"km0UNCuT10bn","pLuosq9GDevX":"PZQyLYehX93k","7AbjQ8ruqXtA":"JCi3CCiCXcwR","pbU3mBvhoyD4":"YqkjVkWa3ARZ","MkfpPzDKjjvH":"kU2Eb5Rm9NAb","4CGmuJr933pQ":"Qi05X9Q5m3ug","zoQtn8JmbRo9":"Uh3iX8LoBkij","g3SVIkPUQ7ba":"3RoXv3oxn8Q5","rpsYkAFckeFr":"lNzp5hlSBSBz","APLEIKLEBa8P":"3d7cTGBoAVXp","1oSfVWFjtHgN":"t1voppAmoicb","Jg54m4nbhGhU":"2G9CTHwK5QoV","LMSHN2WIDq6F":"SlSqaO8M9si5","SPiRiMABy9cz":"DQFE1kfeIh3j","26BAu6CAxPfS":"6qjWvsDRmFYp","FET9jQnATcrR":"TatqoqRKAYI2","b8eL1aVsoqyX":"Rw5Atj5qapzl","v8Rmh0MfpuSH":"ooFMFkmyzV8s","qyViuFyXa8VB":"Sti5tNdTyRwA","OhF5TDYwKAAb":"aeaRDZWccoEf","eNqYAvkBY2UW":"893OkuawLLns","0uWd9TScmRWm":"gTZZOWCWmkcS","eRzroolnob9f":"atDdlZOPIjdY","p8URiJsm7bIl":"9fU2Pg1ggqUr","5wc9GZ7aUGLi":"4R7Nrdn1gkyz","EJuaSDPsfXex":"4MXMtwggkDzK","M9eAY9dg3JWe":"ZxZlcJybCNNJ","H2tTVeaDWTDK":"qZprBgAwX8ff","clD2hJ3616KA":"AKOM1qvRlhsW","z5IqgbNMcsDD":"3GztIBz9anbl","UDhu8izucVQL":"7ydpXgChHT8q","kAx84Ru0kCIJ":"O0qVmQQafsvW","hEt8fur1im4D":"pQ0Zvaz4KicE","6Yh0SnwbkBPp":"hVNuo1OjtTtz","V7sx1YeXCphh":"HAFgfaPQyqDK","gTaJrLDAioyI":"z8BlkPyFvcCX","p3oBVrCYzwty":"e57LpaF7Ynvw","Egz3QVAH89Yp":"euPlzdJCbwVz","cPK00W2X6txb":"5LnJnhTEp31u","x9BDnPQk40II":"5t797c2aCteZ","ltwdabFshdLg":"E520EDr3qXQB","yJHVLZQ3GpPS":"VXMnXAotXyxS","wKklGDH4OdVb":"OoHNA02ndCps","AVR5FQIojGgc":"IQAvAfrDT9Fh","xs6Tfju9nX4K":"9gZqh6PDgluS","GjXK5Yh2YztE":"cOLeefH4OT70","HGDRX6ngIEhZ":"Z6QMblN89nJQ","uO584qHYIZ02":"Tp8ys1Wzxw3G","sfyBuh7t0TLq":"YSNjnzcRSUlK","Xw6Zq1o1U62U":"HCEQ2D7lINvS","bpoU5YbXVobB":"2B7TjYHq9vCD","G1GoRMeXrnLS":"QcmC3kvwHyNQ","vIvC6C6EMZPy":"hTt5vsXD2tPR","vUl3yiC9UAf8":"qH4tLG59SYUx","T97BKOEHWT8f":"mxmu7YUzmu7B","ZH6RZkJcyTYO":"aQHt1930Dmhx","ATxBArEaBHHz":"B2vgusbPByUN","GYHQt5oPqoAP":"HJPN4YpcSZQv","entFjnaWOX6d":"nsN4v8uWPERR","BjoqZFH9cEXo":"Y7z1upskC9OD","obdp6wztWt9h":"wDdHPOHSpZbT","dsxwcRheeULg":"LPdswvxIpfO0","tbpF9qmuYQ9U":"oGVGZH3mSmRx","azQrYUbRuPiY":"WzHht2zmfj1P","ca6GqQcpEoQ0":"lEhWDVl7YLfH","S4wv65SLImzL":"5DuTTNY7dbQK","yOvWuJI6m5Lb":"wMpSAWrv6d3A","GJYsSLCJfDD2":"rWwI9dYkhMOo","27nAy1xqnu5z":"rBUb8xo9Xatv","UxC0u6pRlO08":"83nDhmHN3TuX","RsCRfTrkT4a6":"r5VHtOmuMxXV","zYouugt40K35":"KhRgNAHFFYe1","9HUdnF2n2RGK":"QNW4UvGTskKt","LD6Txj27QRN4":"JhG2xP7w8m2E","GRvJn6Ll8Huu":"AGb8wpfSIE28","ZkkPEobYUceq":"8L68ERmbotkM","EW3kqbd8yw4r":"5idxtjkkxef4","q4ch1V9IUJyW":"Ky4KuMnkSRWV","zcIJJAQCszsQ":"a8H0rGm3TZ5C","ugGjSJIiUzYY":"qXuWEpN2cFwP","tAOS0z5FQLjo":"5sMhViBRDmNR","EuHa5tKREOG0":"1RRoVg3qYuGd","JLEY4XYSu5bb":"NaTaqybOkAzp","iQAdIqofaVFc":"y7m5xhUKNJvi","QoFGRSThA1fY":"ahXrYabyyc3C","YX1iyM9d3PIj":"Iun9QxJARS3e","3oUn1iOtvcME":"hpcH4Pf6dWcL","kOiDDuUJrqDY":"pgFX7IwtdKr7","TzkgSei9yq12":"1dwpZKhy2XH1","Q1NbVloU0WsR":"wXk9DSMFzlG0","4Jmk8xxUbXse":"E81aUTyEKnIV","ezv52hoS3Kkx":"1DvXMI8H6PvH","cZF1rx7k4Orw":"1SEkJOnmDlZy","hHiBZF5qgz2C":"0esNA0zuwhsO","I7hqTDBDSxNQ":"e7eeGz1CwVS1","am6ZFd68qmNW":"BwM3Zxv7Rra9","mQX8i5DwroDb":"rFHaTZdA3xA4","9RBwikJIPTrV":"hvfyqe3H8OHT","7mJ28R2VrAxd":"yKFFNuNfo0IO","eFCmeJE2Ji1V":"sQuz7PjZKWEv","u6UOr3XKXmxj":"bMw750OemTzW","uuc2e2Ut6CUP":"eEakXc2Blv0P","mxEKafaRUaxH":"A7tQ7QxGLtq7","cAbBOl6oIkdR":"ljOhqh1BI9QF","LuVQuRNSonO4":"zwZFUtsVoLi2","3iu49FWYKHsW":"5ueFaXP4GUCM","CWgs8qdlRxFA":"X1VUXgBsrkzu","kXVTvSrgMr8F":"nxCVvVkGni8m","yS8VbANjPf3n":"S2zN8oXvkIul","wtUyUVYUqfUC":"RZ4GXUbPpcRs","fTiXdebpKsNa":"z6djPPMVmOk0","KSpfE7Nas2lP":"34Xg8vjO6CKv","zrbgBgZZF2k4":"Bg9ZFksu5Asn","o6VWd98y0Abn":"zkPYYYw1LN1U","MtF3m7NB5VAx":"bFC8UQI8NfZK","Fq8G0BU0WeuW":"fMxFMKDdvi6F","JHuAb4toqf5Z":"TfXu1RNeWgzz","NQSJQrK0EKxF":"Uo8yH0lC8ZhB","dgxZo7qIw72j":"16PG7FbzWg2O","YQ24gWw6wrs1":"jeSOm47NRTXT","CA1C7dSd8v4b":"Ke4PMFmJmIGy","K7hoQH2dTvbm":"ez63SvfYuJVj","JbsQlHCgbdCR":"syaD16MOB5Qm","ewJy0pTKYjgG":"yAG7FNgujsKX","jRC32BjiQTi3":"nt22qw5tVnhV","uQLi4Df5DA0H":"FdN9ROLPjk1J","8QNy93YfI4Je":"uiyO5AbSXYyU","WyxWfEmd94dT":"rYvAeFMfY5ut","afTaaAiKHcq3":"qPxKtCGlwcs5","65YAcRdOYpKs":"dxley7fsR0MM","NeQTBEBTxaKV":"rvTtgrMPiuJL","DeTJWIU3QeUw":"CJO57KWlsdyh","37JsrKK5XZkT":"6EQNJqiB8JkL","QOdOYWclNAZF":"ib3ZWhRxIz2t","Pa6bYMKNi2sE":"2czkUqcGz2TK","5U9ifgOKUulx":"hW5duZHcFCvw","x7jqXcEPIqeE":"Pm3GsMtBnbnj","kPegvZo7Yqap":"8YPeLx4fKRtu","D9n1pNlqU30P":"50fNGapq66Y5","rzWhZjxs4ra5":"urQiaEQ8bvIQ","hzWqh2vuIagJ":"dKFv1P7lBbiT","iIJ5CHR9CnvF":"eDILJOuhtife","zAqn9m78LmAK":"kVGobP7xLKKM","ybbUn72oHwzw":"dT7gwd8Lolne","qPXigMwqcBkH":"yPmWJYZloUJ7","XZUCzbKP73Sj":"TY383agsYzvp","1Dsp1lKV8qd5":"iM2gCoC7B3om","ufUtG3Am5R7b":"TTm4Os41KVuR","oZYFBllgNg5S":"9AimXVRtfUOF","c0DMqCch5BUm":"gwrfXGZw1401","e1u92aHOxuOS":"NTFz9Hcavq8Q","uWDdUzgMZOwA":"ESzEMjlXjXV4","uFHUPU4y7flI":"fMrwt1buxrft","gzKLWhX2kNBn":"mDgnpVPRuG07","S0lBNt2Y3GpG":"8ydBBznzErGD","jfiJ0rMbB4U9":"iC6C6d8ANLE8","MrfHbnLPWzGQ":"vcuoUDbQMIZv","Iz9zX7s5xYWB":"cfgMC386knYX","CfLVXPx194HH":"1G9TDCggnjos","D4cVThRNt8vK":"Bju1qFbkdNEb","ht845kRBVZoy":"VMj00W7G1feO","qTrQYMQ6QZWI":"yMqZgb8H9Gpo","b0nknApaXpyf":"veEqcpgvNEvb","0jBEFmZ9PQ63":"UWqVDjrsxWll","jC90ekT5osTi":"rezHW8XxxdxG","9x71wwhGJyGN":"w3QosjPhTA2a","0PneQrsLP6Tg":"t0tb1cCHC5IW","yKW5XMsCE98o":"fjFup4iLaMbf","jaEcYQDea7Ep":"idybwgo1ErCe","cQOauoHQzQeD":"6WuQ1VeDf2Ko","tMp19gFERaWi":"PEyJu2pm45O8","QIZiX4vh48ne":"vzr13LBs6gn8","Dg14WPLrSJQM":"knLb8w55C9th","GdUs5hggx9ie":"uHxDVOx6Necp","l2c4KnlKrtDj":"fVB7YrKCa1jU","o4kEpcQDVBqF":"AlYsyBuVYgUQ","HxG4XeHFMTvR":"gi4o3C8SDHM0","i9kq2c2iQYdJ":"d1s8w3nDOQir","YJ5PMW2skSd6":"ogHrZ3GzTP04","xxzYuhuLa6vc":"DsCzJiURVjot","3ELqXanpXHMU":"lmhQVDK7BOjh","fyg1zdKmz7Zw":"s5XdB5Oflt1T","st2BzgI7fURh":"h1tWNO57oNdf","jwdTC3TGqAGR":"r74qhtS05rQP","GoPIGgb2IctE":"nufOoLhU9zzY","rhDyxwfZYG1H":"lZ2TH7n5aTex","69bq0za8PyDL":"4wX1BhxcUZ4w","id2HCeioRFjp":"NRWaHWXeafFr","LLNa9PzZHoQe":"gsv8mAnApI1U","Qnb12YjT9gEM":"uNanRXpKsnXB","VficjF5Md1Wr":"s5GtaWhf5PPJ","cJHRn4gEbaTI":"CSzLZKW6djNB","w8v3CweBlbME":"8y3hyqUlVWiC","6rTcUJe1Cttx":"qK8gWVr60Ctq","QxbuyWJrPvFa":"3mKm1vpEnPJl","6QTIAt1O6WUN":"sY6Pl2wzW2WG","REMmPulWqfuz":"dHjAvQ66OPOp","BvjQKIlZNLFU":"RiQvC3DMRaPZ","8olhK8vzhFGD":"FIwK10lOmJM8","7aeo9D6SMzr2":"05qSzv4wWLEv","5m103egDupol":"Y8TL4DTqXudQ","x2dJiNPHLUML":"L8OEI0Ao6BVN","p8IelyeqFpGF":"KEOFQQVHQXeR","MCgPfdUyO9cl":"ZcHAKv7HF6Xv","MzlJJ72EGLrx":"aNzHRbO097RK","6MpD4ZcBU5BO":"23nPurIuIG1A","kU10aUjTvbSH":"GSzC6LCijz8a","7lL0lvVe9sb8":"KoH2MlOaZSde","fLKnqLDvW8mb":"7gIHZF1keqQ1","8gsgLnEWepiR":"58asxJg1EY7w","jgvUEI6lVSHq":"63Qh3Ih2Kxcf","XYpKFdcX62xN":"KksZPnd3xw2O","twckTyIpSkg6":"iL4joLliiB7y","hM5mk1lSx1Hh":"PrfxaisFwQwa","l5Qan93rjhqS":"c0ziiQSBHgDD","v9e6euZ2EKKa":"ZVmTw6eRGpKd","fBP6jxOWAiBn":"HXtrlAJYFdth","UKwYLLPnhYxk":"3CnoVOdrB0RY","Ari8FtBgaXqN":"O3RqP7WbZIXh","Zc0vV4cHhjb9":"5Pun5AWInxi3","ITfa5Up06bgS":"wKDslSPBalxJ","dXor08fu25Da":"tyGuwlVQYNQe","JG1GNlA389wY":"eQWun6O8MPDl","GkNaCTYMfcr9":"BcpsHJY8WsGJ","az1NKEsTfhf4":"wWdfRsUMZHhE","rvLp5KVlwWBE":"rl9RsME7NWdJ","vfjBvJUbXmFP":"TYGZwk0WJfqw","xmksIpFarSn8":"E5bTvdTN4YLY","StKjztDNytGz":"T6GQaQIttsmj","QZ9ESHDySihG":"U2VhPbwUW5aW","nmd219MHp4MJ":"9gWXSFe5GAka","w0Un9RmVsIW7":"iMlTGKItWyol","dyMk87kRL5Us":"kR68ozCs7IRn","Y6baIH4mRH6r":"aocjjERy6gYq","B9aOqzM3aI7v":"6RuMyHyaytBl","PLxAJ4JpUkre":"lwAAeM6kYg5n","9gxjD9yC34dA":"wmORrOV0Aoq5","P6z5Kp7AjcBY":"07gJ4QiNhu3v","DTCP54ngGdR8":"aCPAhHwUJY6P","sXFKsxn4j1Eo":"SnMaaEi5PTXC","pHqkMDFhm6fk":"GEI3aFUk97dM","FVt5vt0CKwsI":"xAn5sjczvV6u","gEyZBIFvzjdt":"Wwb0sEnw5fvr","mYxas0suoXNJ":"GaxkzrvtTu1a","r4xxwXHenKAv":"HvknTW5TWreK","Z4fY36ARAqmM":"SOHOO8NRaXbT","5r06NCKNbcEx":"HNqNI7S7ZjSy","cCCrrYSXbC7n":"fKhUODL7LJaF","ZC3mUkSQ7vK4":"VZQzsS3Nui0o","TH0BVQELiVpz":"Px9laqgV8YSR","prnIgdK5eg2s":"8Nm2WboTsdZW","nCUYt2sH4Hcn":"djonst2V1eBT","Py8Ehma1pb2H":"Ix6XOJ0WdQS3","2dW1cYo5svv9":"NR2xxTxYs6iA","nlApCYGxRUBZ":"iIoZYRXXjuJ0","ZOnaqWvS9Qyf":"bpGyCNPjSrlH","wC1WWxS5QYR8":"zBwqBgLJRA8g","rqC6PuVJX2qM":"B1oazNNQLa2i","CbrbjHcsRKU5":"aoVl05yKftNr","z2GNwv4Bw8qx":"F9X2L6KyZKzb","hitRgBEITxxL":"mzmgSwyzrptN","xrV1OYeEDPTX":"O8faM5qQeD2f","TBSHpUByMOOt":"841XxkPhD3Vi","ZAJeLkw6kMC6":"gGUJDiO8qFu6","x8Uu592V6J5o":"GBHKLnp70AuQ","yBpwSdpfVvtw":"mtgOZvlaDiFM","D7jhlDGe256R":"9YCwFTXMnfxo","XOXDvIRPxNkW":"HKUbBIAkgfnb","D6iHP2euaRRj":"WpUt9ZiM9UAs","TxSpOTXdALAX":"DA3PFkk3Rg5F","YkwSlsb8z5VF":"KQZoVViIt57j","RwRjdJkqr6kx":"JG4z6CVHqfEQ","jbLFQIaon4bF":"6BXy3majlk6h","E0FpmkFpzNBB":"ZcDaLtMmx2hM","UCmFJdfca7HP":"PghaAqsyeY4q","bUB0lnODPZBz":"USa6zb0gjejh","ruKP0TgSAElI":"6uBmtYnQNmro","gK26zJfas37A":"7yG70lhJMlom","Ujsq4LAqes4T":"3vXdnUrRbCCd","piPhEkg7tRqL":"0qxZX3lMc6dM","WA7sAS23dEQ3":"MXdVgHEKSZEO","I3X1lWzHgBGv":"LZJpagHDnjst","9apScJQAQAbN":"vhq2CLDa75X0","Ul96sz2x2siw":"nQwk3glv1Bua","szpnBkPgZHed":"fSt0ND7nAL5p","ypO2v6ALOkkl":"pg6XgqRrJ3ec","uWDWs7W2d15V":"DSjYQ8NWYpbY","j4dwdU5MQnvh":"bnVQ5oyQWfIO","LXxZaG3z6CTo":"0Pm4uZn6587I","Vga1T6qPGe24":"8vCo8EAHET6b","gtzLhBlz52mv":"IfTVitLcAfWU","GtxSztzcaYWu":"H7aGLBuZe89y","fdkRBj9vP47U":"0dMXZkrX5HLN","foD28VIyBmbz":"tfgt91hA8uMj","ej1PXMyNGnt5":"o0j78xDGHeIL","l4Jy77nECFbF":"RTfLZUhhfBzB","U0f9bVwrinJy":"rSb0INxxRLEq","HKrkc6Wgw68m":"6B9Hp4olhyJF","Wmcm3dc6rkLy":"RcNu1U5wtwN1","80vRfw2YEsGU":"o5MMV0FQkfR4","nY1LDLV6dkKt":"SCM9fMLdfVss","2TIS7IjGS6zi":"kIFUr8matNsa","IQhzlXKEIhKh":"6GpLtGReO83Z","edBHAfEIGoKn":"0yRQHn2I0tk9","vVVDignAPdlG":"RZOxcnqKzBB0","Tw5KqeAb2uY6":"QrtWQjJUVMVw","PSyRpBmnGiFc":"AP3R2FqJJuAW","cpJVmP2krCYA":"rNxrE4Dyzp6p","vp609p8bcKHa":"OzeaZ8B1klHA","8StfEJ5WeDBM":"0M5gzaiQbVJE","lqOK7FPigIME":"8wbR93Xyg1gF","R47GJ9Z7ubWS":"6Nb5tEaqBpsi","87vwawlwl0Ld":"leQmhDlSbLrc","Hps41aNuhLUH":"S3YxZC0UiRb9","PZECaV0DbQqj":"TatOSOAENHKP","SoncopEJZxq7":"9Or52pDqJiV0","jstRe9MAuXF5":"FaucnGjsx4L5","PgUJn2KHjabm":"ESXXjV7MJFWo","hJ1zhpAG1n8k":"cI68DmzvDfja","HWvu70pM1OwW":"CZHkcq739X8T","4fsUvUCvuqq8":"955Zbxcov9gD","yCqX83JoT2vG":"3PRIjqMXrDvZ","aJkueJGGddqm":"79vR8U2lMnxT","YGc6XmFSbfgX":"Ae5aa18Qa1UT","XcymUi5cri02":"nUuD38gdQ1PM","FrqAENpB9vt3":"mb02KTjoSjV6","p0MDDQnxEuYo":"MMBYrFg82AF9","f3R8Z0If9PZs":"Dv6Nz1pfThHj","hGS7xYBNzvBo":"8pbS61T7DZXm","6im08bPrPKeR":"XVmQ54HvCzOO","CJTD6IJmCMZE":"Imcu3B9alyvO","9Jvpj4CkETgu":"fy95Lnhh128G","GhQb1pSU4BNl":"A6WorPuXl3YL","1RbfwyluIfxu":"P6pHqynToYVt","a6KQ91xQqMxK":"F8k8poKqgS5m","89SrumNmPKc6":"MLGlbN5dLnT1","48PxqPEREQhK":"BB9l6K7WI2M1","iazNIXp0F9X3":"1AgEilPDfJE2","zUFJYxwrXdg8":"41x7sO5XTXuz","48D4f3QNsCHp":"eSAtD4m4iR2T","Am53XBJ4CYIk":"7Gms8Aj2jlD9","t940GdbvJtn0":"yry9ZzZUfLNo","12yX9z0zaJxB":"fkypvVIFGU3G","itH3dprYx0yQ":"mspRqi0QtD02","gf0hokZz557W":"iXA43yoXbP6I","XPKgEPncsCzc":"ifvYH0JBCCtj","Ephlyy9KplQs":"lBDegXAAjIPP","7QNsKGfr76rI":"Vy1B3VPFy2xG","IKy0iRga2oBs":"l2t3B03x7faJ","7TZV2KaJvoOR":"t9TcCkm1qtXu","DwUt7gN4v5u5":"eGgrH2XteXH2","MFwEFY2lUXka":"Tmb3SrATaHrs","zdpyGqM2ja2K":"pBrVY2MNDoOu","sOBJ3cW2mFP8":"OLynVqYyyInI","WtjZgd1UNSq5":"0gm7Qq4EhdSC","iQfENmL02HIN":"qxcg1598Bi0m","Kghnb5coDzS2":"TYzWNnIRaVL9","EB7FCHRlimyK":"7mGlLVrT1vSp","T4JJ2OgaXmZH":"HKTZohYhrKww","cWc6Psl345ED":"H0OpvoYBRQmZ","ed7PRHTuNgoI":"XVVjDkSBHPgH","BVBc6GcshYLD":"m7ICd6P7vEu1","vnZoh8CrggL9":"aRWYH0MsMFS8","lPzDBxyThYSU":"UIayVlq9rEAA","WpC9uy2lHMmz":"OmCCWLU5t2t4","6ESC3XrAkfi2":"AddYJ7s9eiF9","E4Zf8NBmK4VN":"iuKSpUMP2axV","BhOKIfsVKF5I":"AqJLS5anGv8f","vvaIJzG2k8vC":"gBhuJUOgFQdX","EtOi34M1H3lq":"syqGR19Hzmq8","CMVWHwRWkpSQ":"jDO9BU6AOWUB","bb5X4F2Vb7TM":"lRG0WyhMsVIE","3tctzmcNc5Ds":"ZVurwFwoFiR0","8VSAAzJyzRn3":"mUbRJnSu8sol","vduyDIVLy3h8":"gzQsvDArgkaI","KAOQKO0x4Dex":"7EAsrpRDtKPU","qb1jfz3SYC9Y":"THdKfPbevpt7","HzIr5v4Yla1r":"QS1kqVkaaoo4","l3HfQLtN4MMX":"W128oxitbm5e","UQj6oNC3UQzK":"6FyldgnuCGDe","X7lt2Ny6hIfv":"NJMswbdWqcGV","cgnzDAJNw2PV":"sQ64QR5rJqHD","pbJ7zy0gWsHw":"m23Pxn30sFiH","mbcpFXCNdidI":"ncR5FeGs1Zsl","HZlOa7e1YIni":"FZOztOiuczt0","8u7JtZAQ2Qlf":"VWwC7akqSbrb","ZPgZrilBqMQM":"vNhePDGIw88r","0WpHbw5ExL0N":"DIAoDm8kvc8W","KubtsSJg9sNY":"trBo4nOAZaZG","o51SfPYxR8cW":"XAa2fFsUM8Jv","4QETwsEcpCqE":"AlXvmzbt36lf","1flU972o8SwK":"ComACbiGneny","xvOkrUFuhFhO":"ErymSwjXthIp","0PHPPy8bdNH0":"nDcdgu9PHrv6","XNcwvxJyzux0":"8g0E0z7qOoU0","hwsDayETB4ku":"ruAVLqsE55IQ","ZA6n1hvKIrMk":"wvt6aWguvQwo","9lwyFtnd0nTi":"JGq8OapkjMDO","kbHWnbttuA06":"59vhK3cbDe8j","1i6PX6TlEhyf":"6eQYuElw00nZ","a2jLF2UM6TPC":"K1WZ6uIOLu8P","Kdxv7X9dS9GB":"YQTJ8gtPIu54","UFJp94Ay432W":"A5m6RegIG2I4","rRijWDBkuREJ":"L4uDBSTDhWrO","UidX3awodU7o":"8ESMdwInArnE","5unQhAF60wRR":"BbLt3ARsskCF","wYKO3jIg7cEl":"vJa05FpCtHNE","nBQRNOeD3X6q":"GEO0UDoA8AZf","N33vkzTqOOla":"squ83hm5rrWD","fhhc75zNgwKQ":"tcK1WwQlpGYN","Sfcao5Y4aVoZ":"odrhsj2GJ5qf","l9GXpaSxo4dd":"bz7Vjhe1V5hV","zzTD1TqNuP7w":"4bxUpDYrDUcu","uLg2E1WdMhG6":"jkY5nYQsczSC","gfGWCbWRnE2B":"8Fc8Y4RPvOub","OxB0QdgcAEEg":"YbHQfar2trc8","QbPnKb4S5wSQ":"UVLlUmWI8XtI","TrA0xy7yje6y":"90UtTc81bs8o","LHaZvZdERLKj":"XANGKpHEaPfe","O4GF5uaJtGUD":"xC0P0uvTio4F","DOj8iqOHYJRY":"MR19tv0jJLkx","7b7WAlpfEzyN":"XBvlCOTxt3AW","00Bpl1vdsY9J":"RxTsQWGYN2Rv","PB7k5ld9texH":"mzjvuPvcM70z","l8q4l472aJsW":"CwQxxfqilekg","fnVq4i2u26UX":"nM665WwtMx05","C2nkz5TEQLZt":"jU5kBFmeOMbf","DKPaaahmMwee":"tdRYa3buiDk0","xVydYqM2gfA5":"lhylK2d2rSMy","uuuoeV4ixabj":"qtnAod0kdAAc","92J4rcUrjKrw":"cOqcJjIBcvYk","X11pr7C1wyc3":"4xVlkDqggag4","NLRoRhl91eYo":"bXhByilYszqC","VCbEuAUG1Ii4":"DjC5agf668CI","RzefSP1MNk2M":"AtGMzKGA2Srv","UEieMpRwYJuX":"J17E3KzNuitL","mrEmHuVOn4BZ":"SplRLoDMO4ke","fMLxpah3iQ4u":"OIPBlJ4PL6Fx","Qv8YuXClOLZX":"beqtlkhc6GEt","gzkJ9kKsuh6y":"Xt8Yby1hVQX8","0stolvtBUblZ":"OLDhRVRhyb4f","roFNzai76dn4":"2lYSjTIJFpBG","K90QVlHCHpGY":"SqsYSqYtudf7","TwTJW5rrvRUj":"9Z4cnYOsTNxU","OnwzyNWEDmu9":"KPMvz8pKks1A","YsiH6K4b51Of":"jrxHQZCng3pv","aHGQ1tk450bx":"AnbcixHUZgry","7AZACqhNaYm2":"03CbTBEHcTeS","aaKe0V71mG5x":"jTYH1J3RkCRI","NOVhBSeS7NNp":"VDJS74VSQAT5","Ot0udP7OhkIj":"SE3itePCop2T","kWn3bhXbCJxu":"jcHWIA5ZSIT9","0yOV4F3cG6fm":"7ceYejpVcKax","DuJDFUEJidbW":"pRA9yd4nZVeQ","fMP9LBJr30eu":"V2Pp761vBor7","TYUyeRRZhUq6":"vZCFye2A8vd3","iy6qWELBQmY1":"zkSnOwNKBu6r","IM6my6ug4LaP":"rqRQ42k3qc7p","fOXUSLq2ix5a":"Tf6kHxYEcLgR","Qlpv1HLBXGSx":"cvNhM3BzeGQR","wC6la6x4b15a":"0obYeUNE4eDb","laBmjzaHo6Tu":"dFHIWtNjTVp3","T0epYst24Zc7":"yJMuapdLQvaJ","m24YkxjhXrah":"57ajh3wLdD8U","Sik2aoa9otxX":"d9BcWoperNsM","9EkgQcWQO0k9":"E8oMNGWpMs2m","5HmrRyHt0peK":"9EsSAvYiR1Af","UxaMgg4uAe16":"KbBbx4tNSk1E","DH9efglU9B4r":"IAB5nZ6ZvXn5","pjpVpSh7vfAH":"qE4DbhrcuwJ9","PQ264UwMmwXa":"4RFiYPy9mxng","8lL9JjUS861w":"tNfbNbwX1iv0","7cEgngshjcMa":"4rehLWq718sI","mXDxYwcRzpFN":"eLm3tUsCYgpD","cWkYqRURMxZL":"faEqwTG4WIcc","usBhVWU4DFiu":"l2qBBrbOonW7","aPHScBAljoWf":"luDQIwSNqWgg","Qz1LTNkChHpU":"NnapJYPyCSQP","okgA8HH1f2tD":"0eK0H8bIkaHc","9JctyqUeWwOU":"ejOoqcFxb1oc","TuZ4yWfvjoNx":"2erNBP9Fz3rQ","Ma1XCla0imbD":"q7IxdIGLsvXx","6pCjlcu05QIe":"mGbCGWgnNIn3","Kb9082zQOp2l":"qWocGcmSLfa6","kETDN63b4pCi":"Y0Mpbo2ZMTaz","9zBSH2oRaFqs":"mGVSJsOXBsIC","Jy8Z0HCdqX1G":"ZWf9RypVAFXZ","PS3ve8b2m9FG":"QH5a5FDh0VAk","zVRjV7EMboj4":"uD8GFnonB5tD","13UqxK4malG0":"ubS8a1k9dRPG","6fYSNlWXTcLT":"KTMUFffAdxcw","ROHjIj3qYJed":"Q4eDws8ErM3c","teuQ7xp9wcnm":"UYXCd8m0hS45","boXqSYulFNPR":"g2QibTSJSFM7","7JgWaP6kjUr6":"1DpWXWWABnJt","nR9hyNI5D27O":"cEOHu8kkDLLq","ElqInGKJxStp":"5ZZ8UU8jtAjo","Od8EP3ZfILJR":"GM5Yz3Qg5i1k","mTxFGoSLurTL":"T9ePt8pkTbHp","z5kGHuWQtsOo":"xXZ2uY50yPtq","A8B7rx14Bchg":"tmdiFpqkSKBH","h9jcNphcK04T":"49sD9wHp5Dvt","6pzoQsF9eD75":"Isdoqzm5OzNM","V2zZTbKtcWIW":"vHkMeMvtErYC","E14FO4UfCkZT":"6Gke5kRlYJQe","mqmOd2L9pADK":"fSOx8qRwwlbZ","h1otobo0nczs":"EcDisiZz5E2U","xbmF5elvTFdi":"qA9EfIOJ2z8W","EOoFgSjnsJxZ":"KiA6pn05LReI","jQXLinpotHZ9":"j1YXK5PZMVp2","ISQuUy2V2MvK":"Vppje42QEizb","Y28P5tdwTXWr":"CKzIyTQiR9VT","L97ZwTJ6egQP":"yXN5ulbGDEC0","wC7uDoQywHUV":"2381UIq7VUQy","mpJsJwoEievj":"k2lSvLcWjX4P","rnlWv5L3fJDE":"Dk24rvQtpdLh","lAuDzM3qWoju":"hxkm4lhbS6qo","cEp1nJLhIH94":"IXy0McwVvw68","kTctmWFRja5r":"38lEcRnQAEUC","gTlGBHbNPIrw":"loDT0hPF53xb","xDwuBtoNW5hK":"qpSAnm1rv3sk","J5x7zRsu6ipu":"N8loAIzq667Q","xHeoauoqyQ53":"ntpCqg6M9K1Z","32kzIp0B8h65":"mPmtyP30AHSf","H3RSyf5sLtjJ":"onriL33LUHR4","oH88mhxgQSG8":"fqWjiTzextPm","1zKt3yp9YnEo":"9aXBqxJWwBFg","9YPTQq3DXOaD":"Ipmf8yko9jqm","52XySOU4jXeV":"tHyVrRMWFlH1","CV7rTHWkUVeO":"cFxHrwj1ir5g","ObEMCJIRpeNq":"fJV1a68EKUCh","n1JwrjcGPoTs":"mfa0rNXuggpa","N48mgzg1btdP":"EfMJisyTKcBp","XVgv8pdHg7es":"EuJE7ZrryGzw","RzRJe3r9i9HB":"7CLuzpe7DW8u","tIbikO5gEmoH":"J6eTeYrQzwWc","XC6CiEKbwDGP":"mCABCqEDwXXF","tTjcmXwMLuqP":"3H0Mnh3aBobc","zZSjPKzPyhiX":"qjS7EOBCp0n9","CxGlBdTPQ9dU":"xaoR6BLn13Y9","LSonbbjKWCeV":"V59BslrOgLVL","zWwDmnSgdlTJ":"3PLxQk6pwHUh","v2yPFHM8yMgA":"PQZs5ZU8Y718","xh5NWYyPx1C9":"B5dfH13uvwiP","rm9wObBUeq2v":"FuV3DSkEVSO8","dX7Kplc0SB65":"YB9IaDpcsuso","KGh2IMxsGnAw":"ojJw7mny180d","mQLOZJjtZdzp":"M4YzM8R93fwZ","ktYog6pp9jyn":"EnRfAaNnrMFV","WLJtiAx4uI5K":"iECOQLb9ZP26","UFZMz1EjybFk":"Hc8QTNUDFLu3","rAjeRPMG4PzS":"xN7qWRtrEc55","h8sJzbG2ZhXg":"ZyO5fyRtkuPm","exjd48cwwTFW":"Xis4iRsONJhE","fCUvnvA1DMkn":"NseppU728eUD","V48kAONjn3JA":"Mzq6L7bewMIF","YsTgHPJE3dZL":"U65I5SBfeMkp","ZhZicluca1ZX":"UzkJu6wLZlQ5","GE5bMaaltAU9":"zqoVd6ZMG5t5","G0WXnMLaju4V":"RS0nlf1ODIS9","WoFpkVI6IQ2Y":"Xsn0ppGZ5Yjj","Bd4wueikiRB7":"Ch5ELk5d04kQ","OWB90zANy0ei":"1y4vR9luogge","kILOzF4kZOOK":"MVYCzQaYWSbS","mGFbjQ1MMCxi":"AzGLsy96bNL9","nYxCJaScg0jm":"lhekb2eVsZvi","scbkklZlH2BL":"x3RqNlauE6kP","EAutLPz7jRHu":"mFWgxDlbZakx","PI6eVM6ejv4R":"Kuiq5irfxjlH","gtkkVb6WaEGA":"fWecQbTGWPiK","D5Ksu1U0A6CJ":"XsrDu03fctsb","KoVUA77MwH58":"rLUq3d2Vce3i","2qMd1SRa1pQi":"Z237Oj34rctH","DPXY4KgOL7Iw":"vFbbR9ZNVtrL","X1R9F45BPgO7":"VdfT9B1SAEkF","TFz4vZykC3Gi":"zajgb0AFiUpW","wc4NE8GhTLar":"GvOosonvxy3W","WPt59kBppdLn":"3U6sdE861E1H","6hFKHs0vUWoL":"MASo59YS14Bv","kjzYcdxp4jHB":"fxGNg6rY2QVe","zEVJpKAt8FNP":"M9Cn3KhCtp6z","i6p3KlACdteR":"zAHVMya2GVq0","XOw40iYfvd6x":"cKluxU9xnFlB","PGskpOv5WsNl":"8JkxOtDjMmdi","YhuGwZSu90zM":"KFgkmSfBdYz1","LuPcUlV1HVmh":"m1PLHjx7ICFY","LN7yFUhBjt77":"wzPJTlyT3sHH","vikXycYCIqkb":"cn8j0vyXd4KD","tpPrnMT55MJn":"lGkkr3pLFXvZ","by8jTfLBTjP4":"MSSjlZtkVDqy","PeuPdFneV9En":"29tBfs6JWZ8M","6JuCH8Z10aN7":"XVkVLRpVf8Es","YLw8T5apQqAJ":"4ZQ9cDSD76IP","GJnOoVG77nbS":"4Q6q5aP1NdQ4","JYgMIuieiYYe":"59GHwQNqqtx8","iiwjIhxjZqRg":"UYOiVW4DQxja","UIrUKN84LcDc":"A5hGPuJHmNHz","jTW0wQoB3M2z":"9TfJdSQ7ySKy","gf4NeGWzcL9m":"QJQTOoABs3qZ","iNckUpmxyKJ3":"uU01zfW9p1Qo","kL9q3bxuQwUf":"cG2IymiQnPAL","cMUWhJu17YoK":"j17KiZGPlSiD","B3TIKDWA3CxF":"M2FEMKVQ3ANd","phUZ6XNGllex":"vYttcuC4oaDz","4FKqh2R3lxbU":"OwMGn22SxQlT","VzBpLc9ZSgL3":"Bb73Rnyhd7zY","DLQb3RfTkxeJ":"Dr3luBla8Rh0","ilSF8gnpy111":"gGslVfAhVfkc","02oQN8kKUJBz":"TplOfhDfKv7x","PDMtJdpHquGj":"8KYU9ueY6oZQ","VvFuO1QfiUj3":"QXsf5isSBKkf","pIwWd5IBpknw":"vUPSvaPvDo5S","pJOlDiu4MNon":"cwwGOxLoDgpN","XpZX8OtuVGGC":"TRKoqkUS05HZ","jf9IlpFudSyU":"RYsjekkH9QLd","fjZgP8kYh8do":"hPR4Eermm9XW","3B2rq9SC7te7":"f2bmrAt7NqQi","YYtw4t5A60Us":"HvwYSmd2AABA","4PFwUcSvn074":"AQtp7K0VqHQf","XZY2t5591iuU":"6Vfvl8uflsA3","5NNZXk5Q36Fw":"5DuDX8AHyuSl","deVZvZMtSBud":"7FwnvzglL5nH","yPKfQwdLtUZL":"4DvwA7u0y17U","UjHMkmdTwPcV":"UvuIVokKID1p","RGsquGRhuCGz":"6evNZf3DBzMM","G4nc5XDXH1Bq":"6A0Z7z4h9KuL","dr3XDrh0WJE8":"RQRlqvN8CfGe","hG9Dvt2dnYIK":"e8cpT7vTkDi4","SPyr6dH4mM7U":"ateTJUYgtRGh","CurkXGXp9Rf4":"Kvn5gYQgNOsM","ztnW1v66snxP":"8IgxpZnjCTm3","NGEFaswIeBNB":"q63DQNdpC9T6","BZDEul8sjWD4":"5mOguGxeEouJ","J3N6mLcSNMPT":"PjrhetshCB3m","vq32UxzrMJIk":"5ytBIihdvhhK","R0TLYZpSlZOp":"1QOR73YA9uMd","XOzdb2Fv9iHJ":"Q3KZIfwiNZ96","b7ts9icSnPWn":"ihvXY2LH5cEV","X8r8CnuSfr1H":"EItt3WpJ4qNn","VbOBvuEJEpsH":"2V51j5jmVqlr","Iy0ZL8nOYbcq":"rqUMDnfIi5YX","FIQnAMyEIBdZ":"Aas5AX0QR2tw","sPjnx3QdUDHg":"ryr7dzIJy5b1","6lsAE2Kw65sq":"JsAjicuUnj4q","eOMOTs55tZs1":"n7g5YbsfnKyX","9Po6Wk28TatV":"A0SVsYmOG8fg","ZGPXYWElA50L":"WjjNTckgFY8I","n1FARoagOEB3":"Im4s6P72UyZQ","ZZZcKgYGB6mF":"ugV3byqjJegd","NMZEU4p50842":"vhI4wMl5V8X7","jmt7RSMVzPoE":"YqJazFDpnlrb","2fU49Ae8PLUJ":"D199iN2XoILf","VEgieatbaQJJ":"YR294gUUWExO","ksJJezb8TbgV":"37eKTO9lMYWg","8fiXjDxFyk5y":"u6LM7fb9zHvM","CrsK10T0hVqv":"HgfF5fkuA2zr","Gh2TZIDJEjig":"oLMb255uskxH","hqCKx3ZQYROb":"pTyvXrt1k6WQ","0xENplAQ886N":"FZWWposhAURr","LEgjx1uUK6MT":"ZATG5Ft87DOU","PsHKdrP4tXo1":"ZQ0cMCHbr2Ne","SznJpRUoBWRy":"Afut8mURwXqF","VtqbwqYkLUvV":"hz750BWD7oFU","tsSKyKEUEOpa":"spB09621oosZ","ihswaVP159vW":"Y6CFXeo3adQg","1ePUZw0E7ETh":"MBAFAgE7KOIJ","HEHTU7sQtrOL":"UZaQy3XohHgJ","vhzRnJUgS7ke":"aXFuvlQGXxFh","xgxrjr2UOuk7":"rrUTnINpfRDO","Ptx0qam4DgFT":"MH6cWSZu9sAM","xYwhEdtITeZ2":"jOMaNrGvLMmk","Rn8K7UPvS71D":"qYb1Y64WBa2C","t4f4PhnPXGpY":"8HcfjINrG0o0","kyVPdmXXWMPT":"QZoBTgGSUBU5","q684rwoBMHrI":"ZeulQMoFmE0B","ERh3PETdATdv":"iyUxZOkUPFnB","bJyIFaD8UYtk":"s4ava9jZGjBM","XEVu8hwruPNW":"t6mzM3NidcOM","aFSpvfXVDfpY":"ig980BCxVdnm","6Gfyu6FVxRzY":"TPLb2NWw6ftf","qo8aJVoExTXL":"3yBf8h0kzsH6","2dbVuHt3wGPS":"ZT3jVc8IYYMu","nBlJko39U5Hl":"XB8ItlGuyaWO","B73eJ3DKGhzB":"NkpTrXzhxXyS","jXfjG56thQil":"1LBqg1cNUTk8","wYFkIBIEw0eL":"G0alX1CnGb8p","UuCwYvvOBhWE":"YYSpC0S9IxzD","KIq6n0SDNGtO":"lsYgqmZKnBsS","M638FCSgHm5A":"V7UqIiuenzli","U5wCVSu9jTKs":"Zgv0BumYtVLH","jle19euSoBi4":"9CYG2aljaVKi","Sx9deUlsXDt3":"xYaXonaVnUZM","EU6KzpczX8UZ":"Ugtd5O4wkh9D","TCjCC81YByeo":"lXZjiN2leT5y","tr1aFOlvPMUS":"k3g9E8A4LYFX","NwYRUu0Uz19J":"6YlXfaWfexij","s1U6B9wuTeDv":"P9atbo1OHKhl","G138pFHTci8j":"YnMRk1ZI3CEV","54y5gYxbQfjg":"5g5El9a7nYJf","H5ZISSrzGia5":"B6wFDOdjBPbC","w0S900F948VA":"lFsXdJdwIqyz","7ykpcgktGBA7":"ibe4HqfZhhwn","DayMr7lWngk6":"Xh9HnelE94w4","YB0vCZofu6zK":"LDUDVvsfihuI","xBXjTqF5oolu":"b4DmoLMQlzz2","Rg4O7uDg8Glo":"RpszHgu9lwQP","1hdTaTTCqiLt":"DHGXxOuxuRtJ","WWlnsLxiDbvt":"ju3idgDtlsOY","6nSiY2DJd3FE":"j7flHEXmO8yY","nQznAhXFw6hi":"HeIcb44vezcB","uoX0PI7If4vc":"Duf11Stc28PS","PDLEJBVg0shd":"bE4n6AdIYM5o","6nVqNJzUcI3V":"JYt4n3u58KK7","ZTNowcvWHTTa":"wADjYGOuYeKr","BRVq4Troc9ca":"dDl5y7eQqJ55","piH44BveO8w1":"y6C5U2bnCKFa","hlxVyCGLFMV5":"smKELyGfne9q","oYANjd9JGbVm":"6kEyaAF5uafk","Ky20MRbLQ8be":"djHAjxfX8W75","Zaly7tAjidWH":"n12Y2RBGD3Zy","uZWrKGFtJFLg":"4pG3mseuLrzg","gmzhZ2rD3awV":"rSv12WImfsQu","f8GwS3x1zTqx":"QI6KQH6EVGPn","JvlBVEQGDUSV":"Y1l7XEuRXSEP","F0PsoyXarats":"XGX9VSDO07O1","iAFIIfjp1oyP":"vWYzPcFVeYGm","DeSWLx2tmX22":"TVlACZqbbIXb","f7HrdpTjea0y":"Oeyb0M4QDHW5","BDyaI7d8iLCR":"vCWW2KFCHWkO","23VYYgrOaaJJ":"DqalUvP9oyIz","D5O1cQsiMytz":"yqfc7QlOqPrg","G58MOeGiYceK":"KA9nFo0EEfPm","fUZatyGua9wb":"eEFFrapncihn","qGvhF35nsvkC":"nLFr1E1uy4Ld","jJCVKF5jgIID":"6odKgqpU3UuP","ed7Mpm4Bcg7h":"INDFVPU0pB2t","RGxZpTmrgrTO":"wZTecKMKwcfO","jhWaVzdW3vuV":"ktaI88i0Em3z","1sNEukgktYVh":"bEjAS3Vjk6CD","TTzUjlPSf9Pj":"cWPdw40DrUgp","c7ETOcLYOurF":"HO4pnBY60ZDN","aET7239qAIfj":"KKTWKtGJYO2D","euyLtg1RDulj":"wPyPP5B5bq1G","w1Ix5jZJFIXI":"luMBaFHauRWQ","lAx3cWIZ5ZBl":"RbvNFNqF2uQZ","QGQYR3EDCM2s":"mgSMdMpq7Ikc","SOyYlPfYhj5Q":"5AVYvJh21JV9","shyVnnf2fkLj":"FpTmsYn6HbOZ","xvt0daV4zbDP":"XKglwNAN6rpP","ykxZ26otToh2":"1sAjSbZeZcLF","SbdqOMxG8DtB":"RdAr8puYUyQe","0fFOHniy3fCC":"H5OqqjLp1xVE","L1VkEZzguZho":"1XoqabaBhlyB","DKejtvPTNhER":"SLyUbVsVBqRS","0YRTiGs7Q1PF":"x02WswD2bFfq","tMO5IkN3SwCx":"tFhf8o7ch74P","jlH4v93WIyPA":"QDB1nzah9ivU","gxZGXKYXmu5B":"fCppDvs2patr","unEwk2uQojIl":"jShjWY4lmvXf","ABWLPhTGd980":"WM4ck7AyK5NP","rXnO92PtSGhj":"f6EewdXOEmhS","o03ZzAnsurBe":"xZfr4CLoipI5","hBpNYKlOAXlm":"eG3oB5xo8g6X","OtH0W9KEznGz":"SYIBB96wocVC","XCxaqxQ5goDL":"jrkzGrjd1mY8","MJvyw2xDqlDb":"HheHcqBVvdvB","3ma0bb6Y8igV":"MPrZqDHGk9WW","x2IblcYliV8u":"iZMxVKMi1zov","vPczV5oOGhLZ":"A2eq6p1HRb9b","Yp5U1NK5bqUj":"4sncIsUyGByq","1nByRCprjwp9":"oGFWDqwIj9Ur","JEuBNaHyc5Ue":"x5ZnIAcrHgtk","nfmW9VoUwmWT":"JctnAwlvk8fi","ff4JiKOyWOfY":"2W68HC4dnX1L","FUVL8iAJQi57":"BkkJ7fxPXTDe","QazFQkPz4mZc":"klVTbUyljIoU","7SY3gYZYm9Ff":"MbhWV9AojRDy","rXsjmqL5rSVt":"wivMSIsxYAaS","0TVeO5RysOe5":"sHbBDTmYoknJ","iWku0nai9c9w":"s3S9jOk07TUX","xFmS1YDOAtjI":"ZkmFRhh73OIu","TkgvEAs9dCaE":"yhjcWeeozbje","5jEg30p2hHDB":"t9hRrVsFEaMs","sHSWIvddZvN0":"nGPcSV79BA2O","2obRgb6hicH3":"fGl8bLKKSHFj","OPcTDBOGqTjm":"fRyEcE0M4QGj","JdtqUlINcAdD":"8AGOj9PS8f7e","jbwK6lb2vz0t":"WXNcA8LrsrKa","HHoAhf7bT5ps":"kAP9z1LRxpGD","IyPUa0Dc6zhO":"ft6J4iElTKEv","iGrff0FN80g4":"1jR7tWOAiosR","hFJnj2jrCac5":"ObndcIipduVE","0gDWTEFPKOM8":"IcvisKcJByyo","2aeG1mMmP5ii":"4UBIIZRdYILJ","yNHinoleT1eR":"c99bglb2zWhU","3BeDe6RTrYPq":"KWpKRBmDqZoi","YPmM1YXFlqej":"jDrn8VNf2ZQi","Eui5IFSzzzbv":"kkYiS2hvJxUT","K7HnObMOLAhA":"ZeZQztWFZ9V4","Y11dpehtfOb4":"4YfX3ikt1Fap","8VkMEzX95SRx":"mBnTox9VnPfL","0YaPygLM9k9Z":"K96SHQ4oYOX1","QE67IlP0dumf":"vGBf7TdMEvRz","hbk2tNtxQ0mC":"dbf0Lw8ywnI5","5SxIQjlTUcC8":"jRUspOo3urbJ","VFwL2zYPzkB0":"cFxiK2qCpGWR","w0zQzllGV8Oi":"qeBOAirZcj0U","ACtOgzZkfofX":"SqoA2N9rkydn","sHZ8OSNrkGzl":"bvmhi9ThPf82","WP1vPH2ITr06":"Jo5uP6hKUZfP","5WPyz2bkyNqk":"WHJi65fiFMAy","E8U6f4ZJS0vC":"7y7xSCzmNJes","nvWD0XJCfXdn":"GMoIg8hzsaSF","Qe7QdetX0xHM":"SiplAEO0xPi1","fh9hPJ5zltg2":"MguJfixa5oAI","GIjPuTgqZGsB":"KIw3f43nG64V","y7Ek8PwErZNz":"54GkotcDe9Rs","Me5gQgacMDiE":"LvqoNXeYPOQq","UAj4dC6MAYeO":"wgjsYaBQkMdd","8bxih3kBrvaU":"ymrS4q17ng7v","p2YEn8AwLYUV":"KDIPviX9cAaN","9ruqmp2hrZcA":"YQ3owGwp14ln","MNalKqMixKKj":"L0unGvQWKuH1","1L3in23OapOz":"7a8heOJjR5Ax","wbAtN6gaT08m":"QxnnhrKoyD91","RJYlIjKNSQiW":"0kH3gs0Wzjpl","kss0vdKUcPDz":"rj9x5KXg4Dvb","6G5ajRfqg3RT":"DHlgvXRWCImt","aOzywakRQ2nS":"2K1o0HOwrzXw","K0BZOAQtK79m":"5zp6jcI1sifr","6PmOBzqLQaoo":"wHDf6O3LXbel","5g7mH4rtnrnX":"LegjtZX0iLg3","0ZdPU3kBuFeu":"62SIu2ZIfK3E","bVDVMZYuAF29":"vEaeJjn9YNnV","IZ64QmVLeoBM":"YxZOuJskQxDC","wTiKLpXgvJgG":"hwJTJ6hhSfY1","SswZyOvY7hZv":"9WFV2xCA67Qt","uck4Tw9uxKBh":"3qvNBATVhBHX","lxt8ff0iwHFo":"bLP81Fow2kCx","WNGLcFjXcQgw":"dCAp5julCIoA","2E66gBN591mk":"rnUWoOHd2hki","96HnIdIs0yst":"TiX4a3tE6k7Z","91zYWdHmzbjx":"qbMLS7zcbodv","tc1Qkx1QSfJL":"KPqgcttZ0E9D","TyvvmAhNGTNX":"4ZWfSx39gc6X","FsSQXJHwXXuz":"jvcr5PcR8r0Y","pa7yDFzlRpyC":"hZQFu9chlbIU","racTjx33VI2I":"KDKZi567mBGJ","T9FI1dNyNmCb":"LpiulWjDobp4","68IZHXXScmiM":"YfWSFCgaRAuo","F3eUvfwAS5Ri":"TyN4su44H3PF","5bZZCQkWE2k4":"CH9aiCu2JXQM","asDcIzxWZcMh":"siDoj6gNYmX0","tTgT4wLhxsKf":"C8p9e0TiKpw2","wr8hSe5pwADS":"qBSjB5QdP0pu","wFAHqJeukOvS":"ZoWLektWs1Ru","FkU2IIqL1ZVE":"FGNE53r9f23r","oN2Aax5MBKlx":"XmUCwEbv9lyl","IKaucNgSE6Qp":"d1OVHsMozAYb","CuXgiUQepqYP":"nVSd0dr0BNqM","I4fjTPjWxNX5":"aDMjJzddFQt7","WlCrOkqgEzSG":"1bL1CcU3I6VZ","sbPSCqQIAyeW":"GdipRbrLShIA","ZolGNcRfMcuh":"GstSCSyWtCpV","qaHbzTigEaDy":"YlLGKAKOsjYi","AYpFDXNVNcwo":"S942a8X8Vqak","R39NKnpxtbrW":"HFMIm374ecPh","DKQTCa5Zi7l2":"9jHSc97yK6zr","lw0H1Psih1i1":"hTc3e0v01CQQ","naQV3vlRzgq5":"z65TLcv4REet","9DNplYY5spnP":"UwPIPQEIx8sP","g5q5kqfdnxMT":"awmmCK7pkGlL","Dk9VB53v7WKK":"Fkiq7w2tQ8pr","kKMStH1uGp1N":"jS0Vbm8xNIvy","nr6fFCIzd3ye":"rS4fHzLGxPk4","f9quR1YvkXSE":"S5IOK8Bvyand","TYOhP1wWB8mg":"OD96zv6eOORe","DWVqHvlA9H7x":"cLXiz4cv7zN8","A237emNkBthQ":"zWc3shPw9JYa","8oKDWyG8kz4E":"goEgJVYLp6O7","fBR4cJQdzHQ4":"se3rzTewNrR6","hVT7HqHCes2i":"ZddIdd3xDFpj","ViEy4uRE3ZYk":"lcGu91fVVVTh","EqDmhObQLuEU":"OyRMqlgo4Ph8","uKXqgw5mnolp":"4bMSqwJhm1KG","IQUERibigqgP":"GX9qJJjRCjl6","9tcDVoQVEPuK":"Bn0AlsTXvD7g","tTx1duiJwrLk":"5GCuwFquhrO4","QBZCeQQoaGoK":"dxRzQjtBCtTa","0rERzdCxM2Ii":"UyjzRwO85Rhc","5ZZ49PQATxys":"5CwKWWM0H4qJ","5M7CulOwqq0r":"GxlmikIYjM3H","PzNqZDRFyu5G":"xn2eeda1VQHO","vK6RkBdnw1VJ":"T05A0CgNaZRX","7sxtDcREWqDM":"yyAmyWnjACwy","FH5Dz4oeLQLY":"Q90H3gyexxII","Q3esZ4Ajq3Oo":"BN4CI8jOkPTE","Q4dSLbR9vnOB":"pGlttCZlqujJ","7SNTPr4cN6Y7":"tiVjbr54J9M8","hxmah9jj2swx":"xgYGUBIVXzuK","SCoJaJCsxKoC":"pqc35fmJANw5","U8833VHCOVxN":"8AT6KtYwiELd","JGCigZ6MRCfH":"wl4vQ3ijtgem","dtdFx3wJ2tZg":"eKtdSqjIeSoq","LqqEgKFlsAH7":"jcFKv1GYIbEj","08Zdnpy8KONt":"YO1dZy1wIaue","gAeu3JDCphjC":"US4gsCj8F3GN","3nObNEBt6S8q":"EHShiepstL9J","4WLN1eQJeGap":"qodbFVbUoMnd","v0N35gEFwLAh":"QVyhYUZmziMV","I1qxAQ3PUq6P":"ovmkr1fABbkQ","fRtCpRCOvkjm":"OYic0LA44S8y","AqvYsUSOq1AK":"cGwsMTk9jq1p","8u66DKBO8PN9":"Ehce8Je8iqGR","08glStecHy81":"aIJDXC8EWU7K","1EtvHDcSBE2k":"3fLv7i6zQYdR","6KjU5alycGVB":"rukeSOq4jeab","zmXbpLvLusYZ":"5Ql0FLVwisXo","Np7CE4SfAQoW":"yV7gERo6BUsF","YbSQuBk28ZXg":"7u0dto2LNH02","FoRFgy4tlaAY":"8Ao1mp6bXlYc","xsUNOkjYwltw":"WhFKBz9b6zla","fF3W1oOEdjHN":"oj6EAnpgTzoz","3ew19raPGs3P":"HFpuhJx5iFd8","kgyCYGQBNPnK":"z8D7DV6lXBLS","ZIHPvpNosBEf":"50OhHT33epnq","zZh0bPDnElDz":"aUbSzY5nHATr","3Oailem06cfK":"AvLUjOu366z6","yz0TxYJTJClq":"Gb3ayMynBCXX","7ahGon7o5ppE":"ug1DUQAttTmz","F46B4bvfKY3j":"0Z1lzAepvls6","sqkkEkHIunG4":"I6SUAkrzfLgS","Mk1xYFrUJb8k":"sFClt3kWAgV1","OXDDv70fP4EB":"kQ1h862BCGE0","2e4zMzMQnR6F":"naBcyNiP3LlA","04tYTZlJiatB":"3jjouR1C3jLo","QUCTj4MXteln":"IUy4kU7Vtkd7","RM2brgIzV9AD":"kgTHhlMtYTlO","HkSjGQXMp3PF":"F1f4SPelsuGE","v6drSb9LsOBL":"cqenBGQobHAt","PExCnN9Osq5u":"y52hKJNUwAar","Ggj3ui3aPMgt":"4WyakVg3sAhh","MRNoK1rYXj04":"FEK0eQC6OSbs","v2ECANadp3Uf":"5SiYRQozsiAN","fTrizZdaS51M":"gRERU97sFIG9","XxlQPlcH3Oxr":"2DUqye7Bhr9l","6siJP29dOvCO":"8m6rcK5EkbRE","56G7rzg36QUz":"gKjVNTco44Bf","hlxnbpWLZler":"l8AFMRfmFrOx","kwbfZH8OhSPF":"fFtilWQMSC8F","3kM2wiNBFrkn":"DHYYmV60Jgwy","J5Ix0xpsTTeh":"LTOwtXk7rHoM","nMQkxOUtU1wR":"B1dK30il7HlO","bOzGeukYOaMp":"H7z59gA1IBcR","bcnvVP1Vw97D":"hDCJ4X5GKpzX","0k54gzhUk0zA":"4suwUU3Db2eK","Bo2b7eF60C0j":"U9IPOOrAMDbY","fWJKTNgPrbzt":"mIX14tN3jxHO","xyBdN31hk9UK":"IeEYwoIJDQ0n","YTgvsX2y3du4":"pcjyp3L4tKLm","nshHdYu1nnYk":"PISKr0OT8kV5","UxY84MPK8mE0":"qigFrX4KMYF1","PzE1mXIfMCiG":"f1PjIU40igMZ","xglyrsmPiILY":"nS21akezmYRi","s3thEC0J2Qtv":"qpy20kTEST5n","kpvmC8QDzDsC":"TXkubkqSRx9s","K0yPG8LrJm6U":"rILzl0ZMHpWU","06JlJz1KV2pS":"5moZ3oKf6V9t","Zj3QUowNSljS":"wQcRfEYTbkw1","uh5dyq4kWaqc":"4IgnTGRVErpE","l9LfI9vT9bFz":"oOuzoyT3yEic","mzaygY3nkwRx":"jyPtvsJPWeUB","GMfH2AF65P0E":"mpGHbETVyIsy","k3rbGdt4wQfw":"TESWi5HUXmKn","b3HkfBECdQD2":"ZuSfvfgbCP1T","9ne3lHhzHnPl":"dcvZs1hYB2bK","p4Jtye7l3w3q":"DcRbDfDPRqsP","86wE4QNjub26":"rug4ZidhVmNO","nbJV3voskYjL":"FeKE3KVjDSh8","r9ibNXMAaYkW":"ELwpZvHGnT37","TEdYNN8zF9w5":"ueKSFNRmogbO","FqQSvSBIqnKd":"QSoX12l1Eb4V","7qVJnWYLNPFK":"BSBfZy271kV8","ROPSqlReREg7":"JqTXnYOsRoNM","MWOCaQsPDelb":"WFJ23z0pqc72","aTGEYi3JGoGw":"ovC69H1BZbPp","LSDDI9PHyYp6":"R2GHcjZujpSM","Lky1SeOsumjE":"xF8FwTdZnSuw","1nMVQuWxKVaL":"HHCNXQqOhVx0","9iDUSjQRLK6A":"BfYPgvPKmTJ3","CB2GwgeP4IXa":"AvqY8Le1X7lh","VIdRP6upunb4":"f2gug7iQiTFA","qypgskIWun6g":"Fm8t2Zm1j3eb","qW9DTgKzzZlh":"rhHqp4cqpaA7","tpeiN1nELOOJ":"WmHxYRca3vUR","EYtpGB7gIMpm":"QzcN5JwZFfeu","50jE9yy1Lx06":"jWsHQpF4GwDh","1VTu21ptZ0Xd":"IescfbYh437J","j8AgQ0ohIk7a":"VHTX6pNLUOHc","66VI7P5P1s3D":"zBIVlkXl62Kk","1s6uvLsP5qjy":"TaB3xHn3D0HP","kihvSYQybYW1":"O4pUa4Uerx9d","34bpx64fUWzO":"wn7jRsFGxhXK","89mbA7DDhlmB":"ORHWYrtCEI3M","AmtGOzsBWCZb":"r5Y4xIvmONPf","XIVcOiq4Vxc4":"jgdct02gfA2W","qIwVMqwWw6Tm":"egBSNM20IDy6","oSk3tEl2Ix8h":"C0cIzSz1CZ3W","WuUAuKWb06VB":"o0Y31CT6orXh","5DKF4EVEUSHd":"MA0CtBiH2gfe","bcznDgGcKkC0":"0d6jp3ub4YdQ","b4cYWwooYUDH":"nJmd2KBk81RV","Q1PWV8b9i5Lf":"tsKw4prEhZtk","7Z8d4FrD85Uw":"NtCE3EjT4A6Q","qtTMeNwUfhF5":"D8gKPFS4lnwv","qrC9DNyQSKF3":"YgPIZyWJvpuD","GHXxrV4AqTAQ":"ipJUMuFbfmeR","UZsto9rXj3oz":"Ald2qTWOnPKY","oLGZ3K1ZKrHe":"1DZ8U8chR2BE","PiKGdkuUn4kw":"8dl4l3x5zyU9","ew1LL3Dg2grl":"vTL40SqBcr9F","9xNGoSR5nGAQ":"lfsDhGONPS6x","WXejMXbzFCx2":"eR7Xh1VuR6IR","RH5fJxz8qGbh":"2TOhtt4RKiCt","EhyaQoMAAraS":"5cu0hfEsz3LH","wILHoBmv1RlV":"dRZeC7wWnbY7","FJPdCvO4xVnV":"FhdqtLLaMvXa","vioL8Rb6Cyyl":"xvLCR09tJ2PV","NS2djk3iY8pu":"05vIV87YYaQp","lKj2FOWza5ID":"QBg0UN9L3wfQ","LcriYkXfc9K4":"Zbh4a4Pk6QNy","7kmxwNGyA145":"bp0V6mUXqmdx","y2rChyYnO1ge":"egs5iaW0PhSM","wYBZXtUNGA7y":"PMRQmpzn0Rcu","TDTKnT9J2SuQ":"1R5kqXjjmgp0","24rQuI68rwKE":"Z9LRk1y3yzu6","MgZint4hykX9":"KSMeQFpOqwcx","uGKk7o71xlRq":"cL04CR2Q7s85","O0isLHUEoBGV":"AIBpTkfhjwC1","gUjSwdu9GZeA":"npNWLbynkHze","BhZ0vcEBtKi4":"qd9bM6UbNw5k","LEdcBFcY3xxN":"WXH0LP3TU3MQ","oFHQfbCtySIn":"b5v9wRJh0xGp","qHOjEwsDwaBb":"Wywofk0qVbZI","bhzXsrHNbcAV":"Av76Sljxf5fl","uE2kilp0XxGO":"Hk5UHq2E9qpK","mcMgpgqlSjjm":"XgnrauMoN3dC","FX9oSGJ1Q1mT":"jlira8rpPX7u","M0jXfObbJSWE":"YWjNpSnEyR6x","7JWAXTIIAYNf":"TNrFSZtEBFYy","2eJ7m2vT4OrX":"y0eDAcwly7oe","Kv8S3VKHL5Qt":"jLLQWtn229aE","2QXXcpYKS853":"0UO2FiPbg6uw","hCToxcX79ruH":"csNMUPRdmTsd","3mPDoakttkU7":"MeDjhVBXHtTc","a79XVo9cSW55":"2jJOXouB25Wl","6dPsfSqqiyH0":"rHin0hWHG3Kq","yGVG1rGBEZIa":"O4D7YphjAL6J","H88eNEd8T8gn":"pAJGBGQr4cw1","8sGCPJ0ZJ9BV":"kVW8lMaylgz7","We4hbOSO8MGn":"EWITab6Cytjt","PjqFFmj6nweJ":"bxbXwYqAojVR","NQw9bvqscSmQ":"DUJx6TVRPMCU","XdOynGYWPO7o":"TbUhTIX5i671","hLmYyd6T8h0o":"8dJQgndKJELX","o1KB4DvrpdJS":"kXq5hbBLHFyJ","dCpTc5DdP53l":"vtCBQbbUVewM","ogVVjXPTkQvT":"oqdE4djVuDg9","2qO7kgPatAea":"WMbokUBLHqX4","83uVNzDJ91Nr":"PWwi3D16vQUY","T6yOZI0aMmJ9":"YQSoBycjLFNP","xyyhx8j1u1Lb":"R4eMjojVWkGW","ceDJsbqVDfDy":"a0A65U6ZlG2L","GbqYvZKPIZ1h":"40GMKlM0Kkis","wSjdZvlF2ubl":"RqUaH4ncW0wH","c8ogSK9x7Ils":"YYFY4CtK07FT","dZPTJ2QZAXnH":"TXqYgUbCKO9n","qlQJR1QqAInX":"VcLrFQhjEx4G","WajuBz3cAI8n":"w2W0ARUZVvYc","v4EHeuCGQtg4":"x8zQoQjTvoPm","gLnwtf0yWVWw":"7MtM5lHnatAU","tcCOYNFGaYoj":"DFdS6pe80Lsy","i8zJ6KSw3WeR":"EhVN3OlSZo5D","RExCqfrtUDO2":"QZNcHWII8UQ6","CLEL1WFx5EWi":"9FvmeQDv3OEm","lCqCKSJxLhlN":"Y1JfpilqQfsu","p7jAt7cp8Vsu":"kNEOXdcA9FAN","OQhr19CcUj2c":"GHTRG0obWefe","CkcwSMW5LTGo":"15v8v00jHxXd","svkAKs6MBfap":"yMAKt3f6dang","6w9R51CsTTNb":"w5onbRS7HFgI","910EsOjFBJCu":"sucfuKu9LEgx","qXlTn2nrmbHW":"hRPxgrwKIfUb","MEuqLWTFhS9L":"0SpScxWwbD8O","D9a72zFsYmKQ":"7jExo3JXkXUs","ogHEGYsy4FYu":"A6HrNqGHgWSm","6kVjTgMt70DT":"ycRbrcg4odeN","Hkxt2imsYyBE":"8bkukixGLdxp","AguBPgTK8LZJ":"radSSIVhVOjI","nDIXABXv80lw":"xS9fi08kHH7N","WbmDqM50XPlE":"TgSZVqeftk6Q","wpDXzMqGgvdQ":"yiLeEzrAS7hj","9c4IVXHwJvQx":"2Gm1GqkOystw","xVwnB2NipORN":"B4A2811uQwBB","UBwDMDpWVMCt":"Ecm0j4qdSLK9","jUgcLE3durUu":"zCuW5IFggRtv","t3M6PanwApFe":"oMbszDu9xsXu","PsJ4UsljQT7v":"EFgx48t9CoKv","BprNeFLN20MQ":"xryYh6cbfoqj","PLPUhAVN4Npo":"A05mQSTMUmfz","ER5Nw31QP2x1":"CHdBN0T2cvug","kn4JaLMACTmv":"0qdwZhTNspRZ","unc4FXVsnOWs":"6btUzlT0kfvp","1WQwC4yMxUwc":"PuZyyC5HSPmJ","bGhca7lCttq9":"KTofkuNZOFbT","If2DeOglwcUr":"iqLxYSXWzMwZ","hVIKI1P2OqXb":"HL86oiNBwy1x","beQ9bGz0EesN":"lYuWvXvYTASI","MYDbfLh20lLx":"MGDN1gCIkDDR","55oAEB1V9jA5":"e0uhyJz3UNh5","aI7VFvquHRTA":"IK5R6FERDxgH","mWsZ93lsi0Cp":"3i24VfcpfPIo","MwhWvUj8BoS1":"y0GVD9UAR9z4","3zim02GmClT0":"FN1c4RREkOEu","nDGXTLOYimdj":"JJF3W7jlbWBl","bQJGwbKKY1As":"G8LkZeyTBrEw","BWFcZgwH361U":"seAJaUksOhM6","hgQeFbNWgbMQ":"IZwLWkrbv4zZ","YlAIeqomDBRe":"QalbsR6PKzD8","tJMVpmrPHW5v":"RXNLoxTgO13E","BBN4XVR6BsVJ":"D6i2hjdXHLYU","704dXcuO6s7d":"SJhSBGk49C45","RMTjtZ3oCZGm":"3eOiqEuvMt9j","4yHJClXAnA7i":"RX1zJJDI5jDs","SMep6h8L0NMI":"SzcirfUSywaq","T7fKDvyF0n8B":"VR6jxEShSV2U","6E6wY58MEmTQ":"hxdlP4XFB059","D8lP3XefFOc5":"hpJCD60gXF27","sk4bRSYNdZiN":"B26OfE2Sk4Sx","807aSB8u0QVy":"BZjlpPS2YOBS","nAUryNGdKKfv":"dd63Ga5dt6Xy","raP79g3cvYL1":"jQCghH5CtBDk","AcIgX86R9rUd":"yvnNGwIFetgu","NgRg09UZ7kN3":"LeWz1PjcM9my","mwMz8824EM2s":"uKJ92hZXqHWx","14XzFWo3O0qj":"fErZcZHH3lGU","lJaSCFEqIDbT":"8520Xl12sgZS","uDaFNrZ2Pfrr":"havi3ZDiTnkn","CvJnJC5gjTfd":"UAJErSEwCEnT","aJNs7EPn2GGU":"08jJSGalU1C0","QSV3hthJijbf":"WLQXN8SnxAmK","19IuavFk7M8e":"XZwllFd3yNH0","HvcVeMjsyXwi":"dANDfekf3zCv","XeSXQs5Hr1kL":"N9jOnguHdIj9","3AsZJ8v751BN":"p3DjwtOPweoe","P7fujFHOMHc6":"rSOzUB5bDVrO","yBO76AMOGdL3":"gksnr3x8D7FV","lIzLio6q7IFk":"jxZAWD3ySxtm","6h0i0WKZJ4wF":"oDvKbdER4wsa","ERPZGTcqxsWw":"qGBUvDHyxDJz","lxehIg7qh91T":"mdqLsJ6XcGeR","oVyyUSrQEchz":"L0c99NEcNwKP","sX3NrEA0xjVj":"K6riFRQdbq1y","rlIJxdEEaPqi":"XXJSkkrFnQb0","dWyXIaMr6Xt7":"Lyse70eGOrAl","Nwx2kX4dV4IV":"en6DyYnqvhZW","9cgFVrUhTbpz":"XIx2YD8gJrAF","ER5O5Gg6JCIo":"sZ99VfEFa1iK","IiB1I4T1Cu8p":"ae6J4x3hlZbv","sS6xEgOmnCbN":"pHb08fTfAfWT","kN4xVkb0NzwQ":"noLM3OyIk5o6","VAymuF1K2Qhr":"Wvvmakcz2mIj","C7WiNwuGHbku":"6wFy24udfLyE","5EmOdtsnLz20":"E0WRHOoQnYcJ","ehJhotWD9bWF":"GofETUUAPqrl","ox05lwlO9Yx3":"eokcXJuMyNA7","A0bPu51TWQkW":"594zz7VZAU8C","4T80pdAyAK6M":"tnYRbgOMoGSO","Nqp4jsn1fhfL":"yVXke80ziLE3","AlNciHVMFfUf":"MN0LZm5n4X2x","7qipwItAPjdu":"Qyr6I3RMKPGF","34tnQkXR1pYr":"qQMPT3pr5Stb","jL0bT3HCBVJr":"KSsUWV2JPDLX","V5H21stebwkU":"F0td2Zhvv7pR","cbgTmFxxtrh3":"3XFSpXlKnoPC","dbDGZ6lDi7UP":"5w5q1NH2jF1i","TLvIaldtsTEP":"mlnhY4auzuQT","u16zY6vZhbtu":"nWv76qjNilmW","fnJhzfasNmmh":"qt6MnzaKXlAw","CKmIOs050mPZ":"G9Ht3u3vClO9","FPB3yoogo0wS":"z3YXIB2QllrE","nOsJSeNPnO5n":"c89GJVQoNfXU","BeuRzpw7Myco":"3RIXSaOOb2ou","cHs1ZzPAXnvm":"y0301tn81sOC","fvvcCdIFxuRI":"qJGvQzq8eDqw","R1TunuD7LG25":"PMVq0AaemFeR","06XGkXFakLqL":"TuJup0qrDrJ4","Th9ZCuhI3moB":"1juX3Qabs4xa","pixiCiLLznx3":"ljL7G2prJBDe","lDAE8T8ZvYHP":"8FF09NoSf79P","zXZyiiz90J4u":"p8TpTSnxkCCy","LRps9H2LmdPm":"OJTBN1DsZPjl","iBODEssC7sMk":"ee2XZKbS0qjo","5wy5MqaaD1sT":"LgJtd4uARR13","apd2jGeWQpjb":"CBtRCyvsLuSs","2pp90YcRG25D":"2gLOighBumS3","3Iv3fd1QXu1B":"bE75hu7AUnSN","j8mhQPQiNAzO":"X50gdh8O1H0l","10VA9ncuHIZN":"MNzKxU0pBJAL","TpC1s8ZKGKKz":"yulYoq0wKq8x","I22sZc733ghh":"KWEwBSi8bFX0","B7HcT4dlpxyS":"u7xPHm9v9vc9","4Bjt62IqF2Vv":"P1SWuJFxAY8m","q9k7BWNsKYw0":"k3TbaXLfvqhW","Vo3XJxXe1xnF":"gYXX4zJehVvy","Nrcr4dtclJgA":"ajFmE1XjGDyn","LdBVUhCG08Bf":"Tgv1ZsgPRKbj","MeHhjMF4a3oE":"yROii3O1dKaH","SmFUKOBv7HW6":"sM29fHRLTnw4","YwA6snJWn8Zu":"es9sVYnEgJ1m","t2XSrmoiMIb8":"FamciJeijiyj","ZA1OSnjetqma":"2zVYDhVVq8Dg","vEM2TEYeaTT8":"TRtng9v5ziiT","9HrKxI5ksMq1":"5BDEuRLySuVS","yXpBE62PPbNa":"OgAvmOvJ4DhD","XbWM6TWg8cKB":"YdxbAVXZNPCg","YuuCagHAcabB":"7WIqeqXmvj5r","CcIMkIHJtyMC":"iVS41QzLqGwY","gYMHekPs3YSM":"7NbhnG3rLgke","nixL4Q2trL7U":"2AOgZOURB966","32XXGLTVpcms":"9XCpNlBQloy7","e7rmIhr1b8RX":"RKreKVjWaprF","lhAsYT1nz5mM":"4RfHYovQN20V","gcvJw9sHci6q":"afMPaTkO33B5","6YMrETV9SGU8":"Lj3OP0fmj0Wa","O7UJohLtBrCJ":"Rd4A9jyEU9xs","l6ewxq8ubgCu":"h3CNGvsEAaBJ","iY2X2KLZhSjt":"JceU7IbG3OeM","QkNMTzfKLdnJ":"3aSpm0VpLE5m","tAdtZBQXAKaI":"6mhHK3mo1sWI","fY0JZmHyaUju":"4uGK7bQy32Dn","GhkvO7sUMrFL":"zfXzqy2L53R9","XsgJUvL2rg8T":"EXDpG0rMveyg","cWTvvpWlLwV7":"CkNw8RTA8WxR","i74BP2VFsUUZ":"Gm34iTF9qUge","EWQns3EmOhhy":"65SBNhVO9h2a","IDONJ54LcvGj":"MBylojjQAJwW","NZGsO0pBom1I":"TBN8iYLu2QRm","NNBeK4zky9pv":"aYMacvDNlzyM","B759RoOBUTRG":"kCZ7YdmPmQzH","yVsFRF1Zn5k2":"hHsPvKPkRWOC","Hyl6V7TPbYHq":"NUvoyyqRYSaL","fGG8E6k1ROUz":"ZUIgA8hIimVZ","b0vzFPZzfwv7":"uTKhrZ7iNLeW","joetz467QgGb":"CGCG0WWv26w8","KLkQVMeI6O3G":"ZL5klOKQ0i78","q2uxxwRHqWj0":"KzQz40Nfhal5","F7LrpAgJfcXB":"4ODcg8mQLXcs","NQWKxwJ4aWJD":"1k47siQKlBKe","Lq4UZteBrPtv":"0eplgmhOy3zY","pX3ICfAYDanA":"9RwVHNtZtRhS","Jw9kZg2xW7lS":"jTWlgpMzS9rd","cgTdzOKA5Gt5":"krsijdHQHYzL","mlvhWb4LeJav":"yXAbroGLGBiG","SMHInxYvbuc4":"0zFFrYRT30YI","g5Z3ZTGqjmFm":"QQvTaHdj0Koa","gpAPHG0nhWrj":"K52C4duKJPvc","eClY0MOZC3aJ":"wCquyNPZAzqB","qzZ2klYyRSJ5":"ppS7odJk26f2","mQu5PE99MJW3":"gYzFQIOfEkcu","zuB9h8lWHbWY":"jcPXxDxhE7G2","hYcfhwPHvOJ0":"thfgVaKv3WwH","s6Gzvycx8jYB":"868j0fTWKud9","ka2GEkuHAHMB":"HYf5q0AvNWle","x8BU2W8Sw5pX":"dVoomCW54x5M","LhUveT8RsD0S":"DKa8dGYnD6nD","83UKkIbYTy7O":"aTx6lZdtD8rb","0W6Xaf0Ur0t1":"KAd9CgfwGrI1","N6n63NlquUf8":"HXLefkfOlixa","XTsHOjlFOo1r":"hOtKkq5IjRJP","tXleZ8JbdqBg":"qrFQu0zQKJuE","rFoonMxF99ru":"q1yzVmB7SXnK","To4fYL8gmymf":"gGMxWRfZPSLn","5iKz1y15692G":"ysHundGbGgcV","HEXoYtzknEpl":"aXHPUXLRbyYa","dpJBPimotbEC":"UmYA6LGoKpJP","5sVNPu8Kvm9x":"1UwQDXKOOb6A","ZPRGaYv7fHJl":"GORfOP92qpMN","uK23CfvfKmUW":"x9sFgSvfn6fK","K2AhJ1oPS0rw":"UtWjEY18X4QP","ZGfIig4vIrPQ":"Iryp8lnbB7QA","bMacl7LwLYng":"JJhxjNSEBpHV","kDp7om7YzYFT":"1wgYBnNmf5yE","GWLcNMWD8e0c":"PeBZQRKFiZNA","lsGvBKUvYiyS":"8Q7ygPhBHTTK","rsKYfJsW1ZKS":"vOa1AhNqDEVA","QSBSBfsXRAJl":"74xZHVCwZhHO","BdZGTy0J6JRD":"dTpEjAZHiUJN","SHUq4wNRsWwZ":"7R9zCg0queMf","rLZBNhIQkhRS":"Asc3J2AfUrgR","3PCfjLOJblqR":"DTrJDJ1GPQol","nrtQLrX7q3zR":"XZEtnzE74Mns","dGjLiCLYv6ew":"qal2ZUP5hmTf","zh9h6MWMaLEE":"Gyh5cWnUXQ40","WMv4byFgSocH":"N5pPH3ntqnOS","VfKoaM6KGS0l":"BkuA2BOhzBal","eJFjiTxEugMf":"pdkOfMY4S8UN","xsivH2rj8ucp":"JdDKF4q9NRIh","dGpeaT4ww2u9":"Ea6JBXiGiqr6","cjMMVCYAf2K2":"fK4CFCrJc9rd","gIJuMekarq4s":"f9ZK6smVjHRd","CZMvKI7Lsjz5":"uMclNjSlVRcX","OrnmQS8a0vn1":"rrbWbaGuVcUX","WJdrdoS3FU9w":"VRiVa3RA0LaT","UEayETOnuykI":"Cinm5NE0DS2p","82a5mpBsbLQY":"DhC6z6zPsZIk","QeBBOXeCtb8T":"1mesHFvqNJNw","Sbjl10z8DZRy":"Ctau3x8xRGdd","VPGAnLZTPifg":"43OS47ZWCwRH","1K8qyfVQwdT4":"fMboE4YkPTbZ","pGioQ5g1HrIK":"rd0uYR74QNxM","Agqj0YURYEL0":"SJ5LnYX1RQhV","OX7MlJqVbBtf":"VwepSkiEZC66","24EgWNKoPXve":"lkhv92nRo38S","L6fmIrhQhlzE":"Ivgn4cmvYydu","r153MiRfJREV":"Z518v8zhgFZk","2nYyWe6P2bnn":"O5AJw77oyF8k","wxZdZR7Tn3uB":"KSd6Q3xd8cnJ","Fz9ZxMproHm9":"1na0xn60szYy","xzncyy77NmaF":"s2HBkn23TApx","edvFc0dK5AKA":"d0xwYNpwALK3","3swqrvg8gGco":"AYAZbvSjMe1y","cmXOvtH7rdFR":"W0klXazOBiLr","E39LvdlEyuET":"bsdreXIMTBlK","P1OKCppJFFif":"wuDQb9L0PgOO","7tTMNPWHGcCY":"6bOhRAkhKS47","LAF3snWpj6I6":"Wd60biG8deMa","QHMGAFZgco1F":"czfPR2mLTzhv","5BeburZoVvcU":"ZSFoEH1DJvUM","oPWtewX1shWA":"mVsJYs1pUP6V","R8IamwbktbHN":"hgvltpzTedeI","Lu9ojyfwIEGB":"JmmZaQDdXlrW","iwai00UJXW1k":"5cU4w7qsrl1s","Pho7xo5sqBYW":"GnCk4ror9UaT","LjMvIkfFblBF":"QXzHAbLYEfAO","UZQCu967GkZi":"z006v6DAsMor","ozvdkeGzvQ4I":"yhJnqZSxreE5","bQpHw78gcZaQ":"Gi2P4kDgxCFz","Nj4pmozmqpBQ":"vmrLQjcMjiC2","Rc2iKnEr10Bl":"NQsBT2EGY4dO","3d5pTHGKBPbU":"hxhIHmn1wF8K","EcojbnDMVEfy":"Reu3mIbmd40e","dk6qJI1nO1E4":"zGkXOSAASDxa","dnEqTjuJRea9":"AzwRzERYp0VO","C89aRTRXH7MA":"zOkXQfjxSLNE","mUzzzALUiVZf":"rCSvza088HDO","pjJ8PfViF15g":"PolBfwYYmZOX","mjN7r4R9jP1t":"9mpbbdZIBzMR","LN2mgea4OD0O":"s53wwT1Q8evO","rH40B61xMOhU":"WxLDbmqYsrAq","JbrFC3f2RiKQ":"DBFZ3D0zYfOS","v7ZvtLs4F5f7":"oBdy9DQQdhRO","0TXxnHLpGJgh":"1E83TKP9DPX1","kBNjztQnnWEI":"agvcYLJG5w2e","OHSzXeyzdJ9D":"4bq1MdtMlDXM","2e6kRwjTw46t":"R1cBQolkSlMQ","1l6ueJEjl6nY":"63yhhqWhpuTU","Ck6WrV2sGfuv":"alsYgZPMWdcZ","7Joha5fi97Gw":"wMqTryU3quEG","l5NEV2OfGC21":"9Be4hyJJUmIn","jtpclP3WEZXk":"hh0SA8zqDRAa","LeGE6atLCW5D":"xwC5q5TGLT91","f55dYBZnDyB3":"tqaCsP7piiUV","ih8973FrahGw":"IUtCAab6pfUb","c173wrpbVOon":"i2lp9nhy8qIA","8DxR1fcNbfAj":"1ZkZL2JC8GLC","3h7DoDcVFnJR":"3hdBLSjGHIrC","5QjhrUOmrwpv":"sqa7SBftOLMB","1SfZc95sVkc8":"IAKDQSfls7Fu","mLkIlUKotUaX":"grwI7h8RS25h","KciPP4Oo5FLM":"s45gdJusxkQ5","cBpAyMw5OWl3":"A4bwHPoNyCQX","tAH5MyFjEF0h":"XjGAXI2lt84A","4RyRFgBkDnuV":"Flw04ITmo10X","W0XyaJq3tUon":"6AUSApy6RDxE","KW849ZHKPq2G":"W3RrcIBKMfeI","OuZLrbAM540e":"M0hiT2wAJOYS","7CznQP925GU0":"cx6V2qbRtvrL","zMIDV5qu0R27":"bwKvTGlBNUlr","EooXiBH6nvwY":"qvzUI1klcyY0","R1xSfBnWpvoX":"4lzKCGX0luzK","qYtRXqBT1k2l":"UrOArPueHFze","KaDVW8RkT3lq":"WzpGBuwh54QV","R37gxC1PXCc9":"Gqq7ZGjoUgo1","apzCRQ4fo64i":"WmEoClpD5jcl","9hk7ZSp7ywvJ":"lCKtCLMkyald","jUWXHATlwMR0":"1egxZ6SB12jb","rRQKcglZGPqR":"hEkVf8JHzPDY","LQyoexd05owR":"NihFJYob9vtw","2qR6vVRRzXvl":"3yq5dLIlARoh","UveihMdXDfBK":"1hmQlvpu18be","rg0htOqZYjFK":"PgvEoh5PHUIL","M1Ucu7MCF2Jd":"ynXursisIe8g","n1jMWKdHvpIA":"8Zh7skve2F88","UrFid1EMfu3L":"rpjzAbu9fGUi","0Ksz7GFZFwpI":"JJv8NBnAn7t0","5kE1sdx9D5KQ":"KOaRN4ULe3Ob","o2yURhEDyxdj":"FLL9Y3QS3QOt","8CcfdAdc2U0L":"FiJ6STt6auWk","i4Bz2hnlg65i":"KnbZi4NTFmPu","Vo0fsCzIwlJ7":"Zy0Lqpc8lKfa","vqwjENsleDq2":"smRI471o4dUm","sdiPQF59PXg8":"TSbIWXGQQWvv","PDu7kcVizBFC":"PKeZJEnvItbS","FgoFo2vpV3Nt":"0034LyqCUiXd","pkvlXFfn5DDo":"A8luxMPwlSHp","YMQtlj18iH2u":"oVYi5TLCX3Fg","95zc0fCVzEvt":"UECjyEQv19uu","yhER5cznfdcv":"dWzA6GbDXEin","RO2whOJursOV":"cC1wjndTYMkW","deuaeCxm9JyD":"YNHLTtMntllE","fPBfqNXxQdNN":"r8V8n0fOTdbO","DrbANP11EkS8":"EJwG8VZ8Yw88","O1uTD0EMB8FG":"KQFWGkP8DsYV","Bgmdqb4uqXRT":"arG1wrKxc120","3RoI3qqSF9dF":"tjPa5AR9QzNS","y3ZMD20BhY0C":"oT1RDDadHqDR","fmo4nYKHGVsz":"hmhVVCqGGCkD","EZX6Jafx0ZVn":"tB5VDtcy39EZ","jaHd93deuiI4":"Cx18l71Tzzmo","CyEuXUb17ZYW":"YPp2MDUSvUhx","4f62cbigzzM5":"bQZiBcbm07Wu","38Jj3tAlmmoS":"018EkzRMI79e","d5cmvOFdQPOU":"6dHbekfRYbwt","6JgLjSVapbvG":"OONv5fBCh4IV","C5pXYhQuc8Q7":"HF5DhGVpHjDu","aftQt34z8AVa":"cYSEdadVWpGo","jRz8n6xUyICf":"q0vHEfxDtxQ0","Eofm7LMwHl8C":"ITgYkGxezEPv","C1hFykGVhaAf":"kSQsxcENH43d","ZmrnbnCZz4Ih":"lI6HteYlY0dB","kWjpxtHVKhHj":"Lt6rMSKej2Bl","TbnVPMH3H7Gl":"jqei49IXJ0Pk","wVwcc06l8wuq":"pyrAK8RNmHAq","AGVLqDEI5b6d":"cFmCYY1vTTUt","NEP34EOBqfF9":"mFVW4PASPyPo","WDPSKzJfXRoa":"SL0DuJxrw70v","31UinFuaNiiy":"tkqwA1R8P5U7","W4NBgBVhRdHy":"n3RM7xRmByHo","bLhAtRac4UiY":"T6dfv86vtH1g","rAiIoSurgMZh":"fhdcrLrITVVc","j3JGMaVqpqGu":"9EqOMAIJntXf","rT12bbKKrHSo":"dbF9syxU5O4H","t7nFBsHRUj6S":"LXkLdFFLwLGy","r4qKI6kOPEf2":"urGNI55s5LEj","mTsasghlbPlT":"oKUqrQk82szT","cXzZXZnOPRVI":"LeMtZ10pWiWN","xWMOYFRlVeS2":"ju41D4I91vIc","f5rAA2CqZbNS":"TZE2c8SCkZEQ","Udn8BovSfQXq":"PsSGojnOS3IU","6seacd4doGiJ":"tye8qCdKXOf1","jWy3h8Zbb7Qp":"KXzWbHD56tSE","F5XDZLy2O3HF":"aroEPRfOFHtY","2g5BlIUX8ccq":"HrOKQrXtiwMU","2YEdsJXpoTMB":"2ui9tJRqJ8Hi","5UP0GouWf6sg":"LMSl2ARL7VEn","uNzBtXIcIyHe":"YiuajyWUmx4b","Baen0AhsYbk8":"LipQkdbTK1j4","oAUggoiOvYGK":"p1cKseU9elRM","qb4NqeR5nQAz":"2bbRjphpezFE","0WCqKmGE3xH4":"Gn5RyYdlsxIR","0Ixcll38kLGO":"Vm0GRqky83ko","cGCLGVBB5sMl":"eT2nWJtbz7NA","KzQHwbzcdijj":"mKzhQgAfDu3S","4jl4XcYOwzCi":"ohMT8xyq0COm","i6S3lxatwJjh":"eJfVBJ2cOi1A","Whg9WHETo596":"YnkO4OHUdeqr","GPWt8BFn2fxT":"5frwVvDlPVcS","LpCvzwtCGvVW":"9Ad9jv3bacc8","F68JiuXeVQ01":"54tcqkSrYHp2","qzZFHnCrtSf3":"z3yZpYVpwxGW","JmedTO8FxdkX":"QhRRZifi44b4","K3FI8rHa85UY":"oJqXQISqHNF6","rLJT97xNM8wt":"yDvx1EsKppqV","hMy02yL0p2N5":"goC3V9cSD8EA","WgztAYZNO16V":"Qm5WoGXHIvWx","cJMJ7NI8uQtr":"4iqOp9wG81Sa","4khFHNhwxfPb":"9uKNwmiICks6","8rNfCowt7GCN":"mi8KJkNP7EzS","h5WElREg5UUX":"bHyxph7XPVR9","RvKWlMbzb3SD":"yzwnIR56Ncn2","1G3o5PomkRhZ":"z3EGnPQHzlQn","Fu4nVwqlgbBP":"b4NSHR29ymN9","2ALM7FNmQ8RH":"Zx3SSadJEH9S","QlSAJ9xsNuVG":"Vh7bEdgEGAjE","dfkFsOZLiK5g":"I0042e6RyBP6","AyJFcfcyXbdm":"31yQOIQ4CocK","3V46TC5ugzRo":"9PY04XBUuD1I","7IA4XTXlOMsK":"yVwqNls7hquA","iYiM3dmX2Yi5":"oGeJVTBX3xeH","QS0VXd099W5Q":"L2nO02PzG9pM","zO9mRna7yqlS":"3IZtoSPgflFn","Trqvlr766HzL":"LT8oN9rEdOHH","cBCmnXBoCa4h":"b4IK7v1SHXb1","4gkFkruhxwo3":"7w4wGm9VKdPt","M7jCYRGMZkWU":"6sI76s12hLdV","wMm43ZH1Xmup":"UqbNN0eqA2nr","cFU0r3el01BR":"fPt2jyOlc7Wt","KyDO53jQFZvU":"uT970rfifO9N","VimGScdMCgaq":"16BdFGYzPZ0l","p4IvwDVZ8Nss":"FHdq60UPOqhQ","B0fVCQW1qjXy":"QiwnrQiQXCPU","NmBlo8QEHsLs":"WVhZ5KGzDuVx","4oljKFuMednq":"obXT5FwNCzpp","njconGjIi52N":"ylPUunc7R7RJ","88fRaUqtDgBn":"wJM13aDh9c0I","YG5nJujmdqH2":"w24rDZBz1ov1","fqMibEwYvghe":"gmHdC3anBcGP","25rYj9WNWuZm":"F3oeRCZjTfJT","l6uWMqMPFnmS":"JCShAYwDlloW","P0wZz5n2o6Jh":"uyRcUCzliUk9","x2oXoTklCNoc":"ektUJgxAj4NI","bbGmYYk3eEwu":"Pbj0uSt4C4lT","4Kj7OR5Icibs":"AOs8tJq88ZA1","ZFZAUSjCwYLn":"qLktjlb3qTNA","OLMRY7RQfzoh":"OufrXCafFxBy","NEMarUUuoLRh":"QfIs8GGSfkO2","AjpPIkTHZmJn":"rLL1gaTOQEoM","VC8Yj9wPBzSG":"8RtMbdyz2mtZ","y7zSQRfufBvZ":"t2ZpX4geEHEH","9m7JixqSVxdy":"dXjtHNaw7W1g","0qxOcMIgT4wu":"MubxbsDLryZB","sproE30XsKZL":"k5ZfltLiUynt","9pXtIhKrfka3":"2KVf4g0EReqQ","JjD9oIiZcu6I":"ZmxiKm1n7kj3","6qdJPIBItuzv":"o1IcHZ26IoSx","xY4TwIZC9Swj":"wW0MPNJqQNGJ","9TFz24qlqgFv":"9E4e8BcRUQFm","EVh9b7DPo34y":"eco1FsLDjuQR","0KD1V9TTAW2K":"zd95o6V8hc3a","RDeFUm4MTkpE":"z6LSfwvfHZDG","xHSbsubu67Pj":"dTtJ3CzWGZ0Q","7UfqkaHunTBp":"CHx3T4x44Mco","jPTsdoCtjpDW":"AGNunTpf5iip","WCauTmIXNGAe":"PLQgatDkGuPn","Z7UtwJsGFSyS":"iK3tXISIOb7m","XW0KyHhRSxBe":"vlPZOmuTImZ5","lcwDiAniQayK":"pcYf3NZv8jAs","pzOKN5zAiOGc":"K1Qcq0E6lYIj","NMMtgXUWX9nL":"jHUYzGeRLNoe","DjdhuumbmH8M":"qNuvKWovN76R","FeujupTpNGLZ":"y4DLA6UUlDfC","rJmzKXzltzBw":"WTxPdMCvSGqe","GbGRKOtS5DGq":"WscOi2IggKO3","3OK28FZNHTI0":"te05Stl9bcSR","U9NRh9NmHjhY":"jQWUgPbzCDFH","VpL9xSBTNjCK":"5ZxwX3rQoe63","b057DcV50b8t":"peJrU2AeBaE1","09sasLWc87ZN":"BBilcyo3cYKo","hDOfxck2b3Ch":"tWznJmA73N4n","AvSu64gHnIQT":"e8zWfpPNHKYV","XNYe6YbIzrCJ":"vlK5jY1bWJgA","kFgVcQ0ItSa6":"ynp2EBpboDoQ","Z4R4zcmWZ44H":"vR2yDzT3b9Z4","Jc5GgRLgQIbp":"zef3TXhHcygi","2YMgDXvLbeFG":"ftVesYjQzkyU","11izvn7VEywE":"fOuxyKqTty8W","Jl7iapOgyAJT":"7PdoMOYLh8Xp","athikYP0w0bz":"VyiAubP8Xa8F","omhQnb8yDQis":"i3bwKLe9Oibc","4Fv7Zm13O1ht":"N7ihzpQ9mdtj","86ehH0TdAucP":"OLTrnXFsXDud","Ey8stVdSLJaf":"YTZ5EWenojcr","g7cXfJFeUmqC":"AWTG1MsIEnic","R0kul8zfoycp":"Cz8ogU308d5N","ObwpA2zjKJB1":"DzJrJtIOYGcf","f4QPwhn1eUFF":"jBeHn4QoJWF8","3NgIpqVHeXQ8":"kfJK10gM9MGf","4QfT7HfgDcxW":"J0uHnOdMys6i","poT9Bi4jfFAb":"V6EdK39rgaQw","JnYvzXzrl78o":"rtd7Xg93Fso4","XMpdCho1MP81":"YG2WDnttMSM6","jAaEXIZacWpy":"dygdYE9tyd73","oThWv7EXQVs8":"RSDrmKZQ19VU","SoZpG7R9Gc25":"Ft6hg1FOFhQS","zfZaETP7bmOD":"c4bK6Bi9GrLs","nJOec3fCAgNn":"x7o0O2eiSpYU","eukKJSRjwLnc":"7vyoANQNRGDN","JDLVnWCdsYNW":"ZrdvGIkyTsmu","Cl5EgznrJWCD":"J4SQEH5AqSrd","TrtXAHWnVB5g":"YY0MpABhyC0b","k0Wh6zu0fzQL":"SxqXEe5AtVl4","yW9yl5NbIyNW":"RJa3tZXszwWB","yPlyC2UOkmCm":"rsrWWOa0KGoW","hn4uJlWvjEIB":"FF1rQ55M0orl","FfygBMGgAdty":"D3ktEcCkOgQT","eCq8mdPKUdcc":"suBh8dyxBL57","6Sxzme74OnAt":"6I5azGZK0E0s","MamB60uwvjjX":"22LiyPBN9h5g","fB2qdiGJLAoi":"DsDvtkCyZT9D","YrNJg63bWKQ3":"2zD5lymNg9Tk","c6Mg8ZUs7GDc":"dmEgOj326w2g","mPmK5ighsz27":"L74OQamaUhFV","HTTGiK1cZTss":"RLkwfibwro5R","ZomEpE2GXbKN":"8CGpajQVg6ek","PMFG9wUAaocM":"uarR86FeIFtr","WNuv3upGe4td":"hrtRxDPPPyaS","SSxtJI7OQJWj":"8yccAy1h77eT","W2e9Yd060rf5":"Nmq1eAQ4DrRY","xGQbBWu9XZbf":"vGCmWH445USz","hvlzEOjNz5cy":"4vT2Pz6WSqWN","hKKXoUvDlUUN":"aYs8HSniQ9rC","AirWyX2BJrgk":"7gCNJP6FAWZu","gulvB1XekkAD":"6zHozzaxRnrp","6poeNCNKLrI8":"OsOSSykc9h1J","jWoplDqirzeM":"XeqWQKZHYTi3","khsx39YaG2EC":"l71GlK23NTOd","SBuc5AESymKd":"f08V4jJDyxpq","epAzwOSmcTAm":"2b6a0OSbnu0T","QDdWQiXIQD7Q":"h2kxy9yitDxL","8bABofYWQS4R":"7kNr1lOhR5zc","BFjIHz7D8vU7":"UfiAdSxcguDM","2W38EzVQHLmI":"yt886g0d24pv","S16bEPwg6KT8":"zd8mLkB6zZor","qNddMVOCSLn9":"DSmFm0r3Wq8m","4XYbh84d9EsE":"TgVLDDzHwYHB","AUydKKHoZect":"htqxnerhCYfB","wPIUbwrD4V17":"9qC224Yj9p4U","i4Mh2ORcqSaA":"gNeNUhQCU7SZ","lVrYLvv0spm3":"NsDLCQ9b3HLb","LvXlKbnbm8Y3":"9bvwR6gqnzBK","dCWCU0LPAPUN":"qghb5v0RjGOR","7i6tgCiHkLfU":"b9yDIcF6forl","Az7CavpAo3yg":"7nrN3hjkXpMr","fVP83cNxQfFZ":"Ng5KojAih9pZ","hCo31Yzyx5jA":"Y25zZmSpGZL1","pAZJoGIvDG3u":"bTggE2nAwhj2","3g0XxwIs5I9H":"M1AlgVcZhxzn","kT3G0HFp2Ylu":"ZrN1Ab5a79NX","mOqd6tN7b4JI":"nSGVZ6PRBcjH","H2nryEYRU2x4":"ib1ovUdSsNqZ","cV0VsDJzHRgO":"f0neeszfR9Ft","48RwVrdRySyw":"sMu4KSoQcKa1","4GI3isY2KAJy":"0Edtrv6AzCAs","Sz1vOLcn1gPE":"E6WNPWTEipzT","gU0HIMUueLDU":"ronmTg7cu2vN","SlvppG1yF6un":"GZ60TJLlyYd3","w32u04iROvcO":"A82nWpt6YtVQ","ynqL6ya1NNEq":"7JxBDm8pHYEB","LdgPP2wd3B6Z":"3kKenAvCyG26","mC8wPNp3c9l6":"59G9XYrdABE2","3E3NDMW8gId3":"gRUHhA5GsPko","NHRoMyM0Y5B2":"Xzu0uNS3yjDp","IJ3R7PtIhXyI":"JhNg9pCKAlZz","9QuGik9w5sVx":"BsXicrmJM3AZ","cszKznukPqtO":"jezm57oxS2qh","WtHWobn1T7tE":"Furkfeq3nQe5","d2GlkSAXfcAx":"QlZ7DgVtZyYI","fMe1JousXnZo":"iI6O43g9WWVY","UswAKpZUdUIW":"ExrH0a4WTlTO","kbEh3BQOiX2o":"y6fh3yjFjk05","5jA96nkdzhZL":"nr6gDnh50y75","q1vrr9VZ0ZHH":"aCYzILB4aOUT","NqtGI55e05o2":"oZkyWFEMJObJ","g3ke1cj8s4Ko":"mSiB1EUqgOfZ","vJABUpuSjDCN":"dbsgnIBV3sBr","lz3qIPbMuPcS":"v1GmcFNnlKZh","SW6qe22oipPd":"CGuxBImg2e8w","wiX8peLL98mc":"ZSSwutRXmc01","mlWuVFAHZOrY":"Yrz7mf5jhIJU","LfkRUguGE4eM":"GrsJKAM8qaLI","WGVjJ5RVcYjR":"51kebj00eFKz","ZnEvPmqHfRKq":"b4NL8vkk4sZ9","y3amUvoXSR4u":"JN3MYG94gF2T","qsQairI86XJk":"EpHXh8aNIU78","nzQn7y1xUbkV":"SWIZtjV4JHxh","1EHKpYTpshrJ":"DI6jZOd2Af6t","mmEnetFBdBez":"otDr5flrYZyP","u8jD3paiuPbh":"80fXP5ht1K8B","FytiLOpMmBCy":"AmjlMk7Un2ue","oTViU1NDUKaS":"34HqNYfivY5k","7q6GuHm1j5g9":"Jo2IP01QnLHm","8CbNl8goz6G8":"xZgRpOX9wa5Q","xtjmzOfJ8xVz":"hJ2Nw1WJZrCZ","6sAJGmjhLv9T":"nXAXVagiCMUV","DgvITWEn86GC":"VEFNxBLEFVVJ","3v2gcRlx52aG":"3UNJVTB9pg0v","7raBGsyV3fp1":"Z7vXfyPu970h","Jf4g2JXVPswN":"3Abn0tYhECuy","b0V1FjGz097r":"q0kxiT3K3dJr","1fouaR2hXVnp":"EAbJA6RL3Btc","p09eHW1qphWt":"Ip4p1mfxVKy3","cuwjdsw3iHoh":"n8QP27EYvpbN","TKUrXyod1x0d":"8X7M8vYpRiOD","693KnHJIA4JO":"p6cUrPuj86FG","7Oa7czEmiony":"u9OXQRJ9qZn9","M6IOd0B68Dji":"VLNqykSA1Mda","fdGpLdd8vnKi":"VxO4nTxDxj6G","eoOmfOEuwe5d":"5xHHPyHlE9Id","NGKtFibHqwPR":"ZxuM9nC4N8pJ","3nWu0tsPHUgH":"1HhA23ju6GMN","TxeHvrExsXJO":"XBgExhSywvxt","hqovArej2Y3Q":"CwvtMy8Wxuqk","uwJZTLTTrOFO":"vGqZP2KduQcE","gBVOoGPT584T":"rJrScmDuBTEZ","usWxrJvtKxeo":"R4lpdRlPstv6","r81SBK6mwTGp":"0uNTfJ7qIV98","B5b309YXOj0y":"tIDgTCgaWSjV","7CxlnIlZVGr2":"Kv5DWU3AczFg","CsjxoXyNvo96":"GI9gT7lkGVeO","5FTAtnnIlLJd":"xE54VxyNzE6B","fsKsFIELtdiy":"5R5OYBHJ14GF","3lRi1dCDaH0p":"LgTKimY6wVA9","v2y7T9OwvHEY":"r2v0WbUcyUIu","pFHDRQ2HepK1":"122VkjEyF7vG","vBm7FdkTSpFN":"vRchi4fktzUI","ByZakQEePV21":"S3ggqt04G7FD","JwjgtFPMwb9S":"r3iauzHI8eR7","6b9xfEfdSYls":"fOFzCBj5r24n","CW8ZzksKmmrR":"UYnGVJA78ZHH","odVUCnJVYehj":"RzKWvXwzZsDX","7gflNGdDrAIk":"oesEjT2x5zm0","taNqNemk8a7u":"2DOoD1z85ZIE","XMqNoLPoy6IS":"q99VUPbqlKir","TwG0VVbX1phe":"O7XlyaqjGlCw","SAnaSDf8Lf8q":"9rgzEMoLXtgK","CAJ7pXodEDaS":"Yc8PSA7FrLDA","rk8QEpjncB6r":"RX0hJXjBd7Qd","vttdXl7ViLoK":"ZjihkterSrAQ","SJMQJzdubP6O":"zLyzFK60sCZX","ATWpEes2WDw4":"v50ONtjb8Ci7","xwTRiAVOMOcE":"k0deth76kvnT","v4tIigutk3E0":"66PH0tmSll7e","YM5DV9WHIBrU":"VFf8rOJL9xtT","DGbE9q9vrait":"xa4tGZNr8l3G","Iu3Cvm6EmGqs":"713oqxRxUZJn","dF3nrMfgSIYd":"4v4t20ZDX048","okdYxrhA11Zr":"tYq86oLVWBOF","QKa00xG4P7fr":"xjEnVz4HXwq3","kRQC4Ezwnzmr":"2S3TYQDqHT1T","KBX3GkvElCcs":"WUDOxHNjEajY","wT7wFGksasPM":"chYNdS4F3FyV","4QYb60a2HPQx":"SGHTYFf1eVZn","WPsiBqbRt9PM":"XiD9g2uVTXTU","XTVjXeoAWQUQ":"HUMxXZdbCtL2","8f1f7QBPYyw9":"FMSS4IA7QHjw","IzaHf5GQOfdn":"gnUCggZRmtUO","JVuRfW9OiGbl":"BO2FCe47f9CN","93wq25WByF5V":"fOtnMx1jwzZ8","6RuZWt3O8gz9":"cdfqYvp1oULf","UouwCI60D7GZ":"T3RVag6hkDZD","mBmQepKlBuak":"CA4pwWjQm5Gf","hsanJRq9ICVZ":"muXzcQe9hlDy","0ZvYGMGYvBc0":"gk6fgn7w3DuW","7ttfSu0RGQkW":"hgyCPMjukt42","suTWqPVF1iBD":"pCmpzxzq5U6h","FYNpe4zitKpF":"R6pDtAoWzaQc","8XmrWLZ6tttc":"jyskh7Il9myN","yijqdlEB7mih":"eFKn1Ud9mQ41","SicXT4df8pDE":"Ofm8vy5zZiVv","AecxET0Wx60V":"J8TjqMKLskeJ","z1VaDdpQ2maZ":"HTd6YrQgwaSW","jBjQyHQLrGHP":"lWWxiQTENzsA","jjfv09oGc5Fk":"PqgUdFTWNccW","tAdmlEvW4rrI":"Qm0vQY5tkACG","PI3SQKa7g5eI":"8e4qgsG9bv1O","mtpVz17jarCH":"Jn3dvIoq0rDn","ebFEtmPXJerU":"LIwAa1A7bqjw","AXiNqdk6ozDA":"WD41bgoRubIT","4VW2DVnfPSRO":"0dxhSso6LVmx","Qk5WzNG2xj0I":"kAXYv5ntP9o9","A76e6ZfNOsqs":"V0H7pzybH1zN","cwGImbWnDcD4":"ZzKMcyPReKSh","1YPjuBtctAA3":"p6eM8Q234vti","hwl2lAVlBWu8":"JD6VYSIufOSl","tDYSeuDSFezz":"yEBLEdKBbcpT","B7LgNhu2GOxD":"EbbXMVERRBWK","0flGwyH8i30k":"eQbXI9l9kN8I","qd1hZCWGjCv4":"tV6Itr9dMHPZ","xZ83qS6ISkhi":"2ZcWlseLG9YP","d3RDLJjde5ca":"1mvor2nMJUC8","47aQXuzwsAFO":"kB4SWrxzftws","sztg9Moe8BRy":"WCinKGAfBMgf","mhWqcTklDm4v":"Ze8DcBrR6Nfx","ha4erSBUWesX":"giU9AsbkawCt","eP8m0Uet5kfK":"8twvUbs9m7Xe","vbncAMr4SyOG":"OqFxiGibjmPl","56B1FCQqCIhe":"Pr9BJY9jJYhQ","nftygaFSRiue":"9qhwQG2QoOLW","qzmQCH9W7BRS":"zCtEcL12VFs1","ohW48R8uzCFg":"qb4wDPz25cuO","bG79zASUu4Vk":"ThzxIW8dNdvR","f8CnMcLJFWCi":"ULjDk7KuOmbl","igJRqQX3AG8l":"DSF70c7HJyaf","1iX2KTE8Gf1t":"5dHqVvjCt2Te","553rIDq4Alq1":"8hCqyMzb9VJf","DSkqA6Scow8p":"9EYSNMd0gKdg","R5QpLNdQr6CG":"VlyQsFeocyXF","8ceT2JChBItc":"RFtxZL4mdfkU","W9YhFz3JeUYh":"pLyS52lWeWUL","5coOkqwJoPyX":"xm5RbamAILL7","STK8seCI8dSe":"9fWWrGUQ4iCl","M2OzfhioZ9yO":"ZRdaxe3Cr1Ok","dgu6MxfFLrwn":"jtNT7qmrtvtA","9PsbSagXLyRs":"fc0si9W9cXId","iITf5spwyFF8":"XgxKDbSlImlI","oX8F3ubzbPNh":"iXEJ3IBoMgOB","M9Q21ew0zh7s":"0qfTDUQAeYOH","DbQvOdJzYAsV":"6tEaU3AIYvA0","YssVePuE3jyQ":"q2MEbKaMIT6y","Tbv2yCnGRMOt":"q11S1i6cEY58","DYhswXg6W17W":"K7DGBRSRUCsa","C3znWgoeLTkq":"R5SW8lRH4ylX","w4I2SsIvrMJS":"nFXiHdxsHwLn","xSMkMKafUo5N":"GR7CPMYBsSY2","n3hfuShITlOR":"Z4kSmIKPGJot","15Toi5OfPCl8":"6c7WOALJ1VTm","qEVJfpqxGU4S":"pRB7Wy4BT2sC","esMroZfREZAA":"zu0Vv8jYQLzM","RC5lGDLDcWlf":"8IfOlTzKDVEb","UZyfxjYOMyTv":"wuzDkOE0EK3Y","dqEFTU4NGDWW":"jKDCpjxTJsW9","JQATgmJYqVma":"Ya6ghkeb0F5z","DKefR82uqUbU":"UwtI8E3fLu4p","n6iuUdghPtTC":"yYBOgVw1538I","I4q8Q7EVa3kS":"kNuH1RXVXdLD","acgGvvJCrC5j":"pC5Id4inj9m4","qILEDMGXMyW4":"JFansEOofZiz","CbXiHDsZFuf5":"tyLrZgq6FAbn","Fu5iRjTVps0u":"pkwfyiZHH7Pz","H5ZDUDTEr583":"sKmplqVguWPU","Qfpmi1yWmL5Y":"ZdRBwef2W4Yo","h8gz4Q5J4HpB":"JyoHsQppfeMP","isKVKcoC5meO":"xmB5H0pxStvx","NmN6yIScBMJt":"bs3yLe9i45Y6","ngCvV6w5ieL5":"WaMqtF8HZQX9","gvAbW84bjRQx":"6jWS0IUFjUd1","KsRHyb5bTuHX":"tCU57o3oGUtF","pHyzuOQwhpyh":"svWRQUe8cafI","da5PsQEnMG7p":"7uWdToMuNZEt","tAO6ijg7IBBz":"mL27DjpoXH5N","nAkVw8fe8Dq5":"ocyJvbf3YTDY","zHbtBghEtfmS":"V0267HC1tNyh","n86Z1RikasVq":"E24agDlAROqL","y6nPwPOsREgW":"Q9dpZQPb4ksw","n2xdzNq7F7R8":"JQwfmtfU4tJN","x3bQHlKmCV1A":"O5lBlU2fSSG6","DoV98y6U74aw":"3RzE2nnZCa3g","ow50kvpQFgT7":"6BZEwCnEHCCV","ndM7d6PXCxTB":"1Gi10MV1WW3F","cUAhJJ9gZuMv":"slDkYjFaOVpc","I2FQQIZwZDSP":"cFAJRvWFm3NF","ynwhQM0PckQ0":"dGYp4yB3dsSq","6VIc8k0RP25P":"uhZ0L1CRwbxu","3WaDsfAZkTYR":"jTaCZwmpm55D","y9I6PrKrK49g":"CCyAKBfXNQJ7","mVCctxLais4t":"kITVvhrjGpWG","4Ghr6Hlw422I":"Y2QUKVs3T0GD","ONaOWGBD7mXX":"06GX6dkrFiSn","wZtlzPnRc8C9":"8cpplyyYsqRx","Qh2twCY8yPaE":"2g3cx9Cto8wz","EoB8meJiic4z":"1HGnQ8pe2tmo","4PUuiWbb8c0G":"qQf33IVFJ8Zx","teMfF2ef4jtV":"vLMv1F7ucgm4","m5r2gGY3u3rO":"f3eTmXQATzEK","ZNO3yGsnckBP":"wlu9XlRfa3J4","HiijtAIujy40":"SEZIyS4DdM8G","x9yGCI893ChY":"hUclrbnMfvfa","IFDnwD48HK3P":"gl2iujzYkt1l","rUC2pQLU2JIC":"fpM8qhxJ5084","eMeUwnbCzG9f":"7iKNhaPIunLR","znCMiaGm8Fv0":"sgpj6sqPCmC8","HL2s2qiqRRoh":"WqBc0o0TvA3Q","FgA5smh1Vu3I":"qzbDuojQBvPt","1Ag5qjuWbsrm":"lZ7sn4ZPBfzv","gbX9LyojZGIw":"ZKxV50Iw5nuk","iF4y7qUiXhXL":"g6KP8XKDbo5A","F6DwdEJslEUm":"sWxked7zsm8V","I8pJCVizzr64":"r5aEklafDTml","uDiDmcZhPW00":"MeAXX1ltu1WV","3uiy5lczEExJ":"KkNSGugStn2m","kLjFEGs0FH4Z":"SDLhFSXIqIkM","IPnhENsydncD":"i30n8xGrBLFb","7zaH9UAUDaLJ":"v2EUyGPNg6Zw","SoXyPdgpkzl9":"Fw9wjVtzSJXQ","2q7FxpO0dElt":"If8T5vkLiFDC","ig8Gg9pZ2kOK":"iWzw9besQnZI","A8gjQqmb2ohD":"JACm2j0YhCib","DGPorQz89NtM":"kSKJvUdKUlSQ","339wkjHBJOiT":"Mb19LfefoVrN","sZyGoyJ4k9im":"JDlUEAqZS42F","1Yw1ZDUd4CM6":"I4pkH7Ttihra","ej45t67Dqp3H":"P7SUgCYdqaBG","cwFzM8BWUg8H":"Vbs9j0OKsaUk","eE94p7Bw7FQ7":"iAfLGET5mEBi","NPycySvwPpjz":"FEH76Eua4Mhk","MQS6u5yQQWsj":"1cgzauONHFQz","QWj5bbIiu8uI":"PSyJ0H0keQnt","SqjvRx810dCY":"1XinGv15Okr2","sJJx0LGBscvL":"jTIp42ebOY6m","6UMK0jpbGCLj":"LmpfuVr01rRk","XzEX55RouX10":"b6xO8Gwntqxm","bkPYhOC5Udbe":"At8x3VyJQa7X","uMhHO56qvz6d":"JQLZyD7M0ErT","Htdcyub7INZ2":"cBQfFFuFfhCS","Sfzow3DPK3om":"O633rpsLtjuF","BXaOLe3xxwyY":"jQiWAoBUf9Qq","T8QYGCjWcKJl":"ILkvjZTDNbf7","9vnXStfMN82u":"Z1bPVTjpIrS0","eUnHeBgIMCtU":"JapvfQYV5xN2","IrzCHsy2qRzG":"XbZSGodc7ZPA","CG9xIUlDcqOo":"i6AmT3znBmO8","9k21DXPamwpr":"Yc9bTSgO2DSi","AbbRHVQSJRKa":"BSpQSBxMBpdv","xWSifCQ0BUmj":"GFuGLJHsUEf2","PMd53ercBtRT":"l2XSEbtfsjes","NDYhOVjeLJyI":"ekjOJysxyfLU","Noe9yxkdbPE4":"HBa4WwVBjbtE","lk5WrY1tGvO5":"eC69lqrLdFaL","cfF3dWOItKcm":"k3P6CoK9rsVv","5Km7QrCaBusr":"X3nzrhK8tjbW","wh2nEwKfbpEi":"oF3DlS9Z0Swk","okzgcMPYu5LI":"2wWnzPBHIrJc","JUn8WkkjyeSV":"sbE4vDi3PXoy","FJ4TNPr0ZtqI":"sqlIxVmtXaoE","ozriGIyQg7yJ":"vBRb18O9f9Ze","aGeMfwT3TmZ2":"jmG7c9JP5dMY","GMOhfDK6EBIn":"sNbPa5uocg3h","Pdu1iqWYNnfI":"Ke1JYyMkhT3o","8VGvksuMWAdg":"61qhK8SkBJ1Q","FHrRlAuiZ5SS":"I0F9OrpeSpfA","zKrOjtmDSPVA":"djEEHqZa9mt7","hKfXd5KGKEAt":"FEJOfDgPNfla","lxiL9GmmtcXu":"kNflg21yOrvv","UhPoRy4oL4lD":"QaxYOhbEDFzF","6owLSvjTtDpb":"5YsGj5uptuoV","yQRaCSNQTpc9":"djjiraLcVh6r","6LoFb00zmmLy":"ghV8y9q9Jpgd","Yp5KDnxDvDKr":"PvaoMYasimeO","VE3Nn4nwomaY":"aXt03YOvDoXi","GlAcoonMGI51":"MrGm0YSeYxAg","iLYqI88dlugY":"MrX3eMT3MpJo","i6aZ5k2rXtnu":"m5G6TBq8cdL2","nAgNwguBS7RD":"VNkmLwIEHfED","8H0emorB73Tl":"EuLgRsLF3Kx0","J9NGcRvSI47O":"f4lQ1SSQWkzf","1jPFQ4KfWqFU":"1V5mpFyfokcZ","0yF4YakgWR3n":"UiFJsfdwYhlR","7P8Mzq9CEIhW":"hjgrT7BXnkgk","0oXDmr8ZMpe7":"iaScbDr3EkaI","bpiqP5MJx1RZ":"IttXSIoaLLye","bEeM4PY32hk8":"MAWe0oZ54ReT","bDd0l97WIZZw":"4nO6zoGrbwM0","Y2z2K8xCaA07":"1AZ0TbW89vds","zcyV9hZKi1uI":"LqefeJOLcS0d","uKWbr572X54H":"CjlYr7BHN0Gj","U0XGLJJee85o":"8IASGdPkaWZr","CbXEhLVJzNHz":"XsuI3uQRTpO1","aqCxQCwQmXUG":"fxK483t9L9sK","etSEG88mdhFj":"9ZECVQAJAIc9","A9btovsSZLj1":"A8TwIpuNdJBL","RDy4BhBYcuig":"dCrtaBPs7w1y","N0oaqG5U0ZfR":"6Qs1LNNMQK01","TLRjHXmiUieg":"7eRkv52gXUXI","N4lUme5E03By":"DIwtAPOixEEh","5AziGORseQ0x":"EcLAQduCahWI","LusVshFXIUGd":"HRGC2QZByYak","h9ubgOSHfDy0":"en2iYnxQfJ98","1IRMfJYrkduz":"Rm8ZC4aCzK8g","Xe5rdwA2530t":"wVrhKoUYsMSx","7rXjRMzJ4UYH":"njHyqLUiM8uq","X2Up7gmjvQMT":"A7hiot5ZU3jz","26Q7NVhk0Upd":"Myj2YJn7GUx4","f7mnmBrm1h0a":"jwyAgSJgTjDb","CcwvcWm4aXTJ":"K9PyCnk0ehfM","vxpvjOBrPMJB":"uNxMCjEHwzqU","79U4UegwriwQ":"cJjd00TZTPOC","rAwGwuJz0Byp":"kiCb0IGzCRIw","Nqe59WRfm77k":"wR59QsE3QQKo","Y4slTwwRqGpF":"MgWasNUNgMLZ","yyJDY9C1EfZa":"a1UWSPiSMAey","RH4v1dnINflQ":"4f758nyJ1Lql","J475byrw3aNZ":"CRoU1ra6fvUb","9cWfxbDc8AwL":"4fVEnccrsEcm","DKkbsYDisI6x":"X6hYyBIFzHuG","hmVEI4baYP7q":"mtjj1OZSiCC6","WdspsYh0cPYL":"81Z6kQWNIhrg","LzQ1Soo1sPGp":"5LiDsZGCg7D6","J2sIHDAHuyVx":"3Eh9NEDmnYsO","D5FQHf0DIEZA":"qvyGrKfgcCej","C05e6mhbhDxv":"rBlLhFv7pp5G","H0wjzU0zch06":"UCXlCY2f4EoI","NRrkc5qQhDlG":"i2JiU6kedxOu","JYczWnMYSOn6":"NZ330iLIpSXz","lwQ6exKuBP0Q":"xlRzNECyOom9","thGYEyNZEvTs":"e9cPWfGfHGXs","5IP8RJQYmvUP":"iUbPVHLBbVF9","xOlki7pniKaB":"gVhRXXqOjHIy","rO1uu1bX7f0c":"k9U7wO37Q82L","m3ZhvoRrEWcd":"DJv7P8RK4jtZ","zvH8JYpdnUIJ":"fIw5Kl45FhwB","eQ9TX1eN1fUO":"003Zm81fl9Bo","e6pu4ZY7HPk8":"fV4WiS6Ga4pi","0jxT7X5x4ng1":"TFpKtFk2DL6L","JW1wi2xvXq20":"iS5wTUhXCvce","6enAVpx19Vh9":"h4tttL4BqCmi","5lAPaygAud70":"pjIbIImkSbxy","0rKKfpycnjDt":"7XL3447fSldP","q3Z2xAvPpTLS":"j5VH4It8KBMd","VhociUVAst5y":"1JzYntvoQT6b","e9PJ7xYB0KTL":"aTODAEnZEFKt","baEXrzPeTQ7z":"emBb6N1U7kB0","tKRyAvsVYF4n":"haY0O3DMakpk","6DhGLjepL0ix":"iQ70zag0PXK8","VJVHPhOTQHmJ":"SMmXWkxI0OmD","jvcxTIbXfRLu":"9JpUp4Gq8Bw4","bjVeSdMZN3GJ":"xByBTIM2UKU1","GKB0v4eO0XDU":"YcOSgr9YnUAo","TNfcavvYq89k":"XbLHodfSs6MR","heS7sqEsEip7":"NwnCq8jF8HFS","e4Lh3ocn4pA9":"ryFHLeuDERvo","Spfn5RoBCQwR":"7JZg7Fw8s2GW","ZL59MHfrbFpM":"64LTbhmbaSDs","BEo2leA1zdCP":"6hfdTi24paKc","qbL6U0dPA0Vb":"5DF4CwstCQ4Q","CP0V3ZHiqvvr":"nvfGhZ5axfFJ","olJcun3hL1uG":"BkWKI2aD6neS","VnURAjqqGXJi":"KKnOybsxpmTd","lxldI34FyGrw":"dcDCao0oiWvZ","OcDfZ0GtgbX2":"09iGiWdH29wo","ZbBPAEpNaJoc":"Q7fhLeuQj4kM","tKHTIUrlXopu":"gp0P8LaP1LDs","HONbXY1EsYku":"LYWTFZarVPtK","Lks3ZjEUNSkb":"aHClA8CvhNbd","jTcSRJ3efFkg":"81HhxCeQpR6b","XFCe7VCF7jF9":"RcGYnmEDXTbp","2Efg0mIFfaeE":"vqJnDlq0hYTy","YBbP3ooLjr4p":"wLRW2B5ue4yZ","e4rcGOFCwucR":"oogwGyQSg1ms","QSJ8YRdBNZSL":"1Gd6NDuVIT18","23pYplsDKp4e":"s3aUecMzU16o","yE8S67ep5y2k":"5OcrIFluhVKp","qh5ht01U45iy":"Rl4OQGwtl8FC","NqogJjoBokBD":"zLNfRtMOZJ2L","EB8wSnDWQkU3":"GiZBF6G5gAG5","7odE3D7t9VIa":"wgWorhio3PSc","u2zKIMff8m91":"n5GL5eNe7ogf","eaFckcKy1Mkr":"aiCjhlmbO7Ak","0LUbEdjDTZrM":"5bpM2cpI5IKf","DfeFfHmYjtmK":"8jwwgouvaA6A","yZHkRrs1nQaL":"plE4DZ1Dz6xg","6TqV4r6UCSGF":"KZDDDuyNeyGV","Px3vgA8Twfja":"KXKpnaCjsBtS","Bg1TIwIDVYRG":"uAm0UXrJlb2s","wLuEiAt3za1t":"FBREhh0E114A","EChYVP4xNgOY":"PzlQoAw2yw92","OvhvfIM8MPBm":"nobiiQgRHRtx","aInoPdtuhqVI":"FimnBfhWDltN","PvpEV7qOJjKt":"7pmCdAeVMRvp","VJrNz6m2e5Yq":"G898HMZGGTl4","Mb00xNiMSkDN":"utw2Tr8K5WR2","3UN96EUSe2LU":"xZowfF95yp0y","waeFbIRb5eLr":"3fJMJcCazSTL","X85oKI96Z8QH":"V73jW1tMWKZc","C7Yb7KCgYF57":"QbRTu79p3IzC","Bl3LDnFCiP6g":"ib9xafRlrX4k","pHoBocjf9QJq":"21lUQvhsHooa","FKlJSFD630sP":"66UJFxZemjJj","awAtPNX3GghC":"3nDXNQJNeKoJ","HIO44hW0Atfv":"Vcfb9yVGkVS5","buERrNcFHhRh":"iskbwfLX8vgZ","94t4xOAsjzLZ":"EAP9r68yEPAj","jtjnmfdHHrfh":"AnAAYHbqPJX2","EhHbwiPPqVZN":"uYDjSA1rHLoe","oyV0DfsnHeuM":"rBa89MUzhVog","FsuX88aKVQi0":"s49MkmPafZkB","wugHgBd3fpfY":"Weiia1NWTDhw","hDxmTD23YGBb":"YjWMwtsqEJjZ","v9sG2nVs5bLK":"FTAbdVyzi5Hg","BFgRernlgAA9":"wQSMN6I4UAXd","ZgpQWyabfQJQ":"nx1jx6y7QtP2","At401ruMnk5G":"war5bfrQWAlN","Bz3dn4m4Om69":"DE4gVRHVviSe","oS4NL6mDpmx7":"AzB0Q2fC4XpW","L1ETkbjfiMoL":"IqSbERkam41i","ycyqlDft6Ql3":"Q90kvjhQpOlt","pimBbmdtJ59O":"DRfjuDaPtrDI","X7AFhfhRbJI8":"mtVptyCn5PwR","aQ1X0SrKA9th":"Ri6Qlp8zNzds","3ktLvGlm5dAG":"IbO114zfWXAF","Uh3k5yMr1Ruf":"1zMIZYWaf3T5","k5ASMvZIZ26M":"OuUMHIKC5JR1","CPBdepe3dvcy":"qRk3ssh0QMAJ","Q7UjrfQz0hVU":"N97mve9sSKsF","6OgA9xL3AOAs":"sOHlx8roquNn","26SUpL9JDUBI":"G8Oz3CXlKMqo","czabqmtSj2B8":"LJYX8rfYditT","7TN0UOKHrSo3":"bf8BpjaM6U9o","UIgKp7AjIsQv":"NQUNc8eTlTmS","H1fK7GWEtlaa":"BvCBnetGmDQq","SipaurHtDoSQ":"3409JNIrZgJ4","9n5QyQZnqkuV":"a2gdzQRmohtp","Hgv6IOrdbpCX":"I8HfsZT9kZqN","H0OSnHDsAhCM":"KU3HjUfKG0zc","EcXK8yTVyBN9":"ZP2qn5IArp3U","YgRH7avXFelu":"wYzmub0ynIR6","BZhwuoXmVWKH":"SeqbbmdRQMsC","WKv2v43XnpaE":"jLgqiTXxToKw","kEAiZE7CceTM":"B7wuPN0LlNOQ","UKEzTuMVZqvy":"tLc5DdzyH89B","kO5Lt7koEMlR":"YVPlFw0yKwSL","kJw1FrFJ2b8T":"RR0STSQdWToE","jBUSjTemqcCI":"YflLpqFE5D3Y","x8C77TPAiM9T":"zaSCF6pDZFwB","ouppGkj5jFoG":"KTjwYstzf6CI","zJzVQgbl7ZUA":"Dn59bTVfEm0s","xU8KXBHQ6clz":"O69o4tq8nzj6","nqX9BqmkUHUP":"cgn73SEKMTe8","jasD3HzHBJyz":"fqyj8RL2sIcd","QXEWFMdMaKYs":"Q1gTtS3VivWD","y9DShT7AWKYk":"eABSdGDLdyhK","24itbgpQzG2v":"l2AF3AnUh4Uq","eH4tV3xOEDVf":"sd2X1Hr2oTpL","ADRRTmkYPXxA":"HP2wqbsOsTu5","HQKUhe8kxED6":"7sdBBWIVVyVW","B99gbm2hy5H2":"8NeSO2fISZMj","6GdxgZrZr1dq":"FYD3ywJGay3Z","xWma1gXcyrHS":"NVhRJQZ59pjW","uYApoVaEXf3P":"aJXS7qlMWG49","qxENM0TrjF3j":"Wz5W5Eu4oHoU","9raA0AQbSxEs":"SAwECOYUzuwc","h475BI1O0pEm":"0hpXGT6NN5ih","hTHTPaFyhj0f":"Hw28aQoldqyh","6pB4jaqTuSr8":"A0NDL4d48puF","yNeUfjkBqnyK":"lRaCB8XbvSyP","W3yoYmTkYeH0":"yTbSLtiEylTI","uOH1MfJQ1iZM":"EPjdRsBtM6PR","8INAcfhASewn":"Hqg2nMFhkq1C","L6DCcodMPLbM":"Zbpxy68kfZbQ","39IiQelOjLdG":"jwSnVyS4NPhE","97DGb8ELlZOu":"Rb1VwOqeJJFk","LGmNNT8XmOMH":"rrcnVo2HBe9c","lPgP2CCj4DMk":"jvj6ngvWEZbH","DhjCqsNySety":"7VPdhshAyel9","ePpBbG0hgCRH":"NV5CSdGCGXJZ","cKBGbMMyDU9C":"RTNzHXHT0BF3","4SdO1H2GpDKQ":"v4xPN9hmTzsy","uXbBYz3EAOYh":"vG0Eq7ypOFjA","9J4KOQqzjgTI":"V82LgiymbHfC","JU2ggvhziSxi":"pw9bPODFLuXa","saP0Igajn4Ev":"09MX7XUkJIRV","x8MIXEaC4zJQ":"FRWmCC1U8ykr","RfbVQfqMw4WH":"wzHJi8p09Ep0","RWUXqks4a6ZT":"mte6KGy8VmGW","BMJmmtUF0bFN":"w1DO3QTU9oII","OqBa23DVgKPP":"fwoDFd2uEVpz","lHbBffuybOQl":"RRomAft1CZwd","s2vDggKAvvYu":"5LTAc2s4xXFY","RU0z46nB62BW":"g05tq2nGEAHf","kmdOFUJVlctn":"1CRnkuveZrr8","Xa0MrsAN34Aj":"MzUtwgKHzpEL","WroCtbjva0lv":"54G5kv9hlmZX","Ueu7BhFLlO6v":"30QmjXM99EUi","ljGWGQTbdOvT":"GJifEljsPX7i","KXfcv7EFtNNe":"Tdh4uZFx9Qua","OfG8v9jobxRf":"rVT1LcsEI77P","5RHF41ODs39t":"Q9jZdfzS8LJ6","72RdYI8Ji7Dp":"K0xpDMdBqdoO","CXN9DBCCnMeP":"03NV54jm92cG","nCalAr78TNWU":"pt3TbLddLsI4","EujaUxXJe77w":"C9S0ZR4nGQbr","FayeR9HdDOCF":"nzMRdF1tddEM","QqlYMXAvSOfm":"oppKub1ESOhq","OBPJjA5fieyI":"u5Njd2oY0Uyl","7gGq0uoF718N":"EYbrEpihDWDJ","DqAMGzFlUXeD":"2NVM6F4dIDFK","JwG8bayDtcgo":"BHVlM8vaGc2w","i7XSIlEM7tls":"n41t2igq8X8L","myCNtYb9qRQ7":"pMG4pMYLbupn","pDmzQNJhKzyD":"ZKJeCwyRPz8P","g7F7hE5rpdqy":"khumyKEdkx3r","QFHXqL4q1xEa":"B97YJWe7XpT7","SSFbGQJvp9Gu":"JL7CF0PVoN1N","9HgRK39riA4l":"tPYBHdU0MivG","dDPVxQvjclvg":"FuJ8fLxnSuzw","pNP5VurosCqf":"VynJgpJRPsDe","w5OQXy9rJ2dP":"91QoV02NadN4","1mVYyelvCBbP":"KQOUXvR52rHr","XAwKIwrLZXLE":"sP9iDm37yRWM","FKljnOE0ZpeO":"OCSOQRsgNy4R","lnbXZJb8Lhak":"GRNM4dikhcrF","BDUnKr2KPsuK":"caAyQudQjuwF","utKGVGCS6hFR":"ibCqkFcJ3YPC","gHXW2RWZyzB3":"k2mzvmCD5uL2","iUdAhVYd0FFB":"CgXpTRqgmHQK","qcoZ0tpc1D4A":"vTBtSCrFW2nt","Okri90lOJbeg":"Suv1SQv7Di97","1gyc8P2BY41e":"7fP6ht5nZqQ7","Xhg9CXmRRvew":"xDPv12XXP4ZI","zQRPm6YyWvkS":"5eQKpHtBP9jo","57adm1yRc1hf":"YSYhmPGDxzxg","yJPFdojJ6hK6":"5ShcAWuvdK6Q","VN63B2ci145K":"GjKODyAIr7fH","WnOZ1qGO3DCf":"qx9kvPHPfVLW","9UN2hiH0Tg5g":"NX4BCURBeeMi","JHldgBTWz2Wu":"LykpKRrSas5s","mV2LNQ7PyN1n":"luHaH7eh2UUp","dImLK6lgQJYS":"3WvYKUtRvY9U","juczYBMfmELm":"GiCV9YrTBbIA","N04yo0Ssohrk":"G3zRBO1OSgOs","Fh0jEC761R7u":"dUVgj0Vkzv5w","kz8lDBto3pBC":"VJDvpn9npap2","zhe6fyCjPTUV":"EenvyhgYQ0w2","JOw8cC6TOvIr":"KY3ldzhhLh34","XIFAugA96E0e":"vRvp0D0Jj3iz","cdCoxXA8Xvh3":"dxfe0fsa39mP","zG8y5IyA5i3p":"lzFKcxcULgJe","Mn4BuMSxUnjl":"e84a8mdLm9dJ","EqPWugeFbyOY":"HNDpyJzHWDZm","OYxCT5e64GJC":"1G1Sh8SkSnmf","5V5NfZaQw7Pf":"OBaqOnTDfF83","TXXibKPXP66R":"HMFAvyy8DLeB","RmYR2Wz0M60w":"I8vCOuKO05IA","472T4FzVHo4A":"d2qWKW4XlPSH","Qdm5zGAANzRj":"4CYYvcOFheOZ","nHUaWORgCBYK":"7kTxhNbIjqFD","L7nkAK5oI5dF":"abSRBN6wyP5j","5ZSC5ZnO2FaU":"julzLiHi5TZZ","Q954UEOBNvP0":"UC9ejKAy9eB9","2xOZSQYv8Bam":"r60UnA3Kk0Gg","fb95L4L4wpQS":"5xHHMYdKo589","FypylRy6gEWW":"8ShcGVixpC80","wVkMvb9E36OX":"1VQ9ey0kHX7E","4f9Wc9mXUYtg":"vM8oVxw374E2","iYtA9GK9ZYol":"4ogLC396ow5I","HH4p5omvYiSz":"K2gA4gS0OGkn","MGSFpQkASFDv":"VfwtAi4KFcbS","vXkbfDPiOb01":"DuMho7Jv0KKi","wk9y1aBKS86C":"XegKZlXZV3I8","ItW49lKTC2sn":"2D2LeewrcfER","KOr6VQ50mPIU":"PNPqgv4I3DOB","DsHvDdFB6Xmx":"nJr4xCAce4H7","G0gppDn1ExPF":"BV20Yn8NOOjF","6o6iv9DR4KkO":"JcvxteRwsaOI","S2E5OETOU8OP":"M8undUtfDDYq","EWGDVzu019Ho":"mItTVvJmUhgN","hQwFpckj5Uyx":"hOLU1et0ofXO","nOiBU2zJjo4D":"FzeCZxkVSOCT","qcK3Q74gNVhB":"gIPuQoHrRjUc","nEEQijy2PTpp":"ECP6SkZAhDaR","936cFUJLD4NJ":"iLpNDGEmHZGn","bhgZhIa6xfL3":"3piN8z8ge5L5","B6HXzc8JqBD4":"oZhu3RimU6qL","ProYbwOVwQDo":"9XxsNAm1FLyp","83VL0knxPiNj":"VJsPyk2NGoir","XSkaT6mt4aFe":"KgVZBwi7RU9C","EhIy9m6AOhyF":"v5ycaHQgyZEC","3VX6wOT22yL3":"UCefXCgtBEvb","kDAckU4RU2qC":"0OJs8wCZRL8d","SitEunSu66LW":"i3rAuTW6AXm2","FfK6f2f6wDao":"UX3LlG0X0WYo","Z4kDbtysZvEF":"q6q95EcJjQmO","GzQUMnPCwNNn":"kGB5jmmmUB9L","FCGMRmSkRiLs":"avcXjX1b3VAG","xaEtIQkuTLEF":"8c7TuHMU4ybh","JShzERRoagUy":"btArIz6uGmIy","rFsI9E3s1G0S":"VeXkItZKMgsH","WpnhENpxZWle":"TjFYLjUkchws","80ichuriNXil":"Of9Zb0nhCW5l","y5Pq5J5bsGRA":"cpC7yA0UtSYM","qE2qKoXu4azJ":"gSbm9rsNeBzO","QyX8kUeMkDLN":"RKRdl3DpSG6S","rF9t3lSHNidq":"MeShSyEyqd9B","sdzH5eHC1HH1":"X08P6aHlDC3u","PJdyPeXpfZeL":"lvxeZiC5tzUn","d50NATkFyJpW":"UlcMThlFwfgY","gYlgdTIAJEfk":"rNlJbTIh9Iqb","nfb1QfXJxxrk":"AHJLpU0IouMC","8EmYkewB1742":"VLtzdeDYIhHg","htavZ8cIpNlK":"un1ARMZ4U6Vk","Nwlc3k1sfjiH":"VE9IL2eKh4IO","IQtEeV8n666Q":"RiVbOoXPYrtz","3wS4L20uHpDo":"GnHBq9c0KaJf","6N3kxcSjihP9":"0JTX0gj1ToUV","gJgTX91yeECD":"eN07egpYJLQW","MN0M3PwVVU1c":"c0MLn6FTjHnq","SmznsbkS0n4I":"0EXvmIhvFEHU","bbSSqvGLExdo":"Kogeb0fVchaH","YwXESrgQ5PXx":"ReIguI99k0lT","GXXY9H2zwqnA":"yTEQdTnsLGY3","zSyXIk3RsMpk":"KHFJl7DowurD","n0ICy3bUp1MT":"XalxmaAvQ1GX","Im85MhjIoVV6":"zgJUTEYagpib","Iy4NnSkstpC8":"mDam1SjHCzEM","NnElgtlRcAN4":"3omREyQOVmOl","b01DzXscwPkr":"8PTAm5vui8wa","NQzmGM6EMojO":"p0WYfhCoA7JZ","W896QvveVWLh":"e1MuTmwYt029","j6IHUcpIGVCD":"4lL3FepNu0pW","p2WG9jp1In9d":"nQEofYUFLCHf","yh2U4gyC6tZX":"FfO612k7L56L","gVkD0DMohso5":"Zu2NbcX1ETxd","2tCBbVI52wS3":"PvQLRrbHTsKY","4NprC5y4Fp90":"YDHi8dWA1w9I","L8b05rOxKfjE":"iQA2F2UUEYOC","ECc4hlpyuZYX":"3CSsMD5XHPCR","YYdQQr2EYM4z":"xaGpNdNgKZcj","xp3NJmnIxBD8":"7fX9JG3vmp5Z","BrBwiMvkfmKm":"w0WDKFUQlbJJ","sIuXOsNMdwtt":"76gv5yl9drbg","QrrQazIgaJHS":"jvQZQDhJiS1R","eaXoRX13jVEX":"aQIPkNUnF0d4","vykTIDTbaN7L":"bHqlTRmmH2r1","jXPSm7pZdVIJ":"UHZjlbRvWxlS","j0YcKmlP6Apt":"gF7nBaNPrqte","8ZfF04cOvvfT":"5gJ2OmeoU66T","omuSJyOjBBMJ":"lV97clQl1YC9","Yad3CM9KRLTE":"XYe8kTNLU5v7","DuU0Vhgaq7dE":"1uB6F7RkkJed","iJt35WHWtzVH":"0nZUzutzJoJu","RbnPxvjKvhF3":"pZBkrRvNWrB8","MGPIWXbw5naE":"WECnvwqMNjVy","5YyclNKAHve6":"nzQLlS5rtiV0","pIzWh4gJrK0L":"HJJ7A1p2S1e8","8BBDK9ijfxGQ":"iMY1EsLyT4FS","InHmHQ20cIz7":"IZDd7qRSZFMU","zAurEynZZWor":"XYAWGSAN7RGC","BY1RkgaKROdG":"8kVRzUqOT1nD","37WL2sXmzP5j":"UKQy2Wf0AKyF","Mx51wI65QaaR":"M5usixuBGArU","q4KQwPvlxzBD":"1mxh9K9bL9tj","80oPE4xplLK6":"pYfPyVLQNY4G","WcP7SuhSc2fv":"NNzQJIO8ZNXP","PtWf1Pqv3RmN":"zPx3CHbEWFFL","MoEZWqU9trPl":"YWtlW6YkSpAg","IOEovipJUWOS":"xmtYY3Jm7qA7","QwAY4cV8uc2R":"IxNNCwfNe5uc","4SMc0zEkAHFS":"cDNlJtbfYOaF","ThnSz23aFZVq":"QtfgTAgoMd82","9aKb4Jy6XTqe":"JSWc9GIubpla","Jj9DKjq7CJ2H":"w2OxGxOwdaKs","0jTWSkdIQwOF":"cD8lhEZGG8Gy","3E00mq58q2Vk":"wFw7BWQIjJxx","N5tpzznqQcax":"FXIaM42YturB","iLFIUKhADKmv":"AG7cRN6vbMAY","R14b3Kcfdbsf":"yOifGnSacAFo","1UmvokZwboCK":"W0LRmOibxSbs","NoWbhdc0gjUO":"c6b3vjVLJTpn","1FgEVaBnsPBd":"lqIZr1zptg3j","2vaHoVRIeDwi":"SEbB4aGlPZjw","j6IbObGaHNaH":"AVjHmDOA3S3z","CAEJ757FCKY3":"5xb222FVbhOU","ShDxKfh9cQvp":"KAevLTfew0Pt","ElabO5OaQwpZ":"2BjET2GvbJjC","lBIzRxwTRf9H":"oWyFyQuNRfTF","UM7uCCqYlHG5":"jUqgiQTjLcn3","dqf21IeRBGWi":"W66waLmQ6mwQ","zqhQhqRZjq2B":"xf4qlfXWysyK","ytofhMqYXMS2":"ouEJ71ABzL8q","plWEO02y70g6":"Z76Wq4hYGI9n","cYX5cI6KHT7C":"0JjNOjsG1Bqx","crsq4Df12ECz":"pB918l0Z91wL","AYsProce6kNz":"oZhdJNk8fn8X","B5O5yqVOll7q":"8Qabf3w3EjT2","M1Pva90XK0c1":"NNMnUyFF8CYh","gjnPRNBE8stK":"ZydRwySQ02iO","mPNfyg0luYnJ":"b2wamSrNHxOm","OT1Xw3eNjm1N":"sRBTXsUjBlkx","GRlNBpkFe9HX":"JQRuio0PEBIp","Dz2lgKue8Fk5":"h3pqmwJIfbec","LBbQzCg4yNFZ":"EpW4UDBb8h0A","SSAD44fqndUb":"xdstkJRYw8oH","D3G0zpg5KCJR":"ABxxoy2UtEaQ","UBCBR01VeOpV":"hHIPkB489RIe","TY4Z9zgQW4sF":"aUZlsaecHW2L","rYv1IkviKLxs":"1knCZUh5W06D","IuyPa9b3fFoy":"VzNDzMMU6xdT","wLEMnexvs10j":"jnMYENwZS4Hb","UbDE51r3jTIA":"Ro0LRAZTpnBX","8OEfNJFK13x5":"YixO6BcBPTOV","wKaZIiWib4cB":"ieQ7DKlVxWO2","64Lkv1NEJcSe":"86V4ZTYuqZuo","J1GoOAylb7Pi":"I91IPLjlShY7","R0Yrpr1LZHVK":"YEUpNySnjyI1","re04VO8FTYPC":"WfAmc0cWqBLa","lYhFouRVTrPu":"YysN65ujnSJ7","BmniXbH4CHCZ":"ri28Ry87Njg9","4ssiuKSnpwuo":"NqzNiAVvDzoq","HfcGAyYBBthZ":"QUgbHRgsnxyK","9iHepwHf42Bn":"1IDE7kRSBPsF","IJ3gQSBcrsn0":"hUcxaaGKOpz3","tLgULJaMeLKq":"E5yWwpdlTeWe","6bhGMKby8O8j":"YP4DkBwdIPZw","dFwUA5OCB9AH":"zFLpuWOW4BKR","JqaQXzvkxq2b":"frrRIib9XScY","TO52ojxyI7e4":"WeOxkOE50zil","UMa2qttkvPnS":"s7Rp68DvGsrl","NxM2fS5L1I9U":"CcJd7CkEuuRY","vYNv4CD2Z3Ma":"qXgRJmX3Y9WV","RQEP2BdybuT3":"g5mz8sIYyEpo","9WvMaqsGGfZ6":"0ns0P5YnRM3a","4O8RZ7XNeDJi":"2ULtwVft7HGt","NssNuzSWFyck":"obfxD1q11HNb","FjCvkT27ZgJC":"3s0atGE9tDoQ","bIrzIlmwsj3I":"bQmp0fxAFCWT","pXWreFfrVqKD":"9qwxQikrqCTe","08YPxkYPOcsP":"WGKBhAXcxBuu","f1LT1gjJiBXN":"QGGznDcixXIi","9DbWWuYNUkoW":"KvOWJemNais6","BDOcZ6c1KFJr":"AE3WyYKgepl8","UCaCqYuDi8SN":"6Gp2twDIFrGf","Aj6g4lTorsZU":"7UiCdxSQWx4C","sBBqILk6lXGK":"MK8S9Z0C6Js3","msluoZpSSfsH":"hlqssFKJsM7F","5wqsi4IwZFeg":"m1F5FTWMCS6d","5K3HMJnQsFZr":"ZSEFrgwRNVU7","f3NP7lAyuYuh":"y5wGYrQyWH0s","H4cTu3l67Yrb":"PKzOnRBU6yGO","xM5PCeQinPYi":"jb7Wg2mptXVF","V6knMgwnxYKv":"cBz6oq7GxHhq","5hwmSz3xEK8i":"QTFY3uTNJGrA","YqM3rGdZWil8":"EQkWUnpY6iHi","KBCM0MLer0k6":"DUrdkNwDC0LG","tGntDbh8Yc2P":"XFfmUwrGlxah","n2lgeYKt80FZ":"u7EtakIkvqHw","aV85DNTcn1OG":"CdBuwWyUuf9K","LsIGbfWGhS1A":"O4qzozVZpXGQ","o28KMBdcD3s7":"Z7B3SHtPkJce","748a56wiaq0V":"SlPZuviLr2rW","URsmZKC74etw":"NuZNE9wfoCgR","b9btpSnR2q8H":"cQUQcco3ppgD","6aMYZ7jMaD6v":"l2B7vTmvK7JR","Du9ZBEJfnluw":"YxDsGHBAW3fb","gpS6XTIqDkYo":"hDeV1OwoEctq","d1YerR8wqc8a":"cwfnAdC4EVld","RqobvTSSrDpR":"DW7qYKHSSYiQ","hORmxFtZXrfh":"aax7uFjnJar2","SoYL7xu5NB8E":"ycenBNwLBIpU","KQ4jEY4JSYrV":"USLFCrgEqZ6y","yTTxFuLRvEIF":"FWPLTcO7mvGT","Xk5HeJLiU5RO":"V3iyN5QSJpv0","MoIzhIoRFY30":"mWnxOqDJQDEV","ga3BXK5kl2iO":"LqB20dOOvFZW","uuvz8P8YtdyZ":"vdTVshatv8mx","PqWgbQccn1G1":"RQVflTfmZJ0i","1QG1HHa5SdVD":"uSZ7firywpWw","fgDzPccz8jJn":"NKHlFB26pZhL","KwFVrLwJyXxv":"xkDRx89Gno0g","8SItVFIoIIHC":"ed6lxx1Gv399","A55Lm89ZPulB":"jeyaibWmYJV8","T2l7lHTGVCN6":"Cd9zxjWbJEBA","eagHoOZUz8Xu":"qSmPaobRu7hj","KsPUu4Weaun5":"nYjOk6yabdHk","F0qjFmdr7lGc":"oCDjhR022C85","jleaMjCl0hGq":"MTZwJR7xb5QZ","VdSwzuaSrJY5":"1wJGENsWl1Fa","fFthg1TRBxL8":"z9p2vm6jobS4","RQ8nSuNqqDuo":"Simo56mlLWgs","gh4w9rRigyjK":"UNYgN7T1mgdD","a6tozBDxwlJO":"tUtyEM4Z8XmF","EeLNu52rp4OE":"F4tE7mq9qQXw","ZqSqLUHRgdgJ":"BLrbyn11IapV","wcLQTFxtBC76":"lyqFiHh0LC70","nRYuVYJL61tO":"Jtpt9dqnzEmB","qvicuBaCnu8C":"MTf8vigbLEnZ","HFSyD73mjEWO":"e6FIQaTzJKF9","p67VkSeay4DA":"EhEj4Y8SJOGJ","GtnsawF8t3yX":"5IXbgM0oWWLl","t9TppFK72Sta":"moMOD8qvIgkR","V7XgGV6fBBYs":"h8eyEN5tn3v4","te9h9r3J0qhf":"TGBCA13ta0nb","B31lCIN4Tai5":"WdyCgJ5aJmhQ","d5IsJcpqHLcr":"wUlKo3XayBL3","MJ8ZVHIuoieU":"HugKgzojgrE0","1wvAPuVP4HfE":"4MNh61wZRcZw","NeVLQiIUKWUu":"runPlosLi6Ba","QQP8FzOMdPNU":"eiYstd95uSj3","T5ffPm9Esjny":"vCDvaUhtUSj3","PdNGwkZXdy8v":"WZCFrJ2OmuL0","R3T1HFdXpX2O":"sBnQc20OlXLj","oi4r2o7QoOXm":"RnK2UM9xstT9","2mf8YhYShAmP":"ZTI5CUL0gTu1","qVtdv4uxIg2D":"Qm4Cyofz1BkB","oApsvxduNC6q":"rdqYP7MCs3bk","lfhLFPhWL6tI":"JMq6kQvCT7V1","DksmtHZRDsdX":"8gf7b674l90j","JoOPsiReBfPR":"xkIEsgXhqyLL","VaiD4wfLdO1U":"nWeimYTnpsrH","CczWXUeA16EJ":"s21h0NkJTv6Y","76bcF0ENjxrt":"d7CZGR7a48EE","S2qy3o3bP8vd":"4fPZEGEasEMc","g0roH5Aj4y6J":"lecMPeg0H6me","m3n78B4wj7QW":"3S5ICkUGyW7l","EJpju9DUB6t1":"ubp5njRciqUf","KHoeyjsGjEN8":"wjG9kcxOfcUy","Zwws3sLKa7B5":"SKMSmQowbtfk","wApic8GeVtnV":"JmMODnXmVF0e","WjFCzW5S8WMe":"YO1q740qvLJs","HtzKP9PbuZuf":"4byqt2oL0CoK","JfsWxog7lkia":"E558fJjjDrFm","H8OkWhuzVZNh":"7jUS9mRmrEaj","2iqZ5FOlktUc":"cZqMxwmAzQ9j","QCK1Fag4elwS":"O9xkBT2D1HfF","hQKIJycLcYjA":"iWiHwb49J4lJ","46XkiQ8ytoHp":"4g9m5QBuFOcg","meESgbdGYJqb":"br7Vc551fn5R","MSVDNCUMyBkr":"fdsujnMkSAr3","0g6MvHp9dtui":"WwAQiAf1mNIJ","7C19FqqRYyrn":"gXS35v6JJ9JP","KPvZ6KneUh99":"eZPVbwRhF1KI","ohszCXhrxoMd":"yRTXH37iHMgJ","iZZgs37rd4dz":"RSQ4oUY0V9ls","Wo2LntKrJX5j":"KhamS82R5rn6","fbGEo6W2UbI7":"I62dKLGafKAY","yQHnEXGetDzG":"qp5Xq2aeFqjn","pRcrn05yYx9W":"Rg9nANDxOwSG","22g9gTsKZgS4":"bgGC7m1tZZgq","SWuPYyK59JEv":"4EqOQ3EJ6NXq","JSNyAOeeIoOg":"ePWGC5bjG2vX","jKKcbLayOXXu":"RsPnR7IixoFY","xzMDwZzuZivN":"Fo6BFVpCFn0B","tTMNhrDnR7Cm":"eB7KX32Yu4mA","TCVW9LvxFGKu":"C2Aoa8TlCY9N","zcAnlIPFxZj2":"16pqdQdexvCp","iu26EJ6zmNPO":"YLZQw4NA7Cb7","9JnSqlXzmyBR":"KIB3CMPqADpg","BvbEzGVE6Ut6":"FndjSybdhdbj","htZzm7E3XC4G":"VCod2YsGqHR6","4k3jbxl4Lun9":"MDdSmNTWTP3i","IvVFdVy3AHCn":"NbjnYJx29jRu","aER1PSALrQkw":"Kw8009mV1aMj","8tMOdFq6e8S0":"zCG07DIpjecc","hVZBkt7LeP1F":"rAQ37cXY6qRp","wBPQ5BKpKssl":"dwdhsMu3RPhw","ujN0fnJTj58c":"R16PjQVUgxmZ","0SHOvYHQKGHp":"CzEJYo8eAh7n","hlW1WtnRDqUZ":"WasSwnP233Vx","LcqjSSjTkJWL":"VWp1FLbbWFQf","JOqAFxqEfnyO":"JdBT8kcQ9RgC","oT5Pd7RxLrip":"XxFtUvWOPKm1","Z1T4fYsME9bw":"P3l7AJwURAVM","jt4AjRfIbes5":"nKbD7eBFyREd","z7PMjM493fQd":"CyEeo54UOpJv","7OvM6CdGgFtt":"VmrwiyuLSESy","OusscpncVKXH":"oQ95jVgFrVrC","fad7KGX5drpD":"ziF2vRh6zuXr","PIwoNuHtfgp1":"SgLC6wOs3Aev","6MeNXuFEU1xd":"hGqszXturoxH","ENkSe8rgXbgB":"YfmtJdWotSn6","QL0wuMO2IIHi":"FFlriBRTVysa","EHLlVlegg0Rt":"RMuPXxgFaRQ2","jER73LkFjTRl":"CZCMtg34qlHX","Zjm5WghCD4qW":"uqt8GabcsdC8","Fi5P0eBWDyIi":"gzRcV8Pz7h9y","xs5Kde0jGoZm":"THa9aSoHRqFe","ODvoBf0udnI5":"aGfsucyJ1PDd","neQA0OCrWqYd":"9rfNt730RDTj","uMfCuBVs1ne5":"z4tZxIwZCSIU","xkYaTP473if1":"zrKHIUfd1Mys","k4IoUsJmXs3c":"vAfFWFteZxUu","d32VzFeHptD3":"WNVpeIVhUir5","IfEgcaymJubI":"dkX8n4enpHbB","sJp2YlalyYky":"jwxipJUCRPEs","0ef9py74yuuH":"J9MvS944J5GU","MePJO3vS7psZ":"enwnM3qZQnAJ","JakpIFPiOCwy":"qfdJLa8WDtAI","plpkmHRyNXb7":"UxQJEFIjJ7uf","sPgAVu1Nbg0K":"1nBHbXgf28gF","gkuB8Y5Lgb7W":"5x8qXeb61i6B","jfuezGMC3Y3k":"ksUAuYOAVVRW","M0E2X0RbWAq4":"AY2AxYDHdxx9","8D24f4TKtNwv":"psTfQ0RKId2H","TSPgk9KepbHV":"bpvOmaFxvdLq","2lyvxSZ0GWdE":"6S0zC95pR3oo","0nnAgR34fAdD":"69a3F7Wil5kx","0nv3zmgolHxo":"Jg5INjV9nLnj","T2pd1r9pIdvv":"Pc0FdvzmYXRz","ZT6zZ0UxQJ5L":"swszu2SiRqfL","Aq5CiV3rx4Te":"kwemjLBdSnmo","p77K1nGcrDxG":"vTHLvx6iLwFb","ZxQfCpone4cd":"ChWjaFzP4ir0","WsjMQmW2zhM5":"gfphJ3VcbMAk","CVa8Vm6pXQKg":"KC4WgLB2SVnf","u3riW13DJeqw":"U7Mt277rF7id","qVXAO41LvnSq":"SRFNSBG4bN9X","WHRDP6LzgY1i":"yjPPehB7adFS","Hw13uKISYv5J":"hBhJ2vRrInXB","ifo5WJNXRiWx":"yQ3jnctHOo06","HYPcBdrtr8Jq":"ERT5PCvGkTUX","gdWALIibneqR":"EjidyJyS4YaI","UkvQ9PORuRws":"kMphYAXN7FAn","CvkiB3JxUwb0":"x7l0NfkrUYxq","FvWJxjZ3SYDa":"1vFKxeWIEEVe","BjDduRuFqvvz":"k1uDwv13keZv","ul7JSgmn6z5u":"iq7XVwCox9H8","YIYdmo0ewoS2":"GJCnCIYnKXBp","Eku6CETymGnb":"1yRH4FYRWGn8","e29H6IZCSAWq":"rg76gK0RkJrT","PgTeSF5L0uNu":"cJeC4p6LlHcR","Pwqew0A9NuXB":"HcGMXKlU8xWE","PpDPJ33gE8tW":"VyGLTinjJL0Y","U9dfUV9DFjZp":"hQLfUAL1ih8U","0PyPP7UIcBcT":"9pMPtZhsRfdD","fRKSkrxV7Jbj":"rSAAMA3JL4Fl","kUsS96kLEfj3":"ylmMiRnF3XY7","OBH7JZ1BoCAM":"DXR24X0PT5t6","8220MjM9P9Mv":"VnGyznz821vg","MN2RtlorPer9":"hc0eP2smTMan","aAHjH4i0jbM1":"cpJnrzrQ473Z","uBwnf4PF8QwS":"vmO6y6OJKJ9v","RM2CQg9Kv7EU":"qhCuQFpdHkqt","n9zOy6DBu16p":"MVXZnYDjG7m6","pmtlS7ALWgjg":"80EzcpuWTtPn","d4n7InOXp0qL":"SSFjYdHp56TH","1pjVRvK3qYw9":"N3e8Nb3KD46r","3Ey0zws6QI8k":"Y5Phb9D9a0io","NbYRHXe6v5Od":"E9hE4fzQlEjO","ZHvnhTgYWgTm":"AuSEwYphDRpW","2dSyB5Vjbqp2":"nDenUMrjcV9i","2mDmp8CdfB8X":"KaqzHbgdpOOQ","00mEg1100clt":"49CziJkrUz6V","VHFbcCG2tDyi":"qxNkFrOhLlYX","bEGkqozq81L3":"KsY8WZej14qK","OMokdT72vurw":"6EDT2PZVKGQL","EtFOkZyvQf5y":"GMDogj6XlN75","sfn4bZRBR27J":"7QMtr96ihU4r","56fLuTqEi6R4":"kaYZVmujRWez","4GgrGl9CeRy7":"wad4NhgKkeXd","aea2VvE4CKyv":"26Qf5YrIeSqW","b3OzVdFBaLL2":"O901BtyOTTIg","rKJo5FV4f4qZ":"mi8xmy9XRwsU","scZQZK1Nwayh":"uzMHicAuE1E1","hSifUhtSlS2u":"e5ENLtXScNYF","Q6mvEXSpVNFY":"ThRE9rggW1CU","LnwOv30gQiDG":"xPECivVvD1a3","Hz8nvaOjqQCZ":"60E7nRF0i63j","rrcm1Ooc3EC2":"xbwD81f5cDpG","dlgb4HyY89b5":"LXBZf9LrgO3d","HlDod6j5c4pO":"oGLMPpOUj2fc","8IYD0dMJC95I":"t4soZjXdUG8g","lDAFVmwx1yuu":"PG5hH9ZFcnz6","YxZbewU5NEGT":"Wo1JrQx1yh94","FHxuSx1T0rA0":"jMYVdfHSFJIk","P6Pz4SZNeVpy":"7s0iWl3HMsRy","Nolz0DtdkALn":"JULAafyb7Sef","fUD4dAnoWDVO":"eKWa3J4lj576","aYTp9sVMGImB":"SMiMT8OSgabz","bVzOmcGqdzr6":"gaI8spHXTc9j","mDqYP8Dmd79Q":"2qfl0mypnV95","zHeZuZm9RE41":"EQ9nNUBuULwq","RnC6qzqPtRIm":"6Jwl9JSxHFF0","7wBongykEujp":"erwGAAnhfCjQ","WIDOYIpyZHjw":"DNYzak3g40iM","wZT7Rz0hX8gD":"AHv573JHtc3G","HB3fQZ7ZPWXi":"qHcop8YCkCzY","1KxHPvjxhOPz":"tyu6n90hKh2m","VUkFu9lImlEV":"NrjvC4zvlUWi","5omAx6ZP2Xl0":"fmMAKuxEL7NS","ahO18A5vevgM":"vZdfeUWZUI8B","A8io73lSLes6":"UnfeLAOaQ88e","BWN16D2c51OE":"JABZrtAmWBSb","af27rG7S5ARW":"rntG2YtjfdBP","xtOz1X68yXwZ":"BbsXgclxy4TV","f89aqVcRcHvS":"WsFt58iAP3W8","mctVB4IrlPYk":"4Yu12GoKYLbA","PU8aHAK5LCfo":"55tFMHNFLGL1","ubTHLmw5X4xR":"hIl5iYQ7JFW1","mKG1Us3H1MI1":"M64secqzcrXQ","ZsLpb6zyCdAL":"FODwbupmgpwy","IUDHep2LJM2p":"Jf4Oc0DTpPe3","jsuJ6rYaS4N8":"jeX7oxz4mV23","3WeXo6KvnBad":"D26XQ6v9igve","siBTOa85SAhR":"zxkHSbK4Mcay","v5WH9fHlf34J":"Zerj5UALMMuJ","K9fGsoJRFbtG":"Hgmn3sy7BZcv","Hg2zWrjZK7ct":"JQ2NILfqWWp5","D7TYMy1wfKoz":"9VMDjHan2lbK","AJEQ7JTSA3EH":"fzGNaXYt6Sh3","B7k8Opudz9da":"2sane0wfQSdH","RRtF8y1ou2NP":"nlBZBep8IuMQ","dsSmCcNwFQyC":"KWH8SO9J2FEH","9RBOt8cC7Zzk":"vX0f4oQ1coh7","kiuaNMM3ZKQg":"b4Vm8GhGYMzy","1Vkvu4jEpryd":"QMcLlzGPRFAb","G824nafJHoEw":"Q0L1ihb4z3bz","nxvUG2PBc2e9":"w20APqexK6yy","oVxg9DxmhnPJ":"E3WDdhHgkNYl","gTmBg4Pe8SyY":"qEbzpWNLkEM9","MPIijMFHaIS0":"dGxAqaa0xhuP","Ilv2FO36qVPt":"i3LfU6pGmpjG","p16Cd1oM6Nkf":"gMb2F3LYn7zg","dUIBzGWSaM0b":"HzNuUmOAsl5R","4fTMRu4Xyt8d":"K0cdUJAUZzBw","TpWaiHRaWpR3":"HjjluNbEC8JM","N7y2c80PlCdl":"aZtS6ncIbwiV","ZphvdoJ8CxGQ":"pxnjcetRx7NZ","urYoXfbuAxra":"PfgmIaRd9l47","pu4Fo7al0knS":"4Ehm7NvtjKCh","FGvsVXO1FLKH":"Z39MmceMWzpc","nbraI2geQnTc":"4xOo80PxboHX","sBAd9jj139su":"mVUKtFRqwkeg","5SVo6p87XDAg":"IHEgrV3yyNpR","HrG7oORDMxtS":"2aMsQEAT1buf","6GWyMFBO0b7P":"gJgR2YoZNMr5","2EKTxrBcs3M4":"AvPsMGOKjspS","VRPiORAFlYVe":"grGJG248Y8JO","yuwJoBdFtAoR":"vrs1gDMT81tm","l86iRhnERYzh":"UrNPJYICVZH3","TDaYiR7Y0dZJ":"SDrZo7LxDJ6P","HkZupgDO8dKP":"BubdrXXaiIuI","DVPA8ZxbRYfJ":"Tg7XlS7CorBq","h7IF9dXVILNy":"GBj9JPubesak","08pnwODidWjw":"55HDZwWvKg5o","jpB9UGopOpnP":"96qKxP052lJU","MAiqEdm4RQrM":"aViw5SOeLIxh","LZPQ4y3brGXA":"qvaFT9DiXvt1","1tg2Nu2k3O6M":"plFx3UjHPeFR","JKjVPp4rGbTE":"Gf9bsIwxTTrX","RzOVoHFoYk6S":"0Jh6pCbuhPxU","uMNHXAqwEirK":"vzgjsTKCJfbr","4qz46tw9Ge4w":"rCiy6BrFmYbB","67mtoWSCKl5z":"QWiD6jOnFzA5","O7IfemMT2odL":"1OpXNujoqj4D","10sTwR9rdrE5":"wp3A1JPaE5MA","jk3evpsCTnKI":"gOfoixPxCNRY","Ea9HpqL9EgKp":"ecwrgzQtwuU3","ccqvIsinJ5d1":"H08YgwfYHvWO","MsedFIETkHWm":"ZoSP3uoKihyn","FvT06IFAajqV":"i3barSG6Hiky","y877dhOvYFfR":"LX1izEcfGiPi","lbW1ydpLJW6G":"fxVFWvPD1pV7","2MW8vzYsrpOu":"eaAuJWUWYXW8","roRXzO6Phfus":"n6Gt7yTTnvbY","6WmqO6PBDcLZ":"aBmrh96dTFmW","zgcm5p2yMLqB":"rLTkMjZxjgnH","uTO9cIGynkSO":"XVsG4OqYZmdo","g4j3ElurMumR":"dLHGdbLL5iCW","9oAJ1vLd4g83":"H7oiqexWqgIh","ZJkU0Tgfiflw":"k8eEdZLpXvtB","LN03vOiTXCRo":"28esDgMmQ9s8","aSm31WsHl3T9":"rSl4i9uPJ3Tn","xYN2whIUnE20":"6fLckD3t7aaW","7qUu6Pg1LCuD":"5jf4IBKip8WA","z9seJYPFTq26":"2ZEM0Pj7UbAE","LdNyVazzl9bs":"g0rEOdYUZX7f","4UqHRp4dfzJe":"nHtc6tC1o6BN","u2f3womELbWb":"3oVLi2OVk8XU","NW7SxvaYOnyP":"ayIcI5NBjk02","UfrRG5Z9Rexb":"BXDsGT6IahL9","W5BBp5L2uOTF":"wTdD3l3Vbzs3","9sISMdKSlVqq":"AroHN9LfD6UY","kNEO3yjx12XL":"vteUjeqdix7P","JoeBoVK8G4uD":"VPvkcyg6LLK1","WFYP2jwxEszf":"wk8yifG1C9HF","QOHhtBwPGlJQ":"BPL4Bczrr0ce","kQAApKCV5xuU":"jZR4ayOzYFKs","EzLHDpOdJAqy":"UPamkn7EUk23","ZoCv2aGa68ql":"Gn5o8HHx0ErD","6CZOoHJKO4Ek":"qRTMbu0ntRye","JhTCkpRAxyGG":"Ls0lEeLkPY7a","SCIMCoqlgg48":"r00iTePpSSAd","1JcVFDa8oaWO":"xTDfuqQ0ykfv","aoKP5cvLJ9Lw":"O9LbrxwFPIRq","YMMAm6bomxEU":"IFb7yAVmq6Uc","Tx9UhRHcSRVf":"81VWf5z5m8w5","IFgY1EHcZdeY":"6PmaphC0pkjF","cNaWSoo4BOUZ":"yGvEvKidzNgW","yTcm4mlLbJKZ":"4fq5qaHA2hYl","L0lv4PEwSQi2":"IIaQnluo7SV6","HQLc5w1fR3LN":"OETxXYaBXilw","4msqFM3p8K1i":"HCB776xftkYz","mYglchpBtmc9":"lo1u4F4w1eML","jjTeUnR8D0MH":"6Ssuo1M7l35i","adv8UQT99dtN":"9xNStbLRQyxe","Ao6qqdK0lASW":"819hGNkybGAG","AWeR9H3YZOyf":"A2MMxAbynZOs","SI9KeI7V7BjK":"5Fe4Ui94OUbO","x5JSjo29W12f":"HkXEpxColYtf","2sapSDY89qoi":"x0iEjVPbrJCp","bt6ZQZzfVvPe":"hc4buY0E1RrQ","J4QNKqFmYD16":"LpExhJR1YY23","J90W3IvWFocb":"V6o2HLFwhgAx","ev92AhgdofUg":"4AIbIGK8jXbF","pSoFgNKsDTsD":"NQNqJPccUDfe","dIjQPvhJHcxz":"vaHiUDkLeRPd","rKCVt3RObPFB":"DW3PM9S1j16z","eIxrFw8qdJmu":"4MaWOfXbfEUt","NBl2QJHeEjAf":"meJr9ocX52e9","R8M6EUtK9zXT":"nbMwABFykLwR","GmXAAmx7Fnpu":"Ls3Wz4m1IDjj","y7cQ2iFzNyZN":"ZjJbDfgSEj3M","H9T8fQXemmox":"FIge0Ouh0cHq","TCJmDVSEUehv":"FJnghGrqtpZm","QshRDa5AKXj7":"l0RIh7scWS3H","jPcWEzvPaYJz":"TcvnYHeBnGG7","XBpPMoQ3YFe9":"NHbCV5AT2Xoj","KTWwTX99fHl1":"lTZMDMlFEwvZ","6JElBnlmjK45":"bdD671D2Gvu6","I6NwQbyx4Kg8":"8pyrsRNycgTd","VaVxWoyPrSUT":"6SviGnQpA0e8","HXUcbXUC0kE7":"YE5KIjB0rkMm","uzH8cfNhh2HN":"rxiFNi0r8hNb","VPma52LZzRNo":"JzpeXfwN6aDO","7fPWKrlVpvyz":"5dq0SZbTeifT","XpluqUMHQWnu":"UsUaLhjw50Yt","GbsR2R1VCT4E":"ZsNfhsd09Pah","uKOHcik548UQ":"Qav9wpiTfQOF","9FW1virc4Udl":"oz7DwhcFWdWG","7hKCZOkBsZ1m":"4kzsnre7J6p4","EOZgNIImL6gY":"ih6tgDPa36aT","aYthHh84vtPf":"fHkGPlctQw2i","VxtaGZG4hkRa":"iIzsG35qMpyj","F1vsmBrUJSe5":"apk0Ks6hZwMc","LUQJeapFvMhF":"cLXduWwzPbse","YSxYGSJ9XLvt":"DkT6ycp5UOCS","frI0sLJCFUX0":"0vbWNnaa8bUC","KbqS79nDrxcs":"FsacIvj2mSrF","nX4kKQl5bjmq":"n4KJuVt6XtzE","AWeak7eWnD7f":"6KiI2jSUobnm","FTvzuosdkIaM":"23l6mDS53PIj","iBoweO20deLI":"pABUcgZK4Jbv","rgMVNGbVShAy":"CwFrE4egW8Q3","GtBPvML6XyCY":"LKqBCcMio9TS","4KczEel7G6fc":"Qp6CboQkOo9O","I230K1Ug4aqE":"2Avq5YCMDr2v","ycCfbJC8qS72":"6lVIhFImeIz7","z2o4CJGrZn5L":"yHsNxGNo3BbP","u7BEEthfr7CP":"FPjYThyEM7ug","iDWS9FbOtRz8":"4jebmkuMnJAI","0TUWcsLflXm9":"HCIVVEnvLNrP","BNdk3WBSnc43":"mzS1wAwmMIyt","eaQyvdNXqzXm":"rqZzbERZvzwU","EdMcBfxF7UlZ":"uv3xC1XqgeRM","rZcRRPYUDdAv":"zG6AexdCu6Yr","Ywi476TUQy3f":"qyTOKMHU1Tvi","y25c4kcYluUo":"YVsGnfnmmrnZ","yKB4JEnLIRdt":"iO0Msdxmdntg","0sqTib9Em4kU":"BEZIhcOH0NsR","Ms7R8sGKVjZI":"tnj84vxPBtcF","743VVAd3IwoC":"WA0K96tnXG5o","9TlpEWWhry18":"LkZx5Ooc3BiY","zx1fIfQum3zQ":"RAczEFeYuSJO","gcJlRNty8rXW":"Inz601aeAK1m","jhJbQnUnrKKw":"Se6gRKYwToP9","dwCXIUQ6Vohx":"JDBpSdpKG3uM","k2QZUxmMhxqp":"nukNnoSpMnk3","RrOuGhB4fTaR":"SlECSvp64YrZ","Unx0AQJmnPq4":"yxfnQ20QcDah","gCK5z5Te6Atg":"ul8vw6LsdO6H","QhDMJzs0GnNq":"3zwKCv0UxFLd","aFSb6PGu3yj2":"Tneco6m9VUsn","2g7P9sQ7Tm3k":"vQum7UzXECwf","lnsfI7JJG8ze":"EGz0vH7y0X5b","bzA6k2snvzaf":"j8LhrOTH6RNE","NhhpM1bQ10L9":"pBhk77Ea8gNz","APvrOyuyu2co":"qAEOpTtNZOCa","uQZCQ97pWF4u":"ctffuS4mjz9z","i3ky4n6crdVf":"PbgQMQWGEya8","r7WBl9bp7tgg":"mDx9BotcKmVR","b5TvbBRq0uEl":"1MOVooLBNlFD","FW2rhPVPFhY7":"eO9FfhVnbAj2","NB4JZswJuBAz":"QrmCHgqzMuGY","BXuuQP6VQjRZ":"flC6yHtdrHm5","LaXotnb8Nuu6":"1YAk6usCaRMJ","GVdDnkppa6Js":"qH9uLCgBcXkK","37ujedCVu98h":"RPZilFzKytJv","99Y4npXzVaU0":"jWJhD2UdfFtj","pQr0t0CiqTAi":"mvunL54s0eTz","Lna6kkagg4qB":"C7QGdfF25NtM","FWJOqNIvj5eo":"FVvSQnPN5dDd","v0nsudXjQwk7":"f6dNsHBFBcuQ","ZdvRtlZ4ceyL":"1L4Mb6PuqU4A","JV3jl4VLZffo":"OlrRVphimFgF","R9swHsp13Odc":"LDuGuny8b1AI","2Z0tz0timB7A":"eqQmbxQYhL0a","CDYoOT0R9aJ7":"mKXneaIh3ti8","MIt6gQxT58YL":"onkYGw4JyMYs","PMeXvicfTDhL":"V6TNpqrr5lKg","TTDH2lYCBVWg":"VxmJu7lyutSg","IZOEpB5k36XH":"1t6dlrBkpO1y","cYsBFNHhfzVl":"eLbcC1M6qmKa","iPln2Ni46ds5":"PffiML8vyIpA","PEJLbAHMxbjV":"VDFIgL5BrxLX","zE0Mk8saA0A5":"gp3ldT8xgk1X","DmJzXKeuklzt":"NgGCmtBKgidU","TDY4YgmUmF8Z":"B6NuGFINypLA","5i86pENPU5u2":"YZ4L60UoqFgd","IHnFBKyZxbFr":"COrHMKnzSkJk","HtFpOyTy79uM":"2kjS8Y9H9zef","gqgTKVxXD15h":"yxyXEtg6Yn7M","YIl90TaJwNvO":"WvdZ1vfK4LW5","qTTW4d3TZEKF":"9gbXDLtfryJk","vwqHfviCSsNc":"pVSQiIzHPAnv","E32YXBxQxBW5":"fdBLiUJ7oIEf","rVPiO1xRFir0":"nitwHUTGcNox","qusvqjQJqqZZ":"xYSqLKETN1cf","6F1NtU7waM2M":"4mNUX8Y2g3TF","f2ej0pfsmKuv":"Iv0ahbwiT19l","9mGZvJX2efqq":"O3JuhI7geNGa","H5khK1EoN66d":"WAIzTMnn0ZcR","bVGrVjHeJAex":"57dEmfAZbUjw","ueuVebUu5uKe":"OyiTgoSe1Lfg","GYcxCmcB72OF":"RqSkILICjuP6","nL1annkqbFrr":"yXDxRgHTmme6","6tujUhwX2k3h":"z9XcjSUJGBzB","SL5S1volHKr2":"OSOlDW47Ygwm","K9KVx9WeuHa9":"pxfALgAA1heZ","4uT2iPgjhKje":"rmRrMNboxhvt","oJwleHhj8wqR":"1gfhkV2XI4m2","VAelDsKOJmiq":"eGyK2Cv5edhX","P8SB6jb6zOjN":"6yWPLSMxqpMC","3iianatlWNJN":"vdSoWeGs95Vu","qkIDJLTa9okC":"HH10qPWjtbuk","ykxhIWGqMkCX":"07pJvtnLNzfm","LJMoEfdOlWcz":"zu7ljCKEEXtn","qIXnmZiX4VS6":"2e5ix5mSVKaE","btY69O5clKQ7":"Y3j3tri9LrPV","QPHW7fAYQ6kO":"CO0IldQKIvtg","dTRYlPopCRAB":"Q7cv31O0bmPc","1VO3hWllYNhJ":"kcXxlnoeCMTJ","2qxvTD2Msqyj":"PBiegrjqLisW","DXL4r6Dunurh":"gTfmUkmszuDX","1vOe4KrVyYxa":"xgDb96r7hTSY","WlsIh7lZTm13":"AHxu765NHYv6","Sz5selq4oo3G":"Ew4FOUim4ZXH","vygBy9ivUKHk":"nIrOTVoc1MNk","ZfFWdCVCSLBZ":"lezOO4VG1VGy","TCNkRz9f6K16":"fDvNjBoT62Ik","CcQ8EYFOSIYT":"CDTbxM8pLeoX","EpEmRe0MFyhQ":"lp4hcglquWtY","X5vGskUyh3PR":"KZczwSTYRFDl","j68X5Mnb0xZQ":"9j3t5to3k3BR","cHK5mLu55uQg":"AhOBGq7Ftxdi","mOzZGALXId4x":"FXy9gQNsUVkF","KGhEZcY2pnFw":"JzHjRihnWHyO","BTasWFhHuCkl":"O2CzxZsMCfgk","o7THlsvLuQ4b":"Wqzu3dSpHfE2","B9r72ZgdZzaL":"c9dP7YHNyMTw","GGOEvEb7N7Qz":"8IfoFGPnafSS","kkRDpx55aVGP":"xVSaLVXY8oHv","wl42DxLcMlWq":"NP0HVg9qvFbb","waw9mPVXbbVR":"cihly1lbPyl2","xL36dquiEE41":"HeC6aGpV6vnV","Uyjh30VlN5EJ":"iGGABQfFu6YK","UTI0oUWAxAx9":"DRFNNR0XQpfn","tnp7WVjqpFHI":"3tj7iUBK5KWs","CQSLBny1zy7a":"mVzbCbCDV6Ur","pim7U5qgArd4":"a233b9ofAM4p","oK382mBH9I1Y":"fmeU2PYPeI90","BiSurBeJDLEV":"2N7SmkOmt5BR","cqnoHPfaQKfn":"fbQJ6bDeUeoR","MxO5tII6IZV5":"Pis61K77kpl2","4rYlFsTIV7gG":"kxhZHUoZOxWP","beLhRc1d45vw":"hDx4xpuxiRf3","5T5D2GOHStlz":"UccbuonWUwJV","HCSh3jPpJRUe":"kIWdFEqYGIkJ","J1FQnwvRYiqD":"coDN0WnuBKx1","ZGNugNOJljb2":"ZVRSIgCKEVoV","GtcAwVtQumbV":"9mxU4ZHPsA11","yRUVyx9bQL0k":"L6o5gGa1sYJQ","nYWElOrEWoZh":"stfQsLC5t3Ty","9ZP1dSoo4hdc":"wnjrAW2r1zLf","obmkZD35cZtq":"9J5mKJ7aQvPG","A38XyGMBoj6W":"ALg3DIzdLW7D","LNsprnudlzx1":"8TrC0RiKgEBU","Ad6deuRPrKqA":"O9cOuzbCzuzg","AYS4qB3cCL2u":"6P5cDz1k3b6E","CuXeKmaFiWVZ":"JjBfIldiO8FP","kzY9pH0eNzEu":"c0wah8v86VgO","NXAuV9eq9eOL":"UzBaju2JDrCP","tt3xcB0SUuv3":"tJB6hHXsmWZ4","yP9AUKgMKK5F":"nNh6fR71iBBV","qL4onMGQRUu3":"fFmjtLeNtaDg","X6zRduUEa7tf":"61HEGisA8w46","Y0puA8V4dXfE":"mb9TuZ8rbDe5","tTfIguU8E81x":"eUkYrvtDsvnz","vGwlnMxI3g3f":"oQqpsz8qOz5L","YfgtPX6kmW9H":"t1SPKjHBTL8p","bFurVBB2woH8":"FPSLfs0h3nUq","iW6VkFAWpHmj":"Kh4aCwI5t72g","GhL0n62zrH9c":"CgWj609Ef6ko","cN9i2gZSLsgB":"ZyiOeLas1Yxo","qAa2y5pGGZvx":"lJSbVFyEh6su","qVw4vBGXBFDS":"FduqD6iugaFM","k02872uzt6IL":"PvmDDecnmFwl","c5YuKWe0rKC9":"B06mFaCWdPJM","krfm0yiAT4wE":"wYpFjTTStGJJ","7tMaj4IOA7ho":"K8L47xotXsTF","dGfcFETHgERc":"7ic53gBMummH","EyW8MFjKOWss":"eiQtxwq8IfrU","ZpQFsCjzenvw":"iCRYKSlgDRqB","Y6Xw3H6YTJca":"pyS3bmmWvYvj","5qJqm2m3HpLS":"eY4nxsZxRvJZ","uhsfGUyjNall":"ywp1ow0ii05Z","mkyTicXL7dP1":"8mtMGSsFG87R","1kSmpILtkgxj":"1mG44aLGQFFH","NlqTitWTJvLl":"MIY571Uo9qva","PFHxhyHxKE13":"RaPMu5QBSeIq","qh12KUEh28C5":"ppCrcndNrm9L","5gVNoX0dVfrm":"xCPlGR86ZOm5","2yGFj5Rpm071":"K5HGQBgheIFs","ULbQOuIXqs1L":"dH9ar59PceV7","7Vmfn3t1mi6Q":"OGkZqjkQnKrV","Ac77M3tnJFV1":"hrPVRyzLUyHr","5nqYvUqM7Abh":"wvznPUW0wrhI","3PmWLQbN9i9y":"DCibI2fCwvlu","vjY3r9vTZd41":"09RZn5CtwFIg","SbxVRMgRtYoI":"IfzZQWBZ7R4v","vkedVEJC4Cvi":"yNgWawMIWnvB","U0sYf0jnxHJ1":"AkozjY6vmVQB","DJEdgVal5hqQ":"Zu4L9SPpSanR","9sTWFVAzzaEr":"p6oIRZ95Cbxe","x2E9jUNCRK0o":"IzT2olscOOTv","NCXyjHeB2dA2":"1vgGf6GuVuxL","9vZfRQpIrv2U":"igEHwtUFrzHH","cliSXfXVXxIO":"l0vG0bTaFrY8","COVWVJXdLHvQ":"3VZZAwlZ1780","pvdH5K6sqP6S":"hjTY7ZBowizg","0WoUNc5cbuCT":"mEfjQXbfM5Oj","1eSsHYih0JV8":"TIpWTd0Bt5H1","ELfsEG5uYpc9":"kAupnM3bt1Xb","ZMUX6v9kUDP8":"E8BgZUx7N6RX","lmUlZwCONRy4":"8v0rHNVniTjM","9uldp98T1QJZ":"6wkNA2Wm3TTp","rSKVbx1izmO8":"f9GeCCiki5FN","bvUVxOMcGE63":"4aXiF3iqy9E6","L1eJFe5dnbOb":"x53fdpwBjOG9","KN7kC5vcdSmZ":"7P7V6kSWUbUX","8tNxuIurxNMT":"Lpgfj0c4UXah","BpuWzMcpSIOj":"CPHITtEMHYYF","rzmcpdx8z9Uf":"TD8FEv8vZ0qi","4cPeZRUv9dkJ":"5tz223Z3CRDi","bPVhN6j2KAHF":"pDbv48sNGTxM","7B2hL3I5CiBe":"9HItpCFBFSZC","BQZ0hRQ6Ilq6":"J9FN1MM82gV2","dfRptBDKROcS":"NFgbVAGlyxyf","gIzIjHJk5LXh":"AronpgVDRFas","dFwcZBMdbPWy":"SUHbeC05ijKn","AVnuvqqPwouY":"h3Ve96t7oqcR","uee2Kf4fUIQj":"sOovbcN3CVyw","7fyryLj9Niqr":"gr7BkxPyZtBE","xasmzgUibt2v":"kY4hpIJbve94","VWSRUuOt4C4A":"fKULY0LF2j33","eBKueBlFQTI1":"JtnrcEfk9s7K","V6so5B1mYika":"HOs2gqcTVr55","7xq2WDwtu0AK":"FtB7tGbOyVE1","E0FoNsYCOqld":"hmtBaNG3nJQu","Mc0H7dZWDHQa":"kcgvRYVTUFqb","OHfFQcJIBBCP":"fWwZW7a1NsBm","bU0uC4c0RviZ":"n9gbximHLPSz","vemtq8SQHPeP":"vDTHpyjPTDhP","ETjK4eGtQb3Q":"9oBKBMMgDaoo","vckYNqciHKhH":"BMgQ1AJ0TYap","GTmpHDzsM8XS":"nZjGMwO398pj","xunOsniPTJvQ":"4zvm4M7yZLgV","X1I9WIl3EAhp":"aSWqQFH2HUVG","WXhUK5OEh2Bd":"s54rNWENQLrE","cROhSeLWO65Y":"9JcEgymIRadn","KunDL3qUvBbQ":"9KSe3F9Bo4eU","Jhk6TZHPpU0n":"1b5bZgVILE9O","pVrbZTbwyTCM":"cF1jkxYgi0ZA","R0xAZXyGHko8":"QrV3RR5OLkqV","B7jnNX4wZFUT":"QfPst7kSeq7X","Rnrzaz6w4KMW":"TQw67biK5OMo","mU3xeSu41ebc":"VfOC0ut5k9DA","UzlyzuQTuDMo":"lIr1ouPWDqTc","oNCiQ8Q4BEAh":"oP3eYFJeLGQS","jHzNC0tIp94X":"mgmwLxo8Xob1","yulOsRC7Tr53":"7nKDYnLjS3sl","pNwPE7TIewFz":"gLAsi7rV4DYB","UfBvKmNsOPcE":"4NBPg7g8Dz8F","jVb2bNyFW7vR":"Rnzxi6goOUKE","BqYXEMCewRiY":"N7MGjxUWKaoX","wOmliQnhZN4m":"EYR7UB8L8RLk","HCKEzOSs9y2m":"tu82tM4SzWcF","NKCBPf98X3ze":"zVK83hL8l3lY","yhDWKFaaMU2T":"XZkARdEvCODa","uOlDWbRwqQqR":"ggyAN0f8NxT6","pYJkuPNSHO58":"2K1abuHL4FBR","bAV1aFtgZL1z":"06Lva2xSg4Vl","PZz0owlVpcVd":"H8DLtePHNcuU","ctnTXsCUQ13D":"v7tc1qKkQSTg","Doqsudl52aX5":"xQLBmsvrs8t8","2FMqLstDwP7D":"IxK2BUOsIyB0","6elbc8HUdIUD":"dWVDyrWqesp2","GpWC0AFqEfKR":"pX2J16n5UV2l","FWRefCHLbbSk":"NFxT0atJzUOM","zjR7Z386WH13":"bjsklUHNU7oA","mI9AwtLs8zC4":"bT9aSkW015z2","HlaSMp2gciJz":"zELkai70LgmJ","s1kKfD2FGxcW":"QP0NvYTyK6Hs","FXiMtyY8ZBjn":"vUK4NTihr6ZO","oH8qgyDuGJId":"lPUib1mqNn4I","7IuScUicm0pX":"VObCvBnFeTdc","tMzR2CBragsp":"uU0bnb8zma9H","PZfuxdukA8lP":"ngyleBlPEBWE","E3JjgrPqhMoQ":"HfI3ARAPF72P","GdADxk5J56xa":"EKmH0lid2hrI","jzegdity9exz":"hwltfP88WOg4","4vWTp1PtoG9l":"Hj681RM4heXS","QeEXf2vBlPEe":"4lkpI9Z50v60","W61CEWTVk3zh":"3XN2elY4EL8H","fMH9MhvvIlzt":"OlvJnRC3YuVA","RifV6Axyffyj":"L9eK3qPgo0mC","glI60wxGt3E7":"FIlJ37deDHCu","h3omIY5IbzOG":"oaOzIB0EsIy6","ELYC9p9wq4o9":"56scL5zYlimC","ss6733pK5V2w":"og2bBHe9igja","kqA4hjolXiIm":"wNrqnWbpb8T6","9R0RA7yRoeMQ":"sK2DfweyZuDQ","BHlExr14gNWM":"fSsIyZ5qnf8a","RQkhXrzSBwaU":"Z1Dvsh4ZUdTb","WSLqcN7pgoOQ":"K1kPaXd7LBBo","ph4o3zinJD98":"9to0Mnjlvpu2","loLuwLSWL8Wm":"FDjDsJNauC8n","Ik494jn0jGlG":"8mbdi2V08Id1","BtfbYt1pas5v":"7NPlpmzl91zh","Hz0CYajQUFNA":"9uCXmMvI4fuj","1fwvtHR1IHOL":"L3xd6x5l6SXn","okc8oZvJ4K85":"B0TfVSxZx2Om","1iPVEEM9lwJZ":"Iq1rbYEJ1Z7K","tOBGtTTHXRH6":"73ZbZXafQAd8","AfkhAWFRFhrM":"OMJFKAD6DBnJ","tlBTtcknDR1h":"rtilT68McRZP","IRqk4u70pVZO":"Sat0kf5gdsrr","zmyB7LE2tHVA":"TgkbFmSfcy6K","Nyp1zbWowx9Q":"oqKtDuSlGx2X","BBIkyj3U4Rx7":"N1V2UBw5W6ax","a5qSMvjuePTS":"hAOwq4PXvLTU","z8JxXbp4aVBL":"Q5Ig3NVnBp0A","DeIQXXv9Cxiu":"A57BjwTgk9Jg","4z361bATcJeA":"J63pvNZFA4sU","33dv5rnxi4cE":"5S3KTlB9ExLG","PRNotao4AOWz":"VYNMd9gNQYAp","HmCsbJL2MTZS":"M4wTZhXjIzJ8","VUp2qm1vp6su":"nJBNq5eAZCLt","qVyKCFOM9KSO":"CjGzZNkno2Ic","FAXmzqoGoY1T":"p7MRmGaG7leZ","ix3iOOFJ9HKT":"Cx9TtBUTvBWq","XbCmmYbVWUjo":"v50ZbtWFursv","6YnHjhW1gKss":"Hr2y9BQdGAbm","jzPKEC9CgREs":"rcxbIWWkz9LW","42VZyTRz05JC":"Y0zKhlwF1Orr","Gqfx7UZkIRVH":"EOavkRKAz7Dh","S6Zp6o9DfrU8":"Dbudr4KoYz5y","nOAQFRKQaS6S":"3R3Xw0ACpU0X","RGHlO2ez7EoL":"xQi1REDyJVt0","ViFht6Olzfiv":"61wsbLxzMN79","YmlCJRHqHhmc":"5mlP1NPCEbUL","nwFw1Uwp2V8d":"STm4krlHANov","dAF1nxSB3QGH":"szXyp9rCbqVT","mRgNu3QZSVsx":"6crKAcyFh77a","0NMEzyitondR":"nYSqJeCwEux0","U8rrB7zQtmkI":"Fabe2ZymyCvy","2tFcGVkxBnsE":"9YPoPQODPo3l","ENfwE0KkCyoG":"MMEEEVMdBXiN","K4vuVwOhqAw4":"lHda9aqI0oFv","fVOL1EkJI0jk":"EunnRxGFJGdz","5mYF7YCFvQsB":"bpYqQMy0g9YN","PyTtF7kOTaZr":"8FPf2D1ZUa49","pme0wGPDNYl1":"um1uPrxzan1G","22wuq7Rk620q":"uYrSnSN4XIrD","uBzNRZ8BS3iJ":"51BclFaAJzCv","0SDR5hLUR6bp":"4TSOQXHrwR3Q","LWsTr1rdURyh":"Ru6x9StwR7EV","w08DkI34CdLC":"zfe7eABHn4kU","FwVjTfMOnNIq":"aC1QND9O0cP5","587BIG3MpWNM":"YSOoRizHvp9K","VYxBTY5YvSFN":"hqVLeVpRU3pa","Im33Cl0kvrwp":"wlVIq138UOWD","7ep0U98LFL7V":"GtE3LcY1xKer","kxDQBoH3lazi":"LAhBtApPtwgG","unPliufL5WEn":"9yEc66yM2BJ0","Uc2pNwsCFE7Z":"WZKtHOenNPKf","UUU1x8DlM1Em":"8PTRNDpE1ghR","zPYD3rKOoWvx":"As1j49TRHZ50","o4AtVU8JwtZ5":"GvVC5gXe5AZ1","aXFvAWnpkmbh":"3eUt4AOgkkVT","PaSjXDJvgF7X":"1uMq5QTP26OF","kpuVA6QF37ni":"1QHKhlaOlLH5","JPHxZZX6wM9G":"6Ydr9SPknMKK","Cp5QAXBXIwg8":"w4fK26aOdMeK","q27qpWlsZU3M":"1VAcwZLsS5li","HQd1mkgxsKoa":"kd4vx6N620wz","5tucYpQB1KyJ":"Aijzr0cE7LiF","viEWv16u7EMP":"9RwoejLlIP1S","ZHsNF4IVlrAz":"ak9VQcYPfy5w","f6tcv8IbgK6R":"MUgok5nGKcjS","pTjVEsX0jioX":"RYJU2HFWS7F1","3PR6gLaejHT1":"3jJE5dogj4qa","ln4g6T6fMo5t":"nSRSqCUbsdY6","ETVBhzWfiNxS":"8J71h4qArSMN","33GfEYkeVjau":"FIh1kkisa1lo","2Eem9W7P0WUA":"6PdgtYVj1drw","pugrZrb45okH":"GBxp3uTU4AfK","Fn3IXSEQlPT7":"v01cyKRnpemJ","nQLFX1VWjdpS":"YlS9kiDj30q1","YsDd0FnyKTAe":"WrrCIexWcovi","gYpBP6sTmq65":"HczSwHqyv87j","qAR9b2jiv1S7":"L3cdDjTxbxBn","VPCfs7Xi6rM8":"M7IIyBlFZK53","cqDdsOsGGtRP":"FgDNZghsBoao","rULL95C9jlsQ":"3eW1U8n3L6GV","PMdgJztB1daW":"FQynsAdleNOQ","JcV0h9M4olcK":"6dv9xmzYMq1Y","Jgeg1haNcS3t":"QbdKxIp3YfyN","Lw5mUVlEkaQQ":"eokfiOMsXojP","sMStUnWZujC3":"kzIAla3lLPp5","sJhZiYM6RDBj":"ojP7edQWtf1m","v44UiJkfZ2S2":"nBBsJlCdPkki","pAV6bfDmN74c":"79cXKDozWww5","UcxQQWslL44C":"ojK0ePUCgZov","OwPzPLsfPO8P":"KgQOPS35rW0R","ISah66IpYiIs":"9UNhGqLgFRB2","OuyvdM8RupNi":"Fsx1ozJ7fboT","yulzTRJd3va2":"QSz3m5gFIDT7","bQfLzs9n7JzM":"jVHWrnccnNXz","xi05wevbtZIc":"nmDsvdkRef9S","Qqlh6OXOIVff":"FLS682r9VgzZ","t4G008eC1oEB":"8CX21zDIdglL","wVo4iWGpYMoh":"snxrxgaZRcp5","mYgo0pGqoVZc":"aqOHItzGcrCk","PyhpulVYLv4c":"sgmZxN7xiCYj","iqMrHF7lMkI8":"qLb17wf9szPQ","o0Sl9s3YLK3n":"xt15vXlAbtNC","t2J2N5UkXSg2":"1t0WBUPX7qOB","Xx3uEtX6sXsJ":"LHvhcFSzon5C","xcGBrD4BsGkB":"9BC46pWKboTB","AKPBnvPHyGTg":"ScEr7aDGBAWp","z0PenfqvCQpB":"miRvOvbGGyJt","E740e8D5yw8e":"H4nvgShpslLc","Jxcz7XaUznax":"dAQkxPnG5rD6","XGJgtmLEF5vs":"b7AT8EmpzKDH","Snx1DHshTFhP":"dPxcngPypp0q","u9QWdbEjl7A0":"hJ2CVH8J1iQ2","K1CCwhwp9M8G":"763QMDtHWPEd","fLVj2IZYJ7gN":"Jg93UMhWmoUn","T0upxoZd8DUg":"mnrAEK1QBEN6","LJTBapCeKt4I":"5zqaEL8rrC9y","NuCZMzIRVbpP":"VJT6aH62VDyj","aAFMLorfULD7":"HXyRjsbKRk4o","hj8VwLdrjI6e":"NkQhliCxW1cK","q8HN5c3jl3r9":"t67WUTx4s6aB","iEMMV0jLqCaX":"Gcc2PNhbowYa","dHN20BHFs4hY":"3LVGV09fiLiR","nRVT92WBCqDX":"DrqfZs1b7rSj","D5gojHg6mQpk":"w4Ns2MLxFeAj","Dosx3FYM4oV5":"YDPk2RXejHZZ","QfOAzR9coGLx":"BcX74OXK9m7i","prYnK0oPMbsH":"EdERf3OBJSaI","bvEFoeZzHRwi":"QOODX8jsqAQq","PVKDYsNbXKtg":"Q9sdJChYKZDU","1pEa1T1S7Goa":"H4LFCmvEp0Cv","8fvCwn1sOW4c":"WcoOzQxwl4Zq","l75MVFCNHB7L":"MACJgOaLp18g","xxBnHQr88OAv":"HXkix99mHqws","RbJv8SpWkmlG":"7o404slBntNk","3Yx3gl0RP2su":"4fOBLZjyIluG","qI0e2us8rmcm":"SEf8SetH7pVb","hc2Z58OkyA8L":"VlMxGgRrpRyN","MZC5sSKgTBNT":"3HY9f3aXrPt8","CLahL4co1lsJ":"DxAtOsu7nu2j","jxRdkg1KByN8":"N3OlAeXLTzl3","6EjkqWrnzRxe":"50RQo8kgFwNi","8I030jDQAYgy":"1gvt5n9GrM7o","PMnTcbyLsKVz":"ux67OVORjGyh","CfoPD2rnYV2p":"idnRk5FRbOcx","8Par6zj2FJxH":"QxCw8qbwJDMT","n9dYZXi3S4jd":"ox58cghlcJrk","wleojTA98xVQ":"CCLEWEI7gIUT","kdlLiifcvYGn":"PTLvoERfvEpW","V12SKS5jnqEn":"SuprmFTctTrU","kgZKYpUhsrWl":"QVhE8V4zk3xf","9zXr50AReM0T":"tF63XwljmDk2","6bBQzYXw44RX":"pdYGvQpEzzy9","Hfmyt4FQkn4h":"a7sxn6GLASWX","NH5i72u4YH85":"X7hsFqxmGtPP","hy0faS6niizK":"C3fp1eAVh83I","bKgDTkMZHMTh":"vkRFBrzknpNU","Wy5ZNgAoMDzh":"kKsIvEyS4PBG","ZA2kZyHNlwUF":"r8IXR0G2lsqh","CJcEkjN5znZ9":"WVDXRrz9yyVZ","t5SqVven2k2m":"cd0j60Hmg0oK","XnhnXfiD2oUp":"tFW0ZVrjgejg","pFx8UtXFNlpS":"qHUtO5hjbhKQ","LVmKdCndkVWh":"1GxSgogFwzpF","Cj9I8xCvCxqZ":"dE6OiMUjOHdW","X6Aau3Gg40zL":"y4GrhPmKd5ta","dGuGPoWCPhf2":"RGWhiFcj39kb","rwCZtydKPdzD":"2fcA8dJR8JTL","MyFuipxTqvSk":"tPjMbblvmHdK","zwFKTsOHITHw":"bvvcHPz0TOmO","WCGEzPWdl7qj":"gI4Abj8WPCyB","YDg17u736DNc":"ncRoAH2fI0Ua","6GkyLaAONtKM":"nMITODfdViPi","aURKgRAec8MN":"eo9HMRg1alHo","H2sJqvFEiTXA":"sGV00LvsRS95","FlsSIkix3Zso":"RqW1YGnISMwd","jNVcpexf8v4D":"EX8sXr2CW7K9","RlnoQKzdX7sA":"Aeo1dm4TQn01","dL09xdBDCCDg":"UIDPvfIa5cQu","yOUpqEnUWjY5":"jGwnBNrQ9dSS","6C2zGumNyIEX":"CRCryLyzxEPB","Cqfjl39zp961":"JYbeBgHGYLNM","NH3anRxwcvQr":"9fiOF9xHifa6","rvvSsEz9NYpF":"dzdtq7IZXr0I","q9txhrLkoQ2F":"ugBiMCGZr9SH","z6NSevvRk8a4":"iHI6uc4GKkNe","g1UjFcfP9YKE":"mdLIeadLxkPY","UBXTvYRecu6r":"hzTSxCNZ2Ohu","P8r5n3QEsmvR":"kY8pC4eAvZt8","eU4iI63Rz6gt":"pRgtCgWZDhbs","70pgEme99XiW":"ANr0KK8ooDsT","KttH3m2spMfb":"3zsqH9AeZHTd","iI6jbGVQLAYl":"lj60G0RMHtwo","ko7GebZ5xJJf":"d3k6KN7n7Boz","8TkIvqWQFxWe":"KQUtq0fJIi7X","SxuEaGxdqpLG":"D6iTVf8VODOg","B9gDJu9SfUG6":"DzLUZ5IVLXih","51sXmSz5xyG0":"R3fJmH6yWIex","ICqmqFonSNsk":"suuBz0KGIDTN","7BKcuYTNNndN":"lEroWsfORqdl","9PgBHUM5He05":"SKaxtz6JPfMZ","Fn8EMUKgoakv":"9QvHueOaAzcr","pDPHzV7LepCx":"owxpxUgLneAg","hkTyYFAlhGGN":"pwmkx3DcYDGW","BDP7tKfXayxX":"PdfSDvMxULLd","15Dc82MY09AZ":"OwC142WrypX9","guHNJZAyobNH":"aUespGkwpVFe","Mzh7F9amoM7h":"XIC2iweDjxLi","q9wwWiPLX2Mr":"GKsQJkESRUtl","cHDypQ7IiWf1":"Pdu89cQkgKtT","tVwpxGJ7ZXZT":"gRKOsg8c5AoJ","EfE0MzYKp1my":"AkJpKaAGixbR","0FjMc9rqz4ZE":"AcyQUfK3Rdch","pUh8Tox3V7cL":"Gyfx5emLFvDc","xao3rCaSkz3l":"4NBDlKIsP0rY","dA3sUeTQgwql":"h2hrBM8Swigl","TcUz4uxaN0mp":"prOtjSiZcJ28","WEphG544Y9eP":"WheeezB83Qsr","EbGTOkKW7qfl":"49261FlJhcE6","r5E1E4Lxqnt1":"dSZb94JrJ50c","0y7oABBMqREs":"yP5gAzv6hOOi","el5bqzVH1Bra":"MaKdHq33HjD3","9YyNiVn6f6J4":"ZJtRcjFzaFoA","Qe1SlZwjNhPc":"waVvIboKz6ft","7sMSR3ha0Ox9":"bxzxY3z2jqPd","DIIEUyqVsSqU":"pp5wEB2So1pt","2l0WunfwMHwd":"4kQRvKbGOGWd","q5G3YMjtOQpx":"QLzOtswPk9Zk","6jJ3MuFUjvKm":"N3iIVCb60P4f","YhC4MmjGQWXi":"oksu87qsTl21","JCX5cBmljV8R":"t0znXq5TAT1F","ZdbGHSB165e5":"ZvKC2fTJw1rx","PTpjYiVcJH0b":"hgLdL7TkOze2","wIEW8nI5VJt5":"1xDGKr1HuoLI","a51RwN6nA1s9":"jaRnixgfGlys","Uw3gGlWHVDEa":"SGjGwdPtzP51","vgzI43caiFuj":"3Lyu5IqGyx2j","vDlkezWyiTZs":"hSzB1bEC0Tby","6BCqI1VduVeY":"eZzOygfZDBKN","XgjwhGEx53Jx":"tFPR6bMTVrLZ","FdMNkkTE9Lzo":"2lLH8dJOyL7y","eLYE3An4zK1u":"dCSmG6lohJ8W","v23YtSfkgnD5":"09vwf29fnxyf","CrPJdENOuNFY":"wm8nv9VwtTwj","NEF7YTsib2Do":"uhnrHqQyul9D","xp5BS5F3FdGz":"NCPAN6xbsud7","N9HEou7ZsLM0":"lTwHyhvPie1K","PEP38Y1WGuo4":"ZWJBagjZgSXz","yojOoBClJKqf":"NuwMAhARqtEv","QCOJ1WsQAsQQ":"l4ZssubfoMJx","twnzN1n80CbD":"FPkhkoXpjK2E","3mFS2SlgODoi":"ZGmnXbGzeH3g","mMgJcMBiSK5U":"8zp3QPXRaBvo","1BXw4LOlTt72":"qoXDud1BYw1G","e34uUU54oY3p":"fphJoS2Oj4rU","OXKROLTptqDS":"HspNDIkzviGr","5Dv6gv6c4bV8":"FAUpvXh7uTyC","d5FvuZypHAUb":"PHUqIeE7WErc","i4Y0qZ8zV7K6":"zeQwp3II2Yyl","VkduPax8RteA":"3fwTcdkqEL0g","RsA7HX3BdIvT":"2JONvT1RxaKL","qhfAnaLIoqY7":"cKdDu479HKOD","RWmHRXv8vDx5":"v7P8wavi10tc","7xFJt8xF5oiX":"GsojU6kwmIpV","nkWb6roGaSqK":"mAhI9EObxCIP","8BR0xP3ddA6Z":"64AYgHN6PQ20","ILSfWCg3xIvd":"eoBpO8mit3FY","zvCX7GA9bS1X":"A1v5VXltnh4L","AgS7G0WPuwpu":"iO5mZBFbwje0","qHyklPmxic8x":"IvmEdlf4afhs","eXrWRZgD7jwW":"REqzmd6D7zgT","M6kyd4HBzC0b":"a7lqfblbpanb","s8BoAY3aU3xR":"g1Q9vSYDg9S4","2OnuzPaTn4dA":"mCB3UmyjCwu5","6OWLwtOJAUDy":"sUlUzNCtINKU","K7ro9NNHL07j":"RNBz9Gx6zo4X","9cdKMNxhQvwL":"gHo1srdTX6bM","oZAS3OMLU4k7":"QlgCzG4c3oN6","irBzAqgNJEkn":"RVuTvKRmd6XR","pnTAk3C4fxFt":"qBLhN8ObGAHU","dE1B4szj01lR":"QYNmniFgakDw","IJ2UgI6VJyvT":"OT4OkUDh4ffp","EaP0oaFxGsO3":"YFg8cpOLh1WQ","K5veogr4nBHI":"vh9riEhbFGW7","NUZ06I8itRTM":"IdBrw3suq3C9","o1hxtYJrV8g0":"aC9ngyLsOmS3","H9K3n3StkDai":"EZp1Y60Nju73","pyugEUKLegbc":"TwqwzpZAEFN8","OmY85uIf19lQ":"J7MIsz6rKlVD","vs8RsN0iwU8b":"1ZYTtRUkXvs1","UpZzPkReGvsF":"Fm8PfRQeCplx","P7k8BT9zeOct":"nOgpHPcF6WaX","KQZuWjDssbcK":"8KoR7MWwh12l","rRj9A6DIi9AF":"t5kBYif46Fwr","TFO2HqwAuSgE":"ztyqrw66WHLb","syyRTvnKmIXD":"Log82lHMHFie","KzB85GQtAD8D":"BvCAhkdrwwTL","EUslaulLGJBp":"JzzoRk38BzZA","LUvn5TG5LWQE":"xwIyCw9qzO1y","FNMUWXEhL21l":"PbOc0SY1K5Vv","siKtNGBfDq7K":"7Pikuz1AhjMp","4POxyuUSmJfJ":"N35QEbd8Wd0Y","MbOeqAevz0j5":"QLoqEBbGFl3E","vaigXEeB87B8":"mzEk14oYCyvV","ZSHeSOm5Urb1":"QDQpUADyo1mQ","j0xEV9N0enm0":"twL3Dcfol6s6","p85QFXL1c7SY":"hpauSRcwmE6n","GOc8l0oshJDz":"nrK4hfVnfM6D","D88XF0f3zTNc":"Cs072JMY2u4n","Skbjhles03o7":"IdKD1VMRN9bs","lB6rGf1bzFdc":"P3tVtNVWiGT0","2VqkIej89GXd":"8l1cKNkH8FFg","pHC9vuGSLLn5":"gndm6Y8Z1ZRe","m1jbxWydwcI8":"rJ1RXHo7GWW4","4RGOwvn38utI":"QVOXjLEs10xZ","3duUX9CrfCuL":"8WWxy5o0cP0y","52mddg1pnlhC":"TQm0AGvnjcpQ","G1tmllwZkDew":"HwWvtns1x1uV","9UWJxa7yqtZn":"ZOlQ9dQMl7mL","TloQIlwh5dbz":"6oYjVmmDPQPv","oBdTkIeaLEgi":"AmP5lTSayHOz","FESBLsmIlsfQ":"nQadnzQUkNcJ","YeWDB1SVMLHb":"6PytP6ofapx3","XsvEtcHZKySW":"gJ4gH1lzTYVy","0HFCDFhJYNmX":"aYyBV42z5Ndg","lc2FcRjFC1Ks":"CQvrpzzHxniS","ezjMea9tvCZG":"VaBrqC1GNfmp","u6VeDHw4xW5g":"yCNNVcSIYXDy","Myh87Sj8makH":"QeuIKjeSzzBI","fqzVIwT5vIdY":"2z7IxbfmMUVl","zSP1GMa5lmKs":"iXzoGODB57NB","ooKPHYFA0tW1":"tgSU9IWSzoFD","XuxACc0gbyb3":"wlETQjz8zRie","6DIZVw6kwPaN":"z1ui8Sy1QHv6","dAa0uWFPHHUo":"sbRjqpMcklpi","FieHeyLcmFZC":"X8OoydX1zAZI","iLOa74ZWcyhx":"zxl19KhEO6NI","MmAWxDq2AN1u":"H4OtZcYKh20h","FjMwZDJB0ph3":"MBkwG0C4hHev","NABPKHBnRsd2":"NJG8M9CLHwgN","MF8BU96C7AdY":"32mWbBbIHT4N","aSyBWc6tTX1E":"E7ibMdZ5eScN","3h5NPZ5DfNju":"bFVFkbwMcoSi","jmUjXuvU6DdA":"l1hVLy6nWIJ4","88Hgg55tWAHK":"N5WuSCbkHRyx","62Frc8uFGSt0":"qsWK1rpnERid","DAEzMG53gY7o":"3gYDCNDh4aKl","dmZIvvaavcWh":"PwafCufme2nj","wbwzBWqB4nca":"woopYSe4Jydc","OpxFfu7Uid54":"XVGC921Nwp3X","IBxF4Zu651el":"4rr1ANGcWylA","kJYfRyeVwu99":"Z5BtsiFIj24P","RUjbAYt9xAaw":"xNKGXkhmLCcG","lhCiJmo6siAE":"x2EOWUg32L1F","PwCEuTHQPoUU":"xtn9bywEp1OZ","Nz6hGLzh6o6d":"ZNwjtfRTrX4J","hQwozi13YbhB":"qy7a3KGpUoLX","8DrmQb57q8y7":"KyNXvEYl5ODK","UOmVASeltCVL":"v53Df7366aKI","SPaV70DJIa4t":"gbdEMGjVLKYJ","ErqFbDWDNJ4e":"q4b9t04O8Nxk","vrzkBcEFLGuH":"Eq1gOBiJ1fkF","mQOYELmELvY6":"JherIKFcBnOV","XXf22OVUxwes":"xpcrTeeJjdEP","OiBcfVIA6hXd":"HuEDSDlsUEs8","s3NHyV94RXo3":"F9MmoGiTXstp","R6DSQWyFWABK":"5rrYCPGSk2Ef","KRLBIOgPEjl4":"wzmrF3DAXDTK","q737hg8vrCIz":"2sHba27BykVG","LCwYYmdiw8Fv":"HIp8uD4DF4or","6Ex25XYDbUcN":"3vFHSvMDBnRB","kfs2KDsG7iSZ":"wCGN3uZ1NdQQ","vZn1RfvZRXsl":"jOmig4e0m6e5","xp6ZJqPkpFy2":"OSxkfOTB60i7","3yUBzU9pqi8p":"IPwQef69R6Hw","DpKosEP86S17":"fl7L7J8KQWrL","jo0mO2Alnpol":"TDi7p4ARkL4H","hXNIJoBs3hA0":"FeoUzxTX1FQX","QejfwuZB4Lg8":"dRQ29yPOwS65","VwiF2d91hvMS":"w8qWIXWlsqtT","xj953d2QMBLk":"zZf5Fi0KzwAh","fzzHRvpqT6T0":"8hhYieF7j5ny","MDgzJ01Ent4P":"KUfLcI83BXba","yBHEISFHTR6w":"MyjPx2WrEl1R","wqEliq2GnAv7":"u7T8iKNWTc74","LBldfBiGB6X8":"9i1HRNGK9LCh","tF8L5iVH0K58":"CTHHSezBQQna","x2TIQvlcmmhZ":"pz52Jf0g7lN3","w1Xrp4wGjjQU":"AEaj6gmd1L9c","LiFN1om4Ockb":"EM1oCBHaWnfN","o1dra1OnglYO":"XqtnkUDdiHHL","jnqoiZU70gu7":"E71SPP48k2ko","dFCFL0Fwf4zn":"zo9AMmcFCVqT","xTzoHEjx6l3C":"INScv8WuTnjC","KaxDyNldhJSy":"27Q4AYidywBW","ONskX4yT8ja5":"LQ9WPWUXMykh","ZEzz8Y6f3e6Q":"EjknHsnYPf3v","x05wL8zdi4VI":"O5gMzZ205Ohx","zxWcliupzs96":"GNeDaTFWa2fy","7q6MSyvMK5gx":"7b0zFOPyEKIw","Z9ovk4dYRmUa":"MEwGbMnGxwr8","bRhYrKmmVsl3":"Aq1FsxEQdxxs","zjAkbxpK9gOr":"qmZCHRfbFu9C","Kbr2IymVHd4T":"rzKNRwrP0Eg5","WUhUfmbgRGM1":"OQ1hTY3LFzYW","9lPwsBf7oeu5":"HBvWwR5wywBK","dqUTEo8a6jnP":"P2rhCkAIIdQ4","3nVQ00F4nBjx":"FR96gFEWgmUO","eryInHJqcaiv":"YdN0bGGXS2ND","k3dAB7G1BYb8":"iIiOxqX9YHNp","ivFStUdhvAJe":"nlw4jIGE3eli","KqHEPmvQPyA3":"1JFYlkuOJjgv","1m2aK8OfdVZT":"UJT1YHuHVymZ","VzDv1U85ojkn":"iktM3JDCfWTv","QkXgn0pDSWrr":"sanKUqcG7DyU","xiWtcUiEctpG":"yL84x6TB5uqY","2MbaTkMxQ8Tr":"N7xnIJd8xXAF","zwp1Mwew6IQA":"jNjCV4dXeNqd","9HUkZS27AcLg":"jxO0RtIaIGHb","a7AquSI3nTsu":"q1A78Slyjx0Y","qBhHcpG5QXMB":"sogJthmA1JCo","ivyJdFsDtKM7":"UPVvPFDejbN1","4uCIMxKfFvWv":"TxOzdE8BqAPp","6a6mIVt8cxyq":"5Ndc62nHB48B","Lcs9nrlMaw4j":"VLBMuGv7gnbE","0WArhdxe2iFj":"zXBcLbjEx6SP","SAhnY9gSGz3A":"RHPHMBgMP8Zz","qV1k785HBbtP":"CEEMeC0nyl4a","5q5JjOOmMX7T":"oZPzzMVND9Ms","lP6aTwxpAAQj":"GKvaSHHzFLRb","p8OCFD7OG7kv":"wtiGuvrKnfP9","IcaDhWcEwPJM":"pKi9c0f4K08a","FKOgUt7j0BSJ":"ZLQkmf91x5QE","hWt6iOvKMig4":"ayw1Kq4xxuPf","Gunj88rqXuU4":"0Mc4yfyxeXaw","K9IvkQ5FburT":"ROVbnHAqrbgy","ny3WmXEQSRu9":"OQDgW2zvcKri","OLOUHFAtUEaT":"lP2iUVvRAqMh","VmoNI4Q0BLnF":"LER5fbcJWlhn","tjP7IR6pJApo":"z9qrcfDtUDid","mKO7sqHuqs1T":"YfYn5PlSsSq2","jI59sEsX5fvI":"qgTdWuG2PBGs","KXgwciYTtlq3":"WHqxdBWMReeR","nYfW8r1anbfk":"I5UUKEZKusWG","8fWfsMQcvptt":"ZE4Sy7LZvY7W","D4EioM5HmbkW":"IEeQSLQdiPph","Xy8GXTlRH0ve":"PLOb7KsmS0mB","JBDzMPuupNGx":"ZIsHYQq1EA5j","1blkU3wjjwLn":"nJnQCqepmDmE","HRg7NbzO6a4H":"Zuvxx8priFCH","CJrbu8PcR3qS":"0g6qWA2nJSdj","D9EdHa2WiBMU":"lgLFt9mjT7Lo","n3YYgfjvqobZ":"FptRSzACGT4i","9rddROAo9lCy":"G61rUSmTVSJJ","jDOoDwc6JHpr":"qNyEannjxzoZ","z8PpAVbzGePr":"SYKX2amqcdy3","HUtP4E0pzqtb":"p0XwYYPPnBR3","KORfOCDjMKPv":"vYti1YZvcWd1","8YShTU3ehUnc":"SupPWaxIgarn","aJjiiDUit2Yu":"jNKEHMlAfjoD","pQeE3TZZRgw5":"EI3wDuieOLQW","Sv2awQUWeuLs":"vq0z9GmQxrU5","ozc0AsSpPOp0":"d00dGPLkNKBm","2tKerMMNHb2n":"UqFhl0oTVBdP","IjnFkMIgW0jK":"eJONREwBIplS","GKRlq2i4jau2":"d8DKb1EfZ3tu","WoiuhAyWK0lK":"ErsBwr0TOXXi","FtK0qq3GxNV9":"qFhDOhCUVNBA","ktCyMI6tAxvi":"ZVX0PqyLO7Uq","cim7EMwKEgiQ":"HU1GNrozdnTm","OSrAkiVdgzqp":"AOmTAD8gFDOH","UdA6iJNZKyMY":"eckbws4IKZlv","1EzF3yVMqHpD":"wk7iVuPINbYe","QCHqk9ORwVmp":"qjZQyVoJAmTw","61Kxsldi8lOG":"uM9zliPTnHb0","Uko6ig5dCNdU":"hMpkJiFjktVd","eWSiyKVEURK4":"vXECaihwsU8b","mn2xxIDURvpU":"lqxgfAU2JcNp","sAJTFQ39wgJL":"rJNfRXFWtYPM","hPByHUXjfL2S":"wcQV6YV0uayy","G6Fxua13IL8l":"96W45usNWP9W","qSjAqg52AJcv":"gXD22prAkO5q","BNM0YDigsgES":"lqfeh5si6VEF","HzqRlJJ5S9Jg":"gKcNUyoeprMi","HdPvPermBnwR":"cwB0R7FEXftJ","vAMW7mmLDgD9":"nM6lRsu0YXY8","KhcakPksASo9":"BxjiVsb7E2Ws","J23CgF1IeEyN":"o2UdoFHEiVCm","Xs78jRIdfwp0":"VdPG2xQadXxs","ktaiV4te6Oxh":"infAp0zQimZZ","vaW1kJprEt3F":"tsW7TZdtVN3A","nDyK0sdhIAsQ":"uiFpjgTdbJLi","tpSUbI5HZ1jN":"8SaEmCfGFbo5","QNCncr0J3h2F":"n6bKyyNQPsdA","DEYY865VWzxU":"P7bZsjuVN3Qs","BvWSOflqn5xJ":"MX1DyIgMd6tR","1PI4DPa50Osd":"gnVX6bpGb9K1","hX0hRp0ObvE3":"p5Mw3x7FUtZ2","1P1kCz1jFG0s":"1r321UzxfYXM","FLGWc7I7g5xC":"kVkEh8sWti5B","DLt8LHGI67Qi":"mBJ7P5ZBcPL1","NEKKX5oag2xf":"5dreorw4f2FW","K1EoX2OufWpv":"IRGCDnlTw6eO","fbu6DvUclgN5":"imwD5ZEkfCR1","R5uXr5fdMJ6W":"Hjr0dmgLziSN","hGajwA8MP5gN":"WnIODiQBNZXv","n0oxjRjv9Mcp":"68CAV37Aje5T","J8ovW71COGCn":"QosROV7nrHFp","E0UN7mXjCQYm":"CECh1EgKPWMV","vWTJppsgzbhX":"LDvtrWUVDK09","UzQ9nL57rAtY":"OZy5lXvr3xlr","Iw2fQRqnOOqw":"PnTtqsaJyiFv","UYKyVMoMzXFs":"f0bCNyB0MfR5","HzxOSwPuBTpJ":"XRO6EGggi8N4","EUeXfZPKuMdW":"wp0LfJZRbKrM","q3U4zyfwZvh5":"2piEUk5DKZ3Y","BM5qCnwLyuf8":"yngB7SpPcHPt","WN02kS6N6iEE":"cNu01jlywKGJ","kcIaiM2r2rQU":"f8mVWhZpaWyN","wBpuxZpcGABy":"Z0dZG8mSi1lB","Ujvdo81WvWsA":"ptogogz4T4Aj","mQki4axNGhXB":"LK8aNSCGcH67","qoMgPyVhaDgP":"I2kyZQJXyNZp","8dtMSodzctyv":"zO9wTzEoGUU0","uIjkQ4rwl1D0":"hrvNNWY6p4eu","qJKu9bXypWRq":"XPVIy8vwuBCD","cxZFLJjTlf26":"cxadhufCXpjk","OaIxybmXI1t4":"x9XVDBfmS1Mx","ocx2p9gTmRQ0":"WZVCtEvdaIbz","xUS7p80EkJgc":"35BIANBR81TE","7rDkCbkB04Sa":"b8BClilQmIKB","rEnHu9JD0UPV":"xyZJheMcXU1E","yiVubRtjTcwg":"n0fMWRiiqJQQ","dbffpqZd16dQ":"AXah1UQT73hl","6h7Np8EAb9RR":"rsMWRTaWF8lK","5l4VUt6Rpaaa":"tugTpdOawOpr","Qnc1HmrqXk6k":"4jmfQ4IBsggu","V4NEftfwMheE":"J5ugsuA0GFyA","odnoflUyMUjM":"XK71EDSQhsXE","VDwV2DfG53nC":"7OcMq9oF5aXk","R2HPd7AX7Z5C":"mB308z5w3LW0","7ojRsqxooipj":"ttifct4hxhTM","VlNKFequkE1e":"C7M4WV42fR9j","PkEGcKlvzzsQ":"z6byy96vOFKs","pmFDlOAjYYwW":"IExFeX8y6QQk","GtWreo0D1MkQ":"V6nWRvuf67pc","ozBCUL891Mj0":"DqZcTQ6StG08","5p8oeDZ2eHhk":"HzwziROaI4WB","n7Qx8XcayE58":"cFWNKGihjFDJ","v9At4y0WABJA":"FKLWLHQeHDur","X92yaIdLffn8":"hWR6zynLaIVZ","9NZrBk2KC29M":"p3GK0p3rhDpH","TCa3cZaql7ZL":"gNSk5kxhHwrN","7obu7FKBlQ12":"W9upWALQ6HNt","8zzChoBYYQLa":"bWiNy7qi06yh","2LbpayvWoGQ4":"XQfRyT06Dvgy","v00IUhpGppMu":"ltJCs2UXYdPC","q0VYaNW9xwzt":"YR4UhliQjJXL","YKKIiy27jJco":"a2zgWTzyEpKC","9VJsxWTZ4YCa":"0TpdlTW5ePM4","8Lrjnil8j1zK":"FHChOl5cGPdQ","yNgA14wI41QV":"c2hvF9ljm0w0","gix0zxZwLDhX":"WAi25ihlRZqW","fn8v7HjZSpPN":"DfcoiJ75zcog","UsSmMOBPYx4o":"Ie7PwX0s7wnb","15pPCSqxoKsb":"EoU0l9gLjXjy","8QhYpLwNi6Ts":"Kk7WGXfqnXYJ","dlwuHGxXWypc":"GlGmh2Qmt7UD","fNeAt0ofcTl0":"xzGs6ExOKIXv","EwicWdlU4xMH":"uyTsJw1Jcrpr","MuTd6IYSgNW6":"E6t3OZVfH5rR","OkofczjVVpaJ":"zixi70xU7r1K","9R11GHKA95k0":"jVH3B1xkftZH","zg3vVOFTb7wj":"yJHS1w954uSp","tGhXr8yCn2kd":"HbcsT5GUiTdz","RDfiyl7EbzMS":"ZsOg2yagblpK","Uxe51GZ56VJk":"Q6zPbnqvX07T","DQoMJMvf8KIt":"x3MlTIS27yH0","PCjVd6p4zeSI":"yL6Y2qGG4C4H","MVySieA7GV1z":"evoE1qahB4D5","ImzsGwB0Xd0D":"olnhfHWNYo2u","zbCcekv8ikMP":"IZXIztnHaYDw","ZwUqabYNMRMq":"cv6r5vVpnmmV","PLuMDj9lupO3":"IQ3WsWXnIiSf","SfvTbNghG6bh":"wamyyEoqSaCv","fozRhATM2W7n":"RlQ1an4n0BGR","CxZTVxjt9UAs":"3XjcPODSBIkn","umLcr8LEpqx3":"Okj7RTgDn2aL","7Jh0JBWRp38S":"EMTSfxF5yVxv","DSD9GBR4mLMQ":"xnwnGh01XdAf","81EA0qF4rWu2":"Y1ew2qnGpjEB","4DB71470MAWd":"zeFSmfRtVT8n","Vjh6832NWkbH":"CyYAegWwDXNi","9IfoNRZQklwx":"FNNz4oOZCwRF","dptI6FXYsTOm":"Xnp8wTls4cx5","VapjdDC6iU0h":"blTX87sONjD1","wFiA7WNxk5SE":"yzn1ekZCQkj2","zOb7FOPsMFkD":"D0WhLgG5g5Fk","6XmvyVnEbgTB":"6MG2h2KsGbMp","jAkKWTNpqAjm":"Ni9XCZOj7OIR","gwSOv6l03Lv1":"1JKomKLEQC25","dItFN07ASmrN":"hkb7dQhvhd83","4RXekipUSsb2":"TuEo9r8oULyF","eqlAE6pRHtjI":"2VlcMWQ1UzE0","bAqTl6RcNUDG":"edVxtW6E3Nf5","yPesHEYWVYJH":"82Jtt46I75lI","5RfszLeso6Ks":"BBBri7zxUkBD","xFKEFNt96upj":"Pm6RYj0e6634","iAGgUv6Jf94d":"aUTnA5X3vXNP","15D0SHaZ1ocU":"COGQFkcs4zKU","AofUpcHqTNU1":"8WRcBIn48IhW","HBGUS4baNA64":"F2p4V2T7Czy7","XI2ezopGEZSp":"zQS1QOaI8WgB","4TOYFY5yJRwv":"GNiKf83u0dQG","OFxuZGy64RLg":"JpgsYpGBzqvs","73hgzb6fZ8ow":"QrtNbqAhSefQ","eBc043nD9vYt":"GJE98wH8JzL8","yW1jiEcC2Q5R":"7vvxNMamGh8C","STSCY7hn7eXh":"D8Tx2tABJnbO","2NDv1WFl2nIU":"3Cu45AXgmWiz","yhalYGrJGXJc":"h4stsbE9gISQ","AkD2mdYTNGY0":"fMRLfgksj2RV","hosfOPW4dUOo":"6SjgbXpxWznU","Kck6c05cvhK4":"O6Syl2ytSZum","oGIkdrATUcvX":"sgOflekxp6er","bsNSBot1dhuU":"JJtZ2PAcDuOx","XcmfT0IXGdQT":"arofs8HuMc9S","ZvDqbxz8EmFC":"OTaJaEyiQZ7k","FuXdSLPhmTxk":"3gIPlg8sEFjY","bEMk85pOSR67":"iapxa8RqIhS9","LqxLsl8wlcAN":"yjFhfwekTWzb","B42Y6Ib3RhD6":"UoJaEccSg4Op","T0XnYeXW5gJO":"CRshUB0rUNa5","HvR8hFtL0Zxf":"EPKuQpLsD5XV","XSXbiNmFkMhn":"RHJHEcW0Kwhw","XWnWXqVIY6qA":"yRotNZ1rFsS1","NKBSpIqk9Brl":"w1Pi5xRlPz2E","YLi7fX8XK8gx":"TXbEEkxKRp5G","TMPHPjEzzhIX":"e4yCVNLzb9ZX","LR7UqLYQ77TM":"VONdrKgJR85o","iiY7GgTMGE7V":"PRppLYVuEN4q","gXt9SaNLGmet":"V1XdnH8lDfAY","Jby8MOxzPR0d":"qQRxM4rkiUJl","ZqyYVZm4Zgw1":"TNonEnsVjXcz","anjGq9OPKPVn":"qsLe2ApkCS6z","YI4TheHxkrQO":"LkQyDkWbLt9V","uFmybs2VDkoG":"KOJQ9NWIKZuX","EtAPCnmrPH9y":"nHTrpOvy9bBY","zWROTlpKIYkX":"7OKs6bW8u8R2","zVG0QqZqU3j6":"23wVNUejBWlA","jbnODHy3eUPj":"7iW62ml99cdq","2Tf7A4dPXdLs":"4Sqz4VnMrR3w","npQgHwPQ7nia":"2m6XYJsjPj5Y","T9Z82e7GYIZk":"6TnDpE83ygVD","ELv0v5YI9P9F":"FVYzYmv4eUG3","EsKgLhhZdkXf":"JHqVgDxAIIQp","Icccoi9lNA4m":"kVjf4ayOpLkL","yiBNkuqqQJlt":"kIvduIeoRW2T","zepfXAVhwufz":"DZ2yb6hdZKTh","EDk0qFnnY18U":"T8IIqzcQs58W","7VDiW1lP4Sjd":"3KIdQerk8AoL","wX6AzPu86z9c":"jcMNKXPLh4x0","CItZt4K0Yu6b":"M0PaN8MthHOv","Gfw81XsTlVLr":"QVc4EZu78epk","HMoBRU2EiyZi":"ZBEMtgTJ1XUu","qURX3pivBmtf":"yAogEh1cGsho","3i6JlzOsXyA0":"MTzEf7t2Dcxc","0AWDNtZiXyRm":"CabhgSo3jdtS","GgUqwSSG8bpG":"yl5FD8znooof","sYUizOJfBHxU":"jLXoOmxtEWbG","Wpu0DFGsv3ON":"NbrJQoyVIhgG","lneH0DtURQxQ":"56eeviYfGkqx","bPieTnLewav8":"DEHrjyoZ0oHP","RtiiMCjbniOg":"3XucRGVx72qX","zxgEJ4sqoD1p":"jqiiVHTV8nyD","CqpnzykP3dGm":"uluMmJp87XnL","LFo2CstrgEm6":"KYy9COif2SaR","4C0rxPJlwASW":"2RvE14UT0oXK","6SgxVJeiGvIn":"VQ5yYng4niWj","pruvIAOszGc9":"VtRmW0KeXc4Q","Aj27dvPoWljH":"NXdXoq5jpsgZ","f3SE8dj605zU":"8ZOvqMTElAj9","IBmBOUeKvEF3":"7RbLuz3420Y2","SCSSzIrBfNOu":"2y0JsvbF4xs7","wf7xbsdnoCZ1":"VfpPpUzWyhu1","b5PjzL1P1ZP9":"psTq6mUdjSIq","xf6uoTEueaWr":"iMq5RSuNRLTZ","iRqg90RqjXpg":"ZHvJN7Obevkd","VXt646Ewh6Ly":"HLL84vZSy48k","2mKv0CwU7juO":"jtOMDcrjyn1Z","LughhRDrUviP":"TRVq5wvFsL8Z","WYUJWiQ59PSc":"m0xmngmBmhv5","7JaQZVhhndAJ":"UPw4CO5ZDNQo","PTEIU6m8J6w9":"e9sW2qpOFrob","TZ4GzgaYoCUs":"qXxN8rx2i8CB","udMdNMAMA8Ex":"yuhT9wsKEYVl","gzjmn7O5SlX3":"SMhV7CFNPksq","LJZCNLOZZFjq":"LmbguGj5O9Zf","0qvbmkaGwDYd":"SLHQ7tPmpPKK","hc38R0loQ8Ne":"GyG5NUS4TkLR","P7jD1osIehqb":"LkaStFEUZNh4","aTM7GEGNMyCC":"HyWFXLsLCUKb","nu3SQF9q63X8":"cPB1cdvWNWdF","bpGnqZNDdVIf":"qtoKUUK5jd7v","7TSuAv76FZkK":"4FpzDFj0WtGT","5zOHQN4XASuz":"s8x5OyGzEZby","ZHqKAERbL3Dh":"1y07T8hz2aHJ","OwZULiS066sV":"HhgIvJqxUoli","lV163HlhHb6r":"ctRO6bWX4k1N","qZrA0eVX6VIu":"GnhdzByobxPX","lcMtVBMwpthj":"u3ZF80e0XgyA","hoXZIa8TfbrJ":"fYabvKRMiNmB","HQmysAI943DW":"h4Z3QrOaBF6s","u3rEbvvYrTY4":"GjToa7HnImCH","7GZrpiBxj9zk":"xwZHYZ9aZ9RD","8cYXVMX9hinh":"wmvLgFIKH8PW","nMHwGkAHyrMN":"YZIM33HWJC4y","RsiST95kguxm":"MWsfEIYjMqc7","zHlp5h9TruvP":"HbbHRFlLRHPL","R0dw2AQlJHLs":"0zzl17XHOgRp","O8TpZsn3lXq9":"l0e1D8rTMkXp","j4tjL6HpKdfn":"cJKDXQWuNRbs","k8kghAtHk8mZ":"yqC45UP2PXQh","oa2oG4yekzoE":"KZuV115xwHuz","1sc8J0KqtadS":"UBdndZi1QUl5","R53dQPHRCTnw":"0kFsVf3BEEku","o0SRmmSBHomV":"8l3NmMig46MM","X9oB1N22kez5":"AuYCnYzocuzz","x2BJuhxi8Kz1":"hKTAZq09Kjr5","IyWQXj0arqSw":"O7Vi3DCaaNTX","qO2QjxLYPqJh":"ThzErUAv6ZnK","QpDvorqaeWNh":"GnsqTR6ZwxFC","YJr0Cz9jkVgH":"TIaulzMTTT6J","REXxuc4qZIii":"AHzjEbdfP9rz","5Wi6QcJgkK2h":"mzetgueaEhzB","zgo91fPWymmD":"0z8jBdShX0XY","r3OIvRw9nfVu":"hOM3Kvi9mJzZ","XwxKNuzZxXKU":"oqck18FOiS7d","tUrYmgD48SYI":"OjinlDz5rLbc","zAhBaGLhwdxI":"ZjjLDPr4JpOA","T2qAuQQt9y73":"TK1iM8AMbcAy","psEz4ewGTImx":"RHmnwwxqxq74","8zyWXNf3RBka":"w1b7JxtjGJeY","oAdxgiga2FSe":"Ny9ZyQfVJrOB","tlMgGhgPxEhu":"WfCNFvPZziAf","9QzlyXlIQsK0":"8lMgQ79J4W0N","UBwbLdOCnwyY":"hNL6Bp9YaV0Q","5Drca04LHSyY":"ZQmlyT9CIri9","akSuDZaozi6f":"6kYxwlpKRNzs","9DPIuyzCcght":"pWcMUfZKlz10","B77fim3APvhH":"gNT1hknnw3Gc","vsq9gqFtAg9A":"nfKBxg5MoWGY","yoUzj4DxgvAV":"MZpQF16iR82b","jCYwTj6DJtW6":"P3c0EgfXSVVC","AqtARKOHT6AA":"WBV8ovjWyv9E","UQn0bNZWt90U":"fcxJeZXuFj8Z","nQFUZH6J5HRN":"6acofFNCZDF8","Xq0Bx9a4oIby":"kckkAvyL8htO","On1iaqBFtyHD":"XkTLU44rlBoE","bTjpIXWhJ4Hu":"A8GT33Z8tjp1","GqJpqd0E8l1t":"bAkSPjaytq29","d4sdquXHbI4P":"9T0nMW3NOtNq","dBLznixTdef4":"dPxwBXKE6txo","CN9aAuiZ1yIf":"Mk9sjMTcyPg3","85X9dRwIRGy4":"Lm6tKZjCxZP1","1c8vUIFmp1aQ":"gxRPKb3jpSdI","65ghOvnYSNZW":"imw7hsJiH48S","CUCMgQC3vkq1":"Bz26wzVu4yvB","cinwL7QHsAyK":"UxwyLBF9ta5B","kYT7jzNHxlb1":"Pfv5u4VjE4WM","AxhmxiJ3bay3":"UiVLWKZpdDsP","Jlco9jk5eC8I":"5o5jrqTopX42","l5ASar2ZVlkB":"UZYYqV0f0oEs","ggznHw4n0eda":"26LSJVgpsJLi","RdvQSx6mrGfi":"1xSL11dolSuW","gGVPyUCetY8M":"vYLqYbskFnJU","F1hOfh1OMFbg":"I1BB9QYFnKMI","f3QGSVA4GMvH":"gUDqVauUEORE","Ewm1cqSkvF0Q":"KTtJudLABba2","NnYoiw2BYGwg":"AScP5Idj6yxZ","7f007AI0TrQM":"JU8ogVk6XPCm","g8GYLRaJ2sxT":"gRO3fczSbvXX","QKcguiDf43gy":"AHQkmXMreF8p","gXCx92W5ArpQ":"PbuXmrTf1ig8","pfc4YBCmV4se":"kmsNj5tCP8Ja","5CUa7jdPv4zv":"Tqw2iP8U5HQg","7sIf5fzKNooO":"z3dH7bCC97sr","1uWdEZMGbO4o":"d7ZQMZ5qCJZJ","jm8GtCnMpmwn":"zRBEiUHwpjap","bwCrGaCJYe3D":"lFMRUZ25epdF","t7GKHQTVOshg":"abqtLWYQdUcR","NjSfR0ewRdN8":"VtqHmYdkskeA","HKR9cE9x2JQJ":"roYsSKBe7B7J","S62EoQGlO1xi":"dsT7pblO2wf9","QEnqGyoFa8bb":"ImrZD0mxahIp","yph4o4x8TZyg":"mz9SsdDfyj4G","TLVuopd2FUfT":"SLXy2MTTzS8j","lw6lWlCzYRKz":"j5nQ1yuw48n7","PrQrIUUI0jhi":"1llRZ1VudXXU","wJmENfjoEn7D":"e1mROEYUKMoC","7qDimh30ifAW":"knHZ7eXc865E","k72Y7BxTPkLK":"aeeCKYWanJB0","jkQlj62FbB1G":"a5hj5R47ZTIs","KHvDKLLO6xPE":"F6hdcdOolJlX","x3g228OKMV4j":"Zf33Zc3qNCdR","monDgbs2fqUo":"6FolxsSgLdHK","nZLC2uFKAn5g":"YW6IAJVpV43w","dLtHuzmELrda":"fsyoefvRQpfp","PHNaJAcqwux6":"6OE9gnywQzsg","UBCKJmRCd9dM":"BbrqkQ4VKNTd","7juoyczCM93x":"M96Lsj15lAyQ","kzZ8GNzLONWL":"JOx0tn7Pu4ql","4rQuefNG8F1W":"jG5APe8ml6gm","YlrUfqq89j0R":"cP235c2jZxSo","8NbucxqzuLfe":"54HrbL7Bv5iw","3iVAlbQ9DF2c":"0JPTxsZPlwkE","lE9nYyI19hRI":"oXJgxy11nJws","wElsYY11rVzn":"XSlmxtaE4hLM","ANHdldxAu45X":"WLbAnjt3AeUa","8i867GxrYAqW":"RfzwgHsMPC3M","BjMAuBlhZDwt":"xV6Yi3e0W4cM","vE3Bt7Ycq5gs":"3gZp75ptRwuR","FTQyBDJe7c3R":"NZnBHIzcWjZN","ad8hvJGOZGKp":"ecd06lGYhrxJ","AOJTFmWvCMjT":"JtwJpRICDJfq","UencBiznY5JR":"taPOBjf9HPzq","RRGq84qfjzTh":"BieLeJicizIN","ByqLUdvFgD7z":"0u8VjkAQ047C","93uhJCqXUOqJ":"QGmB4eOZH45W","erHtHXc9MQRl":"s74E3k6eE7Nm","sutwPJVGF1QP":"CfRQXUzmn1t9","XwqiHaSAVxBh":"Ng1RXH8iz8sq","zxJxBitucoik":"7k5eH8N0j0iI","IxKlNxvuwyPc":"rdYTQmTPJRhv","DCatAsChqDv7":"G3XCXuVmOrzf","Kns34QibD3Dd":"GvOrwFhaZW2X","fB3BOmRp254Z":"eMYUk7t2CWFq","A2jVFeLmp2DQ":"RFJnpMGR9rLP","w1YYGVgDsFc6":"aLyVPjh6FO8a","4xDYONegAEaa":"0oC34ZnlnQ5E","paS1pLiPon8b":"7iqPXkZFp5iL","35yGAfRyQ9P6":"xPNixaOsyJd1","Fm8AAvc3HnYp":"1lvpydJAL81V","icHWtL5h2LEz":"Hi3gVgXBFim9","13jW9JGQOLvT":"JR6poqFq42g1","Mb1cK5yCnCRC":"H04UUrJ1qVt1","BJD0B315yQja":"IjFFFDYwCykB","fC0ZJ9llFIHL":"6HYkT3boFJWQ","WHheghZ1KFoL":"GbGhtruqaen5","CqWosGQx0SYB":"UryyfCyuv7He","drk7a3vo7rgr":"KnwHVsEtmSqA","QmLp1UUGyQLM":"2KZwiofvlkMm","GHcN2ZSMlC51":"GaMj6xKvTEY5","MkaBs0id1KYm":"fR3vAFG0qaX4","h4H2rs2sdRFW":"nE8kaKObCIrj","0h4e88x0CavU":"oDwbIPVpX8II","TF890vg89ZtL":"eL463kMwvQwR","sNAPzalb42Be":"F9CCmIFHzmv5","LQ55ThRcKLgy":"fwdEGkzSTbDm","G68eYxoCf7dt":"awRDqALlxoGC","wRMNZrm12vNm":"nlOxFNC4xLCd","i9hVm0tKpgwz":"4A64rkmov9Rn","xpQorPXYpmWv":"y7WdelWK9FQC","OghrHD8QeB5H":"qdPn7AQJ1v6u","2EYEzMqk2dyZ":"WCTvT2TgUXU6","HSKW6LQAGcpt":"2L87dCkoDk3w","9ODrD6IJehBC":"n8idAfhSbcQn","KODPLqZXHOZk":"14k4qw9xUQ2z","gxj4MRFq0AJx":"Lrwb58jyKEcU","W4DILU4Ab9ky":"D9rhgY5vSVo6","DgOwPsUT2FHL":"BZ2h27nOOhVY","wINXIcqDWYFv":"Mn4L3BQToxjd","IKKV8PYbrU3I":"z9RoBsGuZ0Jm","KU3cIUH4wBfP":"NsvsdWof4v47","N1rk5Wd9bEye":"3qckun6U1hAt","OExoAUjuyizh":"QZ9KKLaKHO3U","E8esgaH5wVAA":"UviLHMHVCECS","3VEo14oKf6yR":"k1MQ8sfpMMN6","hzLLu9EuvVRu":"Dy44ZevzBXsa","DIDkfkUOiTE7":"fsF6gEWFW7L4","7uKFetxUoXwH":"wB6uZNYV33Lw","J0OvAfgkFTtd":"t8uhHNm9YRw3","Uj0e7nwTm0yv":"dRoeNH5syttG","qRsQzyvQRFJR":"ggBx44MuMp96","5MlQqqrkDzsp":"UQekZd99lfeQ","uHDJ1sCuTB1T":"SbTzG8i9SPDb","XxzgCzbxNInE":"qiEhJGbo9ymw","lYlvt20iRa8g":"uTUc67HgyP0e","w0SGxrqfBVv6":"cXjsNhqjxf1F","EJnuMhhAdezy":"3yVhlOXI8Iix","fXZuW24b3XxB":"lsG8mQ0K1FNA","Rlmo5I17IuJM":"sgCP6LlPJtjk","CQ4VI0iIQXoi":"pCaAtjpcNgS3","qTlri8209S0Q":"BAFlqriYmdXt","G6ii3Nd1a6ak":"Zb8OX5IYj5I0","R7FjStymjOC4":"TRuhsEIPy75o","bVG3EsWOVZNO":"W1fUUJ6Eyekn","tXXLQwGOUWSY":"jLNH2qdusHZ2","mdSWbGz3XXoa":"DntlZNLdWu8z","SCovcb8CPgtt":"M1HDFRB96XSA","Y3tzs7O9hKRA":"mgzARyUaoWM9","MLw9WESC5TDS":"1ujs6ck4MMX9","KrqOoxo4l4T6":"teNwiBfZ8nLi","tBMQNGwfZV0F":"V878vPEcBldC","SFwY64adYXTq":"Y5twVAm0MXob","zyQfAQGKal0p":"hUJMArpLYaG1","j5vZWNxEjsTm":"ZO92fY74rFuF","LjJzC9U0D8BF":"tDVMejQSv3z3","fl6hgMiZAG0B":"i0mYtnE4YbNX","3OQiIABSMvs0":"2QDnStw4dEmX","bZZAEpGe9OVs":"q0AVcET0SgfV","Ws6CgDN1KEwO":"ZybVcSYtwAn2","iDKAchD1yLwb":"0uPAUS43cyo1","CrVkJ7lmMAQF":"HjSohbn0xV8g","zvRkoWiLZpKv":"fl24ZIjbE2B1","oiIZmzU7L8Xe":"DHMAWv5hridK","IT6hni9IRazy":"qPOoG8oAQ1fx","eMYLvWJkC1Sh":"qb3bIL4b0eSx","4bS4OWV2ZZfW":"bnbxwkas0aV3","2CAw2ZZD1P41":"Ah4ACE6yGux3","xxsLdZ0Ctj0o":"aqYPXPL13kUT","2LtiLJr2T81N":"u1snEToxkZ5C","QPUVl7UYswZB":"uD6jcI8jtTvJ","eCkHEf7iFZyN":"T56vjl3YKnXM","YNnuGgFd4qyM":"s0hb7ciykHTy","HhPAUekjLuQw":"JPLDol4pUtC7","uLMfHihhb9TF":"EMNidLeZ9C8N","rQ0rBR2svXDt":"yo82a4T6TVSy","n35bqB3p9kBN":"JVacV9kXbunt","NmkxDZgsnWXu":"8IbB6SXzt4At","ecdXLVBOjKY8":"cjF7EGCUOPkS","OCCYytjdrU9Z":"P12BroSiFetU","hHVzoW67G6pr":"oIIhamIWMiaK","7oZ11RTrMqus":"Mgixs606UCoc","YmCvb5vtyVwK":"Xcwu1jarI0Yp","D55BRuOuJaoS":"4Cxhny3g0Olo","ia8aWTbDkYkQ":"vBwonmptjy7G","4Zdo3iTBhsot":"fK9uH3t4qNMe","ChRbnRKfgAPs":"DccUueiVEBBs","ioF5Y7OJr5oT":"hGmMNqqzJYo5","7joe5ExLpsib":"ETHNAxepSc0i","SBnhaVCCJ7w0":"50SPSI64HElT","jd9rOrMsv1KA":"dIfUPMTMMyaW","r8exoi0hI0jN":"sOtIW3CKnOVs","mhFZbZJDQcU6":"XFu8SJY5iqcS","MdNvM3wcFTyZ":"MaUGuggTQjEz","USn9QDKSbTei":"j5wG0mP27ItB","B3htY7opqIIo":"rJvWP0MCZHkr","DRGZvWOkfPPG":"0xaGF76pbEUZ","TBJSMRXCRAZY":"aXMtdakCGMia","u1DLmXHUJuGA":"UIs0r1GRGgfu","1axYkHZ6palB":"UDCSusEYw4mn","QfGxNjInmOAX":"j2LWn4ze1gTd","H80Ksto0nI7c":"ftk1hmLNm5s6","IUDGJhxDt3CG":"bTotr3lLaNrv","XNNyVSkdbxXH":"ekxCX6N2IvHp","scAqc0xdjzma":"igX6zOBJpjHq","4L2hyXqPGSTz":"yC4MAQCRFgId","kEkgZqWM2G1a":"cPVEoKA46J3i","rWl9KVCXbvq6":"MpMyttB98Fjt","sHfU3gd929ty":"LSYvKDnkGsXZ","IvfaKi5Gasb2":"cnuQFbe8ZNR4","9QKyBxVwEEOH":"lT1AVydy1Os8","PcibUxaRzcQV":"PzwM1bYbpomW","HZAjncs3TDSx":"0lsQmFkh9PPX","NjZJOVcaiwsn":"DPZfO268KyX7","XIOeS0HQOfIc":"VWNbP768v9gs","3OiDgTUDQhVg":"sq0GYOARfByA","YJ9jq6rAKcZI":"aZ0u652daHAS","OlSJYlQ8kSeG":"niKObOFdk6KV","2j1Eau7BxUKC":"wVWmO3LOTbUh","W8zgSn6oKsjQ":"Ub0CYh5wvqfu","S80BjYsfhlbX":"LLx0PgiQAnrg","gY93Kf5H7rf5":"Fjc5Ik979Bes","57K235ClHdYd":"6RQYBMiEIP3A","k1kJt9luo5O9":"DHR0nhZcBVLY","R1tfrTJJ5ywx":"DynDR0IORtgB","PZTteTtkechO":"x6KVIyh9PxxO","LpZ9v4wIBWF4":"v93eZOIdFNFU","YyFAnbDb1Rew":"h3wUdnZzCpn7","CsQ9aoqmtlUv":"TcWqkGpM9JGl","S43ooaxBkm3g":"cDKtoVqqLXUS","mWfLYeYOMrll":"0nu55idhDeRK","9JD71hqV5F5h":"cknxwabLYElx","VHYspxLdYwxc":"Krr9wMmTWNtv","rXkSKgnfbGVr":"cPoMTwlLVZRm","QAguNM5Bym6b":"4C6s3su1HzYx","924stPTTxwDE":"xEkVqwc7geyy","80Y10TnvABQj":"y7TYwWOnK5aJ","AWDUJsjYxT0O":"fr2cMCIhdfmU","ufwD0QUVlhd9":"JyQfHSKHsDzK","9rBKdx1hmh6n":"n7C8IOgBux8D","HLg1mTKNGDLl":"lFzNKjcLOZML","aZSJTSj86UYU":"kkBMwJ2XQysS","0AqA9vOGQ3lE":"Cec5JZQ6wXIz","nCzQXHezDBou":"kDemscAiwDRX","LabSIOg6lAR3":"wUW12tRjY1a8","HtDtQw2up7iU":"2VWGPKTQBDgW","v4zN387QWiV9":"35jU0fmLrSZL","E4iQiz0pfxxr":"Pzyncwdg3ph7","s6Lfu6VDhMVL":"x8xIoWQxKzFY","Dyafot2XnRGL":"EqM9Ko1e9ha8","ZquY792dvGre":"pdzSXXd54ZNS","LClP8KjaqXpx":"tnJ5VuIlt1Ep","vb3n1fn1rsWx":"WHevWnzMlm6u","wLKpv8wOcWW5":"cc0Syy3vNiDo","vTJZx2CRHxu5":"rbxxvicjYbTL","iZSjttppiiMc":"OBLB5xGkykCe","bfzYBLBDQMC2":"u9J8jM3h5UCX","r0qVPwjOjhet":"lfRYMLPYlMIs","x1gQ8KK2sbwO":"UtAt1q1WNZLx","dZognNgjMHXj":"8K7HBJNRbS0N","MwwfgldNGZen":"3N6haztNPhFc","Yb17rpU8JdAd":"FgGa34XaZsoL","DDn2njZpBef1":"4AVTdLQiXgwY","I8AXZxZGULvG":"NVqSqUe3y3qI","ldiDY9a6JLWo":"5HbYXLG5mB5Z","5UqnV6f4vdiS":"KOOuVEhLn7Nn","GXs4aoCaLdoS":"IHaVgXvcfmRb","HgBGrOdpKBAx":"IQIxKOWuUbg3","Gep01oRXc7Fl":"rmOJDjTN68Ku","JJBe3ICeopGX":"lym8yoTlqH5z","x4vsE7glTK27":"d0MwJXjye32F","hAf4MKmvAK48":"5DbnwcMAw8bX","A6eqUhygCJdT":"UdWNcmbcYJtF","cCvZlSkF2jwq":"Qk9XRKsHdFjw","OmjoO5PMFzTT":"3HWeHecnWRfI","KdoTbpGdGwaM":"gqOU6niNdCgx","WcmguSPUKgq3":"99Nr2olgAUq4","GG9sYn7zChG7":"qJ8fLFGxgbus","rcY52b3fR196":"bZc5q9mbH71s","7v3NkhShBJj1":"t0UXxhFEqEu1","jQ2NXfib8IbZ":"jLa623PTptk7","nKPTBhh1g26I":"CaCBv12e5zMU","ewYBufIfOj5Y":"WwROqqbEoR6z","vIdxi0G1QKfu":"m47RVGBL5ATk","jgE4FO431Zem":"itqGsUWibLtN","nW4D7LiQtcSa":"Nkz7xOewu7xK","rcNdVGQGncZB":"JuF9Ekk2R8Ma","4VejxjiPhkkl":"FjCS37TDepB1","QrS2nrNd6vIV":"6qVehBMpkwMh","Uf2tAe7qQW1B":"UNb58PNagCUK","5R9m7KXAfo6M":"eDguKIOklTyY","8HMRtv2AD0Os":"wKT3CkiGqjTt","QfFMKcW1aQXE":"1xmK255mmbwH","Fs2Q7FI7xVdA":"VHlPp7eJjRvh","7wgx4SoTqTz5":"PFeTcuTOvDdi","LhmCR5cyjU8I":"XRona8MO7vUN","BQed5we81j4Z":"byyW49Ss1s69","7RbVQXtE1IPI":"j5CuUJgMAWh0","4Tavv5O4WhY0":"d2ouquuM8YFr","D5FMm3e3IH4f":"sQvCcv6wngeX","7rWgRopsUQzT":"vvWTiGDWAi8M","WSApKkphhVV6":"kRyEwQUxOLaO","4tHyZbGPyHpY":"HXQpS6X0jRvD","NCAayBxRddr0":"WC70aufneJrw","6uNxwjMHKbfw":"BohrjcymQ7pR","ET0szbRDQUz0":"TvPNDujortUj","gJBZTOCNNNyJ":"9q7DoYEfmp5x","aJ7eOpY2coAa":"YKTeQz4FWZTg","tQRAU7SZNxzU":"BLIE3VpPy0oZ","4TecD9ymeVIH":"iPLbAHXtXXL8","0tjrHTBRwUuA":"JuL3R1HbMncD","75N0B8pVBv9u":"z6G4Ugp7uE6M","jiUfOeY21U6t":"T4gmJhb6M8Cl","80ghWHjSNWME":"cPhXNb2jdz1A","hmoKVxjW0v7F":"Va2O0rWxNKZ1","sdAS3RGYxP3M":"nOZM15SFFAd8","tYlnG9kCD2mD":"ZtaqhtNa2us3","0NUDq98YdrcW":"3kRBevrQ8ICr","nARfTk9l7QQW":"PiJCB5K0zajs","84eQwDvZlhG8":"lfNtWGk65EGt","sHY7EkYHg7V2":"E1nrT3lpEApq","vJLpmYnCjTTP":"LsIsQnhcv77u","ydUrzR7kGPLp":"87Fqi0lSX0OF","2EPvQg4SZ7bm":"dwQCYM8dbBHb","oMCd0KyrtQsf":"faHPgHEyrnOy","D1Eu5989wBmT":"9h3WsyQMmd3w","KRbFlFPPKgHL":"2ayjwqFSIlaC","GRWRIVtb4HN8":"NQIVjXvkO8JI","AMrGmiKqstlL":"LyO1BcdPQ4vu","7zO3Ql5bPBeO":"yDyqhYkq78Tx","rh01VCox2jAM":"DGbyTva8jDno","oKdvfdbttc76":"owUSSuKQHkoF","AHqBCrvmGq9I":"AL3GhTHmrN8M","6owQk7uYuguh":"qeI5U5otHFZn","Uk5oxQOutGmg":"m6nWLj43zNv9","NebI1HbFRpD9":"G4t6L7JBs7VJ","rnW3EElj3r41":"6nVUrmTEhwxw","OGW1KhbNX2OG":"TlWklIIl6jhh","4LI4J0ssqSb5":"JYwTIWWt8n4M","E16QTgZ2242P":"n91DpJXXCQAU","D1hk7GMw4rUe":"zm1NV2YZEMfA","gqBfIqlKOe3C":"JWgEUWPg4ee6","qtcDUjbMhMnr":"aa0WwzGEQCwP","qBhBtcnygJ9y":"XkinxS5xa9rG","3OfrZTcIN6zV":"0EjkaQs2kUYH","JCJNVnEMZypK":"0ZsVgLFhCr1E","YDxhkE0I59v9":"mBUD0DpS3ORd","aiqsQITN2LkU":"1Fx0UooMsR9s","DuejlWCgoCVn":"xylhsZ6aTw7V","61OhOeBOMgox":"NPWcZM2FXo0i","TSs32NWhmBVx":"DCT1KGy17qHr","XhWuq5emaxbI":"M7BuetHvHmfM","l5jXIfZNJiAI":"6SkAvtMMUoEf","C3mxrwB0mUxc":"a1oBBlC8fyhv","NbDQlpPWKy2g":"kYBajrOAl4ay","Aj0EZOEexMWk":"0onUiJkfTfbz","60nKOPU4A45r":"mORcCaipdIeS","c6cWpvF2hEpi":"jgafj0tS5jnW","Lsa5MqHyy0wa":"HIo6KtbUjBTh","LNH4WI9WtoZW":"v6SwjJgBrNFM","wmJ579J6JsOr":"428sZMqslg1l","S9UuFWpLjofB":"DN4yy1QFIUue","EbMACf7X8Zal":"XSphXfi0HW49","VSdFLqQWrc7a":"mywN9SxppU60","EWhh1vxqwCzm":"fRnYVoe7Cmdb","T2rmkTiGbOGu":"UF49boWas9f7","DrNXkQXCIFFw":"8V0OUQGzjwLI","3syraQGEUSU5":"DtvlNkmDZr2x","HP41nSImcLQG":"s6ZW52THLrdQ","DgIF5QU0qV3B":"ik3J36F2wqO6","GjooKViE9O7h":"BG9pHhrskwdI","jvv1fwaw6FtM":"JiRawnup8aQD","h4MC57BvboTI":"bqHcSp4KC58s","5KUxav46k8el":"JlrQkuE5zsVA","JdWMwEWNh9Qs":"UBYob7s4sKbX","57W30o26plfY":"EAZbFvNA44lo","5saRzvutD5wd":"lf5pX8ekVi2k","TaTJIvBc4YOi":"nflMdVHUKfoQ","3k02wJ0GazUj":"pxFIqRa6B1TR","fITCpala87nj":"rLsP6fwt940v","8gMwu1aJ4Fz9":"teeP4OKPePlB","oZoAqTsgG6ui":"h3ja4fXNcQ3Q","x1XCC4YWf6UM":"lXpBos9fZKqa","HpuJmZeg7c3U":"A1Ek8Yv6BK9m","MhisYhwNE5Xn":"Biur6NHNjeSG","kOfsrO8g70kY":"mlcWkr2j8NKX","bLiEAaTMv4oH":"R9dGZ7TrkBbu","9mBd9XBDphHn":"cK7TMyYAcgXg","R1uMgJRk6sDu":"S4oZoVD80Nrd","90DRW5xyFVZe":"fMyNjIEz2GGp","cFfKRsHugOxB":"QbqhEmaYyKIr","HjN9zcGPn6lh":"jbu1ijUXKHZ6","5q8OaLPFYW1h":"AqhhEsB6z5qH","TuZdVY03t7dK":"ZPtQNz065pkc","0BAUm0xyo4Tc":"xLw9OOmKcxqa","rZpTw9bf8J37":"X3ZK1Ca2GOab","ef36eGupHEnI":"siEuIeVCQPTp","KseLznKTxnZ8":"Mgia3KeG8NyI","aQnUOYEUjzVS":"P2EvZkFMUHFy","zR2KQYNiDb7h":"0CsMuWu9Iehr","D7OZuAcYB2OK":"U3F9g1TBOrvo","NjaYiXHPBXxH":"w8W8DX47jceG","75TwO7vF7FvI":"n2yxNVjhGrPw","bL3khWs13Fan":"ELx8bXw0jwEL","XEvoQtMSIfca":"ddkXkgJaFDSt","wI4LM4imbfMK":"3IS4b9GPdK16","Y6aUBTd29mtZ":"iMyMJsZoQ58l","1ggQ4GARB66k":"hNCdJUBP61m6","44nGqwco6ZJa":"3y0sGljg7PjG","ydZRCYk3Ckz5":"tdxgHsR33ByQ","hy75Wiahhyrl":"JAxLdZZUhyrk","G3jVIgqWZht5":"L7LdMsoCUjpR","31NVq5ORNMTV":"JqWwpvOf77em","AqY28Thi67oM":"ZHM1pALcRjEI","f2rzecGiOgYT":"vK66oG9jl6P4","4LwdQGyJ8zlm":"rFRctZl1iTJO","UNUrhh6GAGie":"vTtFTlayaGIt","R59LV1VqjCLx":"UbLOYEj3O6sx","VmtUKW9544b0":"DhPefeWIPOry","CSctuDuQejye":"3WBVrFLHQnFA","KMkrRbqFCm2k":"FxUjFPRK6N34","fSEvY3o23kGe":"Efa5qnl0bmqB","657OsFpNnVyD":"xGCmsiV8XSPq","F2osukuLiTBJ":"vj8xfyj1i4Zu","DQHZSJ1hNwAq":"BJmPDVSblu5w","1okHueX30gk2":"JnKjPV7XxWTp","2I76CSGMcwR1":"csskvSajqgbc","4AdgFkyVYbhv":"6XwjKvTEl4Qu","o80CsRjlQ42S":"TRILEMqeIrmF","MBMQz4LkHNRT":"4oxtyvttPMms","qsSmPVQFVC5Q":"tqHbbkC7vQIO","dxvxpIA0ef41":"NabRB2LBzMhe","rgWBAooycMX5":"ZB0YeBZzHBNM","bVEbvKg1HU38":"2V0TEAlAS373","OsTlgN8UCjvs":"7DkrZ7us5VO4","cPhoTnXubBki":"5aaZaXxnn53H","TYadpObbIAPN":"hbLkJ3A5wt0a","GaWfGq12ei7w":"r1QzSOonHXJd","LThIk0P6UwPe":"PEtkndhH2hpm","a5gLC5NQtNtr":"lLOXPiFRlIyO","EHRUuyoVAKYv":"5EJYmVf1pqmU","jHkIbdbNgt3B":"qnSk3jzDl8nW","jGuSnxNnMLbq":"qDW05tHohGzY","Jxlt9BPb0Af4":"FOOtP2McJ19o","0w0Dn0eDGLeZ":"qNhGy8OszN29","GTMG56HplETc":"PpL4Sp3eKk5F","6Z17OGh2pgHo":"BH1lhMcLhbDA","es1POz4VsnHR":"AUg9jTCnFrEc","jc7MDcqeVAca":"Iat7KPeXsMpL","zmWuyzNIMtR8":"Hs2AJAmw30CP","lfsEvHAhcf5f":"bZLnqiBSCczS","8rhvBTztj9qb":"eCewz2wFiPBK","TnCx4gKqamZI":"8HUEn4xxdKmO","7z29FzwEA53A":"JQCt6STt3tb9","5xaUBKsxtsau":"mBJC94FdzcWd","XkYhe2STS7dl":"PJmiKgAsdOrX","ucQsDtsCYMdU":"mEHysURJYGWL","K2qCyafnnXaL":"8QXvJ4kdkc7r","Zg1sptAXxvUQ":"WOn1hKUXESpu","XiUT3hi3H8nJ":"M9V3RXoaQj68","rZKR8hoWCHTI":"4RptstvlaInZ","OT2XOCJ4D7cT":"dxrh4wxRR1bI","SH7xpCMCKt7I":"2p1kgiFR1gfk","EvJGlYpO4tKN":"sKoU8yufJcEY","zDkFqdStqWuh":"CnCXgtVNanVC","GykYKZh64JRe":"5TzPY98fvE1B","IS3FDZEvQliU":"aj0sAwOBCq6t","ao4e2ai3Llp3":"gJ9pPwYFp9x7","OZt31Ne3YRhc":"OoOMEP77vHMs","mvDkoiE9y33c":"fpVatPe99pDp","EsuPex5HfJKp":"1F8M78NS8pmq","bRkZlOufq4pm":"yNq8jdjqVmCN","Wp7BpOHVckwB":"JbgRpu5ihYu3","X84iD8SnyKRA":"NMjsBF4vzOMx","nWbtDedpLsV5":"FvbI2njnuarG","Q0jKMG6if6YS":"9YZx5w8qVYdy","w4lVP5pAM1Pn":"5dsffzc2KlSy","HLWtzBkmewN0":"fR5RvrVqDO1m","ifKXiCybZLt4":"1L8ZUsd8XMjE","smAZxTDocHzB":"CWoPE5MLHUwx","fWbfiuv9jCf0":"cTfgmOx2PAsc","gsQ6oPsZHDJJ":"wjpThZ0FE1Qq","U4Qe2iWX8OJq":"ZSHYel5K3uo3","cxOA01uydhLF":"VQyIJf54K7Zz","oJUxsLgdTWT2":"kTElgdrWlKAg","4LJa9mu0K9c0":"RfupQ0v763Oy","XFUdARe5duhw":"d2Dfuei4Gmh4","pj1op2c6TFax":"XS3jxRKjgjNq","8utNPSImWYD3":"rfa1DowGWGc3","qJMgM56zUnSL":"AbQS28Z96V4g","AYbsfKbRqKXo":"NaAdUxXXu7kJ","fD9MMzr7qEeW":"Pb6dzJNixURc","A2aGEsodZHGN":"czMTsc84ChEC","dfyzd4KoTTIs":"n9bMoUDxEO7p","VTlQ3LMFV3WZ":"xbE60RBaLshp","NKhU4KpEXiug":"3rYB6OpBPGCW","ZjZyMOczMbkB":"L2GEHbESMfrd","F0o4LwpIgh1C":"fnkyYalhKuq3","mFdtJHxeXw1U":"E5Hyst4vV0dT","yMGdxrUopXGh":"6MR6ljxa1CZj","1TtvOzH2QhxW":"mSZpjSxymSnK","bh22v9QPXGlN":"5ra77WTBhnnj","GnrrMreYXXne":"uI6C6vhRwHfQ","JGMftbhvr3Pl":"mdHnvlxlGhCA","wLZPcCEDvCh0":"h9eWYJv1Atdn","47HrDp7x6hTp":"FcPWaTp8w445","MRBg62tKDKYq":"GyyF1RxBuRLH","qQI2Pnz99iB4":"zV5gjwoOecZ0","VDldHTFFGDE4":"6Sv4R6B9R1Ug","qlgiCyXLGx94":"31p7eGhB0lrS","qFRqpRMsCfZs":"v61AZAK7UI26","UQEw733yYQeh":"VAt7e0OqoWHJ","jMdiZnQkxsnL":"6InRykmPPAvS","RZNPV0M3hEqL":"v6C3Z8GPJtYd","AhhAWZqo62nD":"HzTeCP6guaJ4","6HilqjEUP3pa":"5cVQL3698T6T","LWwiU32NYGDC":"xd9LWekFAW6Q","1orZjvNLRQll":"qghwZNhVzmi7","QMpNpMwYt5T0":"FcZKSmpgToFn","ZTtABOvD82dV":"hC5up8cqgAqU","CH6sh1cZRyLI":"qjrIHm855OLO","YlZ9sHtXysvI":"08lqp4fanMXS","X1gDJ3MWLtGa":"fwOfYjdLNOcV","67UBDptVCILC":"pGiyfkSxmuN6","bbnOceNXQLej":"t49iSgwCmGQP","7BJBEnO0IFBa":"1Tz5KXg0x6dB","rJsNQu8Ln6Y7":"262dZs2ydg1T","1g8S8k0nW4b7":"sL0XzJeD1eaS","ybButkoOrrSB":"sa3JmoEb9gSk","a9yQd7Jw7hts":"otd26JCYTmer","nRhmaFYBLFl5":"yt9T9tedFqdJ","zox1gQbPGmyX":"bOkCQjx4PhMg","tzJPlYGEPuYk":"gPJXQikijEYG","GR4R2Rl9UFp3":"Qv280QS0Wf5Y","v9tWdjPRJWTM":"XjEka9PqWzqJ","9YJFremkERzl":"AoxrTWya3L04","sGLfpNT7c0IC":"peUsuXEDgDgu","WdHfkepUYkQI":"mOhePF0DiuSE","qWWYYP3xad2o":"Kq2AGYhZhYvA","Vlb7xbXpSM9c":"61B1NO3ndDsu","pe5u4y74ptzC":"Gr5jOdBaRDFS","LMS3XGziWdCO":"BfGUJfH3urdz","yaw4RmYXpXD5":"xxYeyHgSZLr8","fcHkglc2Ve8T":"NmxA3dK3wpiB","NRLMXRz9fBGP":"fxYrxnAHwvxS","svzFNBakLR10":"c64vdTbbAN5J","sUqc9XajbsGJ":"jBUFHXTJ3sGZ","s1xGpZeP8fK5":"18J4R5XHlEW1","COsRQBYUXn32":"vdrsSqqNb3qB","ppPa9aIaG4kg":"loTHBozyPRGp","VCIG6rEZD8Te":"7F3xRJ1I7aki","SlK5wXw17uDd":"mDhmyz0RUq9Q","dugvHGLhG0nj":"EPQtybNMXzOx","okcKt2tWjnPy":"nOdXmUBcDbnS","of4FWOOLnZ1y":"CbnE3u8bnFuR","ZJAvzDbjlsnc":"qgK6zpiZMkVt","L8wkZUVhcFb7":"HJshoncbHIvp","D72H9Gwhk3Sp":"9dvjFJZK5K04","Eq3HhS8xsglY":"3ytiwV4YHsiG","j6pS2NQc0vRk":"o1twims2Kxpn","TjbCNVjqscs8":"yXYDq0gajGT6","RLLMorau4rN9":"1H2hooUiUv6U","gAQO8RoOp9ae":"f7ExwrtNxMkS","nArPBHDKcovS":"6mSxs1wPmCLX","M2z6yEDkl4U2":"6blK7QjF4el1","GvjDDpFVCWZY":"JI5Yjhgn38Be","v6KHeEzsn9ny":"j6hKoG5OZb4n","jk5oNnA96IOy":"eImmwLAOnQAi","SazozzR2VvX4":"2BWNjwrxDZr0","PDhEY0nwuALD":"XvWgyvbsacN6","2sukH2t5RFsS":"N4Msk8yLt8JO","DqoDPnEYD7nT":"qMdkCsor1vHk","T2qftkbmxQOD":"hfABmmehkOB7","7aLcIp8iTaEq":"qUcyTUYThvhD","PUCaOERi9Rw3":"NxTRr7m8aDn0","Cq9gqfCHu6pO":"rMNmWr1Lz8x0","BFOId5hUNLnw":"jQwv6F8LLNGW","DgQZdpDOGohM":"4JGRe2DXQV2J","lnoxffRPiAYk":"5lUalOwR2BhY","YUi9Kk6ZVmgF":"dn0WimENiq98","0os9Uv0JVcgF":"n7fzO0grKqnr","JtXUzzojFHkv":"f9RNEOslDPfk","Qh5uOTy8cjp8":"ImIQt1p971xq","sNJsbX5w7VQd":"ROz9fNYgWEEK","g38hJbVgY1HO":"2WCxf1efqsFO","PyBlCz1jqQfe":"ozWY2rCbvGcP","vdezYVCSYQFy":"sqpNkx2rYW1G","VrsocgddOQCC":"sg2hHyG9HbMG","KLtIR15lTleq":"kY6yw7IVPPUU","29L06PodtZuU":"vfa911CFYE2K","A0a2QIeC3NMr":"kA1KOmmHoa7w","SerXDsUqDP3R":"rzVBPeeNI6ty","qoUPAgefdrpF":"9Fe9TzjWozsf","QT1etG4orvDn":"q0xkkUE1Dky6","yV2XN6goprRh":"DEZRWKgBbZav","q9H3emKursBV":"sVrwBy8n0q8G","NAK1cndYJn3X":"WTE3On0X0rwO","47dxlNHTfFji":"AjBTVTOREMSl","tNnBUK5OnY5m":"fjuN9DxADqvI","b3eyISAB6E5W":"PoC5C0q9vIbj","kfyP6SniOCXQ":"82L66MFtaVWI","1uAGNz7qY6E5":"R5iGgdjn9HAk","4EVETZVD458P":"V1x9spoq9WkT","ER2gxrqAL9yS":"oET1xJf7KVt7","gpClvkttXuS3":"5aK0YgN5MBTT","LdTQo1azTwPd":"etrymkKwrHNi","6e9iECRrImlk":"aAeBumRlzx5a","bT4BdDp0UUwO":"1AjTZdZ57p7p","KOUctUClftZH":"wYgtoDDoacFa","StYo50X523fW":"vB11JmIGR1G2","vmE6go3APK8i":"ynYmcfzliq79","Ojoe3mqLmleO":"WLTUHEPe53Na","8V5Xp3PJlmQL":"lFn1jZMFTht8","ivDCJzh4wa7k":"sBBOQb2ZaJQ2","QTq3ZFVSFyuv":"EPjjTMT5YE9B","pma6bMtaSSDt":"qPbTD0Qj5Kfm","nSaRmnFotu3O":"7m4lA60AiicP","63pGBsSESF4Z":"ntMxEltQWQFZ","Ro5xKMAdqyl0":"MSD2ZuSw3Dt7","gkIpZJr0mCeB":"0G4A8ByNy6DN","EFlujHjRfqQq":"zSdrWmersemX","CxV14F5Hl7DW":"HSpWAggkaGx5","ltGGURJfQWxV":"kDvsLaiEokJT","WfYefZxdu9Xu":"AqWeFtaWMQBt","y2B7F0UuyB5H":"OkbjL8F3GZXR","3mNlMvYXwbOV":"FSBKparmL1PG","Zj0NW2PVyVqE":"CO6BnX5EflRI","H2uorOEorUYp":"q2gClFdGNMrA","QmQXA8fT4w1t":"7TppsXomO5Zq","5n8HYM7WSNP3":"rvdbJmM8Iljf","U9Tp6H5zhcI7":"TkMbOW2aevZc","0u3NJqkZRbC0":"RBU1aBK14awd","Jp8bDrfZAfDd":"95lorTASIrjy","wKbSS5zLbTbm":"MX1NVlv0hZ20","oJZqBSd5eZ19":"H9wwwHWk0DTl","kjNx9MKV41xM":"4mRSbos8JSD0","zjtOYvv46x1l":"ZoeePYhgs74X","UTzGDtS7mpmS":"dZhPpN1qyMyz","YLpbtOxFF7xw":"GULqrUG3tuDY","M4hTLTI9xSk4":"bRA50vLJt1BZ","rOQoKybz5xpZ":"PIUQPGQ8Uda3","98w1ICfxo7oe":"nVOKwkxGXyCN","a0n2CYVI9J2C":"P38ctswNBEuN","U1uDLend1V2M":"3yFLKrcijCmu","lXZ2F1pEZnRc":"fc0rLDRwgVoB","myOGRTfG8erK":"jNlTdO1dwn2s","TwmBhUhnWiip":"NXwGfASgyDCF","JiZyotQep1jY":"phhQEpPb7CFG","J9v6IdEQkGqT":"oEPnXeiisCa8","xjhLjenXAe01":"J7FVg31429ge","bDLwa3oNahDc":"8P8Ah2w68kZq","sVcPT2EXfLZB":"wW0gMZ7NFNMJ","U6bWfcBke2OF":"BxtBuxE8qEkm","qWpt8qZvuEof":"PHS7O1hgE1P6","3WGDIfV5dtKS":"WWwDGJAdi44u","pgDO2NrCUwDq":"ZTZna5iNNBAI","pT4jMCQfKZg6":"evGrmxf9FdZk","SmYq2E5HzlOi":"2fP9nUU9vUcl","eF57LSofVpBP":"1RF1IFh2xN9i","qVl1q760F6LN":"kjLVEggomxXW","pM7S2epcQrD5":"BrgKqjw9Q52S","QGnIKECFiRrE":"iMZRw1rscMRJ","O1Y9cwtH3zaf":"SCrlryg6EMwP","liySdARq1g8h":"zzfYzgfWHX9r","Be9mINCzhyiq":"keIslaXpIh32","8DWnwNNCCr8u":"IJlA8g9nIaHq","FnhVqdiyDjV5":"OvMtnuafMI4O","JHK8ZUPVSMCe":"Em9FpayzdRhk","SMrYCPtsARAF":"tbjQlfde5zqC","QWdj4SlIEmI0":"HP30f3vR54A2","jo9xU563vRPR":"b3r41N4aCXgY","gb8AeAe81vDX":"fIDUupC9lLKx","bD2syk3K8Dw7":"tKxLYMvWnr5L","b9SHRrz76hIO":"WqlYnYrhMTKl","Jpye90cjH5d3":"KKuCpx7Jxpdh","whNBU4raE38D":"RTRGRb7JDTRJ","aGQCMa9vdvBV":"weHTzVJ5JkaP","tedeuQHmS5EW":"M60KYDqjzZfV","QJvfBQKFvzKD":"nN8x1cTPNP3x","TeB3alcfIyJQ":"Wz2ST0A50RQR","6nDnnibMknHg":"y2W4mDVLfcuT","6Vjr7Nrrw8OK":"iRUeaW6wb2Su","B8SMUz9H3442":"xFqRq8gthbaH","vnbDTLOy6EKb":"80hgscg1bQSH","DxfUKZ5qrVAJ":"kVajosIWBPCs","nYebxik6Tpb9":"w8JgNCiHnseO","1mzxiVuxjLmq":"dQ1lS1h2rkPS","tvfa6XMeXla6":"BABBvqdHa5Ax","JUnRB71BA1aK":"wXTF3Cr83DQ2","b3WAQ450cwNJ":"INbm5PX3641C","2q4DuBfvKRDk":"6KQpLX4mDVXx","d3FfIlqzaUXY":"SeOWpGCr2pPC","khiMVynWaADO":"FkNbYn1BmmSS","GQMLcYpAEdZp":"E5GoKrTIdbhJ","hGUGVJbg37HI":"3yVmDJonfEYF","jRh8sPfUYkXU":"2Nmsmn9xohZR","dHx3a2MJBMyR":"MXeIfbzRxL7n","tnxueImqMCey":"ih0RvMHvTKhc","fInLuwp0lt01":"1hh05ENeLGo9","pUHbyc6A7lgY":"HYPSTuKHehx9","ATU2TMBSOPRD":"ek4aTGxezy7Q","pzhT0z9yFRhk":"ZMdkonfdH2xq","nB7R6gmTIo2C":"ayYik8AOLwGM","oYnYa5KRAvkh":"d3gXzdIaRuCe","MPrhvQIkNYlI":"ulSXWPe4nsib","jr7KDdgCmbvd":"7bucpdNQUh3H","d1KppcUVzcnN":"GVaR0xjUaVUk","HJT7WDc5MFz9":"uLq3wf5FfJFB","TDb7ofSDZZQi":"QkZpbrEd9HRV","smSIIGAK0Iyl":"NxW1boLvekTe","f3RwXFwckCXt":"df4EUxdOpidH","H9RUlNhjFPUu":"S1XKuOYNMPiO","hCGXzc5qdtEX":"KZxVHEWbrFMe","rVoDtuMcCA0U":"OU9DMaBtRgPD","j73xOuir08Je":"W2FYGl2oT2zB","N7kQmrqG8kid":"yEJv4J20weUo","Nf6CpwCPTHle":"fFGUyBHWYIGm","RFNNe2hsXPmZ":"X5Bm1Dpj4v8c","mhq5KmUeCDj9":"5nhZGGJ5RNT6","amRcXlCAE3Jl":"aB2p8ClIEoWR","TKAzzeeVBouj":"FO9WC0nNnbrT","y3TafwOqs8ma":"zDHKrnYyk9Ca","Qto50uWUT3hq":"fcOlETwHqf2K","lWiLHcROgmZM":"ymUyOgX6dVP7","ka7ux02HMZ1u":"3e2RAfG7f2k0","I6ycqU7PE7jn":"XCqJunLRKjfA","V8kmZmDRoGzg":"Pnkt355WRkXR","t0toV540h87y":"f1uBK62hWudi","XxBxdMNz8dHA":"Zm29mh3Mxpg3","QHltqV9EFjqd":"fNdRNdfuKZY0","0r0GaEZjPP5C":"WxXmFiIDItwC","vFUXGFpSkuym":"en6gt9uqXbzb","P1A0DvRmwYuZ":"zFhzDvCHcz08","0VxoSPhT41X5":"nLjeCxEjoB3V","6KKgWd7OWUje":"GboHQ4FpWomT","kjohU7A8oXHS":"D0jcCw4YqHMU","YxIviib8zZXB":"ymPZiyVVFIQi","tW6Ey93hjHQB":"1iGMaa0RTZJ7","t8nwoxVbEfab":"okOyNLJUnQ2m","A6ApDZ5aWE0J":"D6c5qxop6gnR","fLVUXPfLPAMW":"uEBJoXAehu8j","Eg9mQrRCWAOj":"csBMcEH9WigJ","9vxmVCTCVL0p":"oMkF6yE97Vgu","SQ4Kp3VqJp2p":"p34vDE3qNOKO","PaYKaufVNS2R":"yNQtgduUz72Z","YTwbV5hze3Fl":"5f12gxlE1Zqx","lcNQN8j0SVHN":"4fGe4r70hy1J","4u2Dh8rI3GkG":"3pscdt9CvzA1","YOXGzydcC9VP":"DJYvGw850J58","ZUV7mtCMNs9U":"MbIn9dEvDnUX","HwxlncO13k0n":"cEwEsLQobL5T","ISoMAwE9Rij3":"X5jN8uaciRI9","AaTCkQRKvqE7":"N9N9rHw5Qiig","F8odU3gjoTDs":"iUG5e38ncdK9","wx2OhH2CMWMP":"VfAGF7u7gnia","I3MWBsvshGts":"rawwBtyixDTq","p0pieek9p1Xz":"itBOnBi3cxT7","qMtb0GTWNWRs":"5CN4QCdBWune","kXuJLENy9mnN":"fWloB6KAicvW","YTtCC47hNgbK":"Xvl70eg6urUC","lFXZfZmoid6r":"LesShLqbaLEZ","6qqLPlt6zaar":"QyfHYJvf37oE","2VgehmLdX2YE":"RwRHF4yTOb3U","0LkTitTlKF92":"TRYFYKtsSxyE","k4jnW6HMPhKi":"oRjQBM50Ta1O","B5YUvQfpfXqQ":"GK1P7WnskfR1","TwMYr7mhvJ4P":"o0UmcnT3mlgq","ZJ018JkWsGkP":"UtHAGGjqdRXW","5KLIFDUjotpH":"2R5OaURMlS2U","OkAB1lrvxJDW":"Sc3bu4dRnXRC","FFZ6qQVFd18K":"gko7UWQLMWG2","vTxU31N18S7t":"t5U2YB0uv1ig","Zi1acZH3Xmab":"LnMSED6YYrsd","wHy2jgcnuc1q":"gPpU76jqWBrB","Jg3YyHqXdWAB":"bqSF3coDjAOC","kKLfC8Q4YvYl":"fofjmXQaBXMm","6mKWhfgKVmU9":"Cr1Lxg9kFepL","MxzHIJnl7MrM":"lkrJ9MFNU4ik","nQxb5TgWMMWi":"q7F8qk5WwBzH","QaJMdZnZ2H1M":"sZHFSzkQ1BJb","3hS1C4deXEx4":"zM7dw8NZor5G","vNYexmGvEL6q":"0szHhZzti4tv","tOnpVCxnXoEu":"jCFhU913d73n","fu1aIC7zkoas":"0nwr61WDJEPm","DRoVXxUuNTVa":"cGwTcYXG0qp8","at7kKSu58zJ1":"oRD7mmYKf0w5","HxZ06082Dfks":"yock7xAXmbnm","vnHIo5CEpHgN":"dLgSsTvCbfMG","t4zRg8itniD6":"IH60RKhvRUvy","pBQdVxoC1rFV":"5WIeXuxpw37E","jt3mt3roxQNv":"2bupAXQA3niM","2oZaMIVch5q4":"IE3RPGBOlynx","sxU7sF2JEkEn":"XKg72D1grQMe","3u0w3cyZRnjI":"KAMTEWghWm3E","2TfhjhjPbcdH":"DnJ8G4utuTtQ","rlU7OhhtkzL2":"4g6ZASvsOe25","KcqpRdvSXnhy":"rZmD07RJoWoU","NcWMFekHrRcN":"JDLxGiw3NLD3","8yXpHVFc5JAB":"vL5B05BeqwLv","CnIR9QmkeHvN":"B7euZjSgUTNt","Nkmm74FTe6y6":"qidbi1mLCXbQ","u6gA1fFbY9Zu":"G6U8vvHeXavO","IR16ernUh3fX":"aTOVhBtTW48r","kDyohSfvqY9Z":"ci4Hn7tXhyPI","QSwKlHmSzNLy":"k3Ce6smD9aIR","k6lILhtE1k7H":"GCoHdPFUBgLg","CgwD1aR7AdZN":"dFGw9YraF5zw","1Nijdi5AZbYj":"5mXYzD1mNRvR","EJ1wecFiYYbV":"a0m757XqUOiu","NcvAiIN98uXT":"0n4aLvdkNatr","7YmNn3GrDDKd":"wUoxwEH2qpKH","1vS6Yt0A8aMV":"sU9XQ4JOK0La","hiKF4oc1j0yi":"FUwEw1NG6bWt","ARFdQIUhmjXL":"AdmyRikYSqFG","Gp0hdow0Zfq9":"TUwRcln3JWC5","7vPFW96XY5Zm":"pQ4AdZFjzrRQ","ZbLopQoTab5d":"88JrfVY6bdde","9UThSweOFpCf":"0CzraLcRZABw","eJqHykbx1tbw":"C0gRYWmvFrSG","URxrL4aK4cbR":"V24hvyhJmH1L","XYxeyqi0OFJi":"cctqTu9g3MLN","HhCGGfvaLrPO":"9Sv9oBPqqpJO","9VSo90qy1Y14":"SAJqxwLmtRoM","hWrhk6I2kAXK":"StjSsc9qPKNn","WM0zos2OAndw":"c9X2gWKqldYd","qlADRWHJM1cS":"Z9f0R2C6lA7u","WAdfaioS95jm":"rCdLnwqrVjxP","QZmTOyAn3GvW":"GPEkes02Acvr","ymU4HfdqW0pw":"7bRQcIui1y63","umnM3JgflOJ2":"gIbfts9FKyNE","zNJK63R7huNx":"dth5heaiGVMd","nNngZZgbKRDn":"SRUr3TgOuxgw","L6vU1KYMjo9z":"RAf73oKqW11g","BvkyeeG2G2Yh":"vqJuKoPQa3Y9","XbNZ2Ey7PgEl":"3p0XyfpYBdYi","jC7jNXaA0vYc":"4FWiYRiWZMe4","sdeeo7pVA8fe":"m0FoJkIYYymJ","1LvSx1EbCQnh":"VDi7M5yiwUWD","RhwaT0cuYWhy":"S8tdMx7KKfBM","gDJhg8tf5bhu":"NQLRKHe9mFp1","zEKFLk3CQ4IX":"AKwrKW9EOMdz","y4tb2S94MOal":"4H11WU4AzMGn","NEcCoXjSH88a":"bPxWhXfdE1Kq","Hp0zGVReMK2V":"yC2tCyv3cpol","zAlwLQM01fSI":"f2MOs3diYXeX","myO4Phvh4hfq":"fPWR0Wi0p1U8","WUntVpfGsHbC":"hElBznUzcCPX","L3xj8QAWIqxP":"wlNHIBJOONUa","ZmRAqqPei7IF":"GnPjQEHn6PEw","IhrrTMRN93UG":"z142uVbErDze","qHRY26haVDOP":"x3iUfrgCqtKi","HERVs4aDNXXe":"S64hS6JR5oAx","JQbGQKSdIpzX":"kPxA9A1MPFCJ","fwGwZdRxiKTI":"wPwM6icJXqYP","u0ueTCs9BxFw":"PydaFRhCag2c","xu3WaOWq35xr":"VnWO32c5JRuZ","lWwkWKsR6hSV":"U0HLzSO6pKHN","Fl2LKP7tG4mW":"RjC2uDnueYBN","vfcNW7K5kDbe":"PMglcLMHXkyl","mBbkes7I90Ma":"6Wxwl9tK5aNK","WGcanX0RUUZx":"YZsvsoHCrhUj","L68zU2g0x5p9":"7Izcxz9eNZZt","YQ7IVT7hKsXT":"1jfzGrRIKlYR","6gZs52qXLXHc":"FtLJn3kRLrrb","Ssq3QWfGlLsF":"HKKgjJ01S9ya","0fAC7X61n6rF":"NDjiOLOAoOm7","ihRE92Ps0daO":"5ASMgYMjsshH","tTqTaO5pZZ2p":"aWSgAruom3Bq","h6aD0kqTfvAw":"Ugn2cZU16USd","yLpvXW9ZEIdJ":"HTmGXyQ7IoDu","cDns2nle2YXq":"b85NHFN2NzKO","QJNewJ11iXQL":"vxIjMfUIliQv","Qj3wHhR82VeN":"nwuCSLYjaDC4","2Zcna4YXy27x":"HCR8kZlgdr07","5DIBuh49H3MD":"ac9jFgjrVH7n","j2EpNXXq2aDy":"fYqtG4ETF1zG","toab0qrJikyQ":"hAPIJtfASz0Y","g8HOVDfXTIJd":"hi1EbBtzNYbd","b5UfpOL45r38":"XhqwxWYW8nv9","uuUn2kz4M6dc":"B7dxm4GoOZY5","AWmysBPFV50W":"fxzGjivlfrXU","rZtnm2nnpbAR":"Z9FXkXuZhxQq","Cb8DK69AQK6i":"UaCsLV5qcead","ZLvz5sTBqtj9":"07zBybKbJ0mI","4PhHHZw6ojut":"97RPkl63A4jy","NoCfq3VCRUCR":"qt7aerCcaen1","Uzf4Ubsm46Db":"PnP8OBuAFEJa","z9oGhny4OJSd":"6ITNgt1Ll99z","pwk11002S68u":"r4VkF7NFxTY6","V8XuBXRswHdm":"QKfytiUlo2BS","Z3uhRwyFi1UE":"cRWO7qzfOSxC","ItnTd2v9drTg":"XlZrDe0cPXQZ","gZnCVyECyVFe":"dsEFBE3xBKrB","U8PZFskrwCiT":"CgWnGYjuSXPj","eo336JxZ8yUg":"vDWqn5b4DMyH","1E0I9sKVyZCh":"6SH7LqTJAARD","JjgEXsNGLfmV":"fECQf0xBiH48","aHS1a3KcGSgc":"iIsmRnslE7wN","EUD67YPyPeYE":"E2dKS25Q1hxZ","Hp7tooyjxMEk":"NRkBSSXyp3ty","HHzKucoGxpQj":"6EN75PhYCsXC","GPN0JP1uwYvp":"jJEqrQOrlfy7","WzB7WnfcyFxP":"TNuzPH6FNES6","nyjCO70gtzYb":"vynuGNbwjyAd","Ul7JRyNXMvjZ":"IORprtTlT02q","xev6HzmCbhtY":"hzfPcufrzXqi","UBlmFkOTRLEE":"YnbiBpMu4Xd5","sx7zFL0OWAly":"EWIbM0u8RRRJ","SCTKNSwTVbf6":"r5zgH6GIcRGj","jmCMGhPoUEbT":"jVRA7hcyXoQR","m7CBgb8gDMsw":"n0Ubgsp826Pv","cSX5hsURPDbR":"cAL0BQDAGyqE","LYlC2AAnITcu":"2rCL65sddEJY","FEtWavmg4j5T":"QBSIpHowycFr","4n7K3all1Ufr":"uBgSPtFd6ooM","2GCV6LmwBLuN":"awFFh9MOQDWD","cSmlQDM9SxBK":"ySIjYTmL7Ckw","9rmSEexmvO3W":"wl6zS30AtrLK","IYfrSuuyM9yB":"ElwCpXiYEN7Z","ZkITkEKBeQ8N":"3QhaKM2noSfc","6wDsJtHAErbs":"9wnZJyDlSwjc","TyuAXClmuiBj":"uVg4q3jfmY3q","2aX1wy6QTJDh":"PNNFlInC4dOC","cwsMGPql8NGZ":"hoUIvcY4uSRb","EVFlmiUyI7fl":"b5x1QRr3oMr4","OInXeSKNvNHz":"VaxQ1WUXUVBV","BrrpbhSL1AUh":"ctMwU3YsxRuL","GrqWTww38OZb":"2aOvDbUyUzMa","9QyT22biEGCF":"SE66UpLLZWAX","A3NVXKuI2EW0":"KV9pESwmx8yZ","g1El6enQjSmj":"UUbku4HwwWWz","hFdMn50vXZgr":"d1DtOHbsGLu1","OwlUiNzGkOv8":"4IvCG0qrjvOb","miGPtLTnooyJ":"DpPtcz9XiHOD","3HeU93ropxq5":"gTPe31U90sdL","TnCx7jjGEajM":"VZqSdYcTsPMb","Hc999air8Rb5":"s8xRYGQIIStp","iU3vIBFMMd1W":"jGtq2wcQC6tt","3e94GoW9SqKP":"zAkSWGFOdVi6","i5ZHe2LKseol":"c8cjSS5Vc3XX","yw7LF2A7KICw":"e6uib9Z8QUSd","A73QlEaF7unJ":"KtaK1OU5XQ15","DGuDu1oCshqa":"EMn3ckQcH2vp","TZtjswbi52dK":"R2KrG4ZuMd6G","MWlk0CKp0NnB":"5AstYVUU8AIw","lMWJho200Ihz":"HNaUdz9aFAJe","uQlnmY6pmmjG":"tcUfTbOjUlNa","iB3LamS5Qo7l":"d5KP9CzcrGBV","LzKbCeYCaCkS":"WeeYqKQrFzUB","rAPmhkRE8K0J":"FrAtNbytT5td","iyYojvbxMAmk":"vigaX5aNyWag","PMGQ7xxYD3WN":"BYN2oX5mAx9X","91JaxcQN1wP8":"Bvb0ya3bizOf","u7Foqh2D265J":"MUTmrXGywliK","txVq2CfeiqAK":"ArFCLVUR8Usz","jObdGTc5r9PJ":"MBN14vk11hfY","Gq0Zc5mSxiUK":"Z6vSTp4G7Ojw","Mxfkxax1Q44h":"OqWsVdjtrMrk","PPgHeOD2AOtr":"SFQCkU6PASwD","paBMqztBYgpf":"fsYRKa8IZKRT","NEhK7FmvKoCA":"6MB8x4TkRx9a","2mPh5fAKnR4r":"zIwPEsD11AyE","uadLi8pM9YWn":"x6YoczGch4HW","iI0btRGmfRlG":"h1d3k52HjqA6","hClABZJCSeyk":"RoBWqEdURNlb","GpzYB8neoiP2":"pxqrY8SKYtPo","81fczIufn3ZE":"HEae3ShRwA4h","2uF8FOEGJYaO":"zcAxA5BcCepE","hcmWabfXkUiN":"2jBmrD9mgxp1","mhg524ogoqSR":"8Nx6iDGCNliI","Wczl2xUNbBvm":"emVAa3Yj5QjW","idb1dURDutXM":"9gOvP6xTV1my","8oUTAtZqellL":"oZgijlvEFBhL","GEHX55PWDqA5":"hraWHAOTlvE5","QBamtP2xhPmo":"tJMy6bUzRlB5","ma7X6Pbp8Pdt":"sARI0da5psx4","0JQB2rSrepRd":"hmWwSYLiGYXC","G7YyLifzCpIM":"uBLhVL7L0x0T","nwR7SbP1t5X1":"9MAldVR9hIIC","wnS2QFiQYtOG":"2eeqMEcKJeBb","kXO8W2TAAuuv":"TzY8E8bshR2m","iWIG1OC5xSLM":"JHSX0Ite96F6","OzRnuU2qjT0U":"MAA2AyxdnhjF","c2HnlERwwucp":"O3pR9zkEbtIR","lFcWd2N13JGK":"a3dfGNseNIXQ","66z1N8QhN2hb":"k5D6SsYI3XZM","2QuEcMrKDjBl":"1BcyzYFYBKGP","H4IwsFhlOyLR":"0L2mEEDzmIGx","FTW09tQdf83j":"vRUphpqfymcN","j6zbr2GMptDa":"32LZ4IaOwFCX","rk9Ovoyrzq3Y":"epS8hUYotZKm","Lu0TUZPRgCA8":"D6nS17yLYLhM","0ymayDYHGMad":"lDWbFyAl4bTs","alkig9oDF6sj":"A9fRBNyWpGnB","aQBFEpoaaiBt":"KKVomHWXSMfZ","ZH6enI5PDuxg":"wC9RwhuAwNkj","UtG9EXpBzdO8":"ZtKxJNluaUC4","AY90VuQBzKfJ":"lIXQcMERknTf","BG8y6B0Vo4TX":"gTNU5HtAuu0W","0WtAMjYXP3I6":"x8CpyVcZIVKL","Nb833prM2xhb":"lEan8f7WK1EX","xb4gqZrS5tvh":"RHUSstAOS7VY","jl3rpmtZtFfJ":"eCDfO3MwOE1H","yhcJoeYIGP6Y":"DzKXKfJmmH5D","SuX2vyNCFWKv":"iLpYXbZQn7B4","IMtoeGbcEYhr":"ocXv3eIbqIR8","UHHwsJj1miIm":"bTvP3xFVslbs","ZFwYrJPLZgPY":"IjnYBICHv4mV","CqJ99kxBswIE":"AukRpa8z15Fe","SgpPezrztdiC":"ZstoKK3ZEeEg","4ztVQlEaGNii":"GTtITPTmYfZC","3YYfjGwyxO2d":"PBuItCJlbVVq","gMp19f0rzEEK":"2hQplKUWnnYV","85V4rpvlMNKn":"dddV1TaCfOlP","dLvAkmznMDEL":"vBrUhRxoDSTz","A032tt2k01SX":"W8JM5ISS2nTV","aRGk1uOFaEMt":"xDyzJHEvvy9A","cj0fUC0RXPKF":"mfPujQMygZQv","1ICckOfsLjSH":"bosZev05KEK1","zrAVTFefDcQ6":"DlUHFBB2aZB1","JYixnFAF2KNV":"o5UILJSMmtB7","JnYgPkCiEYDJ":"EhVLz6IQTHsX","qi4x5Y1W6F06":"7dT2WSbo8swP","wVf2rKC7Pmke":"QZSZEzEuatwT","P30I5ahEHwIO":"yqLHKlVSJHKj","bd2cKqKU9gju":"UBBQ3i4451PK","uvnvpK4GJ4Mw":"G2WEaauuv7nX","q4SaF0PhLT2Y":"lRvr29eaRkZB","a8RF7hK4Zguc":"c5S9t6yWmhpU","EKpCU4mOA4Z6":"q10hHuDlR0wb","6kXF5QVNZyRQ":"u4QKjOVOdCr7","ze4IiDTX11pG":"vYA8ilvrD0XO","zaq1HSFvcbzw":"kICkFrNuhpVu","YvgEYvaTOjBk":"1BZ9xSTxXwoH","RK9SEOeKz0gm":"CkEO4joYWaSg","foTYLaDo9uh5":"KRhp2293n1kx","WL85DaR0jXEx":"j5gX0dlJla0S","fWtWI7aiCDbv":"oC5W0BiTxBbC","q0tInpT75wx4":"MGBputMr61lE","nKKJYm8fMUDH":"mgJdeihAC5OZ","s9zgpEF2uxMw":"zop0qT33JLv1","XH1V0TVlHqQ1":"PMKRKYyAF0Rr","clnypFLFfWlu":"jNXo03lycJpC","Y9BHEr5LVD0m":"NDTnnTWVnvV8","AEj3oIKUlPnZ":"FYPZeAXyhy3p","OgtOUnrkyCUX":"oSKWyoZ6MScL","nvIBzuTFjKPW":"mRogNZaBIRzk","T0mM1zcNgfeJ":"x2Qc20pEtBo6","9lkafur8CD5i":"6cpoy2077du0","fJ5geWyuTDP7":"MMLcwdnmqcZZ","P0uvHxvkIBso":"M0qZOeEjN6qa","M4zUlo6m3i3T":"gqSsnwVipvcA","809JZo5hWbBM":"34S5gw2rjn9N","ZmkTMsXqXgom":"TsgSo9wvzR0N","5eTm1Wv6mO5r":"Jxfyc5WnbaTI","o2aqOrkSg3yJ":"jqfO9KCOyYKq","e8QTzTKOLShv":"mDuF55pqrB2K","rh8CGQZcDrTN":"P4GVwYBcYdX8","hecVsNj0EixK":"xSCYRH4ALzhP","5kE3GRunXJAA":"7d1LHr9bHU0f","RYXsC7PsgdfP":"GtXFsw8JS4Jx","uf2bqDCFySAY":"2b5iTscIp3bF","mpzADOCAohzQ":"xhKwrnWEvkfE","8SOHh4jMFrUm":"K1uRWWV5nLEH","Wg62x4UcyvCe":"grnkspWSa2Sw","qyHiwq9ogoMb":"PgbUXqBw605m","CAu3bQdTLi6p":"zHe1H2NrhRMy","XQ1qwFp9Yfkz":"JEx6k3fGAOpe","oaIqWvYhidt1":"M164DDueGHTg","ii8VYtecHGrj":"mqOjNmEoPwLt","Q5ZEFBcUfpry":"d355moPapU0m","nrZgTGkEAro3":"Yuju8vaUQAZb","MfQuVm5LY3ez":"rdMgYAL1KirW","USobmgqFr6z7":"ZiPCBZxEoAJ3","OPCiNFnW6eHd":"yMzIpnHF1P2b","EDTd5OkVbDQr":"pU0MXpsl9G7F","6SNPwuPq5ZfW":"FHyR6sxdHZS1","IrQcsrXK1F7q":"kkDOceKwUM82","DfOviQrJJNIo":"6YCiD13xMu48","NMpLsJDxpfB6":"UGT7qrbqBxXr","md7LEzxC0913":"QZ08QegRUjGX","sARpXCjf1P7Y":"mRSk6LcDicqH","YWEtzAT2Uz31":"oFFx9e8UDeQr","AdC3eb3QH9eO":"qfCnXaWV5gKy","iN9pFmldNYTu":"MTigkZc3jSpE","ZfD9jad08u61":"gBKJ8toFjRHH","yywLhBXMmRdg":"1kmEM6qaWJf0","7rg7wQiSJtkg":"NyX26yAMG6qC","eagjQ9SNidPm":"doSdhaiYrUkx","j284LOfkGM4E":"WLRA0vFSAdVK","xJvM6BuC2fH4":"9gxMTpqr5DyV","Kg48DXv3knLa":"36KvtrCcFk16","SVXZuKY9Juf9":"kVjIpTdHKS75","p7b3DfjEIh8a":"hQr1qjsEp33k","UyqP8qWjQiD0":"iWaAlc6iDXmA","TUcq3zZJdoLO":"EJpuikJKSokO","uGbtdQe6lFsV":"ykINatktvI4Z","WYv16QWeSTwD":"YJREHUsIfVpO","Z966DmQh9s9U":"t9iDPn7l8cOb","dAycPgRP6kYJ":"oTRqf5XT0iHp","xYf7EJtV4Bg8":"ikjWHcVBr8qB","pvnsIkDFnGcU":"nq1ye49QTRoR","Yy4iZWLipjbH":"lqgROn4UMDQd","Q2nWuEhRlSpn":"9LhLx8SGi6CM","JuqKWkklQg80":"QXSMnYpKTZcS","fCBAco8i7ZRG":"6y1HaU6hNvBb","sKou7an9x2TR":"hUVq5eSeficy","nI5Qaf9tFKu6":"0rcCWuX55SWT","Uw8ltJ3fE0J5":"blVMASbSwmOP","5e71DoTVq6YY":"yQ8KYdIzLogv","r9M7auELvLVs":"EJxBEpB63WX0","jfv9HqSPRRNC":"V9bnATwDznDY","e3GyAwfGTF0S":"3JLB2kHBwedr","zHkBrifamwU5":"DxOUJ5vHlyAr","Ud3PuTp5kDxY":"XXVzh0uGIjvq","3GH4srz9tkHU":"h5xuRDIDW4Q1","YoF3iUngaYvn":"RhDRWxMmjbbB","bOlSQZIyp7oe":"oVBNRzBF2Y7E","JsrrDxivtzLa":"wU5TtziFCa3e","lYx3ybDjn7kB":"lb2uFkNjDdW1","8r5nLHukYCtp":"QXkNfBX4ljt0","3dZTjwbpAwik":"nCFS0NfWTvtS","IrcOP1N81wbR":"cH8qPE0i48f6","NgE6Z04HRAud":"V8TAET2J07ty","pEPaDUlsB4CS":"E6wr0kvtR8Lc","KnztPm93AUFv":"Lvk3TQLe5eBv","28N7IP7uHuwV":"TCvDFyvwkmsR","kamW976H2ICr":"BuhswG0YLu8L","0DUzYIkEmaDD":"7SfGKsLG10EW","COCf4D7Wubt6":"LoftCobPf1eL","gxSqxR13cc70":"oMWw6UdCgstk","vVqZn4Qsq0kW":"SkVGPVdo4IN8","FubHGeGEOnob":"E2ymPtiRL8Hb","7hHujOCOgoG3":"PMlXPvHPhuvj","0pmusn1oB6wY":"MvJVBvJe2Z2M","m7U4ZeXBDoO7":"4i62gQPUvTsD","zyx9t8mgNtdu":"XnNXn3MibUrr","2jJ88NeBu1Zo":"r7W9QZ9IaCBd","3vf7UrcdPvjT":"jaao3pL96yKJ","MR9Q0A9evvox":"QhhsmbQJjhAG","OdhemSLQZH6h":"3PUgVZt9zoB8","qq11pnbOaQNl":"ZnA4Flz9xeYv","bV6e1ZI28sn6":"EVKRzbL7IIzW","sD273vu7NDr9":"SMnZTBtpjRFg","eS2Y8e0CiD0Q":"L7GeAZttVGAL","QX3RfnfHbnOz":"z3BzUlnUcZi1","JefnepviU4gs":"hD9Syt8vgHag","CQnCCcu2jCcd":"gP2WsbXkrIsr","yp84K30wFZIe":"9DoNHFqdjYwO","MYAsmdECfjDG":"yosWbfy9eAWT","gRdr9oZDKseJ":"BT5Sq3FExHLa","rSLErTk1IHMc":"DVco9f7W6jVb","fo7GA0xGcF1X":"MuNyjtTAu6ty","YQ3ywAgWe3XR":"JcBlu2cUXgQH","WvbFZKY3zyhh":"j7rBanys8496","0n7qRFBVE8KL":"7TqOW7VJYJYd","APg70VsG3o2T":"hhz92mJMFDwW","pfqsKVugqEZe":"iS6BWMHTJYv0","M286IVRK94qF":"41j1UOnpqkwV","iIyMpgwPZDWu":"ypurrHwOkPvH","HOJK0DPaqZXY":"mmHTwGn5vqeb","sddO5wf70N8w":"mtKuQTQAgNfc","QMS8rSYF6Tf4":"1JZHth6JLNpI","oCtAzaSv0QRq":"aXOqkqyaQVL3","94gUgmesZ7wt":"XtiI0wZ8YbuV","iTqF2Yf1OMge":"3IISFyiUgA7o","JYZVU3aqfROZ":"3w7lu51XqvMb","iyPBRvnZNTdi":"uHtI4ydbSn0N","JPqSZJmPjRv3":"v6LTzxFOQglz","jKYPRMuwVeft":"d3hBeEJwh98C","NkBCeB8Wr4DG":"aUiLoYa2wohc","m0rRMtO6WgS7":"ByR5WKo89Vm2","Ymy998CTCWtI":"GimFzSp2BsNq","CyBiNVZsmKF5":"brzbsy9NVgDh","LtccdbrALSe7":"ajTdEgjaMZLB","n0EDQpSNQRi9":"SmdJmeGBqHqe","DfxKd5olZkIT":"G2M0W0B0iNoz","QAtBBgkvgocj":"wrNYfowjRBqF","iOe0O0NGd4ol":"fOAitLFLhO29","GZwUMs5sczhU":"qBSIZO7XUeNJ","UWITQUH1MWRY":"N4ACzJCIuJ6m","dOqohA3AQUYr":"WFp82JRy7T99","mYcvNAU7Ih4t":"NR58IKdHQw3B","z17IEqI1uRsJ":"nLh5B4jTdFTg","Dx1FtXZzwWSm":"J321beOBaKFi","VsEeEvJUcRNI":"XmhuuGgaI8H6","ysTnIyXrvH1h":"t9Pne9Lbnuvw","3xyKWJuiewep":"18Easd775kai","FJod4VaAal0x":"HD7O15OuAw18","YgFPfzT8dMC2":"WcM4OWGm01fM","1eXSh8AYecDo":"2h6ihrCZCJOK","AFxFJFTK6jvA":"E3WuUY0XWKLc","kXTWWleAhdR6":"g7BbnQH1QJtp","rnKkPJzR2Fg2":"979apkpnIJGO","JU0GZxqgIbna":"5j7qw1NYzew4","bpudZwJtF0KA":"xjrpenqAtoTK","AC7ra53PhVtQ":"zRIK3lM56FsN","3rO0VWnYwDih":"dSwPyju2PeqP","JaK6TPzIzKQa":"9Eb9nCyTEl8g","uY5BOaBowXXp":"yoOJ5GY7CWQt","C0oKHWqR705P":"XratpbbYumuh","V3Ws1rAogrex":"KrmAD5Cmzlxq","JY1fCUNeYa3R":"GNeWiuygNG4W","bZXbL0qgnIpU":"o8PRxPEaS4Ij","mBClxhB8W91P":"s9x2hW0EOx5s","Q3HIwBk4m3QU":"bpFsFfVPrbDL","k6qxQ0JnYLZB":"Jg7XNVnSH8SI","Na17xTY8v7HQ":"QCADX9aCOaZt","isITAsOcA0JW":"KZC3aTEtxjaO","aTpgwshAS7i8":"IKJEpvGd6Qcg","8PPMuIJ8J8n0":"gz8WHN3kttHs","rCtrx6chyeGC":"KKrdkcD0aTnX","74Vfsp7DXygi":"7fR6pivYfvQa","WTWTuviPmJtO":"JsyViGIW9Fuf","pvyZvoIFufFY":"sHyMzc1wi1B7","r5jRttYuPQ8R":"zpDI9hXXO7ag","9mkerKzzaHpi":"ZHrdnEgKx0tP","sjBn1fvL5ZlX":"L35eqCMhna5V","AVcPHgRRMk5A":"ojaAr1v9ucqH","H4iq6KKUHgop":"JXYGRcV2iY8T","OOlWklGteIEk":"oJJR5VSp72jM","cHtcQ9rCxjCT":"9A0JbgDfsX1a","uI7Io0f2OcEz":"ku6MLbBcJuSX","mh1C322szdcH":"i9qoZoclO73J","OWsFw81vBFkT":"ZXT03H0Lv3kl","QEukJc2aKxax":"zjbTDjeOwFVy","O5pcwW9deRKf":"F0Flk3RYYjjF","Cpb1b5teF9GJ":"uhjv9Z8UEqdJ","nBnb1LTOP6ni":"m82662gBgNiW","oXf5dZzIuyU5":"C8jcZzn76Hpx","rlj0DCvhYu0I":"hvyPRoPLSlhz","bsII6jo8gyUW":"UrOdrKqijQ16","GIs2OxjG9Mnv":"2dWZEa9IedUF","lPOFdwI9zxCN":"YysSMM7TW9Ra","K3Q0fMW8i2oP":"URc3daJKFdU7","PY86S9T7ckiI":"f8K4gQOWjPMh","NtMKhjt1083A":"axnYCFSIVYWA","gvCCn8PjNnCo":"4om2770f2fKK","az9tSmki31Mk":"OSvKXUwDYBmX","neoN4ZVdqMIh":"xqZrH2EdQV0f","5oFp3hZViCg5":"oA6pmbM45Tcs","DOFvrPp7TnFg":"vl8RC0lrghdx","oMKvJ1SbfKGt":"FQ9fnEptO8lx","T1DSf7FovneI":"Lr3QdEiBMJ34","eFcuRorNQ4xl":"Ul59lYl0spY9","NzM5GHmgnPQr":"1zEDX00zezUn","otXTE84ttNoQ":"7IuScMBYzYnx","v3fTedJTSXk0":"Zyb4BuFDPQVv","pMVZK30XP1hC":"1YdsgaHpPrkw","rxrCCRokziDM":"LprlIDwMQjAc","238hQmaa3gWq":"aI0VWMeNKtkb","nch7JtIH0Ie8":"jDERiFyKYQu6","P7R0XAU7qGQR":"kZiHhX1IE1GC","ciLSa0Y1JmZq":"WKpP3g72PvD8","VPtioTuj5Ag7":"cffBIq4zgBjV","vYrKlHAPV2y4":"Sz1Kdp1ZVsDS","Ydms42T5Mqo2":"1c8NLCL2davj","mPI7xNwFMNTw":"yIuE6F4jCLV3","4DRP9j7GZ8sc":"tjPEPeNwk835","vgLjkvieq5Uk":"tsJCdJ5ShItz","v3rYPveOZ6TA":"o2nMdGmH9J2M","yeUV48kxjIol":"VUqyCwGVGLXO","7KpjGF4rDXGE":"Hxh0FZiitYLe","KeL4vPPxw5NZ":"yLnKiFFqa7Vu","ZNoG5DSXLaok":"3EO4R2zS4qlm","mfw2Fugj4jrt":"iGOQ5oBuCuG3","axohb2a8HUWm":"TFNv0PZuaQjw","3onIKp5DDUva":"L3wFeOI4Ypt7","e2zGicJEJHaz":"VE59PFS2QpYc","ptymScZV7llw":"p2guyJY0IJDq","zOGzIXHNPlnN":"TxeZamGznRuk","OQe7X23jSwmw":"e0GNrP0bJET5","QM6bPlT1LDbD":"J9DBZo8KE9EZ","uAy4OexmRG1Z":"ltkaJNMDZ9DU","4DF5StvIyynP":"NKQPkxzpw2F3","ZkSgOsljRjt8":"u69Us39nwWml","cURX3aIC9t0t":"rlnaxQKTutTv","P6K137mfh03u":"PsuBaK4v2EmL","hwtvfs6Yy1kk":"Cm6cIabOupTp","nl8NiIzYFjj8":"2NlRtXDtOWdm","7q7fvMyJLclC":"3suCmbN3KGFA","zMcp6M122SDN":"5pjjDtH5ubiK","p0sIZMtGHNRX":"Uvx1MpGEikV5","CPO3rI7Y8nA3":"MWXUPRc2nFiN","2m4CYHcHAYYP":"EbgglPJindxB","myVBBGN5tBtx":"Irb0wbx0sZYY","Lu2gww5MZKpk":"9noZHAQaejT6","aJFE6IHBYWue":"SrasfgelPLPG","DjlYbrNhlbTz":"bPZ6manbHdF5","8fl9V4olwXra":"4CDqE4DSqULs","Bkf9FLCHxfiU":"qWHGrX0Jhxn4","FQ9lroxyogyh":"pK74ZPvdwRYn","f0bWzKF0WBX3":"wSZXLnrS1dYG","prFWjUwSwPKy":"4wN7k7gpMVJ1","jPyAmF5BO0Jh":"dMzDIeiCYOfl","gzlVy8nV2kpa":"UcUBXcxQ5h2e","1C4Y6VcOUzMF":"Rn0saH7n9G0v","vfXL8nOdNpzi":"f4V8mIxv8wXe","CpWLH3G2FOnJ":"WL0l5SecNVWE","jc8htU7G6EZ2":"SJ7XcXh5DDhx","aLGQlh8ZxZ1P":"EASv1QSRcf49","UWMtCXVzZ1Uq":"IXKZRepZoE0f","mORwNTyKXTCG":"kDboRJQ0HqLr","xUFbZF8BKfFp":"t7JfosShhvM6","t3NTWnhXTfjQ":"5hOJFbAjvoZb","Upv2u7cfLjKP":"t4VpNtdYSCr4","ZulZDqQttr4I":"mnCeAYL1Asp8","RNAMhEqJxFSC":"L4CEcX3lANPi","DhvWEfdHGUvk":"2LhiNCMdBBt4","Lqqf1qYShgdC":"FjOkS2ZV256d","aJRPpGRrH7yA":"helTtSycIZi2","rRdj9yGVVErz":"he5WIjaKAtki","epbLPrk4v8Kt":"3k8B7dyLKKbq","henQT2UkInD7":"NLU0knr8sVtC","YfXH7i5x1efb":"XEhodnkKH0tt","A8PZ1s1sj3OX":"ZW0BglrtyIVP","Oprdpk8iaQEO":"LOgUhkOaMLkl","2Ecmu1Jd07ke":"Pps1dM7jhZ2y","ofY5X2aXyUEw":"WFdO6YuOu0Qo","N1eYW0pIv24S":"c9qp1gbkCVUg","rIr34MXaQcOL":"e6JUkKAzVpww","oNrCDYscayvz":"4xJtEqTKaYri","T484emn3gVmg":"ParnyaRSosZF","DVSguhZnY0Ss":"myZqB2UtpYX2","aYpp6umZXdiv":"BwtQRYjpMcJs","90ah0ogsSwhx":"DdOyHKCKM8ci","taM0SZlWTpBS":"SvWtZnlxET53","vAZ6TvdJz5Xi":"0YSr2hl8hEV7","MVQGy6mfnbrr":"SBYcdVlCxHWM","K9xVs0tq8Tn9":"ys9OVeYkDpwS","5mzbBhANephn":"ojD0xMDcBhkL","bDSXgyqOfoGi":"tah57c8ABKWJ","dt0NxO5Yjsfq":"M32ckSWbpVvs","kLwYsieCZRRB":"eguOVlgoAg40","A1OW3IMFCqWk":"KWVGIV2zmsNS","rvJHGhYZ3Vmh":"tK2OnrRADh5h","7344vR1lgU6f":"U5a73UBcouxi","xqUI3Z42F9j7":"HaQl67dsk4yi","RHM3tPc6wDzJ":"qnEFRbNSj4jf","ShFDMwELUAgy":"mHKiswgK8fKy","O6nrFnUbP8UF":"BRkX2wXOSA0Y","ByO0SlU6b7CT":"PGl9bDE87clR","8CLhcaG0ZNju":"CpcbBFHExcns","23nGmH53fKAp":"UdPWmEk62Zqb","sOD8PX7pC2Or":"LnCoZGSv2XQL","Z1ubRZQQbVtL":"pQThpm2LRNGw","cdFAhnXhGA5S":"QmhyGXezZrkL","TPRCwAJx1a5q":"MgBYjqDAWYMa","zKMKlm8O9Q5y":"aZxfLxfVHTGk","BGez9ozXzU61":"XpTPxPk2IuaI","THaWRjw7E4ap":"1Qk12933dwt8","3jbygCiR5dw7":"Xq05Trpp7R19","fdOgqC2kdECS":"Onh22oH9v5PG","PBqqdD9guQ3b":"MN9xhoCacdtm","qfCx7mCM8FTO":"ckxcnMGPJAzu","8WLlQgWrr3S0":"6tLbAo93Q4Cm","w2CzXAuyCAjT":"DlbIS1zRsvMI","uxKPSBfXrbzk":"83FrQOPp37ce","fmkDxAhSXmOM":"hjhxe7Sh2GzT","dEn1uSrZ0iBg":"VOU3BJ2Sc99p","jYzKB2OuueK8":"S2a6KaYWNnDS","GZBxcblzqZBZ":"FSPtuoicjcRB","oUSiJBjaAAts":"5ZNqr76iP73Q","jbDX0d0y6y89":"EL8V9HAhorK0","rQOToMh5TtGU":"o8Ro1Xn2nO7c","F2D3JHHT3One":"ZfzmxoDzORM5","E8ZFwaFPhxu0":"za8fL5L6nvaW","fDnQBRGYvxm0":"ZqDHgkJs0p3Y","hrpc7OuuVUQw":"8945kaloRxq4","uTy2tZgdZQYx":"8AR1nbWb7BBD","kXUNIURpdkCz":"YHRhpoTcwh6P","UfB5ytgrfrpZ":"L38ohEeCTHE9","NcyeasZBSBOv":"dgth9RQQpu2g","ZFwBZXVRWDqU":"2r758f1RC6KU","wjyNmklmfTtF":"9i5rOVHkgPpA","6i2RjTOdF5uC":"Am6pG7LSRLYw","rzj1rEB2zmbA":"WR7QTdWjSfot","zoJDf12CRRV0":"FATWAFU93YHe","1JZycUsjKS3E":"kDriBIKHmUN1","A71jraZd4zTM":"8idZjek0FbyK","ZWO9IGYl4A6W":"ALdf1sf8Jutf","aXjyhU2hq6r2":"zMIUyizmAv1u","mnn6XhTvZVCX":"cjLIMQ8KN281","AOdsNRUXUDna":"aSu0TjsN8bxZ","jtwFeAh50GA1":"GWL3F82B7Kgh","9nEi96QnJqD6":"lF8iOqQXCN1A","LLmDSesbTJzk":"9uBvzQzgyy5N","Ne6xmAfi7YwX":"12JqmcBeZVbV","8K4G5MVc87k3":"qRw4MHNq5mqS","S2jzVUkzIRqu":"DogzzCjeQgXT","6crwSyqOWnKp":"wCF1T0wWmz7g","g2UZX0pTHpEq":"hta7eXk4IIc5","NXFvlhqmY7gx":"ifBUHmabxWnT","q4QLwwgpV7oW":"IsxVM1f0WM5D","6HWtBm5Nqf8D":"A0Kf7icjJjuQ","RAYkYWhRkAop":"bYm0jNPEd2Iw","KABvQOe4wmkt":"y2PnBMhKg6NR","kuCXn8FEczKk":"m9iK8UxmVjOd","3yiayZj2cvcn":"28L97pFEYrP6","bun00IwCci1x":"fm3rXVJS07Dr","4naV13ljFnpB":"t147Ae3ITCWU","tFhNiBck6JSj":"KNKifYjr39s2","E6ww8DDfhGeA":"EYFfzAlKqyTT","oL8OZOum2XyV":"Ka9JbjGLIV0T","hmVqeRQ7gvSq":"1gSQSQoefcnp","2LZeGW3hOctG":"YroLRqpcU96o","H8QFzNfHj5M3":"kK0u8i3c7sYa","1ztFZiUXJcAW":"ABOE61kSMhWu","8RwLTkhacNSc":"SyJZ2KYU3G97","j0Ib2EIi0PDA":"x7c5sc63CxR9","2m0pI27Hj4HW":"0oiZR0gayyWL","UfsWmDTSTZsR":"QF6X3nBJkbcl","gdyCIueeN0Hp":"3LY7woWUVoWW","RjHH0HWQU25A":"5Hvs2fEkyBhD","TBIHfaY08QJ9":"WBe5fl2LFf6W","VLIiD9n2tFsz":"7RKM3C2jeYPn","PmiGTsiOH7dM":"ayRyvBZ1VvRD","thQc8iptAZiU":"lj5wMoWpktrg","vSijPYarTOM5":"8eA4gAcGiM4f","4buDnzgRoQnn":"WiQ2XNJqkbFD","NOLdSkF3kwYg":"Xis8SwihGb1f","bY8KPZqvSUJT":"eL3kIzf1pRxo","xda4aXqhnRX4":"anCabXq4wcYc","kfXEE6olIJeq":"vH1DNCz9hE0Y","0b9R66tuMeeO":"1EGTRiOptiwm","H8eksYvf3JjS":"ZLpzYTZ9CquR","oHehjE2ai8EG":"CDjZJH1y7EQl","fmNlyR8qSjNr":"VzMveqQrWWfu","V3OEpQ1eIbw7":"G2WlG7ropv3w","zyfpyCT4fEEY":"HcXvW8cezWfK","bnaFqKiQn6kn":"99qJFfFDqWqy","wm0PWfpbCt8L":"9q3iiNVnwKld","h6303P6j7ezv":"ndQDfZLqjSYv","tCm42cwFdBMB":"kkLFZoGDaIs0","0UT6qnK0NJFX":"48XJLKd8d3r9","F9u4t1jFhFwa":"BhduJtRl9DIo","hH5Xo05buuvd":"I6Uz6xhOlgNP","O2syIJwu0OL2":"T1h8ToUlNrJe","Kfkl2fOYZILs":"fDEW625FJx3V","hOGmZyEEvL9e":"oYzD84P9TP1u","Rf0zC0599Sns":"TPluZKUQtIzx","1oV5b6h4hz4O":"tpqLpegZeRKW","bTvGz3o28gvD":"HheftgQfAfP6","EzrWlV4ATHXl":"zZLwz8b5jAKV","LUVL8Lg4ZTXN":"McgsP4DOvnb5","5IfRSzz5vPiF":"Rh7k4U8DwjNU","etCxdxRsmsEh":"CO4IkKqhS21r","2dzpe1ICfS9N":"jPKidY0uE6yY","G5NNondQA3st":"BT0DGBLPJGCq","JiwhDNvPnsoK":"aIQ0pzFgz96o","7ZBpdmBDVBG5":"I3OIGs1NL7xp","R4lZqq6iQafs":"tUIbqqtv1GGJ","gBPsnDPZ8bus":"5X5Scgkr5vZT","6FNCVNORreCp":"uj6OS3Xioskt","jc4iy4yoN40W":"82xQhbIemwOv","pD9bmEWPJkaA":"TbcC1pnwYMac","YIcNnnfBFcil":"SOSwaNNhyQtu","W7fOOBJtnlYD":"PD1lpo7nFOLF","x6pvo4hTYlep":"hwhwKLknGGLn","otqOW0MJOjDM":"AGAsyTSjrw3x","U5nd9DVpvcWl":"jEBlCdz4wzkL","J3Nr1KTKKV4r":"b9DtTJirNFPZ","ANhxxJ8oCz2n":"wIGJg8LX7mSI","3HBi2I1GSytn":"e3QsFKVNHWlp","PNWa6il9hTs7":"1hLgok6HPLB1","WF9ETopHczkt":"fRheOZjCi2MD","obzbwmPJaPPF":"vl8dPVMEb1Zs","HXwB2iShiAYo":"CD2WYkgDuWlL","jP7oZbpHMCRi":"oE1F6ylOqK9q","R2MYSZqZYVtI":"phixxK4oPPU9","ipTWQrxTyVGN":"HdjhG9ly3u8n","vezUQ3Jbqi0I":"mG1UjeIaJC8J","oSP2HkxzkhPB":"ha3L8vqI3Cvw","8dsW7ajnuFRl":"pHHTUhUt2lQi","q58UoU48dIVg":"cKUdEKT4pluw","0uncv0Px6wo3":"oiF9MBoj2WL6","Pu51hpItwirl":"s7bV4pU6ICu3","PATXsAZLmXUG":"QpCeaCNi5wo4","DD0HahbqSlYs":"gWy1OKjmSRGY","AVXgcCQJeoJX":"Mn9ANH4miQAV","vntADQci3BKl":"xtr3baQPCF8k","ca5clJ9Jb9QQ":"E6dsH137M6rW","TJT8mRNVaTJZ":"4q7S2FnjziqK","ruQ7SruPCuyN":"SlxDxldNpxfu","wPueDycNTmj6":"wYL68s3yVJpE","eMUhmnhCbGXN":"y7nIlpN9cFmZ","U57BOrzwO3L3":"5tVIHOBleAsO","rt7IOcAjO1Do":"7DSOww18Awgn","cMPYDpVfzDtS":"Ruk1uw8RMMuq","63JXEuwcpxnO":"o2nO8pR1y3jY","B6JJHgQIhXen":"Y13mFOHLyCvU","PCtG5DkMIWum":"baBLe7cMlt4b","5D41ERHIp5J5":"UwiC67Y1W0kF","4DsUyVvZejzZ":"zHQrbv14cwXs","jhIAWiYKraT0":"iSzB7B3h8xAT","pUZVZndXePvL":"jWJraY7jiqhN","WiMAjuHzJZNq":"hYW8t8s0wblU","WAUyDYS8c6uX":"L0l1HMihuTub","dRlSZMAZOVQv":"GMYY9h88W3cj","VgyGrIS9XBeW":"KwOuCLAkwEGa","4Ns66iwUYpxi":"y6c41DDWx79l","kES4A5X4P6en":"nzU2be5bWWCF","H09T41RcMORe":"ydpvbxR8iGdh","HAnjgNaDTQhS":"WLOxkTMvNQSG","xNnYIF8aPTET":"WGEsz4p8P64j","FtcNtB87DtI3":"vn7Oy4wJCMR3","YHjF2nrSLB2c":"4e2xnH9h8WqP","yb7ac98oQ3Th":"dsY3sS4OGuBD","tgJUAnGEdqzI":"snBoxW64lk9b","zlPTAfEE7LO0":"3UyWOep9kRzk","gfj1umD96gfH":"LLqrQHQWzSWV","PBwHUTM9ST8Y":"wgKGWPJM8N02","tvKdK1texsL7":"mneCYsbl41sh","pHY5jjdF2DwV":"ya0QUda5Fc68","rGEkE29lnpwO":"afjhQG9zZMUB","NSKHAe62mXFi":"c5qLbRF0wbLY","hnRBZLs05e9A":"UgO3vErpuh8a","RVP525slFZIS":"Q125mqUfTSIU","smNg57Jnf82E":"mfrQD81AlRkK","aLsZtZEh6Pmp":"dAcpem7ZgC4j","TgYu8UO6oRDO":"PJBxEMjCAQey","XAYN7T7ysDbu":"VNbNRlOQC7s9","4s3UMAXKvjWf":"fbWApdla4q9t","22ZgpfDcVRYX":"hzVMurDb1X45","APZi9vdIyF5t":"hHeDLw8RBZd7","Qj7yZ0kYP3fS":"6K1Qa9yFe5NK","NZDKyK9xJQOK":"7dILKsbTW1Ky","Ie08pcwbgtri":"ekQKrsSpMQIE","yDoq4DOvHcV6":"h95PcJhBw8pg","SAvZ1NK9JfCI":"vT8UN3n0qeJ7","ddSi8PBKU8pR":"tUjAOrjr8Leo","X9AOSGltseKf":"QNvIwdNKlYUb","Hqi7rktwl7jE":"3mE9FQhMdkSh","ItOfiIEFqxlh":"yes7SJSzpkqK","BykZlyzoyTDB":"OB0HZntxE6Eb","L82w3wfB7CjT":"I6Lsny53eCza","YjKTOgoFZENJ":"HulEhp0xOOZW","OzJEoRQq6sos":"GR5ZWbFiRIbw","NjJS0zqUIr8b":"uhtoWtp92eKB","05rD7B7FJGFE":"B21KPx36c0A4","YvEbCbVObdaw":"u9eW78y92je1","N1BicDpHqbWk":"IpT6ojDp377Q","XvkVMVd21qhS":"66KmvQJd6Oce","yUQk8c5jhBeE":"VQ3jl0ehwXXQ","6bsZjdINC45A":"8b5pV3JFJbyN","CgqkVPdjv3RQ":"KypVocIpKbpI","aBF91YJO0P2y":"pDFhNorsEpLO","dWuHiJtSxmCZ":"IR4ecjfksem7","wfQdmr4Ue3eP":"twAK7oUO3yrl","8zn69c1jWVyn":"qa4Iy7D5NiTH","5py0lpRbCMTj":"qwaEgAm8Napz","APL4a3deDsv4":"dUIRIiyXdfpQ","WRsk64E5ykEj":"7SZS0W6aSYfo","Gk10pZ0e2YAR":"GCV5VDUXvw02","n2dAwluVzihS":"nJ9HlPNkK0Nd","FLxpMado2lPJ":"7Kc7Up57EP3k","jQSbk4V9bn4y":"uj4MNDd4ZApC","fqrADDGOweOD":"NsyF15yVbZdw","YmNGBx7U9mIQ":"vqGtzIlqhus9","M1nAHrjgY9JN":"Q0bl7m38yREU","J2cS37N7Ebjw":"gPnSxgyhNZ9H","dXYSzECoS1xb":"FoIpBDunJUXb","oTWnAT5IzP48":"3M82OnDCqudc","8xYBur6UKsPn":"KQWWfdpdvFrB","MO3aU3bJBsfI":"VLudkpKU6loD","cssjzM7nNz51":"rCHjCSf7NFsS","tUZSPQERqMMk":"j2yAo17io0hK","hHXVEP58Foye":"cG8nBwih9LAi","3UAbqKoZCo4r":"fDVjTl1FJoag","RRp548CGUc8b":"trvfbtp5su1L","pUFd01rPFAL2":"FOuAQOJ0z3jH","hVHiA7eS5O97":"MA0eEMrB59lR","0aGWcvQ3BZ8B":"kuM7GAijTArm","MUHrDW5lqsQm":"SyYVgCgSNYls","ioCPNw7tlE3c":"PooKvNGrjInD","I5iUkSXjT8p4":"GTyXo3LH4qfx","KTTLg5dQ0gbr":"pEBuYX3RApvf","A9K9dx33MTqj":"bmpC8uPpmCev","y5ZjBo8fjJFD":"viPUkEi6Uqmh","nPjJElM81Iqo":"OLEqUIsPHKxR","fj9fwZBxP9YA":"nmCi440q65yv","vuCe1pNN3DZA":"pgkOKs7RAhlL","8DBXy5SUilNB":"RcJCOkXdElZ4","K9zJgdBbMipN":"TWMS8Ru0XMyG","I7gEaHAlyAxQ":"dSaFiBWqcU3m","n3xurPvFX5bl":"tc5IDSLMbnkW","qi2L9ZZcBSCC":"9654rwT4Jakl","xWeyTTL3WZGr":"xA4RIFYzh9At","q4Fkns628X2s":"LQ2QmYVdDWXP","Xz0a9TYVzUQF":"TOpTYgNS1IKj","ZlxBQAPkUOkU":"WcvB3nOlHCRd","enIDfr6YbrAH":"dTwSzTfpoKNL","KUwaqO3QFPWc":"50lcb63tIfKY","5kRTSXMWXmry":"ApeQXSMKlMvr","ZTQ4XKLtLEHE":"mtG6iqCdGzwY","p8mXw4FdEJ8L":"WJIKtPIKLIlS","Ra1hHWsuwPQK":"ADfS8xBIa6TA","DLT7y5m72pHc":"Scfe30Sutyr2","iGAI3P1FXlGM":"l77tnvevRrKs","zSNm8G1lxw9P":"CTAyHrNqS3v7","QNLlLIt1FyMV":"Cf1aDDDwlMgr","ZTQXbEMzGq3M":"4FgzCj7OSdwI","M0mZ0eHZllSP":"DQGP2gOYca85","lEJFGIdfe7F3":"u93n9vMYskBs","y7qL2jOHGPGy":"2F8griFtLhpd","8Qm5OAtIXWCW":"X8geWdq3fhY1","x4FU6MypOpLc":"uHbBQcwNYvZM","r1xxGvMxmD9t":"EpcNPtNyNJfb","xurMlVmORLhj":"SwEl3pdKCmTc","gWEr4UeKVWML":"5VXteCXKkCG1","Jhsl9TLeVFuV":"l0QbArdGaEUu","NoNXThMJPYS8":"mLuUjJdEVJBW","Hk970tTtqACM":"Nsx9Zj1c3vJG","TPhR8PQEZyjM":"duhwfaQzc39K","ZLRGO8k2IWwc":"7rpr5grRqnHh","CXsppTWNxVjG":"DxEmADqAPyVs","XOg47vJbJo8g":"K48tkN2FPWg1","JIVNYgrhmuDi":"VjRUnGLGUTl0","ILvAfUXFAjQn":"iTHjPwcPsVKV","197CYh7lQxfQ":"ocKId30Y1nhd","a8Xk7jDjdu45":"6bwxE46I6CAi","5Zt9hHRGL6TH":"tCjymgVbIkij","ldE2n5PxgLaj":"HNQSYofK3xJT","WMefNppKY7zh":"DOZ5xeBJ3ZjC","zAXQBx0Lgtea":"Nw9tyJeLV5F3","9I1r0cev2ctI":"lqeI3jbMtGCA","8ugfvHFbjtKy":"1O77d5XFJOsY","MR4rpqKji70Y":"Uy8BWARjudIk","Wec03BqKRqnq":"KE9VeuJ1xxrW","CNge55ebqQYg":"Y7hHNTmBzzPa","Uk6XEgK1qmY1":"9pSLTAapPnQO","eANz7V2nx1iE":"pnxnapwWoh57","WDenAaAHbBpi":"CPyQpVM3eRi3","H0D9bYwJ6zAQ":"odwdnpbfsHR6","KKrD4BK69mAI":"b3pQlikr3V8Q","FgC1QGaAddpB":"YRoqs6382SGj","wZuSAvHfMUMj":"4Cik7HiuAO5z","J5CWa4vdDeWv":"BMwi2KKL22Qw","xqSKC4Tiv9Zj":"b9Ec0KYCZ3vC","XvHuDezktNDI":"70rdKeDWS0uY","tB2W9WwBpDaM":"Kt6ey8fEcyAb","RBIPy0wAxjcB":"qOfhkhgqZw1L","yBP6f5HedKv7":"T5SMBjAMpIew","0v6xrocIycfm":"HeOGuyvh6qHX","dKEPlgdoigqK":"9rZdrNv5tt9t","hKLAZsWBgbAV":"ie9MATrmvKSk","l5qcFBLpyLqm":"tqoXg0OoF4dE","6bdHquQMGkWe":"7dap0tLuiGTw","adLdCKy09ua4":"lum5KVIl8YLm","iAM5Roor18QQ":"CUWxwv4xtxrj","VfWnxLDY3cFn":"OSUYhQq3Duzj","nJNW42CEuWKk":"AIPeKuwI9GDy","e8lGotFaCJMn":"SHwLRjMTtFRm","54dbuv2a41W5":"2vziG56AHDZZ","WPZe4nirG3zg":"5cWXBORcBlUc","vcxVvc8tfgZq":"n6wFbUOU83mg","cPaGrHK27Cs7":"HL1dkI0pQRof","fSB9UswQXjV3":"O8NUBRHzL2Al","YvTPU0xOvBle":"ZedYSgSIxYu1","elwv1n8q6V44":"TGRGVAGEzx3q","7gDNEHkm7nDW":"kls1Ks7a2xuX","hsBCQyvEKlmW":"Y4V3v7pIgVtp","gZGsx3Bbe8qe":"nUMKthhdqeP1","LX5tyLA4XFRQ":"phR74IIF4zCX","ByLl0WVhXOUf":"MH3sc0fdOCdv","3zLRAHeYCAd7":"ank3psd9TdnQ","7o8HneKKlWY1":"bdlFchl0DAwP","plKLTntnLwD9":"LZePoesLaZP0","X9sTCZpJfNdV":"QYmdQ6fUVnsv","wwh89xU5qXLL":"RcWEktxOUkUM","OixB8QcUQsue":"T4v84KH78pHD","fCtzYrM552WN":"CEUywlkkalzi","pcX6yXS9eTHI":"U8mgVmk95kys","nhK6yUGqmSyG":"K17TXgaCROXa","YfcQF9sPVIpJ":"koVfZG6q4FeT","fjSQSKqb2CDD":"DqZTETiqluho","tLwx2IOfAtZa":"35nkGu5BGTVk","kCVhIqzWhDm9":"wezfB1mnn5Xq","xBYNUG9532L1":"3VOZLJexVONg","lJgLCBGhqsDB":"zDkiGdkA7e5a","kAetGAqiaf0W":"83HznDGBKmJj","WWGeRsSStx4P":"XWFL9DR8m8qT","W0m98805mHVf":"WcYGFCbP8LPI","hMHmmIHoIRBp":"1wRXmGOtMjRU","Lspnv8LPIOnP":"VU7Ht48iXc37","lABievwzOmaO":"BydP0IW2MPgm","Zm6b5OfHXCgf":"uw6bVZp1Ty2O","66TIWg6RhXRZ":"qfXmgAtYmKXZ","LVcGeCVdo6MG":"O5rWeVVHfJXw","nG95morpPKTc":"KndAgLNtkyDM","NCqIqjlREpNy":"e0eem5yFeQGB","zVa9f3Zv9VOx":"Ycf97ZqywfFX","Lh9xsvYirn73":"LBE2FZnzaeCh","B5OvwEc0NLrk":"kF8EihdgocKV","70hHcfpqeqTb":"007glYn1NRAJ","LuIFohJfuJ91":"A9focyvH88dI","Bs1e56S15KGw":"9tTst8H8OpLe","lHTwtLXV6OY3":"XneGOdyhfE0W","CVkl2ojiOTPX":"x0Km2HeefZjH","kJvAkCyW4mJp":"lbEeu9FH6RW7","JdHkj9NKxNZZ":"jdeNVKuyRMa3","yvyGNx7yCJ1a":"hAZLI0GQmdZP","3MElTHF89OXp":"sWmM2LWz5tlv","F8f1yH5KQrmf":"8PlIjwudemwb","i5TcccTXziy1":"vVWuxNQRRusC","44Lr16MhZjqd":"gAdEUtoXFm0c","Ac92cOIaRhgy":"8pWaqRZwjyrS","AjDFMdSD63py":"MBJ0F82Pdj0q","XAt7GOXJroJy":"CdXCWERkTEr5","Ij4zRIlkyzBB":"Ig7HX2DmiBoU","V0uELYsRnfDX":"4r2dZRIhxd5Y","AcwJkds0r6pT":"nYxKd2gjRta7","I169gNm5eFNP":"JZQEg1qI0eeu","32uNWMkLPsis":"fAjFUu8qGkCz","zfk8UE7Bc99G":"LhwMuue6zFoU","4tTcvdadneOd":"7eDyiyaX50Bd","WtiwtOsVsYM5":"Z6Li80Af8dku","kcHTvXFrYsaC":"2Q3ZNDqKJ4ij","8wmedzvgiZBt":"i6RcDhsPc0l4","MzPexvyQxEL0":"YuK2KvsjxPwZ","guYeZB0I6CQE":"BUycqyD8bdHF","ra8zTpqqBtv0":"RUIWfQsJIZGn","zqCfIxtjE92I":"qlmeoZMcpGdp","jN6JDlrwybq3":"zeS0FbNc4Q8F","iVn1nVGaXNfT":"kr3E5f7myVUo","NaM8GhpboLLZ":"Nw0OASwJ1YL9","mij1vtQJSoG4":"nUIp3u6xMQxl","xEMWNTD74qnt":"ZdfTLRw3sbdE","L5J4YuPCtgUF":"0Ai4qRcqp8kr","kkWt3SHt8JOM":"9pY3hqUi3PWH","jzirogAJF7bh":"HWXTVOg7cKKI","01WVGI1euwse":"oIhS7LKO90tE","6GHfVSIYvnR1":"ZuZ3ESHaLb6N","hvIgIApYmmz2":"Vsa7WdbxCHjN","aurTYiH9kgwu":"mNcK1rxX2eHs","VI9rcgFStlWt":"2E1jwApBTlPj","FFMjNZNNpaVS":"ItoCuexBNJwF","Xy9jOmysumq5":"EwTRSZ7xfdQj","cj9sS5Tl1x2t":"cKjJ6b9d0UGq","eOuzL9WT9Xio":"oQ15Z7S3MVx9","b384aj7FX6fk":"W1NCr3Lko689","SdW4PIPK0ouK":"gqUn5PWFkUBk","izxXNZdkajyu":"XueJkCMLew2n","3XTTfDLvs8X2":"IYnqNXPZuswv","JdFOAqE6tNcA":"ZnMWJW0HHCj4","RZuaVt80EtZz":"7BvM4AA0aM3A","GnS93Is2jBlr":"XNaJgFAM1xhO","HnfBJr2b7XNd":"5dTk7ShSXlM7","1SSdKg7g9U12":"kIIk4IR0FdOT","rhjreUSA7Dix":"bqbMCjysYrEQ","W4hckKJ6jlBX":"AWAdFLVbzJ4h","ydh6l6JNe9CZ":"hSTPQtjmjWPO","GAAMPzRistat":"pYOTxQf3aoAF","L7RyDarBLlRC":"4icT43r4NT3v","hfexHTGVTFbH":"Ck4GQOF53xy7","oMifNlIhDS2y":"hrV3dqqe3KAV","rg60zCyndulB":"1Gt4Gvdh8ZGK","PcDWYf8HqvRj":"9X8ZAm9yMy8a","ArLdOX6i0oof":"FsIX5D8wbc7T","7qE7mogF5JWl":"c7QRPnY0ADdw","3oJ7Qn5e7Eml":"Fv5LrXxSkMKP","8I5NgzciLBoC":"K0JqmahuWt56","XEHfpI6GbdlF":"VcTK6gsnX7ZJ","mDMPzZO2VaPU":"nz1gXYcw7U9v","H6r0jiRomDmV":"98w0IztgL4z5","4nF32NMBZP4Q":"f3iZQd3V33DT","TmxFgxAAWWPq":"znpkHDG27yOX","MiiBVYtF8Wz2":"SD30WhBXASZi","gHDCuOZYhfdL":"eVkNNGNH4PAe","qcR3bIqWSemk":"37e737x1vs34","q97x0Vww2f3J":"gjU0cgICXQlb","TIrdPRrFx7M2":"2EAmdFEtz2dn","lbdbZKbclT9X":"LLcoTgF9OGwV","XIxIjnihpyQM":"oE4JmK2Uaqj6","b9qZ7z6uWjlZ":"lEFcBBZwXQkG","NeihFeg2XiMV":"EXSU82o3RTji","1xSo8vUL70nZ":"siJMUsAvubTe","GL1tcKzgKnpd":"6w2BM32Qjbk5","zl7htEnTrxSF":"xrZUa0sYUBUs","9c3ciZ0JDQQK":"4VkttM7LuMHT","EwiT2nmP9Lbv":"12A52RYPccZd","aKAYnafkZVYT":"fZY8UspDQAF8","UshimPj09fty":"VpXz45T4gr3A","uwmugxETO27z":"GX45S1jC5Kyj","rfeb2V0hkjES":"XEVhWiRXS9tO","fzQQoDGPD7pc":"PW6W7DOgy5ne","IgLQYW82iZGP":"FIySVost20mc","aXHQy6eRcH1h":"6wi28h661mJw","ii1gdxYxUojA":"dU94uUuDwaXC","1AB4dibgb9DW":"eJmP9HYgwxHQ","Usan2FFkB5ZF":"z0XwE8nBM853","xm5Wh0v6twIt":"I9x5IAfqQVx8","SpVCWImmKn8s":"nJkPRYFVPhPf","yBB0W5O31oor":"fuoFnkQXwFOb","zcX9gHeV0sIE":"5wiNjbsaB6DP","sp0eQiSEt0NH":"AGb0Nxvholk5","GpWEXMl2YRlX":"5z8rfqQvjIvh","nom02osXf5U2":"nP9R3u1WKevO","OOLykjU2gUTa":"VI1VOPNkqw1o","gbzU2HtaFMRo":"ezPu8ltChuuW","jUyQw2T1mCLe":"ssuRoksr3UqV","wgik60G5KxAe":"71goe4geN6zo","c0aIffNeaSCQ":"Yiw2gRUQB9Og","yWzB8BhXcPng":"vxBVgvBSQvC2","k7zgTknpif8n":"KzshcJ6tgex7","xc6usI5mKp3j":"Nbcgy6QraefT","t5xlqTEp0SYq":"PWNLMtgOopdU","VMa8bJEWrojc":"njEwzVlTWhZP","1LMtYEBubPAf":"a3cqjT3id9RB","O4IAO8wZqTql":"CZaPjisCsNSV","M99qpL9XImcF":"rYlvCgsOX0JB","wlOJuWhPNALX":"ZzJqaFwfN6mH","W9g2gEc2Cbqx":"yBOMy60CO1LT","574zYW6vXR0Y":"RgBCWEz8DqQJ","I9Akoh2y2CAA":"Yq1y2uegHFlJ","PFY1tX1ljcyp":"FRVJJIjblJTJ","3OgwwWLTM39q":"ROGkSsUGxD2z","9ZxIbqUIvx3c":"3gATSagwY8DO","PP7nnNTEcH5n":"8VgeBEtvdOjN","5kgaMVi2DzQ5":"6Al5Xui7dhUG","VeROQQgkk9Li":"tRNlSP8abYaS","YwRIROKwCcq7":"pCohuEXfn9d8","0f9FR7b8D3wx":"NCbFaYMjrrUy","2x6WzqlLsTfI":"SAxNQ3AbFnNZ","8P21fNsz2sEE":"Apx9T5ILTOhO","xKc1Bs71zxG9":"0ZMAfzByQv22","fmcUe4wbqEI0":"HesfSQH8OnHT","R9S4xpKguLyH":"fsxal1uLXLxj","WE0W71kpvKHm":"fmCorIXKyhpS","iZn4XpcGtdFJ":"OvNaH4X9Qr7C","xfkt2zooNF1W":"By20SWWWGn9U","9PbwQfablbTd":"DQITMgMgOjwd","YKn79JJ2aizu":"KcbSbOezg0Af","ljVvZsk0UNkR":"mj97yohHmx2L","gM5bjB0YbIr2":"8qP58NvDuSyK","UofVG2d7WzNM":"MPyaGQgG8zT4","PzSxFpOuqzsd":"7yTLqjwoC7KI","o0gjIRsS8svJ":"22qNPjoWNAM7","tof1IWQlcMaB":"Dw0Hg9eaE1ml","fsxOKN9fpLnS":"lIWSZhNnyZ3S","Qr3u5Agz3ivB":"Vp8jTEfzrADP","TnfGuq0Z2m3v":"Ax6Xvu0H8vM9","6FqH1yaPUZO8":"BQycCNHVKXaj","hW3TlNwwOjuh":"AeJArRWaEyt7","xb8fURUZRt07":"YpgFimbIshiy","tQ5Z55eTL8qF":"StbYD6b9IvuT","HFhltfsIgw1j":"GIqGNZ67IPsG","m3MfziypwqRi":"6aMBeeIjDXUw","oeSPTxBIF1mR":"wtqKUhNjh88I","OUcp7Md7WPLx":"oDwewI8Wp8uu","Phzy7uTZoUCB":"MuxZGKiUiT57","vyD414FlNe6A":"yxaWUpb7gbEK","Mjk0o9ux0TsH":"CMwcfbbtJxcp","hcTn3YEAHMF0":"oPARjuVFkT2o","zSoPClzDvLJ8":"N3b3Jkfpg5Bq","4d8M9XD4gUWu":"6n8HnjRKE2CY","2bCrNGRhA2KY":"w0tipVKgRx56","vPiS4eTK2UgI":"AvaoQqKBkx9o","9SwBCn9hGh2h":"XFp30PQYLqo6","kd10d25Iv6PO":"clZdNqIMRweJ","sLYFkIL4UNFd":"Q6VSFC5UoDKS","zRDhbPEn0P2R":"Ts0wgrpUg1gV","qg8A5vmY17P7":"2keYNNRUCwaU","p2hpYOawlBg1":"MXhs0ZERfgxl","LAEFLvtwWtT8":"lX5DFBATJfY1","QhpwJ5FFfxjd":"fr7lybe66nbH","SazBMbYe618m":"ENJ6LWM1d2OM","HrG7IZP4M7TK":"LyBsdEaMcxMw","HofWj29AicMB":"mNibE3muQFis","RNgWSRcqGq2Y":"I0CDzHTkpwmJ","sz6BRwrOtKJD":"FASu9mRUzu0J","MV7MbT7KP68P":"ZICzOH7WwcgS","UQperiMLY3sQ":"k3XuwOoniTsJ","ajrjIkFxOubC":"xHNTtzbLxacF","1tfbyS5kmXGs":"j5Usd4Vu0fY6","kCGD8RlBUiIT":"7B0GF9dONOIB","BkjuROEKWr3e":"juoXBCwIgI4H","k0g01inOkiQT":"r4pdfzkFOSdy","hsIGy35wq9R1":"S4XTngwO6kCr","I3J5H14xCK2W":"q7IHLtMSho1c","ejY5zxdnG1oL":"k8deWUuxMwgC","EyvYep4zJ0qN":"sKPeAssTD9s5","3sV6c2ETYvlk":"KoHx8peqkkA5","WYrscaensQSv":"FiABcMnOZzFZ","Lph8lscAVD4p":"TDnyhMy5uj4Z","HwO4LEGWgtdb":"AohwvHnDKMst","Eb0WrSSsbnC8":"tGpAevb35lpV","nhKTbR5Jqgvk":"xoabGHzt1NDD","RrqImp0PRNUs":"BZrjmjeAdQGv","yxx3dEuuBvSt":"ucH8FBXGW0ot","W5udK0K6o8qa":"XcP6r93AIvPk","WlYxjj46x26N":"HkL08PRHGMYr","hKmsHV3N4RSF":"uJjjGW3UGQnB","KAfVdZrzEAPS":"FKe0R5ID63DZ","HNfzxXfsxsvT":"4Z7uiftkdMAi","epRRJqcsxjvz":"vI8NU1JC7acM","eGrKz9iTsyLP":"cvUDwqQ1kP03","oSQWWeHzIFew":"rpAomJ822NVY","5yuF0gI6p1j3":"UZ8Z4AqNWmxH","ldJozUqy0FlY":"E3AFb3clV4iY","ze8SLKzxoRp0":"MoDhaSJpAUYN","Ri1q8gYAakLM":"bbc5MLZ5LQkb","mKp5qxNqG2xS":"ePMQhiHWxIaK","P0idaJLQ2YPv":"fttzY3lTZ1an","WEo9C5ZmNeQF":"KXTy6Bov7jSX","CRwBR2iuhpbX":"h8Keh0TeZQ13","345Jil94rkoN":"rTl9gIsPLUt9","o60C2ButvJXv":"9U7FjSyAYygQ","JBeUoEgdLmpO":"fTcgZmdocebn","6Mp3WZBlruQ8":"b1ErP8MaRfon","QPXnOpNJevyo":"e3AY9JdjUyG6","DkuKb2pLYCZ2":"p6adrKDoS1Xl","fC7ogJhHdb6a":"q46REQGJoVMx","0L87IS4iyZrO":"B6K5R9rHMRi1","TqM5MUQMQXe4":"IBeo3odxAgCA","jHJGLgIxxLBM":"dyrQDo8nCwsl","CVmrHBaPlOZI":"j71p37evQuX2","HCC8kTqgcc80":"WqGrB5mcvvuE","Zukpq1rK0fd0":"7SW1chLEW8LG","zvzpMmogk9Up":"M5f729Cfo1he","C1UpIgzz3Fvh":"uarEgXfoLp5r","lETzbCUdfZTv":"9PGU33AcHUZJ","EmuudoIt8Z3P":"tuvAzfgaN4JC","Q4AeWFx4NEMv":"TXLFlk33K22N","IR5ap8RB6lQD":"tuqNqBKLrkul","deOGnf9DgXGC":"fggKzYmjkK3U","HlYHQniP8TdR":"tMSF3q03kkI4","Gti1H8yd7IYS":"rxp9UtYbr6Y6","jRmQjydKfE4c":"sEzSpNFQXveH","2m8CwK1BBq3W":"B6SmmvQv1coz","m7NItnf5pa5f":"Ipv0OvpBqDkd","Fn8pJQz4V4Ff":"vjsSLrXbxg1P","Brl2wYLmOcnm":"4WiFk1gzVYOj","FDBzbgf0ZPVX":"cekTGNY4mZ27","7Ks501cVv6uB":"Vd1jKJBMmuFn","OtG1cRaF4UGj":"PRDoAOy5hNys","Xr4ffQH158H7":"n1SQfbOvi8oE","HmexBadVKaX5":"gWvQLR5yFs0z","NeSbokkhvpr6":"XRk7tEcC1pyM","77wXqVNUZmqr":"qx7St0wVeEgm","tLamsZ0vxxVu":"YIGLAuMxoY9o","vY5AM79NSWk5":"wwoTUTEUyiV5","uIzENQkE5auG":"xtTXV9BpzSUT","vmwYVICPKqlI":"ndvJxLDcT0UZ","O976G4TXd0Rd":"WvVU8jrXVe9l","M5VWSisGsVrW":"UnGrpRTyygWX","KIRxgUGNnzqT":"m7zavJfbvFcz","hVuESwFTLYPf":"Dua0C5DfZlOt","6tb2mUimB9nw":"h8ehhkArq2XV","EdY3JXQTI2rG":"dtHx3DKwti55","ewjJuHaJIty4":"3E9WjZJbEEgM","HXLYfbCPLa4r":"DQQjR2fvynPB","zB7ze2onHdXW":"I7Vdw1cgKVCw","zRYKnlcgIDHG":"2GEZRvwaHsNG","UDiPPDfWc181":"LRI1LO72MjjX","pU8Rx25Hsiwr":"ZhLJeNkToULF","k0OG6IVNQDQB":"OtGmF0jGlWAj","kVMcXFY7XPm2":"yn4dnXEva7hV","sTAvBZGyJCI0":"9nj1acpVHNdo","GfA4CXjHRKQj":"chi28aXcPSuR","RmBRhBueg1Z1":"OAw0C4r8bsT3","fOQ6F9bqLVxt":"VeCjxOD3Rgus","965lDFDmQOCO":"lkJPQXa87zIz","q6RvRuN1tZ6P":"nzVr1K7RR37X","HpyKYoGq7U18":"g4OB3F3f0tL3","pk04mc4iAAHs":"OSDIyEqbbtqQ","2MicHDduofgF":"Onf3g4rIrX6o","j3lH0U3THvbu":"b71tRd1aBEoY","9XWnidNq9rPW":"UNOeKWadAZzz","LoV8oTlAMHyG":"ia0TQLwwpexF","Xe5UBJAHzww2":"eSagmVD9WyyJ","XFiRLVh9kVtb":"ThFf6n5JSPAz","mzirDozR3z3V":"usLn1FIqz1UB","4wCEquaJkWlQ":"RwbSUDUWXYQE","kFKS2h4uQpDc":"MZbOhnnmlEzX","ddrCCvymdDan":"uHcXdRzLg4QF","Msx7LHhBCa8e":"8WxViyNfJyf6","67teh6X31Qao":"rc8sFwMg2RL6","u0mZ8cJut0to":"UpbUDKcDj4mu","aRrcdXOE37iL":"fXPMBmxbERBt","PG9cSjCSAXDc":"VB1ojDta8vaT","7sFlTafmwr1T":"LbDbFRVMhQxq","jPUkBae5Ztw4":"zMseyK4cud28","5RBwDGJ07pHe":"IsyUakzlbir3","CHATcO7hxplH":"v7g7vQeg4y7O","gfJ2JecNQ4Og":"feIHM9G5E6PU","BujjSZtOqyn9":"acT84yJ3WtJQ","nsO25cA9uq6N":"3qKZP2QkRgi0","ISixnO1mL6OG":"mM1nZhMy59yS","cGxpm2HrIizC":"UOBNVurGxZTh","vrVSEZlLiDEH":"u0IgbDazYirv","FghZ49WbOElE":"BorMwtIIu27B","FXqrFPg7AaYA":"ROxlHr9Q1jji","oIwRrYf8np9t":"JKNeZNhfSMoF","HXPjBw7WkKMw":"UCxUBK3o86ey","9J3bltiqXx2k":"wUVbWZIyfIMY","rBV1H5kkdWvu":"o2oU6zFSNUtM","2ZpQr1lkaVsg":"Sd1pJgE7l4Mj","66IrO0e3zTcR":"B08LGL765GL3","MirMDXVQDvt0":"wbVoCHDbMcXx","EedPf5tAvQpJ":"MSEm8WY30PZZ","qH8te9GEVxqO":"hdhMwwsKiRuC","D5ix17xEbaek":"XSgaWjmwLwO7","YGbEPt45rNoL":"kR6YxI0WCZCl","gRXklMz90oi0":"9P5cM7ljrb6y","unzx5jGX4pZ8":"ZhtO1CObc6ZI","6u3yKLsr5jQl":"NtqbwwUKSKN8","BL4depB2ATt9":"ZiyoMG2YVI7S","oiu0mSZcOWcZ":"v8aXZHig8GSF","OAzUoLrpIPCN":"Sm3God6RdmGC","cqCUeVlHT1zw":"2iP6mpINlWJu","zlutbLdaZzd9":"RzzvnLn7nhYz","bco2oMYuA5Wq":"kNOSVNFh6hGW","an4ybGd4wD1f":"Gd8tDULkihNK","ZT3qLUiFrZKi":"3OVav5Qmyokh","7lKc3BpBWdFH":"3jYo1n6rQXKA","BQDz3ZRBnMfj":"MflSB0Yk1xCO","q6KP3ADjlgMS":"lMqo1NmbHFqR","AF8fNJkX4bRT":"VhBBDkDxXmZK","NlJzRFnAhLrz":"yMVKOzgM3jps","lvTGaDVg2Nl3":"n9bMCPXzU5de","82EsDkwJ3Zr4":"TpA8VnitAumX","iiGlzVtD6L23":"KGPFycuWCSVj","vLpHIFhefR5O":"I3zEtNhjSr9N","MhROLh0WITIZ":"kysCQGyjjvgU","bWO0yirzRsx5":"HwepPV5pDG3z","5FMkj6I9uScl":"ch1HDcfDPZ5D","wfVw4iqU0Mz7":"so7JjqMGfZv7","ZkutJL050RbG":"eGlxzWXoHweA","cCsIo7whYdG9":"YT6JXkkUg384","nvU43l25sgz4":"vCUGLmk8fRhq","SM0OzlzYJBWj":"ApcolVC3NFiJ","eveZJnGgi3dv":"GHN9bMAKwDis","6R0yANGprXwp":"UI3cdsop9FAc","aQqH0aSaibNW":"tP6YPfkAMMrS","YWKR61QwKKua":"tIbIG9wQ5kSw","8ez07NHelcsk":"6TonvZJc89AE","9Te4W5UStugo":"KZNqO3y97FBk","RF2SpNUYTpUy":"lKNCfbPznv4N","4EpuU5lS4y5r":"KxmbRBItf8gX","mKCM9fn2Fwkg":"jI0rG86uM152","H9VrvBizmts7":"TBt6IxyAwEfk","0Ys07BNxIgLR":"Aa6b8MFu06Wy","w8trIAzBdZJ8":"KI8uRr7aNV0w","uleYMcXdssLs":"115cdmCmurub","X2yoxpBC32dY":"1OCiREiBh5E2","KT9RUQetfjup":"bnbIeKnxUTdz","Roz7X0n440dL":"8k8zXJvZ9HBp","3f02i1wTsuwc":"r2I6noNVRD7O","d7n0zPQtkmkK":"tLlHXXzRtn5E","KF4jgnL0KmsJ":"AflzvUC73TBI","IGOfoNfQSC2E":"YgFuH1tVELAi","IrYywMKkQX8d":"FgRFc80NvPQG","b2ukaYLsTqVb":"rm3hohSMTZjO","3Dr1tAigbrLk":"Pj4bZPfKm4Wr","SZSuBm7oHDFT":"zCgjtGHgRJfX","f89C55Sy9jP9":"XdQtAUsSXyGT","kyaaSWclh2jB":"a6vWxL11WywZ","PeVLjaPWxPZB":"PwQs5Fpi58cN","QCdVHNqE9Tfa":"G0YI55wwlehv","RzEdb7rRw43a":"YaYmr56FJSK8","LSBTB28azD39":"o0QdzpIEAroy","vNXDIWdbrSTB":"uEqgRby670tK","VuNqh1gY5rVU":"8BkMblCZd7JQ","3NS35M4Buemm":"P0GGmkldUfu9","59hs80i9GIag":"KfWsehyvdjaI","FXEGBWHf0UZJ":"Y8YvayU3Hnx1","0dd7He0JEWns":"hhnbxihwxMnT","1DAQopeqrum5":"Yo79znfj9Fr3","TZrjpz8hfHyE":"X5gQhBSLsHAr","CsfyRMOi6nLS":"VXX60CpeimYa","Gx9TClVBggCk":"gC0CNftSipC4","uWn86qKrig4C":"Ax5HKXSZXewc","RzVPj1wizCkt":"cARVdyEHkmh6","w5x8NEdEsRL6":"aKgRjtTjxXoE","Sku5lpzGdKyN":"9a2mNUWumyyz","fPWVi4rucxvF":"rRPRaxh4d6wq","bK1lEYvYxz3l":"cFrnOQeyqKyT","Ra3tS7Mp32n0":"6WTXa9evquz9","CxwJ3GbG0h06":"ySBhUgg8vDNc","EoJFvVXJVuJx":"uW8zYwReAhVr","niW9nby9gbHQ":"Y3aUgurQBrUc","Gi8IZQR8iA3V":"CPcSzp5xQWzj","Iku41t2lI5EK":"krKf18ApOyaY","BE4lIr5gROEz":"FBJ6p5tgcaI7","odtI5CRVgv3z":"GXNw7Mgx4cy4","iBZcJsLZTKYL":"EBqa6pbBuO5g","9MCIWpGuyTbi":"QR24Nk2j2iry","tUn3fTO2Izo5":"2Tu3Knq3tXCp","i4rXU5AfVw8Z":"aAzWPuCdU7Br","TGm2MtGu8cHQ":"NJPXzLr8NXms","lht6b61eGN9s":"lBSzThV50V51","cwW6jcWWIvVo":"bsBpxafoX05v","S7Y3CnvY5gb4":"x8vjPgoOw6rG","vbjPD3dzmhFQ":"vUn24AG9IvRJ","A5R2FvCpASZg":"hjE3S6RqDtcT","1QVOXWUBODrX":"ZN5QalWkHAm7","tM61echJ57J2":"tCZZWIDRcg3E","m3BovV7ZNVWL":"8lGpZJCNTCgB","oHJxlFJ3JUTe":"DbHiyp8NdWC7","8DRtXkzDIpbp":"2cJsDKX8k2aA","bfN8q5ewuJQ2":"o38NBCn2piV1","YnPaERhuz2Mw":"I2ceswt2vhBm","UE2T7YMp3eu0":"XppVzDrINMuP","jgDMTjZwCZ3l":"5lmzf1zYxgif","2obSDpeJ5BGU":"fYgGF5xrDfSL","TtwXmS1VBoW3":"6MWNmN7qr5wI","i5xQcwDDIrZx":"pd0PkFeLcWdg","Y2F1zaYBP4e4":"YpvRGnNqgdXa","YI23cXFhXXEV":"EYiXnOCYg2jX","Z2FQsGNppNtH":"OutN8uMZCxB4","IK3EJ4DetTik":"x9Hhr6OdMCcn","yetWzEuBJ9XU":"iaEwzUkxaGwA","Rvb07k4GwokU":"MYOIiadWrBlb","cLjx7o9McNdd":"d8xa8KiiEVRT","2tV0UrotXoTH":"UjVDxmR8xlyQ","fEHSfqHYwUTC":"20ZL1O8yYXxH","btLqjx0QM9uf":"Y3lqoVRHVEz3","9sV4yOS6Rp21":"tesnBPIuYT5h","6C7dWBgdRd0H":"GU4SYLst55tn","Tj7VRXHRlLo9":"hIBk8UY0lLj7","4oRpn98Gurrq":"r1pv8S0R1r6Q","fi7XO9fNP7OE":"fDyVcq02uWBL","JoigFatG4W3B":"J2lQYZ7qF4e0","xoW0Zb3x86RY":"6ZjlenPzcb8Q","XkxV9T59SME1":"H8ErPoaosMVS","OZ2oWJyxtmN8":"7u4pqarIfVTs","mHvXWBB7MzqP":"i7CUkKHXL0NN","5Lm0HlfMepaI":"pF5ny1KQwXhM","ybVvDI8HWJf1":"HjlNHg5mDcC8","aAUN6Ztz7rCr":"wvTOBuDXyG1G","r7bbXgUkwBRW":"OSLMXIGIAvUq","joX8Zxx4mkBH":"VIgJeWRYURzE","eNR0G0VMiBoj":"GF5YcxhDSWRB","L399wfSa2oIr":"fO9KPawNqXAe","0EbGMbLg03CZ":"nhIyOZZFFOxe","tz75W25gDniM":"ctCmV3WnZOYc","6x4u0eHvATZI":"ERPnhByF8bVo","Q0esEdFkZFNy":"Mf9cIE4u0JnY","RC7N5Vxd8KbR":"Bq2lJfqOHbpu","XHA5NQHpo8KQ":"mFLOVwmPFISh","2weRYze8Z1nX":"rctp8iaSD4ZD","EGuJnaHdr8T7":"wupZk4vDcDik","qPZvOOid3wLa":"jaCga8W3nuHI","q0KXpIWJO8Qe":"enOENXlL6VoH","4Sc6xpNh5rpt":"LLUIwC5SK2Z0","wQUwjYkaRsTt":"OCOrH6itzIv7","5v0IEfe4UdAi":"SSxKgqmxepSs","oETk2C9yAVJJ":"1Q0mn8khYmxw","PPGtfgi9G7Vg":"xEOY0VEiDXWx","4QCwPyGPXGCp":"YfElRF2jNf7m","qUOOzXHU0POQ":"Vqs5Aq0c2PtB","bBMD7yem1oZu":"gqII0icpJnrc","cZ6O6SFbpfQp":"bYfGi8mbkcVp","eYo8mS3gXRkg":"dsMpdKe04qKd","769KGruI3l5N":"7HwsQlsMApGV","pFyGioYLCfPF":"jGqgZGMPygVf","4W0M4vhF9K5c":"EpCIgC4kfma3","wVvOI9M4sKXT":"eTC0zjFcT5A2","15oCekys6hib":"UZmGXW4LMWSZ","GV5SUXB05za8":"WSsnnC0Y4Rl8","EeRce7BHuOU1":"ue57fHFLTv57","qUpFiMPzqQCw":"pXVjqPeRx1I2","JirXrE95J74e":"mv3vZHPNdWO6","IXy38mMdN7lI":"A9CrY2d4Pffm","kNPLtvPHlW7h":"V4EU0fsAI9ur","8NFFTXgKj9nV":"xSvmyDJMyXO8","9q48vA9ihhIi":"6THea8sbpTJg","twr3BlA6TwJ6":"7qsvOTCBM3wR","HijI1boPCjyM":"oM8o7dFd5PgH","rrvInigAQOwy":"7HvAGVXtdXRW","nRRvDEQhJ5Ag":"SEcBlZ3OFznQ","EWexnlOc8y4y":"YAdA4qlRL3Hb","zxljM82B63hH":"KZIVELszfVBr","t8GLEczPDsVC":"luw6LWXNYP7i","SjF1eqdhc497":"sh7VMwjABMkc","sAU6mBh1PeFJ":"2duaDCfwzxEt","12xUuV8aGBhn":"wEaWShTT0wO1","RBdWz6FLJjIb":"wQauqBHAbzSf","VDaSsF0fUmHt":"kta8ZPXZwDwf","xf1aR1OPVtBX":"I7W7nma4Rgur","3TieoNa09wvd":"4qAIIjPHI1G6","qKz2SrQGqmdg":"KQB47ZWPbnGt","tn10ektjybU3":"wEe8vPrHlMHi","kSipqj1IZiie":"baDDTqe5eYpY","ESSpygRY8Jgt":"b6r7Rs8Bc7sT","DkRHykyhJDJx":"Xo4DJqHE4MuF","xe2yKknWOK8w":"aEbE7l7Fwypt","F7pv1M8X6eFy":"02O7V09HMOU4","nUROfeHlmVGW":"0CTi5jaTXqyr","ZDGVLKg5gf6v":"bkjL6mcofhy0","81sa94Szdnid":"q85yfa9bcU98","tq1iseP0pCGn":"dJxL8UFQEhNs","kN8rLu8UeRGd":"jfghXlxluSA3","d9axlzc9a3cN":"O1ZMaVmvS3se","T2R2MYRBzVZE":"B1sSTcwxpyOf","Kc6euS0tgtZE":"JMjcouQ76B0J","Ub6ViOHQ54tp":"kRrBMy8NPypF","i0IAQaqQeMZ8":"NeVXiaunQyuc","Mc7iZy1YEwzp":"RVh71mbfqiTZ","nwjLVJVlVdDS":"SlPcllBRLbYH","d7bRTnmE27sF":"2zdmn8AgKEeL","v9RHHlUznUc5":"PP8JVjpORNBi","nWYeYMQmiotm":"Ud5E0rdeJCXM","UlNNJdyEKHPv":"bF5LQOJlaqu0","bRIJI3y1l9qA":"uB7CiiCGvNzE","GFwb7VW7xVtX":"OvYAH8gLOenc","BFQqsAzh09Wm":"yMm85E4TfHdd","ErZxhhizPkcY":"JYPiigt0Hjcg","WvIWaoCpWIVX":"0nVHa53Slqxn","nySBiizeDLMe":"ESN5n0KTwtCz","irZJqkPPwKyi":"Is9ozgHe6pip","5DmXPJSB1BK0":"wjb0aW1Dro5o","1Nlf603k27wV":"A60egGtI4qyZ","VKycf2xX1x1L":"Uu87IJEho0ZS","i1yFxpMwT908":"PLAsIUj4pemC","x46YBhmZNHHh":"q4jNCWs3xVgy","UHvYSmxZG6va":"q5wwHimAh8dR","Qb94PqeOTpIK":"qMuGTxcBi4zr","gDEjY1GJiS57":"GTT6njU0eDHh","WnVO4kaOwP3l":"K8Xcthn2SuCi","hQODx2AIe3zs":"bSnjvOtc0t6a","q24h2OVgbe2K":"ieVLIk5RAAOP","0OXYnoCVDl7Z":"EnVKp6fOFt1t","gcMpnRWDeCnS":"UxuqPWhwuojL","uEZ8dg916yXk":"BEfKjIlAFYzJ","xbLMUGR60IcY":"9B1ORW4mjW9c","QPuLXQ4B09P2":"2o56l3JQT9Id","ozeeaGnAG5he":"E6liygnP5jXc","GZp9zZcGmHzj":"MjqqiyNqu56Q","togAMZGVtpBe":"jPupG10rlfZl","34o63aq2Tfya":"Sx8dlNq6tvfu","6ZNBzuiL7le8":"ghqZT2MO4G5O","RoD9SqZSUNmJ":"6OlKzTRlomht","Okgqo5U24xWc":"EYIkln2UMkQf","WmLyxP1DVGEj":"fFCZ2Z9P15zK","iQhxfkZf7ak0":"07kXASy9c08l","nx2ywq5eAGvr":"eJ5YDhtaC18q","gBaLgFC9JMI0":"7G773k0zMARM","dN8TUIkQCQHa":"tquwO3G6weNV","lY1vng1nXe7x":"6VH8yoEpzFpV","3dqVhmSqD5Cw":"W7OJSe0bB7Wv","0C1LlaqoviWv":"RKJpMqIJABGE","50U4Eab1tYac":"tsodWBRrRMHQ","PPZlntU21oVb":"BHqEbZuocmz7","g5GY7RASYxsB":"jtFO1hyFMhAz","vjWRwcgvD9lm":"EO0nc03PtOY9","Zxx1jIXVzFCe":"jB56csMS8a2J","w4uNsGsOUgep":"dCtsU0shKCYv","HwKmi5bF5gt5":"hm1nyTLdQl68","2A2b1M73U07n":"0iz3u1RUPEVE","JkbLa6fY938u":"ccuZnbsxJQYu","41GJR39xjs0q":"58aq4HHZVKU3","f4mG4ukf3pGG":"9hBZ0q0TFWVi","FZib9RuYR2DD":"EzvPpvrCjyaA","q3Iw01Ef177T":"rLCh3Dk0cgM2","RAVZfOFDi9CE":"y8aVElVLBaBb","eIPjvP9tPln0":"IGnZJSY9JsXF","bEcRZuPBVe3F":"9U3sPgPka6sb","G09DTQiNkfbZ":"eeWoKSztkVqV","RBSHFo7BITv0":"K7HTrW19qw76","tNoEPr8bRl3q":"kpHikpGtP6TA","hNdzqhZVnkdz":"iJ9r7bHyRVlE","pT03Zzg9JSWD":"yDBq307yeNAU","fzOgjmEC6Def":"LSzL25t70Dnz","jzDLkwT4IZ6A":"ay0ylaWg68fn","B4PLij1KPUkj":"7k8FC0D3APow","caddclUJK779":"sW3qttnPGUFm","YAAGMrKo2iNV":"lz91rR7NmLlQ","x5RBlmg9zskg":"YjzJJsgk6ajX","iPFxTmQCwhyk":"SKbfVzduj4tD","4Ez3dDN9gIis":"nR3NTS1H8DFC","ubLYKGQfErTT":"ohgmioLdw9Ph","fGLxVLnwTgXu":"Xn4lpHPWDNuu","HO63MfojfHv6":"JplmKMTiKPnb","9puYoWKkOJtf":"FpO7yOQxOyhu","AQXZW7eQvi7y":"pgpsxUGFecZw","kTeYBsY7wkYq":"5b9R4DP0D9P3","C5euGqhbfP5m":"Ch8lWjO2TAUs","3xxgiRQamy2I":"YNTnWR20aUbH","5dDq7GqNs9v7":"RbKhCux4hzTy","nZhRN87t83KD":"W07LFQ2wZ4Im","rGmEvVupYYqv":"sduU6imMEc2F","u5zOamIQzagU":"H7UqKmRBQT8l","aNDkkFit3Rf4":"0yBOak1NdEOy","xrWRj6JLLOc5":"2bjFcooaaj1i","ZkbLDsSjWyUN":"KTMFktqtI2g2","7tTrVsTQiaei":"4MjrIC204Cg5","1Ty6PL34097L":"OiueDBeRHL8a","oKcYWXiqnFeK":"6VDEYwsqQdsf","NkfQbmkZBmBF":"YASLvPIHBYCb","3X85NrE4nkEk":"T15d667M3qN6","FLmeYEwp1wRs":"rYSsgozhqWrR","jjcoyp2LcTpL":"Ybhi1Eyu4lc7","9Y5X3sbtd2zz":"dd368ikaFHuf","H59KyEP5ODRq":"7uDCvCpqn6BD","PyOXbc1bJs7z":"k1hyNVu5u6vs","MBxLlXGfDo22":"fr7FoainWucR","I3ycxd6Iu2Ru":"ubRcbbXHLmeH","xFGeqXNKPiRO":"qnzOU9WgZIHD","z2YirAEbI9HG":"nxMh6V86Pu1O","epSZuMqwz3YS":"qPLR9xGpniEC","CK1rf9hCNuL7":"3ZEGUnZCK3eO","xgE8qIPFM13U":"5kl4NBxoTdim","CibWEh0VVXrh":"mWT7yK2L1GG2","ILQDaRLLc2MX":"owY4aDUAsQKt","H44RdRaA0ku6":"betyz3cRTPfO","y1bH8hFPail5":"knE4ZUnEYeSR","pgwObK4xQ6Vd":"kzmcaC43GWTH","GBT5FAoo5x4A":"veqR2AszxINj","O71ambghhP63":"Z9Uf3DCYfXTy","93xSF04rdDUE":"j8UUJSeUBpdh","CiugGoDFa9E6":"1nf45VRnd7M9","eBLdB6CoGwnt":"c6ZFJUjlcxc9","WJNlbgz2cmPu":"qGAVfc5BAj8e","pKlqMd9fp4KQ":"MThsmsCcEpTQ","IaHlu5f9HYYX":"0rcd9NhGWb8x","ZX0THWW2SSuf":"s6cfSnuX1EPH","DT46Jxz1Yyyq":"yaNCCMyRYAGS","KBV3RLiYXM8F":"YWq2x7rhA8P2","t0rJuvy6wEoV":"SqvqnbKcVyeI","xKN1eYCCmMIS":"TartJMIK2GVO","Yjmfxg6QnmQb":"jOSQ8WHWz8Dz","cEmGaiVChatU":"ykJFDDSRGV28","ifQOVg0Zs4MQ":"CKnzsEKtJMff","LY35oxgt6V0u":"hhUHNSjoh2TQ","ph2oY7HuJu6X":"faROzuyFskZR","uBKaRSA7GVPr":"UvnEZLGbil5t","sYOjNHAM9ZXk":"dHcbijA5DxfH","MJ76jqATarx2":"TdrBK221PNgM","Eqt8q44CViyu":"9CL02Li96dyj","uoW5fKekxH3p":"1vQp3R55RPw2","Wc8CjDb4U2KE":"uOsOisUgvOsE","VIYpO6QKy8hp":"Yz3AVY3WrQoO","HLhOuzckGGIS":"6appFzYUL3qc","YZB6QT6YLIW0":"c0UaPzAVYVFO","XiXn4CRgkKGt":"EXMiqfCu6bNX","OXAEAJrgWRAZ":"jTChGlyLBPdU","IXYTlplSMHtJ":"ZYlshgK7OIsi","GpUBPPDaxxm0":"b2YwMtdZ36eE","5sjDWXcjRHsm":"8zg4Yci2XeX2","NtHzrrlSJC1p":"r60wr0ZPtGpd","OgYyw9b9DtsV":"QmvTxRjRIow1","bEBWUHpWUWpM":"pPS8M7Llg6SZ","b2hrNl0z4YmE":"MzukTubUhCuo","5cOWd6yphDlH":"JXKGXjXHkOOz","IpuNbDHSMnUY":"ao8UPeF61xjT","7c9TXTT4JY9O":"R7nUWr4rHhBz","VFZfMPBDArmO":"sY0GozSgR4wg","pNavt2mt7BOu":"4elogZwpINaf","6K7lXnNWJuEu":"Rnz4l4f6C3rb","3etK9t8YmYr5":"WSL6bk6kF5mb","NKZQeeiUnueH":"2NVSLwg9XkdA","NuURnpqaR5v6":"iKySlq4qjMd8","3L0ElHhnH7xK":"zmQlZ896obUk","Zj5vj03SuEqu":"BogYlnjGnTYd","VfFp1jDc1Tgk":"GIxGAiLg53Dj","xQjbqXd3tSpO":"fR438EYahEUQ","UwX5hyfiBeXF":"WEpxF3VkPmjt","GkpC7qH2EdG3":"JwpJMJOJijNP","VetOceVdXszF":"ma9Fomgidyuc","2j4Q95lCcAgr":"ds9PRns74iep","SdFFvskG1Amf":"VwufT6cYxaOK","ccSZ2p9iNf0w":"eYVHWE76mbFV","NLAdiHv9veAt":"AUeJ4kJD4tB5","FO9SBrDTxhOp":"AbEtAevLGpB3","DeTGkIygAxnn":"6BuRpnvnRVoi","AjjS1KfOflGR":"FaXl8YuLoj8A","LIXg3dNxe3Uq":"Y3ECL1N5aEWA","0PD6FIgtY0oI":"lRGTwryu1WRs","S8TvMFO2CL1w":"lABJ8k8HyxZH","pjLQlOaAKQe0":"uobffhK2Jm86","PfDon8SSSndh":"4kzwm7Tvxdz8","uZCqROPPsKnR":"Hecx0r9aTeq4","VAJqjE286TDy":"47522Rz4eFhy","f8fCFUImNDLB":"fWRNqjZxbFRj","aulcqVKASkAA":"WnAmSgZbsFQT","ugV0zs5SvK9m":"dZsGwlbihQ6x","L9MXjZWSf07s":"HzHjGDjPBLIR","nZfiGyxcOexr":"eTnSQXwhwCDJ","2PKhYz8mftSr":"F6TNrEZn9UIW","iBWUCES7a4c6":"P9Iw1kL8sguc","SBCYxk9H7sKO":"DfIFh8IkL884","2AIHqxSaDtoR":"iTwp5XEHxPNH","fUARFOKgV8F6":"70lVsBnyk63k","27zKHCxUSVyX":"dDMWqo6y8bSH","iaQDqNjJVqTx":"MWiGW6fb0bgv","w6vUERmSrPO4":"1yvBN3pDBTu4","OhE0ngKodlRk":"sVR6Ju4NDd8z","r9rKhjzNJ4KP":"HiIhyMvyMiNp","6P4eH0LTEdq9":"xjcx7pLJMwfK","wp0ZsWKA4RzV":"iv7PeLZqXjYM","LdjfSWlhkGm7":"qMdwtivT4NaI","kvlJE0tU8lY5":"1NaGmfegmwlx","YGCMviGexpe6":"Faszlgy65I4a","3YDBJVLgPlrK":"v43xJyjspzXI","uz2Tw2bZxhNS":"iNdkgfOfRbQS","0wntEAqg74YG":"7pSucZn4oQWe","P5N6dZTF0xkp":"DHOsyrSt8NmJ","WGLG63bglgIP":"zaKebv2kQDvM","JwjExnXEnBeL":"s9WY6jHfWBpo","plWjZZfWUtHY":"W7IQhnc3K38y","SUZ6EQjYqMVE":"kplymm7iBYaf","UX5ivsDsGG5t":"s5dJNKzRPNi4","t3O0Qw5yPo6r":"rTBisocKTO3t","h1cRq0NtQyNW":"ixMetlO2XdDd","lIEHPfQSElUk":"qpATXEKjsPPZ","h6ikNb3akEfR":"N586bJsoJofa","W1W9FmHUaD7j":"YApWDjb0L8F7","tnc483qJtgYT":"fsC95RHeIIZ6","B35kJfzfFeMt":"lVFsbeUPd22p","5TtmWCr4nY7e":"nXrzdYbc5Jm4","4SBGY16DLpnU":"mWVIfuH49tcu","7xplWNQHWnC9":"W3hrODnXXy6b","YA9jsa3IaIGc":"OLDDnyEIrpoY","6DB3XtCt9BdI":"4uuzfHxkMf3H","765ws9D1dV0k":"LTRzXQH2Il48","2y3s3b3byOsh":"nzQs8Meihuim","nQ2XufO9prxa":"JqJ3a9B5elED","bLN5B5x0ZL53":"CAPFJPmQOCa7","Y2Xh8KYI3N8B":"IRCZAq1shKHW","73vww7KFLqVa":"c2C0f0TubOC0","I4WMor5IuNxD":"fNzEtU022vuN","WO3HQPckfW96":"RsbJiwWhRiv1","sterFOgwVRVZ":"Vp6nkyo9jkWV","fymZmEKHFtyL":"juq4nb5xtq8I","urWhvvah279A":"Wwgui8HPlKFE","OvrcIszd0mHi":"7lGGHBmJTDoi","OtWDOMclOLYK":"W5oFHMpMaEP4","UKkfYvC3KKB0":"BfH7imHV6jcr","VovOInagh0ek":"QOBfmJ9xDcC0","XdxbWDzUTs2X":"zpKPfdkrvsqs","giytPFS52ygX":"FdpEKEpCBMCp","Y1X957tM8zw7":"BXAfYdzd3XSI","i6mjTjiRR1TA":"n1B1HHwHbkcw","tCVRdchM2JPC":"Oo21p1MTSWaN","JeUTdOmg9lfA":"3rtWyRIuhf1a","eTi7BfjHNUCC":"YxWhncKoB8dS","GIXX1aUp1mRf":"5f3Y7AWHYi8u","FBzsky8UYhD9":"dTVdlDRXt3Kl","ZTRn53Voba60":"nkd3itarKp15","4UcEuLxpGUA6":"32ikMOiBXoXX","GWxXYcosuIjL":"oEl3XaFntx6V","MA0JrIKDyFSk":"5XX0hVUjJAnw","V6rcsglUs6kN":"6xwp8TvUPtxB","pmv5XugjO1vr":"dei4wVLtgnyw","X9Ad4fmx7saQ":"ATz3tjzcHRk4","teUlw1AKiHbR":"itRenFca7W6N","TtFEZV5Ejiyp":"3nHvYScztv00","A7eE3CvHDYdD":"3eKbDuLh4l8M","l8uaIqiELuN0":"2NVzG7fIb3C1","CLEs1ahk2I2Q":"vA5jIE3bNpDY","5z7is71bHdHR":"FHHTeQxTdeIs","jPsuhFcyJGF9":"u1YvVo8UokYX","AeOTta7psGJD":"WUO6Xw654cre","wALjMiZVhHzz":"pMC2QQ1oR9v6","SFhZND0qKLEM":"nBztsgNu4Dra","2UV25fe7is4F":"4h43m9GR5Oc4","5ugYfds811HU":"OJ1BqHbCeI91","wjFcsDLVQWFz":"VSj5iGgUzmSf","QnpAfk3BlM9L":"wRVdxRDcdDBc","8NavnronHMwi":"3FjTFBNRV9o2","wlRo7mWPiuF9":"3IMDDEb97Rj9","GDmf5gktaL9X":"OQ3C8VOImAjs","2R70CxxIRIB0":"Be4r82kVGwAD","NpZyWmBnrA5N":"Srl5YMqvBgV4","62ZI1ExZFPrt":"mq2utjg9pYBC","ZMm6LB5yngn5":"qWxRN4nhjaZU","Ou3Lj436jLrB":"Qi7BUIs7HMCY","KRSfd4QiSeNs":"anrGqS8nTk5r","GZwbTww8yTx9":"uQ1v7uqUB5d8","KUJURal3WmxA":"8wTIuMSKCveC","ejaZXGl9Q3Yg":"nafnADHLp6UZ","hlSJjDlQkvgS":"kgQ5SRHq9MJ4","LMpj3ULEh4Vi":"drp1LrFafMFV","vo636NLp3POO":"jHOtzjYIRGSU","5u3LogQkvHvK":"0zjdMvUKcyOw","r7HRrHRbNwNY":"4sB9wh6SMzGi","CrlrrHSPn2SK":"Iis8vkY8dU11","V2zMlSXTKcsU":"0gdXv7t3dxeh","FIOyGojOMPVH":"KoiaVMXVoiMQ","UsMHzZBhtSmC":"X7HqG1wAZ4Ek","HE8uRNNR7ygy":"sdLIFkXqaQDW","zsbJX55WyBey":"VfkWd83O5ORE","bs82tNKosScl":"xH23HHMYmF4d","GurFZGlmUFNG":"vPDPAs8BfBz6","gI4lXAMU0ewJ":"2REahZr5YSsN","fji35gq3PjyU":"vj9VuBQ9bXpo","cgZqFclloHLv":"qR0MsOkDdn8t","eAkgaTzzsCjw":"tKQSsFbvPAT9","IkWmLyi4PPJp":"yPdSynpfWoai","6iF2idkPvk2Y":"huov1e2Apv5O","WL0wGmXqDNew":"GBVsGSF3ifFA","ZhW8hMHhkCZs":"kU98vRs3Sv5m","xz0VEyZ2cPq1":"OUniOG0sMo8K","SIz6yKyT54wD":"4CMZj8bRvONO","EucH1oKnLS1o":"uwusFN8OarKB","DFdh0lUGwmK4":"VoSHSCDcTNp6","hrXA4R5vmtAp":"k9eTPNqTcFz4","mHbzsuHeISeT":"b8bejOCY75oU","izcfQ5qphW0L":"Tn5jP4JcLCHG","JMYPwS5CFDcj":"u3f6ow8UcdaB","TtfOWCCQS49D":"MV9jh8wEGva6","XX7CWnVA98Zq":"BbJ3y5toJcI2","VW3VvvW0rsuM":"dfuCkgNdcg7G","2LAm7cP43rkk":"O5dGpYlk8cDW","IStT7gmVX2Wb":"hPYfL7t443DE","WAUbWFHgYi0V":"n2qBKBpEIVYu","Gn2EdrlGUqyj":"OCbWdWI6vlhg","mUYtz2xXBtXr":"UxUMXeM574Ed","2jgJhFj9TWml":"soNCd6Y5ZPsC","gK4oOEcinpST":"p7kl2BPXlkma","TWE5Sx7tAWfZ":"LxsVM6klH4wu","9utvxOdKn5HK":"pWflyHUT7wpL","AaxUaSkVS57W":"Ad1ELXyjFt1Q","KdEFpEfORYVE":"f2ITMwExJSfQ","SgR65adzAsvM":"pvOz5NKWt7QY","KETPBU0x4pvP":"NGcf2JH2CNP0","qJprXmkmswmy":"1Flid7LuDiY2","JdsEkYrJVN4w":"EmeEplfiQkU5","VvKSTFX6yGfH":"q8GJQO7cZLZW","T0iUksmb4jVe":"BDi00bFWVBDI","6nGk7DwSo3TH":"vMdMG92nOiVA","yNfTskkS4R1O":"S6VFwFKoweQ1","ukteHcgbVemK":"212p9GYahgkY","roiQArCucTBt":"HwLLvSsBpptf","21JAwjQZSN3i":"0Yf5ZQ8Mey8L","ohDxV3NxRHdd":"LiZ5JoNZr9gl","eFkpdij0dyPl":"f9QQ6bxsHOsf","o13yP8b9oQ3h":"OcDjDWUgib3d","0yCbWONpIjkk":"qmZ5UuaE8DZk","leF2mRBK1hJZ":"VjYTLeZHEVQC","QiO2kj7hu3Ar":"msAOpPifeGOb","p3JrveJ2HF8n":"UAhLNO5O6qtr","NQ6ky1PJ1wZE":"PD3de6wh468m","bRkw0CNroIxG":"19oRGaE6ysvi","OwcRg6ONVPLc":"eH5kgo89xEDK","j3CRCKsxvn3f":"g1s7eDPnO8zq","LZhaJqzyYSF4":"mpevZhgT6HMY","Ps2xGegkws9p":"VoJZaNbrgpEC","HVg1k7ZHeGeN":"rKWKn0pETggg","D9hMlK9D3RKh":"mlZQPKbe9FkH","mfZ1Us8Dv8Ix":"DC83ZnoU9aBR","I2HOLFabHbEj":"2L3Sfit4pYHy","znh6L2Evhj0w":"V3WnVRXGz4vG","Or1fhg2KaJAL":"LjrBOjNlY7SY","UPGohzjh0wHM":"3jeUTuQ6Pmnl","DRJCMNvFdiqQ":"PStmbWbfUHLj","cVNjErQ2a90f":"5CNKmAh7ekRW","LyHN07uWA9oY":"3YrREOAlgAIK","Vk0JnBSumIlO":"VVQUAXT7KwuW","yh6uyeIRFRp9":"WRFdP4Q9TXZc","4vVrqROgZEhb":"VkznjXZXKtYu","q7A3VJ80j8VJ":"lXHjsr6XleIr","9AmWWR6Bdlih":"PcboAl8tsF90","oOHrfbHYY71u":"ECM3Us96S1wE","KNxyeK4wvcQE":"hMGjexOljDCG","JpgLnGt2A0Ux":"tqkvqHdqs4l9","pgOkBWsNkONO":"eWlf9L9mtSiJ","YK6Tnv3Pfk5V":"VQpgufofxW8Y","q8W2bnLadrHF":"YqPfv4YkMRne","WHq4G1ym1VJS":"HmePqxEiWIyE","C2oTI9evgzE1":"FcdA5ugwuRW4","HolmwHrGlxv1":"j7s61FuF4oEa","mpbAO9MymVL8":"DDVtDfLHbEuN","wrHHqnkd4gZP":"GrncKSsHRjl5","3kjtWsyWHRlT":"pkl4rm7CacAu","EXmW4DxsmeCo":"hPpPCpmDubHj","TuzpNdJivGJ9":"7BYNqAvzpEwy","BJ9n22ewJBg5":"m8KvP0XnMD9L","8IKeAizyGrQl":"xjqnrQMmt50J","FEqYi0y8DsHo":"D3rvbVHoT2L6","NqYaueNPubPX":"ZDfZwIs2TFIA","xSrk8Z702xvX":"PBYlstDNMqs6","G0jg2A2p3vNn":"2QeDznPzZ8UJ","HAFcZ9dGIr5a":"Oq5p5NSAxVk8","LcJeBKc78Ztf":"S3R4IXZo5N5v","UInzSAs2Sat5":"xzA58TGDMXVB","FShUjKexEf7Y":"QNqe28cCwcaP","eFi7GCeXco8h":"c5vtoMA6WL7t","XHWKunj3TBG4":"cPBlWhhJO88O","nQvDjNJx1IFs":"9abvOwy7ZNqw","93VQstpZDsY0":"bQSR8lu10a3g","IzL9Tc7wbec9":"PXMrpSmkAXEC","aH0M6vVHKoYa":"hfGSgrSrjyce","fVxPdCxjpREv":"jU62I4ZClmww","jTquUGjaSifZ":"wnTcAJXAg9Zj","8b6LHrP85Fow":"UjfTJtDUTvc0","YEUF4ROryEE5":"HA8cuXe5BjQS","TXvgmUEaL5ws":"4YbOIKjlaxhP","76sMz1OdXrYu":"WUxWOJNNN8az","CVfraCWA8I9J":"jtQvXQhmxF7q","I2yYk80m1EHF":"DCbvGlEUWn8l","opXPqiqXWl9r":"sIxjlPDGwDdh","Hib0aO2Ar0kC":"wlNV9vX3LWrA","e3VwFVp4BOnA":"gnU4R4WlZSEf","GjMwEa0esZQd":"IYymTSOSCf5h","EjPJtwVfNRz9":"kkXWNoLplKBY","zgKganrEmMu8":"GZF6OWhVdsq5","NRMeiLqD2Wvh":"3Z74Q9H7jlKU","T9D1hl5hrMIP":"wN8oFwDCWMAp","sl8RCTTwaINd":"TtbEAfgaRQ7H","yLuGEeMrmHNe":"m2DhEPnFL5r1","dxiYLPUiRMlu":"3KEqub3hJmcS","ScA1LddZUEgC":"eSBfnib2MRHa","wcwRFQKJDA0Y":"IzkbFZUOGyf4","Q8kvOKDvhCPp":"vB69b30a3vIe","FPSpGJ6GAc49":"R7mQ7smQT6Uo","VDCrlloMfoNP":"FgszvFi8foXR","lLdG3tz8RcWF":"PtshvqUwSyq8","mwH0FmYl3A3I":"qiyfAkLtG9aQ","g2IojNirylgF":"5YlAg232irRl","RdplvlTEUId7":"WdQiadQQ84b1","jDmrrt2VQPPC":"dmpUZlGmNi3v","YDhIcLy24cLR":"Zo3myUxItDRn","XUnHOEDWHHL1":"XDEYWTXREszU","D4Era8aAGlVq":"9iY2Dlv98Hn7","dIeRr1VTVL6q":"xT32nt7unRSa","SZuXYQBaolsY":"Bl3Bah4ECYAN","ZOdEJHgs4axP":"A8wJzgYPZdc4","lCLtVhc7lTYx":"kQG8FQE6ggre","BohiTQYFdAkw":"70UaDA1lG8KU","yaEyX6iWbrcB":"PLz7eBnKbHx4","nmAr15C5Ma0Z":"8ZZjXpQyzO3V","NGoxk9SRZFhl":"eVfTGU9roi2p","dyrdqX69YAV5":"tIm0WRECZhZ5","5VXyWEObQbhE":"QWf1tawpnb8e","PD15GwMzndyd":"PiLOIAHjXqxl","PTv4dRDIIZ55":"RLAz65xFNwTE","ZXQSwezv0qHg":"qri64EjEL8nD","vQBFZtpNXo9f":"HWoEpWuMHetb","0O7OvjyRtgdR":"F52JEDAylx20","zhCzUixMt8Xs":"4S9NuxeB8yK1","XWIfUW9oBpRs":"RlJXdliCIaiT","E8z5FtTAaq6Q":"7JzpE4dxSalm","g3Klvt7JFpgb":"BtAOeeb1zOBo","63478GkbYEB1":"KSLZawfjck3F","L1yF6yVZTGxi":"ai7kTwuSBFMx","nSvaYPKFSxhJ":"ElmrN7XkSis9","zmHTUGu8lrqu":"ye10WLE0VnPT","eFyybqF9leyN":"BZTs6nLtngTu","3tdut4Mu9BAc":"ZcDY8EZekjIe","fe4zGOJzFC1v":"2WGJgLRcoDPe","pbA1P4rva7xO":"siZkjHnoBGLB","gaMGM0qSpY2X":"KTgtjgpEzVIk","71Uw9Eof9f6B":"HRiSf8mW66bi","QNdfacpWXPp7":"0ZJSrLYmB4bZ","k1VGlxDojqL6":"AJxb2eTkDN7N","JYiDD800Mv2P":"MTpc1JMg0zq6","tkcHROhXoXIh":"nkBRQNkTg0Fp","488OaXTbfSnk":"Z5CbD9BpiY5n","twSpf9BicFXy":"vx7CTfJ2D5rP","JOrCOE75uVXQ":"DJc5ZQNAFNIG","ecLk4v7NrvNW":"j8yQwn82DVXT","DoPE7sGG7Pcs":"OMVR6v5Ix7hW","QFOaQhTD2yUm":"DVjCJWOVP3UN","QGbmtWPNtwEh":"eempYmseYlP0","24UMGX3x6jdp":"g4R1TcIPqlt0","MCaZEdrnmmVH":"xsD0t20G3pew","76Zy5eoTtQUZ":"Cb6KcXn5s7Yr","Gc2Ag6gip3vm":"82gFEBZkAaTk","DVtu34GKEHnf":"frXY5X9NO5JF","kXzYRbRMvRXM":"lSniNOkrwY1B","cbs3Wl0JuEM6":"9YxLhvioICDQ","mz6ZBbHjSPle":"ZoB84kdljqdw","WvIS1jvySMcA":"typEl2y6fIUW","Kc5EGCiROV5D":"7LiqMulqWcXG","4oMW2PZRq5oI":"qqxjT0hH9v28","5yEtjdSO8dfb":"Yzsvwx3rbQAk","wcJEQ5si2hQo":"REu2OsiiaPy7","1FfiQ2kawVdK":"8bBYgUd90mhT","vuELKmWmHh3s":"st5dAs2P3RuK","ArkvX6WViPs5":"1Rm7W3kgaHSP","0eWlWVp33mzk":"Okvlmhzzj1ep","mwJWxbjz0A34":"VQ6225hQ0RtN","jb75v9oteeMJ":"5Qh7Fmp8XLPc","Bao7FPeyul3s":"IeJTr1TJZdpL","KO408IOQascA":"fETtepqlQXVc","R6y8S9L2Ub87":"hYyLLP45FcfC","CZQvoNNT62gK":"zPiORzZn0Atd","MlcEbbfMUOpq":"y4abzVbuQ6lW","pUOC5xYNdJff":"hgO9bmo5aPj1","CQ8sYpI0UFQi":"N3lqF3onrWqU","BRG3GnwAuIHa":"9bvn6CrmtsAs","NmJuQPh08XjR":"0tYKW5Csz2z0","aMYZ4a859Ccl":"cpDTUxG7ASal","tG9CI8x4L67P":"u4095mVSg65a","C8jn5QrVaNDw":"EsJ2eOWxXCcS","B4A9b7G81gsi":"fBLyBMOLyjEk","fpyUmI6Ak4Kl":"p3QK8q6yQPRi","iYXKGSVuigL3":"EuEfqSMNJ3Py","YAWLDYIQ1a3s":"y5ZvtSaTg7Fa","2nYNKRgMisTW":"xy4iz7xLZdVK","j9Uot0gFaqCb":"u81rl0vKOr7Y","7mxEQWMkEq4w":"kYt6rBaKx17l","7uOOGk63qE4y":"ZDGAiU4T0xmA","GulQMHF6RpSY":"HwrfWNTpLRQT","xRkAJlX13cY8":"uMOUNEjYNqxT","GUoeAhja4aKx":"X13T0GFe7k14","tgA0svR1Vcfr":"OdY84JZ2xLay","HmkFdnK5gOSw":"mZy9lSAtWrRG","25EDkGZfSDxN":"wx90fuY4LWvk","gIeEG7EkAFXK":"s4bqIH0wXubn","Vq5kqbfgZM3s":"9Px4wKxcMdvl","a8pMYruiBQEq":"SMHMcGeBmIll","DT9MNDdlSzlU":"aO00B6a75i5s","XeUilUlNEn7Y":"DDc3msEMN1BO","I7bCM8tTfSHl":"KeaL6Al4nRuU","JSLfhc8pYves":"LBETNT372qZs","NjBCm6J5yLFn":"jdMY4QlvzDX0","aFoF3cnhrqlJ":"68p10TNiPuQo","2EnbFzk4HOjU":"W8cA6iqQB2O9","WloBkA4Y1pwd":"F7xsh5tbnN6N","RY1mqdVyL4bI":"OcbnMwRMrajj","lYBVhoW65wcQ":"5t4ea3U1eYuw","Tx8Tttwvnm2q":"64N02fPCYR5W","2JHd4XmJpRKy":"5bHhSluQCSbT","M3T1xzoZEjHc":"Vadf2ZRqkhuT","mw6dkD62p7kj":"K5RyaevpBU8q","4e5UHnKs9BkA":"a9wqe2tD1mwd","Y2e0u06M7xHz":"aLUmBKsbMLDW","UBvIXiPS5OfN":"P6yaQKO8a5bL","op8bVeBIsxwU":"zcQOw9UZ6Tus","9MatDGpmLPLx":"lRmN6DLkxQC1","b95OgrazBsP9":"icV2sNr4ss17","iZyaoq8TNBZP":"dZ0NFUQEMMTi","l2CtDguDmODn":"ibPBaOTptraN","CPneuji8ER4m":"fnDeH9aiQPyE","wTf3HKKSHWJM":"wURts6o6YqG4","7rEiqEA2wWXv":"sDp2vm7WGSIw","yla3xPU1vxjS":"kQ8wAEfk6gRn","rmaPrj6kK844":"z1f5UM0awLAK","nW5wsmlChslK":"5qVRYrwRymh8","67HQD4UKfoeK":"pixaYKJpkuv0","LLw8kS2SmayD":"HgUgoxM3306T","6h9o8wnGT9Sl":"X6avW6D4BxsH","zj8o512sMtyX":"ELSAcn79dJl0","V9PaGC8Vdvya":"xNMgmQsTYudx","fnwj1hka3nBm":"TANLD2LoseGy","L0NCAbCvWbJR":"Pvgoz0N337eL","42FQEmOR5ePr":"jTsQcZjdxPee","he1A4wC4XRJC":"J2k0CT2e4zsq","HCnq4gsNzduz":"ckr4FpkgWocX","DAqkixKJLv4w":"elgwVVdb0klK","3VWZ8TUe0Skz":"g3F56qH1RjR1","v29MhWacJVmZ":"sx1I12YeEHa3","0RaZJp7jnZTb":"TujJefrhC4gZ","MefaU1MghR3Y":"06jwpSrfkQKw","COQ2y4NONvKJ":"4A4O4LhYErfs","pb4iiMaK7vyy":"Ic2RJejlcg3m","PfQoEgZcnzmk":"pJZUXfBo0r7h","a5l47Lbk1iZj":"FOv4Ssn21hrA","GvcE2ASulxZs":"OzsKk5H53yMK","WRjEO2Gn97ng":"1Fc6TZwmx57z","1qhvK8QIh0Nf":"jvIcjjiOX0Vf","bOIHN9un7siQ":"ZYQmpTPXcCCj","3RiOVqsDLjT1":"xl4WxWHMbRTH","lHVU6FNrXVQV":"rGVHbNEZwV7I","cXc5GvV2tsp2":"hEQhdxLKXi5R","aPJfV9etUU8r":"lrNzsGkV1JmJ","d7uphVHIFpOs":"T2WfP8G61EAI","fxPV30fK4S0y":"Q4pT3scTjmsz","N6EWLjX0ePbE":"g7niSNqse8XZ","kEzwJQKW5Kly":"pKk8G1s9nRrV","u8HwBFyHsJox":"fRBwa2jbfKmp","UGf5ULsSnqS7":"6pZSeyr9lOeS","ceXDrjMcGKVS":"NuG6724MVMpP","1XX9TSIp094T":"HFZp2QPbITtF","wx6f2jSdn9Vd":"B3lA1Ryh8jmh","Hym6HlA6Mk0o":"HW3oFBYXTj8O","vVGYzgXRRYpi":"zBdTztrSTiAf","PIhBDoK37oqg":"jduqPlfxvPhq","AyavtTJJLgKw":"zQBQtdOA7hHK","wBeeSTNbdkwi":"idepsEryKZDE","gQLUgNpCmS62":"ObDbIaTXA8a4","hLnPmpnvVd3z":"ikb9eOtmI63k","O0P80xlzbzir":"f1seAxjpT7mj","7I1gpN20H3hA":"eaG8P4CJNu5s","Pwa5HY2nyaHt":"8J0CEF5QcayG","1MjYfJOPWQoz":"Y8w8mo5l4qNF","scFqb2wtyYtc":"74Oy2HF8DQlR","TbBjA7MWbJxZ":"ZgiObPSbHF84","vDvknDJfj3h4":"SBBncHmggHno","fM8btt421t5I":"sE5b0OmzTidX","x7yiHuIFnYGP":"McPss969Lq6u","ODboQDeqEYMI":"9YztSX2DEDNv","kGgrvkx372KI":"OvTSiduPPXgy","t6hlVNqszRTr":"VIZMdNeyiDFQ","3qRZfZMHFAs3":"1nkXQBMl5w5n","HinaJC3v2Pdx":"582RkXrCgtUr","amgvw8GJVwyO":"oE81XNPUfDHe","usNtnjXEsG7O":"8g0EttqTWLmh","c5rzwLSn1s1M":"CCPvEzG10Xq1","b4lbEtS1hW7F":"kX8y6oPuuKJ0","R3psTSUwDQ3Q":"uhnsiP13ngOb","uSpGu4ThhoiU":"DruuogizVJe8","zl4oOOJrV1QE":"ErKeh2DymwVT","ajNaj7uiDTh1":"lPtOnFUgK3pc","lLVpoAiTPgae":"QsoK3LDFvyNU","YQZwblGxXgF4":"A00LO4EPqcQU","zJIMA2GxvyXA":"nzRGcRsywFq4","MM4yVEOLqoC5":"6epStm4GSLuj","QAlUcyc0nwiV":"OIxeCvHrxU7D","nUGavIBcqzMm":"x4Ca4mpx4eBh","kfxhY493O0uk":"gk2zNV4VYZqy","v7HO8gtztgC3":"PAiOZbIN1Psu","14lTCAAd3w8v":"mI7LpUkUGRXe","eiUmiU1tIEyn":"pCQPPOlUwZLO","cXzO9xa9ueAl":"AKUassfRXIsk","tvlOVNYkRvpo":"Il2D3rPNsFw1","T4c5AlBHpGbp":"Q1cCfkX5kgJa","iGofvYJMLrpD":"d7zhLhz3ZkMu","SpIcMFoeJOWJ":"NQkZWAAXRs7J","hFhnGGt1IeqK":"ctXzcMxZz59E","UFJSyiUhOTKt":"U6VW6jFxRKNa","saAzIPHx78SW":"7jKkP78M3L8e","45Mr75nXmTde":"EmUbgQ81vNj5","SNehaglHfxIt":"l92oo1KULdbF","wm1PxL2NPyU4":"FokxelazVKHW","VR4JbEXBT1Vv":"4wI4tjGNGZYh","kPpchFR0g8jO":"awubaqKgmQWh","WHzZUOIolZPL":"FA0xhV6gYoX4","H29bG4Gj4ECj":"tcGY1ty08kj7","kh3h2M3VlWPy":"tIDSl8eN0M5S","yN53Nmx6oOJM":"vBhElQVyUDNu","WQeM3gb0SutX":"FbP3n2xHkGIM","xUWQ8TVoscH0":"rMxFHCKb5abl","DOPgR1g5Pfe0":"kS9NjYB9GVtA","QAaoM9zWoanT":"cTvJ9wdA3F1g","HzQwPIYYBW1g":"W9PHytFh8KUE","Zlf11yKgta5y":"glrF1vbO7QyW","wnIPbthrjxUN":"nCueNPApl1KF","XaeFwcZa2TCh":"K3Y1NJmDlUkp","EfKdEFwBvKBU":"VQX4ZswZMMwd","9X1fX0VeUWBx":"JQVRmxDj4HbU","1x6wY7Id3eB0":"flTB52XDefb3","QvzbW8IXxxUt":"rxGvlUCwTTVc","PSXbrfP70n5b":"nchlCk4WudIn","MWTJxcWSf7fx":"1goczwovls2x","J8KvAVaTkZ7I":"LgTPhANcEObA","nVXDlX32wINg":"YKnpLNrngnY8","tXW4y07bvbAI":"d2bnG35mMSZ5","QW9oif3cjZgW":"TGJ0nTYsBPlb","ScGULvMsmC7N":"ZPD943c9VJOb","WzY1Nc2fGVWN":"LsBHwlTsqRW8","163UkjDs3f5B":"4aglyQp6IGGW","fcUOkcbQzl1i":"7AKEf2Q9IECQ","R4QdUcaq17Gz":"6Dz1onLO3EQr","gAgGfeb5d3Cj":"0UenFeZhKuBR","rKRxIHO29NuZ":"uX9BiDT1DtL2","hKoINJLXMxRB":"Wl4GtpphIwUh","lKpUxW7wuQJs":"i3tsZwur7nJr","O1l6jjmqw3Pd":"PkbWGOw8CzMW","qpLpVnX2dkLw":"UnRI5fUNwnb0","k6hDoSKiKxKF":"RGOJFEEBm12K","B2jSYH4wi4fX":"43ze8efDDr0V","jpXKDefWjeVm":"p7LsjYT3oepj","MUXx8lon6hST":"7CLNVjivTige","q4sNHSoIBSMQ":"3kxGJ7Tx80Ha","nOqRz6pGaZ5h":"ZgnGbjEItjPs","p4YMPwmgf9Ce":"0yTAMh7n678A","KFj3Tv31vtuY":"aZLnsuWXUefT","pS0yRNUnjIlM":"WNOpwMAI0404","kPqGKI7cYzJF":"zEgk93PieDov","ebYdSxxLxnPO":"zazNMQeLDSHz","ykdkiiguRv4j":"RcPkfJAhCgTB","ZPYJhFDibhiW":"bMLAserRCjJe","GR7HsKnGZD9F":"R85R0aCgpOol","u801V5LRiy39":"dYTFZ7PEoNFb","FZAStrBQAdmS":"WUBmwMDID9Ox","YLWdBuJL2Ywl":"gPoZKkyFb2Wk","ZxFBF8RARR2G":"Z4TfDeJ9FhVw","KoMqWF664DTT":"13bbWnJ2NOFf","QFAyxcuwIWjE":"uy4zGgPgot6b","ri6A43hDl7h5":"ysgrMQ3SOyYa","sS8yRFwGg1xV":"tBK09w0bgeMo","vFLL8a9Gc7X4":"grnAcKKy54JH","9oqwEo4YkvBz":"dJnL7SXoU6JL","GjvRZfPDPEtL":"m7o0D7KrFBvq","IRSuYMnFNxCT":"suMbOWyRXFE7","70i0RBng31d3":"0uu7cf0Z28aP","yEVfFQSodMBt":"nS2OCs4ci4l7","X9z0jXqlpfuG":"NT81iCGHNNRc","3UaMN0CzkiZQ":"TRtNBKjeutKN","0OJimehKlAu1":"dpnnqx0VBQdB","Kco5jCkmqZp6":"YHnuWdlDqMKc","57fndhAKXB6k":"2aIJrNraW9DV","YshvTs1hlQNx":"PR3sa6Oivn5n","IjcmCGm80oTA":"FckUvCGAgfpW","i8NKMsbCHAph":"GynrTGFyJfNG","Dgv6xsKIefuy":"P2BatSEJtxUh","MRTM2fSS2Z40":"21vo8kTO1Bpc","yRnDOT908vJc":"oXPyzP8eIu7h","JtSLwFy8pxRC":"7zMOTGprZCB3","hzlMSi51eo5L":"S1GHXpycF6MT","xVy5pzHoMJub":"pjgCXk7gJoAV","S52E6uOxSrnZ":"sW7o4aywhMQD","Yadir62oydfQ":"uJfZfGbeV7Ml","7tHLCf8Km4nj":"5QpN7ecYshUi","IgCMTZzQBqCF":"AMdAKKIqRcIx","SeaYrKXge9J4":"RV3iRuMSs4A2","mqKtx3xzUdkJ":"7ZXqhilw4CzY","bOBGG5VH9ovn":"G5xZoE4VsFss","PEMnLYoJh2oV":"OxltQXg4D6BW","k3rrOan846Nw":"9DtQfbeHd8mu","W5WCVpGrwOPC":"R7iWMZjX4YRf","iWquWX5Vmeu7":"sOrWDjGbWi4i","pxjs5HUOADZ1":"e9QZA14AVPPc","pAasRVBJGqMz":"Izp2Q3PMhzSS","RuwHqDV3UBCW":"G9PTCQVOdJQC","xBUhyDrjdTCs":"sRmNAJ8oLFXL","z27yQn19BavQ":"5B0kCaoZERbN","unhTRJWAQsnV":"IdrzWl2lOvep","yqeOdRUbdZdh":"jvWpiCvk8TcX","liWutGNqGJ2f":"pxcUmsdGJMhP","74le7bqh2j4r":"wUyhEbpFjjDv","zfsqAH2n65yN":"cuNnnwUWi38p","XlH1NkGJNN52":"xStolHYqRDKy","np2kcpjL00ad":"6eYMCOcnSk3g","GYBOeR0BlZyq":"jHpu6WdpWmHB","OlqgnHKCscDK":"hSftieypuB4o","odWGQnhsz6Uo":"AEVhSev5kmBH","nsFJjCrhi8Qd":"vBCCEYnfyndm","udP9jFftszNc":"HK8sdBOXTH06","VacuB8TeRooC":"aOeOvEmeZMgZ","WjrPq00G8NJd":"odiRDsNCJPiE","MqJXccpRsneI":"T3XcJyqGbhBl","GsHfD1RMrP2P":"ZRyPGaHp32BZ","uuBi6GGtmEGl":"q5HzyiubtUXq","sVpkEsLG6ZdD":"Ky3XiKAC5LaW","86Ogy9D6BONx":"L7M5uiabeza9","zmm1tx8AXqh8":"5cxZD6SedJO9","wGtHZMdNmegG":"UZeB6eAXTkKA","NglaIPwDbju8":"0ZjN4M8oeyIe","wZ52bDzpW9oH":"cfKaAwCyUA9V","giJsm7mUIezA":"6uAP8WLHKzE7","cmVT63nJ1rVC":"ZIl4GdpuAnqt","97qBuELvPW2e":"Jb76MrypPtfA","a67T12VXvEcE":"YLCdpByR2slK","aXHzJwSAMhuA":"nctdAGDglZ7w","0ZHsetoVTmOB":"AO5y0xPyouC3","gCzD2P4FdWXo":"QcRlghgrUVGS","I8zQxf0uNFHK":"NuCMLYSUcSuv","U6erwyYpExDM":"WKnSvkE3W6VG","Bg3LIhTWwqEn":"F9wFaw7SD23U","u4sPNuS97DDG":"1g2lwyyX3O4P","L5ne2lYPxEXC":"goRBkpdwEWIq","fRzDDfmC5YF0":"vVrOKv5DHeAD","ZNhyEkOvY8s5":"zdok64V6kr3K","mzzvxeTB70JG":"EiDBuSaLRSwN","sJsL4EjLjLJJ":"ljBdoOWNyqAy","z0czGUZY1sir":"hiNPIgzvmsH4","uRNieaMxBZCm":"oQrr9pTqDqA1","IDzwJs5cW9rk":"tmFYUdFcE46L","Wtt4TyIYbCLN":"1Md7wiVq20P4","1TnZy3UHa05k":"4e019VsjRTwb","R4WC9F7K5899":"Ey8k4ErF37tf","I6gKOqPDR0Ja":"TnPPT7hVOGjQ","InYpKtFZo0OQ":"ZuEcYb8DtyR7","S18UBA0IlN0i":"df4mha5yENkb","PBGQ6ytNA9yo":"uGkww4K1UCrZ","QykhIHUHYHK8":"a372V98lwuhu","84UxcliY22nk":"Q9XJdhPfgcun","ohohsb2Hr6Hg":"GmA83KWffBbS","bufmroc1F5PU":"jgEqP4etF6AP","Kd0sGEMaL1SA":"Ti6j5fQ3Cr7w","IafrOs6w52ZP":"pciLQMNo3MtY","iNzeWn2Hvejc":"bn53LCQifZci","hECQ7vSltYwY":"oO8pAjekEKZE","WGUJzPnxaH3I":"HhSnZpl089RE","PuDRKagQEumI":"hpgAIUdPeP82","Lx0Pk3hQNhrO":"cvG3KwruOn1v","2LlihQVJ7CTj":"JVjv5eeKzSUP","xQYtsEc276Ap":"6oAukvNAfmEh","BJfIB5mPQlta":"rdFUM5ZMQCa9","ARO3TlQ4S1KQ":"ySxSNtng2dC8","4YNbgL9NbcfM":"vq3GXjqAYDrN","9oRx66sqFXsv":"LO71NI0buoi3","RTxUiPPej6tH":"8JB7xyQDuh12","8wPPnvuStZ9k":"jZysUsHPIFCl","3np99E1UgePu":"Lj8AjbeFbhOr","MBq6GfgkP3gW":"CoFwzvsyMAJd","9d3mHnzwx2fP":"jeKhw5gXIJuR","mByAaCKbAgdJ":"daLuA65vhyZW","FUO1Q3LSxUnF":"aneFt44WUDTz","zI8AGxvw3St1":"UDwlwVXCIoh8","7UJlPigmR0e3":"L6E4PFfsVwmx","gqZIpsrdUpCd":"gOn4d8LhZPBL","f4nlYDzyEmp7":"gm5212bGfdrA","aKHshnmQ4TMW":"QBrHzmVgytvH","YN4vQsoFN831":"riqmO2qCpEIk","ZWD4WPbwpaLq":"F1MwN3ib5zo3","mMNt4UtLnhuG":"sHEI3aat8Zog","W3l26dyYAjFg":"joA5TgQH8E4c","gESi9U2NToFN":"JyI0xsaoLYMa","NklhriszR5W5":"zoXmoqOrOWDf","KFG2gZemdBK5":"3uOnPSwmQFvo","LNh28VOK7aFf":"w26pvYAxOSpc","NRtsuRUNCbKE":"MBn3xi6Yyjeu","ILgwMuIyf2B6":"CMy0KKDUlKr6","3Qn4WO4EbM9Q":"rKC2hhd4egLM","E7ODPJn7cu22":"REj5sut4L80Q","ZuVooiEvx10i":"0iaQjnWMCNr2","aUnD7ENLvPq7":"cr9qUf7TEYrQ","0tJZLnZkYXLB":"hKqGaT30naEQ","mwGLEvp6Qjx7":"d6tt9LMLmqbi","kkB0IdGMFPs9":"QQAfZq7zE5Bm","oU1Q8kgulqJX":"K64zuhsSDVVk","nmju2RO378xA":"bbfXCTB5LlPx","XU2Uu4h7V6zV":"PJgSnuyWU3ou","kuBXlXDycbah":"vxTnqo6M1iU0","A4JZUZwzy6qR":"hmPNlK3yt5eI","CKORYwVUpapa":"PGAuLEBeNMIO","FdtEJfslxuql":"Cc47VvmPLIdd","ydHJJvs3jSfs":"c1Tn7dSs3QPS","iSfnRmWMf5rg":"PpDswuNfiW80","5k5Oyd5czhe6":"oyUvC4heGn4k","3ZHBQt3Yf4Sf":"pOMkpu8T5a2g","QbcsJy4dcVum":"J0Hllv3ew5sm","kTQ3xEKpra1H":"i7rGiZpThMD1","N7bGp6k0HbsT":"Yta8xCEMph2d","XjINHcQYUIir":"IIctej4yL8dx","x89bqXHfu2mi":"jfeDjduyDj91","CvXN3mX0bVtv":"QYOLf7WMEBRF","a3KGT95Vx7iY":"KmaxehC71u4z","1A6fSxZKBT12":"HiSmbOCUhWpV","kSoxoJfEnPWz":"c7dQDGNLZEhG","9VSmZMD6Uwdy":"y5xcJfttDrwy","4wqjxGQj0KyC":"wHlSmgbeeZkl","yUVZ9CbruFz4":"O047UtzX97ax","PxBcQPJ6Spf0":"2VsNxN4t4nnK","dbQzlzUdake8":"AZ2wNRMaDdRE","C80efDWmXKlP":"aKTk30gD10GD","eSNbpzfOBLqe":"Qn17qk8XsCwv","IhV1TJaWyf09":"Ri63cRrphwJT","U71qd4Qodd3r":"dnw95M64DjGM","5M3kHq8fbTtQ":"9vljCTt8vGjq","DX9MU9q3qUs8":"jhV8AfcNuO0s","6P6SXPJsgASX":"78r7PiazVS1y","Y5xZieK0hRRf":"ZVAndyZkljT7","Y6lN4CuU3KeL":"FF6EL7nl1bIU","y5lVp61zeFXw":"DT2VP1iReRNG","KA8YGtnf5Ptj":"9rUJ5gbAuzir","xX6kTus2dOGe":"fjHsGzycHKDG","fycrGIUYh2h1":"XihCKerqU7mC","zxrr7B0bd6lU":"v89v3P6BaAK5","WAFWSJBRpksg":"982o1tnH6Qlp","UiesVQYwzExm":"gpfD1xsxNI9H","O6meL6GlMEzC":"MIGqEO9Ciuyk","URa0uRkDj1UB":"sXgcd3ysFO4B","fpbnK9Sx1RUm":"6GsjmhDfAwQU","ri6d7SPpX8c5":"1LhQeZhpSiMi","VzlSO8mPB7uR":"2Tg9moADCg3J","b5ecebtXDWHS":"V2JjrctkB3TV","PwL8d2J2URR1":"7gpbUir82Z7s","PS9pXOKjXEsO":"Gb4kHge8L1Yh","x2O1Pw4LgBhE":"7TYpwzIEU22J","vBrpmG62BYF1":"FwO0e73233LA","3cNdHSiKl0R8":"StKCOknOS2KY","34iCm25ouJ5Q":"QN3NN4DWzofy","607mIzFrk3eR":"KLdVH571qh9r","T6EUvf5pQ5zR":"l4MPvgD3OYSM","Lb8adD3ShEyw":"jZ4eqgA2TccZ","9kIjdbwB9sp4":"ADZI9Iu8Qwup","3IEPbbMpuBIK":"3nf8ofuIUwFm","ta4MvQ6y2EJw":"NynTUZUz7AG0","2VYZWhslyhUX":"tNaheFHDIE49","Zg3XKd1jezpP":"iFmJFJReb3H2","l8pxcMvH5jv7":"lx6wU295bV4n","SCNJtyyyxqDP":"30BwwFzuIezl","ehZMc2MUx1Ii":"eBYHQxFOlqut","QEl2PmtvegOI":"0rdDQnh5G8ya","xDSR3kLS179L":"xQCUOAJ0CGfv","4lemnKRHa7Wy":"NYibOY4N3Tyz","R13tRKCDqrf4":"WMPyi4ZttufZ","QAmSQZtEreCL":"SiS1b5S3kE4n","LbNvYfa2kRvK":"T8fQ7JcY2a7R","FUysjHchz1Ao":"iKR7bqWlsB6w","NUYRnKh8nsVE":"tjXeosyCPcAQ","TRgKSJ6gikzl":"N8azrV6SjSnc","fsqyJ3tMReEB":"xnnAJZ0Dfseq","ib4FrCjzYATI":"YWsG7boD58YZ","1trTMsnbsav8":"HsicG9elMoap","0N7hQkfw5IDI":"LSmeBqpkrVpK","ArUmo9KCy09H":"kdwo6e6dZXHr","hWGiHxHVIiaS":"uyeo3Tvglmmu","0OTTsZi2DD5B":"3IifzQWLcCqS","CzWkPVY1ZW3R":"XkuWfP2K8Uu2","3TJ6WaxrqyyD":"OF2DbyvIcvf2","MvezGZ6jciig":"l94gRVcmrmKK","5MGQvNrrxc3w":"HlNexOPSo1sI","HXpLIVbS570i":"MIkrc1AatPPt","2rNDLwKCyQRp":"bm0zX7Cm0J11","4Yeeuehd5odd":"NYOZvJdk9ta5","UztjDtVGrONg":"EnSXLEAxFwix","agp49K6wMSUI":"sk2N0jQcxjYN","MWaBF5QC7kA8":"27I0lyfQUyr6","V1NkuxpKKOhH":"odxiG860Is4j","dcDSveKU3zBM":"UFmMDm8AMmN5","V95sWChT2sG0":"P20PdT5Ocpvq","G4J60CoEiCMr":"kSN1ekCO6SEZ","JHWGqPzKhH06":"nRVtqKXzI0ha","epBqSF0kw3QD":"YpPhMJHNFEri","OFfJS6nNXw3O":"h1MsISfs43ft","YaQFZvgaywhd":"ogU4oX47xeXK","PCNkDjdaPVxr":"JyDvpMIRtQP8","uWak6AhaEaRe":"74iyV9FJKJdk","aZl16YFZzTzL":"ZvO8VnXWbwMb","12LhlKBbKX8O":"PtC5ZhYFY7fI","uOWg3ivXHzA9":"54TW8Fq3Sh3o","o8QjGYsHNIW0":"QfXieYK0LYVx","y1ndu6b3Xcps":"YE7qHqMyzqiP","fTUP1kBpCxGr":"v59U72uz4GRD","hSOcTqVyYjMF":"STQr7XTDBbQZ","1ZPe4IsAZ9Xg":"PWyp5wqoQjSZ","snxkapXnwyRh":"RvNXiDCgBxfF","FdjzHKGo0bHY":"Fmi1Btl8Lvfw","g0Rw4fyrcOza":"MP3JvLg18FOM","G7RRnN59QrOI":"qmGH746nFYAt","OJybumtR3M0g":"LP6beasel516","NufOP4erXe1b":"csU87LqCYgKv","lIiBeSgw265m":"wGkp7i6v3PFy","j9lg4KXsDKjx":"oZY3XuWWrTkC","Tkiv7BJHfwdu":"12xvra5KGLZN","Zv9l2iL6Z3jY":"b7tqe5ghv61s","xu8EypbUu5w6":"AooZNxrR9X4T","0LcrCyAMrUZR":"VzfWdAMD1DEW","0B2RERlRC5ZC":"XvvPUYDugTHB","c0PLawHHGAIo":"fg33jY9pDNpC","3wbGuQIALJ4T":"WLkIFdSGT8sN","cbLST4SSPnPk":"OWVdxyMc2JT9","w4MVUdKvtl6K":"Sxl8Ah5zhb54","O9XhQbG5i5vy":"pJGh69dX8nta","yFN11qbpSAi3":"pWNIQUXrmV5Z","2QFm2litdf7t":"59k4olL6VN8a","8Q6JlLWaJD48":"apiCdtB01fnt","XZk0PZAVne6M":"ebeJlyCpR6nB","VGf20a2GUG4I":"SsmA9Xh6Xex0","ejZgP5It51TE":"7xZ2xe9ZlMy2","Y1I8nnTgbgw2":"M0YodMhwOh3c","2SOHl656u601":"K1AVm2pgJjQx","EHHY4gzydNDU":"KkY7JtmWJtBU","eb1gDDy9p3BI":"qalZLcOpSqpx","E5ugdFe7TgTs":"Ep878XMiqjV6","OuQbShssbEnB":"odfq67BrSAsb","aiBsQhOHfCr3":"qAElKHD3Ne9w","Ydlvz7GQGutr":"UxgqHM39qJpV","Imgw5tZUdgvD":"FtrdzGvMNetU","U06zwAfsR97s":"4sCThXY7GRzA","b0Gm1W05AAvv":"tboyKU4pWcek","40DrbRCpyFvX":"M9wc3bSHLhG1","HuByTJ1CLBFb":"DNnPWNk5AaO8","pgrT3OXbhK5e":"8uSREhkyAiYA","LB3W43vtzLIG":"LPLcNbLfNkP3","FjKwhMWwz19l":"MeOqACBnxwVi","tdZVVRBXPN7Y":"qiI085B7M2sm","aSHPrgdWEdKe":"q9D5Nl3wRHxO","YQVzkmPWffjI":"pDlGHxwSQZQW","cK1m7JHNkDrt":"ZjLGUKMk8HUw","PMdVHA4M5F54":"xfOVyv8KWKNh","iVHYWFauRVhB":"rZFhjKtVgoRy","76wBqxZVaTUc":"pj46sNNxq0fl","VbN8SJYLPd3m":"JmdQ3J1m55T4","mEGaD7M5XNyV":"sAbHcPjGSTri","11ktivTdvUHl":"f9PmDt7oohjT","5l1IKz98CirA":"T5eprichrXBC","CwB0sNuc0vOQ":"jOcHGAS1CRNG","rVwNaK6rIq1U":"LowxK1eUknqq","duOWMnnbp1vq":"rY4VZ3K1S1nx","7L1z4vhwcKyW":"xMwxRDcAxzti","Rf9nOOt0CuWo":"PC8mbf4041nh","I6HiDJX6B4AA":"Xow5cqqo57fL","AcRUwlfGhe9L":"uRI3G7hRkpNf","fZnOYJgK09LF":"uX1ySTRrbrVW","P7Rt7kcYSwmR":"1dbS75bxSaJs","2mJzLI2HePxj":"zahmmRCQQHH0","jLau5z1UbFtG":"LPIfcntvRKDa","iTJ3WS4YVQRN":"k3jtnNL1KGWt","DrMUv5pe7Bun":"Ao3sEThEUzwc","eJ9ewY7VjjpB":"UaJDnrSXAlTa","JB1Nsd3KCooM":"FgRTRu4zNarP","yoMVzhvtMSrd":"v1lDdexFxIYG","ZQUmhfUjvaLE":"RbcSyqLa7Esr","9Mot1M890lGk":"9pkrAFzPCdCK","jbg4obRJ4byu":"mrGOv2UZf4Cx","ZVsK0SCcEyOc":"DMqeHlue2lTT","PAsJsjMZpIRT":"gW05Ao43cEpC","70NGp7ltuY1h":"kyHXbBRyNLTv","j3NwyLLbqewc":"1ByerZOpE2As","jFbrMjD3wLox":"FlthZlUH4rVq","S2BrK2kerYww":"SI2OUjl5ELcd","tRBk0fTsVrEy":"MmpMSKzpHltI","fTCgsZ1fPkWz":"2rlhuqNdkxzU","GL47ruqRdjCA":"XapoSWMtI4JE","DMMPTZ9EGhB1":"PXMtu1s8NjV9","jyPR2mQogJvC":"oulJLgBurj0o","pZ3AIDvgnef3":"Ha9ppa7JCCrD","k6uWUDoIDHGq":"OKeoQLGx5KLn","WeIKdeVbGl2H":"vZZc2c0LQ2Rf","LgrCYQyiGkZ2":"Sn2Y0P1AO8i5","PS1iEOWuSM8s":"vKTF44gv7C6U","JhGvazDyXMy2":"do2yXZifayWZ","TGO14udjj8qi":"qhsTYm4H0ub4","8VJZpJqugGHS":"rdq0GGtdVHIq","LnpU3wMRQOWV":"rmHUXmxn64Zp","H5hrEQmA8Lw2":"8kfN36nx31Bk","zIln0Wf3WLNS":"cbaQHengzAMi","cjUWCWWThcDe":"1stf0lN4Rarf","BDyZ8TMkGpS2":"cLKmgMoKxitf","2YbwP1jn9zxQ":"Jy8oN6FhXLE6","oqIuE6cmmMiZ":"UR4qd5MrR8NG","3QNI7M6VSdFG":"60s9ZMA1Ggc7","MkaQzSKdNE7o":"WVOfsZWfzYIC","JLlapy0yZdMX":"ocs2OqfxoWSi","yzcUFa3Pm4IZ":"HG7KILk7etHK","BB1NOBnh9cUU":"mleg5oiMmmcO","lvvkj2ZvLBIr":"QCNo2CWPDV1b","VcDKuyPi1rRj":"nmkx4hTzCORT","G1eLRG1ucZ50":"znqJcKZEsUNn","ZA4frvBkO3Xr":"O6jKRBuAWraA","bVruwrZNUFWD":"9iIJYo0dk2i2","Han4nJxROUUP":"AI0gevHPFXZ9","IeCwAnt9h9Xb":"xY8CLS8du9EN","yV07rgw7KDCf":"UY3M5YO3uXty","weNc6JEcsBKN":"iP0i39t3G3m6","Sa2ktw1rXA5i":"sy04jhSPaKp7","FYDGTyZvJPFp":"MnZS7sz9W21y","DlE0rclFsk2Z":"uR9a4G27tghU","4bm9OHmNDHro":"NiOTpBdDVgxN","72n6JjerKYiB":"vYAn47L8AtRr","d1KMy1vakOGT":"Jtzs6dYlDT7u","KgpxVV0MfjCi":"bYEMXYWQtEjO","gdJ8BSfeJWRb":"hKRkd0cshWI4","gpybhUzcHtEn":"lHKQc8Ha4Ooc","B63rkp1CQ4EA":"IGNwrvguAoyF","HXFwyEfhZjc0":"Naft7nkgJh7L","EopLwDnycbP1":"QEe6HOLAsWwU","jMRrSMGLrhKi":"PsqZ4tardyaF","c4s1cBpKiXCs":"Rv3s5EHgKEXM","cnGsyAg0nCDE":"TWmmM5SylEqH","7rgritbNFxll":"PAgM77eyInRS","WpX0VcadPr3M":"Y5dyhIicxnWv","BwQoPgnffHvd":"iRJ4Tb0NTbdt","zsebjaRVi2h2":"b2ujCtZ8h1o5","ml6C5lIDnWgV":"q9J6gYGF3nUQ","OKOPxSn2QzJR":"G8N2Ddz0Yt1C","i3X9kYxMwSmp":"8kLd6fXXrSJm","45VzM3DgmZvS":"PKMK1yHE6HGo","ntCGJWHOKEX9":"kAiupvYfRozA","NvABSEhZk0gu":"wlXMqYfmfvgx","JtAcrXSDDSWo":"UOHuz0ZtzFj7","Q3R6Efw0H8wV":"hcFLRof5zzo0","wdWkOuanGQgy":"Wxb6uWWMssTb","EIbXcuta1WHc":"7ybRhn0mwkA2","izahIR77N7Fs":"ATBjgsFKe6ve","rwJtCMK1795k":"QRW2JflT5NWD","d0ZhBgAmkzk3":"Y8rYy0oF6PMW","QVqd8QfB23Sn":"T0ZwDaFhLvVg","BuzpPZ6F6Pzg":"OHhJGIh66s3T","MFo4KIylfeGn":"7kN8L5PKMj78","EQwngHh4I1Vs":"d08tocI7tAfM","iH6RWwhB32iJ":"edjHHMd4KKwV","AC65CT45qneJ":"JLTCxTKfYhaz","LFwLnBwzbzhC":"oiHuxLuxznaB","bZj60nccpj19":"c15mTQ8LpQM1","zZuUGNfGFD9K":"nfxPZbOi7Yni","nPYxgKvAf9n8":"z9ShXblwRRR9","C5l1jhiHk9zx":"NamZimJ8QzJ0","c4r3XpZ4h2Vl":"AzgW62BRAFZT","ywQFFxL1Rtdu":"3FqQHv4ZXTuk","udVbsiWyAxEq":"s78Mwrpr0s4F","5sSYK3CKGlui":"OalBasoCErUr","P3Y8NRa2wkAI":"ZcYcMsA1y81C","5LuCkSWcEG4s":"xxbdbhHQUX7L","bQNr98vyCTD7":"Uo6eXlpVfXG5","exp7zbr8hdZD":"u4s4zpv2pPfm","oJbUKuhm83au":"SluwUF5A6VWh","q86g9z209Gp4":"bPnNYtp8Dcx6","aIhdX5lLA9hV":"TfUpBVNC7hHc","62vEW7ZJV6Vr":"HbdFyRnfdx2F","aEzm7iqpCEIq":"4VILFIEq4Zcx","AxVM1DEG3Rng":"q2TGNQli90ST","RFGl4aXBwYgB":"mL4haL2UdXGh","8IQENka6cfVB":"sIGXF15xwnQH","q8TtSIYrFWf8":"lcxw2wAUKTOk","JOR33Y0VEhzk":"rI6iTXG54BRu","DHglvUU0DswN":"Cmkq4X9l8U0K","pN3uj7LVe5pP":"Rb15Vc9SAbnP","I2sC5hChSufo":"CocvrscQUz9j","Lk8UfkuQM61K":"dgCXnU5z4VfI","WpQ4aCAhHXum":"YEHRgLcalUdF","OsctdV6PNrVJ":"c5RuMV0MZUqs","KUu7vaRpoSHE":"Mav5OVqu9Mku","EZOFMyRvWY2f":"Pzm7CIqSXEg4","Qt6UsMMGn8bf":"OuGOgnE8NqOU","uiajNGROkFo4":"xo1KyZOvL5bR","C5zeSkY0XoWY":"kAlPr3TVKKRM","PjzCBqRLHYjR":"u8yIcTNrW1KC","WSr5oFpIN60x":"7fW72FCIpPkZ","i96io8lwsy25":"aG6XOsxgp0jV","wLJy0p3eZlmc":"TKkfFHSgpOR3","czDv3IoI1nPg":"wHvtsYBjZPku","tqiKfbMrgzOd":"d9BaWcnRiQuN","swNrUvxNKR4W":"gmlYqgFMuetr","KIPSKMWe7oYG":"ctks2n7lEq4b","t1pPodVHjbo1":"9SCUzBG6LKg1","W840ktreIWFg":"fYGZPwte3uMv","PF1ilMi86ltY":"uURxsYz8G2eR","iFEkZgWUhxm7":"kDt32PUtf5kM","EvodrbL3nf9i":"bf4BqZet0vHR","F0oDdlhXiIG8":"aLSx3URua5yz","Bz0AQex99nOm":"xlNx0pOBvQeG","avFs5Nwy6uou":"5hsxKqp7LKrp","HcdQGpAzw2mA":"oYiOaCFDM3mZ","2iPtoQnyogqY":"KOX0XcQClLzK","bACf3SyELHN7":"o4czgNu7hI0l","zerlRLfr5GkJ":"kcrukEX2FGtH","j7Cynb93jTw4":"6pZB59pKJKXW","h1jB6zQbrqb2":"V9dRiqH0xatr","offmKoRicsS1":"3hDOGdhiKt5x","dRVJ9MPpQwCZ":"X1KBgLw1k3eO","qg7x7RMBFMVJ":"Fb4M2Dm6eK9J","d0kt3tNHKRyi":"TGoqjsbdZlpu","ZRMkw3k1pLgj":"PE8LDJMlaqvH","30zAUZ8TlyPV":"ZLy6AugWtPjo","9CcthJzWPKtc":"2uR3D1IRxsvd","dXQ6tKWEqj5J":"T6KCFicClDJk","wik6rzJQ2OWZ":"los8Ke86mpEa","8bNHps5RRdis":"EPCzBNDJEnpE","HBLCDJPQBpoB":"0EVxYS7sbv4c","puSjDyzCtviM":"5sapKjNT5Aty","oBPU18HFz5NR":"IsnOoVA8NzeS","negxmovogeSF":"CRvvzOfa1Kig","WWicUHDIMg4Y":"r9zpjbgu1OqK","GtwV8JG7F8Fv":"WhRiwn0KlYE5","MnnQUPvvNenc":"h8U4lJspOz9J","lsFyGF3laxh2":"tyoa0pXvxxYW","XIYlFvBUoJqG":"hI13rfaSxlBj","zDKUEze6bmGc":"TQFPc1T9Bltj","232BrK2YH9nr":"td4iz96HtxcX","2Tiwckg9ZIS8":"BcdlcYo3VmaF","LGH5YOWfZiBM":"lj5lQfV1rZ6J","Bp7ISn6c4trJ":"6HggBWEVJWjn","Idy0sh77YUUI":"PQDRAhSiuNZj","LYLUwSrLyJpB":"Q574ed9Dg4pU","s6cg9zbNAJK7":"YtVIuv94GJ4r","IomzzrgSpZWP":"KbXGYYCWkddV","3KBAhxbltjy0":"ltOwaBFEFGN4","l274uaOwuBe5":"PlWF1jieVsI8","sj4C10zAYHmN":"49d9DqfMg3no","UzeB89rJ0Jrt":"RTliMMTEapCL","7pQ115BLRoQZ":"05qnFVBMJkYC","xbgmciqJ2oJM":"duddUeOo1mvM","inaWcue4NMwh":"7qWfWcO6Eier","0ogcYSpv5AJl":"zrIlN8t4yDr2","UF5p0aVnDpJg":"lNeYThNJih45","xRd2CHwjVXE8":"ufsl9XEiA5hm","wKTPUl6ghOSA":"dnI8vau3zQcy","zrL90AfhaV71":"T6VNYcpls2xs","rn4TzjfoDyNJ":"QX5piajNw3uT","Tvg0MNGwVUwX":"1ktsPOncU6dr","8b9dtVj2Y76R":"GO0cO4ZB4Wp7","IuTmPaKNQOXQ":"aKTp3MkppyP5","d0BBDbWOZaSP":"IdFQTwTuWrAT","ndkfg86PLiah":"CKmVyV1Xb6oT","U6zjL03UNEno":"LuYfpP091g9T","4pfYJYJCrrbj":"DJuQtaoDC252","i6Oek2bm7wBE":"UHPOCGkMYGso","rhF8lOBlayA8":"ecKBjGA0qOsg","3zHly8pSsCWQ":"aSaIQIbQhe67","4TZg2vqkicyL":"CIB2PPNaBCVE","g63c45mshNDt":"Ae9bqMARoUDK","on0uYv3I2cO5":"GKUQVf2xMqnv","HBSmARcM8mr2":"t0LdghN77Rw8","XbouO7852Yu3":"R9H4HpU26GP6","emcfHtFA2WS6":"68Udwub9sZJY","f7PskDH5PZX0":"AY87WRARCVOy","9zKgqnSWDAbl":"9iaKFKPPyO0m","2NUn74NVIRKg":"ljtpgdvKna6I","Kg058UDiKIMq":"um6jhlmkgR1A","GwVSckcjvGAP":"5XaqXSRWImC5","z4HvNVBmDhSN":"zpHHyksekSq6","BdQP1WefUIoE":"3AQw0RJH8JFV","6YQZLkMx3uk0":"8tR4XTwxGENg","f1po3WZSL4PF":"zm8Co1U22bPg","IaM776OSYXVK":"B7Ix1Pos594z","otIBGZy4zj91":"kSJk6nISXdyy","mFj76d2IIZsJ":"UE6Vy1kDKVNE","awr8Lg3m5Iox":"1URr7qd185ax","0vpf0GngTGv2":"cm19CfWGHSrc","SoQo7Ij2YImc":"FxWRnyBDq2rd","RPtY6dkbPKkm":"SKC4q043bbNR","TdkiZlAy2GVa":"eyAqITLHzonK","eH6A7Q8MU3Cy":"rpR5tocKQUhz","7qvXcCK24LX3":"mDZ8GyqXw4Oa","LEBu21Lkrjq1":"H1XJxf6qjzfO","ZtHMHHJfCgHK":"JjqEOoDNjUxu","lfh7nZHRpklr":"TJkdXjKM97WI","TPVQwN42icUR":"kJkc3QaM3p06","YEkpFxD60gWE":"wJ2b021BK1U7","cbR0OzAfjDMY":"rZNRRBSawTi4","p0LXvNGr8yEV":"jKMDKCnhDSa6","jeveY18uf7gf":"Lp9vSXeAcVaM","NHOQScQxdEQD":"QMRV9QOW0wmA","gHir01kD8BvB":"QDuclDmObp8V","0wwba7cJHSZs":"28beLJ84ktq8","tMl3eWIC2r2T":"4WqQD8cGIxan","csOe6zH7kEDg":"1SC8UkVAUkiY","zUJLbdG3n9bC":"CI0EBHrpK3Fq","soJkORDwKC3c":"JZoVbS5OZsV3","Qtnxta1wYn3z":"1aAYYbm2r4Zh","GdnIPF4IaH9Y":"duU4RjYrBE3d","g5IIgblDk8AO":"M3fwLY4XbrtT","mcTKwEY7BlcC":"mmGDBUs1C2p1","nXUJJXYnRRrt":"GoHLKbds5FBn","g4wxaXdmEID2":"zuMFi8I0ewiR","LAb2kWR9HN49":"PcbCKnlgJgxy","Jh0clKVzBPBe":"OWEoNr9Tw7tF","IA1Sbzdb5rjP":"KYL33iBczTVs","G2pcRHUFBTf4":"7fKo00GZCqN9","ycUVL5wdOhy4":"WQoXMx7BmYDG","zkIeNLS2Gehh":"ymErEPbOaApk","JPE7pKLNChaz":"TSUwxduxM0Xl","8IxUgnp13kaT":"SXCsgcPTSpR7","9ihPh3LK9sZd":"VBjoiGe9JTtm","kSnSWohIhWea":"Q0CVxSSLqNMy","1GpcwxS0UY5L":"lPvZ6prxuvYe","U8gtVVU7pLll":"5acyhkupdaaD","Pa80kR7Sdrab":"qj5nz8XzBcEC","9Oag7DqX1K8a":"K6icrr9KbGLW","g1UpepCw2WSR":"1TxdRpY7PgZx","PtV76HrNmzOu":"o2dDFOhRQCC7","legcLNK1kCZi":"Su1SzHP4dfPC","MQtE2la86Pwt":"PRyNNJccmFZD","rGuqPFcmg6jY":"HythR9PLcon7","56wmmMfoqbcq":"Ci2AyxMujmCg","FiJ4Rsy9B18q":"xJ4MS7PdfzxO","40vQ057ROp8D":"XM7WxnPFNtF7","NjjZbrwio99j":"3aWS3O9FTKkl","QcqGCZASHzOp":"WHyAe7KnleLo","dsgzqAUedoOe":"af6jPnbHaz5q","Ll1B4MHIPbUk":"Eui9S6taNS1M","09O2n0K6EH2r":"6HAfsb6SYI6A","w2gxoC9sPP8S":"8J9AYQCRXKKE","RkpOfQKW8wW2":"gZpnppNUuRrQ","MGPiMfPWvrT8":"Bp3BJ15OstY4","OY5NpUDTm6TT":"ZCUdv4XZjbsH","gVk0tDfsyE8j":"O4wIBOspn483","td1gtk5T1SWl":"HJ5pgjY6GV8e","ytFRh9FbrGAW":"8a036hHURvGt","3tC84GaJWhgy":"XrNRX5J6GDCD","y7sVdun3otVj":"xANWs47XbYJt","vEufWBPBSs7s":"5NbyvPUJAJ9C","wYqeViixlyyQ":"MKS5BXqE3cu0","rzX17bt2EqBV":"rqnRFdwg1x8b","wJ2ZTio1zKta":"pzoQRldCOvtX","8y7aPyBB6hNc":"h5FyLY8OOeCG","DzFDkHOsCQdl":"rjp6ZyXa51BN","c5yyzisKSMOJ":"jjRcXN0Q0wbM","NqOT3eIWdOG7":"1VuYySI8H2pA","jJfZfweufYbU":"H7pVsb4dMKcl","nkNYx1grcqju":"xaq5QMZijL39","iY7Iu19m1xW3":"PgPFPGxpy26x","qQ7mcPfpxxV5":"cbVjvaybpuVF","Xwka4ENguCnV":"hvEfCLAx1nky","VxxaUgJcoX3a":"AzglvzUuMCov","tRLyhjiyelxK":"OWD9XZgkDsob","J30BFl9R2tiP":"Kpi3xHqEIIdD","rIRfD1ljmArg":"spGbvoUzm4Fe","W41OXc5beT2v":"QjVJErLCjCbm","2nZzGUSZ9uKH":"WjoKLvYNfzqc","YS1LQEchw5HN":"dFaMeau39UXA","zJI47m45IUPk":"zgpws9SRFsaW","6rYHiUQRL2eR":"Ux2hCq3FH2nv","s2IioYEclz52":"pMu95j1haZxT","zBIEkQaPWl7E":"RSxkzWfNJSjH","Un4eOhAkd5xq":"GmqqRK1kqcbi","6S2sTkmTcwll":"qPOMrXstvLoc","vKibRXzrEauQ":"x5AxEAWpkuJf","1KgGhIAYeJsc":"k9NrlY6xpIYb","hGJF14sBawYI":"P4Zbr3p7TpAD","PMZs8XExSOM1":"hLtjSwwfGos8","riXMaVDazdtZ":"u6iqMtWjFnte","zTj4yaAn0XEc":"lXasxZ9JEYdb","nC5gHBvCr17y":"yZcvLsWYk7Pw","kp7jSmSfObVJ":"ei7HJHAOtwVK","nffugjzGG8Cp":"UQneJMUX10Ot","DljGqSfL2Yta":"DuVdv05pwUp7","PPdmNyyULsI0":"1Bt8sCdm5MgF","5ZihYV2wJFda":"tAd3KcEYENYD","l4Kic8FCXU3o":"35PI6GyTsJqm","PtemktlclOjy":"NfcsNpZLWxma","CnKVzQQqEXSY":"M4YuV0dcTHWs","qYOyhcrWX33m":"WPxP24OwpZMQ","HYL6cTEFrihW":"xYnSbu1qV8yD","pc5jWkcQ6mKL":"EyTM5JtnsD4R","J9V6v4fjaCTh":"avPlQhspcbQB","zIx8dg2uBngt":"pXTotw57ggdx","Jn8HUzHMPP2X":"rVbmM77Qbnlo","9da1FhHYwvfn":"3aRleMqGtflg","ZGG1YfUSdnBW":"5t1J5N5SrsbS","LsUZMKGgrD8v":"ggmpIQTCmMea","f6dkDLt1xm7q":"6N6lPRGjhdDQ","BXctKpjsN4zs":"i6kPFqhploHD","Q5chbAFlm3Bh":"dVGuV4DeoHoI","dT7Zrvmxwx3E":"Kv37G5lUhopH","MgsvfltCjnYb":"g9yJOgjkpnpd","wI1Y2cBA8pyl":"NQ1E9diqM0kI","iB1txvJjauq4":"yrRVAlaEoS0y","Tjtq8IMHkPmX":"06GVpB9CI0sJ","c4vUFCIQ4T65":"P7WZO06PJfLJ","l7iUToKzeUl6":"sX9fDvcx4N2v","15XsUp46Soen":"rhnebyREV0ys","tnxLZ6wUB5o0":"t1NfzHP6vHj6","T7nOjH35XSfJ":"Vs810Lwkd5zK","hxLiSnJpTDXB":"cGtYvduy9nt1","GV2RxX7xpqWw":"amuNbGKjpRWC","FUGNBxL2dte2":"ts76SHOa25xI","NjTPlSdRnyjp":"arP2SITX9agz","yuC7hw5JRxkD":"l4DVYw92QQsR","YaaOwcWwf2iC":"nMEvdwN3wsTT","Z1s16jcffYzX":"nDAeKjeIt5Qa","xSNdSIRh9MRG":"TbpMEpqCYV5k","yMsDiaP66t6M":"XPBiVCbcBCGm","POXWvfZc6NFm":"bcdZhz3egIm8","1CS4O1e5wPf2":"vdVaGJmRW0Hf","Vxxyl6m85bGB":"22wkpnRQ5hKe","jqgewH38rSkR":"5wKNolWZoVm7","cKEt2iUGeodh":"axqv8nSUL8ow","6VTEs7f5yAtM":"NMeTVkMF3vU2","EJfDWDgDAgmx":"OIFjY5YE4zax","ki8xtLZdsqOT":"haymH9v3fzxC","slXmQ3eFenrd":"EKQNPF4Us0p8","3cTgK1ZGN1rv":"eOW92lkIa5Zo","BTIJ81MLDCUA":"BJ1dZ8GgB03m","nCQMkGr3FwRk":"yUmWP1lNJCm1","MIEmRDQAQpRR":"C19l79ewlX9G","oeLojD0qQyFH":"Nz6sRBCvE6as","0CP8JxLNWMVO":"fRc5ZCbtJ18C","IPGVTdyia26A":"iRB3nISWz3r0","imYbBow78lWf":"nP68b3z5Q1N1","YN2fR1oWVKb5":"ttYy4W1gSGat","iaWBpAEyIKos":"EQcTmE9o7AFr","fJzo4LX65ZL2":"kESq259Dza9l","goDr870tQ1nZ":"tcJEtTsPPSYu","NwBIrxpfVTcg":"Eq0t68eJ5XzH","2EBAvROgeKkP":"rz5Q7wlCyyGA","OCoidYi7IDje":"z9VzlDeP7FYd","zvv1YR8v0aRw":"Kt7UocVjwJwc","L1orosouUQR0":"1dblB08WpSNE","uzhAYgkdLBgf":"2s6fJxMIQJP0","oCgNSr0sxTgK":"7VMCMhFJoaT3","7PhSDjs5qkBB":"skAEtUec8bwh","faV2WmdE7Jo7":"TtF82HVbMt2z","9xhAl7xM3qdF":"Y4EoYhUTJ5fE","Jhosk6ihmyHJ":"DBQBT862pYTt","0uLDTgVOZslz":"AujuhhI2oeie","2GsvtYsMG8M9":"b6YVFrQl2DyS","iiAETbhLdQCp":"3gApl3OkWySe","wyGq3oUq8HTv":"RjQCkLJpC3sj","ztBMBkgr2M9X":"FCGcebbsBO1B","hKjNbPTdz8WU":"Nha8SanCbazt","5rL3nz48KZCL":"U7rdNYsXyJ9j","nBEXK6cyz50c":"XQd3rSHD7fGZ","gMsJZbiQbPjr":"mLbxbau3PaPe","yhaW8NHEYB54":"EbGVEs0YJMin","aurhFY6mCwu6":"Szwro3TCjca9","sB9YeSFSZ3lq":"5pieKEkUp69j","VEdnBUxTqXiv":"Uvye93Zw0q2h","HgJ5PSn4HkEe":"i0Y8S7Gm9DVS","WoH0OoTnHrMt":"kbGMQ5niqP33","gFzti8yKMLLx":"FVzNavfjdG4S","K8HWvnW4nK5D":"aYPYzrC6ng13","nmAewzj2kc0m":"a7NG4cc5NHFx","kMYscBpazRdU":"uxawmTEipj46","pCfbXFjK5ohu":"8ANDMz1U9JPz","tfb9PE8GzAnM":"8LrmdzJwUTz0","qy30cevNFoJz":"JVsTMcdO0WOJ","NB8EZ4rH5wlO":"BL4onjnWFbCF","5inBtdUjdJcv":"k1WM1YVZ6A0G","iAdrH28TTKz6":"NC4Bd3jCzs01","pYqISohmJ5uV":"5D9w0lDCZ4oL","x9wWjFk0qCNB":"kCAPChCkVyDg","xWfMHjYzBfvQ":"vQyoMjrq4k8l","NIqHjXgvOSY8":"OZBlI7b9tMST","ItRgan7iLe2T":"zADzd9KEHNz9","F5rI6uFnsJQ1":"cMfzewDuZu0O","kpyI2NvrA8mC":"9tQbqVBT0kR2","nw9KesM90MsZ":"tpFv5SYEoSG5","nAvcy1GHtToK":"HSvD2ehVedKQ","NO4XpxigsnMj":"rszzBL8zLJQW","CNtkWdWUAz1n":"9tiJEFwNUend","cb4iYxESzCV9":"I4ioqFIxHgt8","70nW2PtdiWT1":"2BPO23bJFG2C","TJVgJdpOPZ0u":"ucB8UvhEU9hV","bN4z4CauZbYn":"x99ADiKLMtWT","3ChMT6gwg3hY":"TiIObCjPBefS","HIcqksa6kaw6":"udjTpgkMA10f","4UXLiqx71R1S":"fYpi3WmAHvzE","F2b39IfAuBbR":"WlzptNTQwVQM","Wqod8fuZAo1d":"PCkolfMdrJX5","Zta9QRCWr4FY":"VGwyNlWXR6BL","Kkl14WTiez91":"r1mwyr4WzJQk","e9kTMY9LJx9W":"AFKz6SZtCFXF","EprGd9DKt7nL":"wnyGJmS5VQ2W","wJ8LfOIYXRTr":"wIpo2dhhuJyK","HCrYJ6LjhxVR":"bgoNNeibS9WC","LMaaRbpMH7J6":"KrxXzt0Esi0m","sSeG00qhhs5b":"t0SmG6j5Yc8g","ErhfKzD9B4SX":"FK8t5jnpTqSe","0j8jOLNqmNia":"U9yOmVgvoa9K","zfnVfKHZZo7b":"00QtJOfgeHCa","0fN2vpUpfVMJ":"5TGyr217UuvL","C97bKNbMIEKS":"pfqqvFg1Sn8Z","YR4I34ihHl8G":"7oDH3TE6sgrz","jnTM67ARmXVG":"iUa44mYaMDFq","UpBXKHPc1tfX":"sBg3uFALWkvS","nx37ds7AaIWZ":"jfPLzkflooN3","dM9dIxgfH3ft":"fE5LNCApRQbo","1ONfIFbiIrhL":"eTjYHkPQDuWp","MWYrpAawJln3":"y8lUVR581MKv","kQ73V1bgbpri":"Tl2hNo0Bvwz4","xD4SJogxD0iN":"jhZIHeuGOoIw","WgmjP8z8I8q2":"bW8dWvmBpUuu","mILlDhNS9Ru4":"SojCde1BxDBb","TQBUD8A2ZU4e":"VvlWdPpvM3e9","GxYE8H5s72Ee":"ijRHlP01kHze","d9BaRHpQ9qTU":"Jq6Vw5mwvufr","0PPVEYJ6xeS9":"IUYREwNMmkgb","pFqDhP5MFh9L":"xnkW3vJzzpJr","VsLDzLNh9zsj":"IxuqStgWEM7w","CiLdevtiHpYA":"N8kMWU32YP2V","15TjqzATyAgG":"JC4wm0L9KMku","H1IyyJu6K1bQ":"0DmDBAz8FbN2","mdOCOZSYHjbO":"vLb02yHaS4GJ","vkQeLt5LoKrE":"tUgoC2LxT3VO","FoEokdN7jqwv":"0q5w7z4lHBmK","SMqSVtPbZMcV":"Vjf0XUswmYro","3R1w7R7jZrHB":"nKG2LkYcHwRe","tQ7dD23HzX2h":"UaqwbzXW3MAn","uSpHGbgHVQcp":"rz00ygHm6VCI","HCT60SeTXv3N":"AkYVGCziGHG8","8zZsb32W8pk4":"3nJFV87ua7Yb","uvy4K7iQruCz":"c47zBPKwxiD7","9wc4zZq7gjj2":"1QimaZOzuiC8","GMGgdalEKlxz":"IDmzdL1i0tDA","VRWJvnl3uZaS":"Or7buksaxF5N","HPacRV9yYLxH":"YprQ7yGxeImA","WpkvFa76k6ny":"5Ne97igcfGRI","25Ys67h86r6Y":"q6xd7BnKTRw2","9ao55kTKVo48":"giGUdJmpbDqG","gg1GA5QnnZ91":"rjC6GyxN3hDg","8qxa7Ith0qWU":"TwSaFXnAnSSx","HYwdTkEW3E9j":"UPYbJigptIOA","6FUGeIZTBF6C":"fwZdtIErEGS0","C9xMHjZsYKPH":"BGIVhcdGOevY","eeBCOVMb8C2o":"CaVjNtdnBk1F","Uf1iSfuXpBQ1":"NJE3rftG5yan","ePBNFsrKmV5C":"tiusFPsRmFBZ","SGqzOWOKv67C":"SYNifHkxBF8E","YEHpglLqisrU":"VJXs7PHudkpU","LL7BMCgJPU3C":"OhRvrAjALFIj","XXW8K8Bomueo":"qxYOsMDjKa52","702cu6Queaxw":"KlSDq68lILmL","t7UOHv0l7NIF":"kQ424saDD2aQ","K0D0hhLLLqZ2":"x2b17mcWeVIX","7Ul0eh9MaNOT":"vSha8LK6PtTm","QYTSMU3exlXe":"qUL6dyVlM5G4","jCy1Vhh51rx9":"VyampuSuTkMt","404KDgHdlgpJ":"jobyeT9gF8o6","hkQ6lPWjkGRD":"uSAlH3ZScNUx","AbbxhcrcVTsc":"1AHu0VfsRq4h","0VRMz24Xzv5F":"IFbifsK5Az7G","baj3u6AoN8Cn":"A4ymsEDW4Pnx","O7RRXraFLkeo":"qY9WkDsV2NOb","orY4I2g3r2O5":"s02ru3JO35CY","dretDcqVuYrw":"oAvmG3ZuJIp5","wkcoiqOxR2Na":"OCpwFiklCjQr","RbsIWS2OIcqQ":"425Q3rYifamJ","QIkh4ejxBzBf":"a2jqEWHy2eUm","TH856aIMpMFQ":"3vicLk2SAiQB","tAn1i6OQhbxY":"0LRO1jkQr4T3","zPt5yfl2tOiP":"ep3Nea1e8FPD","uvq3PCj0WnBO":"a6dM3TkZfXtk","KIwJcbJ5unCv":"H84eJ1Zfcble","iZKhb0nGPs97":"qknBsQOqFT6m","4zIObNt2hAxb":"ZHOGftqpM3Fj","NMrJ8JGhnDVZ":"bN19bpgCWoMD","RMby9eH8Q9fs":"9o2VYqEFy3y1","va65iHsffzeY":"LETB7VCspeCs","XZNqU7BN7gVQ":"CPxhA4ATfFnw","Ozxw8zvrRr1x":"JJB9TOvF2F1V","vhhQaCQXF7dz":"kWSUsZ40Kulv","KGub1M5ItRK6":"MGjqxWDB4aoX","lsHXvfHqBkZc":"1G72JoP0H6h3","rAC2HfaBjN79":"ReludiRBxPpc","LIrUw1f9XOMi":"QI0UEe8MNP5u","H0qiwDy1jTU9":"t5WqqWLKJtQx","TAcwLeUvtadM":"42nv8nmITKK8","cJ4WuuCz8xyK":"3tfsXBlBJT6E","7D9IETU195NX":"uAT3FBlVYl3Y","Vrc4rxxgBVE9":"EGg5UgezscFA","i7IwG8R2YeKm":"fcEv5RwjWhq8","hGZ6H0TAaYNF":"SRIg25nSNvIB","T9NzZQC8scWZ":"zWAz93v1OLAM","RbvgobrztOyC":"SYwWZThTo6h2","423HhhtebJXB":"DCF9RT2lSy91","IjPHUrM85L74":"Gsuz9OE4h0PY","9PeXBnJZixCz":"7kwdvWn0YTKh","XnsvWl86wwuA":"ycwxmjiV9r3J","zDlPTbGk9JXE":"tUdxGxvMagvQ","f4dkz9oCe0Tf":"MmRssbz0VAqE","CRDnAeVcFX0n":"vLYxgP8GBH7W","BBTBl95L36UT":"3fnZCTsF54kB","4kwwrXzwOWzB":"DMErqXd5xFsh","5KaIPuwkhED1":"ROZGjKbh3TSZ","4uyxPZviO7U7":"TyLVIWbW7qhY","dfT76Gb9IJuO":"7CNyDcLs5ADj","VwVDcyHupZvw":"kT75KSDqXbLj","2SigKTWPPxLT":"wDQTNjtTmt2B","SVM9pxU9Kci5":"fCA6MaRlXAj2","NYxjnv97jWdp":"ITcM2xYj0TSh","13y4TSCIwP0n":"Vq9SjZogfyUe","iczvA4az0X3Z":"XKwrUF7uvvGd","YtRoakStzBBI":"55hpO43XUsRX","aac7hQkFCkq9":"hElYzUaHA0gD","oGZ9b7ztNnuw":"Zqeuo9ze4Q5N","hsTLPkNTVkTa":"x9fWNBqiGHVW","hDi7dDIsjOfh":"pmjr1PlDu4dN","ftfYkbuMkNQI":"sHlE0uz4oRrw","5AwrcgNl5UpR":"dsIF2YyxERqK","Iw2VXb1Kkkgq":"BCHSFR1thkws","QgiHmD9adR4P":"EuB3JVBFeuhl","N82Ocif3sXB4":"tAoYrkGMSAaL","cK3PBey49fOs":"IRegp639YHSU","VEF298wlUCzK":"m4u0J7N4GPkg","eJ95TjJ1sAqJ":"WdlSVzHTt2pd","wn0A0gBIueHM":"sfKmW9kuvFBm","mpKSLU1wULoa":"Td6Ho1CadHdx","l8JQc95qI2ko":"hDNt92jO3frC","zMmtjIIXMLfQ":"0GYD2EYXMfq9","HywmCbIceFck":"W81MAnB5HaVq","rdNpVJGFao5O":"vt9NZNzwlBUk","PivsYApyKTsX":"oQU94LzdXNqC","BVCnmegDia8C":"F8D6mh2W1pq1","AMlsD96MhE9n":"eF2VMbSxUn8d","xXHZIT4T8BX0":"GBLZSqynmnIU","XYKBM8MMAuBl":"jXrZtqRA42KF","crSomOCS1Rtb":"32MyI8vbsvVE","cjDG09gY1p4p":"gVAdY7k36lUG","bt6duw3t0Gsp":"yUDZkNR7dwir","HOfQxs1poKy6":"CiS9kYNZr88t","RRDdbICyJTjk":"CK8kLbALs0EH","d6h1ogENwN5m":"whAiGctu6c00","7fVZb1mr06zo":"zegtHPGlNwNX","t3llT5CTkeud":"BgEb6PlVZCuD","jOBi5qQVvR3a":"tm7QohyXaZfF","9IFfRiCkaput":"edjB4Ifp7zAQ","GlYzdOY7bOLY":"L7t2GRVBBBim","alMlS2VUJ1xD":"c4a3a1jaHYCs","ORIHX3ZK1tZ0":"LfXrnjnJnD5Q","KeddoNMZowiI":"ffKSBkfOinPD","KIYNk3vg6c59":"TnsLwJLIXfxH","2yQeUyZG67gf":"iXzD9NhmvSQy","AoIXD6AQeii6":"r1hnMVsappQ9","aFtcVZfnHwmM":"6Ju3uh0FwCIg","l3u7I5Uygz5m":"nVEOdgtgTZmq","ous5OMMlUlwq":"ICNVYECzaqu4","v1pd8Kc6txBa":"DWlKSZ0O61HL","8xZGYerQnfmO":"buYznq8S0QGa","Gp6MrtaGcYTU":"fABlv17djp5p","OAXK6rcZ5hlA":"EshSgC4cgbRV","5vAgrFcGPsO1":"NlDtgBgsDynr","xcA8v5qBQXZX":"gnu5fMlkRSCp","NncpIjUmdozC":"RoGjz8oF8L4R","FXdSokT2LyY0":"51EF2XoQDfBz","ywIUfeZ1jI8A":"celUEjqK7H1p","RnFsScRxJZcw":"ZccrGuRMXCBx","Vh2z04b9qGGs":"ev0pWhVvM1UV","E6yTkbu7kgON":"NADoBApgIz8F","Sq3i7NQeFX4b":"5oepAk5mYcF7","HyD9ALxmJHIA":"iFrTtIajvOsy","XV1GqEuPodjW":"yWu1tFF11g8S","TamtIonFxEfg":"1G6XUxRvE9Oh","p5dJXdnhJMue":"2orqpJQvLYKF","Hgf22zJWThwV":"gzYMJvK6KukU","O6DhZfVbXm1v":"bceZxdxfZIHc","3Bw3NI4uKr56":"4WZ1B6mWQLEl","gGwY6zp0at8g":"pESAQQvPRAGM","1O2EfGmY6DWR":"7PxZtxSnNtxI","gOSXxvpISFev":"TE3rBBxkZ8uQ","YKiXs6RDutPv":"mdoVEj2Mv9kS","hbxZyVZns516":"NZuF9cWVyWzz","bo571q064BfF":"nRA6uqi5bYEY","VTxhjFqjclUi":"uNJ25rva8XSX","J2dOqL80lPOe":"4Mx0JVU9gPzy","zpFhssD2EuiN":"FlOhFWQbjY0u","BVvg3LI5MX9B":"S82kGxbDVeuS","jUWs1vvHxEej":"OJ52JjbQSAca","6HstEWAVvjyD":"TvonPo36tZDR","r1dcgDRfLtEX":"OMzQWAhBWf2v","XM2AWGnGddTb":"6Bx8pcr8lUJJ","yz88qInZer9B":"7QUSjDm92WLg","SWTDZ5E3PuhO":"eKCFvqCOHBnM","yyOeVkc3B5WA":"890kxsUr3Pah","3s0govtnG84Q":"i5rQuYCe4lfL","9fgC8QcJlDSf":"116e6KuUkDku","ywX0pPtp8YQy":"BWCc2RORXANj","qW3PMxWtxQAH":"ZhjDEYWBV9wc","94sOxGNfd2UJ":"NEHdoOGnQAGU","sYARb0WLpgWR":"Nxmcvj6XL7VV","9z3iyjMeby4E":"VG8jV6mcLZRl","MnF1J8g299iO":"HguAfy8ZoKC8","wLPDe2um8AJn":"MDWKP4DB5jFg","Y141bXSCBSAz":"e4IK7Woqbpzy","XvZDu5w0kD7t":"vo7q8ra6l64k","G5oE0ALsxW2R":"bnNUvOulg9jm","xS678e9eiEaC":"vwBlJS5MQxjI","oQyFNR6MmT7Q":"zo434g04sNk8","A0aaILhX8Dv3":"BHbE2xieoKAl","WJMtEqQ5IR59":"W4M9PHhk9mz9","HxxZ9cxn2imX":"Q8NPaEx2EGMO","AHLMPHvUT4FP":"Nxe1csbeRhaS","w28xhnLN1vgK":"9T0tXZtbYmJE","4b4b66rWUAvo":"5iwosdqofTWt","xfAVxTnsQ7zS":"cpqFsmyymmBE","8Sc8lXWU5act":"fGzkCx3yJc36","vLfmokJgKTeG":"kUFZg0Jvs3XQ","7YypRDpLzEqv":"OH7zvsIYDODD","oW0kBWx5QrSU":"vKdHSOynP7F9","cImwRp0dwOMs":"uQvTSZBQeQbu","SiVouSLpFP1P":"5LkrHWeO88t3","VHvJoN6LABge":"8LOojBT4hkDL","XUnAdXMf27Zc":"df2JcPL1PDzK","2VQXXip2G62g":"eGjMfuggFGcI","AOysCfph7iEe":"66X0mPIZ1BIO","oD7IeZUwoU4l":"5RmMapJwJwNt","5ZfxyPa7GsNZ":"f7SPSteVitmC","AlWQkY72cIJa":"URs4yfV6cAjU","7UC7IJumB4Us":"VU8k6Cqt2T0I","EG5xqoqePhb2":"CV49H05Uq4nM","SUpIsCHWQxsD":"8TJi5b20yT3a","Ui3ELKGCo5yk":"5inRujXE1YLt","cxscGSOdzzEU":"CsgeW557ma7m","BKz4jJYCOzIA":"2wSPPrXfwe5l","2zKUgzgHeQgs":"F4dpIAFPAKRE","VPU4QeXgOWjo":"rpIiB1z1QIR1","ljkwayQaJEfy":"DyLj9talMZLk","kTaoUOUfxdtT":"wjYdmzAsrykg","oziB64rMefwx":"D8pllvzbHjX3","D6yRjCrunuYC":"9aSgjoFhaa3d","P4p35DzmKF7v":"f4Q5kx2wFK2j","CQdRLlAUkjnk":"awz4SZz7nlwN","iz3jl4X4ohVr":"wSgliCvJAqaC","m2znLGJMWMuH":"HbC0bTLoZC93","gLM2zH9uB3Uw":"B6nmSNOTr97h","a7W9pDReaqN7":"yrpWH6yFNwUR","1bZBtzfPPPxd":"w9ulW3NziIWE","sazbasARsx18":"FkvGMeFV5oHi","jFe8Y3Am5aAE":"5n2WZaF40fsL","3QfuD8sgT6fb":"tVrC9Bt80b5X","px5ehAmm7cCI":"A2OYHRgYOkvY","F4SjY4TniCGb":"eK6bVypJh5jN","4cQrMG5x1oFV":"vh60D0GMJMlX","9xVrXIEvM31Q":"c9yEX5qicaok","1HmbX3wWIeMk":"qKgKKgkVvB0v","19dgpmoExydo":"I7C2SRDZnYVV","e7a2vB0cZLca":"9cM9q7NAxc5O","XUECXuDodG7M":"zPmvY3nFT5sk","7VuSP61RTcDx":"lQCJgzKnHI5m","HyiaFNILE1hm":"51OB5Wq1xF5H","A2PkKeE3r1Hi":"k55fIzvks1eg","bpWZnT0X8QcG":"5Hf999PkiIlX","8oSRIZJQRcWL":"1YAyjlRcHku0","8mylzrcUFDR4":"qkrJEUuGQT7M","YOPHWOhtZLER":"2qk3mOTHmTQ6","K8jFV6Fijvoq":"D9OSGnTFfI6M","XTwgoaxsMGeX":"8dyLIT3LM5Hi","LswAgiwZ3Nem":"Y6dqOcUry4Fz","DtnmvpeB3xBi":"WkuWXyPpPs3v","x0t9T59JdmcS":"WWDnzZCVkPfW","M6y0mzq6LiuU":"8gZYt2isVL3i","fNgqTMWNCBpe":"if40RogObzsZ","MuaRIZafduL8":"cnlGduewkqoN","Bu1tCW2gcSUY":"wHr1plhntCER","2KOjLJYXRFoY":"8PDwf4sHrMLi","r25TVaeBfyLF":"nD6AWD6h2NlD","Ti82erGCqolb":"lmtLK51kQue2","Rib1jkCRQCRa":"XQeZzYFH32bj","B7MI8pjDFQQ7":"Q91YMECSiLxT","ZOj1UAk55CZL":"vHEW127uiouT","kdtvRIukTBXm":"atcW4tXdmPtp","drfettN3THZ9":"IsfLfwoP6VDL","7may13a0kmK7":"I0DhNRjvaR8q","5R0LQBUVCCde":"6W2Z62CDaqki","Ge3nNjLyHBIr":"oKmph1PRoiZA","QRTBc1TiF3Ak":"1rTtV0ZsSVje","UrEk9P31LEQd":"PiXKa13bHWeb","nyoY03w68SSX":"gDBixnYWORYF","nA6koEyTut1L":"HDTzuZty17D0","Xr1VuFlcED0b":"cUqjUAEc7yyX","cfS9AxoPursh":"RgD6Y0NqylWk","sVhvtGanbea6":"iVdqCu620bJx","1QyzQkpg9eSv":"gXqRkROtPsBH","ZZ69rgBegJ36":"s9pzrhUBwOfE","IGGwDQEhM7NJ":"mMkS0QdR099b","NGCxZNt29voX":"lD9y76j69Fqo","g0RDkImefGWm":"6a4M4mdZSXDY","HMGfdd7UX7Qc":"xcbEbmAUncFs","lITe5jfcaVJ5":"pclVCkc0x9aD","qHo3RDfgkmOu":"AK4uusd4aJuP","tCwYabt7Zdnb":"1ZUp0wbjSSgG","oVpdbeAaaXz0":"qi4wGaHbUVLB","xwLmQnqxTXwx":"CWphs8eYIHpA","lUUFDz0pFdEP":"tajTHjHG4hZL","FkK4wJMVTuws":"K9RoRRS71Lhx","a6o7y1f0g2Rp":"WGX5uDiGnmsA","3XhWRy7nnDZD":"FV4PWM1sTCQW","So8VdnDCtsL0":"Aj52Ob3aMGXG","RdIwJp48brY0":"jzaL1g2dcz7Y","pAKr6gS5bbXl":"s7nhPMQOujhk","QIl04QsCvSym":"esl5IALpqjyj","S9AkYIec1ICo":"xOmqtrLPunun","zSPf62ouAtKr":"pKO9Uw1wfBNi","6Uv9okorRd1P":"cWJ4JKpKmIo1","dMUw5D98wqGY":"s49mJFJZWBuZ","R8yAvLRg2lnd":"hJN262mmFr2h","FRCTXbHGPPjW":"Xl9obhEkJP3D","nWXRNczCoJ7d":"sDCwS6MtsIOl","rRtzwqd6h8K4":"LWTenHNiIqc2","LofudRGnVVoD":"VxZrh4OXWRm2","6SpZin7xIY3M":"eSbZY7zfPE4H","FdxKRBvUj0kC":"QLSoY7f6Sf1K","rnQrcuysfrZq":"Nq7fDNEcolZc","eNLBH1ElG6cM":"plJVCIaLVGEC","WavmfcL9U40P":"ALH4bpkAdv6o","5fiowt69OlMd":"ON1VuzCoeRho","FUJGhYOCwduz":"FeUnnhdwSsQf","z7lHHMlRKiSX":"nKKmMSDRF3YQ","5cruFK0ywPoh":"pUhHRFIX0PtB","bzDwT9nZkiHQ":"50hYW4pDusUo","cKrtfxSqFyQR":"IcAl9QIg0W6T","LZ6YCKed1nbY":"Cy339MSGbYL1","w2Q0SIKNLy27":"htNTcqZqLPUK","CsbuZbe3jFr6":"UPQIKkSC43ub","JmnLS9WZNijI":"vRg5BYZzvx27","RjHQU1a0pZfe":"uM9xkAeTGFSt","NWtoEMgEJ2uE":"BHQWIORjvCaX","6xAHlQq7z3TX":"cwUq5s9Uz0Tf","UsBL22h1YYgC":"TFGXTK2MV7Uy","w0qt0UDMmpjY":"mXLfo2jav2CP","FMsaY26tDTzU":"bFbXOiXVoCC1","ad8JRPTw7xk9":"liqnhY7zSxzl","1oFjeIAopEch":"ZnY19xdUOhtu","zFqaac2eS1MR":"0Mq9A7ZfojJH","LXvs2jMjv976":"bd8SdyK4bSjW","Nuy9eN9L5lkQ":"FZ8TBtRWsnqY","kXVKzezWJVQs":"kswMHOikjILb","TnhGgAEGUHaF":"7mpc5qcGVG12","e003rcTIkgxj":"Va54EbuY7lcN","E6LRHay8Djap":"fUrSG3sGzcMn","1IAeAZhcB1fW":"38yTywKLF37F","mR8E74Ddsbeo":"e27dTwtTEiPe","K2seBmUnW8Me":"927VOb3caXmN","iB9nC83iV6Z0":"de6ntIufHDRI","PsOcNrnd3jME":"IsS2buO7wtQB","ulWjLR3sIfKI":"qtDKJRri6xgV","hI0LqlYVKMT0":"m8K0iyd5uW1C","2UUwYYvb0NW3":"V6GzMC66JSan","zP93ouoHxBLq":"EZ4rLc0IDUGN","Sc9YY8xBIgez":"jSRdl9qOiEVQ","gGrhX4rj54K2":"jswv59ugG9Mu","XE6uXGQUxGJg":"beVLo15lif6O","l3QVCD8DZKnF":"5pT7tK6fpC4S","2BWlJli1K3rQ":"Hl3l2J2zxsLl","iIXq0EECo0hu":"ZBguFNvTYobc","T1wNfTtM8dxm":"CeAbQH23rsvG","DdlpawTjzB0W":"LkXigBIhdvVe","aPnJn2DqzAGA":"txNDIiMeLijp","1iBNsj5wzsHJ":"Gq6EbXkKRtak","tMwMo6JyXLQw":"pgaTW9UwxHxt","54RzTRdbpI8v":"RT2kSVSCA4S2","94fT9AzK9XmC":"0C0MhzyaHkrw","Kw81NmEL7RIh":"fYnFW8ovgR18","xLjH4wLneqRE":"EVXnoLOgPacW","S8bo256PzJJ8":"UM8hT7vYHTid","AZMRA8HXo0Xq":"C2BFkl9mB4ey","EMincVcMApqI":"j1x06FQzCRxs","xRN01g85Kq1X":"ikcBiDfMYrj4","F7SN1hF2dU7R":"cqWaTdo2VCfv","zollNu2I9JqL":"GCRpOgbRJcBl","Nl6aFn4HJzqP":"7Vs3sFJfJSqQ","zPh71GgJVttn":"uNmd0h6QRzOS","AsEJ07m4oBtk":"lsE473aGcClR","AWdWzUOzKM4s":"hE4nbs1kwAhk","sXinCskswqpG":"UxQ7Y5gjK5TA","yn0Dstw2Mio8":"STstBqs1NDgt","WfJOiz8oNLnp":"BNXJE7sgVXWC","XEqWvNmkLcUd":"QtXS7CQSGzqk","dJZNSlMgKgJs":"I1pjLva7U6N6","z0kX1rPIkHUj":"TdnwaHKQR44N","KGYAINiJoE0N":"e7pE7kpeQa76","9Zaq3srdxuqg":"zCYiWFNsQdAR","dmiu3JvlCyDH":"YScxfzZanH74","fcsMcuotg5JJ":"XTjTZojH76Q3","2EYdFMsxUxQw":"SiXOZuJMeePu","gha402DSPm17":"pKyzD081XcDa","cw1NmYdTU1cN":"BbhdMBDxe0qb","NmiSiQOtN6HU":"QqFKgVNNviV2","gmHzZ4rrc4UP":"v5f4zNiEtx3p","c61DRanxxIPe":"rBiODnyxF5bc","ourbwRZgOqiy":"TIxK92mpRGUi","VQk7LaAiXJZz":"hi294BhPaBFl","qSqDQ3MUpzfc":"XglfIyh1Q4Ry","3W74sGegIOKI":"EvGqgbq7Sm3D","9Ejl6uxGPvOp":"hN6GKJAUtrgS","hpe9LYPsHvu6":"y9HX51wbLxeh","dENNV9hV7U3R":"LXBkTyTlNLJY","ZYfQO36YzY3O":"MDmN6ur9elHY","RmS8DEm5ObSN":"iY1gPgJyp8nx","DnrJpdOLQr4E":"c8J327Dv7xeB","KWd1fXnbk5z2":"qVaUDX0UbfGj","MqT7O0BmNjjy":"QT26Nb95DqiN","rJlXLJzSFNQl":"56jQZ1TvlHeM","sXulu1tfmh5e":"eIuOmrhB3iWs","6Xn9Zk2cmXRD":"JJxaT0AGNuoA","5j8kw2fj24yT":"soiKsMgbSoXS","0r5cssYkzymx":"dpOJVmhQzYi2","lT6M796Yb2gd":"zghwxM6JbFFi","W1qyHyXxfRy4":"bKffulLbHxej","lY89xwjBxrCV":"GcfZoovCvUYb","fHWWIANwBIfA":"oKgnmzY0GFuN","PC0ECQZsXRgA":"0zObbtL0kdNX","t5cx0vPgJVTg":"z0uRScYDurZ0","8NjDrZSAOu9i":"Jymw9jB8dMHy","BYMuwecams8X":"S6A7TO4YjQot","PMDOQ2GfGZbI":"pUibLhr73w71","rRpTMlDXnuLM":"pjvoL4Wv9rmQ","a07ZuOeb4IsT":"dgx8DpQDQ6nn","asWcfkHdn1mt":"djaQdsosOdBe","Hc2HBGILKnNe":"laCxe7RV0gNU","ZmLIRjOkExaT":"tjzgJmMFDrbT","R04eviwm8Od3":"OZOaP11Acnp8","xeqiQhZ8s4LT":"YIhawgYkDjd3","x1ye0YXNZogc":"7nbCIFerVhYy","Ancp4kh1anLQ":"m3jYNjmzRsuA","ZhvQBndt0weE":"m5DOpUQx0gRY","BhAHHryGgqO0":"jcCdIoC5I1g6","tT5SmwAITpyh":"2xOQiSyCdiSp","gZRegRlmmzKY":"tRs48XygTBhZ","PnUgX9g4nywk":"9YE8RkvxZxLm","F02qAVeAj2Nf":"U6odKyxNv0O1","dnnluAu3dM2x":"ic2Yc2Ysw1Q4","xscCyZsyVh2c":"h4aZqbodD8Yo","3MjldskHTBN1":"7JqH8Sdrmxox","pS00atislQ8a":"VW98eeMqR97z","dz9xapmJnuJq":"XCV8EgcWFppr","SFTwPTZmKuHv":"oq8nOHlK8zE7","YO80LuHDmP4Z":"qZardGwm1MZ9","W1gBnKmfDtwD":"rhfYMvVmQw1s","XncuDOkc1W79":"gIiXVvr2rFv1","KgDTJ13eAXFw":"G6ieaOJrEQPH","FkiM4lNOAZ0R":"32hU5jhjWq69","ow3KBCGrdSxk":"j2q4MYzwjcSJ","qb2GQtIqBMN9":"YUAX3GtUy4pz","WW7elKUlWBy9":"HdhFbOrGfAfR","bvsi7YAK7hT7":"ch7j5bjdLsnF","5HY4MPy4CFLt":"wnIplQrGgctC","HHkoEhT7dl6J":"ynzpixjYANzP","IOJTiQXWWN8Q":"05RS46S0WoOK","4qHIX23d8DJC":"nmHbW9xmtTnr","t0I00weIvupu":"OMKgiXuUmBpl","hyW2gedevLil":"GNZBnJgZ1Yab","Tjl9rzZRZRQJ":"CEyCLVl4E38w","8UuGfsGFgoAK":"joNmHclgaFh0","7xDVjSrEXvN9":"4sm2O3xvElMD","L7AvL5cFjySF":"t1Swi43pyxTJ","9O3d80nTamcE":"tFVEC2bczbrM","xakxhbSreUVE":"JolXU11B1tr7","RfzbMLm5X38r":"LLW0UEwBHPqm","ugQgUdKjG6BR":"MTDlgMQlrSOK","5Tbxmauyxkci":"zjA4V3e7HfXf","OZmcK0kCpspB":"eWhlKhja7DXq","dvvX9oU8buom":"cnRXBegYdnr0","CgqooDZ0HJdc":"XhVdpJHBAiY7","OEioYnP3yXXj":"q1mfkcX63IR0","8Szi3ODlxKMc":"Kxl8037LQR4J","WHVaSyrmC1eR":"chqsir9StZ2Q","9RmqXjlB3Jo3":"PdozPht9xxu8","IvTFeoGqC2Uj":"PYYqRkXNBXmj","eTJAYT6uDJeE":"cYQUen2A1ifM","tZvQvpjlZBDX":"FzPAsBZRadK5","i8WBKSUdHYEX":"mas0xsY8h7WY","4OF4RvhWP0xX":"bfdfrAajNWkM","58NWlcOHZzAl":"5SPt1sEqyQcA","TyrE8MfAjpyC":"wmIoRuU6d85l","sVjlRgbhFixL":"768VP3iNhvk6","c51DOUX4vULL":"uJd5F83K6PJd","TK1cjcuH1ZM8":"wwoGWvBvElH8","bNrYYPodCvft":"f2iU2pML68Fh","8C3fouh4q42N":"1y4DpsnHDAR8","LDI43vtfP0Xn":"Xqr6kssxaMpc","uvP546N8PKoJ":"RLJRJce2GETm","ASTFWFpzIol1":"yWBf32BWNSFZ","CqZ71pJg1oJ1":"cHLNQc1wTGFD","YA6HgCkCNWCC":"L8cHoZcitbBi","58uktD03j6pU":"FAXnAzSX6Wvc","Puj2yknFuBDF":"9GvgxLcHXOX9","v9LMkSvF7Lb9":"ewhbjuc9PoMH","En6bK0AWO1T5":"nAkpUKOvEvd8","E6J8FJl2olzG":"8Hqxpy4x542b","Zd1pzAilhIjS":"2wM6P1vn6Br6","Bmz0XPnDOsYN":"BlDv4XVFmnQC","fiev99kZGQjG":"6qnWVepsLjPs","QN2iqs4icuL1":"zDh0PsEv1JXn","4VW0F8RZVGtS":"WBvFNUZTFk2V","09YoAU6sXnb7":"z6sU3MQuCMzo","D1ZqbsyqBIST":"9s6t490jeL8G","YLY1cDYC0lA9":"whwFySvGKy81","n9jseBmAd0VN":"vHVNeWydpbsi","olQkjC3prtG5":"JQxcCFLaeWt6","UcN8CP5Oqe7z":"MbUlfHrM80IL","Oce581K6G08v":"ZVHLG4wAGG2u","ivI1bfIB7Mqw":"eo9t0ToFGlH8","7lZBooMF7xrR":"2nWBv9LFOLJj","KNmY2egVkMyy":"IQSAKaNr1WLF","43qQcGvKvZSA":"WWVXPYtg6h8p","EfP1yaSbsTvF":"NqkpDmYcG3Us","0smDhCmCB6NJ":"NtBMIzGUYtq6","mMaZEvdeVqzp":"PzZO0JO0H1g5","7wNkPV46xDc7":"0hxRMBPN7wL1","AcP7Q1MLG748":"nhAqZXHANtkR","Q5KY0UyJSBzd":"Vok6rTjbpslK","CbUvnCNzECPR":"GdFcAufPrJZl","kvyGI2qTI9to":"bQd3Iuq5eqN1","5pf3nQygoILp":"Q7CDpypenHHZ","ATa72yji5rQg":"n4vHdG0zpHum","17sJ5WK8kFME":"297F1FhivHG7","uZZ4zC26iLIF":"84VKZyR9AkKO","YGajIMIx9ORC":"JjyIexwOP1aa","HtcwezDPjoQP":"Gz6Cuoi3RzCO","PzfXaxq9JTEy":"TKEPGddLKI1U","737M1RK6zzu6":"6Rc7Mt4nyvH9","gzwcnAugE79E":"kyG7aOQDeMhF","mDvG5huFtcsw":"CKhYCEsRX5T4","XyjFBqbr5DHY":"vGYvBXGBAPrL","addioVDjwfyt":"lNtALVfGaBBz","Jtptsd2YoEge":"Zqvga8FlzK4p","IlVCYM9hgpj2":"yqtRILExIhn7","bIdTHMuz5TjN":"o3GSGDr7dFec","kqv3g0g67fmZ":"tvRqAzlGhY8V","0sSHPKMYoVBQ":"c7BD8VRNPAHB","UNYQQmY5p4eS":"qziN9xQmVKQ7","1xCukNWO0AYx":"ERm4DyiovxSB","v8I0ukDoxEhz":"pL6P56Zz1TuT","2Dz3AnoaHIjv":"PpSpgrxBBX4e","d2H5295rVLr7":"zTH4jBDGg3LB","mu6VFv5l18DK":"ubKGiE1tz9cy","YPDuGQgtIYq3":"9n15bMIlZnnQ","Vus4ZYSg5adf":"o7dFdJht1VvR","OXc5rcSfzJzs":"5oGrgs8IqOQT","f5NgTrL2i7nt":"3kduVA8hl9E0","v59lG4G7bMyn":"EJ70hOoS4irn","IjCCAsa6Fqr7":"84Rz8xtCtR4E","mIEtG22bRqOH":"vweVDJ8XJdUJ","Ocau9eFOYoyZ":"CCEFmBlQu5TK","H9XujDchSCnh":"kPtzobVcUtv8","QdmUESZ7CzKL":"sHd0tRlOi2lV","gH9ImBtu9n8u":"GSTZZxjShEOY","ZH9sVV0eaD20":"fm2KZ45b3qkF","E4voMpQWhPxR":"Fai2gI8jev5z","R8Tck9ZkkbjF":"E93xNF0aN3Wn","SZEBn3qO2h8q":"Stm9xcKIuNFP","eOsLx9Cg2Q5Y":"V1Dc5WHZo0R0","tN81AZAXswuB":"Y4f3WtNW6QTe","b41dqMfNDXUg":"SCJGnWOunRFu","XTCPxRcBeueM":"QvQWhoB0e0s7","mRl7uDf12ZFi":"GTuCCXDsQbZu","NCXvJvJO3cDF":"8nTcPVZSxVnq","vdhwycbnciZx":"VcB2ga8UKv3h","pT0zB5nks0Cm":"ZwQigg8mGFII","oJorRkrdxMyb":"lHjFLxVVvkty","9y7oAvIvDJhW":"5mLNZxywOS5i","VC5ORtfxeOH0":"fqR60pcxn6NV","X8zWKcLCCVHU":"6YuXcVpw9bTL","31h9WvZuTcL4":"631bK2A7thEK","ifl0TrP9JJPb":"eOk2Nf6T2otp","EDzLaQU2vj1U":"dPOPVJs98TRa","mnlQKJJbkp6i":"tpGIBj3TiJF4","CvvIBO5kdf3Q":"SubT43LEIage","bMKtxPMYe1sJ":"tbseJcWyb9Mi","L8TwZXtXcoTQ":"zpsEyhRVebGa","HOxsnmhwicLF":"ud6fHZ424EXz","AdhdmjCn9jn6":"QfJAnosrbPki","7JENk2oLa35h":"kdbZHqpDv4xu","MVIkNvpiojmF":"hf6mgLBmniH1","0VkB1Jw82tuG":"gi4ZMRYLZsV4","TlH9ZqongaWi":"cjRVt5t3NBOy","yFZUFRIOgzWV":"vE3hTmsVtBKe","6RJxId0kbLsj":"wGxWKBJ0jJLu","HKluP2YLT9Oe":"QDtxQEsYbKfA","7cf1m50sZe9d":"KyuSrLtoArn6","8kPcbOcabSkG":"jKl5AU3bRjBA","VMRvpm4xW08E":"P4FPRKrP8rya","cJvF4tn0Itvm":"pO9q74x2M8pV","v4K1xpgLNeVK":"oPBSxxWZoKPL","YZaYZmcnaCj6":"480PcCogvSF3","KFmUbt1C5CAd":"MkyVh6aTqNfg","M1CATy3TLOq4":"ZgszBHEK7xDw","18ewWlCatj6i":"5VI0oJr7fYfa","51QbAwmtzNbx":"GgNMZg4EPHSv","WbvJA7jIHUF9":"qgUIycHWNT00","fKzLZPVlFBqU":"FS0ouoo7kOps","Ns1Y3gIpaGro":"vC1a85HYAEz8","BQlU8X7jPAnn":"tUcgFCoBhlGJ","qx9xOqQV9S61":"QhDlghMM0ckF","xdHwcBEWoKbY":"K0uCYeOUDIv7","JxPxwpXwZxmr":"WMbuQSqY4z4e","1SkEcUDOMgS4":"Y2YcY4x4soe1","WOliNCVJA4UR":"o89Midjt4aNZ","N9hCJjIQWDKP":"RDA9FrVO9DAm","sv0qabCI9fiV":"HRYuVXj6txd8","0N4LRpUtjbD9":"mFh9liLTkbrM","qfRcnGuymAOv":"j1kZPRF8rQeK","12XUmKmHKyC8":"t6SyJuamgD7e","YxWCcfFNO98l":"4N2TzbzhxVTb","78woB63nI5So":"TqRfKedlOPr9","I5xtKj0daFzH":"ppGLllfwx8Mh","VrqfFxiL9hct":"j5m07SaVMqwa","IqEmF7RMjrmz":"3oDwT6hXpvOr","Qt3m1uZzVdaM":"hZrzPdBhtdEi","5HF8mprGn6FB":"zpgcP8v8MF10","G9tYgIEUaS94":"q8qJ6IpBNVF9","SwvcSFESfKYp":"qmTAuIBHhRUg","Ym9POkncNtng":"wdXhT4Nkpljz","EHh0kK2wWwjL":"PPBZ4gHcWH8K","aloK4OoFpA6L":"3O5b4sRBChEc","28GpMZF1xhGl":"RDbHrbsZGt4g","h74YtAoqqtZr":"n4e8F0pWNuA4","9WACufrdcjR5":"KLYSBa9ngPqC","9PlnyfQmyOzh":"VIKCrJqEnTRj","uH5EryZb0eB7":"yt8GVC9nlrZe","TiZM6Ze1BtsV":"YQocOj00d3yR","IZRM2ZC4RWgp":"8BLXXGpMlfAa","5NsywGfNDWSQ":"cOMlW9VT8htX","yOyIKEbzrndo":"ETcMo8R0Js4p","fkyI5AECPb7l":"OBqY5ZDrXaqN","CPHEfikrmmcu":"AWk5WQKG3kKr","jMhdSd6rRJkm":"qp2B7tqbOvR5","nOsAjWxJcFG0":"9eCYeqCTE5ts","XLVYpzKKzG5H":"jnQ8cuN0vgtn","DCLJxuXHvVgr":"st7bhDi2KShp","2IpOslljlJUs":"doxwPt5CzWyk","Sm5tAVHMgzTs":"kQwzCrhlzNBm","GVgnmSLUkF48":"jubeDrA8GSj1","ygfnml6tP6kK":"ONnJOud4rM9q","joB3Px6k49g5":"qk8RSSulVP3k","VtCGNBfeU5Ug":"kFegICsIpksP","lOlJ37OWwb2F":"29dCKanJQVkE","UYDG5FZdptUL":"wACo0CGs8bHN","MfHc4QLxCgcP":"1fD325oM9QUa","Hmwunvr9Y9h0":"aCim7mYRKG6p","5AGmMhcLxezb":"kk6Fm5a0DOZ1","ku59Di3P51ek":"cMfzbAOOzb0p","QVTrbchfQEDi":"joaVS2zBPP3C","HnkTzHTVAL2c":"nDaurMLSRZuV","UO2vnkEei48K":"mGQVWx13CCc7","q4QR8VjdiqbX":"EagK18nOzH9U","t3XqElEXaxEA":"qU53oVIoii0O","BqYxyL7istD1":"3qFbBfGQcsb3","PnzzvvXBwf6M":"jLpxCnYQatox","fPrhRuGbHSc9":"iPhH4zpwBTO2","9nXRMP0QtIDB":"SAdKUhy0qzZS","AAWi3mgYuhZ4":"8eK0ydXCf9CE","ZzYHTRMTa8Eq":"xuRXAJ4QlbUD","Bhlz4LnfBGUf":"pL3eWX6SYvah","y6VAMxUcWIaq":"gZaEoo3rEZXK","dgtL3xJtAr8v":"uQ5wyYiRUJTE","NP5c1u5ZBdFl":"xOkqQ0M3r1CB","OVb05tM21xZO":"q1NnsLiximkq","nKGKuH2MYOxh":"8YB9Z0rMkqo3","I6OleQPRnrQF":"74qBq5CsCQ0s","bRX8SLkqADzP":"Gei6jikQFqgv","tE5c81VLQQOr":"GNcoIzna01qc","RU7zLm6rjYoG":"K5lWu6t76uLw","qziIPsB21G5d":"9SWfIeCLUXXe","5JYjFZnetA4e":"p8QpfIGOcJef","5FCx79m6V75m":"O7qaqQjZdpwU","K2RhrSIi390A":"f63BymdHUFOw","BlqJ9yOsvv6C":"2a86g0ujAbBt","8634EDSE828F":"zWNCCQxkyWak","HGMQ0uNcLEXP":"FAVQRgpKI9S6","lEnPOAcwpFhn":"NvOAFGG7KY2C","fsGA58oH9ls3":"GqXiP96y3HIV","mrUiHa5ambBE":"TiTdFSF6TFfM","euv4qI3DqMjl":"JJbxRKVaIYbd","wdjQBDbfUq3i":"MNoNCDvFgdvd","exBIJ7PGnVAG":"pPpGKgJMLjKr","6v7UdPxOFNLw":"yFy8X0iqXhTc","moBI3NHDfugZ":"HB9G9jGEov7f","j46V6k9YFtdc":"tWUTkOaxsWcc","FOjbUEaqz5A0":"6briD7gpYhMd","p9t4x9vco1U3":"rwcuDnJe7eBY","0ylpgfuKiQ5T":"EdZNDoLwdTR0","OMwmO0lZ1F4n":"kF0DvakYR7OW","gIJVoTKMMhaS":"uHy4GPb4Vij0","iayiFXjgP7P7":"zXDTPp8MWXvz","jkGDtGEXtiDQ":"rqox2klnUvag","2awoCnmLZJsw":"zoTRFDFJcGeQ","caB1WOtzmWrT":"0iasHDHDA4XJ","LBC1zpgCDnlC":"I2R9tdhe6gVI","wbz81iCLe0Zq":"6gcXttXJSdl7","7ySxDGOzoO5c":"EQewlQBvIrKS","qtAKPd9euQGn":"9dyZhQQHsfXm","togWS28VxAu0":"R2wQJkyFGHky","r7ElTOBNnRBW":"iVhL3TPtSXqt","8l8z8aIrSnaV":"vgs03G1Ihv9v","dr9FXGLWt32e":"kcIXTyv92tzm","8dAbPREO8L4R":"nwoR3lUhKYor","J9beze0QRJzJ":"UxKTIFl7NyKt","NOrrLcaMHAK7":"VAYBujpoK6nx","OFGCa6BWd0MY":"rAGiTqklIjYm","VW9jRQ80dXYi":"JMOcWBG9AQQZ","vK9RZljM7dav":"k8pIPfMZO02i","a32w2Lp2vOOw":"wP0zUI1xPyVL","7XJL4DvL3lNS":"bFt47NPy1Fvs","6MI2YccFIHv5":"PnIuQIAJbVlT","6I9MosCHfNsa":"1LllDiEWIVGD","YOcpbioErIdc":"QrY7U1dz6zXj","w45CB2zcCtgO":"LdeDqlg6sLe6","a4IURTOk2hfe":"9wM8vB7b1lsc","afzo3uVwA1Te":"VM4fdhMhQlPp","6SwWxTuS8w2e":"dYpSgErIb9AL","HXM6qF839kv0":"WGL6DHdeRXUg","0pdMBUwcyIE9":"sTCgOvmYjpWy","DNAXAhEALTy4":"95sdwqFGC4Sp","Ji1V8VPkasvx":"1TguemGqWs8C","e4v0WlUWPs9S":"TouWyePCSXWq","s99ArsG15O0E":"uZMAilesQ5B2","vABP5MO4FaS0":"MQF9glSSzm7d","bcAx2z8CnJu5":"byoQigDJI2ym","kEF9uKddZHnY":"m1diMhHgzoa0","VazdhgW8ah8q":"moAcwxS94ijr","LGUKWKpp6Uyo":"TTvzowqDsf8W","rRXRK3X6n9mY":"rn3faNnrWV96","1n8U4BitOsCB":"w3AZEZ3sU36F","aAC9fTMRYHXq":"Oe3MSgesg4lX","aKb4TN0psj3F":"mwr92FUrSFEw","o5TYTOG9LR8r":"5ff8zGk54y26","PyfNtny7Aw2S":"5aWBiTz6js4l","34wZSMpTTfkY":"he1yArfYJEX8","iyuPUqhuFCRy":"ui8W5mHhNQ6v","lJLWpzQ7mdIu":"lqnzTj2Mmreq","U0FmpD0sD0WX":"QDoTMPuX93EX","jltvhCZE6Ru1":"Wy5QB95rZOtL","2rhNUC2D8uf9":"Ga96QeqeyHyL","fP9O5Va9yJvT":"Zzp4buN9j6WU","ClnO9lt4dDYp":"Cmp27Q1CCn8k","SZMspNm7xmoo":"F3PF4l38i8P5","AP5GzcLXF3Jc":"8sPPuPGx58Q4","0uGCeZ49rxTq":"H3j6oxIcPHd6","7OGaDBlMJ9GW":"czHYazMZ6z5F","Ltt5eVr8NttI":"Cot6ZG0emcQT","qmMHzWu9bCSk":"fPwQui1oxVy5","BMkUMPRoVgth":"aBMDiTde1slR","f521f3QJ5umv":"iTHUsYUBGfqO","zOAURtELtPmZ":"2mwsNFe9MnYq","U3HQ1koZriGH":"jRyY0xy4j339","zsQHIdIAmHlu":"yJFDPuD6UYQC","8s3r07OeweCY":"8sMpZDGolwD6","ILzvD6wAOkPh":"1V0SN8MGdGhS","nB4dF6S9s1Xs":"3rNzkMwBA56F","md7t7iidHYkK":"BE1ZAE3Nm7O4","0PAcMLVlyifr":"ds4TesDnAzLr","qca0BvFQLsgX":"J6Fzp9gUrE7H","MsRTAhjOmfoW":"oMpLjPnmKYUU","gwIgrMyQg8mX":"SKu5LMtLXAci","cDOw8potneGo":"KpuZtM8hR7Iz","3YK9gdLrhCty":"exjWkmFyv3Rh","DzWrdOrb6vAm":"Bd1n1IgKsqcY","m1DM7VjBAlrZ":"KLkJVRU542EO","FIMoGg2ib0jg":"2YRGXxSzOqPT","LyMDbORG0Dxx":"AttpsuoDiAf4","gF7ZvucNAeBR":"OkKlOPwfWvY8","Nf2igatwV9fn":"CEWTeMTia61D","HbCQTLmMkrs7":"ZSzofgQ2g5HL","G46wVwBbXBH1":"SpUt7KUrwp4E","wCYRiEckdeqI":"4VuqY267DmPg","dbWxY2VD9YSP":"Agw7M4sP920F","EO5eBCocCFGC":"F8oMTFWwMW5D","bCNM8Nn5Kct1":"yYCf1AGE4IOs","LPyAGlsw4ixl":"MSPOtGebm82c","j48jdxibwHe2":"gQa1GFaU4XC4","8sncfEPerbLW":"WOF58x7tLeSl","TkDZnNbzAcoF":"zGPUmQd8pOO0","U5BISDkMSPUK":"qD7CqkdT432n","qhVW79BKxI8c":"zjGI1niCY4p0","RIRuYwqbsxJ6":"CGHAWArQmioY","A75WrBejF7P5":"gn9Dea2RaZO1","QvCcmhZzSo0j":"grZKjqCA9Y8Z","MNzERE2JKMYp":"GuThEoZQ8SY6","DbUUZUZVf6uc":"FFGfM2uesbXL","SUDbvgSANh4L":"hwisdCcMadKL","XKv87GHxSPxa":"zfQoJV3JMrVp","ahZC86LJyAfT":"3XW4ckHp7Lcw","ls8pBk3Cgs2C":"GtB6XfxEHrIP","J7KSNRVrobIB":"Dv3lJUY2BBlG","iQCMdH7ih7W7":"CjOKM6wxIQrO","Rg1jLU2nvVYu":"XdiOa7JtirXH","uP6fIiNimw7x":"QOyqkm0h6tQT","K0imEYkvogqh":"oBLNXgpW6ASY","GDSFYClT1bTZ":"WM0YrYRyEyZi","8scyi3xIyepn":"5CDeM82Vfwd1","8XtJ2Olj9vpc":"Md8lE3ihawFx","fsSKKBO6WaYk":"gO0ZKo5zyNXm","fdKbrAoA7mVa":"Ii2DATmoav4e","9RCh4RuMbCSL":"8szOoWQG5A7R","YcMoKEsEmomO":"qio756vXwehf","ZxQGh5gF0qjK":"DIrm78c6gh2h","UGFR01GbG4Yi":"Px4CEUWdaIXO","Ufvi5DOhJ8HX":"fxVElGFxnl26","5S61VdKxgc43":"I0vca2NH0Drp","3aLvCqac2xG1":"ldkJcilMK3aO","6zeNyOhW37bp":"eUXdhda2qnlu","MfRSus7aQs8k":"lRWDjlytpWHP","i3dT6ukGmdL2":"6cED7iQNxhSn","ww3XNTsTHlHU":"viVCx8gZuRVz","TfRDBFL8MOtR":"6hCd11cQYDhA","gcNFSf0xFnG9":"14vkPTNqKsrU","AJ8AE3mVPiI9":"BNHWJ8fL0fn5","uykVsFFnqMoI":"eBwHDqZAjgpk","hZWPhImiZlUq":"zxYTnPZnjp9H","ZcGzVmoDyHO9":"Het92lk6czFI","3grNFn5IYMNI":"VVJik3BDp2OW","Jsq5zrhkqPT9":"3SZNgMInV4Jq","UQ8CXCZyrAJB":"y1CkdvykUhEb","nw4Hz1B3maHn":"T7TiR9DuZVPj","7SjvoihNd3xL":"ELB7D6TGjyWI","WxLbHz9kPFgZ":"Z5gXe3hMfzVH","xTS5WLhnyjTK":"WPIX68a4bDht","j1MHMYICxz40":"SJJVifOyTjMI","fUONULv6pBxg":"zOAK72gJnISY","Fx4eSNvmStmZ":"QcoCofZn8zjB","BPmtmPq9Ztdz":"pYyCXbLKLrWY","mITv1ZrwMGlv":"GnfLmxIKVdRJ","wyqtzN1jRpv9":"MCjkg1D9myYd","UPw4USOXO1CY":"fHzNmGM1KFIg","D62DxTQEuciq":"r2xjZUEyrpxy","TSrMtZa4nvHE":"G85IweJu6yes","c6e9ougUqlJG":"MZ4C6WeMzWIa","RD8XthXJ36hn":"FmNfDcKX1Cdn","6QEn57qX3Psx":"EhvTD7cF3Sml","hkueC0BmZghJ":"eysXTosAgK4K","MD5rK4NSfpYq":"3eIKLM9dWE1L","I2EPr9DcpAnH":"cAjG6M2oXRKJ","AW2tGFPDMVGJ":"TpGkBvwBlKpl","63ar8K2Jgu9i":"rSbhbYhdOEvc","MZYoAuS9cfva":"fdcEJk1yR4fn","pCKrKfTtYeqn":"DJjL65EGNfCq","OGtrC9ASBGXy":"wkhWFpDBLi4E","b8JpoHMil8Rg":"c2SUgfMEAepH","Tu7dCnqQt5z4":"DlqericoHLjB","bGcACu2AlD0t":"yqP9wUuakRtB","9um8EQU7PmiU":"iPZUrVstLICp","gM9biXweSafY":"gdZDsS7LRN0I","XLO8eAMLzpwx":"sWYBuIdLN7yT","0uZGUngERBaC":"HpIP8ll0d1wF","zq7dfkzJAwuZ":"6J3y9Wza9VWC","5vFn5sSVFBRY":"AjV04LZFgzPJ","0Zr7Sesv1bWn":"l6tPcdlqCFfR","wTpkCPgN1tvb":"QM9eVXODHiYs","CVDTCjFqTgRG":"Z0RnDrfLUsoP","xLm8M5OKp0gA":"MrTQuZvghV6w","Wkuh5ahpvTUt":"j4AoStmP5kzK","aypZo0EmJgwQ":"4BTkwqJTn87G","durFsNLn1EUE":"AEqigO3fWqzW","R8u1bxyWn1jb":"2pwhfC9bgwWm","khfj3FDCHT1C":"EZt1Y76qxy31","QPfd2gTo7Wed":"MtzhRNaKplDr","lkRexSCGqkAD":"LRjXMSByBKHK","XWSUcqURjJdX":"3lM1XdkLc0Ld","nLVShlDQSvl8":"SxiCKBZhpG6y","mMV2fUy1x2GO":"DcsNfntlwcfY","66lS8QUpcyD2":"dnZhjGxfiLI8","SPqpDrSc0jMJ":"8vwc6M9GQM2X","7Xt33MWVev4U":"BOf34pgCC0c5","WYrWcwVq4RpZ":"bOLEfT6tETdl","voIIqNYGO0Cr":"TsPSJI0RIMZb","RrfOLm4sSfCe":"hhEEPy1VC9CQ","uhIDbLNoRkR6":"SnUPFMmXWDpY","8eZYjSXcgZyY":"NoaejE4KDyJ7","0889S4asAtQQ":"Be2mkBiGpQCk","V80PcD9B080B":"VXrHMpjZwZwM","eEiKoqK11Cv6":"Tl2uVdgeGsf2","bSlM4C2nolkw":"W0xk8PbMOvQ7","pmhPCMd2SOer":"n3GJhHd786T5","4b0bdbGrpbYC":"r1Yri7GpJEUH","sZsyWSrU03NH":"EKFeV6xw9fvS","6N7qi8bfJ58p":"gsKH26yB9TZR","PxR2gdEVce2l":"rxBpHNMnpgpG","qlH78u3fIphC":"y6TJJiasAcBE","fZwoiVdkFuAW":"MXwcAbQW6UdV","pXFCwpapdX1A":"cN7JwRvawvdo","ef8Vbhs8co3u":"wg8WejftEjnA","0ZCMpVkLCAiS":"fnj0IRX2QSXN","0cp67jmklYLJ":"a5CMsIIFlycr","JwLKITZbsuAk":"fS22uWAHO9XT","oExb3JqAZAOK":"GXcmj353GZcj","YnMqsbgHvdFK":"SAJIHNiPWYqO","qgY2z1MyuTfW":"mT2O1gQwBDac","BATjf9zczuG4":"3STJph2NRsbb","1ZluEjrM7bNe":"fz66Cz5iOKHC","mXQCyfbCrDa4":"gRLWQFYLGZe4","yfeuGa6ApNJ8":"gwv20MQnNQPu","TAzL7SYnWyta":"yUC2kXCz4nLc","OW7KlHN61bpB":"zhXt40xeUTMu","oDLYgNdyM9mj":"tVBop62gwMbt","oh8mA0uxkTIG":"TtiPJKIVzOhQ","xyN4gjzeViEH":"UjFDui22ZBje","hHZXMgw5Mtx8":"wV5Wvip1v1aY","72Tc0JYBSw12":"z2A3gHKPCuzt","v3NR71J6Tqv8":"AMKvZ40lM6jm","HmwRjqVgh23K":"BSxhmNENt4w5","6ZL5c2kpopEI":"mJ5ksxp4YofH","30251HoExKel":"ot4LHIgnrqxd","dVBcmId5wNFG":"16QQYVzWeVHV","5XR6WGSV2Mjp":"ycikk5T3B8GV","iBvBsFk8VyJU":"gnY4IZt4aVf7","286uJ1f29BGq":"RAjSXKWdovlG","3pzUXH2rSkL2":"FcBcKwtxHNTr","cvm2LkC1EuF7":"fAeasixYc5nf","KEitssxMn9NJ":"5gYTyz8vsFuk","IXvpiGzIknMG":"NZlftI5Wj8FU","e0YbWsMqsQaS":"1UCNmQimGe4g","OYmKcrTxWZ1h":"hLQs6TR7o0wF","HBZg2rubRe6c":"AJyKPKb2nzOL","EIBWTBT1IA8p":"AwJ0irEzpnzV","xVgwGccitKGs":"ChUK9FRuDuJW","CDQLZO5LSsjE":"F4Fwr60wNvmS","NONBD1lwsl8j":"d5m4ITFLRmvl","HOIkXWaB6MLm":"vhZ7rPgjgET8","F52V4EDkLBIV":"uhjVrXFt9A3t","fMvLHTLo9ZxV":"e1h6XXVWelh3","NIgJHxkg54Dp":"2sLnfnwODNpC","yU8fiYWebFPz":"UvShyPsFRZtE","2P5txnZVbTPB":"A4drjkQZB1Xk","6HuDmu5yqy2I":"MUEcqYdhOWdn","EWQijtoYlgfr":"8Frj8nUlKmgS","8SHbNBLghHk1":"rU1IukuEzmFZ","mNp2r75RldJZ":"5W978dpOA8cd","0RYN4xM9t0U4":"D7xBtBP4ausD","R44jakUhnb0U":"bvsA904wkvSp","bxj3GAA75ZgC":"0fDGelgjhMan","592uiE48J6KH":"W5uxQcWDeLrx","y2SMKoiMAZTU":"m9AKhonQVxWJ","ERO3Ql6nPLhQ":"vrRp9v8ToAxG","DRQF9IxB0yYK":"XupWNGPmfjsd","aYcStaPL6ksm":"9Ovgh2BOYMFx","eAnPLbVNMt04":"7VupHr6B24eD","ULKdHytGlnkT":"EmWJHONkrzYE","AC0l67jw8f6h":"HvgaL0oEfuyc","sjxt5EPa9Vny":"Hfs2eHrp7q5O","2FuXcoWYWMWu":"Tu3Dnwcl1Yhl","pWk1pR9YTLy2":"vcs7xxwnFsvb","x7r7oSrr0oKq":"vC7Gh7xSt2Je","LZvHRwRThHBf":"bgAyuA3fgGef","FSco3JUTu8At":"BJHFckqBvcJd","CuW1fMX3zbYz":"kmfFUP2GMPbd","54k2M3su6rTG":"vmFM2SpdoMg8","q47Fq2KMF2il":"QZDH0elpO0Ra","CxfKTxi1SotA":"LG5WvDCDWWmw","Tuq5JHXDFmq9":"1j5o3QF8s2n3","xh1mMIPwiBbF":"eROJT9pIC91s","QieLOdJscCgd":"YOo4QY8g7xAR","HLWBdp87jpoL":"MefD2dhz8y77","ffXK4MfTQRJh":"3ORYujP169xe","nnmZ1BnBk5Qv":"JZVyV0ujYA4X","O4Xg9QfQBaQX":"87xJKL2f0LaD","nhBwjbJBTKH7":"wO7dMSs8qL1Y","K18Mbglmvf3b":"23knk5h62Ba9","0LC6dDjnsAdL":"47JDNI1wV2l9","B5nUGa1gGu20":"URXxDG2PVcaV","JtM7XJQROXz4":"6rXCLN9HUisk","jv8P0Kkn4A5v":"mhYnypxPssC1","YqRxYeUWWAN7":"ZoPTpifIm61A","sXUhRoJOVXY7":"eYCGRmvUJxvQ","RGqCy8MOiGjc":"I1Er7Hp7wI1k","zHtPYhXBtIgC":"3Q71DuegCb0A","prmI7GvUtL4c":"qXMIcSwchICB","1FIiGK4kFJQp":"0XOQZTtgioVD","5Hyys6pVOCim":"ImOSkkKLRPPq","ypCBtrcIDKf0":"KldP6ZIE329R","BHHgWu47o6Fi":"VtZgyV3lGNES","4NM3Dgqvogyl":"w82wy4Cbgwv1","CqpyOayHhajU":"fABYUG7sD1LK","8fJP2X443REH":"lV6KxpZ2EwT4","M5zE5V8y4WzN":"vPpihalVWAXN","NxICSTFKi0UN":"1RdegcibcJAs","YEMijfYYsiPl":"46Gkvc6TZRTd","8gsdKAIOgGE3":"rNOic73soXcw","YaqmXwKItrBH":"H2AAsDgdbVs7","RaBqtQhqpoYK":"fJ6ZiMIxuIEf","LuHOKx0S4Xng":"kcUK1fHoifIP","UqbRqhRZimaM":"EYuhRTYMkrZi","WW2afU0CNtSb":"ejj9peNwOZwl","BoUWAD6C9NXX":"pqW8cpvO5BcI","gy7GkEC9yiPI":"pEqFqGMk6lVe","iEYx9IlscJJp":"rS8ICPBKDAjj","KtTTa7DDf1ET":"sX32ANYPJQ3N","vshdXG8peDAc":"IR9pxsXOxhZ7","AcKZ0LMAwkhA":"yRUh6thKtMtM","McjxzMQHITaG":"wYiF5LBXiaKv","skNAtSoUBgt6":"tp4ofxvap5oK","l2DGHWjihav0":"EvE6HfNEo8Uu","9di5QcIQKDXT":"ecf6gUfoKVYo","Zn0nebrYsmFg":"vIuh7kkG0OJB","WdsPbBDfxfWN":"wLf0vm7beZED","MXRD50NTZwPY":"ISzV7LNW9bK5","KDYcjTOQ6wBV":"9WsiSp5jl2g1","0KPbG7qc0dIZ":"0Mb8WYdgyRLk","O2rZZ117nN19":"HePhWsrQx2Bx","gqfnwqDhsQwR":"iWc5di2s0o8v","s6lc5ckJomju":"S20LYDf6CoHG","TIMz3AHhn1V9":"QCjpmuuayMO4","8SLPUdJCe8tS":"tHmisN9AhbGd","C41FKZ7Ki1Cl":"MNGuO6rZGITn","ywIhng2ZpPsJ":"ZYKFcXX2Ad5c","KjurSJGiPA0q":"0p9DuWRMNube","J4ZvX0IrWgJ9":"AWwJolJPvTzQ","pkHdzYDFp65F":"fRQIXvFiLvJF","lQebq0tjFBGl":"rlGlGDps75xK","GLVCsUiSGu3k":"27DkKO4Zr6w3","hIqkYlZv5DLr":"x5mvDoUec320","sBoSY2vHpey5":"QKDxFYPeMmGZ","ttZ8XMysB18M":"TA7mYGKZvwWa","eRuRjboBFQ7p":"NRLKKqs9VLLD","6mfKwBBeVsdp":"FsEoiBQLeqrQ","4x489RzZLN2b":"TmmSJ8OrFtfG","mJtwLEZHs0Iv":"lASOTUtsN8xT","eb7Fk9PHBwKJ":"EH8O1vRudiGM","ufOhMQ3l1WEK":"M9bB8vWCzvDv","JomAIiNZtFc1":"b7jqzOZjADJr","u0N5ZdnAdqn5":"nDAF5kFJHUB3","QItXhStcnqBv":"rZtrpWkutPpq","LSELusVa25HJ":"GIAZY5fRaLIK","luQDtv5nwH8N":"kQewQnvcygoJ","GgayU8pWY5ir":"oNBmgOPpqPaD","aTCq3MPoSCfF":"83WHfzSPHMI9","KJQBWRwhjZtN":"s2eB9ySftxhs","vcX9oIeBXFvD":"8pyQHq9br2AW","ivYgkovfSUFY":"oFDsA3k14VpW","ecs01xxI28r5":"0i2MkMzGbgyV","thrKAbEFnSml":"acSuUWZA95CG","62FGsh9a3YGI":"ECRBMgFsdZ7Y","VmZ6ZuSD3eOb":"hT2PWLKyymvG","ahza9EZNLerB":"g5YMpeIJLymA","NQDqJ1gRYyPL":"45P4UFEtoGme","Ac2AvFFu9MBw":"pmhFowcsSXJZ","B073EEjS1ijZ":"2ayXEmDtSAsO","G2Tp8PNh67xM":"Udr6xrRhJZgt","dH1c8LSGdDah":"RSYzMFkLSTOh","IGUjSUfDM27b":"0p0uWh7pGFYx","a80w6eap6Kp6":"mlDJvKqIFSJj","FYQhQuPqnjjz":"lWXhRlbcAKi4","i3u2d43SH3fu":"gGA0dT83YJj9","W9RI2tIXNYyC":"81WuBJRhw0kT","QEkL9xHO7lEf":"PtGRyLfzsEDN","7p3266rYrYVO":"EKCQSIoi5mAO","WhXFUa71s0I0":"mfL41LPkFF2t","HerihuTeaZUI":"S1SHZmtDXEOR","gt0vjKxlK1QI":"UWXSg1Ndo7Dl","XlVlYPJGUHHl":"j20ECdL1m5sA","P941tqBT27FG":"8pUIyZPbhvv3","jcRGEEpUmuqz":"0nfcTvFrLy7p","tVZlhYLBlpEY":"mVaD8odJs5pZ","7UmF5x4wTBHC":"Z4cf7S7E8xjD","jxzOlpjjcdXS":"zeoFOTQnrKJ4","gNPGQlRf4pBh":"oxUUDXdhc9Xn","T9moCM5nZ3DS":"0IMy4rCeNHM1","HGmRhFSW5yn6":"CVdbYyyt9e09","SJNqgFSQLFrr":"HL8iNfeZOArt","jwBIU5KaEiQx":"g0e3SBezRviv","POw1Cdx0jvax":"AMJKaM1xRsxe","tFanUcbIZDVZ":"DOXiRIBASb0D","mtHVnvrgRkbh":"9OMSmMKcfWmU","waafHxIO2zuV":"6BlvwYZ3SRnp","HoJ9JLJew2IM":"zL06ycf1oqO4","05VyCsS7Woz5":"w9G8fJYHUHm5","OHhrvE7rS3Hs":"3cgj7aom8aZS","fXpQPovGthwJ":"mKS02JdZ9X8c","7EzYUTwHQRX7":"4zNeNQGhl2DE","haFKRDVku2bk":"xUA5zHlPwDLa","mie7qRP4S7Mk":"qMvSCkIZxKt6","ssTQrnr2G9m1":"bwatajnLb3gY","Ysa1zOWUInCf":"m9QQzTvquO9i","gSkXQZFD3cja":"x6JOvNW5O0L1","7949dFMfoA6p":"Y5sN05duAmnd","OelppObywG0Q":"Ebe1XS71loDq","MuOYiiSgdbb8":"EaVFREgSbttV","0p7xDSxSNIhP":"FBGNlj1BlxZi","5dK3fuMYr4ha":"AQYgXr9YMCtk","KpQzCynxJCD4":"ymjdsyRe0dca","HlfK20MD92CC":"gy6HH7DSTHzs","Iv6rO5JMQY4r":"sHn148w4hnNO","4YTfXUPoY1oK":"JCXTOIZYNRnA","8bkGahIcX4KU":"slkWQiG3b00R","QYJuWlqgpFLE":"9uleEYVai88F","gAcad7PIClQJ":"d1KfLIzqD5pm","kSYzy9emgpTn":"pbIiflCP5ZEA","GBI0I9OILdEo":"cNLbjOrdfNii","PPFj0s1QrDYY":"GXPEmadjEwxf","AC8KDDESai0f":"ExgGGc4QwDVK","mYYlhFh88yPd":"LJhe2wdWQJYM","Jeba55YOSdf7":"9f8ku6Fqb25a","6BXwD7ODImW4":"ch0M9xX0IRMl","mtWTf6MQAHez":"6cxxU1qjObPf","6hBFvwkmnJGD":"dnWzvbqYUwW3","EYOCa0BYTIlY":"FDH6V98L3q0t","hFBFv6ORInol":"4D7JTZSfPiwi","xxGlFko4MkrI":"Lu14pqQZrsLQ","Fn8A8YweN29n":"Xj1W7UL6kLxa","bHlTqhN8i3gY":"42Gu4J2gYCoJ","6TKbrh39Co0D":"lyfc3mlnaT6d","aZA0F1zxK8RN":"c4oKC0MmGbmP","d1jKfZc83JDO":"JCbKRG31MQbD","TtNkgKLxFRcq":"aCMw3FRlRcfJ","fOL3pzT9WXzc":"iJuIAV88KufW","YpWsUsoXiuxI":"ATWMiPWnyjBm","DvhaO7Bc3BUT":"r3fEddrXXcLW","CM2CqrDBdGVL":"M7FUiCFLaTwE","ieeh9vvWUcZ1":"NH38yk7ixfCI","yMyMryk58cLU":"g2TaJmDu6M8R","QBGa4d8dFMQK":"NSQV425tr0ef","SGawBRB4eKku":"JCgsMGF0YR51","6uAt70OrYDln":"UeudRoZOnZDj","nieAcEsLDxic":"lERA190pf9oH","Im1gp34elaR3":"kQdMAoi7wReb","4M5d1BjwkC75":"6KLwQYO14Q7V","hz1XmfObQ7N0":"vpgeUjJaO7QF","3mHd23RWywdy":"d1I1FUrIpINK","A6QxbnNXzJSf":"SU2w1KHKYfoN","PjXOjVTkQc23":"jNaRvwFdnt1t","i7LukQFveJ5k":"g98O4dl6sHfK","a2GXPuDL5jP0":"gP8yE9yDX07H","m4bmV3QePgKB":"UZpb8DCmy0ln","l25ijZws3WP0":"XM2G7y3HnRtT","lFuh8t510m6q":"iCq9aZ9yFvSN","kYfvMAnN6OcA":"SaiqXXxPQRIA","A9hTNgwRlnpm":"0gcStb45Xw5q","015fr56ZuIZm":"nCgT6VOeJpBz","t65XqXR4AsX1":"8GA3qtFy1ZNW","aWfeOyOZ6weo":"g9Q7AKvPrnRN","7pfmhXQx9l9c":"yiQBKFHMquNR","1MbqcfoT4Hum":"JgWD42EiD2RP","mX0YlxgZ02KK":"nmbThGPQ4kR0","u6tWpZOpyYtO":"VcCmKlNwJaIn","6dnNg99e4nB1":"NAvCUjizt3i8","a7zaYM9Ranea":"gldCafei5rX4","DqXktNp5NZzi":"lrEYDOAdK1mV","0xNCx0eBwurR":"sFUQxUe4GqaB","jxCmEFbvkodz":"L5zjFogTTPma","tnfU76Nm94lo":"MZaGeWncMtE2","mRbGeWVVdfxu":"PMM6NepwahGC","o58Y342b1K2G":"7O6uqzEO4IUp","DL6X9UmezctC":"xidadfAiPA0C","GpD1Zk46tNOQ":"1bjJVtCwwsOb","3f0izGpdNm6l":"PmYWYt5Whnxj","phsT7VMjMDrs":"qTBrwalMmatq","ToibLY2PS3do":"VqFhf9I5RZZ5","7pGug1Bd49aX":"L2Z3ZHtU2r5c","aBXyUB1nwFW8":"8UgiQTNvrP7P","x9cgwj8qqX8E":"Jsc8agaztlgW","MWBm3P39V0S6":"gkB5feQoZ6gf","vJNds2BZF6r6":"vKKuVtrwxymy","3oK815eWrzoa":"8PtLhO576D8s","DOT7ft9rzNCM":"mLsRCDJj06VI","jZPuvmu1X3xY":"4CMZ9x199U0Y","Mg2Iffa50MSR":"1DE14Pw390Ho","oxWRDjWFG0Pp":"ECU3sr4xqc4Q","CxoZYorPRjl8":"St4fOrfR3wfX","7SYv1ikFjFp0":"dxIxLMVLlc6k","XDOIhkDMmy3C":"S5CNdiLf1jXV","cSQzabJZOciw":"hWv2wIF0sK4M","AgYetFTDGsgC":"kAjgA5v2OkS9","gDkZvt43SIrE":"fIQeuFjol9F6","G99E2M4IkuoG":"Ki0o6sRJFWWY","iFIFiY3sHKzA":"GRFQtL0RFNmX","jrXVZ7hq0tRS":"PJHaZvG13Brz","LYziLqXS7zYg":"6Mjk3ul7XjHa","akqCQjF1gm3n":"8Itn90FmNYCp","9uA27GlAgj8f":"F6SAtdG6795O","7twQbYTqOdnW":"4aOYxp4o818X","C6LiHi7heEIg":"Mzt4mh6efFUZ","CkKNWZ17UEYH":"S37ZR7pqhr13","kNf8PlGNQ2S2":"KzOnO7PJ4UlJ","roYVjBvjz1qa":"ZflExXpiGwsj","wCLbJGiojg5k":"jR6yzaqaoz5G","T3MmBj8JPUm9":"GSnKbEe3jxGM","IsDqegUPt4t3":"KCG7GV3xLFmO","1ltaLV3ct2BJ":"cPuhQFZnQRXs","G6ChzCk46JBZ":"egFk9ASv0aqr","QWHEy4Cmcd8W":"EhyF0pJUlobR","hAQ8uSwnVEYo":"mduFQkP3TmEk","ltVCMahThkiP":"ms8svjLOhY5g","CJNKdBbizIuj":"SIHhiNrXK2UK","21Liesyofzl6":"eczf1vtRtZXM","B194S1NnfWpw":"DQy4XlUPZSdj","Yr6RfS5TmBAf":"nThUFPtcGMZ2","yg477hcddD27":"Ogc7W8pVAd6p","Q4IGrck7g4Zy":"Nt7edFG8zEgy","88IQ71Ky4mlB":"k8eXGYChMXag","aBZlEFrkyfhq":"9kIFmiUkPX1I","RedPdpjm0eH1":"VIjz0aU2tNvg","jYwZyPbF8AUd":"8lqnOvnflV8W","IAaJ1EhSOlvp":"5tVlQVny2EjU","rpEr8em7cRLm":"64tbe5w3svkL","PijJLpnBhVPr":"WEXt07oKybyw","sZBAiwxcZ0xg":"lLIqYUf3xCuM","KHk7Y3JODIiN":"59uiyQYxbdpu","mNsP95LbpIaH":"4bsX3q4TKiGc","09aJ6E3h4Chx":"Vm8bi8tMILuc","5BT5ZfzgBUzO":"286jzuWDy048","4dAc3HLVemM2":"KEtgk9Kwf2jR","XDWItwyzWw1Q":"uaCZuL7WhjUs","GBM9dk71rfSF":"qP1MFwYsoNhe","vTOhx0jkaO4e":"CFWyULiNxJyQ","J3KKve3IMK0Q":"LmWdcSscKMBC","YpZpoph4iBpw":"NOLJsNHY2GZ0","aRusz6gK62yG":"ktAlcpcO2IBI","VywKowyPBffV":"gkyNaVoUrEAs","mz7zbx8mVHID":"N1hLYC5FFHHj","mahYzFvWEaZm":"j5lYUafK1qQG","WozDTIh2H7m2":"rHGxNcI0MqIu","8k9u2HcthUMa":"8Jo8ZB5EC7Bl","Je85POzOkF02":"kKXMyKRTDmRC","vXzviJ3nPliY":"HWd0n5hAce8Z","STC5N1V8ycUZ":"y4p6E8tsIulA","SmAhg0QFW9QA":"o1I3Kpaw1MZl","N8AmYnyDV4R4":"MAXogAMcKr6v","jECXfpMNOTJu":"YbrHek7bbRcv","VRcE3OBR5gHK":"EhpGt6oISIhn","scduHKy5tiPC":"JEh3082GTwDF","tlRkHbW0ZnEy":"JOM0SIB7fXeV","6RQ5gKFHGJC5":"IUxuaXI6BBkb","aKLFkEc86YUm":"IPEqS7hQbwqx","7eWfMViU2gq5":"TBJivXlkSQw1","QcHnLDDOqG9B":"5XJlPC2DBuUu","ka3gvVprxxGH":"fnGKKoNkfMQR","6zDrjEIPchM7":"5TJLvG9blY3u","zwQ9kpvRemIe":"Gzyy4GEwq6j7","E0LhBRK2GMpX":"QkKEc99LhzGv","8eIGbK0cvovc":"jyLQChFbwXwB","DCla7CbwPIHq":"RkJvBoNkM2BN","5fwUp1bz6xWZ":"sLRfUFP7tS4E","k38NPEosBgfx":"pE67Q6eVPsOd","wlX1A5Xw3tMd":"rJiUyIHHGLbf","UZCXMGBeBhtb":"i3Ajp3LykDnO","JiAmXJF4ks9r":"cF7izeUMiMxV","4xYPlDTiHMcb":"tqh6uCwOT3md","G4qfGvLIxxkp":"T9lWCDsYFW9V","NybHSfiGObcN":"WiXtMkTv74Iu","Axb14G0Ra4uD":"DDPmvFiLzpov","IKCjrR8sY60n":"hi8ZqqoPt0Fh","2Fk4uTcwFrfF":"NEaUWvpAvpXN","JddocIHuMl62":"4pqusr8zOJOd","ECrKkX6QgCcb":"g9ls7wVChQNF","mleOMetgXqUx":"gXCUydgKjwqm","QIrluScyHzRJ":"8qJcYPimxc0Y","wDPhzq90mowj":"IK5bXOjSPrmW","boIptwxrygXw":"9yUTUZYOqTwI","PgXnDCSXqidL":"1Z9Y10FHAOhI","TfZ6ka3L1vfO":"5yGDLAn4Obeh","N0Nf1JGy9SxX":"5dXUyZyOeNOj","2ZC0WGcoLquj":"mMfXz1KjabK8","p0NklTkvQU7S":"D65domoxqURL","Fb6bukA86axN":"SojU6jLzhrRQ","GdvrQ8jmlu0K":"WujKMcmY1HYx","kwIZMx96tis5":"9vhHJJgksxPL","qsLzQSZyKSyZ":"bUOsEaika8PX","1Sah5rEvJrze":"DGuRBIfuupyt","dnX7WSrBYxBL":"Qpz91oPDdwSk","FYGiWyL9KECz":"eLXoiGR0hfvc","hAB5yOtLcq4J":"0fDAwLiCaYT5","rQosdBX6h4RG":"RRuCNuhofPOZ","uGU8aXQsDCBm":"r7XCBuUgDcpZ","HKMrW2oGTDVe":"rzQueNTVmR3Y","IY7CRiPDrfgL":"dZb8wY6ekxpy","dJsqQgbiipGV":"809MW5XLeU3G","ZpW0CGOAPCyE":"YBy1RhxdYkp0","PkeH6TjQ1LDW":"i3U8BmSlTg1E","yufPS3s2hx9A":"l0EGALKt5DFE","XaBNpXnfHF8x":"lQkUpW4BGcxT","Ba4MbUKpeA0t":"nGhKj2z54oa6","nNtOyr92yUDO":"qDJciVFQY21S","H9FU6GU6NRvu":"xyBzku3nlCvj","KC8tK5GY6sDu":"PaGuh7mNdiRd","10aA12bCT8D3":"jdXPmi71cGnv","9OsV45S74r1E":"iDCoZlptHZHO","4SjZrO3XHEgc":"7JhLrQ7Tqprd","cmTIW1y94ijG":"fuFpYvnZEjrt","4Okhj5VmQfwb":"qN5PoGJdGaaa","KKOd2nmG32vq":"1qgGqcoPk5DU","nCJwrvB2MkpN":"XViooNOElUzo","m5xGYV8xVCCJ":"lfWB94oVzVio","8N0sWQqidUO5":"d5RNmTejxlua","PjM1RstawHLJ":"1iXRn0HCX4yP","JbJBKtnXJUas":"FetWr6t799KN","BEoqBvYAnufS":"rSa8R4SD2ijv","21IymzRHOKCs":"B3EjpN6FbcSa","KQuiLZPKclRk":"e842rQbm1vUk","bNv4CsOW3gRM":"4hsjgNIURNTK","G3snar7gpyaG":"ux8eEL4IVfw2","oAO9xVUZpUGs":"wcE35BEkAWiJ","AHTsCRUYlSBJ":"BTtrHvRzcVFh","6ztoPfY5elpo":"gWPQWx6sq4HA","oGyJL2vUnL9J":"2j8cC1V05ie7","wIxlI0clZdki":"9nuawFJ7XCGx","AxZ1tvOJBG3K":"or1T2iizMCmd","oRZzlARDBryP":"MXFWI10ZjfVV","hiI4ZveWckaQ":"dd23kNOdQbZB","BCq7FetSB3fF":"PfZxRKxq7Yj6","ZEME54VEWAbU":"tN8sWcOrAbp6","rVsGBWTmdMTQ":"i0PTtwkyA9ls","v61MrrRBTuru":"4x5EJALbHEv9","7De9nCqWcxOH":"baKGwH3QD9As","IFZ4PtdoSVgo":"vNB87W1AhPDB","hZ4eLNEV11Te":"MOr8SAc2APXz","MsTZdSSfXtY0":"eqiIoDUEexXb","4XUARdbBWh4p":"Srvgfk4RV7D8","uXz7VX7C5lt3":"AtMvrYbrhOOQ","pfuHmWVDrcL4":"kyPUsPtiGLaw","YLG5mUYW455L":"kw7WuLQPyFGx","tM2tuyehuKG2":"yrmlLicVi5x9","4NYzSN4TWhV1":"YRPZLZbqWCRF","KoqsmnZ9ZDAG":"BawB684pmUlW","B67mVbj89XcB":"njCavXwPBqUT","t8SzdNJHD76k":"bxUFY8apun1e","oMd1ryHHOICa":"48LEAaVS3TGC","XfDT6BfMhYYH":"vAvG5f1GpcHi","TqEqlXvJ69nL":"CgjhEnsts2se","i0d07PEn0UHq":"95npkuOkz2jJ","biElYgbDOIjE":"WvequrXbANcV","9jG6xVMNztFl":"dtul8m2D1tSG","geTNUewUB9mD":"YpbnRDNZyTwD","kcJcH799Let8":"fTYFWlJzP9CG","CqDBLse2qJtK":"tJ5UHt55n6YI","uLUxWwqna3cu":"mL71L3NU9A19","tMEopE3gcfb1":"UDzlzZZrq6b7","xKbxnvXJx6Yl":"4HEYFZhoqnjb","JRXWMtHPTv4q":"S3KmcRCjOyId","eFxCPeKbsxuH":"HuI39OVSoebL","LNYo00NfYqB4":"a93T9zjo7hte","6meg5a8rYEMk":"DAoAwVKokR1N","B7o8L1JIp4f4":"dNGi6D0jM0Hq","UZEiTW2165oI":"MyCaNDC48TLR","OuS47O4ahzVn":"IRnJmlubPEdT","xvBoPxA6LK2M":"s4BcMvxEdnbp","y3LeGX0JYr89":"ZXVtSHl9kVWU","Aeh16aNfHDtc":"cKbp5bkjJwxr","ZW66exisN8RQ":"rUCjRXtXlZOg","8DwWqM7OdLJ5":"v10MG4ZPCSrg","Eyu9Z4w1whSM":"6TkDQ29acvEo","Gh0sGd9mXVST":"NxPBp87LfrPk","coktoT0uh0k0":"4LdjmpynGonZ","lzfqW3LLE9v2":"aLfG2wSSAEJX","vHmKmzz7vz14":"gOOUcXwZAziT","gLEpF8E1BIu0":"9qwrpLp1dDOH","gwof8a0bgdA6":"3gn2ObmwWRin","wjq0bDLm7F1S":"5kaltO7L9rCu","Nktqx63oeXgE":"RtCBw200VCzW","HnUwut6eWKoo":"l1do0F8Z3xvx","gbhEDUtm8x99":"wfH2f4KntQLg","CT1VuOJbvwHA":"9WzjWDRjCOwT","pASwuO6P9LDv":"eTONoDnzv0FU","GzkCfoR1yrzQ":"eYf9Yd11emv3","P13e0tbIV0u1":"WjvCAiHIZ1kq","orsbGtA4eRvY":"6pb75yapn23j","Of2PzNbipNCA":"kUomjmO0Nuod","MvqcLFRztOtK":"T6Cf0VtzXPBu","z5kO3VSDpM0a":"yLwEoGDLe9MV","aqB85kjFkXqA":"55MWcmBLKhaf","8QMeTA5TDc4f":"z1Xz5RI20pjd","kCYzvgFQ8oYB":"9J9C4nEApILG","LEfX72vOGMrk":"MZMqlTF4jFhb","NYTGhIZid8oL":"tSEJpQ1j2lEH","GME4sC2hCN03":"JAhhGnqo2duX","m1vt356ffcqP":"WwpDdKXTMEVd","alHJwJU8xcNv":"ADqUPA9OrnsW","sYTn3t42P3XK":"POCV80lyGvn9","MQheBI035Ns6":"J9J4kH0m3WUR","ndVsLqFuoMRp":"cANww5iTis2p","nYilbyZgnybt":"jwxEVwWY9CSW","FbQKEaEUwmCF":"GYwRxT5eAMpI","IYQQlgUrC1R3":"jgfedkZQYg6S","XJPNnOOwTMQx":"jjwv6OtDLFfN","d55LC3CUg8XD":"HWNnS5ZYhFYW","wRVfsXF1E8OY":"pS2cQSS5zr8F","dTCIj3RdJyAn":"OKe7tW9bwVYl","yrEJ95wNAPYP":"ozUMKNIUQG46","vXvxzaXaZUJ9":"Ry6qe7NOUpVc","SM5uVhFpO5ii":"28nPwho39sXT","YVZMAXhD7Tfk":"GE0xMYbnNofs","Z3NQcFhAA8Pw":"RvZOX56mSzXK","pAglT0XsoZWt":"q628tgslzmQX","mGyM5JzI0ay6":"7hAtnJsqYvNo","ePk54jrbxly3":"b4smSDRVhdS5","rOOqsTHEkoR2":"bmVjdA7N2p7O","yDPOYvcIYmtz":"ns1pwKhQvOzq","v4An9W8JRcOI":"SANBO4WZCyUj","noEBYtDk2ysI":"ECmn21t9v8uf","Io6PdmeFvvri":"ehFzHQE8PG6w","o62pUB55a97Q":"5TvXRQ9hVeZP","gmFXxCO5sijl":"RoESWuErK3X5","VThFeWd894ve":"ag1UaLKAgqIz","5xdEXSgKkori":"UaEo9ziBRtHj","vzBELuFBJgPj":"xyE7UgZelGPj","0ewitfP78GkB":"l7M9pWqxr231","GFmsbRa1094l":"6RT7scuwjDIE","bs0nkDIYjnHI":"LKTCGByI8I8B","I8Af6WSykEFo":"WPHTbHTGKHV4","Ckx7oSw3zBys":"YveSM6A4lNSQ","4AW89xzjpYVQ":"E35Vk3q3uq2j","aF10LtLddPSO":"rZGEDCyi2YKB","Z6SKwqml7Zjp":"6Z5wVmndeAyj","69yRanYhM7zE":"n96djTJKDlrd","jOKgvUl978eS":"RVAJvSZaMzBx","1DlLbv8Yx2fQ":"q8ZkruohP1p3","74GsFw11ypS3":"G1jsA3Zs6eA6","GSH3q5M3lPIy":"NfxsnQVylPne","FFSSN45fwrlu":"QnWabHArzkYT","hrdt8UfSwLSv":"wLeXlbZDmEGX","ydfTz96IHTTN":"DrnfzyOHyct4","UaIbOf8Dni1u":"w4W7cxC4RbJX","kWP01Jjrd4X4":"azXFdbiRx9gs","JGVbKuBVOiYG":"SfY59dEh6L9n","4gZemDinxvUZ":"Oe6FPLMWVwm5","X6ulpx9KbHRS":"PxKdUSEy4XY6","NwmBA0RwDjxu":"f0TlpvQUv4DR","HJlSFScjpNr5":"jZelsNRIwmAk","vvdmK7ROqubA":"omVTgHOMbO1b","XH0wxOEzIHlJ":"aaAMviWWGdGU","EkM3XfjawwwA":"Q1n0RugobK2V","K0rTy2Yktjpt":"Nw9DwpQYFgEA","101t2Nuto3L6":"3iDvMbANUIAC","PvtdAl9MxwT8":"QfVy4BFj5eoi","CwAtvWsklu4L":"gYbPu71kQVkw","jgBWrKkeslAL":"68HXOZqYdq6j","Rp8oY7XzBLTO":"Ma3uO1m2nXJE","DUz7so8GorXk":"cbasOaVxsLHb","K6iHEcS84wCX":"gVczW5tn1ixJ","MYuB1Hzrvl20":"gVq5NIvwN4sS","OyTBuV9zCN2f":"e9WtO444L5Xq","qWAXLWWq4RSD":"14uTyqGxuLuV","AkstgFI6RwQq":"ZC2373ilZXdW","qfsHKdC3Wahr":"8na5qzrT7x86","2aDYwDVWFgzL":"aPEpCWg1GMcl","PRem5c25f4R1":"ZJN5WQ1RrUsa","5s1qzS4AmDzD":"1PrptKUABpdE","U8VGa4XPsBAO":"E9nfNd8D7rDj","axWL2g4Gs1yF":"Uki4GFJK1mBa","6ooDW2eIh4FC":"FUliDuvw0nhW","HlTxN03ZwZhG":"zm1zDw5BFkON","GVTxwvregbk3":"HIxqxAL0HaYZ","6Q7gNvMGpXWb":"BcRk1lsIYUWU","PnVA7KSbFwLB":"E0JNxAljc1dU","11gLA5fjgQY1":"5Cm0Nusl8iBU","xD1ySn4cPmNM":"uAmBMatRNSsJ","NiNBci4pB8QJ":"uXnyKZp0HasT","3UGGYpSSlv15":"gN8JZllQ9HcN","8hfh6lFL97B7":"e7bHhlzmp4Lb","vfL6tu4xEhq0":"uoMCCJzXR8kg","fVTGzfVx469O":"C2LZHORgppXk","hsyzG8W0nLX4":"ywUHyKuBFoHo","tFsIcVEY9k08":"QDSR0jnxkZX4","27QyGhAkUnSD":"7aFRXg8bh9Zt","52GnxCQ0vsLC":"d8Hwa2myaZjZ","0HD95I84HhYj":"jJXxeCJGXA3x","mKb1sbEZrurC":"hCGHdEq0VTVJ","K0woXy8wafco":"s5XNGxJTSKVl","jf4jhwW8rvjc":"ZHEJ3mEQKHI6","j3F6yKCQupA6":"5fqnv59MjSEz","zggfR1laZz2n":"kKgOcimVZ8xd","70UyxaukYL2D":"XWuWGYGos8BQ","rm3s1BPXrEjh":"DvgN1210tXbX","IvZTp4FV0g4R":"LNcdmoNXQnuz","0wIw9QFKm8RR":"ZDW2qFnJTYWa","Y4ZKF1R3IwQ1":"c3j0wxMu72kD","mAgNXCsSsez5":"xcchv1X60PlZ","hyN8SJ0IYuC4":"3cahnmDlgPHN","OqoXu5lT20KC":"8RiugAVQevdl","naqrdgDfpXjd":"8MHsZm4UhVIX","ibqDXSNC31b8":"sGCgV1mm3gNt","tlaUUcuzlXCN":"XiBP7Sh8rkRO","MMOJPhEIgvA7":"HLxKN4ORx4cS","bGXn4MgTQcux":"Ikre8MRQHggY","cu3qgjRZwhyA":"v8pCfqFHXS9s","rk0f8GSrhSBH":"bTxkuwxygzvd","i6QW2sBPDesY":"jhGNj8iOGUmz","zc4dLyYr5KV0":"edUFjGGlUHWs","VXLlMP7qvdhS":"VHLgH8vSzY7v","QhwTgZ6dXnAl":"rFfbHo6qrSbw","u17reBJLqrJh":"GQlhbS42hFf9","wNINqUDEgLdi":"VaHSczFRNeMa","MfWKO88EANGT":"zuUhMOVliLhC","2yX647iEXkjj":"JumZ5shbdKKx","JMlK0yRReYk8":"57u2aP1Wyojc","KNXvW6aUmhot":"4o6yDEXlfS94","0upSxOC03TcW":"oTaer8elYhIR","NPwVqVGbMtL3":"MZeg4R953BVs","imZVAPxOQeF5":"REQxU7wgqP3m","EhzYI53Iu2at":"N8Ltil4kHYZL","CMbjU7OsemZ7":"6l87izeM4xWi","R3EQ4EBCnXM6":"8s6kNuzeK2NW","g7tUW6MszPdD":"KEGltoqfRd5e","yJP82rrIXcA5":"DM4zYGqiGcqZ","GSlxP3SDLiFN":"jvaO8NThhki4","R9EvIjBNEtsF":"hNbImrV166Ad","8uREjKF26AKE":"JGr7m7BIUIp0","P0iyauYLCwKM":"WIetHSllQspr","mXxp0cYJM1bu":"ZF2xq7sCLfEO","jMkuM01ZaoLI":"jyAADqWZXHJt","UR001yzFRtet":"kC2hsePrWGSI","AjdApZqVwdGQ":"7WBiS8RMgyJ6","C4j2r5o5FV5N":"K4SxEEeB1UVl","mZgNXJyBLK0w":"ZpFDWCrROyXt","EOT7gsXPmhna":"2hOwhoXSK3Cd","smKXPw8C8a9h":"JwYrhlN6Z1qd","HjlaEf89BthP":"OJ6dnrmb3sS7","54k85N8ehsoA":"URCqR7kP1PJn","EWVfz5mv8ocZ":"rsb1K4szNYCm","ZRM3CaOQQ5tT":"o1wk3YeR0kB9","UzSq6dzHwk1m":"sJoeUJYRYDsy","pKHZ6rccXO1j":"ssTfAiJKKZij","lt5GPT9B6TsT":"tlzl3F3aUY0Z","9uXxJl9TNs3N":"yEOlPAF6fEGO","wLt4ZPQ4tieX":"MKl69gCiNBUP","zkglWXGxUVsZ":"KL63pISt4TFu","92JQ0WAjNbBL":"ixpH5nbYNyy6","F2LZell1Bw9x":"NhhhWNw8OhAT","bK3UutA8y5Jb":"sFtz3mAAoggl","g882gWYYB5IH":"FelcdLE60meh","evDVNf7066eX":"Oqm0jVJBQUfF","mZV1TKErdVqT":"qAH8FkliJAVH","ZOmYHOPKyatW":"rdYLPMTM0hin","pwEHgqnV5gm4":"kJLKeftFpWce","SPFGO0h1WVuw":"pCb9FMsVPEeo","ixpKA5w2dS19":"D7RBAlIn1ksB","CXQLYjIcNz4A":"jp106OqNdeYv","kewGbkMaVz3O":"W9bUjcL6xsCk","aarDVt2MHuYm":"6MZQOvXSt3p5","LNp2znDIn3mr":"N47GWPtiGeuf","4Wkgv8iisJN0":"c2OmMLd6EaS2","FCcOFHLYqiat":"olSBaM3fL9iW","OSEUSKVtilxp":"hHQiVCuwFaJJ","cZHkktlFcCgz":"n7oaWjUQ0pmV","sbjrV5ALSHXM":"ar4YmTnWFkmq","PMny264LYn9W":"FKrztMWPQItM","CTEFauPodlfC":"MWSHHps6K9T5","x0SV563LLdW7":"U9p56ItwiWMO","zewdshxK7bdD":"ReiEFnqBNd39","BOw6Haheh4ac":"wwhImij6ZjSR","ZVJWGBcQ0S3f":"5K8Q9TsKTqcA","Sc1i2cQXxBKp":"FjdpjncJijDP","gkPxCfqbHvpa":"2IotZ7UFALlh","kzFROaQIaGLR":"wCV4nr7nJbuS","uv8RAImg4IpI":"SsvH8XLBhXdZ","OBrt1i8IgDxp":"CGiqNUU7TB3P","ebCBosknjyLu":"9soDzp7yjxlZ","qj0xxHU2gOON":"I2100To1d2m6","EbWZVzDzK71a":"7LN78UrrwNBp","rZuPYFDxOEqf":"oWwXHieHc9dT","C3TJNVAMXV5f":"McEp1HHz5vLS","midpWpBSLhjG":"5kI8tnzvRnHW","7gwqJoN8Qw7t":"L4fzJ55kFOcS","aDF4mGNRe7SA":"QSFe1pQBdJVk","TaYdbEv4MKMB":"VM6cUzaUD7MP","0wrIxRrStChz":"vHOMFq7PDwEA","J6ddWN40Drg0":"LImrKjFBHiCK","YFYS4uhYDgRQ":"a3odxRSbOF1S","AsfpY2MWWd4g":"RdEsgLLBbwmb","qKRCgsAM9nEc":"Cgg8oedvjNtj","7TNYyLI5esqc":"s7XrXG3TaWTg","KEeFkJh57fp4":"W8faAfcENHMP","BJLVR20Eug4Q":"60W23mHeG1Dj","OwMQZGyoNJWN":"JlDl58I5pXem","DkEGRE5aaApg":"q0nCzUxyhQuk","d7R9ENRpT3c1":"3BIBC7iPNQp3","QlIBfcMVNbzh":"P2KbzEO8hPl3","gvJ0hNA342rJ":"NaQZYRk4cA2I","vnGe3ZqZW1go":"6QBDsjNpBtSf","24OcEfLcBXEX":"fbXeTVp1mqtb","FKSQ5VyzqnFb":"jMrtR8gL1Fya","RIoHBK8y7gV7":"nR1U0sj8dBAO","eOGBr4jlc8Rf":"05VhhDP5wXtw","J9sOJsf6i016":"D77JeM4WUCVK","7rxTrgqlCx2Y":"9NVOFkqyVZwz","j0BTdvNukdZ9":"2WvK3gbRLxl0","0umapJgNBJAA":"q8bXmYskIzDK","ZyJA6EY0HI57":"ZUlA9wAnzmUs","A5IfttxjOEQ5":"lMPgSu0D3BLd","bWFvADjudUvY":"Nf5j6BrDQIFt","orKs7HFfEdnV":"HMZSx6geECHi","bc907pC3JNtf":"rMs1rAUzn4fO","00YrHigI3uE8":"xdd14ZC8YuIX","ozmHavmfUvww":"wgyRCUHw2a6c","linWnKJBwePE":"4eCj82hR5G9s","NyYlGNsipJlC":"k2OGmvaCSxPN","k1IkqewpPYVM":"HrC9j4Nrq2tp","YnmTgLaOFzZf":"fWuCQR3DPrio","ZCwYR03Jn5lE":"gmWN3enTh8OH","kTXvlz5oia0R":"KlFWby985YWF","Xl0ruoAdKVtk":"ecxRNFlbJRKD","FgB6ShbLTBHV":"80muzRW3jBHp","jW5fAGdiarbb":"JQ8bsoSqxlik","PVm8EiBXl8Th":"VZbumBdb5WVH","FABFzosE7fBr":"U9cyqSbBb3C1","smdK8YHSLryL":"rDAra4KSKwkK","gm95Xw77DTQV":"UuNykWAPgt5f","Pf9dR5VJnukj":"S1TKFiklrhEI","JRg3CO5affpP":"ppBlBi6pjRtL","2CnOXqOi211j":"AH1mPv8dFiVU","B71bhtVmwk0M":"RcGM0leSXhRW","xXzEHzck4XRY":"JhVk0QI7tJ6U","zz2e1F4LrTtG":"43e17G8toJyz","YVEsGMMQZwPK":"99x6mEyb2cNi","3vFNFxWnJ0Ra":"UfrzKr7SWPpc","jloezCiFgILF":"iNZb0TNgYF89","wF9Hgex4iU1y":"pbLzwOwmGFPi","dAjzWlHkZFhT":"KY5VWRKn2lXO","XkGZUMWBvpGK":"4mLGH3zipsoU","DhyioUGhgyk0":"CyILvwWmRC9d","xyiqhnZbz0PX":"abiLsxxEA5wJ","rlbDtbWZ3jdg":"yiOBl5pCeKPi","KA8jSoMdiFPR":"w6HzY90iFCjX","2MddcMo4qwPT":"OakJPqgT37TK","HX0U3d70f5Nk":"rwhjsbNICtDc","220zyUYVwSL9":"0nFA4AM2xwHu","ldVY9E3cfqCG":"gc8TEPk7tTxX","6j4DMFTdreDN":"T5XDex29RKVs","fVuvk0wtGwtU":"4v0tk3WRSTMY","Oj88YyBcYprN":"JELWkuhdz1tB","2BLn9znrcKr4":"NqJFgI6VvWPR","uLBPenL3Qu9H":"EAj5Hc1yEZbF","ctAAMGLhr6bW":"3a5eLFwzRZ94","QShgQEwkcqxe":"CTTIw3e8I8LZ","FMc1SAQ8n87G":"reFTUsQ9lZOr","8R9gBcvsB2cd":"O1Qtnuy0CEPI","QoqjRk0dMeCT":"Nap7GsINEfHQ","i8FL3XXFkrgR":"4t4Ggvcjy3AK","tYIonQ9pfDl0":"fDUtUV7oF64T","ltI0hcgjl8PY":"2LzBsjWdy0FV","8CmsNrW7y9by":"N8sOJDEw9RVP","Lty7kALh35et":"Vc4bbhsnXpEc","ZJiJYjWZYgXk":"4msxz4bvJNiJ","GhoZb691PtRL":"dydjQYNgFYWk","qtjek1ezlNww":"1DPB2tTBEtCj","uxjj9lwwh7pt":"NXm2VBdo4Oo9","PS4CdClHQQXc":"PaA6SH9bdGKo","wK449WN7oNZc":"Dj16NFh1ViLy","juAibrpHQsH0":"EWe3yKkgvXHF","rhxvualdrV25":"PFBDmOITees8","0dlahybbC8y1":"2sNcxcW944xw","7VGNyZ83KoHi":"lLlN5kDiGS3l","HUaPHMbrZUjF":"wPdDQ05Tjd0i","qvxbKZTHZHEM":"x97Px8IsLGPD","6cvrtuh8En5M":"rikiooJ4p9Lw","eEdhunTJK8Fj":"M2xE0kqnnmB9","werGXqhqMgXC":"8pXEmre22CsV","IPjNhWY3S5EI":"as0tO3avgFah","qjZMyLocV6Lp":"HJnmNoUjL3mZ","ttHxwLGIhhhN":"G3rcG8xWMaAs","D8ScLQvnOmvm":"N3f6tFn4lJ0H","EmYOneVognR4":"mDid8DUs1mWW","JLjnCc7nONQD":"DLZjHEFiBVc8","R0kJgR6Gsyy9":"e3CeD7zTjUOD","reWbI7juv61T":"PGQCcCbHK56E","r1HySXTQASYh":"QUYZSzsSXJJP","64GM7MbZuLWq":"DTFVIp7nIZRI","FK5UbU9QXbKb":"EqdXObS2bYhH","6WguCyOtcY3J":"xjM1lPCTBJKU","rWoX90ocmZbU":"xm933i9CW3ND","ICTorZGiqGKb":"1dXC2EJdUMZf","oWwqSuvFGHIf":"c6I87Etdg01i","gEyvAugHhzlA":"6DF2FY7y4s2y","b1Xdl5ZzBWYM":"G5m1mOpAjQmd","RqFY3v679BLi":"oXEuRBoB8gGk","OsBx5RiKF2DI":"2GyaMhGORWST","zmsAXsKxaiIU":"APWe0x7bXzce","InsE6Qz2UyT2":"gmaNuVEvxxhp","v2tMVmUsia4z":"WnJixG0PtQdY","6C4UBnDlVlrJ":"cePkUJUNHwiy","MMDYJHWn58wF":"TbBWoXzF8X4Z","PiQGK4z5zhY5":"vfwS4xHMfyjT","LlCeeQ2fx0E8":"iSWTiyLV7NuE","rt69RaMfW18J":"8aBaOZgqAyFk","Y7kZRphw4E6M":"P5eJHwQt5dyg","UpAri9nFFpjs":"H27NTrrjshMI","M4E8m3prxQBa":"1Zzbek1jz7gv","UGXq7CibGerr":"cM4sHHyDevSK","NUCfuHgnFxPh":"YRnSTJUciMHG","Z3wUG6RhgEze":"HOwuRCeEg2L2","157lvPWTe3pd":"uMHL7CNDXxCa","SBnE1ftHSISZ":"AAZTUFXRkzyO","P1ExLWFpg42W":"mMXvzPGB24Vs","xhHYRmjOIl6Q":"srHlmZaRGUTS","OODNxPZj1lPz":"ogYpNhoWrD9A","GGosaDUkINoc":"T5kbBEYIk12Y","oxx7OHHdRYb2":"as0sHao0x09u","joK5BsKKk2t5":"wAwu3HzXyAkb","tJeCNHNGHpbj":"l6NmWjufLV4h","BrnEuO6UlR3I":"ZgimsFqM0vhn","pkxoOuCnIGtb":"kOBnm0ui80g2","xVpnhzQtxWGZ":"jSEYiZ03zfj4","5azayZc12XG6":"3DucKKIxUXn7","Mw5wl7FCrDkG":"oBESYJQsRFNC","GIYiNxZlgon3":"mZFzaVLF8N5W","rCAdK8FsZPbi":"RUhQUEoVd8qp","nu04lyvpW7e7":"ofoLH9rCnUZA","90MSuoRMhIO9":"oLyinBna1LQx","Guy4NIteJXt3":"Tn6er9MjDg50","kjLIXpJoGZOe":"BekydEvVT2MU","F5c8hzaV32JI":"DQmFahsaqv9t","Rfxespov5pT1":"AW2R4LtYMskg","fPlaSky214yx":"Zh5qTEazFBez","0DmtW59ExJ5A":"JEp4azYFxLI6","8Eeoj4DdXDZI":"b7XTvbaMSAOm","ECMDVKCI7hqT":"Ohh1SbyRZqYt","FCvdykkmTROS":"J1aXSwOqaTv6","yytZY05Ia1A3":"sLFZwGb5pbNC","2PzLohcxLcYo":"67PafenpOdKS","tQjVMqCJyPla":"GubAoR0MgbR4","vx6czk5aOyLF":"T74RQ7J9MGpx","KweQZXpCduct":"ubkIAOaN83IA","Ba23GY23G0hw":"h54GjjBZWvf2","sH3K5sLqSPOH":"RisC7QUkmPiH","AisVJnCJqbKC":"XpCymMsDb1pb","mEjnfwLPoUBW":"dVOqKCxnF4ep","nmEHIPWBWfDp":"fN3pnx4YcxA8","mY3UiHHJ9IKV":"x3Hy9lutnu9B","Lso7rOllGMMX":"x5sRD0B655Zg","S1avauFh9yox":"tv5AgtLyyO8Z","b2nw328ZwmAM":"NYN7BEv3I6OQ","0jHPyyOvZyXh":"lmCh5W7Xopih","1qixaaZrn8jZ":"ekrpvy05uQGo","dJqsmKJQnNYi":"V9uEepEIoNfJ","LiBIDckDhLoD":"OK4A6qw2QLLV","PEnZa6PoKGFX":"9cVY1y4YZhLB","ZYuqLCWOw7Zm":"opmiWZpHuaRo","zVmKIG2aYaNy":"k4c6I85YUwFS","SW3BiSAD7pR0":"59g8bTqkExAZ","c5olpbB5YPC7":"HQAvhrIXzQ1R","2FsRKcWlXqwn":"z3OprKEcr5Xx","6UHnjHXhjGkG":"5IpJGlLQn3ZV","4I2IiVRGe4Oj":"XbIwRt2QaXly","L3NN30zYmVyV":"3V5DP8tAs9TQ","CVdgmZHykm3J":"6qEWGWGTO2Vg","nOkvl1yAMK4m":"DNUQkyK0nOeG","AeFt72hu40Zm":"Ao4iLiVTMhEm","V1Gv6JsrMltj":"18Q2rNTqTZcF","uMWAcCIH2UBE":"mHVVazlyl4Ob","vc8WWvVZ2clo":"G6jnTMQEu0rb","v5g1ZA3kLdBp":"6awfMOSkT8xc","uNlK5v6ZAcAQ":"IUaioKtGuuT5","wRDAt4XtCL3A":"4ICitvo8oEtK","vzQDJKT2cFw3":"rENMzo4yJQPe","j0aGzP9lOV6m":"vp7mPWClrA7m","IyJRO8V5QfR0":"qJj8oy4VCWf3","CDJEeQodO3MN":"zHNKuZA0V3kY","M93KT9F9f8F4":"NvpAnZkbSBph","iRAev8A9PhCN":"YmMkwaCv7Nf4","h6CNuT7Q08OC":"LkMMDvCeKPT4","LO9yjERSYXFD":"90Fmm9DWEB2l","qZ0Tp69vnUSu":"NX4pHUqeNKrD","pEJ9q0KufitP":"IpOJJQK8wpby","pExq496razDi":"mScWK5iB2hyK","hPX2vYU2ZCEw":"bU06Qn1Jsgna","rA4sq1R4lC6c":"ATHoMNMQY1cK","3TYYH1NmiEhW":"uxG48pbYYuNx","OdC3I2QavTxf":"y3jXEBt53tCB","M5mMqW66X4LT":"IMqBjSuSMEnZ","O15wwegeFxvP":"Tv85uuKuK8M3","x3ZMYhET0qWD":"Th16lQtBfEmX","FHUDL6dTe43t":"iGkESLrzG7a8","atetzew8Mnmr":"Vs55TXZeOn25","JrHcRJjYWP9O":"9IGBMIVZDbpa","JrG68aOp07TM":"tJui2UKG4AtU","tQ6kS0q7vc8h":"ICuzzQNhFyL6","Ac8kiiIH0X84":"tEhgPk9l8WTY","ADnvkSn1hyVb":"RQYCZ8n5jJxj","cF8GmCVGJl1G":"5R1juZcOkR1F","3EyCIhKdLYTp":"0cWRwd1277A1","vd39K2LfxqZE":"0GqLkNlJvdcu","Ri1bnkuR7KXG":"CsbrMukrvJJi","M5wS2syeKwaM":"fBZRQqHMbeHR","k3VqkWEva6ym":"e5AGofYunnZ6","IjSQVp7fhuMs":"B0XywMhEtdHj","IC13tQ8869Ln":"jNjkiT8PYevV","sNMLYr0m5Vb7":"hxeDIs92HGrx","LQyR9vPUdij4":"2TlhwHq3gQI5","om8JFo5H1mRr":"OGUGiJuBE8uQ","EU8KEPRSfEdD":"WhjfapIqWKlI","Rx9H478Byage":"rsXYLKWoZQhJ","ZnnhEALW0gru":"eAXJBRRS3lKj","xCeblie254cZ":"Aho6D6Rs7Ybh","S8wAA5blZAC3":"XhH3Sc427Cwm","OBnrsWyWXHdl":"rjuPNVbMHxMs","4N6wyF7q7KGP":"Bo9tbavYLK5B","FMNEA1PfNmhJ":"CSedMVjSQMe0","TsMxJG2q3gv9":"QjNNI5KaHijR","g3XmUfPjMH39":"gWmWgjkG3DFU","03YUkSqTEDYX":"IhfXbv5B1jmA","5ccO6DNjOdLL":"YZcQ3kJziq62","CJcl9AKiCuam":"6RjEoYbbO3Ij","mx9Df94pFh2d":"bDIpzvEIjrMo","Roue6whHXo2f":"bk0dTDfvZ3rT","J773qh5BYlfz":"ij0gWtu7ZzbN","qYyidM0EAhKc":"qfYLSco3d2wG","JIG3n9vBXPSh":"ENI789c8oOuI","gOpC2lgIWEzU":"fi3qT0r98FUK","tls9WRff0V8a":"K9E2KbbAnBLa","aHe52gNtayky":"gyVit3Nl6qxd","8ejiBwIRDkT1":"FePFVZt6V1vf","6mVySoc6npeh":"09tcTpEYmLJl","B1aFrcX8xoX9":"LnP6DukPCIJO","j26HOKcRSa0M":"OCBUkY3uSRo4","jybaghYCezHS":"k3qPSs9Yo6Wd","cTN7NUiG3KZ7":"oaZXKndmLUpL","LkML2Twwz70w":"mAHhTUPDtuNe","qWUdehy2OQWp":"aZBI1t8bAmta","gxfeqgCYPnhx":"rSo9QCeENO3P","UIa7sedfSi6v":"FPnQ141DUh34","AwckZP2EuJK9":"mZ10oDUP7DZx","lKZjAk15mcxh":"Mzsxb16cUcnu","gssxO91srKw8":"Vsdy6OpvFduj","tempNN39tuOh":"5A4FzMJv2ccp","4ugjIjN7kZXO":"IRPkjUIVRmco","kWPMhv7MyDtm":"pp5RUpKsFXwG","qtSVcA7zSo1H":"31RpTspslsRm","gMuPx2QfKmOF":"UrZvqFnymMfi","BM0M22XO4pfm":"LjFqH48xcNp1","uxb2t9GglySD":"YiAwYzuABGSa","2fqZaXp6CXbD":"GSrSwsAwpBdt","QRx7Hh96jYrY":"YmmHQLnEFl6U","L5ZjTVzi3wEO":"ySMHJODhET7H","Lyjx5MKgWPao":"Izs3WJOdhEyo","zBK4i4d6ZbhU":"WFu0bBvOwcRi","T4ORsfjnwU1P":"w5bT70VNCD4s","l3VIEhJEjemX":"0MYTvBHUXfyJ","BDjKHlgLHDrZ":"NSd2yuKzMyAw","mSG3SVBKfFM2":"wtMDFhSDNkHN","RVlqWfK26O2C":"WlfOvVHjfInV","zKRfM9JKBEkE":"NkBr5tgPVl5p","7aTuemhReL0U":"jCNyWSHNQ7oM","a5AvA6vtMPgF":"muu1fHXprCfx","zQ6iee0lEcVA":"46XUeqekl8P1","dR4RcHcIrbXD":"jBEV4m5x0a1p","LkyKxmj7V0gP":"p3DhS0JJJi5P","g11NmO1UCJNp":"mYO2u9A4giUo","EoEkwYDaIRYP":"MaSH7qFDuxZw","cxof3wmB7Q25":"t63MPNtt9YJ0","Ee18NNu9U5Wr":"RG267ALP1ijM","hAgWdWM62ZOt":"GLYqOzA6cJFa","G59zDnEx2nZ5":"LWqbJGtn2H6V","yVmlNPmZ42Np":"r3RMOa5RY0sq","kiF8vAXRBJmt":"i4ytDhKZaVu3","s2UtEGJF40Jg":"RIyYuMXiAM6Z","oi8z3hCQxiPD":"iqRoFFkYYLxR","DrzloGqmnPu6":"3fwGnZn0u8HM","gtPCy8l6h3GD":"14K8DQPPGkIC","IKkKI6raoIad":"UpDD0SGQBUNX","LSe4cW7t83kA":"Pj2wx1xun33J","q3cHiFeVoOCa":"OBOoziD4MxZe","WdgoHUAl9MJo":"rar3YV3lwVAE","5C9Nes5GCuhy":"p9jRyGEv5vWZ","HFIXSWNy5LKl":"KkAigpzvCsvt","3L0epJX5rjJa":"SMkHH5ystzod","QGz3pFnGB5Pt":"3YtKy3xxfgsf","sRmoiCnMumTr":"YoxqzEF4I7ap","w7DtOSEBFzPM":"LJ9XH6GJ0QhS","kiLC1oQKweT1":"Vox10nSLbtZl","0yW7GGEgjxaR":"CYGQuRihTImF","tLZqi5lDQFhX":"MVbFb4Mxz1Fq","Ev5IKxg7QMV0":"X1WF2Z5pE8kV","lZm8P8CO26Rc":"nfxekb09gWi6","XaP7MxMwfIk0":"yXpbqWSEp8vx","uTUn7KrI1gPC":"rgE29wJuS67H","4m6QYMrRlmEZ":"0oqCnclBihil","XqlYDYIarhUC":"JejNLiqQKWpV","IGR3d7tTwMPr":"gQXMh7YBd6O6","DCbhzWMfFTLP":"GWy98iZPKn3x","UXhWOmHICyMm":"EAvAGehnkxCb","yYkv43Lgm6HS":"gKAvWLBUh9yY","RjqI5NN9Y81J":"47zjXcFkm6W1","7xmD6BTmMg3n":"sUDAlUAvDsbG","Xzv6rsOj2iFw":"Zcil4DzzRjJg","YLsG3afoWuUh":"wBGFr4aKFrUT","W24aFinW4NNZ":"d0899XCH2coc","OXjZMgJRplJp":"r1PGe88VdckZ","0RpVBcxkavmw":"RwRRl7zhjHke","IowIdmYV63ke":"9hv2ePdVOIzF","BcY8uKVqpKvi":"Nr5jkU1JBsPa","PcztjvuTnW9C":"r7YPnsFOELjM","OaOaLkbld7cw":"Vl6B9a4VZfGz","s6e6GfkT9GWr":"rlZpmvW0mayp","IL8BpCi665qt":"OEZushHj2iPD","XCcGchZUffQl":"jik4tTT0jLoU","UFmFYBuoAbOQ":"mAOw7hk8NQx9","1YkIpreuRD06":"R055FmAnmrFZ","L81XsSuc4oMV":"j2ihrBlqRmUr","LGIkwvDVztFY":"JQARbHD08PMw","QNMGuOqH4OFs":"wjkqkuKHt8pC","yBJfmSpTZfVs":"QsMUYnZobaIV","kU1Eb3tJmsOs":"B31qH0JjtV2J","sr7kZRzJTrJS":"tJ2dkEhdQMth","fZtV23vm3RRr":"Lon4yuQJYbHF","tw8nC0gMdu2D":"JyxaqCcJyBZ7","rBdf4HEjifIa":"pKCAvuq834Sz","RCOCQnZDsVeI":"AvI69Zkyu48y","QFhJr3PFekhe":"vkLcxQw86V4R","PGS8IVV1tA70":"DPSK8grv7csG","TiOIzGEx6X4s":"YEBr3v4zj9hi","vpypdlhSKKHn":"xhaE7q5joNdl","ab1iOfsdGaLG":"lH8GHz7fP2oC","PlM8igPYEc4c":"Rhs4A4rOx8qN","1hGqwTrMDMJg":"cs1BCbX94fXx","WcYPOEj6Hoby":"P5G486z7j1bC","eFlOzqC2op86":"tik6pBzwXwF6","FekPm2oQ3gjj":"wkmhEDyJ5IWZ","mAlsxHKCB7op":"fCw8oJXz3y1W","Ej52GAygRde3":"S1omfHW2zAnI","0PAbd4UwLmrw":"i8BpGiClVtRC","UroJlxDNFagr":"kBAvJlc5MkJp","i2OY4MROXtQU":"Y2WKN31BHrHl","h9kOnMVriXPS":"X5YegUJGE0rD","9dp7GXYm5FqX":"BY55jYsOYI8O","UYCuSmXUIBdq":"6t60pIayUCUb","lVq7SF8wm4bv":"E7gbNEtk8jXi","iDxcsyrcGwiU":"Z8FETfNHOfry","iLEWGuPvWfpK":"M1LWYYcgDGjg","VTn0UduQ0b8o":"DVnZO5h829Fu","2RGbWmWhVn5z":"DowFnp3oR7f0","Q4iEB25MVhBf":"RsgRoF8Bj0X5","6tSsblaNekCj":"TBcPFwiU3nMB","UlY24CuZFYTe":"wT4ViA9QQAXs","0sKYz8U6aOMK":"gJOVEn7f3Iue","lJjTnT8ejFJR":"ixOpbrb28Bmz","EZCsR8mFHuDC":"cyiAf65LJ27p","SlRhlac94OVm":"cKKMubVIFKC1","IgSpAyfF3iZt":"qA1PxjuNIfWT","u8btzpC6Lzyg":"EyVn1p4rDZ4g","DZ92Sq42DO7Q":"rpECaJZ7zg9S","P9jIojaEHeHR":"N1uh9tBK2Syh","TK9HQXINmRbi":"MHwqqBm8FKbe","AbDzlY3vFw3K":"8ZlGJrKimfdg","xkkgTakMLKnb":"pX1GBSsbSBQs","ydOcmdkAmidf":"2iM8b2ZWFkrr","dV7C3gFboSUx":"DTIhjiIWINpe","f8ezeLDN6vlj":"8j3JVAaFI8zd","UdIjg3PrD6Km":"A4H6E2zuFWKB","Jtf1FO9agjlS":"ERs0IfPkyuS8","4R5av81Nbx7m":"v4a3VmERZ4M0","93emH00uX8v0":"pudygMKMxfjp","uJqtG2q0U6xN":"B29lkNqckbtv","RJYBXTf8NZRr":"OvM318r9mbIr","06VkcsNsvOdu":"f7qK2MkYRzGZ","Yl9CFuLP2yLh":"g6Nq1ytL7h5t","xWjc7LdFzBve":"vuXjUrgGwyTN","DAFmUxoPeQ2z":"7ngJqd9C0TEa","3nV5izXICbX6":"z2FFRBBDmLOU","fiPSi7ARLDCJ":"WBwoHAtJVEjD","yieBDn0DG5Mr":"IISRtt3Gk1Tw","xg2ToYx2Y2VY":"zADLjfARpHqS","ZGlLjiNYWH8b":"oPLpjFcbq4W4","vcoyUjqTYVYe":"QKe7TAIHSuM3","vW5DF4Zjmhoa":"17s68aprnlBx","36oNFwuuiomf":"yTW75AgqXwSi","dICozL3KiBgI":"TUlvMItrKD2k","bT8eEetISgdj":"ZpmCcuQtKGAf","QV60kFtAcvIn":"88gfpE0PPhJj","sB6U7FDQUx5u":"zd2uAr86OWBA","AEnB1LOMSNYN":"yb78adnzpk7I","LjKG5Now3TMm":"h2FylCYPIJCb","mpXakfY85a3m":"izjwmd9ifhBk","HFYbdqYDsJwa":"523ziNLVCvxv","EbCQKxlA9Gpv":"rtp1zDO2zuZC","6BcOhl2s6SVK":"weyDJS1aaDP2","3Fy2o7CtuGko":"3d8m3QwJQIB4","4drbf9CQBXzb":"pmowEOu29pb6","ZpqvvypQyi1Y":"P6g93zB4ApRv","QRPFzcLPK8n7":"xv6XVdVdb2fY","2lmAhWsg53LR":"8aZPVHE25mmH","riEd9YUNqlfb":"RWYnSc2kKkDO","0SsXiAq4Uvhr":"fUALRQEZpaS6","hjeHkSitKvIS":"HZjE5GxJqPZz","tCJl34P40RlS":"x8y4JK65lTeS","zj736CGfxvvF":"CpDAwutflONw","26lcpg5VXHHe":"WYtzLXu1CHmk","XsMpUse66iMr":"Ts6nBVpm9Xtb","Sn3kmRQgTnfE":"gmir9Z9oY1ZN","vrVjXVEMPVDl":"peBBRzKC1zwj","exSM6z4jk6ga":"letRAVMYREmM","wcU8Npq2CUJa":"LIRNYjzDZ35q","qaYwUjFRk9AT":"tmE67LyNdez6","khUgyDKbLrqC":"EOMToKmfhfYw","oP5KDxZ5KPp2":"EDyCwWSH691m","ozZepaATib72":"oiHMibueVjUv","Zozipr2jPaYX":"n9wnhYqb23Pl","yJnWmIGumKd8":"HywNnZ2LUZ3h","iu7a7JdoeXyd":"9bzH6Psw0yUY","HWpM8LIZvCod":"NuTTTAkOSyvX","3oqjf4Wwcuo0":"3YRZDPNa0kQt","3UMkKOhhuITq":"OjiPCTs6hMFw","HmDvfvOcvDrr":"WhaSpO9w7Uwm","ZHRBvXjPTVUN":"eMfcSFkDQrEZ","RUWGbj1Hab8Q":"jjnC1RpfFyIc","Ox3ogAP7ajMG":"GlEAJepWD5AE","kSuZE898ZjSY":"lyO10EVWta9i","v0VxOQveD4vP":"2XJmgoktZiOV","6uFtrG2NkM2W":"iVhLRyixEFKH","42xpgi1WjJCN":"vFVqsmNHkTJc","TNZ9rYfgLRCT":"p47ONP2t6w84","lTya0K4ET3VN":"dIQh77XSGByz","jJji2qJkUoVR":"JRVltdKCYHM6","H2aa7oF0Z42S":"HahmkwwVfton","QouQz5rNVV3d":"w03BU6cwZwbF","gLsRwlZoxEF4":"OWOsjnfcjwvS","yPlli4263cgz":"yMfqT8GWp1OQ","SmMutcAi5OUD":"CntNK22IQk6v","Wf5mjJDLBX4v":"Bg3L6MyIwNsJ","FpQDaAmTbIRa":"3RfB2JUJ4dvM","wgrFKfPMdbWx":"IlwLjXaSDEbq","KiWWHsbLhixN":"x06ytiWJ4zsL","BEwKxvWFInn9":"kUvljPjYQiKK","zw8pkdaYBqMq":"cBZLzv12iUK3","60098FFHD7Tc":"ec1lhMkf7m0l","xmTnZItsYv8g":"vYrZxyoZjSuJ","VcEp0Lr5bYgi":"PR85yM6FI0HR","z2He7DFercm7":"5QFHLO6bn5qY","nwe230a6HX2T":"YroDyfoP2wc5","D5waJ3tYvTph":"VDjC2bDCP908","2LVslVFGTF1q":"qb2qg7xS49Xg","DjWZRhs4QdO3":"e5PJlopVHr4H","gSXimg2jJire":"PBarpbcreizi","YV59EAJGKCty":"886RLjhClnMX","NMMr5LV2ZAqm":"w8bDB1IGodB0","2hyjt0IC87vh":"Z2vPKkPMcj5v","CDBlmFANzHEE":"0cdFIn4IpwoV","fE1hYnPKNL26":"WWtPtvuJ0qnW","l30ElYV3kH4G":"e7EtHIala2U2","mlquOYW0pxl8":"J2RB7HIt43PN","5dkMY1Kwromi":"h3UFpwgjlnQV","zYD2Blk3fL1W":"bY0HRfxUXD62","WlTFPknDUlu2":"YERjk47rChCh","B6I0gnhXHiCN":"ssTRjeOLmaXt","Xx7wKMLVWU6q":"Y7VONC8cuuFU","erJCpKd5EqGk":"B78rOUrOsfeI","ouBUwcdbt1ZK":"rKSg9CHfb2In","mvcxuV1QVj4S":"eInZfE4FRypC","Hqt9jlWjGCOZ":"zjg4rsGsPyTW","qimsruCB3ttz":"kMvVvbgA3uA4","9HJqFWcisQCk":"DuOMTSwf4s72","5506d9O6j5vy":"auECTtiijkCV","dvMAlvU08cnt":"bGaWfSHN8wEg","qxUTcegrlTfJ":"kOKk773BDC1R","ogEuNeJaTWXy":"Twfo5ELqDu0J","klmMd8h59niK":"mtoXAyHeRJfA","pgVGW77MMQrJ":"zEt1c8IsQkjl","SqV7tTzgjqOp":"8FuQ28vDS20W","capkuMianHHq":"E0mIdiTcVPhf","Od8CHhZF9Tj5":"QSckutLhMDNB","TLxJ8UUBhDk2":"35Qbi7z4KLeL","m3OLAOaHf6BY":"139AxjbAzK1M","WmPxurGVfBOv":"bzAVmxLYNChj","liikPlbTvZkr":"o1AROIQx3vEZ","Sg5zb9mpJqzO":"YiWr9jvB3ERn","TcyUp8qaBoQx":"Ghwgfg33jtZH","dP3U4ZDe9KDd":"1b8TZHHz4i22","Mp1jbnHLaQPu":"erGcPE0ndM15","4eO7mGoaTpLE":"dfEoMc0wR2vl","Y8AzQBNwGpAN":"sUZzn3KPbLJ2","ywXaJuUXrMHW":"nfucXqupmDXZ","A1PlvxZN7nVh":"w2V2ZEhsCb9S","YTatDuNApJrW":"WpcmtBCPsFcb","RkIhP5bSegNY":"Dx81hZUogeux","SzYUlWSWKsjI":"CwUlKnmKibqR","GYeEmNBdpzoP":"Xlury2jeuGvV","VNa81i0WLbsq":"VnXzFKupDSit","jmkQ1LEWE1gB":"TZrp0yZdpbDt","5GT3vQ6VbW2i":"hFG4wKtPfAev","Lp2NpcNZp7V7":"w9XZE3qsz3Pw","hEm1Bs0hKj9l":"7yUq6Jv4F2vQ","6FT6SgPdpf7T":"WypIOB2T1zx5","pMGg35BD9mA0":"pr84mgAfA10K","5nna5zMkN7lm":"2Y5gzoFEQOOQ","NSZVAb06eorA":"S5ZtJwP4fVtM","D6OPOb7sf693":"ZPemqPgQQ2ZK","DsF71yqmz8eh":"XzJNKvP1CzV3","SxWqgW7MLJPS":"cgvUJzjDstXm","4Qcf2668rAgk":"Lg7OzLX1brqX","BlIsDHc71c7s":"UA9JyFl5K2KN","Y0n499gvCmmF":"C5uOFTs4oXy3","oSCM4aGGmKKM":"C8dQTtsihYox","MfUy6Vvj9yaM":"8fzyLatG5FHM","u68KbWT4jLRI":"8ZuhSWF0hsGh","yJbQW2l0Mq3O":"O4VCKCi1h4UJ","laokUJJYxWos":"jvlkkq47T2ql","6Z7sFDV5iEkx":"yMdPhxGS7YcV","QRuUiULwva4P":"5rDQcRHonEuh","KZPSie267BMe":"Dy0YBoa2z4w9","6zIysNukieqL":"42bO3peRE4xa","Qds4DmLZxACw":"fwPs92XWWEOW","iQZdmnPmROIj":"KdxHWB0pArYS","a37Fkcp41yPP":"9FcxGqe8uUUk","lvUps4i890MH":"jMX2xYWaDOvR","jVAUDsam6heA":"alWeiaFJOCVh","NqkK44soGenb":"qeb2WnTJSmD2","xYJkcNP9t867":"efxWJGoJWpt5","3gL80WOYqFjJ":"PdJdmC3gpDY8","G41jgBUbYqxj":"JBHsZyvbyzPH","oWK7juS1u9cz":"9MS19unZEzYu","eSBi6QfXASIb":"54AfZqNrlAd4","1oj3dG7n3g6a":"4MvceqZtpSaf","pmr9gNyqeWKe":"ANu6BGXs3k5K","F9vEMRVjx989":"xSXaWRsBNByi","Gr66YTp3wr4f":"ZZa1pgXuvn4v","1LWmzLbctCX5":"pTyQCW03nCat","ORpl8yMeXCli":"OrNs2F3hT3As","xZaa3ptp8k0r":"sNx0OKKnA29Q","gd0yGLbH1Jwe":"L1nQZXZXknMl","x5ylTlhRrCP0":"jl6xOMcfYlFE","TzNPyRJ5v4yu":"FcUjg8F1EdaA","ZuSt9egAiZ8B":"ZoiGNLc8WJUQ","6mbUBdVYP0DE":"vSXhJPSKU7Ew","OyXdaHNPIN4G":"QVY1TFRa8agO","sYfvZp3j2ibh":"vyzxoQTMvrFU","N3uUGOaQV2HZ":"ZzxMAuU0YMiK","KjU1wUyk4g0m":"GdS1TPNW5x3k","F5YBf3HFQBas":"Kt7zjuICkmCf","vOZhTbpbhSyi":"gYO7IwhwIPl9","lOIrC9m3FWBQ":"rUI21jZgDVaV","zaHXLej0ffgr":"aXEngxC8Lh5q","BQH9Lph3EeUz":"Nsfb2OwEEaFo","WDOR7hdiMXXE":"WxmOXitkqs6W","3XtUYiBK6IOA":"mEUoP9eWp8qD","54QXQZDI746K":"fDVbzOL5Xx5Z","VnqnrPzMFglH":"uUZRcX0e5UWI","X5tMItbrMBMC":"z08wqsf7PIPI","KvbcY6jEvJzV":"LrF0wS6WzzSV","W5t0euPr7eDo":"6pmX6BtNbbHD","fOrELyl5ZWnU":"tIAZKoh4fk1q","GcTS99X07XA7":"6bC2GjPnHlhv","2YI3Aj23filM":"Z6pMkfV1w9TN","Gv6vLrRmGInS":"WuV1By7ZKzzT","nk9DmWXBdBnq":"LjMwcxlR1sb3","67k8bwTK67pt":"bKdny53oaeYk","mLLa25cnEFxA":"7p3XuJtOddpu","qeRbMUMotqZz":"IzJ0unL5m2j5","66EXU4gz6eC2":"4pvmejTrdkpm","nRgc0RhmL03z":"1R477nAkcJrw","msbIL4hINSzX":"adddWF8UbpVG","1NpJXszzhofA":"NoPsFsBRV8IA","Ub2wdQ5dJrKs":"e6RQdDePajRz","sHdE8p2g3Zwu":"iBT5aD5Up3jx","2luEHobuSQfV":"8cHx76HhXyIb","3wl9llyN7HCc":"XxEB69YdjrpN","kWiMuc1FpVld":"gb6OqYZKiYN4","TYvfCGe1u5H7":"mmanrcE6tLGO","ECISs3e1e0BI":"G3XI9Meu4UfK","VYjaMUglku3h":"sFYdbjIK71BI","Et1uaZd6TL2W":"RsSvt9o6BalO","ZTRjdHyDz1wt":"EBCFFkLAkqeh","UMOfyHUKrJ2b":"RMmo9Q0u4qbb","x6g9shx2xNp2":"kmlWLVOS3Mnb","sXLAFAVfbMtg":"LSxyLK7FXsWa","vMboV0bj2yJ9":"H1uPsObb9KLF","EWQf1DC735Ba":"8PBm9Ha2WuhL","y676JQteyieP":"ItDefZr8keS3","15ebbAmd5LL8":"gr0b9HseMma3","zjWFe60h0aGV":"wRRHZ94BCCOg","52l8k7tbMhAj":"lQFvMf9E8DeB","bBPfQZpCo0vv":"5p7Bj7rvhLtW","mldsSQDFXZEF":"fNlX9tW0Agl8","uAHWLzx9RlUC":"oHWOurJGuhC3","wAvu28HSvvFA":"tTBjGKxQ6swG","F7bqbciLpR8n":"eBaT1dIrWHLf","wFZTGSKnAf5E":"IQ5onMAxXQ6B","7supwSvPISY8":"T66IsrMkLWsQ","i0E7OELHYP72":"4kv2a5PbL0Mt","ardaiFqtsHyh":"tgUHG40dBNec","fxPV3c11y4k6":"IPZJfrcpjU8z","jBo8vDT3cgwx":"pkipbj06O2aD","6j7DGQl8PCR3":"32wsDJ7c714h","apAFcM6PjtfS":"YZGZIIj1eWY0","XJqPpr8wa8Um":"Lug6KA9J6h1P","5Ibc1wgRerso":"38VNSSMbmoWc","IH6vI6ZngIpK":"U2qV0vlKPQHm","RRO3UkCH8Naf":"gC9PYZLq39bE","VLVvSPqFRHua":"nIYLauddqZzX","L12chODVdxSi":"s37g8JIk8yrS","RLGFBxp09g0s":"fDEqoUYTtIXF","s7Ig8YYAH0aM":"S3i7smJlJ9lj","WfXqA4asBa6n":"1tdzhvKbPDqw","6RYe5d9EMV6j":"HRqeeEBbmP2g","cjHojZw1RZph":"dxlwA1VEF1Hm","Gn00IWMHviQ1":"ZTTfTLWxU5rQ","UtWl8URQVzZP":"OxdTrLfC9VYx","SQNjExS8vxN4":"fKGzttB4DO16","4MU9fImNh3JV":"5yg3CWl0pEm7","ID2myP26d0AY":"ig7TCVI3cNm8","Rz3SQJsIHUjo":"OyLKswuI01G8","9GgwYpgd5KzE":"51zDMtJmNSJ0","XnSQxA4bccBp":"qiOT8X1HwJvv","nmy4Rq10K5fQ":"bzGlqoZKWSgc","meRmtKlVeaym":"kMoKTf4Xe8Wf","Fi4o8Rdu64TX":"tcTUAMvcLKD8","rDWQ5WfGuE6a":"yU6Suayn5dSI","0TYavmJbZYUR":"AHqh9KRuadw6","g8WOVAdpnx2h":"2HJGZTtEjtcO","61IgwlrPWBTx":"SDzASkJXDGOi","ybWWugOn3X7z":"4WaVmGKeKZev","Dx1f0HZctbQR":"oLkYW28oLikU","RE1UEBFr1MfZ":"gM1kcBqZiSDU","g2YdETIJL4Y4":"Du5RrmYhojyF","nQoZKH76CsP4":"jQMl0MoKDbU8","UgXBK8bFE8yy":"WmTnsOT6t0e0","RbE7VxEzA0Wh":"OYmMMXr4q5Sf","S14VYCxALEHV":"28Q7wHWy2NLG","JNB3twzJeWyM":"pbPcq1BwiRLJ","NO0YPlYcs1e5":"h4WoPk0R8aal","CWHy0xyjNJUh":"BBXMSZcWV1X7","6W719Z9Uw4IL":"mxILLugQCqOb","1MGdC5IzFowr":"BJ1kyY67aNGj","hEBZ0um1Cpxi":"snoutOzZBFxB","D1ykYLXCWSlu":"mjKjA9aylL3Z","6Md4DgWnHWiW":"Z5q1nG9qzeY5","hXg5PtPt6ssH":"nlRo4TEA5opx","w0oZkN3zxp0t":"wv7BwnzhUs2h","QmoybSEowkj6":"vniBc0KLVtdl","ITRXgCjImowe":"8svV7UsEjDr0","vvPRso6B0hQu":"8q4eATpBhgAG","jtrYBOI0y3zN":"2rbblcgDBcMd","3dvL5fYjgBgk":"u3ZqsIE20ONR","MN8p8ik2mzD6":"zUrZpPDzBaAh","pHWe1x365Mnw":"32G2Dm3RzP9Z","GNJwmBgAuG4s":"9vwm6ZWiXgJ6","cBaL0i7IJtUx":"KYDkPyn9X7co","UcoYHqF72nrV":"aPca2ekIogu0","ojzwnQ1KRMUy":"RuykMA0SNIa7","xohEjItdckql":"HDqRQgrB95R9","QrdOPengW7OK":"FFZ5ooYjtZKg","BG6R9Q4gjYNa":"kKh9eLHBmpio","MjLW31a1LB77":"OcvMtdg9Guwf","3kL3EJUf5TVe":"wBE5VzrYontO","C9aDSqrB4P1H":"C77sFVNWEXdG","yOEO7pXvJMBR":"mscZFC66C5UO","2snsZBQDmOHK":"MwiL41KG5vpL","5uHiOu4r4bPg":"YLTrbSbq72j2","MSwfHVmq63XA":"h16Rg3Z3Czek","sOWb1gV863sc":"HNQyPQKBKSrg","WKARQkInEI6Z":"9ET9Fp7wRg9r","fqugqnnVIqeE":"4EHTU3AGkve2","EQfWsvJaELZM":"BEPoNWIV2EXc","ouQfXn6xbiBy":"N3ITr0QLniII","C5mKm5j6Io6p":"36rid369xE3j","uIgslHIENAed":"fkaqrgZuF0yq","QtM1GFLxBDtT":"1BrBu3nY5E0j","uOqvLmQ33W32":"bptoS6rDrrZ3","FvBsJgR7q0Pe":"w4QyuhfRtA1z","yyDYJXq3xnkp":"XPM9qExNF29C","PsfEoUrg8EnB":"qn88gnHMz59E","wpQlhvMij5u2":"ASjihjnzdWkf","lTm6kb8FcSzv":"XVLEtChgyQP8","zTFW4hoyKMHr":"xN587LmGVQ7G","Z6u8kEQVXGsp":"FriO3YLtzFrB","gaAQTSkvcxFL":"oDmKogzQ6uGM","wdNzagAZ1agK":"frycAwya66ZC","OSBb3icDJ3wm":"zJfzBjCXgfCy","GLDFNuIgShPA":"Xfli6Q6EbxXY","GeRMHBSGhc4Z":"0LXaop39bsy4","1HixcsJXe0Zd":"liWg73wgsLsF","MqRHUJordcOg":"Khl77ShOIDOL","AcyDEEGPEdNr":"f18BCtfP8rZE","QdyRIL1shPuY":"pZtjOCqJSpAF","sN2OWtATqUH6":"Repqb503yYfj","puF0NuwHJbEB":"taJIEgoJjDks","u33xbR8EE8yB":"SLrqtQZHIPYW","r6ZoW1RXr6eO":"bjYSif3FqvKg","zHfxXIUTdrsj":"7FEn4EOoF5ZH","Ab0t8Dr7Zd7C":"DBEFldxf9iUj","5k9ypz8hC4Rz":"4tSrQ7CT2dAU","t7E70Kr3d3qc":"pyf4eerQ7Lb1","DTB1uwbUsJ4s":"QtPFwZeSf2ta","fWgoouraK40E":"cfJKZD8mpmMa","URQkx65wBrsK":"wCZybXA5itD6","Iek9MVYeIp6l":"bniaQdovdpWO","nIaZQuQAgaiK":"hBgnsIKHTOZZ","C2bCYjXnH3OY":"6BwaqvhONlmi","RashwyXqHtXU":"0YVSbvNggdFF","AmN60KWstlpW":"uzoDzKzq6PLA","5HCJew3qjHRo":"puS8PrBMq5DV","MUHslCAxUUUf":"U9j8te44TKIr","yNbud2Uekzhu":"cOxIwps6SxkJ","9qGHS26ryDHD":"4jvT58QCfI2d","OgbQY9CGvgxw":"rR1RnXHid1z7","FndkLjb5FCeR":"o42sf4kVd3r0","24qcTwsPQG08":"eajFqLEddiZv","pU7uOLlsJ3Lj":"onDJEmLqxKbA","jR3yMnwRVbxp":"WfO0aG0qvvAV","pLI7MXi0yqPW":"Wkl7JXaKDVZy","hvoG7lDvbS2D":"Y8QmJbg2xUdk","gtJnV9ku8mEx":"SJKLCc64E8K3","xjzrPEnO1vG2":"DXLtKP8mtbxy","kKyaHsbKjk90":"JjTGCKiOFoCf","qI0Axjx5kIal":"pjYNvsoDzTZP","5AaUEmiXm2bj":"uV9daW3bszHy","uzwOCMPkvTYi":"NvRaBB9eICLL","P3nEJ3oCjvTp":"9bR5hLHeyGmk","dmD4qDMGmnte":"IbI5FN4tQgEY","aAudyhq1iQwp":"B1EXOA9y5T7s","dSOSoCdoW7s7":"eC9Hl7mRPxnD","Rys0Vd1cTxxg":"xndhPmwj9d3q","6mFuCWQSny6k":"WEW2eNr2ouHJ","SJSw1aehND6H":"RG9U0K7aWaDZ","N8YiPauPdLoN":"w9OFiR8nEylg","vPP7w5bIHrKg":"AlVrdd4m9R68","42dIi48PXGCy":"DYUlOpwEZY7R","uc07NOCLX8Zc":"Kf3D7u8OqQUx","2QOivT7cxFW9":"89gUfvI3gTl8","pvp0na80qmE4":"9gTrAvKtWMS9","uWWBZ7crPRJM":"yYgQ9EB7csDd","88XHA2m2SpcB":"uxNetDCS4gT3","pb2KHjtM9cSG":"XqEQ2XqYN7KY","tsK34zzu1FaX":"0IyyICjS3Jnm","fNHZk5kv47av":"vwbFRvvOMjmC","DANBOkHnEeXH":"oi5xfoi7jtVk","YV9F9QkCBLhE":"TggO0X8000Y7","7OljnctIH6EX":"7VSCXrQhZ10M","Q4W97CduPlck":"BLta1ufOvXft","Qw3yfggPpTDQ":"CXFBK7xQ3ziL","zUwr8Vna255P":"PEqJRF1voVB3","k9VtJwzBx50X":"yNUQcgaWGuuO","HFDSGA4XQtLW":"ERRtJYJLmgzQ","EgK9fSpLIEBf":"zZzObk0Kfd9z","DUcksVWsenbv":"rALU10adb2sM","m8fYJ200as4o":"g5ZSDn6IWsO6","AGhsg2PlRYGQ":"SfmHFp1Wwy22","uU8JtvfGlsjL":"82mEx2yHaSWr","hiH56IErk8mk":"5BZgDLmR3Oia","pqheWvWp1RNl":"mZtRJPIwipro","MprLC93L1lzw":"7oVHa7Y2rwSc","82Hyf3EAUvFD":"48IY88XJTKhl","6epyHK197deC":"0YJTWON1gwrj","00UqG0tpZyj9":"1UbvNmMsGIMV","jrTHCn089AYP":"4m5iD8a6NLH0","fOWRrzGHzsxB":"I4oVagMjRiTf","gryoL9EPxHX2":"A45zaohF1R2V","VSTiSS3j7Amf":"gIUDSvYDF5pF","lWzlwzC8nyS7":"kKIf92wAs5mG","ouxLBie5GNmF":"M8gMsYkYoqzA","f03Y3pxO6YWP":"gfiQwiL9iowS","lJ2f1VzraTKV":"FiqnXGvEWaWH","bEW66WPrRtPJ":"TeHmYcOM52XK","YQxXkNE6KrqL":"PfiYAhuz6fiK","GdVgSob20PSQ":"IyivvTwj5iMd","zkPKN6VU8Eqe":"IMZ0GULqNynS","DMMa7MUm3aP5":"MN9EOkAeJauJ","OPjijSZbDYys":"W0nCQKM8mCci","TOhraUyKoZUv":"vs6fjI8p15pV","U0Ch7pluB3LP":"nZ9ClN4EbaGo","Aq4mTOX1xFDe":"YTYSZM4jjr1A","bdH6DJZFNkLI":"TmDhzw6x6LRe","ICA4kSScnIFk":"5ga6qOH8leWL","Uo7RaOG7ul7q":"FvDsevgJf6Jv","0m5EygOJcGxn":"FwUzMoQsCBzO","NFPUUwaUkyir":"AusAtbkkYord","EZip27KswMka":"El3mU9mVQ0NP","ckpBNRT7GmCV":"NrMyX0r1Q8lI","n2LirbLt78aU":"UIU8b3ne3y9H","VVEeepU8ySp2":"qw7Zg7sMQthp","fOrnkDALQHe2":"M3PAbfzPYBuD","Qe5O9npwfGnh":"1FFrhyZYyIsD","RAZMnzh7G75M":"ooP9tiFFgtfX","wnIdfBuRVkDN":"UblUWN8em0tD","PmFXB5aWyorV":"nZW0Pyv6WesC","ikra6acsxBOd":"WL4dVNa5NkGW","WrYYR02eXK25":"eJ68R7HCWud4","hY4N6clYdznw":"AVN0O5KH86dn","jCnzHODIdpPY":"Q1APkqzgsSg3","EaFe1PP5UVS1":"QpITZ0KuWM0A","B0539K7hrIMZ":"fXQn6wZfqjkA","MxjRcAaPVek1":"L9dmOhMwVr8A","qkkjPT5V5zuQ":"YgX3UAsXshi2","QEufYtiPEwQ0":"eEIRs7wm3wGx","kYF7eKZB9mbm":"lQ40udMPNfhp","Xry88ILLWHBn":"hYXL0eEdDYgW","7TzQUaP6V7nZ":"vKTL72t2jF8T","seVqmb8oiX8c":"RZPXPLp0Tb1B","0wqdOZy8I6Cq":"NqjvBDXC3wdF","pKtg0m9byHGP":"e8Zq5234QSSg","SxiyNv7w85uP":"NtJ86D5xDNFZ","FCqQ3AO0Xsuz":"zh4FeVO5JUL3","R4U6hghSNpTm":"ls4UFx3Nfa1M","P3uh4sLxb2Zr":"7y0y2VkvrPXp","oNefW4LRGqSL":"vqc00KZn6dlF","GA50lZjhteDt":"HbH7nIgbMsmR","5jE5tpkYSc47":"FAoMBQQPlsIZ","JvHTuGS2JV8d":"na0OF4LjvuiV","c0XbvjwxqXCS":"ZZQ5aIVvUcd3","EsTHzQyLVGby":"NR43UcaWtOFz","Xl7bshlxIGjd":"R6LMiSmJ9j18","5Npw4XbehamZ":"DQMPRZnVeQrd","DqRiyBqsfK5w":"1QHYXzKa8IbR","0Qh56PfQK83n":"1FMcabdZTtWn","wYCakF0POE2s":"f9wdan8FeRbV","b3iZMTCn7F2t":"VREsCjpeXkmv","1cxSu0ihAxWe":"ygbuUSDNHzy0","zv5iDV4RWDoG":"XHvxSkYPA7hR","feMncgaSxgmY":"EJpJ4CDINblj","A8OJT12rPGDH":"Ch8rBq7pmoiA","YdgECdX2qDlN":"PtHI9Z9hpA5W","KSTOTijVYZyF":"HDDiNk1dz4m5","UyifVfH4SLX6":"Yhv8dp1XYuNV","jNLM59r6H27d":"tvVAzefUoRxI","5lceOI9hTqWI":"LL5SFVoS4k6M","Y2DfpuLgqfHg":"UnGpSTx6uJcH","Vi9TozzjVWIP":"6IbOZpgs405p","5c6Y0ir2Bp6a":"hbRrpIAfmfnP","2QtNXJP0zCx9":"hNNSV5xidEE3","tFlPCJoaAZw4":"2OWhCmAAq7Js","ggOzbg2lym0H":"a8Azrx5JoCX4","Fiz4s4Irg5sH":"U9nQoTHpIxZL","l7nInrym4ytG":"axIexPwkFxd8","fudGd9VzDLTz":"0XDXIMmmTNKk","9WYyomWZg38Z":"q8M5pEOZFiea","6gxNjsQyt6a4":"Kos5Upb5BTQz","etupkfFcSyKa":"os29pwU2OhBv","Q7Fxs2QYh0mV":"mryX9v0XMNMG","o3KdRU7JnZ0r":"IKZbvjkuWk13","tomD0IzD2Ifh":"xpOPfjiC5OSf","5T9pfr90kcUs":"OVIxoiJ8t3T5","HpiZM8Lu1Cbu":"AO07jcFZcjzY","nnvklkUaflcn":"sb2ZxXFWYJY0","mudrCSs3vPMn":"Tt6WyF92jFnS","vuXrEXUL7iEI":"TOJwsKKw7xki","bigz3Lubpy61":"InWtCuJryATO","IkAdom3XW3WY":"xnex13Ezm7Yn","l46fbjo5Oc8T":"TynFn31OeD8B","tVra93D3ING0":"5Gn1G6pDWLg5","XAcqRBiN396c":"7UXupw2R0wWz","g2yC5kEbEF06":"Bw8J2y8ckida","ZLFnT9IaAjQD":"C8o587y7Cu8X","KL1p067P0pGc":"KOVQV3eGGfJt","q72sIYJxHXr0":"sdzELbb0njTn","cdHf93ADqz3Y":"ZkMbT4tu3iin","ygoJWE7KxMoa":"qR419mpIcGWU","XcSJszDPXFWH":"qiEMYPGzge6P","WBBzOjOSqpzS":"eRJ5g0CJWn4l","sJLbeo0EBzs9":"dP1PsL29GOMu","XqrzTurVK8jS":"VhQ6VRzclnMt","folUaCY9w5LC":"qAJUtc8veZSt","2aIS7px7p9tQ":"VJCM0kcHxHZ9","qe0iKgImLIGe":"rbi151wBG3Bg","aO13THRO36be":"Vef2wBpAvuNz","WdHi4VPW3RCT":"ZpOTy0CiDLtJ","0frpmnsYVn5P":"43TXR3HpF9Uc","ZOand2DnQTpl":"CcClfbq67fR1","waWWp7F2tbC5":"25hrzccgtXRN","gf1MGO3TOMBZ":"PfQ9PPcUDro2","xnJc2QnxFfgb":"Q5scP6rTA8x7","KkzCFqghVaXJ":"eLdLFyYY62Kq","ETTOaVYaomgO":"G8lLEmHS5xIr","GWIS9wZUHZqk":"mANKpT3sIk6h","x8N3jGRT24gK":"dkmLuUZBxd3Y","5HTEbq0E7tsm":"znNkmwQoK6gf","Xk2GAWbkf5al":"PBTu2uwIC7BB","e73o3rpybhft":"OAwbEayKnehz","Pypn3hIoxb05":"1USPYH0ZUXIQ","qkXSW1G7PJlq":"eJE1mWnHPG1N","KKY2LxL11oxI":"itlIWlfruLNG","Ch5xb0JgNART":"929dlES5CoXe","DEn4XJ1X9LxL":"gLiwhkXlHBes","9ASOG6xcHOpd":"Dr2Chk00AtxA","ptxgAIsuZHSR":"tf3iCPaMsXyN","eNSA1JVtmoPW":"fXEwxpuaiude","CnVHjFtrEV38":"eJvft9Y3PRjI","mmDPbXvDM1Lz":"3yPWGVp5jBdW","ZW5qu8PIvbxH":"FhJz2JUu3Ze6","JrvqdsVigWQP":"qKcOqFj1KWhl","tkYYMJZHqT0t":"PQNHhyA2FKol","foAkAr1EsJko":"X4RpIbwmMPoT","F6n7Dzi2ZSJc":"HrhRQkXAGOtZ","kuvuVYkrzySU":"SLucVUuWuAoT","tLPDOub6pUhB":"MbqM7ZiVuz0d","uY9fOgGjcIJQ":"bHHgq2269zZZ","2dgNO9tfz2AU":"GGyso9yca8Jo","u8YLDkWLFjVc":"y7CBMHld3YcP","NskMWDubjUfz":"ryccvCP8J55l","mxBt1ALG7SF5":"fHvsbW6gFhpM","0GshDhsMyvBO":"WwsSOxCDojxQ","sciKxKBShpiN":"pYjfgxFcmYTu","osmBIhge7wye":"aTDysSQ1efer","NRK393emBtlO":"uRTv82VBRvVs","0d8R5aCXCE9m":"ioj2OZhLSNys","Mde68LUFUIVO":"neubvKMSVRWG","QvaIwfZYEOdC":"PwFLzxuQSgk1","Mt62lVEEO1qR":"VNHVIlLhfkvT","1GZWTOLf5YxH":"pV9Icyx4Awu3","N84BYSW16YvX":"gv3dG7SliY4x","UKG5cvnORlr3":"GpBIyU9eDlFf","Yft3iu2nQtAd":"UGdBGFFHmzhT","e4YtDt1XRv0B":"LXId3kqlGNET","GFnRcGsh4lJv":"FHKcfaqCXoHK","12oqlITnbqd4":"h8ASWoYcAgp8","0rckFKLyT5xN":"6giorbEhNeLt","CmjwqPDjl8xO":"fp2o7nqLBagy","JdK5xn9itbZW":"FgE41iHDmMbJ","egjMHqyL0Ekc":"Em42KQc71A8e","VG86jlnPaXNN":"izdnAsHeGJQk","z3IpQmwlKOOR":"hUDBxiQZWEZa","wOn78iI03tMd":"1nF08ThmzNa3","u3mr9JARfrp0":"TlmTLfqcVuKU","XmQJTAswQKDd":"9AaF7Vs8kJpc","rT58gzlH4HaI":"57FOLWGbc0l2","Ngx5DJmySyHg":"VhMDvFuLBanW","XHAdQGwFnZJ8":"uLF4jfc8aTZU","EpjaKTA0NWJF":"WRFmiTb4G3Ga","G8lu7pznqLQd":"GBlX0dC1SDiK","8Och7WNtwt9r":"lpQsqlWUJr6e","GGoOuBOz2rUO":"Lc3hFkBYpCc3","iuE0vbfVluKQ":"VhIOn0rKVLKH","Al2hfn55yMM6":"PuA01yglYWNS","HymzAdnZCmUR":"JeWcYO54mQKO","LmFpAamKKh2s":"cnv6QZuMnsit","qkz10wBsTKWP":"B2vLgPFRbv6B","Zbjckj6m9aJn":"0zx2sWy4izUm","pqyYsYDUzuMD":"d07qt8Z2QMPV","ohLPcrdRnnTg":"BhaInatVM0oB","QPzFc1nUVicf":"rNUtN0QNR34m","22auJLo9fTCQ":"Cto413qw5g0n","dW0VXjXyEgYg":"AYTFmRGmJvBc","2hHhq4YoR0Ft":"e5pX7iNF6Nsu","FNg1pIB5kfaN":"5AgeRPE4xNqK","Y9pE25xpJAjD":"7lQfWawTgSTz","M01Kc0SL7S7z":"xEzcoPrHkIT5","Acie6knumcEz":"vmwCWFjev93e","pzuB9CbpmPLs":"Ofcorhx2IZ2C","poXG50jIpnjG":"eG6kWrQPoFBW","0RBgoHQXqDEI":"daRuqB6XUMsh","O0kQcAaR7EcE":"iOPOpmw3lgqH","xCz4iexdnYWC":"p5LsPL5bQwiA","pyTsx1sWJ5xq":"nfyfhLRUUOQB","LryoHqBRnHsU":"Lbw7tZd6eaLB","DqYRaPzTQgZh":"SPU1aQtRb7CE","Qj7N7BCEKYOZ":"0FQKJm1JYidN","aPgEblTzSvta":"LLXXgGOe1zUZ","TYX0F184fxeV":"TWJLlp7OiwGR","EYaHXV2zJMKP":"k5YKf4Hj2vdR","8ZUfQKbHRsKB":"wKnvYLTk3pWY","CTjwiOAouneo":"lVMtL9lh9VQR","3mUnBfIpjK8r":"CpIaiaBB0Aw3","hlu6A0aEAjpZ":"PgBtCBfANCJF","eF6mRniKVzO4":"tI8hGFvg96BN","C8HiXMd7YTyB":"uNZ6bmyMM3Nf","3KEPHjFXlpBx":"zCxgWiuHao2q","ZUQQAi2CMIE9":"1O0nPJwnlhKC","DFNLKn4i1ixD":"jri3Zs99NPX8","wmDLOUzS7Ude":"tb1SAhCt1mzE","m2sJMZHyWIO5":"X9S7fEGiPxsU","V7Mpx4vaBabg":"EAxqYxyOjb73","2WnuTksG8H9p":"emJDcEykWAFf","RVx2tqoiSvr3":"ykomtRE9eAda","PcTU0emryxms":"AjCYkDAcbM4c","pXZJF1ESvXwn":"d05vj415rP1o","lZ08gqe0LJNK":"TEqEqCbHRg5v","E0lwRD8CBZ5u":"sQQBFvW3BQHq","StNaYqKxNfWu":"W36unYjO8j3n","3C7wcgHW7k9a":"gY9VFx72UaIc","JMyRve1piWSC":"n9VRTPbee0qe","gwdEeNF1GbV0":"QKvTQLiC1y7Y","TTjoGcTIlFTM":"orh6H36bfM52","YWI7eb5aT6eU":"m6jmv6zc5DWC","SwKDXOJpMPbh":"TaFKq7m7wIgx","Oe10v9MDvf2s":"L2FqjvVKIbbE","ro1UZGDYbQ3r":"NankjQC4rGOR","WI7wcwKCbauq":"Np4DYTsgrVtA","UiWgabi0ZZqH":"2tO3v19Iugao","1u7zZHAvKswZ":"UltHMRXQMtTz","B0gFwCfUf65J":"X5ThSsvDRhY9","7kpxcXXGbs0w":"K5noSGeT7Ido","sWvF9MLbtGso":"EGkR4NEv4Lo2","BpiZFZdpNnWU":"JVoppxTWzIED","PUkPC0JpM4bP":"veQ8GEndZRXi","gdzBM5EJfOEK":"IfQDkqldqYPD","5kYcuNLxKCZ4":"ijErc7WQajb0","olON53kkKSav":"EeFHxNewE6Ox","t9TFfUDaXQEJ":"3hObzmVDTsLd","3Swh8LKHqAYa":"4OZ0QS0lTLJf","IYgDamj8mXMI":"1a8k1cBRJoyV","lSV7txh40vB6":"RzSlKVH4c1a4","HHFw5VUGSEDc":"NMecphivWNos","NDWCbFvBjeSr":"x1Bey2r8DK0X","WUyN94cvVrjw":"f73xZhcyFJWA","49ARgsi5xlWx":"FzGOhfgeqm4E","6z8jMgLCEOUP":"mq2vMnJe8Mmb","m5GBFrMmuDXr":"hKKOFwzMVUqT","sYwcYI4JHzIG":"8y2qouG94Pjq","lgSq88tgNBqo":"smaXBXquAuNI","DgSatxLG3D7h":"YI8V4tUobKvu","JOTkJQLNb5U4":"pkKQ1o6RQfBT","2U6WAaCOdLGz":"UCDOKuTiaLdp","sS3ukHmRBDyf":"c6Fns8FacG2a","KBhERdBFYocf":"mezEf9kJdccT","Qin0QTEzExQm":"O5SKN2TKrQca","r8Oo1bNkweja":"YBo5JukxkdGn","pXEbE1ztUwMV":"0c760xku8oxn","W4Y3Qp23q2Vu":"HzeuNQnZSJHx","ywQGDQt7qSwk":"ZVQVjdPl0wQb","Npa5vpTSydBi":"i5D50Yltdhhk","7xFodJxjeS4B":"nkwav8BJjvZY","xLnLvCSd3dGK":"iREGUQJaWdqJ","lqlH4weND0Jb":"joegh4O8tZfO","y40uToZ7Qo6S":"gtGz2qc8ApM7","eNF3DN8tIjAs":"J6TTNXR1acLp","YJ912wcTVoFR":"ZxdksEqROKkn","ganXBMmOQmfx":"xyNxI4Db34Mc","3giNhs5Jj6N1":"jq4OCHlhAMuW","TA648qk7hCoM":"SPvEW3Z0vViC","uxliUDuGtK4N":"8J8Eate0raR6","wqPmoQMJc40L":"cQS5ErklvEku","6MlImDixoqQp":"xuJOIg7063jW","Oszre4Q6XiZL":"eZb21PMEvFOi","lsMEFsOiwHbY":"NAtb7l40OdDM","a838p55xsLjM":"PgV2NgTRfQf1","EYxZMA1yuKtT":"DPFTD5WhbOis","KAOnduNJ7Stx":"Jzx1csCWo6uX","M2ZTqif3XEtn":"sDlQDx1NM7W7","6IvhjlUQlSlv":"ZNhLKVFpBxGv","FgWxT9sWicDB":"OfTOfXvfQRlW","Rs0YQngpnqUY":"aqm5osqGh5lJ","nmMQm1lZIIno":"U7cUNZg1ZeTW","wj6nD4w3vbgi":"1iVFaiFzhvBb","S8iPAodLIJKD":"VpotvfaYs8Dn","l7o3rVgRbToP":"4m9kqOe9AxEz","rG0dsrXyYVrH":"s6a3FqC9ESrI","R2K59lIAkofg":"PTLvKo4ukXu3","CpdqGQ2ZxwZz":"WmmB3tfGA866","VzfOWaEZ8nqe":"bg4K6huOaRBd","Kj6ZctaKATTC":"R80RICeTUlUp","75d2sbSy0Rdw":"KUcNuZpZCzSR","yFozAIpf8RtF":"AcvOwJu41XAf","lor7CWyGJIde":"NYBC9cCA6beN","KsbkSAVdZqQ3":"b0HalhoTckMK","oCWrsfYhPKW6":"CfVpAAJsyLqw","higsNuPJ7JuO":"ryII4QNULW4Y","JdslOqDk33gc":"wuaT2A02WN8n","Y8rod0m4wEwU":"UWhQhCURKz3Y","CC7zwPqoSS7n":"RlGxTaELDzWw","GZNYB2NdOkEc":"fBruEvJJpYMh","z2AqYUdmOwdv":"YLy1iZBpwEQG","ySZz9nlMc21M":"zyTc8gdp51SS","5YaZ49gVT6ST":"nxBerB9mD5cI","nFtI5goJxJdl":"dvne1Qix4ruN","LnVKfd6FRJKq":"YNXNA06TLDmp","xmIlaBRpJPLS":"G9agTRW36N3W","N9jYsIndgMzS":"FzhKuzyY3L09","tjN7GWZrjSAl":"6f8ldzWjvLoA","u2vMzgVy1WsU":"v81UEnAxhtRj","FO6Wk1SKmUHx":"v5AD0W0zTSkm","m3G4KDlTPiYJ":"33bOGz7lZPR9","kqIMyiHmYg4L":"goGhahvR0UuN","m9oZ7pnEIzFZ":"QxDN8mOrsS7J","RN4ZfccYwXgM":"jco5hBC9FWJk","A8ku8QyspUmI":"N2CrNvNiSCk8","5nZVTy749awh":"d3GEDvIYK9sD","or3eNzY5DL2M":"7dKXZIvGnBvk","w7JIwG4APO2U":"LWPlHRSo1FbT","x4bq68ANXfIL":"L5GPr30nr8T1","Vt8lWSrtQTMu":"TY9GxQNrcgca","JrciBzgY9hi0":"Nt5qi7DW7vIQ","DLC0oPdtFURm":"RRnsQVinCdFH","hRbbekIw8tUR":"2nmCTbfkos19","CKRbRPRKzK6f":"Z79sybyTRQqP","VGqfBe6c9nVk":"J2sljFwR7qnp","VcbaB4sTsEgs":"cDP3qUoJeinw","98QjR4Fb5mQp":"4cMUbIvhdYhU","QXuZb88ArWvJ":"IkUwn8KQtNCj","kAqILhFXatcZ":"M2GloCl4PW8O","xRAWEfeco7DX":"29tHrdjHT55h","Op7YT46Ba5Jk":"giP5ZCkkrrON","bICoqQgf7aPZ":"meTRo6yIakv9","b4a6Dh6eIm1Q":"LMhKLVSiSZFl","Grxksst0dX1Q":"YRwUNXJ0ehsm","CamFcowNIDQC":"L0FxwXK3GoS8","xiVeRThEgcxq":"fcNkBUYIp1RJ","F1dAN96ysyjg":"b1v1NYIADEaC","DNsY9idVn8LN":"5qhagviRuEo6","41NGEFW5nLbD":"4RUPciXj13Xd","UYFl7eZQeXhV":"k3iAsB2xzyzj","OAQgFkut2l8n":"fIubyzhAO92R","p0AQqCqVWnwu":"VVV0UNyMTBJ2","rHwS2kHKIqH3":"lut7JXc5xMGY","DSLCMUGeisKW":"KJCdHBiloqEl","6TlPyzWLV69s":"pNpNn6M5xGJq","ks2ChYajJKU1":"G2NApje6tysG","mtksOYInOdrT":"n0nWhijSrtRs","pLcJcBCloFmO":"hblvrGQPNE6o","A5qoP74mCdns":"WiA6XO95xBF0","BcGgBP2vxzFA":"ANRB2TxWNnpT","xSDC68OPDans":"S3e5y1fPfvDq","coFtuwyN2AWu":"shB1iU3oQKNN","gkXFGo7hecbo":"U8AjLzw1bxmy","dOTqDJdk2Vfx":"YbT5bKmQpx3E","T9R8P0jL7PPq":"uIadrp0k9RRj","uJxzv7zqHkBd":"BV5foTZTS8hd","yyfD0CV43v5S":"7AjdfqR9Fsgs","pwmfOiqQo62f":"5TKItMAujMBl","DKzTCSTgh1GT":"go2217zyfvTY","D3nnjBIlMcvH":"Ra6JwlLQTk7Y","GQQa66ADZFvw":"OojnbR6YIfIR","0HsaeLAVaTvv":"wYjCak6buN0f","IbT4Gxznl3XR":"CWb4BMXazFR0","P4Z8qevpGe2a":"b5MX0moJONkc","LRDGOUZ1TT7k":"JKiI072jAnO0","ENZzaBc8zWmC":"keZ9CB5UrjfS","yUuUELq7DTNF":"H93N9aSSFENZ","TbmE4TcA50Gm":"1j8WiWjf8CxQ","ENwnMeQPZtTo":"4eSBoGBZuubJ","GI78o1WPhUbg":"P8s2moyo1eLL","F3wklZhMcrca":"b1awCuTEaKXD","56jbSOwjnNEG":"unDYntcCARPI","rWc0UiVHEdgK":"a4KkHlLdQr83","dvKwLeuJzurZ":"ipjyGkYTJvTO","JC1DLSsI6tb1":"DniSYZbulucF","GGosqgnfPx2A":"iB6rqD2ErHqp","HtyeuV7XLJGy":"ks3YK8srFRzx","WimuYH6nmU8K":"4TuCgthbEzQG","E31lVQefXCMr":"kJ6dGbgdtlz1","lnvEA8TIVKxr":"0uK0cbt8SzVl","MoaepCC0iNyz":"vjfzkjctmiq3","amxAInxFIOLZ":"oDMItwpgjFtL","8mVElivtFoP2":"UjkvD75G7z1y","mu9sUexGbaOW":"zGKzt0vS7M92","1F0glHQGbneR":"JOmnnMdXa39v","IAEjKwH5mXAQ":"pFN9kouU2a7v","2GHpMguQc8aH":"ScjSddxmX8FV","90vW6VHRTx5M":"MKyguxnVPjeM","gIGaYR4YYjYx":"Dbr2prFl1v7Y","StnL3BggHdZw":"Uz32G434dUQT","xvrqxDH34Sba":"bUML4qs1UsWK","xRIjixGXPO91":"KRJbT32QY22P","BnpqsVvSSC6D":"bXkzs87yW2S1","216Aa3O3Lrsu":"P5B2QEJk8qaF","TYmLl83HGXwQ":"87LD6aK23eST","r4OjzUzIGlXN":"D3nB4Wuu5wU9","dgrJdtU9efFH":"k95mF74x1jpj","TYyA4PbclO60":"EzrxLVUjjvFe","7hxaWndcznuz":"wGWhL6EG5Mh4","EG2YIcBdGI0Z":"atbH2AFZuQqv","FNQL62q60WRm":"y5M3ZTkiHcvs","XnkucqxzePIp":"K0LtB2OoF84H","xEBDuVRKGUtp":"62kZz2L1Dmvc","6lcN3SQRTrIp":"HkXZ6EVo7Y50","CBLRTU3cL0X7":"UjJr2tLPEn52","fRGcycw9XWLB":"HN0ZOPa0iIjS","3p12ss4GIBHh":"ScLu1nAQtwMf","bTaIgJC7wqCs":"1EDmR9tetZ94","AM3doXwiVPOJ":"g1OPB4oTsV56","VWbvAwNwwWZI":"pYcSQdmhhI3B","L1ZFEOG2H0LD":"ZT8gzmTTWTT1","OUDSDDk4RhbZ":"NUBMWUr6OPU8","1bgHF0I6H2iK":"dPSkrE3xdHUd","msyThNkepRyW":"2VQY8WXDwkyy","6470Atf6vXYb":"0vDnbFebgG6n","2kCWaYD9O1Sh":"BcRumz0Z8ah2","GP0RIw8fuEjL":"s5arFdfeKqs4","Q7ezDNCPg6Ws":"No2MptIkTXm0","hLdPgZrAogWe":"3gzRlHKyut1S","ZJwUJ29ASb8j":"Ykvn6SLXHdrj","jV8FBCvXsPNI":"a6ZPgBcw0G8A","iax7BkhyCq6h":"8cEyhJQw23NC","DNh8fgPv8a82":"Tnz2GCi6Pssq","ksOCMTm4grib":"VToHloIYhj7P","f9BpxVIybKb6":"RkTME2V3Natj","d37v7IEq79Tz":"uDSsqTBkbDhW","y2vu8O4mAH2h":"xOu8ei5MD6Hm","Nof0uV7wnaG2":"aNNRBdaTyG8S","nlVhNLYgaT2G":"F6M2UwWTK2Kp","8KcoXuoLPdwt":"I0DNhpPcov64","oOyzzywVjSeP":"mcsjczJS3tDY","MWh9k2a96KaW":"sdwujeqcAdmt","CgwO7i4vmvaF":"ao7RPvG3AGI0","QhUhwkvWRaFL":"OCitR83x15Lz","v3lVeU6XJdJ3":"4ycThRicXMWn","DJGkAgGYWudC":"5jfOzFbxIhOi","GFqYYumaoajy":"aHQD9pmtjboc","rONTpQlTZty6":"Wu7rwFTS5h66","kMtwWftx4rmn":"5qVDT2mpa5vS","HwM2Qa33ji7U":"FeNHVGxLL70j","i3tJQcxRwTDc":"b0I5MhiYOaDy","gThleoBPtkJT":"AZAwKXeXnXb8","GXNz7w1jUMEI":"FC5YRpxtfVQT","PZyUwzgs8HBZ":"QpcGG1VvbiFr","rXR832N3Rm6d":"0v5R1hwJclBo","NBgCliZyScOY":"dvhECadlmwcR","q2aUx6N8ju2S":"F2iQBcbstqlg","v6w7ttEVykfO":"Tf14FzwKOzpy","8XSIE8NmngSJ":"jB31UCtFsXaL","j3sQ15vvzvW3":"nqX3Fsxu4KNJ","gzqftyguIEtQ":"oxFePIIsm58K","KAwNkhvaO5Id":"K4C7dJIs1DS4","Dl42W50B2jqN":"LCi1dSSXhuVC","TzeKAPtctbVZ":"v6MTUANKRBjp","39r9LX2j5OSy":"TCat8snz6pUo","Lzgu9meU0I0L":"5zJxx2lxcnp5","Z2d7JmUc8M7v":"ZLVZK7QBUXIg","Ql2vhzhVkkiy":"3BB5xBA2eFcf","DL2GLbcWJMTC":"8veCTklK82IN","n4Ms4Bakun6z":"EEasTpw2VX5r","x66x0rtge7y2":"HU1mVT7DtKdh","npUZxj5DOnak":"LVyr2G0roZ4g","XvHZ32ViL8BM":"boXCZDamfOj9","76eZoNYBpv9d":"5V1PllURwJoh","bSa8eViI2Ajm":"MEZaWtFRMQwo","NlyPHw81xmkb":"89MEJGTI1STk","WM7FQyroQkim":"sgBtC2aikpvb","9sFkDpBcmxvl":"B2frLZNkvtJJ","B5XMHDCosloj":"6KdCgH3rWfc1","rwn76uzeil7k":"z0TZLpB4cIQg","ocrjeiiFQd8n":"Eg3CVUe6cF91","d5irsz797f2Z":"nny4lKcIbFSl","wntFnCVwgouP":"FpDBNeXlDWkr","vxnlAjbaYSn6":"MWVpVKOq8VT1","IfBdQndMZCoT":"Jp6x1Tg2MaAT","UTf9D1iWYqpq":"DPxzVdDf4Luk","7VbFvB0jKxw6":"x2ClfnE7mSFx","zAIsbn2AVU0J":"xV1PynBf0ZEg","cCPVHvl4IljO":"LdMOI8bJL3BN","Jnn5EQx3W423":"I8A4fRRpAhmi","L4Ah34eaHlpd":"lZawzJOdGRki","VlKLgPwjYKyC":"YLgdfX23BYds","kMzsFrIucbqZ":"y1A24Tr6Svj1","Ipt9eJ90FoIv":"2DMGpCmurdG6","TJXZd8lokRPB":"v4HaJ5A9q7TQ","IsB5nN6wY8NC":"T7g7F1GL2gOI","KCGRLwZmFrUD":"HUgejM0Tuc74","U7kcdXO7N3I5":"nUwzJPSXdpxA","pmiKV57EHDPl":"VwH0YCRXdRGU","KzPDhFOUdWYM":"zYPRjfuWFuIc","e9gczkRXNF3d":"sHyUWngcO6fq","8WUg752pPNhI":"XU48UhVCNiEa","kqleqDdZyYXZ":"xoZM1MJyUfK5","y9f329fphhZ9":"fBM0A6XK7MDi","qv1BazUO4SAG":"H3ik2VUMnrRA","NpYDEnp4Hnpa":"ZRKAboEs7JJS","SCLJgACHkSVD":"mIXhwK9bvg2h","btaXEZW7UDuD":"TIYCHoKQA2ov","wbN6nm7pWaBt":"oNaNy6XmonvA","aoCF8hC28pVo":"KFgS7Y6jIwQF","XVm8NPngNKe7":"rHfMkCWzzZit","LKFD9DVhL4Kv":"Kr2iLxDFUIsv","qo2iN67Nh7jm":"Yp5zETPWWXsG","4jroXF3wIpNA":"G2RYMHY5FfS9","NUaGpwpxXfqO":"soPidepWaPaQ","6yCj33HYsBz2":"HBMY4kvHBdvd","2igW1NtpKrQV":"Ykw1ZiBtMA1z","0HyJIWZdGIFv":"QOHzeYXnbbzK","4IU64BG6VwJV":"vI9PzdubTXBI","Wkm5bK02yDkl":"LOVq8BiMF4Ww","4KrQ0Nk9xYUk":"aLpK48vo0o2r","xGkZCI01N8L8":"XtMvELMQh6Hu","iIQk6hTbFzbY":"1KIudjxp0RU6","boHZxTpV06oP":"zQnLO9BpmAbA","cOGifIyEyeQj":"qJ7nVhisAvLP","pNCkd2koeG4Q":"jlB1KwjNPITz","N14miUUwpuTg":"LMOYdsZ4Ryiq","TUOBYgy1hAcv":"Ejm1bYusqiSt","JT6trNbyLUgO":"3DfTcSBto1Zs","cNYCfhKzH3PP":"Pdl96Wr0rWf8","LrkEUrcyiM7Z":"x8grXq1jMsc8","6DNlVKrZLlvv":"s0Ut79eTqQ6r","W9AGyK2CQwhg":"fiQ37U14GANZ","fWDvrbzkLvPX":"Sd2jYrleHCIt","0HmnlHPsn7y3":"05fZYvQEfl2W","fh0u2aF00oct":"FwL4mVz6DMNf","LxaqlLoYIoB9":"gUc1rSBM0rLf","XK9n61q3GAfE":"0VldT73G3cgX","l8V0d9xYfvt5":"sQ0qNZJuKehj","CGIvnyeH47NV":"pqPphrjlKtAD","XAVWnebImExa":"QDG6JSbhQLNn","RpDYpm9g2KKK":"BVJn4TbfuKAl","LDdFducYucOc":"0vekMrtnspzx","B8mfsl72G5Gn":"u2vTWCpme45X","XSoAI6XCm15G":"h9tqU7eObeeA","A7X8HxJFiaPD":"qv69chT8Rio1","kvKnK0MQOXpY":"bfOBXXRLrxI5","KwipncYGofDB":"oddKeVP7lg1N","7zNeeZTYXakj":"Uyt2uOPCh7Hf","LFh1V0t1siuV":"YHP79ynX0yKb","9cLxnOQNNGHf":"TlVyYGSNgHO2","T7gYtLE2ZWb1":"p1PtLD1h5qLI","i62xrdYAZnIH":"whTCFvaWFl6W","QpQT5QUYDAfr":"UBgvJ1jY7dBb","40rfydlrohIf":"Oe75WIInUavF","soGB1S0BxSun":"ti9igwlRPRow","UZW6zjC3f5wS":"T7KQ4Uem6V8l","bTdAnxUub5ax":"GjRE7s8eYDFh","KUo33aurdqVR":"SC3SKjDGigWX","xBTFHbN7dmG0":"QixZp7jtLu6c","lK8hLtljpdpt":"VNYrgGWehTHb","DSU5v6rsx5hn":"8XrEjTQ3VnGh","AyjMeI4q5TEv":"klml40HgSNes","ZDXTMa0ZmvBY":"oHNchZngtJZt","Myi0hlVjfn79":"IPwZk4YJDSFq","f70MI1l4Sehj":"brcblkDlhUc2","ZhquAyIjgT6o":"XohSd3BfYPgB","B4kF3mXwHaxL":"HvyffuGDTBvm","pv7oF4f9eFhG":"3hmUshljObrj","kjsV5mxmKVNK":"9Kz9H2Ru5D9C","b9UCciSXtrDj":"1fflc2tKLoLN","6IQX74ynVIK9":"iDf4hTlmP2tB","a6vddaz1RIXH":"K6xxRuWNLoTM","omIuau9RkGvA":"B3Ks19LgqdUh","fHSgiKz1lk0V":"u3z3SHTpPVEQ","i7JqAP13qNmt":"tubjPPIldePl","HPeX2AlZkiZe":"S3nQjTSZjMfS","CZ8wJU2Pwz57":"jEVYrUGkyFbZ","L7AUQGACkqqZ":"5CgEExGpHumk","87815beH8yjD":"zCaZw873NAMA","YIXuPz3asNkU":"fRRilymsWTIG","tMlHuzgqS0Hj":"pAUZ1BIio7NS","gCwJmY5GaVRC":"xkD40d0bXy3X","4MYOqHHaG8mf":"zBdnvViCrscE","Ys9iq57XJMkn":"aU7UDaQf77jT","k5RlZqpe2ra3":"yT3u6fEvisck","04iAof193sNY":"zpwhvgIBaBIq","W8AUg3N7jrZI":"lXuh116CKvI6","v3VSL5qWww4Q":"NqTkrnFyX4PJ","EA0JiIJawc34":"ljKUTQlsdbAG","awgvRPhYQRdv":"cHRqklyuQiZF","DxIsG4yunOcf":"VO83Lrd1Ie39","sHopdAsnc1pN":"qyUV2JUzOqle","6PdW0sPnwnpp":"OM2ogHU43Ds6","9bZAWLnjoZW1":"jlbtPX9r3SNE","OzCV4kGUgC4w":"qD5tkHcvDqPP","o49WV1YIWtjV":"AjQh5MsNopBU","Irhg0jaIT6EQ":"OCqALg4GEsuI","D8rU66OCNsAv":"zSQ5CjGd8YxP","4zFzp3ZHq29Y":"PopZ5EBzHuzn","3WzH94XjlEbz":"ZdrskvYix2Iy","I6cnLe8ZFIb5":"VoDQP2sOT2PZ","xje9AzOCAdBf":"tYdWtMGzh2Tx","it8iXALgAZrX":"A3hPoIAPzWno","iwGSBfUZdfUt":"KooyHQ3eocOn","GGBKqNTBgOuw":"YSu52v8U5r9J","epO78kgcFpsl":"SlK3itBkysGH","g6NPspouFy85":"a97V5BxuDpUY","ghPaqmNBVuX8":"kpo5dMBEqfKw","wD5jCfD8D2l3":"jARYvJcsCAYv","RE4Ss0yq7J2e":"spmrsPPyHOMh","KY38mfgbfi0n":"3pTzLn21KsKz","8eXo2nKCDDqi":"gxDFH9nUJoUG","Sak4sDgxOnst":"pqi8ziGl3Y2R","qu83vGFoBbQe":"e2vpLl8GRiXd","GE9zovCkALAG":"T7QMwTDFB96i","IMQLOiso4RSq":"5xfTQgDSGnz3","iRX0M8p3Rm2g":"AiCgIFKa0G6a","uPOQDsd38EXU":"W9H8BrmxC3r8","IaUb2pbKmyYB":"SL3Ixi3A5Yfe","lzDk87A5RbZS":"3rQEu3OBcSaj","gBbGQhWpD0Tt":"kyPhdVJGOpMT","8Pz216XoWRBp":"fsX8rP1SVdnx","peZvVObmbr02":"CFTkSeyUcrcU","lFUEvKjt3QFf":"syiGr7CEkLOq","K2iJx65savaX":"3MG3sOkVsop7","HcAXct6aSZ8k":"1vLB2dX5Qhgn","Jd2ztrQnWAlM":"UnmqAYB93aw9","71uozSL3rb6x":"C9wrThVIWzPR","YF0s19K475dQ":"HkRqR6xFtBif","p67JAPWdEimA":"8YL5bu9rs1lC","6rRMXbExJXCJ":"cyG4B24uc54I","8efQSm8zX00t":"70b8AzbzTfqE","BRLjXzhfOfTP":"8EpmXIeWkEoL","0ZcWhsJWdWXH":"Me0ctW3Fp6v6","SM8Hp6QiLfQN":"oFX3L6HCjD08","1ZpSDovxaFnI":"BOfey7FiR0oF","3ePDzU7JWHGS":"TL7uYuZpg7tw","qkvdi35LYEH0":"BJNWFJRRz8V0","SDpDqKaKeT1z":"SqC3DB3L12fA","feIkVfYheU5n":"qrLg7fERAM1s","aF8tGTQgpU5A":"HQxH9UMzvWXj","FlEYieo3VPOI":"7uJpiplNBy9i","fla63iDl9UUs":"ZUV0ufrZ4Zqq","Yh2uqJ6qaDBk":"r9rtBmy0Aew6","oGE5Y9MPDFlV":"Y8uuopC19low","TrY6awdCxyZT":"LccH0UE0T1Lj","WmCAexq1CCuX":"dRuDVxvk69Cw","d5iAjbPgFY65":"lqX7Bi7DCRhI","5Q5UpzZIyYqp":"pkf0CGUHYR0x","99E7nQpPCrb5":"eoM6gSCYpOWi","pYvn6mdmBeWG":"Luw8r4CQ8LKU","85j4E7fX2gnv":"RMth9OYf8xMC","yqAjLgPSV7aF":"O7ppPkCu3J9A","lpHT8KUNHsMT":"DCvB0FBGLqHg","WNAUu0wYE86v":"MIFn2cy4Mn7s","G5ZayJcOvbyV":"BJ6lu2PeAIJF","fXNqrYaHh1mC":"QjflNAYnB6JM","OYEVMLcUcoWB":"GMhVhyfcyK8s","dUbLLqINX2on":"QSAuroD6c7W5","8azDDwdy5guF":"fvU8yq9vp7KC","KtMkOCLQzA1q":"nbBwROJkWwSz","ValsnsqHreH5":"uOtSMlmvv5pE","49A0D9AM5m9a":"Ppe7mI4o1PuG","fnxTjuC5wbOM":"JibzJO7owzjg","hjxZeLrmHhCj":"vPIZvgvwEBBa","8cXDkHdfr4Bz":"hgoY5xxwJtjM","rRAp205HcIiL":"3Um41ryj2QVM","0ZzzBrapOeMt":"MlnD2MOLnPIB","CHSYUYiRGTr7":"bvf9ipDnK2aL","1SbYoIAGp8fd":"1d8gh9pxBmMv","qRei3qNfJUxC":"x32QvSZv7iVj","rGlfGGZ6FaRZ":"Hpme3lKFF6Mp","O6aN0Z7q9ZKB":"ZeAMyUIf4uJi","eKhLSFmIsPIz":"EQ2ArN5X9lFt","Wo8Wxgo5qYzX":"GTEcQg9yUdiA","sYgQUp52ImZU":"x9gKXP9xI1ab","0TXvpG0bGBLx":"UWvCBX6LnpE1","UAUR2gcHk90T":"jIUsSY9wkozU","TAI7iOdQEtXU":"Ygo33I6fPeyo","G6gB5vxoOgAo":"X7srDTYX9iKd","44Kt5dAIzUN7":"TnmG0RZfFaAB","ars1gWwzrf1C":"j5KbG0IYLO8i","y3UYo7gytoPf":"l5cRJtSA4Eqv","kQnCRjkM9LuO":"mgtH6qWemEPB","qi6KRdjyNBJe":"ZdOKLpuh7twv","CXzJqMhbCoVE":"KVATixpXQoPh","25IZkLCYbXNJ":"CWJOqO0NNY3Y","MgaBj0WgLWFS":"SrBNrizIpxZ9","WsS9Yj7042fq":"juDpD3lNYoJk","dhIODXYBez8m":"cjllYRiLUjeV","llgvD5yOsFhe":"EsYt4fn7XXXD","OVpL49xI0xwk":"NkYGgeyXw8w3","NIsWIUWRUeFC":"tiJCFC5ScAiz","t0U7oGquuNFK":"394jDKhxui7H","OOOzpBss2MJP":"PDRAWvMBwRWY","43C9g7jbI456":"AfjM718RP9Ig","2NAfASqhUfZR":"R8ZtbBS3Wiwv","1nJune4Hwk1r":"wpUaXem5MPDh","dOWsJmJn9S5T":"pR35UHg1X67D","ND3P9sMITg08":"BxD69kXdd1lr","WZHBKLzvabQz":"Jl3jRMeQDXfK","Ma3sBmQyUcm6":"jiuaDcB9IaQD","EXH9582jTNXA":"y1jn58WLmQAc","v1HY9q1RmKoU":"7xD7s823jG85","uBTELoZtQXeU":"JVJKHFI57sJ3","PQS6Dm0v1RFH":"RxRoPfTEze33","2qlbsSM6T8R4":"oXUIboNQM3lb","hhwzB6KGkZRR":"YyeDvv7nzGex","36UjpyWNAfJo":"HcKRpcA7u86X","XnfNTtY5HLnb":"85wA6VmlcZEz","7CpdYd9C0pHW":"1plQfRjs7A2E","osXm9sN4F5O5":"syYIqua4GlYx","jKVYkrBrXqmi":"LVmTAMG2VrSw","03v0W89OzaEV":"rGiaROprxwtj","W10q7vmccXBc":"LSunP849h4tU","bp1IjynjjArV":"ciyPy3p16SwK","j4sBKHXzGrbl":"e0Z0YiQCyHsU","TsWdV7fexgzx":"tODnd79aK6hH","eQE05OeJvWtQ":"2sOzqnADp4Zz","SJaq3weRAvHl":"ZiVa45w2REk5","dzuy5ySZkieI":"woSqCOBAAKNo","th5vX9hwamZJ":"PXMOUKdb4O6n","COXjpsHe5Tom":"AtKcpG32eT6T","Z6pTVXT6p5zh":"bFAEOPVSbciK","Kj88vTmxwGFx":"4TL9ojSCc0uZ","vpZZgwMXs6bb":"TXVIJHlzaeCV","ZYzinQ5E1nti":"e106V6P6Z2Yv","oNApONvlxrsx":"FO4NiBeFdTig","YPg7fKsmmKUD":"E0YAXOh3oxxu","QmgKR9wNsNv8":"RQ5uaYL9VSkL","vKMJjziWGBE2":"6RdQwBaZXNCH","FBG0oZDukMfZ":"aT9N0FacjcAv","ZMTODdN9n1Bi":"zUO5llwRAJxg","S4szKgO3fxnZ":"mQnRfHpJ25NP","TsUFWjllAx1K":"t4cyg7fAdtyL","r0qA4YZprZGV":"XtRPdveQNGth","De7zJjL9Ob8i":"4NAD0i48qdZl","gQT4kwhIfU4T":"otvPD6syPNtf","aFjS3axcdUH6":"reaRK55RfJdv","GNOa2mUfXeZs":"j4cdYKWyCEST","YbkOgD6EdfKS":"hgsBLCAjTG0z","XlY04JZ405WH":"1wqddHZiz2pW","EZtd9qXgK2ef":"wWS2rz8ltR4B","JRMP8FQdWQtp":"bPv77Hkp2SsD","0hYX7liYLsLM":"Bt6WqTeEreaT","4xiqj5Mt9jXP":"psS8Ox1rbjPa","jtqGRjVhor54":"YIriBZsVIX0z","gqxMsJcBCvhZ":"JF2f2yL2Ecei","8evknAXlwTmJ":"btagIYKFtQJ5","gUHaTOVbMIyH":"5R1k26ndSzAr","OfESxC3FU34i":"Zc3DsfCoBzc2","WKoYCHYpSQcJ":"WHntFB4pAKLY","E16DM2vjxyju":"F5zPo8d9yJgm","Wf57i8rg0M1m":"fCyt8YAnucn4","LW1k4n89haey":"mhuN789tP4Fv","iCtJokO2D3MH":"ErVfK5H3ZJZF","h59kHa1RwVEc":"KAtJb1aXYIYx","yNTvsRKXCgfV":"hK8b8vfyTh4r","5nmVx8Xq1det":"Ad9kksXKeyU9","uIOok7U7S7qp":"RD6RzYvVZJi4","qPBCQOtVp2n7":"7ErS48LdKlKU","Q9HdKbHT2v9J":"c3D6Fr2wRwnS","6KTQILWQXLgR":"esRegp1cfYSd","DdZM4oHsRvag":"ObXuEO4pS6gi","sXAgM6f4nVT0":"s2ryUt4runh4","QtoGAwQh1dc0":"hhxp9Cu407oG","dTEKqB4L57j4":"Pt6XqFfqFka9","fQwOCmJ8Fo5i":"rgn5Cx8zjDxh","v0iuEGLa3Ytt":"iniWt8Ij2PUk","mour5yrVq1IC":"EEEIppfgd8SC","e23p7FNazTVS":"TH5lHOHnuz4g","35A44s6tAApV":"XI4xW6wdsw7d","MX4xhB93j9Pz":"i6ROtCAfQxqU","zCpsD6wEu6zm":"YGYQ6DmEwXrI","AQPUYetiHPgt":"5ewTy0SjEPyY","UOUycPSIPhxt":"Ato8qW9VlPFp","Kluoedf2faQS":"KdVzkCU7l0Mv","bXa2wrRjF1QX":"g4hPy1aq7DNc","47Hn1DVmccDm":"EH3JErQE5AUv","ejKEGgQynJET":"u4VaTXWDCPOC","wicB1v45LJx2":"fJTF6j4pLLwJ","s7aZpCYdWv4E":"ZXrnkKoZSZJc","DKTye9Vx4Y91":"kxJyALIRoMq9","RejyAgNb0Ksw":"hli0iGLXdlp1","PKy1srSFH7RN":"jXrN7GX5aUUO","dVYcIccPPc4Q":"jwO9nfyTb1or","fFRMoK8wESo8":"pQB5YkPu5EEC","TmX3rRQp84Fk":"h88dDXGIkv3v","OK4bmNlDjZh5":"aO3RcCwzAE7b","00owEzJX5tZn":"IswIbmgLQGo7","fH8T0hQSh1pR":"D4kX3ckmC08j","R2ai95qFh0Zu":"oeuj10GMNqTP","XZT71FP18wHo":"6qqNkKfbWMSQ","idOyA9dkvirB":"wOknKnEx5Z40","GlSeXerDkWwx":"cwMTIWAIzpPL","w8KGN2112plH":"DLCIJuBi549H","vuPX7dYePzpZ":"ujOiVdnFSZXE","3FuwWjk2488W":"QJunMSYQEI52","CGthKWh9Z3AX":"C9JiJW9GXZgt","goZHOKsRcVgm":"Nc26WutmmrWW","0wyudUFEioG5":"BsOxqyP82OqK","KxWUtyvPRKSy":"xRG9yDpxXYvc","B367VGD4xYzR":"VtRiam1YyUwv","22EHclvF4JMa":"4en3XjMEqXps","z3sm1jqqpGgA":"MWo3it038WxU","2woADxNrcpqq":"6oBQMZWPpEnZ","4YfkUvSXzjhr":"FMNPvwHRLrVO","b9sdJM6U1Hk9":"AJ7SjfJNeSZM","6Gqh7ubwSfjY":"sAsDWsJ1PRfd","8Wfa8LNN9V63":"6X2chWDy2kBs","R9EvQFZf6PFo":"3JhgWwfrE0E7","X7RWYupUjFa7":"3Uj32LKOzTaY","DB4UmNFu2F8U":"WuiS2YmdZZt5","3mJ0t9NXEZzX":"1nsR2FW9ot1i","cuqwXLEdkt09":"bVheJoJRjmA9","VgEXbGgpWTSM":"Gisn0D7umlyh","IivalKJR44VQ":"ZPZ9FU2aICFv","ZZlSxwpbBpYh":"Gj00CCcg3cy8","WEzjxzfmtZFk":"eyUYG90N8YeL","IcEEkj0gKTBN":"7Qk7GAuVaYY3","SHfFveYpUtDf":"k2KM9oXgq8BQ","Kw0BuAt6eUMe":"ht5r5UyjVu5D","hY8fXHzoVY7D":"bsxxynif9OlS","AlMsnwFaCSSx":"1UZFJheOKlcC","iJi5OxsU2u6V":"vezcQ2izfH0j","FHb5NujJU3Sc":"uXdoYOJSICJO","5yFgYb9xMezB":"ZFsPPUEkJErp","BlxpzJfgyfDr":"vJpKC8cf41GM","S0xAohw8cdOY":"NXlrrH6uqESI","mwvbPT53ClLG":"V4bZyMooNCWY","5H141zwxJ1Fl":"2Cl65kWPfGqt","wYL4KgMf4iOM":"9fXBuIwsyPRo","0oa62fbvg49C":"9xCr8bnU7qOx","MFXgW007Gr2X":"PxNrk7CjYeKH","vmHTfSReTWd9":"MqAbkfXIm1Lr","sJAXmUoeEui0":"2fhim6GXuEpV","25XTst4VfooQ":"5bjmdhTXrOsu","Ew14vLOxATSy":"DHMb75TDkvMV","iKmJQUhfFNJu":"jfurYaoLRXwm","topAU5RWp6WW":"WAbIVeewy3h0","M1eTvRN9KFTn":"g6KxL3GVmCgV","KbK6dk9dKkBO":"6MFIArtxdVFg","6QAyZJYDcTzI":"JmcCbp8Wq0Du","a7THSdkMgnqO":"iGXrnFvtvj9k","GkSuAV2hXumR":"bza5qd6q88JU","ktOBwaKhY1x0":"8CSMmhh9sTU2","hJrPJQ0FDFP8":"TauLgVP2NHIZ","ovtdfjvp1dTd":"p3KvSXuBbOXy","R9Ff1ZPQYJhX":"TahjVXjwHaDT","A1fUXfLiA840":"vuyZUhveOyuc","dWYUqLHGhOBO":"mP5hIXlZPQDq","4x3dBkjADNQ3":"WGRLBGErqhwC","xbnZ1H8nEqiP":"YjY3ydH9iXkE","U8deg4HfFZDn":"0788rb0k0FDu","HrlvtRtHFXqm":"pgHGgLoVx6i2","IgDaQ2mOUFwp":"LEbw1iNfBx0U","QTDra7uKfJx0":"LcGfNgXeCwJt","mEDZLTgfJsrC":"QguOUCOuUxRr","up9zYNqpJlhA":"vkxkKRHP4vkx","fm4zKzZi4EP0":"DwyLw4EfOBlH","CS0a3kx8cFA1":"yzka7hbgn5RP","JiAeoqQrJCZn":"wZ5kPZGi6DMh","JOz2DkOk5y7u":"L9yVOYkx0i6X","UWDu0FHj5Jyz":"A78qdsyrMuBr","F38S4Fpg1OJx":"v3edyxK1fwqx","DV1DdvB5h8sG":"68H9x2qrTGTx","I22hey0Ahrmb":"3k4JkgFtaasd","RZ3Oz8ZZtEY3":"7zsgwTarnmIe","QVlMJHrNgRnX":"l4lUgktHwdgL","BUzD4dB190Yc":"TDTaY6y7Gzox","OjMmTgdqMCnb":"knHGVz7MQHpb","KBjaQdQyOmf4":"XbdL1KE8xgik","Mjgay1gdlAvb":"dEPk9XAtopmM","P8qcSlorwrcg":"Ohd8M9ulFBbk","lFPF9rEHtXFr":"pskzZhTf1bi6","8tP40WPV21r8":"K8W4Qx5H1Ukq","R69t9CC58VgN":"RYxRWwicSXxs","CC2lrkHfdLgU":"r8XZFRjO8DJV","ZTY1oBiiCRED":"9bUngUPqxzMs","h1kCDWpBNFx6":"z4HCiFRoWesq","ySu1IhnxHHT9":"EcgKHfqwqnq6","VgzYHzmStHEx":"11HLN3tOhSdn","D9qP8APiA9xQ":"EKzheEOB0kMu","hWmE19RYcY1s":"Pju6f7eiU0on","MPUZCD0lbl7m":"BT4RgwLPxEv9","0vKtqHJ5ZJvg":"ofQq3dMFWVpQ","GH4pnCCmtgF3":"dYwQ2en0AoaP","GHJPxTAAm4tS":"X3ruVITeP6yE","8NewErYTgbZy":"NYIehjQuZror","gWbzu9DRH8ZF":"9ogzgrI9FBUB","2YFNT6XpcG19":"AosvHtygr6Ss","NqHj1aSLgZnL":"wqlv9roZgh0J","aacn63hVPNEc":"qKTQVghK7JDX","zYyDib5z4Nxe":"xSNEYyTHv2dC","pxhpebQgwsXI":"Jo6igF8x0qn1","ytvtVxoHGIxT":"HBObNJutDRBn","ESKtZUL7aJFu":"WsiVBrMNsGuR","cokpd8ZPwalW":"BR7v99jfMdKo","Z5AKviyz3H1B":"aXLGVncUxdJ6","isyex2YXn88J":"s5wMw9Ced6oz","23BmkWYmAmvF":"tyrtEYAhiKr8","PRsozcI52oJH":"TPRkxOlLoOCY","GsGlDRJATGOy":"GAIwwIQQtDBZ","W87qlP1fGSsY":"OpwWPwNLrIoe","JBU5G02Bhi2Q":"gydZjVFVfhAn","knnZrVCr7eZQ":"5DeYJSOQBFK4","r4WUu049HAYM":"UQ9NacxiiX5S","dHKRMS6S4WtM":"n7XY9uBMpZyX","MTUnebsD0pbn":"QsqjzIDjMHbH","ZDeC0uBOh6nt":"1oYmHMoqEXwe","HOnQiNdJeT3B":"aZSTizgwFeVs","RHzzHtJ1DbGc":"EOj7qc2BQT3J","L4YKAvDAJ7sd":"1k0hz49rO5T6","drb2oPrwt7RX":"DuhuLLNztdx5","sc9pMFOjs0O5":"KrjcWndq3njj","vU5HbtE0lX8a":"q0zbwECJF7ni","xcJNDzEzSfEb":"GOnXzrdsVIWT","dRwkpRLNQOOX":"lXKJgtK4hb0g","e4utxueNkj1n":"iw2pN0ieP0nK","JDB5iDGDt28i":"1pqf2LWFlkRx","1765peK4IxaQ":"N6kJMf6zPRU7","XY8F6Jwqqorl":"535pjNv8l1MI","buX3yeghI8jH":"dsNbJJeNLWqo","tYj8gtvP7Yiu":"2JdCtHrVmsrZ","Byjuv9G14TzM":"cjlJpZeldVyo","Be9pK5brExem":"aJuvCcSlIabb","1SV6XZHuhGOV":"ZU8nRs6T9Jgy","Lh5U56vPYWb6":"RAsI92AvYCVK","Djf33YJ5o3rT":"CFB5GbZJNmZr","vwf6gWsO0x3X":"jKC9yJEBdSYe","8p68dXDhcd6z":"gzaBf5itma5a","zutdWvyjkdco":"Czd1LONs3lgm","1z4i6bR3fUGX":"mN4FeH0TUfgD","h7ntnW11yS0C":"YVBR1LSmEY4F","OxCWoLjCBGa6":"SNIdyetYj42c","Bj7gg6u8k3cX":"OLuNahdNa8W6","p8MUpZ6LlCGR":"eDx3YZwmWB5U","S1chQCC3SShk":"8N7fQP5EWBkv","FdMYMOSv5FLr":"Kar0hbl99JGs","aoK54V4Z95lN":"0xbEhrL2UEL2","laVUETO4Hmom":"yhDIhEBTgr37","1t28bpCO7tS0":"0NEfsSz0fU2I","plOSZzv9wqKR":"OtcZQwlMXC9d","3fL4fn5Irnzu":"1McubLvPz5jK","fkP0U7qufJFI":"yGGptFbIV1QU","zIlx2ShWFVpi":"8HQqgdEFjcSL","Cm7cX3EzxIGN":"JB16smsr5yGi","30QYDBTjXSAW":"Hoh5noO4aQiM","KmgPkHrWVCOx":"fvCQzhpCiAy0","o7SlA5CVQqEQ":"f0exRUqeabiz","nDg8jDi5SzmZ":"zn2dRGLk1KDf","5hsHHJdwo8Cw":"I9EmelINKh3x","tUergfE7kPUq":"xkmmc5NB1PTQ","eNwn2nuXhdm5":"jDqh0HdpHnNi","MecdKZ38LPcf":"SZxqzRK82mLH","aXHee8nddoTG":"ijgzMVhZ7Zmm","TCCbpUfaLt6G":"fZr7a1PmiooF","FsaHIn7REXJX":"AUzEYGHCR9A9","54rQvOewX1Xq":"vcFOIH1GwBHs","KpQHqwpt5p4Y":"LzlgRZjAGN9u","tXcsJq8lksKO":"EwSbD2uKIQbR","9MlocxZW7sXQ":"GmW5dae9kIgS","QgmzIfw2OSMG":"CVtq3OrxOdZ5","vSRkGstkGfAI":"laanEgIXbfWs","FU68kwhtSZbX":"Ae1XAU7SSTx2","gtdZVdJK7qsF":"lsnHfV2xDIxr","MXZaDp72g7uS":"dSpmNsCuqJT0","QFxsjbyN9MQq":"1A71FfrH5bVQ","f8SK02AN8Xvs":"KCXjQT5SRMVD","jwWywoJnq9lB":"THjJClcEd0pc","wzyUaY9if7fO":"zfPny4QEwoP8","o8niR5wdtd64":"pevPmcZdHGFS","mZwqyJnyMVb6":"j3IvCZoPGaq5","MwZgwZE7TAx0":"0qIgWCbYxXgy","i50tM63eCSG7":"jvEG763K7iLS","5SgNK6BwPam4":"K8cPNzHpx1p5","NE38vmAl1mIm":"LbwLfrmB0xF8","gMCjhXSX4kQr":"SXgSwxRQSUFm","g4Otj5EigYdN":"mOw415mW6M7m","XWW47ktfhSzK":"oG8x1NtDrE1k","npJt6OVp23hA":"IRF2hh8Nb3Iw","hc3zHkcqmj35":"PPOXPk4WAlyd","CPfJsZm2XhZS":"88o2pHaLlDBw","DS3lD2R0Rn4U":"KKvjeTcaGuNR","a9NG1dYgW9fG":"WudweXagpHai","Kwi6SEnmFqpo":"TucjiQ0HLrAx","6zRRGkDjkl8Z":"XAcbX6gRsSgX","QIBJSeRJwj0y":"RyFj2ukpnNIB","xnizbvTBQ8jI":"XsqhH3O1Tkbm","bnzHRyFRCq2j":"7ZRwou1MLgsN","QtY6S5E8Kn4Y":"9GBRKBmcOh6J","BfvSF2QEbtvv":"1rlKogaELtnL","S36C6sWNAvC1":"GH2npN1F4mmv","zIHSZUQznogf":"aNRi033Gt1w1","h6DsObHAoqhO":"b49nqp0hEVJu","qxESWvzEfpEy":"elTAb9pNlJwz","wtfdAvbB5Mto":"HquqS1cY54AD","sOgUmC9r9NtO":"VNOj8IXvmiBG","fTCmc6dZRMLQ":"ptZaOEjGh2pt","pPYQgH4UW7zY":"zObnm1S4QuaZ","a2Ozdjgd85NG":"bbBGnRwAoGDi","FkFSul5chTRs":"ZW8fA8jBZNZR","275ROCpweo1z":"NEwC4RCND3g4","30q1JNMetFNI":"0wcasICf0r7J","CZdedN1NF6lP":"09HDiYPIQ40p","yU9maTqSVf14":"CfxSaCBnpdLV","iKoeJ6M50Hkb":"4KVzCQYpMPBW","5VPkFnmNvcN7":"hpPqztlxKnyr","tTcwdIuv4Pvg":"iouoYuPjB0Ap","5IQPvsSBZj2q":"u4xUIyMBQi3m","N9J8MvW7FIaz":"J3S8xTHKNOaw","R4ffXUTaEEPa":"hYzSK4kmiosc","IR0hI0BIwtFV":"x9lHQtkvF22I","FDOA0MqtyKpK":"CGkntBGVcDVS","zC05C6HbwuJh":"LgTIi5fNP91W","KuSuCjTRbDeU":"5eOmPWfgapMl","E43BpPCIFc4i":"aumnoVoGfczH","JGGVU3NjhXFJ":"ktV7qF8geyCq","VeJTniU81Mky":"Tmd7tDk1QGho","VesHHfU0nH5g":"5VVhB1xySooZ","WrU4j0ffxqmC":"hADa7m3BRsCZ","WtqEkgH3tUQX":"VHEKsstbUo2l","iybUSpJczymq":"2Wi0pp4UghKj","7Dc7NvSEguVD":"ZsXMhZJjCSxr","piGf8Qm2cKBA":"Jn6kW5H7sNJV","yrUAYNDr209V":"PO7I90qi8wmT","WT8dcDWicbOr":"fG5gakmHyHuu","eitrJP3xwVeQ":"MpqsAkFNkm1p","O918zGgNxDVp":"9BE8GhDKzCPZ","xFJHpCJrSMIm":"CKuBzEHvOyhM","edPkqV4lR7fL":"VPNHMZ8EVqAU","IKfAbh7nMd4P":"ZVP8TLTJqzqb","VPSdGf60aCNV":"mrJKqJBCheWr","MuiFC1Bz9IZw":"6bOUuLD3e07X","hXdOr20IWvsg":"B3OGgniNRCE1","cM6C5gTNIaeA":"40qkebYSIGxC","OrZf5mMl1ymZ":"rniBeqBEfcea","VX01Qtieivj0":"waNpJTWHY71H","bcrHFcJ5Jb5l":"4Dwr15xwCbz1","DeSTiQ5ErXXF":"i6tVdr4FMxvI","ZL1qCJILGs3y":"MuyKXbyHF2i8","MOCYgnAddfYL":"Bu4kNZYwqt7I","7iCD5Wfl8YSZ":"TjOEo6yj7fH7","4XSyOQRp5QJB":"RTEwjzfO8OCW","AV3WCGOF34J9":"ziDidu1F9vhN","7PjiszMxSG3f":"5LUzGGD0JVW4","KDMQhLIlsAGi":"qi0KF7IqGSia","wZb3nPGRiVuu":"U0eqieSrB2Q8","ofP1w7BKLFyo":"mQAXElXpxfHr","an6Y4koc4s5f":"m8ZMMKNmjDhw","Pr2A7kC3aRno":"SLyhb4eDytRP","4jBF5QhIKlmu":"0Ds0Jn6RhNty","2yH0BSOATzRq":"Yvsc6U0HpPbF","d3qxo3p4dlrh":"fNdjlsUAOmhz","G7YfkBbdXPMK":"E26PbdFPhZV1","QvVexr60ngbu":"UyG4jMWPVyUh","U0gJEk6JTZvX":"PruRNSAzlE5o","CiYoqfHIkkbG":"QCKiO8O2bshc","9wmnZqVWFrX4":"DVNRttor344S","xBUXodz0jnm9":"dF3y2phaBn2q","CQc4RJvSYd1V":"i1dWpD1cqgsF","di3nMr4gdFDu":"t7SYZM0AZZco","MWu8xDskUNuz":"4CjcEYDkS3lD","WRrRuvdzI3ab":"zVP8XxYOM4CA","LargXwMryb90":"QovxlTpyFa71","tfFohOyMeTB5":"1gYDc2CX6gfY","BMik4XAGDdzc":"Aw5C1bY8tR5b","cWeSfYgqN2ZK":"VPfhGIoU4Otq","dHMkNyoQcXXQ":"su1vo6xATHTQ","ugV3BbE4xIdI":"Nr4rcG5J4Hc2","FagcGf1PiAeY":"aCfMGOX2aFY4","L7zxpGs5J1Bn":"rBChZCRgVRWV","9gxsDECQfV2b":"jmVFi61nC8wk","uHj2LENUIEmP":"RBOqDjk4arOV","U0SSbED6CMAb":"boP1eSURZtca","J5bX0JN8oFja":"rWgvNLBM1Fqi","wH7wRScFo9dq":"LmOWcbV83I7s","PFSouQXAgfaj":"gSPXi5I97uN4","7OM95y3BGojQ":"fuIl3635CEDc","hdzmJ4uW2rxw":"dDFd38Ux0l3M","3rCiTQ4Vy6P9":"Jbxn7KzVZg14","2C8OVk53sx5t":"AOIVCDQZjVJM","rGBMD3pT5i8e":"BFPAsOlHHXmD","qq6OEcIUGDkU":"7o3cJFLI23My","MlfoCUY38K7C":"eQTsT74DQ0tA","tfpgXnUjmAr0":"HscEfYu7A1fn","cetjL1ul9Y9t":"Ui7g1TmNF2FD","OkeSWu5qtYHV":"94382uxn4iQW","njRhs1vqqwjC":"Ed4zQx1E5Bp5","dEfHU8t4AD7u":"IBQVnBeQrWv8","YFxRob0XvlJz":"QLmAvUnNusyx","iPwNcv0usCBM":"ge8unvxYrA28","0V3TCy8ZB3NL":"ZxXJZICFN9si","gaMsBTkPHn1v":"YUiXj1qIreKX","15NI9BLwLxcb":"1HdEYhlzlom2","el3NRgJaOUGa":"sa1Z43UvjQQZ","Sc76AqwPpbfd":"2xoK72kxJ7X7","U2h73yhU5prZ":"xMdwOjLjeePO","4D3YSlZbfYgy":"IXlB5VJwlz8N","V8GH42psQafl":"2EfkBTajXgsP","qQRVMJZHnbhN":"IBbqvOKC40MP","JwwSCSTbEbmu":"iSkP0n2kvyHh","ZKvZoRIdsA9I":"Cj3ob7GtqLkz","4sYxqxPlFDJO":"RgAovpyuIy4S","e7p97NFTBPul":"R88fcXXXg6Qa","7qcLMqkir03L":"Jl37M09lu4Nt","UQFIxRzwwpWC":"fW8JOIydcOld","a5z4WvB2rr3i":"w3v1MNNggBLN","gmezE0llp4NL":"yzcWiq78VzEA","v0s2sfhSJ1Lr":"KlWhKRBMFzFA","6hOQOUQ3O1nZ":"qAuz8IjgL5Ry","DKX6BdSTgIQv":"cwUu2y6e1V1P","dxTDKUUTO0um":"mUj8DyzgqKGo","8KXlb2WjsB23":"kwdFG8EnT39A","rQkqo6YKcZne":"Wqo0SP9gKbZg","flOTft06In4B":"a4aPY069V7Gx","TicoivwrFKiB":"je8hsAmXd10w","ZvOKMqlYSSim":"h1uYxNmfJSDs","DMEFDeJrCENa":"rxYk3ljhknTK","KkhyMYxEZ4Pd":"vr2n69UUnytn","zAiCrzl4YyrF":"YeB6QHb7TKwR","SKV8ScwOIDjN":"pJ88Ei0ka5JQ","mefWZJoRbzVe":"D5VRGYk7xVVB","Iih3m2iasglz":"4XJxj2pazB2R","x9ppM25vfzrv":"oCVxCATT5t7R","qs3ZcAo9XtJW":"O6zGpES24pZI","zdcu6FTLrrLc":"drZIyQZSYLdX","WpuaLNxx9rQo":"6NQPvXBmqiq2","KsGR4RyJjz7D":"EALY3OIRiNds","kjJD3QKFQCcp":"iK0JEZi23awi","gO6MtVCBo1ww":"dpTXydxqjiic","Bgw279y9x8Tb":"5Hf9bHvItKWb","5XNDRGYq8Qjz":"QjBgK89DwHvJ","MoIEcpfvnKwf":"jQlkRe18H9mV","AcExQ3nZEgaJ":"VOgv6kASdNhr","ZeJbFjNGix3k":"Bm1XfwePVlBE","DDWGt2mTVVWY":"dhJLbPpqi2Mn","0kwp1qtCo1Cx":"Uwbou5ndk2I8","CTCTD1EmBKtP":"4airT2jcvxCt","mps4rownpw0e":"ZYf4LTcdWSoI","as2GoEAcCvUU":"aJVdx2hBcI0V","6wjsVp9sytNo":"ayXDsyGlRfw1","ULvJzQFQACZW":"b9Grlrwt6JKl","3Lorxg38OHXa":"213FqcJhBZnP","ZQTcLH8BTZAV":"iz7RFDRiSJEd","1pQ27SdHxHYv":"hCeLov2PkNKA","N1jknmK3rOJV":"lytZBaIcWG3Q","NrpyuZfTUaE1":"HAhKaNRmItKA","LbVYpeduWKW0":"SOApt3CGuLf3","MWK2FBoi4qma":"IR7pPdSfrvoP","wtkLf3DLyPVF":"buRRfqV1Ovhj","VEUnmioromEX":"YOZFzE7fomEa","vj5W7mI9u7PY":"L1j6LX8W8Vzf","knHdGHIWi4Nw":"7QmNTUMlR5i2","5LjWq6euHp21":"n6uyVJoNdxBU","Cic5roDNxj9j":"nRPQwsn8xnZj","Tk65Iultn2S1":"LUEmRyaaL4sD","gySMZWHrS7n4":"jTqmhYKWOIN4","SW71LE9W348z":"1w8wkNTYaJJB","ZnGEqcxzMqhi":"vG37W9F1BfEa","spIxVJwjfvg3":"eRDUiSACmQMs","BApJwWTT9oYS":"1XwcCvTE2DTk","9gBEmaOchvSe":"sGTLU0yLjNiv","vmdBLDXOFinz":"fgHEvmdMfQ6f","8JtX2PFfIGlF":"1PnOp0qHRf3b","p8Go5BQQNA6j":"oZfE6Pb9qOGx","KUIAruOxjXgh":"OvBifbcIfuMM","HSNzCElpAxeo":"LPy1WTWjNmZd","l3eLojOZKh8m":"HDMJrLkXFCw5","RXC7FxnWRZgk":"r1T9G4gcnN5O","6AxzzXiCNZeW":"Vr4C7w71wgWK","x5jWY2LUYIYZ":"xXXj5zo2rM3n","Pcj0VzFzap09":"wWkYNetauRab","gSRfWWh1Kibt":"ApwEwOJW4lLC","DIsZTktV41QL":"dNnVK8ZPrzES","5yhvET5GzaCI":"0RgXIAGURRqh","rJJAXh5Zofpo":"pLORYxZSsGar","O7kbsGL1w2Ia":"svVNIsWru0uO","zL3oCZ2ymZb4":"olfBnE00Eby2","PFMRnMF4ZPRf":"8iA1K53oENTr","GIVHFYINAZl1":"eOD4Mp2gOnZ9","LoRNhqO4SgOC":"J5bSXLd0fXSd","noLK2pPZZmKU":"32fbdFU3s6vx","d36otF8hp2Y8":"HRaYrnml58zl","Tda0kT6wPZ4N":"jW9eMOgdXzGu","zDeSiHo8qKdQ":"k5mEIiAYzdOR","T2LRST8FzdSB":"6NWduXqNn1V5","EhSi3Nq5YUwr":"5fRjLoYmWg3E","OCW9HWqw9OPH":"f1QrRafZTKUy","V05vn0LVOZlA":"Jur6xQ5csBUD","t5TvaHbOBobE":"DpjqFnnWl9rL","lVNh7N92pZ9C":"hXVuzZMr8uCe","EDEOnDUedXn6":"wAf7Jz3VMHV7","CElrG20KaWXE":"48fiY95fnFne","jNlLpUUN4miz":"cXAt1IehCeyb","FRmYfReDNkVY":"ARZozVpf6uy6","mw9HlObkJdS5":"brroTtI5YDi6","dCECpad5CXDY":"Y1g9HSjkyr6G","DagrQXrWKTsE":"HqJ6BjDc7Zho","qRDZvs8PVXnM":"4w1Ep4ZAVirP","mWGeeLxkWioP":"znXxQBUXeQsU","tOGLZQBcY0je":"qY7p4O0C9IVT","TMLqdBmQnfJh":"N4ItD60qFDHa","dxzyIjKKT7qB":"4syxWUUdDOes","m0cBElAMIeM8":"V8VmMpLQQXOk","qhQfUPuTkewh":"bPOWEGOucBa6","K5TriDGAVJNk":"IwGa5OOmxEkK","UoPfFB8KPZtm":"O4c1WjAjggYD","Gp2AhNrFCPgP":"vdmqUxQWhlHq","nM5zOgA4kj7k":"MoofUE1qoEq6","ZPSnEaLHAk8x":"Do0VDeSb1rIV","UNEYLiVFCbfP":"hKjODasXogkp","6qpxyOLlf58Q":"eJF1sEQQ5aLj","bz5lBthqBGxJ":"h6Akx66kCCMP","v5ZAlI4vpTdd":"Zcqyn8QP2qYM","2KnMc4UcjCK9":"QH6ZwjRiRFKc","EFNs0RHFZszW":"O6WPauzdUksJ","NFBmYFZKEb2Z":"MpCq4PUOAZ8R","lJweBBaNerAv":"Ca7TqBah7X2O","qN68nKQSoqjH":"kxS8G1LBiAx3","jYB6fXi2VN8T":"1rYXOdIq58Qc","tVdCVGLIftYK":"gVUuC3ylIN19","ioPtkY1jdfph":"6fWcVtb3eMY0","OJJk2fzqKE9H":"544SYnBk7clk","AffJqpM6vFAD":"TfRbNSw3aP95","rVLWXnJVjd2p":"wJ2fXyILe7el","7rNSZKVPqk6V":"2aT6Mb59XAHc","HX9eo5S6kS3H":"JEg1hG3mFvpx","eDVpCgckwyCL":"VocflQFTFOMn","kMpVSrbM3Zew":"ewVMxhsO1t8Z","HcW2WOhSIgEC":"sw6QXy4JlTJl","3g7n0GpNHr3Q":"EEfifR4wKPFL","kdmzfRRKMdLX":"P1fP6UPJFPmc","bl8OMcu3k8M7":"31ViuRxPhNyz","mt7gWYOErWtr":"FITCz86G25G4","uX61Ci0UmfhT":"NdwyJ4RYU3fZ","8ZhFmQdArtXB":"3jhD4f3IFNhP","xFt5Col4fXq7":"f3I6mudtqQEW","BQUIVOk1xP0n":"yrfb7rREnqjT","dSdQWoEl3c1h":"lD4evix1frMk","ynaJCvqa7uXB":"e5ApAFLjOKil","34T5j2mpbtao":"be1EFHF8cXmG","HJ2gAsqHcMee":"OEirPv7vqnU2","KNmeE7HCpKKf":"QPY4eoBIKUn5","rnwyIWMd7iw1":"JmwyZBSuljIA","qL0iiEkvYGWY":"jbjoeiNZM8fo","TnDvQPZ4vnSm":"UxdfSjelhVcs","IEgb6v1lR4vu":"LSDROiDd8ZBR","oXwVL8k30vZl":"DZCs3oCSKFfj","ZSSSxc5VeecQ":"Fv4ZMAvf8avG","dmL2AWNgseuW":"3jWf1ds3SL48","grpr3xfa9xbC":"9LprOZ9FlasX","vso2JpCOLGde":"PTCddsq9g5FW","E6TFpAf1m9Rh":"zUuYxRBxzsot","XF7zlJVOXJdz":"qD4TlqCbZzPX","5zbYW1aE41EQ":"dTepp1RDO52X","BLnHG7XzQ1VY":"68N60tvBXMir","mN0gXMWbGU68":"6rckhRMaIoQ4","GerYnFHynXhF":"BqszOUpMIF6p","b2UtiSH3kah9":"7yop4l3cCB4s","zGd0Zgge1Mjx":"JyF6r71f5jzj","FZyTrMMO3WmT":"ld8A6D3fjScm","H9fqh4NBJO80":"DFmOSMs1D0wV","W45BguSckXjQ":"ldtiVTNSKwpW","W8MHhjzZq9Qx":"oSHoB82dJw26","VCxaxoEymTSb":"Wkj9bsr8I2tq","C2TDciQBdgXS":"Yy72QlXvMwEy","TQrOZkgjGShJ":"YrR3YDIW6sSZ","7WYkjIRHg0q2":"iiQw6N2QjZLe","QmOtUJyjqswd":"IHu2SDgKPg4i","cdKiYB5vyvL6":"lh1swSYeNPKp","Myd7HmX1Zl5n":"KH7SmPGTKBpU","MzSkg7EH2aAh":"Bez9j9O8nQcb","81D0gkwna7xC":"ldHRpvweZJuB","VqQwHBBXhsDj":"5uuwnnQEHi9J","YtYGHTn1vkD4":"k7KG1t2Pxlaw","WqwTPiI8L8TP":"cX07We6bILCC","gOsn3jp176Wf":"hzSEND41yTkh","PIBS2llduVZw":"4etgpYLo2Xi2","awK74Cqp8BbM":"9GtONa3UV1r1","JnPbGtUtjTtX":"QhY7VWjZ3aoL","ylw9Xk4AY6E3":"zRttw3OP2EwG","LbWp696niMgg":"3wSwMudE9bQx","idEGwUE8Ms7x":"CtEMDAHVswE5","Q0rEkMh71H5v":"FrctsBBDPFXB","MPg9GQ2abjBX":"OVaPogQtZYyw","PG45dZd5QpfJ":"vKT4ggImhmsN","yX5vr8YbhaJ1":"vUV9LRK2oR04","LU8b1yfwhD5c":"e4sMgBlmWTuC","eJTc8XRGqvKK":"VpcK3hHQNc1O","Co3MLS26YHZ3":"EcWge4WA1BRc","ZPPceLtNiqy2":"Sl0LA9XfMcqN","B9jnOBeKTlKT":"33vt5lCT7787","kf5DnJ7QXFwd":"JRedUnoq0Ey3","B3GNCrqtbHQX":"WApo5The1JGT","qt6EUoisWvfj":"9WlsUscJxc3i","4XA4xfStT2xT":"lXMYoxXc4o3E","pr3V3sQbsvT4":"0UyZTKbiTeg1","y4ceBwhr4rUG":"LTqPeQta0Gha","3F1Q1d1VcNZu":"qCQRwbS0jd6F","HVs2KfFn44OC":"22jV3CaXdYxG","es4wN0JhwX8D":"Sia04MjGVILJ","AlAt2602wJEl":"oWYWVpifWWO1","E0xiKdCrrWQD":"wYA7osaGxCax","2ZGN2ShMRTfw":"VFfXjrwOLQUk","rOCDnx5xYNsO":"cEId8FPsikRM","6YfiHOrgy3sS":"MDxGssdV7CxB","3pyerqIYVo4e":"U4yQ35iZonbB","FdhbiPbNS4yl":"oPIhrW4IYEbU","WmMhgiG7ZLgI":"AkzxwJe8PEd0","ilujxHUMetxZ":"CAdLZHtylf1o","LIHgWL53eQrF":"jhJaKGEv1z5h","SIHYQuTc5PO9":"S6w9MceAhYop","bNPNsbrAg7xi":"nSyrkDF82WwK","w7x1BFZsdtNb":"vi5LR6mFjdL5","S82oh4LPF2Fv":"N7BsXSa11P0m","xpuyz3bRxAei":"ppIrB3jX0E3j","PynPWeW1EDwF":"p5F43EltSACB","MdKuhSVMsOtq":"lrxNKWZQUQUK","42sflrMHIGMr":"8PehY619EBzv","ihyCLxT59PNZ":"ui170wB7ib9i","9ZnpXDU40Muj":"FARIiv9FYfPM","kulGVLFn5rDn":"hNw1W2ng14di","9Ams4mZRWX9S":"ljhyhHk3JE4G","uYP12euBfwsd":"N8LSFm14HppW","OSo4UZZzdJOp":"OR3AxC6t36mx","yeVVo2yfUlgA":"Enhila7d3r11","bFP9grGhfDwR":"8hEiJApuP4iM","aLgvxrREPbYM":"a4gK9Rei4RIM","I23gbck9cjZs":"x6L6BxJvz3U2","2omVaymaUtPP":"8ASBIy6J5kd2","7AEDbsO1vwI0":"E36imALPLXzQ","th3S3Dw7NtVt":"csTU8ppJi846","4phnEeoS9Hiq":"EwdXoqfLNwmH","22y4V7CHwIZI":"iuMl80dUuSSf","p6uhLQwdnuMa":"TtopwcV6BL62","f1QSoelDtscz":"GQnXzQHjwHSs","y7oSd7gSjtOB":"wPFgqfUXSHyj","8vj2ZEYqXkOw":"V80Z8QeDb6EE","IQS6SD7dUfXc":"TdSpRxRXpZTP","ap028XCd3Ce8":"zkBwiYxFeucU","lGNjxiZSk56r":"3NkbkL4mKvj3","b5JbKyWnpuv6":"4imiRQfHKTz3","g75wOqEbHRvF":"iXdQLoBUk1Es","2YOTQwcTKtLS":"7jnClQKMbczM","Yh3Oq5aMOnkC":"iVSGDQRAYRwG","aRGp9r8xtudP":"7LkRMNmx4dWq","QwcEzFASoUSy":"qrEyvap6rnr0","1h9EYmrctwoV":"Uwrf7KzL6VVz","KFIlu2lZOLiL":"BRIj6BJJAAKH","L8fl8RjsrcGI":"sl8gOFIuh4eg","WndL15BIyiYa":"VVFbWdtRvqh5","kxQwGTwHcWmt":"EjpM77Czuhh9","xkCUbIoJqS3j":"OoT93X9a74jI","Ql2QGSbN9GUC":"I7eRDYHq5soI","SrOh9EJ6IJH1":"E2bbWwTHsriN","38PLrfFIayhg":"xJqWPYpEJHwx","KilTXS04IczY":"gZRTDMftPhSH","yY6QHNCCXt6k":"uSjILiLT7Hf4","b6hDlulPEbh5":"ojpnDMYO7alG","9m5NL8M4jhtx":"tRBOXtmmfw43","yz961uv1KRFt":"45XzkuvcWE2P","RhMOx4MllZKe":"sYXi5LxIxpwB","Ltv5fi7rgGXm":"k5eISoEPZgG4","IhmpcuQPriwT":"BFbbRFZ1BnZM","6FTsm71M64AQ":"P5jEq6w29lRM","2Mnt5ytPSwYx":"0WekQTmsdQts","UJa0svGUdfGE":"WIC938fnmLXO","BzAd4Jci5NTF":"w2luWxAIPNlm","j1ckR5cCNlfq":"NqIxCBO82QCi","tM5ebE709O5g":"RlCuBgXcRc4U","0Ui4DiE8pJus":"3bXJgHnoxwLk","3UVcpVKt8jRH":"q2W0G7Cm5piH","0FbLzYiAjC88":"HB9owNRh3mQl","Qk9Nihoy22k9":"64QdTNFLwOsN","LSEm7UbXPnrS":"pb6oCYbzWKzf","i5nZh1hIpxhe":"gzBNBYeOFPsQ","orptnijYbxH0":"dEskbxzFofX1","A56MnzLl1hBV":"7nrIdf6DTbJN","mwzY1zfROTMb":"dJ9nOWxwYWi4","ZjxNFl9gNqA7":"iG0EMCjey3QK","VMRPhWxTqFqk":"D4tyTMakklHo","ziWzHMEBghRs":"fwM8dNN1qurZ","WqZhgqr3ZoGH":"W2OjHP7qyO3V","81kZLZ15HNfA":"nQ2MlyQfgXLr","FNUjBOf9NzvP":"G2kEStdvesvT","kQWkqU1uA2Kq":"4ml3A2CHIoNP","a7prlBInO9Tn":"kONGhFfBQqsG","QeX1ijdz2n2L":"foDRj3JOEX3m","7420EnOHPJlC":"z3Q9EkwG62Pp","rZYa35S7Df1d":"yOOoJIWa2yN8","qcIeV88WvWHF":"AOlx37YaOI1w","fxEUwxqB7knb":"1cI44onPVRXu","PTDH1vlOF4j7":"zGpSCJmUI411","QZu3GspMnrHR":"nfsKwKqpwUU5","WOvYYoyHPlqE":"cnAJbq2Nyv9N","PL8gCkuweJOc":"W1AvvRwbdBGf","8FEyBLMY8BdW":"9wXxG3eox7WQ","7KFWNlSCHjv6":"oc6JIHevStdt","bPLcwIrfNm0Y":"N2y3L5NLdfDJ","zyIzAJnsPRAw":"UzBp0PwgJ0yb","JOtpC0yZ3cXc":"mBfUnyg9YG70","BbZX6THupvjf":"NsRAiwpo4ZjT","bxRN5VjufFYB":"uuAuCAUQSaeX","hrPt4lrW6GeS":"cNrSkrM4vfPx","PX2DcHG8OJYq":"jOK9TUlN1Mrf","e4pVSOfgmD97":"GzeMF7AIn9iQ","nk55jJp5AnPf":"6DaQfaKMpilS","K8oRBN5Oppc0":"8GH7F76VPhC4","5At5b2xa30wS":"BR1fXIKsuz7d","UQfv3l97VcWf":"TYQG7eLnE9yG","vAny64WRH2NQ":"V9UhS3Q1JPlG","4TOHANnyG7sO":"hqqGPxfb6QS5","SHobWZYkA7TT":"2vlsg7Vb6v7Z","FxmHs7Ar39dQ":"sf2kRlLlEjjE","sqHl6qKgtxE1":"j1fdJfGaLXN5","0LGxnB29hjJ5":"rCiUFgZvv6ED","PxaaK2EAyKRq":"WLCkPCQEP7EB","xmTRwvnZ0UHs":"pdNpdTyjm9FL","FaQzFHZXCB8C":"FuHm3U6Vrv7V","H1yHQQrlBxHv":"nNL6VS0QLeUx","rTLlZkxUkGoP":"OLE4XSTzUzkb","64Blb9TFcMxF":"PPIxPcHTH3d5","DhstgygswMYb":"ML6c7XymUD1g","BDyuEJqTs863":"khwHWIiBFvVR","mYkIwRSdbHNJ":"ls9R8xvbVhNR","aTEyxcDsw9ez":"QCTwn5hJaRLs","h0cXGXd81QIH":"waHuQzERXxSJ","fQdMPFbTvsBt":"yTX8XqnEPd1Z","7IB4hlYxi9bT":"9IpeHAyOy25t","hccou7T0w5ko":"x3f4k694h746","QVDbykKdmArQ":"ZYKJzfdy4V1z","YqS13WCxqfeR":"D2xZ13gbY5Yo","7mgpNzjuGpoI":"rmcSXPzXmPcY","GlJ962aMx3xH":"lcKcqN0vuvTF","N4slaeoVg2o5":"oWYKAfWIMHnT","evN1iNaSrRJD":"YD8m3S5bxRxZ","aHHDz6sqdTf3":"xUMjkZxnUMxc","rDZi05j0huoG":"5ffeVhffIYHB","kcsAmzknIrBU":"BMEbco15ZSeG","7NgfsQOwLGdz":"3gGPgR8aQuAH","14ehwxgpwBwJ":"jwqCxyf3oHa0","ECz9TN7K6YUJ":"N1Mpzkg5JdlS","phlZQB69aVRC":"heMj1WkHpvdf","ZKhdHAth2jbu":"7Plqh8sEmVVt","qCGaFyqjUAfg":"4kwENkNx2cpH","dNJdZNP5b0dP":"C2tuf7GP03Vk","ga29qRz772Uq":"VR9fNRLKm1d1","VhijJDfXQ2Hw":"xStIHwYoAHUP","MoPMSnWmZXyz":"chXJ6PTl7lxH","iVhlnNZbl4CU":"EudvRKy5GOkm","ZUOE4ukPmkDM":"RBRLK1tsQ2FS","uTcLijYasBtJ":"uBuMhTxAYY8P","nMpirF8vdSw3":"ILhbK49BJY2C","DlMvg6jbfGFa":"edSz1j7Inxhz","utxqCgKyRweY":"7CwaG7lOBKLW","IEijD2es1TWI":"x0B8uKNWZKlU","1g6nj0DAt4Gg":"dX5Gn27edMI6","w8m466oAjt3D":"uWRUZCwfZ5hr","BArEmoDiD7yO":"r53VXLoyVpx6","eQiyoA3kz4iC":"mFvy8uskpHyT","y1DAUCHUsh1K":"tvhuebtmXGNV","Zu4NU6tm57D0":"yxi7Vo47YyqE","Io10OHdjZr0o":"h3b1RmGAjPqX","31OFHcCVmTDD":"J0ZXscwZrA1k","rCiNxYuf2B9u":"5iRf3o6VvVsL","E4C2bsNDE5UR":"MEJEcGDSoHl4","lMzPVDqhSijh":"uVxSks9rBxcE","vuV4E6J8fk3A":"nvARr7zAcKzj","mkLL5QejOOkR":"FrxL7KCWClxE","v1331pQcTYFj":"PrMjxdCCKknU","6GeTYJJX6Ja4":"z5DIksgYJYFD","OgFutKMLeLYK":"YXlg8DTCU7gN","zSSp3k3WwPP4":"c28dppHR1UVy","BjmaKY7W0I8N":"xVQEVCY7QzZu","TFuHZdwzsU7t":"4PlMOX4O1Tq5","AX0ukKnu0SUg":"N31RlWqb97bE","BwYA7HjjuLrG":"G0Ov5UaAmMt4","ELC4Y12OfRaR":"jAvaUdhYm2Xj","mnULaQuYC88F":"XfBjizmNqVds","CBuXtj7kTpZQ":"ZtQsZLwhzndo","gmkjpO5GkMAS":"Hu80BMVAN3oi","epW1gvKBe9T3":"IK5M1Bnz4Ky6","SnavO8hJhwSx":"8MokPqd4pKIY","X1gbsAAsHS7F":"m5ipZMHBfKBK","OVeFv9dOyMS3":"1YoS9amDLqmO","4K943c1srec0":"iJ64iNbGaRuN","WOGcc0kSt6WG":"ACAXbQnj2dsQ","FuDrE9Chcdf4":"QAA7ZozfavkJ","GaqEviXKUSba":"6ymL09G8UDmU","rDaDnI0aEitx":"l5QC2quitoAi","PDIW9U1fgjaC":"NTaar0dKDcsW","gCBZSgzXEwov":"YlHtuI7gLTNI","xtsuR8tgmcfA":"aWZY47kBrXBq","gVcYfQJV4snX":"KD0Q97pMJTqL","fR7NTcjo9P0S":"29HAGMpkz9TQ","T2S7PYLhIFvG":"p3Mb1Ngd7xmC","6zKwo1yEvx9x":"bxSpvdmhLpRB","tUSPCfXT40b3":"YuDlAwNZWlRa","aUBwdyMfBteC":"6loyY1WjgNXC","K1l6NpIHljEn":"8dkKSbrTjDkC","9Oiw3oMK1RfH":"MjN80wTw7wGQ","A4JgkHWeJtY8":"41yypB7funwl","77DH5mzQswsh":"9zR0d7qRr4y1","iO3I5WvzEgal":"GTIRyPWkFOk7","zQc6cZ4taVNR":"yNLntMmUA1Al","ZnGgxDzXRFDG":"OLjQDw3NfLGz","E70VpVi8J9N9":"x6s9YwAAnJHR","7djRncItjmbJ":"vt0yfiVXa4EG","jgeqzb3pcfLi":"EcSwOO1Za4iQ","NW87hxnajU9W":"il5Ih3o9fqBL","xysTAR00lDcy":"mmlVkhc3LYKl","Gyttlh1o7aic":"Hc3ERtSzboBn","MTv3LGeC9WqH":"92eDGL5MFlmT","5NvmL1IfLsF2":"fIid1QtlRQVh","v6IWsMiM85VG":"n3Xt2Wjk2nuT","4lpSQHORfBIR":"Sxyb6VIVXvdl","6wlQqCYNlPjt":"Sk5Ost1etHLe","A8C39AGDloPy":"kG7j2xTTGvD4","dnKvRy1kuHH1":"YNMwmoyJvPK3","v2I3NnGFpw55":"oiTfb7qm5rOR","KUxKjv8PRFKt":"D0Nyulhi3WbK","XceM8tcHNUTz":"0irS8rp8Eig6","jZrIHGVCjaC9":"eNSTvTuIGo9V","o6Bg6XuxkW5n":"S7YUCAITK4GA","vGom5jA5RYmy":"KqKsel74PQp2","Y7twm8A3AnJh":"UGrDry2Bcj4i","PYZxUE2cNOiD":"tjI6Ms2z06C2","YsP5WJBFhfkN":"58ad6D2NJmHn","eaPgRwc3cTTj":"bACz3RoxlPXd","g8tCqgWh21RO":"GwweKUKkNQCf","SJ6SM9nKRXWU":"HANxMj5TUckX","GnFuseMNMap8":"lggE3SOsDigv","Rl3dWWkFkxYZ":"UfnJDNafXJEE","IvHbQRMpzoPf":"EPQQXEvVQrcF","p0AUfj6RJibT":"wpmpVWj7EKrm","XnQvuXAKyCVq":"lYXyfs4YkXRz","efcOMa77ZNuo":"BJqVu98bRZZU","QOIntV6E0Zbr":"0yNmagc15bFb","HkFkPBHvBUE3":"G55QVZoipdY7","MeEMLs6vchvK":"GAEuQLNoS7aV","bYLoaZrUqbTG":"mBeDmAkCLA5X","Gau4QGKE2pcK":"2lAzDdo9ge9l","fQ7FryAi5pCr":"FWu0lUFDTFqw","jj01aQbBztgl":"1uKidKHAWnB8","MiKMkHld8K7C":"u1yOtuCDC18d","HDD1iJ1S0w5u":"sBPXze2PykZp","pkPmeJ6abWNS":"vj6hUMOpinHj","V3Q0ESszA2tc":"8VdG1KBTS4H2","NyOvIOKlaJ78":"OgLFn92vlHKn","LvUS4zVGetFw":"hOdK7fgvWjMf","o0wMVX2D2IWw":"Hmef4LdELyqp","ePM9QhohHDM7":"QsrJHPrVydoS","jH4b8JZ7tDYJ":"ottdIfhiEQ69","NCtY5kSHJ8uw":"P1K8ZJ7bigMJ","pAzwexJUgpLE":"MahN0xrV0paI","FdMOE0wLnOlc":"7feWAsvWgezo","Ots9dmAXphpi":"9kqqQ53alHpO","XmVmSbpUtTHq":"9D83U75zVQWr","tgQcqdsLrK8Y":"crAC6HuF8Lnk","U7AqJVQTnwMb":"pw2pmXwNUlUg","G9hj72KrdqVr":"SHsxUQM3xGNF","ZwG5srGBmEcK":"ePY9OLjjrTRb","qZmR44aXVcsv":"myJbtZ0knbsn","Mvp4CtRykxia":"CixnjW44GB0V","FXd9pjqKIg1i":"y8sdJyce2LkD","DHJwbuvbFywo":"5q5UgdS4Hq4s","LIizyYy5CQLX":"dfFrvZC2DdsU","XZDr4Ejz5LbT":"QVfRPyx1wRIu","K9mmuGq6P1t5":"6lbR94t2o4h6","zAAz1f57bt0m":"Y9Wit952QDsc","LtSKcY0etagU":"nR5couqTwel7","7AYwtSdSRCIT":"GYFQ6jmQUODE","7TUEdibW9Sdf":"SR749CUfKMMO","dCCYi0awqOaz":"sR4zgEeNGaBL","OinzLEdouT4g":"h33xRd5rttoe","DiXMD2fJRubH":"ZOcP2n1c9lGK","QHYUr4iigj9q":"WunlzmAQF331","7aRr248asy4B":"8mwIxyRSDf9B","C5pYvf6uI64l":"6sjlVnvXjYzv","EEDFrcvHR41i":"cZK7JhIJtjAx","idr54KblXmtc":"1ju4uYI8KdNA","qljUDHkXX5eN":"6b2ldkumzTgV","253SDZKDfbY6":"sSxMHAQ22XV9","B6N190GlTC7p":"1n7ko6N06Trw","W3JX0hNWrlDk":"9Ea7iuFhEpeH","e80q8cNahECm":"jmqyPgFN1lSh","eDZSbRGnv57C":"duwU1tpRKQuy","CRHC9Gz0NcI6":"doc4Ie3yO1RS","3SIM7GN63WWF":"cMIjFci6YP1t","Gq8xvsqQnX8k":"G6M9wZgZrfcW","jlFaa9k3SPBF":"EaFiHYcPOabq","9ArzD7U9L9TP":"NhxhR7r7nF8Y","YcGtbRAJ9DHT":"FMnAaNVlDOqB","wjubqPqitmCJ":"VPdEZqbm1k4f","YGMDDPWShfjr":"MlETHem4E3tD","ERntuK0OTJR4":"KifpIW6iTtYd","4YsnpeCSEKMP":"um4qk8wFeMie","p1VIB9MGys3W":"mRKXoB7BWoor","aT0bYsVNk7Sh":"EkxRiLFvt4bI","P5lxVA71aoKJ":"DZzauN1UxzpK","wM9hDP4jFhp8":"r9FbxcDGkt7z","DchD0kZgE6gB":"mV7EmTxRPCbX","SWenXaMm7oi2":"qYrypQWul7xM","7MTTt8vs4Miz":"rIZ072pQIh8i","d6yd8tv4rU7M":"ZTRBaiOj97Ip","uvhNxENLyFes":"KhV8MZrWhYi6","MajpSzj3woAN":"zMwgjET4bMSi","XgQzfzb87Hej":"7iiC2vggGvsN","zVVXswmddP8s":"GN91UeS6qpSU","gusgJzxygTK1":"xwckpjMte9IM","T9a7OtBNOXDD":"dqZFBMPCBHEB","K685mgnj25fw":"7YLt3XWEV2Ho","2yP7gmkbYWcv":"cAdAOl1NCP5y","3N6SL3HyYeai":"0ntosLfuWb4f","1clbcx5hNMdm":"o6J1UnXy2Ix7","re8WDTBEEgWX":"MXyVclyPJlP5","K0PZzpmlPfLq":"ABwBYXif7DdA","XRA0KOD6kTUj":"Hp3fxSORLjmH","8Cnwdicpxtuy":"mECrmQNNDgXP","IiJkEOtvgXwt":"gxcngSlhJx3v","HaOKXQO1dCGJ":"4wSkuDckKnL0","kRqcPxPqXBUL":"LQoZhDcg8ZvA","7Ik5qIU5gl45":"s0Q8d82EwBAy","FQ8gDJJF7Cam":"SPhF8PzciIGW","qiWXXQ1dwQN9":"8uBkCrrMlIKD","ysf2vtmtbaUq":"F828WA5krKKP","VcBGKy61AYfo":"S7Ua52VKGA9v","ZCfSDXg5WXha":"bnj8zXJFRSP8","AljIM5oPqPUV":"eTRxDs0DhnHd","FscnqpGyyrIH":"MUiECdX96EBh","bcDCTAPvNGHa":"2K7XMMJ5phhf","2Z8eCY1n7FGV":"XlljBdpl03s9","ryKGwUr8TBMA":"5OHkPoNNJEnV","aheZO6EXTCqu":"pkdZ5D3x5roF","SKoOs1GNusQl":"h7F8dznivFdg","LVIQeavBjuUd":"iXynvJ9S9lZP","y6k3uiNPh3BP":"QsiRhz5oLqGq","noiX5RZHgu9q":"q5zpjmN2Wusu","gToajwhzom37":"Fby75oFrfiFS","KqPu74UdKmOr":"XwM1QXnj8OAX","vZYNJeAXGQCl":"qiskE6aZWHJD","rsARbS1AoIBR":"vEZB7dv81cZb","8NlmejQyJwoW":"cYoDCKS4hQz7","2khkNtB59GHP":"ki2KBD5jzoKv","eWUbx3xNvz2O":"IsiIzOOvEXcI","ufP1eauFevck":"2BpO18hYdAzV","0cgZ8sbtGo11":"GGDDjaLSS6s0","dc0faK3WxiHJ":"DzQg0JczvLdq","A7757Wl5eSb0":"t6ENbGwjRcPp","KlYrPkpp44F0":"D20SdaV9n8c0","XircWbLfjtHE":"qiiszcaktLSA","eSG59NDtqsyM":"aXwLb2pt8bzU","h1L13s53GLrC":"WGbdSXIymf4u","2xeskmsqXV5Q":"dwnefmV82Gor","luk9iQheKwNT":"noYOtayCL0pD","19kwX68YSitD":"Mqk1pC3yGegD","u9KgpWOdu5ft":"eC2GkFkc3HJO","nxbKBDQz1OF6":"V2DyS2RZtJG3","0p599TPSqb9X":"VlgcZtQ1QF7C","XDAcz0dhFoib":"Vgk6UxCufpHk","vaoJ0pvyeHkv":"bIZavRMOD90V","4zcbaWKSXwET":"mKJvftQNWahl","wkM6oboxz7nI":"DsZ9rLv96FIz","uZEZbgNmgSsJ":"gNxUqUYVYsnq","mDtN8MhiYEQN":"X7Azh0EgQ18m","6RLFFSesxPwX":"bKeXFkuNpYta","m11W0vCEYNvK":"7RC0aQbTSItp","IdHS5FHomdnR":"8b61lFfOhgZW","dOhdV9IwnwLu":"hxH1BQGUNg1Q","6pPdpW5xMjrl":"CVR5vwf3RYUQ","rNjetKHvpL1D":"RypsbPs1PVMd","zGYOOjytBPSU":"n3ty6GpUhenv","50YBqAntg6qm":"IFlVBHI2PTSz","xWBGyolvTsaV":"yoUn0Xhlxa0c","AraimORELilg":"QgITVK5GjNsv","K3yYE0Y2uV3b":"m44PYoDraf9f","1GHG4FJYxDPT":"zcD4XpGnrNrk","AUhT3liT8tjU":"9fpSKcsDWFVg","CoTKfXr0ibEZ":"yxBAtiub9xmj","qwjplya538Zm":"8ahFesNahe8B","OHL1h0qoMJQu":"JzL6a6OOkZea","visMrKMTSqEB":"Hd4JuXF3Md7c","X0yCgk6wYnpm":"9aWzkRIzbdK1","yobUyURTspJY":"mUb9l4sOK3cd","BV3Deia3d6uV":"2xiLypf6DUJl","GZi7JLABbLUO":"Xd7luqXMFR78","XzsZAWjnTHeo":"K8WKYlaWAsRK","y9PrwaVHFn3m":"AUN9Cug4L7VG","PzwAQfgk28NM":"cfyo0GL0vYda","5f7IboX2oVdv":"Q5qMtOCk71m5","yolL7qiDN6AG":"IFuVoPapwnSG","pQ6C6JbUokrp":"QuRY928tfFhH","9NUwerE1cNHt":"4Oyq2DvmxOVn","lID8bfPmf90C":"rqi9KTMuYiwK","XaG3WoNvZV3n":"VobvZ9mG4b8X","IeFXdXDhc6Ua":"kbkDU030emWv","7b4J5laOHYE3":"tmOjjDmoRPTr","o7qa9WJ1uQY2":"hLLK5tOk9lZo","wy7w5rW2fdHD":"9yCjyasvnESc","BWnzwm6MGQHc":"w1J9RjHB3N35","MDHdcWWRQgtG":"yFyYE4dA9OtI","sYAay25Qvh1V":"UZMZ72O6T2yj","OKSVerY5vq1D":"ezGgViBT4twp","3eGNRdJZrmAx":"dsCpcyzgeEdg","OAiz7ckFcbeP":"fWEOFOq5YPXW","0jkFEH5qJN2Q":"TOXcL4CriqjX","gjVmsikIJrph":"ye7Q6o0jOkml","Mjw40N4BckgK":"disoE2BGAJXt","6ZegRrTuOiPo":"Tl0HCdXf4kW9","OLo9um2ignzI":"qCyYRFukd6N7","3Y4VNmPapEK8":"NQwYhMEBbu4J","CrCWdj1Ew1Ur":"ZV9O7dOvLZuk","X9bzP2dmbCRf":"Io4LSNq2jfKT","4keA7RlGeXnj":"mvrZDECEdLEZ","PhzlldzxX4c6":"i7ubeQYHvah0","YpWfkiUvaHH4":"NH5WQpwNT768","FB1cfi2DY3xc":"xFiJ8qkg3DBM","VXzuqV4AmfeS":"WEik6tLmSFzf","Ak0Hz5H7W2e4":"D4Babz4QuvB1","QFpFc1xG2mnz":"8AKY1w1TsvMf","cD1czNiq0aR3":"e2ZDkIf8wVUh","gcDvJLOwMoVM":"y0miVDMqQXJC","90urgAWUMUgB":"SB0oM4bQgVCr","KUlmMpYKcKlG":"X0y3c1us2SR8","f4am1hTtyofg":"GCsDIpJRV6nR","HnKEfQEBaPaX":"ZusxQX9kxus2","Xul5cuIKDvSf":"bD2WjPz0xode","Q9FnGtKbgobe":"gbVjxoRK0qb2","KV52WNrXprnz":"B3WWWstPcJxD","oVX4CVxCsp3D":"OeabyPlbbyRv","03cIxlsUYNYb":"0CsgDEB12hCv","4o8IGqSxTWyz":"utauxSrCXdNV","noduHGbFV1YC":"ZAK8qAkqZpnk","LjAx3KIcKWLN":"iI4IlZGLzbZC","cYpV7fSS7AKp":"IaqnuUNeX23r","zPC2VvCVLJYS":"nuKUQPvP7sgF","LiXgb6Hq1Siy":"Iqs497zgbGWg","9vmsLBuGjRez":"jCQnJUdy8Nfu","v9Y2zlLA6cLv":"SOdBm1qzYi3W","d7ZXJCJWq9lQ":"WdYAa4qw2lSR","QswsI0L3CmaY":"w8IGgtifLqBU","r3hDG377obah":"MCY1lbiCNh7B","lec1NRd7vQMw":"wnmaVdaZ7K1D","LF9Lejz8WgwT":"ytQPoh6cZxtl","4FfZblxhmHbt":"4X12JjS2Zdzr","McXymK7lnSKA":"XahC1dxQhxO2","EFIjQgRx1l09":"gHoV71NwJuNn","zrUVTgtF8nS3":"SmX6CyGjpb2F","EzQDJ0RvztMI":"GHztic8s36x8","XYcD67Kc0UMa":"nG7pxUdLSpc7","y8rSdByII0Mj":"4f82zA102UWX","TItOAVwLdwhg":"THXuQsq6CZNu","NqsvhOfGyYwP":"Zm7e5XKr5oDh","wbghdju9fd2R":"crCKs8h5tDEZ","9NUZWmzZI8FN":"8dSeXfxvE6SG","TAbZnOrzvzQz":"q63MfHRQ61AT","oQXQQkw63rz5":"5zaqNgUlnZdd","V54TT5nWDaLc":"c1edxJqhzyDs","6IGRASsLhpzD":"NRvZsUotxpKT","77R2NlF2fJ8R":"UJ3Rak3gVKWm","6JmUZXXOgeyE":"4sNVfNgrq71S","0EQykcr8OBb0":"HC2qi5cCzpgS","GSzHD1cHDgPL":"iFsBBBtvLGXs","zihp0hrW9QHC":"kZvS4wHawbF2","SdisIn9qWr6L":"8p2V6MZCR6gm","G440aXuIUWRl":"1rMw10SqrgMn","ocxgzyKsSjIi":"2fZZSJnpWyqW","Gve2f2Cf5OYh":"RRzB9wpew1Rr","KCGQVHahBxH9":"PFpGrv8nVHEy","L6NGayb1wHPG":"a6yd6Mh0dkjF","19KAMekTpb1c":"d1lVklKFEd0T","7wbAf5B3gsk7":"LhD7IRpAD0rI","3Lph12G7nS12":"NuC8bvdOWpCS","XSBahjIk1JyH":"uBf9f1dy2MoQ","ep7YyQZjNOBK":"59HCd2Cvo0T9","rYiOkUqWelEu":"wplHA3YsazKt","ezUfsHnVX0Tt":"XcPvzGMXl3YY","DRAsYrIjfBWf":"vpQEWP8SLPTO","P0MBi3lWWJlN":"RfnbKV1JbiPS","A6j45LLgC8lk":"MzTBBudce9h6","rhDNyvMVKiMW":"ZK2bFHxZF5dt","OT7jN4qLJfAL":"hPk7HDgx0uz0","Lb2fdvdJJm23":"jxHeNxL1ssdJ","4urd4HuLgamX":"XUU8Xdojupl5","HrSgWKHmlPZ5":"o15XvWLUzHCE","9filqxDltn35":"HRbIjukGLqPa","QXHSlJX5V2AB":"pKJcvQSjq65X","JvGUwzWYfloE":"MTCL64xWQgGY","NuraNDm5OIkw":"yBMdmA1SeSzF","83LP3yoblQRF":"7LDDNofdBfiU","rEgHJfpN1wep":"gJkOSQC5bgCk","8bakPJ9F57aE":"mBXs7ZwOkY5x","VcRUejIBO6BH":"tyNkYls1stnF","y010x7uYq4ds":"i6BizledqsFm","FZGyqDzt6cKs":"6HBwv7Z8XLEV","5pJoQzHGARXZ":"qVDXyhugWkue","UO2878O3vyRL":"GsumBgGMuE16","fNR6PUYSYhuV":"dzbFr4JEuuyH","WIHEcCd3oPfL":"8JfuV50vFIqr","5LPtbMAU9X0y":"npi2mjSABhIJ","yW6cQlIeIeYh":"roJDxGl0rCjl","2oG2zfyQWi6B":"Q6DvikB9r2XF","Lm1jq81mtnuv":"Qn6OS3sqjXDP","WoLF1E37cVKh":"wOw8OPsO4kX9","nbtYdDUNYix6":"rFvWLlHGsuHD","BkCjMjzwsWOo":"PCzrbHSURbwS","bH3bLT1kG6f2":"duNwU3Z6Hofa","Lpi5GY35GePr":"ZSG3OKO74n4D","pWGceDLjuVci":"yjc0o6rHCAPW","S8koQegLaIqV":"QZqbWjBtgYyy","TcCxX8wPLNxy":"CTCCw4eAWz8o","0NxseSCHTFUf":"WgaGYplqxvge","TUJBWa5QyFrD":"dSvRbV6lpp7r","ZsQC8Z0tStaY":"HkTykGWfv5vv","PvaKldO2DQ4o":"EvgzcY7FdVdZ","gDnCxhEOiklm":"xIhFjfYRAvWI","k54HMWjVwISR":"2NEpESlOkvVD","afEDH4BX3p7C":"wiMgy0eANn6J","6oMcEkcqTjZU":"Q2poFYp8ktOd","lw7BFUOk41hT":"r3Z8YEJMNAC2","0h7B2I0eJ1Hq":"7vUtkcR8lKDe","Y6cHP6eCGFvp":"VHASxoC7VoHT","FMiT0duWjftR":"WYSNZDQ4gE10","pP2g2HuUlAnA":"uYxCL8bLfXkI","Y11nnuWXN6f6":"hDUub1Hko8KU","6kTmoTz2nXoa":"iQoiJyDI4aIj","Z1Rnoyr0yxch":"0mWCxQzeNbMZ","6bUcpsX9Mec9":"8QnBd7IvVUyp","runilUuyKTUE":"PSCS0mTQPctY","ef3GI3dmZILb":"xz3IxlOINbzu","d1XiKvXHpkO9":"vFnvLPgwYPoL","TzUwrd263Z7E":"bhZQ0SY0nyp8","DZldRspniCWu":"iPy3p5SGvK5Z","wfQOjq17S5zp":"N1rzcKFkAKvQ","GYLiJBFSWncy":"1emmjVGGhCHL","O6HJs6S0fLSa":"nbci4MRMj5PG","3tAIhLWmffaO":"LOaVj7fy6PRg","u5PXE9XtTQ0w":"0en4cQZYIEPA","SRVNhqz81DAW":"tQxc85WyfGeT","JAbcM1U20xWO":"T4DhSu7gx9Sm","UWweJ9w3LnAu":"PcEY9Cthzxnd","Ftl2FmGpFJf1":"lmKg2kDkDXXh","xQTbxxcJ7ziE":"n5ZvFbfu1Aka","7wHmgzJMTtEU":"pgL2Jz7hvxqa","mbioMPbedJw1":"UcVxrOu7SFWf","WU927eO4vMJx":"NfFbyt9C8R6o","lZQZMnjn0mD2":"GuPEcY9oXQk5","w06JqKfwjvN7":"WVI3ThPiT2JK","kFbD1ecrcYVE":"SdL6tqxQ5mX7","S4oNaOG4bjwo":"2KrzctKjJOOt","0AGos3mXnUbm":"6Rj4WouM4BSt","ipOQqeuNUBDi":"0FPZiwRFyPpd","oI6JS6z43I95":"MCOeuEAW3Ax2","dmA5nfkyKec2":"2sM0NufXTnaS","QG1b59vZZsCh":"v7Vqe7bHjEOb","ELREp2J7au0k":"R6XxrVNWNLVV","04CbrtlzhhEL":"wPpVVAbHD4o2","YeljvFk6HziB":"WENcOVuxaNzk","xq3TVAFd6ylD":"X1zJBFatA5ZP","H6WgpzkT4k7d":"n8mzk1wghKT1","5SKqKHXrGfdP":"fww1KHp7bQty","MRsmV11YjYYl":"9Ri3BB3I4Xhq","m7fHsPXgg5Qd":"vQRt0o8rXHK5","bfAZz9y9IvlO":"sO7dBFrCMGgJ","gXUGc9lQmXe3":"HMeiAvqmZdFZ","khTZfhWkWKAq":"40apskxyfX2y","bZFFWyfaH4fj":"HJ1Wxppd57ah","umi3yFSQZ0R7":"LPQsUADThlyE","ahobnV9L8fZP":"6jJoEOkLb9Oa","TvuUo6FHXPeI":"1ytRp1D2pyFD","etP1zx4Ck0tF":"gz898j7nMFHl","V6y5yt5Y9M0C":"FLukqWqxSRe5","UCoYEGmImGcM":"egWs9K4JSFvR","hcgxSKRNUR5I":"OQupp8H74SSd","chENGZQ1xZPf":"UnxcMFpVnv2x","TNkOPaOTICi7":"OQ3ckxYbU5Hp","40RMpX1Pt90e":"0G761S1Sb3Cq","vIwpwR0u9F8L":"TCXM1bsMZfEj","1bdu670f2kUh":"dN6u7vNlPBWQ","ah00jfwZhuYs":"PK49CDAZhILs","zGxaxPXciY66":"FCsiTgoWc8Wx","uBwIrc3CHMAJ":"wxq8QZ9evtgW","Of68izh3fBbh":"8kiSu8VQPHs6","aPpkIavV62Np":"ElHUGDGmxxsj","CHEmW08vSBZX":"mx3FyaQffZI1","lmMnst3psdkS":"ydJByJNApy2m","HtJ3swNZ0Rrf":"CRv4zQXXxmj1","lW3iyhX1sAPU":"DCPlFXUHLDrX","v87qeCEUN0Of":"2rzLjY4lzC4i","gFO5lv7kMdkU":"iHYCpRkpFhJS","p0s2sqQnXFxP":"V9yrgymJ8RGB","Maf4TIYJYyXw":"fwhtV1cSkwSG","dAZxHwvntLtf":"0iAZr19k9af1","9om9JZJfEF0p":"p62J7Doc7FCf","yO5PF3nJpMHN":"gNKiIMVIs97f","oTuknzOMvxBV":"E9TosVULvTh8","PmUWRewFduV0":"hnvTXmIHUQhC","E10yQae1qEMl":"hXko4GJPi5aD","UT8ZksdCd5Px":"Mj8RQtD5VMaO","euLzG8BTkRsI":"y0udza9EHJ5N","QQMesvbLoOOG":"bCcR2BDLcvW5","tIolSB90bicq":"xJljQkR0Dcol","oRWqAHwobRJZ":"ele8fvKBtoja","wGtcjyRxXAcS":"fwUVhkJkSrMo","Po3m9HTEWOAd":"aBsF1MnMeW7F","G5x2vWdGJXnW":"9TqCdkCn2NQc","Ny90WrlnlDC5":"RMfJX3KpdH44","LKrb5jRVLkXW":"RBp1ji2FAkMu","Y59NXuflDfkq":"hyyOIPcPZA9i","9HlQGEnnTsj4":"BLVSwcMepD2H","T1XyHoQ2PSVJ":"xfZRcDWnV0kq","GuGcXzYN2wPc":"WpGQB8Bv0yHr","BTUeLnBhRnil":"D3GA8zv8PRTA","qErdFG04SRpF":"2gDGQYV2GTH8","rjbFRdqSpzNf":"AfnWzDmvM4B7","iBaqkrvtS9FM":"Z4bA6na1Ymou","KBOEQrQEQVs3":"nOoFFkRosB8T","fyNs3eHsgbGq":"pQQ16VYfs8dT","IE9ntsrRQ8wI":"x7tjmZVWFkXA","tY1QDRTwRKOL":"zHIgP6DOdOnf","1Cxt1s3BH3pg":"I9MOpqkcavqO","ykNADjhrs4v6":"sGHJR5A15Ra4","bsIJEEyKaDxT":"AoqzXUfO6Bwr","te7EKilqb3n6":"3jadHFyKfLn6","SS0HkokQB40u":"HhQuOnvXlXDh","Dq4oUfg19hWG":"gBtZJVVUvY9w","jiIlKuMG0SDn":"eEPMbAg1O8vo","wps7sYEQG0bQ":"8Oe9d2ulSrpq","sJhY5fZ8ZHKP":"Q5qDlTRmNQIC","IgIPRBud740G":"9N9JT9rQeq6L","pcuDBIl2LjgC":"FlxlahhFJmye","hIJ2aAEgQBt9":"y2ydPHeH4rGg","UeUa6Sas7uG3":"z2ad7gWChcu8","yU8YCiwk1jZf":"Dc017vsDfBaQ","ZcRHNoDGkFLV":"X6lxrdsQUZmF","YmsxLD4Cyg02":"qokadAPzKw8f","56VcSA5n85L0":"FQM3pvMDsf2k","7DCRlnAeOfrM":"r37bLspO8lRH","hqAqTJ9QdxvL":"cWgg2yIA4x92","mGZYChHFWYHq":"yrRVQwLv10s7","N9ec3zM1tuE6":"rqtxn9tnCK9e","POjIGwly1fVH":"xCofEnylMi37","PCskoRKe6Zvs":"wKtFn61Ti6NF","xO52jC2DlfLc":"IIP9hYt8dhQP","qDanJ0PbMsz3":"4rTPc58FdKu5","7uKJ4khlEqrC":"dp8Cq1PP8eeV","myoAmwRkzGV8":"lZsvlKRDvwSX","HR7tmfw2vu78":"btkiAHvVpf1S","4eVMJtRUlp13":"i77Aaf5OiYhk","WDGAYhRjMFaI":"R9QtuvQdhCCx","amOSZfEsAlnC":"zUzeEn6WDCku","HjM7OCBi2t4x":"LzxuEOCfnJlq","XjMCcsGvKbKK":"cLD072n7ntsY","5mw29PHNFqgo":"kL9mqPVridx0","2X3SlLCurFbY":"JKUtzaf9VTxh","8P6mrJtzPCkd":"cw5wdLujhu78","Nfvn72Aj3vtz":"8PBb5zFVqUO1","29dcqRx5s1sh":"HCEFulpbMJBq","fLZ1FONeVzdN":"5VYfogh3WCN4","IHWIgyoRk9PB":"IxT56zk7L6Aq","byvxyK9UXk5d":"hbokJ300elCb","SjyFMCEwOI1B":"PIbiJnsoqiDm","q7BsrNRDrHd9":"26vzQucDB208","mrsTWZweY67W":"GhhgIAFvdg4V","E8YVJQf1ooHR":"t6lJ3YleUKHB","D5cXRKZ75jUp":"u44UgJMx0QYC","YW6qaPSk52FE":"uBARfwGTaStS","Q5V3ztbbzZeB":"ojWgoqLs4hPK","6Se44FrHgdCR":"u2XOIPf5sJ2z","LYZm1tTOn1mN":"CqatBvRtTtIq","F13SCtNTNjGw":"17kQVKiudZJU","ZcFglrGCC7nt":"7CV2JHPGdyMH","K27nzQsdl2a9":"o7tj36ErT4pr","ugXnImfdngID":"nvHmQiPBRHsR","jylxxb6kUYhb":"XJI7Gi9oJZim","L2oI29GflMZz":"LjPlfIrR4n46","VjVcPJXevtCu":"8rKfz7K3GdZx","DLzd3BQMgR1K":"8BX2v87rz4rI","bMd75khRtFts":"2rDJYtn7KAkJ","vm6hrIEHSiiC":"JPlsXn6xF0Sq","fg2T38LK9WKe":"Dm8DqdFD9xzj","4UMDSVUfI5H0":"2vZyupoJPYnh","K0kMWi5mVveu":"rwcN3TaqApKj","dT3VKuhOZNN1":"AwNDRwwKhwFH","aGudnSP17eL9":"evLRec8MPExj","I7i8ALmXOzWB":"CrrLKDbGeJKn","QxeCuXVtETUy":"saQu8Gkvf9j1","KpDLloySIitS":"o4dG6cZHwRzt","Fl3S5rJjuspT":"zg7inawivpr4","kDo9plXJeHJV":"6StzOKIiyU08","HyP4wP1Ho1be":"aRAMUPBthIm3","hMh6mA5Rd4gs":"4M7s8aUbsa1R","qo1Y2RocoZbH":"KjLK8Ec5Th2v","CGFs4UwTpIzp":"pBug9t7MpjYz","3YXdfByQqLpe":"WY921QncACz9","wJBKTOHZkCMm":"OgUS4sbknsFM","pRG9lDA5E3el":"9RVk7LyVPZ1P","kxf3p1jmUuIb":"BP5x0H6dSt1V","YqnHu090F8P2":"6IRlfak0sxg1","dR49DAJewBxX":"PzjuizpZYlX0","qgGnyXFxZd6h":"XRc5Hzv89Uoz","7hpuTAJ73HFT":"Lb0RZ7VEUOcF","XGK3m7HgAF3a":"mHptgFz0cl3n","R7NMe2cLeniQ":"5yerXzadzYrM","gon57xnazY8E":"d5zaIbBoBVf3","Giq3h9LDCLNX":"FLVLwiX1ROp1","Ki8EU4PQPPbq":"oUJK7BzJX6Bw","CDy4V48CrslG":"XaKyikEihoYQ","ekHbLhi8pHUw":"Kf3ATOTaRZki","lYYs2eamxdcw":"0QlyCvTLUAIA","MZRIY3DXvVr3":"2HgFUgwmZ44V","vMtNfoRIXH9V":"z63ef0ojYUTS","99uGaB3Wf5fq":"gR0zCJALGfw7","lLlB4YZD3d2g":"eO9Jo55Pi9cC","8Spc85TeklQU":"TCwMfTwcFMXq","KEVpC0yqDDdM":"P33H93rxrbrq","1O3AZLZ0Dgmy":"LLGFmfjS99bQ","hK76pj0Bwf76":"O6L3JdTTttD3","xyxf558NZwPD":"IWiJ1X0OkOt1","Rh0bc23l2FJt":"5eM9mlztE1sa","xXgbWX2Y0R9F":"Jvf1v82b3xxb","cSBkMiUxDgUU":"x4tGukngw0pV","HteuzCQsh3Tl":"qJfdoX0lSEaZ","SqrH5Z5qk58V":"xvQIxxtGdpGu","u61qXcuCaifv":"UMWRv9IAgfl4","5sEMWUYnKc5I":"tFwEysjG04DA","1izfEqS4rBDG":"kKas2b7j8kFB","LWrsqjaYxKWK":"D0pB0vMGKtJn","TKTZQkektGUG":"lCVOKjxqVadF","LIIQi8GivIFu":"kVzHFt2hbjJY","cToMDPq8TQoD":"0TyBPIqvQygZ","fyzknP93KmTN":"MdrQazcq1vUe","Whb3EY8zI2kU":"n19PArMfTOTh","X0aBalXUoeuD":"b5zmlc0dqboL","jS9ptpEhfSXF":"D1IXuC0xsHuZ","kT0h6FFoVwPV":"A0zQElM3yvMs","Q6SfSbciPY9E":"1wWyTDv16MlG","hI7u7a8FINIy":"1Ct2LAB6Qmve","FukQBykQCKzc":"TGKNqS2L0erv","cCPJcneuX1xr":"XZFvvbOAvwIx","CWseECfa866m":"VVvSQWagVCzh","KMJYDVOkOjsu":"dnzDA99Ad6NA","gYzwIdUzsVtG":"c9j9X1bM0NZf","66WefXeuwbhz":"h8ZBhjwZdtyu","xnLR3wTpGt8e":"DRgQIoQdcETt","PYXbtvBs3EYi":"UoJMtt63PHtK","KSHSgOwpqhph":"QDrfTFXQ5kxa","kNlrlCcYkNxs":"GQUGDlzd9zl7","NjwWOQCJ87kA":"QSlXsxAVfJoq","GtXV1N9R1Bo6":"2kVFqLwMiHvr","tmvstIiEMaDj":"u07ImZFH98bQ","853RKylKKksw":"6xu92rWNFKMa","TG4ht0XHK7ks":"jGsf5I7vNUvZ","iAG9f8G8r2lu":"5gUKMetuH3q8","hkqRkSSzBQmZ":"iYsH5cqL3BiA","UpcR7p5hRqvC":"nERgk85epnUC","CspNv9Mt0j51":"DqYPJKyRW5I3","zZmtbSQFgCpv":"DTfsNQ5TYcgV","hu3iSZWgLHgi":"1hihS8diJhZd","GUaOlvR7MvFX":"V3uanQ8RwupG","heEvoCD5SIWb":"1DaM1JbsFzRc","LhWCtE0BlYiX":"A0eAjROYQ9js","rMzoWwtlNAcu":"tgZEhssyjtX6","nxP0mzBNfQXS":"fm5gTeUz4h00","wXl7unry0uEx":"gYnR3pIMD3GF","ZqCLiaK1tpxy":"QHhQNoKr7ZOJ","BMbBdfpL8K59":"4FPXAH8tpRxZ","DzZ0nch0dZZn":"7Ml5ZSBaaB6E","7ukw2bX2BROs":"fMjUVMQGiq7t","79nYoCVwZZ1Q":"4T9mN8N6NAHy","sy69pFG3vXHY":"uowrCSBqAaon","8AcHyrfuR7od":"0u6s52nKqYQc","CIiWhhKymtHX":"49ErMSr3GC7c","WFvYy8Qt2dVy":"jbdY2Fmzkkc3","wyyTS5boGBex":"SGaw5dZbmMME","6ED6Am90ZhBO":"SqHb6G2qPYkW","ksHimamDZau1":"4WVHbBmhCEp2","Ct27EpX2nCmp":"SV4ol9jtH47M","3GQlIBgMKJKV":"Eu8ZQqQbFamZ","qpqBebOCnQak":"br1Q6kcLfTPy","E4yRcsE2t48z":"3tHAXKuuEzTP","hCZq2fzt2I8k":"UYw5Fwhg5exn","9Hm83lgSFHm3":"aLTMnrkg0jKB","5Uiflh30hXOE":"HG7H90sicOU1","6Db1teHsvmWV":"lPYpdm8XzoSB","JuS1bfpYDE3n":"nM0bCANP6VlV","sQgdwv8k3eId":"Wi39yz0REjXw","m8sBVdvLkzhd":"pHyYz7bPWcWr","MenkTevZnRgT":"jRscJwdigdLV","BvmkTHlKE8EE":"xRzdu9KdJM45","UePTwb7HLCg9":"S4Oql1KMPTVM","DlVcZJ8vmG5r":"E4QFwKTNApg9","mapzVOY9W7wL":"26jKOBtmenmJ","vY5MQwA5iofE":"4e95NY4hwqEW","zYIklCAAOIGs":"1dKPmo50cRiV","yWuukR9dSkBn":"SdgnUGiEQdod","spHQGwl3fL1b":"xJDk75ENdnoK","ofhWSboNxBgS":"Ty8yOKZW2kRM","3m9enqXxf4Uy":"jzabGz4drB0h","MoW8jAHnmsQH":"spuc7ahXNgYQ","MnyR2EgCQUwZ":"JgAq4ipxqB9j","7R9T2KEBK7Dn":"o1kh0NEn9QSG","Y0MRNIgcbVaj":"nOOQccpeBB9q","npIboMIV7nYd":"6UqTUcStE3El","a6bUGM0Lgjah":"WfMB9MF6sOy8","hyoEHBvJGWUP":"Dh4gvGQ0Hspk","SMksuj0ooQRy":"q6Nxky73hvuE","ycW6EPpcLlYn":"6lnocPWhRgFx","YJ9irH5pulJ6":"nm90CeznnVYa","Nyy1ybZHid75":"pY9U8LSa0ZSD","1CS7dto4zjlO":"zUPKNgY8jkSM","Ogg80Q1Z9OB4":"EL42FR6xApC7","TnGygnW1tuLn":"3ItlwpX3Ohe3","f5XyaQcwnQjC":"TySRb8f31fly","HqfbKQMWb65Y":"P1n06Rn7ooRI","xHljjjrixQU9":"t70hvWN0N6Fn","sqc4PmUoJnL7":"MIi1mqqMP3al","nEgx2r3UmDBu":"BJEsQBXLF5J4","MR5PA3gbpbaD":"OnC7RlmnNgkp","7bYZBld59wQU":"Io4GgPEKBnBg","5o7K3OQM7qYw":"701v9wu3sUYc","z0iA9YPLDPIH":"kA51GyNsaAti","VrhrLrsfeq2Q":"JRmmX8uYxZJm","ijDvOOGOexJH":"5m39dNrmcQRY","3cmT7kGY7iU2":"VQqJO0QI2SlV","f2SCepImINCq":"vhkWUjFS0Jok","NfR5hQL6Xcx5":"taHzrvSHh1bP","Uc02DY2zLie3":"tXhQf2q2AgaJ","gyFHBzTUcqUS":"xH5g9KJ0RoJL","mSF0UWYz11rz":"muJGa8mmEfMR","SHmmrfNMlhx4":"XrX8EiFvR1Pw","sjtcgZxfDUXP":"ASeKFzcLhXpz","Cq9hLpxaTcY7":"HRYHFCVd7KSX","clS4PLxzdpnQ":"IzTDtmkXhs0M","R73LNXWZrktn":"xZB3kPoCUGGM","QC2NMDvYaUYb":"Fy7OSxoximCB","Dwho6fFMSXS6":"E3YgrdVzk52F","c5LdmlWcCZLF":"NgXhUEXQ0zHE","3HtgLQ9Vsbcd":"cjhMKHBrRKAj","42D3NJt3hqYf":"iySMUjSctlBn","VncaP1OtkkHB":"4QCQMIZYWs4I","I13XxJCZX66N":"QpjUiFww0RL3","2EdclytddE3G":"o829nmTO4YxZ","YimLzY0c2a3r":"CogMHRsD8tUn","7o7roPtFqhJi":"toDOPmdS91eo","fGkFyDTu69EP":"0ci7SPPRMNVf","FSgbD7xsbgPE":"5I7HO55Klaww","sEgHZUybt72S":"9fJc0kbH4Y0r","k8EiGn3oc4Yw":"HTyAbl3qgxOu","I4oDBKjwyqqD":"g1FyhWThHiZl","L5dAtXd8aZ19":"t60oirRzQutH","c0dXVg1skEUL":"HC51Quwwilsp","nAoWX8HgkiOc":"p41zMWhVOxsc","ioLaUJ7GVwkf":"OYFXVNtTLzEa","aWRbpgS7hVYG":"fsfO7VDXCaSd","36JVlY4Yc8rn":"yYHq9wBZddxp","mAQl8jk0uxLK":"gKu003AnNkUU","zWhZQ2RA4pi7":"871R4psYqBUQ","NxGWLoypqTQQ":"PKQzZwGAj06C","Ts3fJkTGtIbm":"Emv0FckPWQbT","YI8TWomQnsEN":"KseaNiYH9NvT","oWY8M8Mr049M":"U2oGFsuwAmFx","P0382JjPcLXY":"YLAeksu1izgJ","dGQ9dzbzsGTC":"Tlntz4Y6NHo6","lz8aMuac0Nvw":"E3k1iFc0RSll","qdLQVUlqYXwp":"j3GgmSb6EDTu","gJIoU3UbIfoA":"9QCsB3nzX5ZV","4IWNW1Q3Fv9F":"QGPjxEctFgaG","XOrBD04r1ECZ":"dTGqY1qlGNEu","5fa8TqfuQyuY":"h0HEU2IbxaCq","sbtBeZtBuFO9":"yxKNqxOV6K1k","6PsBgqtpou1d":"bnt0wCls9wdt","IvdVI6EAbiYk":"IZ9NPWbrbRvY","VWAENChUOZWS":"lPETBnBtr3rR","XzVR8FRdkhWQ":"lAWJS6Pq0kVE","MxnZgkJOYDh1":"IkNvx47sZmUn","41nyBQTcVlV6":"uQxEab2bwT7z","breksiU0Eo6Q":"66JmvVEj5NHz","93Jd8b0feLlx":"d32agr6tpvYJ","YPqYQv2iUp6R":"aOZsiz2tMTbx","ezBKdHa0K7B5":"nl0nNtO3Jwgs","FqIbp93wm5Ve":"daPGVU5PZA0S","FBB7T1gPLXy8":"O8falio9zqbu","kG8SAbokhtCQ":"HUNRfO056Cua","lnOqfl0nAoMT":"r7vkZkbN4yEp","60r7zuPH97lN":"de3OTSvueQGr","3OVPNiC5VbvT":"YJAG5zqet199","FzvyOyNioh4Q":"DhqGEHCIj3Rr","nbbeA116XGsO":"xCnboVYMeMMj","XievU3lj380K":"Zr7fZ6fh3W2p","Fo7eYL6y6EVS":"HBvdgFTTNSCr","dhXqXntNGbja":"3Kk1xlcpCa9d","54EFZLn2anlk":"zGbDOXhpAFgk","3hKPYp4en37U":"KsoeI1U0CsDX","5rrr0YHpgBR0":"VNqdTTMtmgXS","f6HSHJxIgWlA":"8Wr0ppDcw9rK","rK634xAqrRw5":"eWJzotInhC6C","O3tv91ehugpW":"AEuAwWi4IDOU","2sYnjbGXdJHk":"oci0AvbWlkXV","dXWAQ634MEEW":"I0PdqxEZ4Zff","ATUmjst5AJeG":"H6Hvvec3pSPX","VNy1YNbu4Tlb":"v8L1oT0WU3eE","NUcEka5lbulv":"EMNkQsNVw5fl","Sqgh7p6fQQZc":"b4YIRrvUVgKB","KJXvYFIwwSZr":"XlMaeUdLQM5n","LFBqZmsBnvbg":"K7EL9tefyR97","pIQfLkL2xatr":"7DNvEOTfaGiu","3c9M1mzbq98h":"EgTftWMaivaZ","K7S9X8qDruoF":"nLruDa3f3NQJ","7uAty5GcSO2l":"TH2OECnLf1GE","qMv7iI7drzU9":"65qIfHEfOvEW","y7f2GUWyt9mm":"hzjX65rQmBxh","CAvfuGDLcO1b":"HfQaJdXlR8pD","yWNzJJxfAuUk":"FsGIuIZnW9E1","u3D3Fe6Cpv7i":"oT7jHYMKAp6t","lZ2TPcRc38k7":"5y8mod4xtNFN","wvH3xo6IYg0Z":"SbOhCoU121yG","JKNM3VU76Xcc":"UhSiUxwQ1mLL","s9ER9ac0zjw7":"hVxgFwjWAU1R","PFWllxAY7iXi":"6AgdUQdGpVHr","jJDlLcJlauRj":"mMOPnQ9JKP3S","8cWpZG2IdkfM":"KNTBH3KkSCDQ","vQfWU9Rw6zbt":"9sPWUUyzRyeu","Ak3Qe89a9Els":"iJAfEL5cO7i3","FouxONmPqKgJ":"naIWBqDm0h5Z","6LE8EHeO7WLH":"6Im7KSNC1PdT","LWG1McGierv2":"7SLVhNmvxXZL","Ny9qiyrSqvgQ":"KEKY0zOQkop7","kRT4NU4H43ct":"nnMxdNn28goj","MKmzbYWddoCZ":"A7Tdio5dqumd","6xSN6njqGPiE":"aeaUuVyWVyxT","pAG9shX8cDjx":"uYCmZb8P5B2H","1jgwetQnolap":"8LRVCq0g4u4C","g0HFuO2PTpfc":"SX6d0E6rWU6k","UOajsM7F5H1a":"LaWzNOV3iOEm","ebNzgndWJ8uL":"8lX39P26gFU5","w4Y8KlvQ0We9":"Ww3lciTPslQu","O7sdjbds22If":"w5cOngZETnGc","I2UQqSZupUEK":"16MDjghMXn4K","cr26DAm1OJtc":"L9FdFysl8JC3","ZJnWchiu2UVA":"BEHniHbt0keG","55kvD5t5PVhh":"Vcyk8wlAgnw3","ZM81v5La2rIF":"k9NK1JsDdIJj","g1X4LX26DrcO":"cHl3vENU4mtE","t7PO7F2qFiVe":"JFq3eeS88qo9","DhWofbiYXQgN":"E15z5OFQCBWS","zqGPW1C2jugu":"ZMkAuPlCRZUc","HlA2fJ3loccR":"2IqCJBS3lKDr","GNEvc9CEXz65":"WgXmzd6Qxrsw","vFPp1jF1RiVG":"Ga5tuiOLpGq0","TvIXYv9iRHur":"Qt8UAn9n27fF","YrmJKTEnZihP":"WHTJ3k8Xvklz","Dqr5qBmOlpz6":"Jy9eyucB6qBC","8m5I4hNnH3L6":"iQkmCAkVVxwS","Qd3XPLDGFeBp":"fg76yDkAv3ka","i8DvfasI10Sm":"GfP9aCoDoyR8","MNHj38Zxqdrd":"0Udk4QYSjgEu","b5qz151U6VbI":"GdonAY0TjMPF","za40RiRGBY2R":"nPmd3JtvHi4O","xQYd7TsEtsDo":"XMkkLYrgRBW7","gHAkkv8O0L82":"dK7lN0Ep8a3I","9i6RaTvFO3zN":"GHDcOD5SFtAl","58aVs89JtF8F":"RtiS8UWGVhoQ","ldVjobpTP5U1":"XoqmWQMJEhVM","PmmgaZotfZCD":"vEwLSTq2k773","6BwUfHWQpsjb":"1lRbihsVLydS","WzElU9wd52MP":"CyTP7vr1mav4","VTV0hCbxqQzn":"MoxuJuySZTjQ","1iKzFXB8QDCA":"yYBniA5JHjqB","T2Uk4OaJdEwV":"SmAZxxhKKlbN","QGPq7emdvHlZ":"puUkkbllIwZY","UGx9kuMn6feD":"F6naDLwUWqsi","6lvt9VAatHdw":"Sa2xMXA1G2C5","jmPy4BQsX9sc":"oWWPIXzJYedn","56ERwKxXixJv":"COsFnx5XSRrK","L0NZFYqvJjZh":"gGFZK7RHJZot","ZDoSLNmWhQXe":"rpR7bjKp7m0A","ZnLrPKBAdrM9":"0RjLV8NT9yzl","RG29h0fzF1Zr":"a4TfIuteczWi","VAT7Ok1wsyoN":"kC01LrkQHQH4","8QnnzqD31W5Q":"uU4hdybY0Avh","EHQNP1017CTm":"QwZrpTijsxzx","h6aRcHFwVQtS":"w1STldBmhVef","Opo0pzcAmwFg":"CeeI30Lp4s8y","pocbW9OrNO8C":"GNhPucGjbKZV","wD0G3uwX6RbS":"QbOMiDobek62","2vKNvDIufS4L":"yHDERbEZH9Ek","vhT3jMp4DsCn":"6hZqqJichRBd","79bPjSm2RhzR":"fSKuGAqktVdX","GMYrxj0KWSr7":"5vNsW7Ob19z2","VBySKtHI3ADm":"zHJTkCMTQlPB","FSqMauaG991J":"17FjREVR4AbG","b2nWaZ9GnVZ7":"SsQyj1LxDjvW","LBWwPzh8X1Rb":"Ou1RZNSRDsic","i2QGHx0omV1w":"wTajIPqTXuUw","GreLQrH7mTaY":"2C73J7OKvfga","OmIvCBJ7k8Mi":"Fq0et1DS1Nqw","QtjgrRgP3Ohh":"6aL0Ef5jKQUT","PG6An8vZB2Tt":"5SVVTKX5h2qX","fj2FXg2L5QXl":"9wG0Durge3JN","3osSv5nfjYzj":"rD0KM9S45Nnj","hW9XrI5z39gf":"SDlFS669dWOe","MeRoJIRhylym":"2QiIowuchzRj","BfZWPDWJoVLc":"at7OAwz6FWWo","IGNlMgxGznyS":"WCs1cs1K0uge","CsX5XRLSrSRo":"syDP7JMS5lwW","k2WCYaF92bKi":"ZTakxCtG70rS","dQFZEVvxQa3J":"k1mwknom3Zmp","5aOFwlN9FNsJ":"QRgXouAxmsis","o97qnTL4Hp8r":"z2ompL0QQie4","eoC78cbjt497":"CeuBljW9ku96","1iK1qHAUMJmJ":"VPqp69XEjqrC","Uojhvwkmu6t2":"EgAEoLk9jQ64","Kifu3nM0DIHQ":"QL6rRRWjQnSP","YG2rmcwKJn9k":"ASgTFbaED0JB","3puqL2LQjsgD":"KRgvjeCR42ae","PBpKz0VQCLln":"dZvOILvzBSyw","bC2G6ZbiY6T9":"oMnrsIhUdOqw","1sqmSKdhSojQ":"j60iHz9KLgrf","aVJes3Sbh9uI":"c7WfBk4F4JmE","WCXk4eYLI8sC":"0UtmbyQvxLkc","V9i1r6o6cruo":"OWdmGo38NpNt","jeMUfUsjXq1K":"M2AfpEGcnlyc","455Vv2qPMpom":"TDghMyBtdBPs","INd0cxDHdrmx":"NleyaMvIWJvE","rF8t8p0J4dyT":"exhscqUVfooz","aUiVvPFKWZmw":"fMS1yHNoNTrl","kVK1xr09aQS2":"EGYkl5p6rXWX","b4IkWxfenFgH":"jFt0wzX2JHAT","doPW72y3RDHm":"SCjv1CuLYFIp","TNfHxkAcIsAf":"OVvo9LXLSnE7","sBxVHU0Ktu8P":"6gxVhc1sRCvs","bIgdl1MWmRjl":"16tkIq3pIBzQ","N3TSgvmjWd3c":"dLvIPnDtPr6e","iFm4HntCLII5":"e8Nzzl61TW0O","mSCPlYb3Qtq7":"ijNVmF7VpxsK","LQSFu1RSwLgz":"PcijmGYVTq0U","NkL3yMQstCyV":"rotI8VnlLhD0","cK445rG466sv":"OyFXmxME3fDV","9Fk42evgrVqh":"vlSgiSQYIwZj","czmqEaxulucT":"U4YzbcPsaxTI","bv8jB2Pjg4zK":"UIZAfyWqyYH9","Yfmq42S8uJ8J":"PVJMbMIR8uxj","HSc53Fr4OP6a":"NgBr6BhYipVN","bgYBujgtftXA":"8xYf00VGQJbD","02wp9Geu1yHO":"Ku6GKso2gh78","pniDbybHxUpj":"AZXbAoD5D2Un","0SI58cp2JbAn":"9sZ58zkRjwpB","i8xHO3Osbc8F":"v2xbHT2ZzXAj","kzo9fQ505eti":"KsCIR2M5rd6b","PGsfMq5mjc9G":"wxiLYSrxGbWO","bKOumFS7bpQk":"ycq4wwflpogx","BbUq7uOMJkKH":"buhuvViNyEA5","gvBIbgjolf1u":"dEDTrmqnbgyf","mpK6dKJaMlf7":"OmvOA30y6xwg","qifL3QSgq9BI":"0lAL5qap7eyM","Rgv8etUvBKN2":"18HovWhUFaK4","kjyYImq62Xk4":"0obKPetmGnb4","5x7tRRGvXMFO":"uRVzSpBZi4tl","DYGV9xczNjER":"qibeDUhKP2t8","s4965U8LZ9mv":"ABU6phawUIIO","nWzVo7KMlJw5":"LMWlohnIIdua","hTaC0Z7FFQkC":"AEl5u2Dl2yiG","gjCQCxigxE2D":"pDUYXsQZ067V","1UcglO1no14R":"8pI7qHKunLZx","4GlTk6xvZ7hW":"Q3JQvanpVJo9","w4ta3Mocyayw":"7k0PlPS5ynqJ","jQ7MWSpbSINM":"OqBKZ6hqyYfj","biBFU9xZkSxR":"hCovBPadW3zF","k5FmqqzewC4Q":"cufw8XQUj1An","3nwgs6wVEFGi":"vfZ5Gdf4x4q9","Ge4bUe54vg3z":"MU2mw2AQ8SRH","WJdKDwPrpZOF":"lvh3rocgvFuB","Ir8EiYdqpSBl":"ARV3rPV3LqRO","kAJ9cIR4mvl6":"KNkjAe52TGB8","LvGJcgAz5tt4":"4enA3DlxTwab","aXHRAvRVn55M":"kI3pOA8L1b8U","g8MmyTNoP6uu":"pG4DZ1lVtyKw","hx8XzxWDEV0R":"4Qn1UZagqSxD","tAl3KHwHexHH":"WaMrxe65AM3I","W7Nv6NpWYIAf":"arAoTDUJ5Z6D","d4CKtLtOxzHB":"0DfMt2beBAOj","6LdlhfYpu4Xh":"Xr6wthJjmGbD","agPXBnaiaqIw":"IYU1kNJdOMt1","c4Q7sjYvYoZo":"5EeRl5neSfx6","CEcCmX2Z8aOm":"w1DkjvFwXpyJ","jCkupnbtIv0N":"PHUhNA3OJqFy","b1cvPvqcU4F5":"FKjRMBcrastd","C0ORA9CXdloi":"G6Pv3mZWfBzv","BBKGGY5IyM0h":"yTWjct5Dql9Q","FHBNuoVdvfPx":"ditrMz1JSjyw","cdy6QfStOo25":"mErF11AJGWRD","e4DJnlS5c3nj":"PKrvpeCoq5vm","G8eV39ns5cRC":"YWzXrkrBGkj5","JMeDFhrnLjAD":"3SE68K6qfpjB","0hVFYedsOXBk":"DbF4IL3eUOmC","2hJjEVsWQDqN":"XEMtnS9qxyek","D9XrYvetJFgW":"PlQBMIlddWeQ","lXNgPFvxk8kT":"YsaSqclq1hMt","Iywdx64BeEcg":"RkqadNraEnBO","0JauRMyNGftU":"R1eA2YoSr4zh","s3BjDOOTE5da":"NIM8Ylwk7Ckk","F96mUY5VR0AW":"IT2uObcokSTv","cZfypPtjUzmg":"jMrq961TP5GX","gAvq9sYDzrrb":"C9s7MyPWGQdB","cLVA815vDZ5M":"etqlPMf0ohJ2","lrEgjmyQJcBR":"sjHzMkmOoPGR","E5jzSNgTjoLa":"D8R49qdDN0Hq","UoSzOCIMrov3":"5ZDGxZnCIIm1","pNAXfJzUpveT":"bgqGymp0rLpJ","28tS5CQ8zl7z":"zbPCE5UYDiAW","f9HkCWRisXcr":"x04QhJu9JqKq","etf6rAXT74WM":"xWHVNZPjRzwH","mG7R4nPDHvh6":"lHtduwIU1MpS","ccM9WPeGaU6e":"zjWplB52JcqO","lcr99hHQwTdL":"GxYImUdPJFzu","BhBDZ4hht8dy":"0uq54EV9aDfp","QHT0Eydpx4fn":"zkECvY7WfdeY","cqJmWS0hS8W1":"IED0xJ4EqigB","Yp442f9Ek88x":"i4AV0IoYq2ma","fd1m6P7Yd70a":"U9MEBhve6SA5","eWqko4JaPVdF":"3sMMZgDzfjGp","4IVBx26e1Wub":"NXV3jnjG4TVS","0dA8EkTkmtof":"aCEwo326drLx","UwB2AXyBYNa1":"y73AtV8TFkYa","pLTcVgsJSPyR":"hwVa1VOlGALV","Yp9VvUsN9vkk":"5Idr5hpPW17J","ei5FNlokCKRw":"8rX8r2egTQ0A","LVFblyKb5is0":"KNPOHzy7MdIC","BpL0VaiKgK2x":"pNwNgXn3FUXE","m5vpAxnS2PS2":"1RA46SvQOfky","3U2uqksmzetM":"GyPxGtan8mrd","0i3U4fUTRk7K":"WKADwcX7drzf","Hn9ETnpB7ppf":"2VEfiQqVRmhk","jCSYPU2WRTID":"R7KG1skQpDVR","TcDIXj8B7nNb":"0QiAu0Egr2No","sYfMvQYD5HIT":"RUU8iJz0VLV9","RA3vTc4Gfp1e":"lhlU6sn9jEFC","kJ6ACjWrJy7J":"LzVIkPcSDccA","0d0bXIGAGls2":"5NE9ttSJFA24","nVVhsvFh2JXx":"7qVcAJLM90ph","4BNu67EOWlhs":"ApTj5yIgNqvW","6crelWFEcdAC":"PaWORlr82bsd","dgRnczIUkwEJ":"N7cmgtSqQnM3","nlKasCtsAKQP":"Z4SgFzWVCqxx","UH9gZ6yZ0kYt":"LQFcdaGTIOxR","OFX6dF27C0J7":"93YizA3C9uNs","SVt1uzqaXCFH":"FKGWgCcjupoH","ll5zfyWO3Uzy":"mX753n5PAC6A","Kbczz3PZQZKA":"pSOdt1JLsekP","IhNN1IosHY28":"OijYDchlkkcg","ozPEZOkfEZe6":"y9m4uLtnGm93","L80TAdHLzopt":"9LeuA3nQUGrv","APfXAnUhL3pF":"fmV4oK9HqCyz","rTWQzwD0Bq2R":"8oYsq6mtVkq8","MsEemj2Mu6Jc":"F4CPAdfuO0Nl","259j46H6iNHp":"cEoCTtp4FG14","A4aJXcoie0Ct":"B90BmBmUHozm","9GzYSxdzdXQH":"6xW5hn6xj0CQ","3sdMOIdyYDrP":"ddVv6I34ZOK6","SUkQF0GaKygR":"qPsQXBvuxiBF","hLHYF4gYEoG9":"nSiN4MgzsZvV","35lOWlGLP9hQ":"T2aXZhnpbbh4","0SvXscumPUHb":"VqOZc3QWPBGi","vfn9FtofNZGp":"6Z1oCUuBob6m","Yvs6UhUnoLLx":"tcjvSzHvPq0r","GBZh2z40Hx4u":"PdWiASCe4EsW","WEifwdojhes9":"aJeOIqJDXK8u","T7a9baNKoo5m":"OCZRHp1XwkSL","ndnEvapfUkmS":"GBI9LhO8T8wi","VmFT0ZIsNrQE":"FmLJucIDVFkX","EVvrOuwytHid":"cdDjqPK6fKhH","cW7Rw8EEWVkx":"cqRHYHVC2pY1","dK1ob4VicIpi":"GE2A4Ga6cE15","yCmVnPwKjKcY":"C6rcEfb9nbeq","1IwYV4IjfuEt":"HSsrcMxPi0ij","4J6jD3GzXz4F":"2jc8CugY5KdT","P5dMMV2ibeQk":"fCUTUUzzjGzN","ex3VfKzwI8qa":"6mJl3JZE6C95","1h8kp45gbF77":"OctmQHgcQuva","UFA80ISi1SDg":"pYxLpMyKeeLc","wp4UcOheTQmx":"6lxoJ7AYzh2N","lJtYU6ug1zoW":"joVV130Mj1Sn","LNgzdovMZSmL":"oXy7ol0Kh5ZB","blR1lLN3yMye":"y38HNwbYTlHS","3gzeOApK7hvY":"lojaaVj0Zr1F","AdfELaHfxN1s":"5KrSHbqFZUGk","axQeCvOWyOrB":"sAmxWyllFEg0","yPcBUsBJCnCI":"HpT9sFF4tdRZ","uHaH9S1O2Xco":"OFAMbIbBb179","uaxRruRHakNM":"L7aeqOtLPUyj","8JiXKXWrWUrx":"dAZDd77w9Gde","48netdUmBjqT":"LcExBRpMaYSA","R5dY0YN4A3yl":"safCBW1VLIoE","qOZCC887sf7Q":"Q8GxNRa7vLR6","5lTTo10TG2NE":"5PpxYLYKL8Mb","yc0MVGZl4xm2":"jhm9FVMHpV6E","IhpOROaoBffo":"aSVVpYmdIHKV","zj47lhYBhw1e":"JxW9orsL1gP8","owOkwh4wfkZs":"2AIEOLZ8qJqL","OYBzdhhVVf5Y":"AoesS5VzjKk5","6J5TEnqrBlzH":"yurhZpAL4aTh","F0JXEJ4LIT2Q":"wN8ZrnWHBTow","fdaoedyVjxih":"kC1rRTjEYgff","oBC7SBjfcSJf":"RCO4MT5aFLAx","35CmpzpYW4PY":"FKlY7BpedA7H","yDglLwl3P9Fr":"sBQ9jLwWO0QH","OvIIQlPR13ry":"394O45DkFRgP","6zRfP4qKsgo4":"VOhBCQQh9qCj","YLQcmP57ZHrs":"UY5YJ0CvwqJn","Gxsw0S2Bl2vu":"Bqt3TfK6ibaw","9KQ1WxepbmBJ":"Kv5Wnd2Hh1hd","JTVPfVYmIIjU":"KK4Kc2M7103y","UIzXgvcjj7Or":"gt9l2LRpq2Jw","se3SvkwVDmLv":"4OCDuwZfXcCO","RbYii847cZ4G":"MzHxgDHhp5Ao","wX0G2aqo45w8":"EDhe1hmnmR7k","pDF39eHKVnu7":"77iCrkmwHGbU","9GtHcB4SsLnq":"qRmYHabvyMfd","Sd5NvlyCNGmx":"VJcGYjnoX5k8","9GRlk0dXXs2a":"QkTTBtNcUbkv","vnIWtj3ML3yo":"lyjQOhYISYdy","4jpdgtfxiIXL":"LiQKND2NQZ8i","Co3BKDOXyEUD":"vV2wKcgwso2o","QvfKnIEHQBHL":"WdewDd0fSY63","CmB6FsINWwgu":"LXsjOQ3ZgbrR","X8yWlew4GbmL":"yINhTNvDTVK5","FPamUPzPbhAx":"QuWhyLS6tiRH","xl7sOTc6MtSI":"NzVn8g5VNQXX","8EZ27NLy5Ifu":"3y1xcSWdAvUH","gbKEfoDLkeQm":"iERs5DP3orlB","Ql9SAu3LRmCE":"OMxwktiukKG1","lAqS4aSFpujV":"rRmT5rEeulPo","FEkgTchizcWC":"LqCLjilsj4Ut","sWDrxwKwanLh":"kX5DCMYeImvH","xYWMnLWvsExq":"YazNDd0SGQLM","MpsEIjGDPTbs":"uLQdewBduFpU","8lJ9DKDoIXbL":"0NnuGL4nCq6W","qClPcVnH1jZp":"64w6jP7dkiDw","ZwRCMlVRLZY5":"oSbj5hG9I2xw","poNRavLyG8IR":"YCO2zRrSAKFn","gjrYZ1WNfbLT":"VN794muhNpzI","Wai55vLctQiU":"1ZhOAVByLz7q","152Xzy9IWhQ3":"mAEYNXB8af19","yyFzxsLuMduO":"lc4PXSyGFnEh","LnblSH3ZmYNF":"tUf1wYnezBDP","XCnIPwzHESCx":"itN7BW1kgFFY","yQy4p20gDW8d":"PvM8eO5Hv4Rc","XRdNj29MH26a":"BRCIzft3wVNq","Sjjh30beQsaC":"Svnf8k2vD2s4","Xm9Ukda09ifn":"94BgPQnzsDjy","K8WF3JvzrXAs":"INNVNYK5AlLs","HjSvE2QQpWeN":"HLtBeeX32r1r","K7n4ctN5hsvY":"EYUmPeHpsUQl","o2iuB7WYZczh":"MdVz3wahtGU9","7Oj83WV7QVth":"KPlSykaFvlRP","rOZeYJpS30K6":"gwpMJSZ23Pb0","vnjB5oaE3Sa3":"wL6OyER2fnsi","wMVpQNNMkWcZ":"qsO1ewrZ95Di","nTQLyX2xQeZe":"bnutzVDdq32d","3hN30ti2RRGN":"hVeFunPVWeze","u3RApYtUb1vl":"c3GHvWZGbPSw","83UJ3iAdZIZU":"a5p3US1BhPvq","neb3zmc7YP9d":"AB5FUhYThXHA","zDsu8dd6aJZV":"aV7gwWbRZVQ0","29EUj5BFQXnI":"9GuStLGImZOM","GanmjEY9INdH":"oL3AlP9oZnRh","TIx4pYUGJNV3":"beB1Io47bbtZ","FDJaS3ebhSff":"rFgFEDl0HjMh","BHOJZeZNPIxv":"REGiXkrsgnRl","smYsCZPNTKub":"va6q5HpEkjWs","s1Tze9HEyS1c":"xiT89UNK37WN","I3c7qMNLP6w9":"5Qw9ipprJQ7f","He72cpfp8AcT":"8AltkE5n4KjI","N78iTTkwPaPO":"A7RqmI40JCuz","3pLDLQgMFJUj":"ukkwzbziV5PS","9A1vnhG5uMJf":"ShtCwDFl1kPj","2zBqkzKnQuA3":"h7N4znw3BWeJ","MbSS99hpj2ja":"il6OONO5UGt0","wWz5JRerJQom":"UyoEK4WLufRh","9PQle5M9Znfo":"DgrZK0RH0ubs","tJRW70rENvNh":"sM6eXSnbNXfN","yVjRSLOTna9F":"Ah2rJKhEoOVt","Ap1tIPSxW151":"GidQm3WBBMMI","TNG5VOuRXqr7":"lqw3vpLUWO0f","gd3YDuO7Ymbd":"X4Voxr2SyXwl","7XgSJm0Zr8W3":"ZEkTT5KqYIOy","RhBGj2EedJkO":"gzv9t3GhHU2P","N8HnP5XAxsIh":"0gecsnq8ksTm","6bQF3q6MYBWF":"ukERBPrlyNdj","64xqbj4trEdZ":"P2hdzJmDtPOa","T38V6YCsmxGH":"cfVf9HbWR5DI","hUL6VLsrYYHV":"WPRh6pDmRgRD","WvenktPeFSPu":"vzM6VadhBlbx","jRD7WFWP6r38":"vF06JUMxWhWP","eJQE90g1x9vL":"ukzVrtytbnyt","bZNsXTqpcYeb":"7lA7AcKHN5F4","u8HAmH9OfXjj":"aB1cgBgHmkhd","jPi5AfMGAmz0":"41F1wer1twYx","D4PvCarSzlqX":"pJ36pWuJKMTS","J3okHmphtTON":"PxRKngwDWeRT","jR5MWRHY7rIP":"6XuhYghFjm99","iADIbeUpS1oH":"dj6kqDQ3Twve","WA5TZVZuYF6h":"gg8dXcXMrLQk","s0NV0jGxuQ5m":"eejAzilC76S2","n7l9mjAdOXZ5":"IoUGqjZXeJjl","OwJjY4DHEMtq":"j4GeRW16aTGO","yMxC5SgGz2af":"npEsHSwATgmP","cRYO4isWAUJO":"azbluGbE6NXM","vMzz6ksFhKJm":"d4bnIKnItHRW","jPosxjzR80Pi":"3RthMATnkMLe","LyFZ4PArXgBd":"1ou76hpw6dtC","zJ8plfkONxFA":"Ii6Z4uLPG8JA","M1tepNc43XXv":"WpWGag4un0QI","vF7GPndf0d7M":"dC8YK3wB36Cr","UrLnxu0UEZIV":"ni4WbL9QmrOq","8jMbR7DjKBl2":"IyrNiPrJEqXj","DLPb2oiyOTHk":"b86iXUafmjyW","402lydwPjpxw":"sseKmQOcvfcM","qXBrasjlpjMx":"RVePkVdw8z9O","1lHjjWKUp3Au":"mnZjwDDJpsIY","AJ6pKKA7BOA4":"VEUM6emZqB5M","UQa80EjC0oQ9":"FOxxkhkDztBo","XwEtychGFcEA":"CeSRZINUGSse","pkK8mLGTw1pQ":"tiIx7UuopFEp","SDEl8u3WZtdo":"bYMyzUYSoBDx","mHWm48EaduFk":"omM6bapUV8c1","57faOG1i8QTR":"LP1vKjXXq0p1","FIHyX0QHKC0L":"VLuz0Kq68QP3","IPwFHOhaRUwW":"uH4msYJFirAw","wcduAJ3Y4UB3":"Z1C7wVcE4WWW","k3v1cQBw1iCQ":"qFJNTDNDAdOF","nJnfu29UMoyn":"GYZXGdZTvBJE","nreYefI8i2aZ":"9RpCjuiQfBot","Q9qed4ShxnW9":"E8FY4GRegbQc","lSjYht9FNXYV":"9qfddhiVQf5O","BeCkZIYCnvMQ":"0eoVIZWkqhru","IhuRVige3z4d":"5mu9zHLR0gtG","LNldy89rSgyk":"Y38IDFT9EUZb","OOIHVIY17IXu":"sYl6qRBiZZ5E","60f6aHYV8hCB":"jsC9LAQY1sv4","qnPlOsMnCeyb":"ZMIaejBPUzPK","Vp0lyDHmNF8x":"F8YmlupfzmCq","81UsbrErlgT7":"Mg6DdDHeMSg5","ytjWrPGoBQEo":"fc28u0lviuIo","miGDbqcQJAyA":"7EUqIifSNwDn","vPrD9V7TQdom":"dBOYQcIGUBwj","kHLRI4yDCRvb":"NDxMk33mCJnf","UAEfJybGDBWC":"S8Qfl18oUcPm","OmNV6i524JWA":"Qk7APNp1UBEC","PPv61k1lghO0":"ZL7lnA6AKQAw","camdTIiI0GGA":"dTRc5bS7aTKz","grqIZeD1OkJm":"YHhFDkHF0BJM","9aMkdjK8UbcO":"Qq9V7DO3VqpX","yQb0dIxeepRw":"skqzCxQQ4xOj","uMlWPscQdOsK":"UhQU2ttrQj3w","d0TQrGVdZ8DN":"rUK6yTSQeahY","heFfk5CdJhmp":"8xLKh279QAmY","D0MGLCdMpkad":"f83rDTlEQN0u","3wVol46cUYOA":"KHpBCi2azSxi","3yXyAh6nOxpI":"XTv6xlak7t65","JqhF6c6Ah0ai":"BMlIenN1fBIe","cF6ZSV06tiCa":"pkLqH6O2CDQY","scmTgi4vmRR4":"F10xNXAz7Zv8","DYvfXvq8S6OM":"H9gsEIBrazFs","vbm9dVB90noi":"uswCLzihWHWi","yqvgKf16NF44":"rJ3iHjj2pJOq","0XdX3q7B4iQw":"uRNqFmo12T7C","LRANkG6odDoI":"1rdPkiwNDt9u","evVyqNnlsXYM":"orOd6ap23v99","qA7PCafTx2fQ":"bQkVAxgxor9q","hbvPGfnnHVkl":"tvdeBsbf9HPg","aTrdlIr7P5a5":"UYCebPyACUSH","BfqkQYIjQTTe":"bffteOc2DYeU","vYxscqOijSQC":"gdFVHTXqXnYE","ye7ESy5RFdni":"REepeXRdwq7l","zuMF9KOYWrHl":"uNLwmySpvbDM","Yo5lhag6I4qJ":"0pLc2SBK4waw","SDU8BfQ67FsM":"BVZY2Oh6kBdC","MSgAgYq7BEGN":"8APLvUsQIwyj","d6iu03Ownj0E":"XD5VoiQvZYHc","78gxl3vqrkjg":"O3v6pWfmKt7F","WhddDzbhpSet":"kTjr665YhKx0","lsLGyUmBrLk2":"LBaK6IR4bJeu","mVbAcpVAKvP9":"VaNQxhu0jcFS","UNRQVA8PA4WN":"9yiftJhaIxu2","ISGzXhJUcMIC":"YebvhGYVuVvU","Ejxy9QpnyUjv":"WgJkmQWfSzbG","XAJnomtx9dfC":"6ZDOPWHP0VPu","V8OL9ZHUNUUE":"iub8gVfX8gjf","idhhZCkWlZHk":"0JqoJSCzc8se","U8rittcFsazA":"7KTLLPew6Gup","QLxE4WRLm13z":"bY5HCi2Re2uB","QDw6JS25TCsG":"w8oMksYox2Vk","KCIyhBmXhgao":"TV638s2lVaq3","SOmvfXTUxBsP":"IFfna16zrl8C","mqZm1mXt7abP":"pu9bkwyrU0SP","DWgbUuoHkOZH":"vxPHSe4O1ruD","Yy2ELV9kLh6E":"0A5IzJl53tJi","AbHhYsIWTBlA":"BPOo4xUxS4Sv","DM24JWmEKjeR":"UjeUslTwEF0g","9VUScl482p1f":"aAKXYvCOWKaw","Obucduzhu3Nz":"FAqXFBGkR6oy","OBXo95MEB4vf":"HkHj8W7DPj4Q","mIo5FGboAHLQ":"mAOfHyJhkZYr","qG5es78jbJxj":"AyJnLTNKTycx","z7JmMtOVoY59":"PgQVi78KLfrJ","8mnwkMp7JGQM":"MP8c3Dn2aPQF","Kgc6KhlQYsdG":"WQCISOI405Rx","QWSRau3O1piN":"1iL9iPJzqNb4","JrlQl4vj2eN0":"LX2KSn4poMox","TWxSi9here9n":"AzLGlRg3yk5w","SH3ByqvdvD0c":"6pPAb1SEJ01G","MipeZZIwo8Fm":"USVUJE4KJ2Xu","PJu89Hiln0Y4":"7vCK5BGmMZIe","JzRkLENyacP2":"f1WhYJ2ct2kK","MEG333Vr3dqD":"1gxauSASxD3V","AQ1ZGLgNcOlB":"0eh3aFtDFvrt","m5sqDjUaaLom":"qsx9Iamu2TPP","NZ5j29ZNvYnQ":"MtbFLGPnzigd","8t5z74WlsAzI":"ERDL1sX6bRJj","6Jw36bRPYexh":"ihvLK6OY6PnV","FqER5aigW4Py":"huThZZYLxUUn","jeJD5VmnXHOg":"aYvGN7KgPYFx","UHLDrgDRuo5I":"pTCyOdABxXqV","i0CzC8N3XL0W":"ERvbsD7r2GMz","54NIggFOKDs3":"kCiKqwAZFKYQ","3q7aAt7Evp29":"PnX6y9SQMT5e","J5ULH1nFNvRH":"8p6fkvx6FutY","OiaJF7pObzEg":"vBzWdMY2Pmj6","kFaPJqZArYEn":"XQ4UB3rXHbvq","Lt7AALGd45Vd":"veXRX59fsmiH","uvvl7CQvXCMr":"CosVC7l4plEt","9LtIgU7asC4X":"uxvtJJOR8vv7","tzMmnsuTcqdw":"KcTSCsZvVNc9","Ky72paoa9ai9":"gwMGBXLf6Poz","VF8Gauc6JAOC":"UUOfqAUTRKOh","i2L607IpfyQa":"Gob1VsAuD1JL","iaa8FriPDpNO":"gJRG83Bps06V","EhZGDqqLOsxc":"vnBivhhxYnNB","p9hZUhuPdcqV":"jQopKy6bzeTQ","0zhuX9i6S54H":"4R9Lkm4r0swc","2ycQKMmhaIbd":"iqdHvXRyc5iD","UX1EKE8iPFD1":"FJtPFbi6FmLE","FefPbDvffe6o":"G2SRvexXeQnA","9fyb3tYePXYP":"EOtm2t0sqDFb","u8xoeMGLBb32":"oSCRNjRMgOVE","07IxjL5XQ1lw":"LaryiNUcAmbQ","paKLdY84Cn9z":"VV5eTQsqk6KP","g7CjQTUDBlIL":"IA4Y67DaCTnB","l8UiYgqjL2Eh":"g5pbN5lwuUhm","XQSZHFpKV7Ae":"9vkaK446zRJv","eURBNVztBEPu":"o4TVO6a9Zrxh","SGVh2iM2toTw":"d0axtB0nYBuk","f3i4WWdqYDxc":"4Veq8Vnu2Kvx","8QCRng7UExhy":"lrd35SWQp0Ba","4VfNvzxfcVeG":"xupawuE87DHB","3sU6q9fInT8k":"8Qaq3vPCM2ZC","uMHnaGGlZoJe":"wtWfXdzDOMNs","skW11qovFGWA":"fhNzX7rmLp9i","rfmlFDNvQnuN":"4nkemGMM1M4E","sIZMeUBDUmDe":"SUI12Ka3Cs6G","1pVpxNRmvznR":"7jWwC0vuBviG","Ln2L3x2Qyywm":"5PM4qtlRkYrL","sjtRhxaHzlDX":"BqJ0VCU9ycSa","sZ2ivUqHq7uI":"DrksdMxqHbVe","ykhNwJRkXeJf":"P4Tgou6tRp1l","pjx9jXIl2SC3":"KendHocFNaDd","z2bKtfjABDq1":"BBHuhlt8J6N4","56xJQxKnBfqy":"eHp4KeL4paT5","umLoT4hpQ9aI":"shwwi1HLgLxP","XKQ5P5VAjSk2":"OSdQWtp8uMWZ","UrEG6RV0PG6R":"2nk6ES9gVMzr","0sFEulasCdh0":"SV1G07wNTYvH","WmCq7WKYoSVN":"TfYKEXRXZIuB","CsferzwSJFPf":"mTBOJ443zlvK","8ZCQzeckqOmv":"vuIAMIg0pV4e","jwm25SyUfSOq":"9VW1gyE1wsAu","0kfsv4LaY9z3":"flHTKzfAjaiP","auwj2ztNOJd0":"W9reqWEk5VMA","KVsyojsHUwp0":"VkiEZLTKJKLZ","FJr96kdj3UKF":"pAnHJW5SM32f","1VM6dwnY7N8R":"QuUQUsLeobMc","5JpNbyR3R67B":"YJ0Z1EFdELAA","Xmh0OS0woHwd":"6O0ns2k7VnpY","gqZOc0EghnSc":"joXd7AfchuwJ","GhqagpamPnZu":"gtx5sOOm1Uh9","qJNUSntmECDg":"soGuy4mYb26A","ZxaZpbE4oOnL":"scOtERHh0nXN","h5cElvjxiLGm":"oD6LY2rFcP03","WTMXejqQ2qYL":"BSxmS3EeY5PZ","Atg7umsuVmhn":"M1MhKZCclY6D","jVnpaikz4xCK":"bHWuVAPLO9bY","10WscE02M01o":"5gNZGnFfyuNi","gFdfQBHvyvIK":"Td25VUfGwyhD","yDMtk6WwPX2S":"Dd0zU8Q1vqyt","EVj2xudmKL8Q":"7RofQwVzfajT","WZwSrnU7cWZM":"dmUJT2iQaqJ3","ugCxcuhvWnpX":"gX6s796cr2Jt","d8fGNWmvlyBj":"rI9Wrz9tsvoh","I8o6F1kjomV2":"9xxInHfsz0EG","BTw5gzfwotjK":"gRNZabqonbt8","1FCm49wAo3qX":"qSDC2GSAspno","PTtCWo8ndX98":"oAfiDV1dGd3o","CSzy5OnCFC2T":"Ju3zkxupD2gJ","5uGx04aqPvVW":"ZgUFYdGJKY1G","TGPf5vVoEfTF":"xPXpZ2DOkP7q","fNSItvCsM87A":"T23HSqYMs0BX","6sGGfr5OGHAm":"OXdMXWTlNb4l","LJ0EIdvr3Yvl":"xFYy7HxWw713","7WApU0N5u4gn":"OJ6uTVP7nVQ7","Y7lh4TA9n0Me":"8RXNotpq16Ly","XJHGZ51IlFNo":"e1gfE5Rkmjwv","aoLoE1zcfu5d":"fJiMRLtHgOnm","wwFEJWgmYk2q":"UNwyand9kZgv","tLcLZVulqGtN":"tc2Jp6xWhRGx","mct0v2ncxQrv":"E1gnSHmuBzR3","q7Vo5yh9q0Rf":"8zbsHp1wEccL","odTm8BQtLJ4D":"5xU3YOqakoHa","hsHE5b4bQFEZ":"EsymXHPCtUvd","S3PI62rSDy1c":"cUsvnAK1Q3PH","QVhvEiBZuMjD":"3V1e1LqohBtC","49gagxi8pH9T":"h4SkeWvEExnM","qhJMUHYM4J50":"w3lVlbA62kVh","7ZCcgfjqekLP":"VZnPWdcq9zWJ","kjYQzwNGnLZP":"HR289xsWNs9s","jjb7BzKT0Uec":"M0EejZ6q6QDJ","8IVou6FpoR0N":"vUG6X22tZkn3","u2wCLtQUld9d":"tkY3zVNUnr7q","HTOs71LvcNTH":"AE4HtdmxWQ3a","1Ul1OiKwobIk":"EAyTnFnScihk","8fg9C0qIMfd7":"GOflEMVM2vAk","CWrO6wGCNX2I":"jx3IdNOlUWn8","QpDYTfEdhJ9Z":"2lj5Qx9EJevz","F2v8O4BpKdWN":"SchalQjKxzHi","2cohadKDnEwc":"anYNJzh8hfot","dxxb8sdsoGQU":"hREyXT3ihKTM","9Xry9DurMBP9":"ypwx3dSBpXjF","7qJLXITkBWB7":"VY00bOG4xsce","erUcwDCfCSS2":"X71nw4Qes45J","jbH2P6hgHAA8":"r50KNWeEJlFc","mvO84VtxgTZ3":"Um4yBEm46dZE","WrAFbgvW40vs":"euKq0UGJo7gc","j5EhzLjmg3cZ":"HBsMbIMSKNE3","rqGxDRtdf6x8":"Fd6cE0538wie","A112bLWX1cGL":"CDlAnHdbXpcL","RPOCfMgngjSF":"mA4X3qC4mE9J","J4eznE8TnQ24":"7fM5gBTVR75T","RKIGfrE8ShIi":"8BwrZ6vCBgA8","Cni4ADSd9ELR":"tXnS4eIQPHij","g3t9Pg0xb7Ck":"65Uw2LPct2NO","8VUyfqjXuxs8":"xbrdfBIfafVl","B2Gti76rArTY":"duwl6nxPkIFb","gVCXsgesKVsY":"3KK0k6617gCc","wOljUK2DlDJq":"axrp4DQ5iNBX","znX8cR2Q76A4":"oHmtL1VYDoSQ","e4lDYKobdbP6":"VLFzac8cI0fO","Z07svdLp2qHm":"LpHHYy4qA2cp","Ziq89hdFohjo":"XJpHLiRMGR9s","EPXmtmDgjDwP":"FNhsc7WfZLlB","PS0PUwQqmJZa":"7zuSkPQ4A1Vz","LT7UUBi9t00P":"l6JOIk5aji3I","xlmIrNtP0yJf":"uVCkRmrmJGBa","XDpPKL2SGKqT":"yvkzmZKpbMC4","0OStMAujBN65":"R7ODXmZTNdR0","rCfuIs9hHekm":"refynN1UatEb","P7n0xravAt75":"Y2cpZjcUicXA","E4Kzll41294a":"lKgG4LO2BLmm","PY09BR7sulDr":"3VJE7ivQQQuj","UewfsL0zCkMD":"X9UKvbpoW1CC","hDlihSAmvEYR":"yR3anUx2UCA2","zpkwROE98XNE":"pVqlRAIoPhO6","xgj11zchwzmg":"JGwE5jaYSPMk","DysWqAQqf6sb":"TXIPw8Z2fmcN","sGb8S19qEHif":"MkbXutT5oE7z","lBBc0fONjkjo":"OYd5abaUUwiY","4JFSjZuCXXl9":"1oYBQGqIXsFt","slLs4Dayklo5":"65Z16lGZPt5P","X4QELORATrWY":"hYUSKzUMMHen","vGqy6Jpf4uXp":"G5WUY1AP2TPh","yTfIbe4gKQr7":"JMDcz6GvhUKq","zp2cR3mFk3zT":"C23IQWD5Du7J","jMTsSGNy9LD1":"VVIzPplrpG69","BoC1xKi4xqM4":"CdG5BVZ1XF90","A10e8e3ef27P":"qfmBaIrBJQu1","PpyPe5Lusv5E":"MaWsk1muu7Gu","1LTldD1L2Pek":"JgYhOwXu1tZl","9RKD7bYevZcI":"QsbuShWHaAij","zeA6pAZXuGvS":"iF3G6LS6heTa","C2EzMhFYlkuS":"U1x3dTZVIOuj","RTPOyTMeOwBt":"de05EqLhurBR","qd41EIYEEz5d":"w3cSjkuOutFJ","OngD27rJrPEj":"YtlMQwOwxUZp","wI0rTTrnWo5w":"QVmjNxdfnZlS","5OeXQz8YNCJk":"9zinp7SSFHvx","sbhJ7DmcDn6N":"kslBLk9lHm1z","DATp9MptV5AT":"iwqGXvvSHjUR","7gGbBvlPRB0d":"rJ0V5jOBQvC4","O7yWt4SahIpL":"ZAwkRPE1kcLm","8vH3xNA0OKH1":"ROmpg19w8Mvp","X6fjo9ofgCCI":"aC48yVP3iRRh","Mrci1QZSwW6E":"4rw5g15VVcsv","wPMDojKtS7eR":"iHS4jbWjN02p","1RD2fsmjpTf9":"292UzCKwh0zC","21PchcwsEgMG":"MngTHISaKKJ3","iYcarY5FG1AX":"402B79Kq6r95","IhaEG5F86iHM":"Zc9kQbiRKikh","fIYdFk1fjbqH":"q1gwzwpmqjSX","LWz1Ms7VChq6":"ONCBlXlFTJ0h","Zlp53JIpUNxg":"2ipt4zEJUtpg","ollcVahKzHnO":"iCoUuQoVoQw1","3hfA7qyDMnAY":"N1lk5Sq2SrMp","XaYQ1qc8FHOE":"xMSZg4RCDQHt","a35YemtyHzOY":"u9c2g8UpCa8p","f3U4G99kyv5r":"pj89me2uQk3J","ze8YfMM3n2vm":"LESWRi9246ro","7G1oXtiiqc7u":"VCvBtLtBJ6fs","ppzsFRbbNTsy":"JHYxY1A7ePU5","8xE6xG8WdDpB":"cY69kKR3aw8b","ciRomri36X8f":"ls6EmP1bwaf6","yjdyXnqM7veK":"pstQKWX54d2j","1ibYMkAzhNSL":"2qSjuvkJltzs","21EvrOzZxtgU":"sfPK0yjIlh4H","xM8gWSjfBLw0":"7TfYRnC3U9uv","GOl0LZ57cNRi":"hsI23rUGnXqM","nZJNns3kUJ4I":"QvIJ46Kl2c4h","0dA7NvzPZqB8":"2fTgFpmZZCoE","dVtUoG7ZeuJx":"qjT0zAkCkRRp","iOjQFGVsBorh":"6cUCsm1tkKQu","zYPTa3IogXiQ":"i3yv0A3DjSRI","NI8okl0Gaupp":"btTP1xgB4tS5","VUuxfuAlvl6W":"MxWsKuR2cmeB","Fv11ohii88rM":"PQo82QjdwTc1","Xg9jqWKfpq2d":"Zw2UYiuSAOkD","XdbrtRzxSuj0":"9nmRm4I3IdLW","o2FPSiyrDrBx":"4OFGVEp9Q4gK","AkN87lCP4iOK":"UTAzBbGWzhBt","Y8kRHUvDwgKT":"Aiq7ahaWIjPF","bAPn5sssUCD2":"2Qdg4bbG6Ush","XnkwFBLd26LZ":"9FS2E6PbHz6s","4j1BSC3dXFcn":"2whOld2UBGbZ","DOvzBxEY96HD":"IqKzsKzMobfE","iIcoQwgEb2j5":"G5wNBB3gGLpk","OLsZutt3PfFs":"qHQfaIcq0PpU","eRyUl85Y0Pcz":"ja7kw9gh6urb","xzHJPRUFM0wN":"VTTVzGzmMxeY","bCwQuQVSXgiZ":"nffNvS1IwPVd","fFwKnemvZSi3":"ZW2X4rgCjSUX","3mrqInY4d0p6":"szUISJrFbzAd","jP5nPSEpg0J7":"5ROp9pC3Gbh9","6FQV4qSF0A4A":"wKRDu1ynC0nL","HhJ77vB6bkjX":"w40yd9qQyqul","AOsPDW1IjWHV":"53VIZw7cg2ug","EbUXPeYgPu3B":"u4BngzfFKVdC","Q30moJvUaT2d":"IRbFB9RKyEnd","6x623OlBSEai":"TWWYrXILpznR","qZuOsw86xxVh":"zXMVkbfuJmMs","BFArcUOaoJAp":"DUSmHHRybkU6","QuDsZSYoRbsu":"IbwRzOukrtff","k3nWaqci0Tn5":"ZRpI9bYtB6uX","PjVjD3OI5FG6":"RsfnCQUOY83N","Rs9blrRPUhC1":"20JH7CxAtfCj","5HYRxfL1voUQ":"isTCOSLpUWlY","cJxouT8lxdjj":"WWvDionk3iB1","aFcjeWXgX4X9":"dB0aQtlMBb4K","cy9XJ5kIq22w":"MolL7Z3lASH0","HaWkwQVERwNm":"jZfmn5omCm5O","QzE30Elo0oPm":"xA7pVwg7n6By","PjSJjfW6Oe9r":"gv6B7aY9qvJD","3s1ZfARXJXpk":"5jjqV8Of6rY0","bVoWcQcy31Lp":"gLMekp5aEDyg","lRRcA0lGV43A":"IDV09o33wHVR","8ai4Kprsr0FV":"SmqvgiZBECnf","4cV85hLyL7WH":"k04BpMD9pg7b","IkR6yHk0gka0":"frNxB2eacjiK","ZBKxropWyhuY":"BVi5lJ5mDvDZ","bX4PY3GYnWvh":"mwl7voEyJmFb","b3bkf0ZQmHOa":"XRXquhxug2kr","6B0hGl5I4SVB":"wkamTCVeJhWX","SBZgX45mSLnm":"ZtS2Dz1ObVar","1DBWRNHOc2w2":"dtvf2rULc7gd","SHpXV4mKOQdr":"e483Zf9TpPDL","a1uuerAIGiNR":"MyFUHElkGB3L","ZyYJ1DQoK43W":"vIqHO93he2ox","uHnwrMPfBQfM":"wCss93g1QKYB","BswaxkwJUWiq":"SISv58c2VnOy","0tUSvM73supj":"J9Af7muvX46B","ki5SWuRHVgF7":"zez6DXnW1Aqd","Ci1IUNzrODjG":"UOfCSY04HZ7G","K2BBKEVgCzhe":"LDAlOksiyszw","nUxJTBVuylfI":"7mUzqVNbxG9m","7deFdmS0r2Kh":"e4EmQgH6gQyJ","7fyYRur5VP2e":"pNF9CY8Am9I1","sGiNxf9Kdawz":"rDNYpgUdbrBM","Sk1KtfvS92Td":"eCzmEFFZlLdb","e33tzkK7Uv3N":"Qdl4EbfkYdZE","NKZYuWHN8Cco":"gem9MysxN15X","njs2psRhzUvD":"yeKPYKeo77H1","9M9toRRMySlD":"7X9bNeD3bHP2","f1fkkzyen3vY":"ViebPj5spV1z","8IIgWTWfRDRH":"QZ5ym0nI6PWN","NkA4TFK6KkBV":"2uriCdeXtFrL","fO8ibaDI6fLm":"Vg4RqYe2PZ7p","KMKMVjzylqNt":"qYjbDZrUAWhx","s3bWOEh6Ux0z":"j0xkufoIhix5","MN5En7gFtERa":"WieVigDioQn1","A3i5PUkLtGeG":"crxSzPPGVCeO","4z0UUVXTaJ58":"Sm1KgochDDcU","x8SltSlA4Pbi":"BsIhr94YekEa","A3iWxzQ3Mlmc":"gt5xtuSpU5kt","LdMBfA17co77":"GGxP3R14ycfn","vR3ywrKPIcCG":"XONhVy0RMKxT","X0ep2fJSHdy0":"fUXskbeKlb3u","oZXrfRBz8M9M":"RWdY9ZFT8TFZ","G1b7RFPthAYu":"ptGPqPpIGjFA","aVyZSmtlyNGv":"F6q6dWeXX8NR","dL0OkgSZdfT2":"SBDQQItL4YJw","EniGHKOJUyw0":"cRw3e3cSogyT","z6oS4kG4d9gN":"d7DsUM3Oc3sY","uwHJz46vJ2je":"XtBR2p1WxB5F","XAF8gAS4xqtL":"ZqH4TjnVR50o","7tYhlpLUV9Ly":"SePtXQwF98NG","AuGQWvGLAvOy":"s3vkDyskeYWE","MKQg5WYmxgFb":"YtxJBEWi3m1M","xzBOXm4GkNBa":"LpFlDbw7C6ag","2vLCSbRlMG1A":"OJfIfiwoiZV0","JP1TiZzIemWI":"8J24rwQ7ixcW","g4Ff8JmrutL1":"PzQXUjYyr584","KzXqVnsCJmUC":"hvgZCenLwqKN","oepEBurmXZ4W":"ax6F9Wn3Ip2u","seRLodZJ7IUk":"QBdddRFI9QgK","b1b6gWmcBfEI":"btcuPDDcJjvJ","TTgd5COiMjrl":"VW5ZOFggORCM","PpvD2lDe3LgT":"tRfUpFYATVcR","Wau0FpDpO6DT":"x4XR396yOFx3","utH3ofg6sHSI":"rpGq68unkxx9","2xxqDGn82xCX":"Nz8FE0EKiarQ","jS5kFWHdF1MM":"Yw4bJ3VdjuJw","0H3Hs9dVABTK":"tL9qRcpIRBTg","QXrq16FK033d":"Jr4DF4ExEDcS","dYhCzA32jm0L":"UxrPfsDExBRf","QEwxgT1utbxD":"npoiW0uDW3kr","NF2aQLWulHfg":"axhagUS2u0UU","KMKRYnG7VHVg":"22YI1hfe2xJb","2NXMc34BfEs0":"WxGNoreEJonp","SHsTzxVyvEMj":"vVqLBgWdBA78","DRw3MdGxVzky":"QCrH4gVQUop5","VHvcQbFDVwm5":"ZUG1D7R4JD0i","LHRg3XJSheaL":"Q1j4n9Ox560Y","hSWAYZc6reDu":"pLX9t3hYjVJp","SNnilotXy7pf":"NHyWhi4ePEWZ","yXoVif7HxRMt":"NNZamZuiOAN6","oEIQUsHeqFlp":"BPjsb7TLBswD","Lk4U5zSDw7XO":"RUrPueHWCLpG","vYGReG9pNCys":"YEnzG0n0ZObc","bIKlE3TKJo47":"2wu4huRm4UuX","ycFCNSLU71Ab":"kC3oqi5uiJw1","QRd9RRLU8rYI":"Y0yAk19D1wra","kaJYMOPILaoI":"kwk4Zc6opoti","2YEMHNhQ37Vl":"cIBW7CT5Cq3I","AVwz5Qaa763r":"FkeOjdF1qSCd","heGKshnn8Arj":"SvMKsmF8dknc","L7QoqZSYPp6l":"G5efcqhEhpnV","8pv7SkCw8bU0":"MgJBgFxPoWd4","HO16sYSxPxT8":"55MdwweKkzld","4F85NTgt9f7m":"WIONcJ1yXqmE","d9PJVd9asr1b":"GSL5In4DCIfB","9KfzoFsFUux3":"3HEI6OZmnqQS","CO07FDrmVuei":"A1Zgx9hf8fMy","xqxZPlCM6pSW":"tU0rVfyFxwrg","Wue179Lto6Jh":"oTxetphznMV6","zSOQkmfFFWPC":"EMKJ7g5TSVjw","an7bLOLuUb5P":"mcxExXtsUSmc","Lw96Rf0H0H1B":"DwK4vMkh3paR","Tliaj0Rz5XwY":"AcV3rEsLTe5K","4TOqWcAAsYPn":"SJOEWIRziLD0","jRoUqpBktp6p":"rWJBfCscW09z","GdNRUmH3Uo75":"wihXMysI9EoZ","dat5ey1axray":"0QYFlVeMcbAT","d3CyNNj72ASV":"XIZ7J72CcEB1","y6qnVTTlbRnF":"EZv6pBBK1UA0","2pvTDzBfPe4I":"jNWtndBkrgCK","XNUgEJPYKG5D":"ij4TLw5GPw2N","EPLaFc1MVebY":"7yEBg84x3lgp","I7c00niLxdoY":"tCwot1hDsxE1","fwk3Adu8CU5g":"2cjzm1ixTXIX","bT531q8JrXox":"yhzOPpEvLeIF","9gARLEj9xfZ9":"FI2TYM0ma8f6","iOCZa707uWDm":"oY4cyONnyTC8","62zFccyCypsV":"elosQEcOhFJq","tgJrPoah9M7w":"NFqoKG8ICXTi","27bjaXw5JM5I":"Y6aq2tTv3JSn","WnKyUMf8kkiZ":"Ly8yBgaoToXj","lBLd0m1StMOn":"QTybGJhnSntI","y2K0NTeNnT1R":"QxfPjHFszeog","Dgv1OLK8cVAZ":"k4APP59YFMXh","fMOZmTUjqzvA":"Aw8f0feYy60e","mWcXAFhhyJ6M":"WdWSHJHCczdj","rU2T2KnsxFyx":"pQuSyvUhh2KY","2jpOGPktWL8A":"HZwy0eNGtDtZ","w5VgQ3xbz3Cv":"XPZCYSzkkiX1","LHLOnZOiqyrQ":"jA8kONwgE4Om","wkGT9kLkvvvQ":"K0Iy0W7MTC1h","gsSRn06c6czo":"xMz5k4v5NLh0","pZ2wGN0Mtn2X":"j0FpgDtB8G1X","xAjFCIhwgKg7":"ewhRj2hQU5ZH","HpHZp545JSnu":"wYA1OrtNAUMS","XrTbHHf7Uyqz":"kakUviolqB5w","o4qN6coCIhvB":"HMsrV2tsaFzN","F30ImFn5pkWY":"4ydaP5ATkgQE","VeesLMNg82WE":"1Pq5y0wlyXm9","uZqnHMaNGaRi":"qpVVOBm75Kam","4SlXrN2hqmmK":"JFIVo0b6Y6sz","60hhOGFajtEV":"2F1dMk0PgYCU","JLAMBImwTQ7Q":"eJMSwxepREeG","pqcwMeH5aFiV":"nlHNGZsFyGSz","V8w52wbPiquC":"U2t6Yrl51TEn","UM1NxooJ13dW":"FFH2Be7kPHnN","qt6s7Bm8SGbH":"BOKBSGde4D1g","h1izAR4zPh20":"DQWWg4QOE06R","hKHpGig9nMUS":"6lL8fU6UCtOW","rHgAC5i2iMXY":"7Rdp0J5B9uKh","qB3iCSrccd5y":"mLy9wIkHtnlv","tDhcwmr7p1K2":"PI6cFPPFRPXI","wEAcdbsjVNwd":"iK7lgMg1zrhP","GZZdXlKRb5CB":"iHZtAZ0qsxjs","4JvoT4NBLNq4":"fV9egqYBYrL4","P6A4ADoEgmip":"TPHwkc2qAMft","AVGBrSZ4TaG6":"JVLa5fwgKixk","8Xp6HKRrGR2C":"Mld8NNoOLAho","WOLaAysQKf3P":"EKrd52pfxYtm","WM6jmqsf7V8v":"NXeEa9daGkt5","0Wkwx0IB3NTP":"h31ZzDU3LjgC","p0shC1oeUGAD":"Vl0OiMcszsJE","AvjDrWaNvDwy":"vK7ZQ0WbhhLj","mV2WqcFaN6uJ":"CvJbjLW71b2t","mxIFQJS21BLI":"Oe7FMh98jSeZ","0N7syYPbSWOv":"DXTarOuc2pqx","APVQneoDeNpL":"sR65F4uhSEKh","JDFImXncKLpA":"ajljjkXzFFvj","2UrQZI56Msbo":"eITV0K3uCPXF","NvcRYQRXHaWE":"fKeLiScumJ4K","JBCupAy5fJDy":"DoSycxDdU50o","ruZ3xogttv9N":"U7tipIuBfKi5","eZdk1GKWUAhV":"u1Wip7Br38Nq","9JAuIfQLuNmq":"wzOFTzskDq3g","6Fg8dG4khmu1":"nkhsL1XrxBjW","4A8b9pGHsaIF":"IiH130magcPc","262r796YVPPZ":"xAghWQsM4usp","vzdzuU1zHmZo":"Nb8ZVQVQO8qZ","lO7AHGwXhHZQ":"xa0sNSR7nBlU","uo3LiT9qlYI9":"PxtcrHcbrpEp","YJ2bBcVnGQ9q":"PlZBeKvTWrIw","fxn9KJuxlZw4":"Nn8La5F1huYj","Hr5F0tywuWgo":"WZbgMCcRJAeF","aI94Y8xvr4AC":"JFLkiqrLD30V","7JO2vkiMtHsR":"M3r20f3Gw44b","LInTobhv61kS":"0JlQGwELNYLL","fit6Q2emGXrf":"m4IsjSpbeCbn","FFykZrCWjMLo":"o3j9LIm4DTSt","6qBZGBm68bt1":"D4k3zokt09vp","tcmKgtA7hMgw":"89Qxn4Lj9zL6","qmVicUUvWWmj":"yyD3ruFtv0Bx","oGvrVhoNYDqo":"tLQIpClGfQ82","fH6sbQXDjwxE":"VssbBp7heth6","H1GSIDVZB2h2":"TgytMeYBMrgh","pSkkCKtQARqS":"g7e2MQOCMYU7","a4AAbIB6t1Gw":"FVpLfYnZDfIL","l2Da8OsPrPkF":"knzWEPEhL50f","C4q1zsDDm3As":"qXNQs9EkSL02","7E1ZXxGTe4j6":"USLOfHapMmcr","qzYBmjOqWyqO":"4L0pErMfkOSz","2cfM6fj6LMTY":"9E8A3oksidSI","OmRPYwfRv8hG":"MOQ2rXJ3OUka","I2hKgZbwyDpT":"XMY4XubgrSAc","y6H9SkiwvSrr":"iBqINrEsElq6","QCp8Fb5Q795l":"7btx5fd68qg2","H1pDI2DFd2Up":"6HrgCRTDtXpp","2CfjvFRCZUwB":"sL6hzS1fWNSs","5t1XXGppWkCR":"IyCSKKnkpJz1","yYefJqISkFpt":"YUonPcUP2C6A","s0pk63hw64o5":"2R4vXC626YF4","4v82swbGXEZq":"KGHbSr5FQykY","y27oQyWR4bPA":"1licnMRNx2KW","RmcJSpfvGk3C":"40pzmTg1g9xa","JVM2eOEvg01A":"fmqoxHn0GUNL","R4HOGiNNwoOG":"qvpJBOWrDjpi","P1WAt9qipe75":"DKde1YaFDvan","iR4PzredtWVm":"lSAmMnWbZEQh","vsGJICoPj6fB":"ovRvXLn9wzKA","VUoonF2T4mbN":"EAL3U0RQKHBf","ixabJDCbkXDf":"EWGLiWD8Jb4U","RF8rlsXx9t3F":"cWuBCB2h8iZl","Za7x19SfAvQx":"f7PXW0VWigR1","jIYox6NAXyiP":"3h32lLZw1Jcx","m2h9hdbIggzG":"w7vPPR8BrAqg","O3XhhHQ4kjhQ":"FtvQQWYntHfN","XHpKskEuxxSY":"GW4DS8Gc8poq","xZXUedK14Irb":"SFn8VUOQJ5w6","riadpMoIoqRU":"xAkUFWrPHldh","rrkkghEAG3wF":"MOOdc0BwQ1Mx","UIEG6Jq0uHBh":"eyBrDGzj1Ri0","giYBBMicRbMU":"FU8eMziZRaVA","UCfXNRGhGudT":"68T0wE3L0xeK","IJbyLVF8R3Io":"71YbVUslPEIP","MYn0fyZu6s4L":"kwzroBoRABkW","Ohm5FzVWC90c":"9fDw2VgjUeUF","ZE40hMHUvnVn":"IahXNv88GIH1","8Az9O4qi8GgL":"jEqziwO8GntE","90hxnBTe3fuE":"4sXtRwbMPq6u","anRLmfWDepml":"eynlvj0JfWVR","JE6xNe5oKiao":"CvO68XtLoI8x","AmG4U4vVROYS":"9k4lOpxX4tbU","u2uqNITPlRFX":"NQV33Ry7T5m4","f9TPVBWuBNEw":"KGbPI3IgieVW","PmySC7lCe0zk":"x5gJmCIrpZ1N","u2PeKoacMsbU":"BbuqWWOR0muw","a2sSHRi0dbNL":"25cwJhruMYyh","pl6tQlGOoRqL":"h8UcCrdz7K4N","anfHCbGsdw62":"Nqb8S9z4qhrh","gMwnkprwbyp9":"6NJCYBRciGxv","omIVp67I0X6p":"asfOJPQfSa90","3gLH8H7rVT2T":"Fex7W0m7eZ0h","O0fJZa79PeSj":"PGZPuQKSCXVW","XZekzvtaKDqO":"6U3ZFFFXt8yO","Ij60s2TwIKBS":"lV27VbmWmfkR","33ZcrkjVJlMf":"qMLDh5s4yOfX","PGKMFfH5Bm5Y":"cS4D9GY0xFOt","FFVWvVUT1O1B":"5HPrh3ipVviX","euuBUAPWhEBs":"9gYiXUSEJ1LL","l19ZOInj4Kv8":"xxrEuaJCwqWi","DAdbf2a5IDpr":"I5xABXbqUjUj","JdwQZhGeZPyY":"WLa4RPZMah9j","OPaGYihnyi0h":"OK1PCIx7t593","KChnDGRPLsf4":"gyPNvORiIWDt","n01TwQsyCu8a":"IpfWb6CD76ee","cHDaYqbR76iS":"7aNSlaAUAQGF","Ju6lGvHm4GdO":"nB3FsQP1MKHw","KQjQPSk0iT59":"cq5unTFuUxJl","f6pB5gVItlfn":"gGanMxjNiRiE","KARaVBj9IT4Q":"slD5GS73RfJG","BCm6Tp3104jC":"Z9YgDT7EaWX1","WSAs8BF4mJMr":"n493pJvZpLiD","OFAVYRmGAvua":"0NZciibLdpNC","EnC3joNddGBv":"kWQu4D8rjxvE","cZVFTavD8QYx":"PZTSzBzcN39j","18bdW9GyHm03":"hPWfWYeBqHZy","8Qq5vz7rqkFv":"MnnfqKSsHI11","mzceOTM9yZvC":"BUSf6T4ePVLn","5KQbAMcyJVql":"FgHUdYxuC3BS","G7DUT2Eu9ry5":"B2ujS6JOIfXN","acd6yCjWXnx4":"uyIeqppd4ulv","mpXgTtZz7zYL":"GkrfeIICz0aV","cJtpDQp5uVOZ":"PuZqj8o2QXOZ","DlALU4Qc7IOd":"1alhyLqDnVAD","jS5yulVkWHv2":"5SDXvuG943CU","gkTqLKgXQon9":"MiI2x0OWehqu","r9ciOp6PRJHd":"8mrZgFUgfxpo","755mVUif2EEo":"6FAGpNlkpXom","Gj35FA4uzk1m":"y1x3e7ltHUwv","XTZ4WSLZEqqe":"CpZP3CQEom4E","hAagYTE5pGwV":"E1xmR8a4YWOB","P7fwIExtSQc6":"RZlbLgSwznWU","JUFewj9xhHMH":"nwY6LbeITLqM","Afi7b62FfoZz":"vOLbLpB5uh48","dLwT1iveRD9L":"0Li33qj3ZWAr","WyV6XaoZxWsD":"HZnVUdyRBIYC","rwUvSWnZG79V":"jLb8ugkbQo6H","DDTn28ZOVXUR":"dAOtzYIYKmkD","UxIzh983fVRV":"Lsoa9hDgMM83","jRyGeypHhgHl":"GpzYwNalYtBJ","5NcmFUXUJTer":"300OD9CSLstw","ADhiCOcJn6lK":"DIN8fe9T4O2e","kk1U6y9BruOJ":"h2gWCC5I5xin","YvLAMBxnZtgW":"x35jl9XGolXE","hcJZ45eoiRNp":"viM5BZ0lDGKF","qfi89s1cRq6y":"0d1PcQ4M4EqX","5M7JApJSMTzq":"0P84rqvDmfKJ","8fUk5j5Onv3a":"Wr2EuKtX1Vc6","iwH3QSiHBStS":"yBQs2AZpBI2h","hz8vzxG6zQYi":"pl9js84y7uUX","0cRo99Q8td0c":"95fWglRwwhI9","glYj8hy3yxCa":"bBqqa1JdrREk","OXck1EZ8jIU8":"ILxL6d3eO4f8","J2CnAzNwWWwI":"l3KvMmCjd3Es","1x62GgsEFHaf":"jh7xngelYi99","Il42OeD8gyV6":"9uHAyzjpXW31","9j5AyAzH2xjW":"AT56FpXgZWM5","STguhBXff7pW":"KZrsLwjh3nQc","HfXHjTVNNdj0":"ffgSj6Kr4KqS","Kr98Y3IPAFBl":"GO4rZaNJhH9E","6UjHJkpqE04u":"g36Xqp2QgAse","5v50L6cdBkOq":"dQxytgcWdxpO","JQ5zcm6Ea2Y9":"MG9DCPFjz2eU","iBijJk6DG0ez":"Wna5DEoFT4Lo","VS0FmUU02p2B":"OIDLquXbpA9k","Ue2r2m7fGAob":"evI1MVMg0qbg","yBjDDlv4ISre":"9mFLHOXau29p","mQ1Mg3ji4aAj":"JllBtDJsGQHd","OjG09EMomjAH":"g1fivsAhzp4S","nCHSfFPjTUWW":"42izidGOVSFi","oWnhgqOJZa5A":"cEOM95DsuPaB","Q3gb8tkp6LGU":"IQ1KMZ7UqkT5","Jds4rKQXuHQ5":"SgUtVmMcg46Y","IucqXwvtfTWZ":"UKClCagKk5q8","dS85WU8ynjKJ":"yX1tFdT2mNm3","n8sYEiLhesZo":"pkcPfMM73e4n","HkeGJ10OGlcO":"Cn3vSLcrcpZh","3T3wA9ym9KB9":"unEWyYB7ALSy","b945Xuus67mQ":"J4Lb5XZJdlQb","RsFcIbT2pIlH":"CmSxGiENU2bz","C7jYwc8Zow6E":"Hm06fwAMmKf7","wCke6UngNzps":"yLZvW5pq1qys","2GqNU1EM8fCk":"btp7PmtParhm","e64QUAICUpeP":"0ZcEKRNewBAU","3ktQ0mWzk8V9":"wyWVQS1oPgPH","QVeygdqXsXlQ":"mTWOioyWRBHx","D8A4soj4SYxJ":"pIs6VzK5Jiu5","QARtnGFjZwDf":"1dm8DzStLLRB","pXIRywjYJSpe":"PZFAIK4SzuXy","YqNMzgZ7yLa6":"7MjlDRgl6g0l","lLQeXXjGsW5w":"dC1WbIdFkRYA","KV1vOhJNnqv5":"JVm3ojK5Pbhl","tnB1KkVDP6Wk":"knTAuP0jme23","W0q8AbdvRoIj":"xheTmehp4Oky","jYx8FhmctTzC":"IdWDu6BkwTC9","vBeuk1hvLHMw":"dX4Wqsy3yPQg","o09kCpNr9Hi3":"b2tctWAlQVfn","oBHd9u03K1eY":"yErs9WulSgZd","Eeh47O0cpkdq":"FFzblP43yqoH","FiUu0WEypqcE":"OdBvkuKea8v8","fI0U6AaGCvck":"IeiI80hYRsai","0YAKq3cux78S":"gNmeQVnAn9S2","nMFUYBA8yAbM":"ILLUEwCDxSXp","qKTPdKkywYZe":"SUgI8gOgW8Ae","NU5dcYqAS46Y":"GEq10loZFEyz","4KfCZOPJYnCQ":"VRPI6Q2WFEAo","SfVPmpQSSC88":"61rArbV4IsNo","07QpTvAhMWKN":"T3scj6URKmc9","dM1mZ50RQvJT":"AYELY9kpHH2m","Owoe9XZdSnXx":"eDJ0gCsirXQs","yfRSsKWWGXXi":"ieOnTMiaaJot","t9mjwXSeE1uy":"FyuMIWTHPtdV","CpScGRXpUCD3":"J1QPzWyap2ev","7RHkIMHuCk4D":"wdGRCQgHJycN","DiPEH7qdpRVo":"ARgwICavje6J","KCGe2u5kWNIV":"7NPGqhDx6FrW","Zsqha3AeYsli":"qB7nSZkcOKrM","LKyrQ2xbQt8B":"GjIU2gmC6nWK","1xv454e0Nevl":"gS3VIqoPGgRt","e0YOSKU7rSCz":"ZlXZYclhvvMq","sfgNASVSkRng":"W8sJxhkOKlIn","DaD37SNsB7gg":"ND6sDLiRGOX1","PLUVT0UE2we0":"wULEkbGAqp4y","m3hOLkksXImD":"FEoWoYztqdxF","WNTBvwv1uheT":"D919QwFXOBjr","Yvb12Ei5C881":"wUXFiMlSQbi6","DFr9vCMQfQBl":"leY4U6lUcO8Y","NXi6MdU6h2HS":"pmXGqUJD6uTU","cf9M1BEoHsSv":"cp0P69mniHe4","XCosNG3m9KXX":"OsqroJiCzsj1","BDoBIVd4TlCC":"V6xwmU4AXk5p","VIdfpnC9xdls":"B6SzbwkIa2kW","M64bnnZ3LTvQ":"WpZS77eE43zG","6t0yeXfM5zbw":"EFtu3uzmBxkY","eoIj4cKpfSdX":"JDyJjC2VVAw8","uQ4YpucyBSE7":"kMIrAN2HQRR2","SrUAAawiiOBn":"MsqwqEEq2K7L","vIySoI0UTFyE":"xMIy0lwk5gfw","Pzn5v6b5HBD6":"IyPD5HYklhZP","OPoNkq1l09bk":"C9zgUdJSJXqz","w2MsyDNy1mhu":"DaFpiLyzbrwk","3fshrgMqE2WO":"A0V1nVa7HvPK","Q75EI0FbQci0":"3oBqp3HmatOJ","c1WftUyqqbXp":"dRWq14hcLIvw","oZP2A7qcoyED":"Yxihxtl5cJrp","4SvtYxV3o1Vv":"fqB57gCJqV2B","Him99UTcZAlZ":"LyHh8V6HDIH2","J7FrGzk9bYdp":"oo2qyqhHQFuA","qhpqhuhwJG4F":"5Txtp4EIyFSb","PVaVNQnZQm1M":"1Dp2B4pPDSBa","tvqU03twPsWX":"DK79y19d5aGZ","i1Q5W7Msul1a":"B3pHUjlnmsD2","QpC8SdMhpn5c":"MdzJWWttNhoX","8NaW0FwEIRGl":"tzM5YHUnkVHs","gPqEgohFHyav":"FzszeGkMFw8y","c0Ss44FeNDri":"joznM6ImYpgP","LA4Klfn3HBVF":"SULdKeG73tkw","mCslRleX1Wc1":"YV5HS8AXzCaD","vbtuDKZMmRcr":"d9w7nN80vv1Q","ESC2VWmuqQ5y":"BXSTlePjcyP6","pbTrA7fzltMb":"w2XJw5pWn7hA","gG3YghNa0vf0":"I4kF23Ol8g8E","E5rT6t3zv6eC":"tupXiUOLX4kv","YggC5etKzB0w":"5F7Y0xN6DbRC","cexQgLx0AFEd":"GXoEVa7Vpzy5","7kKo4zY4VxTO":"QxsYRpQumArc","h7crPDlWf24V":"nkh6dFUDbUiQ","L5piUCty9YlR":"6xPzG1VbZIs4","y2cyLfEMM7Yv":"n4KcvTYJBV0L","r3SauKC2uVeo":"uhZeaUMfLniA","3FAR6MVuShNL":"i2HXdMT1Zl5J","LO7z5mYhpDL4":"V8KA7moT0avh","iL4atESmnSyR":"pa2bh19AidSw","exr8IbJnJj2Z":"lRaPRmBRCcmh","3BMz3B2WVExr":"EsF4qTw8j8oC","qvBh7oG6bUHu":"m71yi9suja7D","3yNc6J7ZBAYi":"LkzkGgNpYha3","zQPoMnD50uFx":"AorvUAqN2Rb0","kAnva7c5Kz26":"BXNTVZSTTnC2","JAFKE6YqDuTs":"uKf6a1DbVEmV","1NtEjCxew7gX":"vtRib1hjSmYD","4Qy60AJIJyXY":"xWIrTbepheca","nTQvVzI7yjcW":"aa6PFKf5bd8b","ETL169fqPvVl":"0vP0gEcmceD2","SZCkBJHr4cUb":"Xudzu5NbUVmR","sCMVIyfW4M6c":"gDF5lkXAgCOw","k91Swf7bSu5Y":"wCboYTeXGy4h","I1055CMIjA4X":"NHJBeOl1frkf","sKKBOYcxqkD9":"ATunmyno9zMF","g8uIv649wg3H":"rNAGPWluUUF7","T2ibTk3wWLgE":"EgCXeUp0aQWh","nwFQ7LNhlONL":"2pTgsctLADOi","oGtz3qJY7R8l":"a6IeJLNYXWFy","RE7YWHoiu2VE":"sI0seVweYHxF","6YCDHYUF7PDL":"NjEZvLpVl9Zi","EoLEJCHUzTDH":"Cgprd9j8Qs4d","2TdRdHR3DZNn":"L9VFTWH0bD3M","b3iZ93zlL34h":"oTXRw1mbGjZn","rrbZVIBZt5vD":"bxLVXtaIXBOX","h0jn3WhKNXWh":"7ynl42uKxBnr","t91g3JSSu3AC":"768diCKDtobx","lF3sExH5dqny":"BWyWQxg4wH9O","XtXStxpwdB4k":"RW5LCFrMhThB","Vv2DiHQ0O1U0":"Sg2unyTbEquH","FUVkf3m7EtqI":"dpzUXPVHDUc2","Ydvje1F5XDdG":"uR599v3QOdmC","yKyNsknqHT2f":"Vof0VIOcMbfi","cDgpecfNOLxz":"3YrgCKzgCRbU","7769BdHvVaYG":"rwdHMLcIEFLu","rCHn9Gd3YFDi":"cGvuLvJBxfGa","PVg0pPNx4SYh":"wSi1EsHdfPlh","igU8878oVvQ3":"IPGBt0yb3GBl","q4F9eBQL3nuf":"vrKGPGv7gH9v","2lbPBnDkxCfJ":"SjmTO9YGiCVi","v3uXeusqZ43G":"XftzUz4WRjgX","qAO9Cia5lK2C":"Y7fR1LIhIAmg","TYebP3B735fS":"spTJnWELUtPe","QaF82my9oxD3":"hKU7Pb8BEbDW","D8DXShjf4CNz":"UbbxI0FlViyP","Z2Pe8PK5x94w":"jHOnJE2zhsCn","S0RNyndFR2O8":"MYYIrUXN7syi","oLpIMjb3VOrh":"rGqoXRR1i0aD","KjOTCqxLRnAc":"84cS4r1hx9WH","s638djN9VtRf":"Thzv4auGC1e8","Ov7pZFAc7Ukl":"rFPzyQV0UGAV","GLSTpj8YMajZ":"NOPdRRmq86cT","gQF1jsOyjEps":"VM9VfTCQF1z7","yxIim57F78bC":"mZ1AR5n7U8JR","AP1rk5LeO2kq":"8bLVk28rEnf7","wy1vCPqRgsL0":"fGBty6ZZmhoo","VlVE3HmwQRQm":"MPvKnC8vsezx","5GtZWJSSPFsw":"m7C4ye06ZsFx","16iGjPMxWc3g":"tURyHwPOZZrx","beczz425EDB3":"TorBv6pE88mz","cWuULWitlaZr":"6Ls1psjHsPx0","UYnEO15E1nod":"o8oeijM1KDJ8","ZN7N2xfDbZc1":"lUVUR5hnfpuT","iOFhsPiBBuXW":"yUG1Z3vSQGbK","9GFv1DfEZL5l":"iqvszBSP2tDU","24ItbefQBSSu":"P5UJuIHmAuok","X6Ca1plHEEpp":"AU1ZkUxquE1S","D3rJ8eKadwlC":"bpQm7UUG3Twi","etlphHEqmMng":"kxCAg7aXVc5A","7dn3XVsypuzW":"kJnBNQJ2oW9H","fkCIVcUCARfo":"n8Bjw1Gp57Yy","EfJpoKP43McT":"EzFmPiPbMKxt","yyNqjeqWBaOc":"Xnda8ojh7mZp","MVapJD74JbgO":"bKoMWTd6sr3I","BBJDoh3bJL7Q":"3uuf3wQsmu8X","X9UA9gyWbZfG":"raKzrAtHucCx","4LiRdbgkbJRB":"3wRjnTEh5Eyv","6zFXCuIUZX4z":"MXIsfTnKzXyB","qJ1JUXzT7jsW":"f8rACtbdk2Y2","0ZOEqegp4KJz":"223thjqM7tog","hqsOb3vsnn8M":"dWAh9kOjPQKC","NukcuStXv1bF":"SSbaFKfY8ntl","PfO1PRuXRcct":"N5YIibyQHCiW","rmDMoTuUhNty":"DnCbAlPQnj0p","60WrbCJeYBno":"AoX3aJ94OTul","YFxfsDFaG45V":"RRA9CioEjMAv","3EpT4vF4Z14v":"nK8Uyb0Wn2e4","q6mhUtjuUVLG":"s2gyatIKRKpZ","VmNSlpTxp04h":"w3INvdgw00pN","inz9Z6HBn97U":"J8jHNhNWD2lq","sKzLSkNohszs":"sp6MWZzxcfpx","5itUGaFk9VoL":"dRJCVqiZNesh","O7OHWKnwY3ES":"DY5SYZzzgQim","3z0790sjYsbI":"gue6bxu7MTP6","XSI0F9ov7bGG":"kYD1hFUiuXJJ","FgXWTn0peQFy":"bMDYuYuINSup","Dmbz6lswB5BH":"uEtaQ0ouDh3y","fR8hJyHZaE0a":"zBkivqv0O4jr","51jnS7UyPvOp":"tLJJAeCJZTCu","E97EByi5lEMv":"4tpBynkc6Gm9","8lAgDtMHAiv6":"PEgcjs1rYRJT","J3fx9x5JEZE6":"jPi4S9LKcxDY","31NOjNJjW7Bm":"ORUEUuMBb6iM","NA28hKjIHaR7":"H5r50mlx6pGc","rBHSo7NWZFJ8":"FbPO9lI8rO4v","Evz7vfc1MmTp":"2hlRRudHaYa2","U43baPdg6mxV":"ZBUhJlObi3q7","ouSZZqfv0Bsr":"GWePLymQA235","ZHLRA0XhdCnn":"eX4Pzk11lEPI","f6kTyo4cAxo5":"ogvV5jNOYLhd","KCfuVX6YrA88":"zpCyibNWTaUE","AbE5bIx0lzCe":"voctqhsyGbky","kQpt2gYDnTQy":"7Y42Uuk7koRp","dhxWZMD522jB":"WjNUf1j6AB76","WNgMFpZlUsrl":"otDjy63F9w70","nmVPC6XuKaat":"7AEsINEdwLjv","8XcKQd9m4fIq":"svYOr3e55bv6","KCXnSr9iC1Lm":"u0Eek2psU1pp","cMycSzSy0cwD":"EPDEZ9TM56gt","am4WjIfqX1LN":"FVH9nhcSdABu","MSTO00NkiRhs":"LjUDa2Kjow14","9VVPmKTssYIE":"K8em7PHU0hPk","NlNkVZsRfUwp":"TZ4d1fZOs1hJ","PcNBw5StXFtO":"mJKtykUYZNXa","WFVZmzCcQuTt":"NtQ2m7UOvuKK","Wgc1LR8ivO3a":"zYrmJLAoeRIO","QknQmTyf9XcN":"Fy1VkpVivOdj","EzZVh3IMD2m9":"dx5kTxRzeQYt","0Tx2rLeSl4Uz":"GfuiaIQ6ORfq","jrySfG9NmbWH":"ZlvCfkUtHsnk","DmnQYaMvycBz":"B1GN9UySvt0w","6PCfml9gpsSb":"sYwQyFxSZO2O","8gAKPVdvhM56":"2S6QfAQasowm","0prO2hvYSbny":"f9Lq2ZIZWSqB","YLxbhixdRJ11":"z83B1FB234PB","cFhcpJ3jZhEQ":"A6bympswnhjE","H7zKPyCEYcSD":"v0pjL0owBqwJ","6xutyUO0mEPb":"iE2ZBn0cMM8P","24hXAR5uX09M":"khuFzIjpLQF7","sE9cJMLtIStS":"p6ejptRd2uQm","AsToMKexRwpW":"rHY8EM9VqsTC","MVPIddrbvVwK":"UpJ0st3iR0Vy","SAizQ3D0XULY":"6jaQBFvqcwGH","z5r6vxFKtCFr":"A2IZx8bZyjTF","vuRTXbxWlPPX":"dY5CA5aL4Akb","c7XUkmlIbWZD":"z2drgsdPa8tH","ifbmukDGmvwU":"hwR0EYZvRKq2","RldMpj8qbibr":"HhwFyuFoqBtu","teUfCMxzOgzJ":"7hoKfMHCen80","gOCFSfRjib5K":"l2vcknhqdI6n","OHq0RWmtcU9m":"az8mRYNtDu0a","orJFImj7Ic8T":"bzLilyEwab9Z","grBaDri8qj09":"siJXkjRoRnM0","OCenggAqw25B":"DMKRFYr4qp5Z","oxDNqivFhWa3":"M3OD2ynBoEA7","CSmi0hNbvphY":"N70lQ2hNjP1y","pipOKmhuaeCo":"2Lzcd5tkqhjf","2fAOYqbkYoMs":"zCpVX6Wcnh1R","vmFITV3TcLzh":"dTwhAev3AojU","4JTgmUkzdkID":"i291xJwwPk3Q","w7hEczkDvrrb":"YkM1C40RJoJZ","MKzjWSdEG8CN":"awC9XKn6OUJ9","BfWjf9lLq9wT":"UA2BXbXXzT6B","tMsi7G18SJko":"RISkuVnbp4IZ","5fL9nPbIccjB":"5BrLwW37e4M3","CrZPTeEtoG7O":"FQtsz0rsrMvr","O0AU5jTeOh1O":"AuF62InCC2mO","G8R2f1pXZmz7":"4ZmcDSJSzoVW","ct03TpOnhcuU":"D7tWsDRhGFhq","ZAQI54aXGCJD":"nhIWXzB8w6er","12W7AjBhGQ04":"4Hh8taMBgCGk","kg4ptDTfoPxA":"w7vpdhpYa7PK","krrdE0JWcTXd":"1VLcb0qPGG4k","ddsSxHx3qcat":"CMsKwkoUsZLC","fbDRfVRdFg7h":"lJgn4wJMp08K","O3X6lRilx0HI":"XM7J90a2U2um","J38yyAiguqUI":"zO0eJmhpr3PC","ky8lyFqlZaHi":"LNtofXVBWXPp","0psJrm7qds6E":"oB7EarbpcXsk","5U1HeHxftlXf":"QKL4jMSeXev1","N8COzZsNUt7R":"gmwu0YgWdLbT","c3FGsc4RiFa5":"fwfK3CSZaWU0","RBRVcxOdyh8z":"zmMT0M5OVlIs","7h09c42PtdBf":"I7qJmEncMCfC","BDnqyvb5TbQu":"rd2AhnZukXf7","kdMsqk7JfICp":"YBygECuCHmQU","1CtJrOVUrkaf":"uoMfUDnsXLZ9","CS98243HwUAa":"TNz2IhYYyFCk","HZekGSOuc6ol":"33xkKQPz7FP6","eVzi10I3eoMO":"8ytIcEfe3lyD","9mbVsBCkyg4H":"9eVN1nONcPnf","GQbcjFlULRIJ":"lMrxDYUPuHyL","PuOvX97nI4ie":"rTBADnIHOMT4","tZIpGwINjYGs":"FVkDF3961GeY","YjWj3ptPaG5v":"dAHeSnkHt7N8","WpSjVsRzfQAY":"sdAaIxHlPz8r","1JEoGdTsrcZr":"alvNhDoNl1rK","FTv6KQxDEMpm":"m91utPBBeR2f","j1azEtCJUl7v":"CT2eAywWs4qu","NoL9AjYRgmFn":"Qiz5lBNNXzof","PLcKHaMXFx9x":"20atoV6A9PtO","aPOVZOgzH06U":"bnSUCsfaEfXi","a2KOCMsWWUU8":"OupURyiVvoHp","WRIZJ6SeKtVV":"JHmWvKiTEHvM","OxeCzpwfsEfB":"aeSW2tojYSUF","5nUQta2NrI5X":"ZFZbiT69KOkl","Rdd7ekJweye0":"RiP25pzo5krX","QLFHWPuEHDmu":"oj0SbF5N8K1Y","r3xf0vSW16ID":"uinzpi6CqQiE","7Jtin8EN1Gap":"ScyD9Rgzs7Et","hUbLOFnN5CO5":"V0083l1feDEo","XMLpk4nAfGeL":"sIV2a1Mn7ULy","MIOILIdfsRJS":"GHiHkpAB8ZeW","q3qqKAYmlAxw":"8ktps8lJbjsq","rTgL16dxA9iw":"OgJbiEtwuuSI","HdxFIlJPfITO":"MuzSUNlsDdCN","teofK3iXTGVu":"C0LDdOIOC1IV","HnvuYVP9e9KL":"HJ2qL0bEz8zM","9vHEIVimiXDI":"kLDt0G8bzAML","CPxWTeVo9vff":"ZWsfqpzJMCp9","7rwWRn3VqbUP":"asoYJiglaTjj","vQGtRQSHlw0Q":"dxtfgJQDj1Te","ygDX9XrQ9sYT":"Hy5tyKiOYQis","KOpO92DXqt3C":"TCaWY3UValu6","EotpUsfskLu2":"NpigUFproRPz","J1hnYBWJZQjV":"EqE78H45iApn","iwJ9SU7RDiSl":"y2CVxIazZYpx","B1F9wBawJrMi":"WfgZ09fy0NDM","7FCN6zljjlyC":"lfl70bZT9AUl","Unv8LtPJXaOe":"eWvzElTG6y1D","3E4NHl2cF8oG":"lFMEyxmzQPyx","U7wrh10p12SL":"vTUza7Lp8NoI","BSKz0MywtKPh":"9gF5FxYFB1j0","CjpNLN3Xz4aU":"Lahi5whwkNjg","xk25piaTTV0p":"GMO50XboIcC5","SdjE0Jdxg4n4":"uOUjYW4GyPxI","HkdYCgcQ1Xbw":"7xE3m3lS8CJB","G9eYuWRy0kkw":"fhbWey3U37V8","2vyCNuxexqFh":"QXEzQGPBu9Ll","YcMk46l8sjv1":"zLUsgouSPy6J","5GtumtoS9onZ":"PMdLTKQ9CrkC","XHM0uyO9EJOp":"xg8R2aPuGz2B","8OiAIoT7pKLf":"ty4oSluj2PpZ","aVJlGkMtJDGW":"fIs1ZvKWhK2I","KTOh7iplpIsj":"3xadAwcV4JVc","TT4TdxkBfhOj":"Cj56QuaoUEH4","9LKWp0vznLPy":"PthJQeGpovPe","89Of7ACnbN10":"9TxNMg1DDIBw","4vaELCcqYEbM":"SlHZjhh9Fgnf","lbo4gGDaDVg1":"v4t4p4Xj2aPq","OPW2o7SH6SQy":"oNu2S77j98gU","z1oWedqOjCL1":"SesO1wf6TzUf","e5IHssF46mDY":"gjXept4pRY6y","gRPaFfD5aJJr":"m4rbn8Y4XPHo","uJDO4sxvaish":"gG9TLSsyUmRB","WFEDtYMWQpu9":"O04mY8JbURcE","U4J438c7z0vr":"Sv0AWabBMtcr","kfQMjN8uQgdd":"yRrcN47m08N2","4YYiqFgy97YK":"Up1S4A9YQQlV","R1gHrQeFrg7K":"XucVnn8rT9PO","cfwhcCvNn3ps":"Ge29Ca9SfUH1","cZrtmG3PBEK2":"9lgUGsYpkBwn","wJB3raSkNELA":"FymqaNy9UXop","JEoXpDMQkGDV":"24VAMVcPmSL9","P7Mh7UW62QD9":"E1cj6xxQVZps","iRaPB4nKTRak":"TEwbIyHpODcw","PCLRzRYkd9YS":"fVhgHSzkt7Xi","SyzqQiTOY4n2":"zwNAHOoiiutc","TKmCHqv6zJ1o":"DgRFndkXiCzX","GnAA6UVmmT0R":"th944zgtve8K","5THheX3hRvgJ":"l8BMWyaAfnbO","46RNkQrgCv7H":"Cz1Io8Bp6n7F","FPStjcHdI0RE":"DGpH1uuDO4p2","2TbAV7ygrN8G":"kzYlMOFWxtCG","VIGbUz9CMRmS":"g5r44axw8ceo","iVpaWm1GLIaj":"9Ta2A0W7owrU","F9LKDRUhTiSL":"OI8kaoCCgE4v","gzHebjJlMcq0":"ZBWfiy7B29IE","Ip15P7lgotTL":"7x5gpc6uc1LK","0s9q6I5w1YvX":"bX9pnLd17Zst","WiWCW9crdeUP":"XtZhpkm4A9ps","h2FdyB6aA0cU":"Cq6uZlljXAS7","ZOzRTNHZE1vG":"VMntZ9yYvEMN","vhfb3HI4ehEJ":"pTdAsg6rDo7J","2nUlVnSEeMPH":"4p7umtLiuplP","EjxLSCchpoeh":"HV7c73q6Ivj3","U3g0ppQFhmG0":"ZtVlg15T5PoY","KRp6Po5AB42H":"MDeM0scl2xfw","lhBzK40sJhou":"W1RKP8rgp7zY","JcjbICHD1cGy":"DuIti8FkjjUL","yGYWSJ9pM4kE":"FDozz6FFSfZm","aurTYG7tDLQe":"ZnpETICl93Ne","5CJgtj1QqVYV":"MaJWUWvNTzfo","GteN5iRNfjyc":"MWFCdEyOGgzS","t2MgrobsXoGj":"0ErRWZvQnzCJ","xxZh1OCMvoiP":"jWg658CD0038","vl1ifja04a8v":"Ik3ofRmTOiZT","hZ6gCTnWZ7kS":"ORIwrjzF5hhj","JdCHU5hIPFYb":"73eIAI6wVGSu","EUBuwPr5UYgC":"1U1I09FAjgtq","voEctuJQ9JlF":"oy2S1olJ5VmG","tgGDiAEU1BI7":"DlNVuXAn09Nr","OIEqRlWlu4fv":"Fl223fHxXydj","5KZAOZiQnlLi":"jTSJmVU6o1VT","lfXJMu3xkNrF":"BPzw6kVceqKw","NmAFTp60iXqE":"nWisKGUkbq9T","nvP2aYO0FVCG":"a4KThZJ2JI6q","RrKeqbK1uTxN":"PcZ2GjIEDwIc","tocvBl6ay4td":"d3fVjKRzTZ8i","avSIeh4SznYS":"ymdj3pyoWJZO","dbFWZrqHFNye":"YZGpegCEBFGF","SDExB0raRcV1":"E7BvIuYtBQVj","iI9AHspMyEZC":"Xm2xXwmCL4lG","GZVXtkxon0co":"mMQDZuseS9yi","43AMLV6EZ90i":"FZMyuUxNJKkw","ODAC3KSozkxq":"sy4P9mc1v0Sc","vKATKnYTEGMe":"La3My1PbHcDZ","BjxgWmibE8DY":"6Lu8C1YGrdcZ","lfX69Kdvkvcr":"lY3weAwkOfaf","pq9rQIfs4uST":"70isNbPeU3am","KsWxzAQ3uZyH":"mrC9bGBw5zox","ZDODJt7V26Cd":"vXcuTCPz0Kvl","ZyCUJoVq0MpX":"81YHObf4AW6I","zXv9YtbJEAtz":"FL8QIs8mRbvB","6acNzSFSAByw":"PGO3wnXaqGRk","lmR6LwF6r8ML":"oKzzpW20QQxU","bKD4b7adJ20t":"W8sHaFznGJql","pJHC11aAGTNi":"xlSlh2AGmYS2","ziI3Ok6SIGAQ":"BLJgI03LX1r4","kRn1oL8o1ePU":"gTA0oTXPk2ed","LGssEofT9hyh":"PuOmZdcIRiqn","EIN70yo3QmsI":"4w8YoxhzhsPp","UbK6hvCFbEm1":"AVOAIpjlY0NF","Owztb5t2Dxqv":"GZbcZRQ4lsIH","pWLMDCHSa4OH":"8Kc3tRTDELLm","Ck509T8leuTN":"4p3E5CjMbERN","yt5UjJEDQ0Mp":"srii5zi92q4e","nH7nzqzmmDoC":"2fCCw3v2o04j","gmaYZfg0NEpS":"zO8dQawnoe9W","fIYb4RzYtTQd":"vesKLAK8H8XA","ymZWJ6PSGJlE":"3hwZoDccZFe2","19tq0cSd26ek":"cvnB8del9u2O","o6wTMO8iRU4r":"iZfiZ28gOE48","VfTIvwF4V6hd":"MRAVE7OwY8uc","ws7FxVMog7m3":"NGRgxd8AIial","Tgj8HxMZj89W":"VcKmHzObuY7w","DOJPnPd26C6W":"cCYGrBmSHom6","EWAbjjAj8PDn":"VdpLE1gHTEby","zhkPNDCAogQ4":"f8r1zoaMnEV5","nrqc9YOW45UP":"ojzeiwJE862D","40FCeNVVN2Ev":"2tA5WPvbdGNS","E6hpyiIk2Hcc":"60sxAOAGeU0D","0CI2EFvvR053":"n7YVdD89tld8","XJJ6Uf6va49W":"gyzZq9XO98Ye","Sw58RqH5o3Dg":"uLb0WxrTwUK2","1kEATi7KZIKS":"ESn3x35uUkDq","CXSUWjnBmRKy":"whxxAA0HgG9e","WUOKscF71qVM":"gNchUKyg1fRS","mxrsXPngbxrM":"6diRCVxKTjZO","PvxCayJGEoVn":"BTF2mMgeavWe","68FJowY7bmVv":"DOvSNXttoi6M","iRsWGwzUYeKf":"NHGy5ZWMShD4","hF3gEXi8qgDZ":"WiwN4aEo4SL7","0ZzJ9Yu9obSU":"DqkSLuD0mZMT","k9hA0xbtzIjb":"4ZOYzXUiBQa8","F0vZ1yCf2P2Y":"HRVmLmYksltt","L5nNcD5UGTtt":"pHgVUQgXOMus","yCAsHLbRuPvp":"kmfWCzLcCTHq","zd4xMEeXxH2G":"G5n76XOqVeOE","oPtfRreUrGM3":"W0xAp4zx81bX","bdnHeWUHgjpA":"u4u4s7vcAVix","07wQYHNWj0LJ":"ykYNyzBoxN15","tuyofGFmsE0o":"oAIWkb9ak2IS","bnASqfRJom1c":"eYyyBxRmgrJL","PpsZ28VVm8CL":"zqGIP6qB2ORK","cKy7ulXC7Cqg":"rBBprhaSgl9B","9PSZ0ueoscG3":"w7Wjets6ogJQ","2TGHfTKHDftI":"4I8G697vbMXk","WNd811dSXK7B":"WLLW8XXLjWoZ","0Ri62CMlzTpR":"05dkPde6DxES","cmXHplt8qeJx":"k2iP2C66xWp7","roF2NiXp4p5p":"hXzcMnpkaMxr","vC3DiraRLxWP":"AQPCojMYRvbf","OnLc7QEb908O":"7e1foHcj2UQz","wk5rDbF1JPFR":"TkGSgAhQH97N","gy0PDQw2XwLY":"mxRbJgKHG0PS","mtLtGD9rueHh":"EmSBGLCwWZoq","E8kDLqM52e1K":"yMx5CjU8OQHS","xagGEAVBjxJy":"0dck48XL6ACm","ESWzvSAX06v5":"ULx5UmjKPoqE","RK5m5uNSxX7W":"UPZWWxvIrwdp","VIOvlfGRihs9":"8M5xS0m4xvwC","zNWRtsarcBoS":"UTRxF5KtwKco","hi58mVPYyoRL":"bxbpu4Aqa9I6","iaqfBfEHowbT":"0LgfuJpI1NE0","GSelsq8ATWei":"vRg3vwLSwe6t","aMOBbiJLXrxt":"D0T4xqdc9Djh","yrECImjpw2Mj":"wdZVekfLgoc9","xi59R58pnJY4":"3gRUOhOGaOhD","WQOCxU0nxXA0":"WPzfd6ImASyF","PWzP69NM6PzK":"omrSV3J7ouM8","gAssF9q2sWpN":"HT27ceIworHQ","r2V3JX73ZbRF":"Q3iNzKsAtNRj","TpLCRy06tPsP":"ruardP2PyGgS","2VnKJwnNSHf3":"EkNTplSdYyjO","8aZiBMohpfTp":"AsTj2g9p810q","6xLueskKmTRs":"6FYku9yaMmU6","yi9QVp9JZlDx":"tRXv9NS0Nd5l","lggejeiT3m0L":"VV0be06ttQ9W","AEpZWp0FLwoi":"rnYkA77oLcUD","Fk7j23MX6IRb":"JqC7fMTs2JnT","M7f0NTFyvWop":"FYhe9ES6pXQs","KwlrakLq9Q5R":"1biADSK10GXG","iz7tMBoKHXKY":"R9RAA53BKRkP","rZa98Kx48yMS":"sCTbUKY8xVjn","ZMjdHnbGPwsG":"V8m65YX0dgoC","Y3nvz72r70w4":"V0xPLAhnjU2w","L4Z5NrD4mteK":"8O4S2eCzj5fr","u7j4wqXgRv0Y":"fKN5Id9wLh2S","XCBtE2QJwijB":"l8wd68txtgc8","0xlm1EwfozPy":"pwlAnvFO3r4f","Zmy5aJs0wNnD":"IbljqMZ4aKvs","In4Sj5yrFLFa":"flD0Dmqgk8Cs","UCnigpL0Ln9J":"xPHjoNl1ABbM","HwcSFWeNf7Ir":"GCu2wcXHeTew","dbyqcDpgFILY":"mZL7SKl1svPm","F7hlQe8KEcRl":"XOAXQ8pcGW5w","d7XjXaZlhSOi":"oWY5mG0vq5S1","Ft9NqMSzVegu":"gXd5JPfZqMAS","wRVCoEJIqJkS":"es7706Ks7Qcr","PCczvobfy2p6":"n5v2BkCSMMg6","Jv4pRTRqZQXp":"ZJD3lVhewkWN","812Ef8a59oc2":"6wNVLyz4z3Ti","wy5ceCRIQxTf":"auMTAdUSJN03","swshYEozxQ2D":"UtK4vhE9aAXv","MpJARTAy1QPq":"e61jXrwxkJmg","jl1EMO9Jo8wx":"ye3TnUrFJrPW","Jl2gwUMUOWHo":"76ljlgmC03DW","jgMeJSZdoxNb":"KGv1Ig2R26DR","dre2BNBqcSYi":"Fx3WZ9Aqlu71","CNH0M7owjn3f":"8xvU5ymc9XWI","ISRrwQMhO5RO":"hYFyn3mUTbCp","kXzrF7rhCwDi":"BwkVk4AOgZQO","BgZ42nmRo5gn":"zt4BdgOi8BZm","raQZFr2I8Z3v":"GqAxbAnVVmpP","0UoFsGOm0cL9":"bqcD0zJhT484","EAASK1Jaw1YR":"UdoGWxetARMU","6VV70o40n7cl":"swrwvDgwu77a","uxpsd2w81H2J":"LeIIpq1vErt4","oFLQFz0Jt1Du":"03daVkH5qegO","X35EPsoOuUCF":"gGqRAGUkaH8h","b3aITr9n4Skx":"aK9JewZyPCXO","7jT8wXUQjVKW":"CBPadgZbpuoc","Vnyy35GyAuxO":"2JUY9BNNBYSh","OfIqwUsnApSm":"qNJFAOJAJbXv","jsFAgeOGeNFO":"7LVdKj3wAXr9","flGCHFhtxpRL":"tnXUDWgnBWUD","8LpfCaIebUYt":"5KyFYpL5gosz","IRv2oVOPvq3J":"C5lJlAYYNXny","VAGVjyv5U9Re":"sLquH1QjJVH7","06SpdcRgiifN":"wf7rrnkYcGt6","drdN0pedILpH":"w0fhAj8qrbgH","BPgHUVgCFqUO":"afmoH4FVF2s9","Fdlk0XHiNURO":"H4oXOwvuOToW","pRGr2bUbJqFX":"u867ksb4f3EE","Wlw8OCLWFgGx":"pCrBC9PgTIGP","587oScU8HRwW":"cQ83GTNTMwCB","bfGT515rp7EH":"GvqJZjXsdTkM","T0G664Q75BaK":"NT82l9l2ug1p","sGvig5LXGLzR":"qtHZUUfHkOHU","MWiuDZlKwcMf":"D2WUPKPKpVbo","8wEelLEIwa8r":"Kkygdx9lnte2","oTnSmZpjq7Np":"5zVuR4WgjHe3","enHhAv0OEwaH":"nxoUgnovBkr4","ocQzLgf1fodO":"N7akZKnC08B7","vdWDoFHz0qxq":"eDVVxvVsCyHo","vgNsS0CHG99c":"rPwamI1IqoOz","xLdLxzJqeyva":"eOJ1locfMm5w","Y5VLxeMQzJ7G":"uNAQuYPIQ4pV","wtjjMOErxljx":"0vBzqnP9PLzO","gn3LzPdiLd6x":"EiDT52cBrIAM","TucraoqQBHQB":"hXOOIpHSwYzR","VMjkzLItkt7J":"8TOwOGtkmVsm","d8NOjYIjKc9E":"UJwZu3pnWboE","FsbwNL7CPuGr":"nFvGioj96jGu","Q0yQ2PZKWOzn":"wDXJzJceTmzC","kf7Uva7KnJeR":"yzlnbuHKH6dE","ClXUdAgysGzH":"Mvc3sHjib6am","ilyc2Kd1OGK6":"FGwNpZrvnzGW","d6CjK4od0rWf":"AfEeGlyQIyK2","xzS13peLMykU":"NviKTgH40UhK","Mk3wGSuCM5PJ":"ya7DdqDzd5eq","zXblbXk0Qwy5":"zYqXiPeHwmCg","6n8UmCSuJy60":"w4swl7nDbPs4","Xt3iHEyEwqkC":"qe5KNzy5XqZO","iRqJeX23SRxJ":"hfs2RF06Bnfr","GkIznUyPB4MB":"80p7vQqHoWn8","BZyt9HoBi484":"9LdhYwr3qEai","0JcSfsh1Wdlg":"k6Tfrc2ONGE8","uCTDKiZSmhZ3":"jM8uCBfJsN4s","2ljUYMUMLYip":"4syug3sLbEF1","kaYs28enoPN9":"XwhvicEe4u6G","Gi8sFd2oxc0H":"YgZGjXdpIkiC","RFWFfGTpmLPl":"9hb19UYUhrMu","99aVSZikcj6v":"y6KPV9pfNOwQ","eFfZ57BvT9bZ":"6e3w1nNqjGFN","nwNDyeNeqv2s":"Ecvvqc3VEqGZ","Ca5wr6SvxeYd":"lJU30q2Ri3vp","v7UxGdSyosWT":"LyHPRtR6k9Ri","LjoMEc6HJw7R":"RHLNP3D7vORy","1W1jhvxQxRIp":"L37AiSh54EqT","NT6IxTnTxO4G":"1w2NJJ5RLMht","lq4kz6SJ7soh":"YGHvT2eX6LYR","Bmhkhy2s38IL":"5OBjc5Ozg6g3","b6isqxYR2qGD":"MDSKOXOwGFcA","yHOjrmxBdEND":"1joNIoNzOcZU","9nudA2nQWdnb":"g70hDoWJqshR","YOp62xiSGVsb":"cG8DpGmN5n5C","azhroXHO0jQV":"F2z1xxOwU4tp","iZaFSs2itLuU":"Fd2HAZP6HnNy","m5XRdAQB0dI6":"D5V5wpEqkEWA","CPBUfhHH18w3":"z5MSmGLeOyge","7eMojPr0iL2P":"ifOihG1FQ64p","xSC4SnGueOPG":"4MgDjL25CL4F","RmL7kCpWbCe1":"lMhOGmIhFOYi","NZt4k7VaZnyO":"VUTjzP25ALVV","cLDKevLScwoi":"jreCTi0B6ZnP","MR4BE8JysYCa":"1ofL2gjy98pu","CZmzrerTBUjF":"fM83vXmkWZCL","Q11FgOYiQuTM":"GCV8gaAlnqHT","WczZaLTa7Ubf":"HX3zfGZyMiYz","dm2EkanhU9Q6":"wIztyheBa7NG","Z2CtVKaT5aG3":"vTYked0btBPW","UFNGrFzgkZX1":"gRDz5Pi4fYsr","f4qE3Hoc5J6x":"Kt7NTRYEHzqD","9vRNnjSX9duA":"ttY4QbBTpbdK","gb6XGGCyal5m":"84nLTlnqPPco","h7Ka7QL5HfGj":"KdZyHnq3b8dX","9vwow4RWpm1F":"vOlwmjvAezs8","vCQ0IEVG2OL2":"fDw7JvRyOK1A","UwN540WTQmRf":"ka3vaDcCMa1H","csrl6WueM0uM":"kJ02VabAGbSy","A2KqDewh5YC2":"xSHwXk1iOzIX","KpmIQ5q2KiFZ":"lpZLZgAdI0ON","ctK3u8j6LyfY":"H1ABrnj973Uu","DSuqQcA1s7m3":"0mqKsDn24Ylq","vCFsgongZP8m":"v5dL0ZoRUAHm","oktDdj7pNl6I":"ap9f2G39x7Vc","EqwQZN345xRu":"t7t5eqfWOLXM","nkipvZSHVlqF":"OZVFJHUZuD9R","7nfJNucP5LVl":"ZIejlhOy8vJL","8oSwkB5EnaNA":"NQ4yAeGOR5W3","AP2N6L7jJMnh":"y8ELo1bRVuTt","NLLMMTIBuiGI":"D5chi0N6Ar7m","nLcbPzwgNtMO":"YCR91khbHMxU","x9btVo9ajqgs":"3XVgyACzjbaz","hoUwGJW7B7T4":"scYDY3qcUMn2","THCgk0XGDXlx":"oNZfFwrGPmto","0j3TamzCyB9Z":"urcswrYsLRgL","oj40KPGMSrOF":"347SEixLNRjI","a9m7kF9bzu2f":"qCtad8qKnRWG","EKQDvN0alh9P":"kIMBYwtCR2lK","r04CyVGX5bDJ":"0mq1T5ZJ2jja","ejRqgnYI93cz":"ncIq4Il5K4Gh","70fyFfQIhwYX":"rbW9ljQxO6Kn","OuKfDEf7NRGi":"79ADuQerTdVS","nXsUxp5cvdaO":"CgNmtUwkNlaN","F32AZby877nZ":"97naSwWtT7X0","UkjPXGJWhYm0":"d3HKQaHIivyL","E1wPFghU7zxO":"JJ09uzyXQWcf","Nei0IcViXKhe":"w4vlK7BwGnj6","48hT6YeZX0pu":"PX2UPpnxnyqO","w6BwT6E0HN4A":"55n7Iplm5FA0","b0OdMEGNg8f8":"l5RydEu6hus3","0IPmg6kOMT8d":"HNJVakJHNIUf","txNpBdMNw4tX":"ZNXmfHeBACMC","ojSglFDxq552":"cwOPAFtcOhna","trrHjuDsNsEo":"2JHntZgW4fZk","Blnlwl8sJNyU":"gZ3WkELRa9j9","wj8xqNBPwFRp":"IF1oLrlYNOEX","kWCitIAOGRDz":"NWEsznYHIWnQ","S6OWwZPKrvLa":"bDagncC9665B","p6GarquyDPVC":"jkZeRp3xexQf","Zfw1j8GIuKH2":"5CZJz7VA4Af9","czFtiXZdPkuM":"WTjfBPWflbni","Q6gFFoDI2Sey":"8cHq9tJX4g6g","dpT4c3a38Qaz":"efqUAmYRYQnc","2aOyGCScg8bH":"Gfr1OFENLMR3","lVd74kBPoMYF":"uS9HmdAH3m9P","eowNkhzC2IUw":"qLqHZpf1QXYv","PdzkwEXTr46P":"5z070hYSTFQg","ttGXgOkZ29hY":"L6PlqP0EMGOv","OaEtBAGRBtwQ":"VxYMyxW5nVpr","3SmQnvKe839R":"zZlBG8Rf9mdM","XPE9JvSjC1dW":"HvBBGsAQJsj3","8ov8VjEdiQcV":"XpEWxXy5K0Zp","ym29viuUi5lK":"vUnzoZDSGF1D","6m1VebckxxSf":"CffYFrdXoWKI","w39cviKvqrsL":"HSwoEC6Xj4kb","x5pnwvHxbE7z":"HGo3FMECumDs","X0AHlkJyrfJA":"hcZ7DTodg4I0","tffiILYH7UJJ":"zabmG5S5oYUN","4uXxDr4gje1X":"CYm7Lv1SJjMO","eCRuaT7y0xsJ":"7Q4B8RChn1PZ","4vB31up7Hr1E":"VtlBgKimnjIm","vVC0VLW3y9Rs":"h8mLaJiG7kf8","iLHp80P2Ckbz":"NsmbblNbLAit","KAS0oNzADHND":"4TmSRJWVPQHd","YRtnG7SKVHPt":"91qUhhWFxgVN","Jz35jsk63t2r":"SDXSz72nJzLO","KYh2NGxPSekf":"5j90CHRAijAM","TYYr5lunJT7Q":"XHol2Hj5UbLF","a6fSaI6l3JIX":"dfaI0XScNWyF","ocZ37kRqKIMk":"RRnRCjdpyqL8","19nnApj5p8Gw":"H3FA9Q7lFg9e","ELhO1VXLNEfw":"JZz9oQ90SW3w","TWmvTcZTRYr3":"0Yrx4IpPfwE6","3bopIIUy1QIc":"I4VhAw6XNVuJ","IeSrwdhmaH7o":"8fAS2FRiKtAg","IkWI9oF8chkb":"cuaaTHlq3c2I","mFGWP2C4IIhL":"nT3hxMrazogn","G3RhGFZPQ7wR":"7THsEISSb8Ou","wpEnfAGqiA3E":"grSXYNl5zFj6","7OgFQQVI0xKx":"KTL2ZxKu9m0P","JyL0b6vdcqCS":"poBHyPgSMWYm","vGuDR5enkWHU":"hDGI7ebML5zT","4YbQxr1bGReI":"qGpteMfmUuCK","y3gwcmHqkd7o":"VIUBAdN3ckgD","PXVuuqweP9Bo":"28j6iwfn1LLP","kFFslPOwJXi9":"luYyA7tsaLo7","f6xKPOZQLfbP":"vt4etHAZcYzm","41HRJ3wSpo6S":"2To8lXorT8tS","gIum3Aya5Zjf":"bVM55MxDVoZf","TiG4kZpZUJRA":"XIUhF7N0eSj7","zQVvxShAKIRz":"pDfLJ5nwIux1","AyFqH7jpp7i6":"ZA3GKTfHtsYV","B31B6cdPxfvq":"nLylYrNcQ11T","GgX2DciJKK6j":"GKWzIeZnGZkd","5rmeQxiIwEQO":"hUZIrK37wMRe","mYQ7sN2WItKC":"sAQUizVZYPSf","RQogHyNVOip7":"ezw68UXFaNqF","3dLyKXzfqkTr":"Wwd2kywIEFJj","wxowmcZdt3T2":"GY112I3oqaZF","sHee5lLJcdrJ":"Jwcw5ZqCPJHf","qayzUXxsgDKz":"hSmulEwaaBTY","0YcbWHbnfLLD":"somCzfekufIM","htOrmXThmX6j":"YFKoWfXBj9Vu","PEWkgDOVEPKK":"YLcMsA3qIfli","ag71NDMFON0j":"VVOD9vnAFowL","g4cHgFQ3dUWt":"mmwaD8hXUcsp","Hj4orKIXR0ss":"brnISn4yPY9r","9bSojOaBcqTN":"OUrgwSo93sJd","ZQlQLqzEx5Ph":"HehL5xThf71B","hO5ReSUnr4nI":"YtY6wG1xmunY","eY6w8tRS7Xyp":"eqbokTcUajnM","x4U4hz6Vmxyl":"EVPwgXtaT8sn","ZQiGGQNOjLf5":"IrzbP0rEODBu","VPrlrNEdQi1J":"5ehBUiQx6f96","rBrCk7VRFk0i":"8QKE0MOHveZ3","YTf1aPb69apy":"YZp2SC4P1DV8","FNRy52XNfkbj":"D2QI2191rsfJ","2SxTqlhQrBon":"QZblVap94RZR","guBlUzb57eyJ":"orzisBEhtvCq","GcVmeteYCTLt":"fHBcmvuwidtK","D4GdmlvUk8qi":"pz5CbPC11yLN","ObZNya7XYhhQ":"7vHUfkbRnfNK","RNLEuNgJP6Xr":"QAnzRiTKwG1c","wkuGPWhcfsI0":"jVkCbGhbHLww","2ZRkp6NesBJo":"xAqH2gkIeaAK","qVtDZOq1vNxp":"Dr7bmIUdFpmP","jTSaL4r53qSL":"NkG0Dc5Xgu8o","RIrKw84PbITJ":"4MDuXMW707VZ","XzaBTPgwficT":"HtHFgo046bBV","EmGVGCrry2iy":"zoBS8owj1uzy","mn3W9NrkLvgh":"rrtC2Cd9NhhA","Q5vphaPAU86g":"0cj2qzVji10T","9MVAmcyRAIb8":"Lx2NHt29WM4k","NVgs7wZkH1Bs":"A2csAdJebknQ","FTlfXFHKPtOa":"syq605gDhX5O","IxWy7Edwc3Q9":"4abS5CX2UxV9","KwBGQw195lno":"CQVgXNBKUO6O","5CkfzEGclYbx":"bvijDSsMePTE","LF5ZtN2TxAfL":"aMhgFfSKL05b","AoHdXzrgOFD8":"WQ9blOSbJk48","3GrXoDQ9qoLl":"N5SkQFrtNFQ2","SsHKC7z4iynK":"HGFnGFb6SpBm","DyA7ZrTpu07q":"aPZ7hqKe3A8m","j9fOrH9ikuwU":"5AtxelIBy3vq","0a7HrXND0DGN":"Lyokkynvyqsh","jqNA2R1SxseC":"uwiYl87tuqfV","nk74CvFpfRMy":"hvORXuUoEbCq","ih9wtjFKzp8U":"P9PGMfzhd4ut","zkuyA5EOFndx":"QmHr9RIWW2XB","jhwyupwBi7Gq":"1bUf4LmRLz89","dfolcodOU1eu":"v5x7PwwgM5La","qtHOLgUBM4C6":"zshpVfFv35PY","eZC95OMvyTZk":"8l4A1N7Au4tg","qUrSy3fCnUC9":"gFroLvEqTu1X","pHlvjnFcug2e":"WCVWaDq4WMmP","2USAGbw4HGBW":"SrcHpmDRqOvV","eWgLnN7hwN0g":"owzA7wOtOZhe","Auy6vVrYrjoI":"2LO8NaRVyTgT","O1lMkgF9d9CO":"Z6j8AjQ5JVhv","5cFBbmKldjXz":"kny7hH6k1Ss2","MItiCcFh6A7e":"FvaAu0yk29aW","prEpyLk8mjzq":"ein1XMfOKlmc","2nzaugmopq1o":"cPVhtkLwj9od","WGo4S20HhAm7":"z8XSiMvK81ZI","COnrifaTnBrG":"YXocMUJmzGpV","IrWhtKte44cz":"tctvYSOVRNEJ","ASSp1nxrcjHC":"ZPlUu3caFpJC","Q1BKEWgXcNBV":"AEcwBV7RDqRV","bngJ9jhgEu3c":"GYVskHxRKLpw","qHHYjyPaC0Xa":"MbYFHnV44Ehe","6i7B5sDg0zWc":"ud127u3JKndk","OuSor1NMbSiS":"Yf1vAe9Q8dPh","fBTzYGlGLNS5":"tyvnlUc5GBzg","D7xQCf63tTG5":"jcKjuKGHt8kU","elKZGCMPO24O":"KgfccEP6PRzG","2pQXQrPHTpVx":"H3WcX0dHGGzc","5EkCJabmESly":"GPhIyY2oMBxF","Nu9d4Nn9sQ4n":"aj4U5t0eBHKg","bZ4WMpqoKSW4":"vTMQDBPLrRWy","ITNi7EuFhpux":"AySThZAKNG7l","mdSyqtYW47D2":"d5n7s2sesJOF","c0cSquDoTmme":"qLtFylIrICtS","Gcvi8uIoMx7C":"R9PeN6IFQAM0","vNnsynB3MNt4":"VGn4RbgxpksP","6jimchOKjKzB":"6LGvPlIMUkce","1o4spA7N62mG":"Ptyw9KATH2qA","2dRldV9MXCXG":"QdyaML0ijrRV","tFONAqE00Co3":"iOxJTs5M5o6U","KPewpSDSkLEm":"BoYQ1v7QkYPg","sQDbGMsRiAWT":"dMvxHSyhnC00","6Z1y893MSXBT":"8uoOLWDejDwR","fLxD6BkSv28i":"Nx0YrWKFneZ8","viWGhG3a8VhB":"TXkaGodvTTt2","ly9uiRQHbZGu":"DFbf3OWrse3J","e9AX0dw51F5Z":"MEUSAwM7qEZP","33gwp3GssvJf":"nL5CX54lfovq","Xz74Y0TLAVht":"sP6SatoUszvh","8rlheJNG4Rr1":"cvQt3VPC8b8X","82N74Mz3zDkW":"t1dDkFIHexTz","ugeMY4MBuRod":"4IKUiaiZEl1m","1LaTQekZMcsh":"iaJvAwHnszIi","0LiRwLm5L9E0":"FzSf4Mg5EMmd","kcDITPvuXMdc":"kLBxEZq6LSem","6a7fOtsndSTS":"ntKFr7DOtNsM","qA3PWB3oacgT":"HXDqkpNnAS69","sIDsI3QkUO9i":"gDaOLVXcJhi8","yVvduMpkns2d":"QVKl0DM59w0M","jqQ2rnXlN9Th":"1tR2quLjLwFy","YjKdVuaEcaTM":"j0VcDwASu0T9","BGLmK5sXY8lr":"V5qEA7XOdqZa","xTd82KC7gecp":"mtEpMyiMwMeN","oUFpZTqsOZum":"UsfxBR2H8OOT","DZwMevrUneqV":"pF1EROGUbmi5","GM6w67GvSoXz":"l55KzqP76QtH","LbdkPtEVOr0K":"juyoCAz9ArLu","sWJW4k5R3yz5":"pp4LW5xbgs7O","ie2RGpVry8X0":"86xJLqLTB5J0","SNwc0TPq3nME":"IzEzYAvZ6XRZ","L4VWjZhEb3JS":"EKlsSOY0gAOZ","aZ2ek6ius4V2":"ZH6DdRtlHrQr","m0K2Uw9dVRBY":"ADHEY7o1Nd05","BE3Qbzn6ynY5":"3HUR3Z9YFjkt","RaCUt0FTy8F1":"iOXGn6UGljz2","mLTji3NZRBtK":"44CuXro1Tj9h","Mah3zA43SWpg":"5FUOd8qI99RZ","SQHJtqV0Lt5d":"0K7jcFXj3Bj8","F1mMoB2GoRC1":"h3q7qengSUvv","emJWGoRJVrFR":"KmWPjjFqJcXw","e1dbqe3O5mfS":"LdS5fHb8Ltj6","ZAbpyxqWJzNr":"Yfs5w4TQejBi","bMEXZ40wNIiX":"qtC2T8Ra8C2E","n0xs1uw1pPjE":"1CAxwNkpQ8N3","wgb54GTKSRy1":"XZnKMTktrAJt","FpJblVVLVCM9":"96wvbs8186Bp","frTfYasncQ1r":"OACYCBWZmM7L","UbNHMS3RTCpk":"nl7vDUol1EhI","a6SR1viGQIYF":"eKMtlOFj67lD","ezuGAwuX1KjZ":"djjQqlYbeTkY","k23rpWolnsoW":"Th8ce2JHWarc","ttaXmtOoDDAV":"5nZ1cGq5HQIn","lzrbs6f0wcqV":"tL6qYrbh5hKF","iB8d0NFylYLE":"bggqZo5FUqhr","leIJGj5FgFj1":"anDnH6xduuxn","JlaCholeyM2k":"9eJQxB88unyl","cpoJixIUcTOK":"OG5LDlYd75VK","TJLX08w1qbia":"CggDE6sknEJq","0OCL6SRKpZXj":"62C9myeXGZfZ","SZ5F1uzRMGgW":"ZFSWxLN6LUWy","f0R64XkHLfNa":"bM3smGM7lyFM","1o4VDe3tqJEZ":"5r0Rv6oJ3a1K","X4s26g7VwLhn":"fVekpaLHu6zK","xygRPlmvbwgr":"8COKqFhJg0uY","AyMpxL0adjPK":"ijmhQMZs5T3j","K3jK2n3nIrZi":"41Sp5KfjbnLR","7QjcLkcLdb7V":"yiVegXJLlYkG","ELp7pB0nvUa2":"x4t5mMpXcLgL","UslieuL55Szn":"IPs01hd4oBQH","dAcxLIU2p8ru":"9HFV6VEg2W3s","H9nnIiA4LGhc":"7AD2q2CWyE1R","8KXz0xoZxtYs":"WfkfGrSsYBiG","z8vXg69LI6x3":"Ivnkv8sl2UuU","4CdWIo2QBIXK":"URYYbwOc2caj","zClCIfBPHhAm":"sKPpWgqL9Dsc","DEqHiWSoktZR":"L6oATekj4RNo","o4yAymZfWpIZ":"00OjrIJ3Zy3i","SNjyu7SysVvT":"TVw3JwyAZMEK","A5vjwkMPpRGO":"b1S1gLmi0ycO","XoObkFB4VHHa":"ikzfGJQ24aMu","1vseMRNRT0BA":"nJf4owbBH9io","ypLAJnE1KTUP":"cPRCTMfdrYpA","vKljYcF87RMK":"aev4HlI8uJXm","7JSwX5d3PmZ0":"HIq2bC3OsQn0","sWTX68Liieo8":"zsFqTW4xAVc0","P90Eil522neL":"3zSR3Fcikznq","os9obr5kjtyi":"1P4cLg0iHYsc","AE0SPHdJCKo0":"ToivndTTLzgz","9ddEilLkBXwp":"X9O3YuNWgsO7","yjEsYYHIjMuq":"NPrx9IpBXAj8","pdYjb9c3CIPf":"ckZvtxsXQUJ6","0lMjHAP1togR":"VuhebuAFMHza","Xg4WmdbjF7TB":"OQ2uzZxRbjJG","tpUA8wKD88QV":"T7gzaoQ1DS3C","k4H9YwyXvZRG":"THLDTMrGvEVI","mxM9MnJk56mX":"SaxUjwuBs79x","q6toHXT0Gkna":"ljYnVV30TTqe","Glyl3HQl6EqW":"7RTxCI6tvWOd","c8pXItrhjchS":"xaXiAemTD1Pk","2B0KBMljP6nW":"pv8oisPHHlWr","zjhkRobI0qxs":"FxYWzirzDOsA","HOMsPcKRgNov":"IrSqmuXQhVqe","RgV49rKTIhZG":"DFFe4nLNft6v","KVUlUtrT1k2Z":"w28LzDjvNA6J","m9B2Y3jqcgOE":"ZpwNu1OcMfBz","Uvnt3hxCljxX":"fgoE3BBaWj9W","WS1zkz8XG4Pa":"5w5Lf4pLOrvC","xZmCRKPizRqU":"BS6sQhwyky17","D2WXuoYrHyCo":"jdkFLkuA6t9k","vXLZY0WrulM9":"0pCOsTeuq3mE","xybhD0PvuVZV":"yevVblOWTEUs","GLopsx883U3A":"ktqQ4sYbFTkH","T3jBc0OPkQnh":"LqAJL2TpvN6j","Z0pQcUBj85LJ":"4In7hzLDNmJL","ZfFYMcD1ecdo":"Z5dze72Oj5BO","wJvnolkImPUD":"W8VK8w06JxvJ","yAFUEBzmTed2":"f7fZazyblf4M","krJvZkz17BSg":"KeDi8d005ses","npJov1yaECZE":"TiOHfixrAx27","Zo0Y3Pv9tEb2":"uzXZMgiPYKLK","vS7BJvg3k8AX":"G4freQStZLXI","REzLHD8WLOKq":"gw3nwZbLAH3u","EXGUv7pGHGID":"TboJztKq9Xlg","M8XaGdxY5Y40":"iREUCB0sMwJU","U0ieSkzTUUPd":"1T9vYCMTq6zg","vrmRubqxpgej":"bkaZcS9MMF7a","AmENxGQoOmMj":"6ekOyx8sWkSK","YzPYDNJbADEH":"jcZiRZlD4lUV","SrDBCgBLLsGz":"g5N5XCDROb5u","Ic6X6j392XGM":"ixutH93PLREX","jupaT465BLhs":"0iYqfTbTTfkc","CjkEojRckGcp":"UJelMtBi77WJ","cfQiiEvM7byn":"LIZqo2ePPRKY","o5VagcbCIZSD":"fa8KDx25IZkn","GQhgawENxhQr":"DoqnnLbEyxs0","89sChQ2VtP0s":"wx5NqquYRpkQ","ndzI0ESRwyjf":"47cCMAVCzJj4","qik2oZvav53P":"W3YVsfgyrMNT","gOEwvzk5WpsU":"PnEfwPmcKcMP","VCzelNWMUmNK":"XfImX94H4k1o","ufgC3nO0Pqci":"Mc2mCdH0iweB","w4DMhKIw0lyX":"zOYU6fCvaMAP","GMExxUrowyh1":"DLpQTq3haPmg","iAbipNWybecv":"xWAwHLKaXYhj","l6lznXQ5Jhv9":"A20TGFwbvWmD","ydSYD2NJoNSk":"YUGjo2sX1bdR","jLIirsbg3B0J":"zjqGHZjnsPfM","XxVlKdjrx9fB":"8JkwyhTi51BD","Go8wmbbewzac":"3awkDDbcCioj","qRpEtLygQCiy":"PdvxwHMtv7mU","QW0JIfzfDLcO":"mgKn4vW7hvmr","XoOrp1N2wRWe":"iqN1xKmwRsoI","SlpUeMLGo8rT":"dWSRqdzLTsBJ","C5ZUQzLp5IcV":"lc3OxbHYgKm5","eIuITV95lISL":"dh4MOzQY7o0N","O6WGWHtdMozs":"VIRe4Q6tUULJ","1jWFgjGnF4ok":"bejECobPYZLd","UFvhFNdFxsm8":"CUb6QJwvZAin","XoDTv0TwXZjW":"zqMFpn9pBjFc","R64QxJsRmAPC":"GvZHsMwpPxXl","rpzc5T1mZFzq":"5zMFrLNntQgh","ySeIhSpJBonj":"GUCZYHvmhLBf","BTk0B1kcyelc":"JIBMrkdCAxIS","1SpH2NZCeV5P":"A0LJkcsSqbP7","5GLRmJk1oiVb":"bIRHoCq6A86r","mNBas9ikDP6y":"6CnZTtBev1NV","Oxv6bjoRkeYb":"br1xiFUiFqhD","Zpyk2Ic8YQ2A":"c8gJDJqevdq6","xHjM31mQfRcH":"ZsfJkaJXUaYb","AhEsQSUeDg1P":"CLz8WWppxeAd","KajmAgxAkXDX":"STVQ4qjRNUXl","Go7n0aLmu2CD":"HwShM041c7kh","HwwFPEpIT6Rm":"eyHNz4dihVcJ","N5y8ueiUobdJ":"1PSSEjRIwaAn","yRTtyd9v3r00":"FGM2g9qN5Raf","cO5IeylyEVNd":"hb5ITsNhvmLR","NtjjfcTIj4fS":"BOKF0uYP1VYR","5SwZPaz44lDN":"eWvRqSNBs0nd","ukpyn1Wproru":"t69jTZdAjXJh","wbuyNL13AfjC":"7dAsgAcvb9HZ","bSw1NokVlnH8":"nherrTu3lUkD","zuwy7HaHh4yC":"DDzjDLvP7zrX","lAMQNl3HG4Wp":"UTWIXnd4RATK","GLoGXrzz9Dpy":"M4fMLK3PIW7A","yGotmZztaGFw":"L6kXItw1UUyg","vWga8CZFmRvB":"pS1kGkzy4QZU","Yv7FmxEGi6oH":"bWoZCbaHZoCb","CPmaaPqQaDny":"6oWIEEshdrrt","tnQhUeHTAMis":"g2WAd8mGSD2u","e2r2m5Ehz9lr":"KV9HWw1amKDv","49G7CO7oun8O":"N8AeMWswfWor","cO0TV7ahfMtn":"TMdu8GAw2V5P","Swmcsd8j2JtO":"ywXzHu2a9JDD","8zQ8Hqrasw7Q":"HwIUrdSiKR0g","hfrUdOoqbFTg":"4QnuHsdro6uP","2z9QsWcSzybk":"nwOSy2tE9qXG","LZiUo2Qgkrd7":"w7R1jj0qUBVI","uKLcvx1gcqIp":"AzVfZQsrYVYk","zrpwUdRZqmvE":"ZrHwELpj8lNN","AGKPgwHkiH5p":"T9Ag284RGkdq","i2veOnPqStzx":"1LoliHWIPQTa","U1hG9ZrvEtlz":"aAiv9slmXuLf","KTIPsP3xurCF":"uLTDusipNjGr","wDEQJNgJ1lG4":"b4hPpluu6rbj","4AVbf3nv4uhd":"YGpIp4bwJeyr","rym61KVOtaLa":"PO9geyewxEsY","a1UFgf7LsxQe":"5VezcrID8xxy","s2wRL5vD64tU":"dVF5uta5qMhL","czw2HKADFpac":"3whpnEnSpWuB","bJlBXBQV1fl1":"GgrSWckmI412","0mMG64V806N8":"YjBXOp731fjM","iFlBza9F73YV":"nUbiT3WVs7Oz","ZxXDTA0YqdmZ":"IRnF3s7cVKqn","i3PKzy5YfsLd":"HerYHVkEBOPT","C9hTk3FiNFDc":"Hfh7Xjdkjbba","mfDadu8bxeTx":"kIYWaUTrnZkN","dHzkYpsxwxtk":"oJDp4kuuGa5g","047bmwujaDRU":"Ng3bXsU9iwnu","KEUoHUfE4ezV":"uG8Qc3wOPlN6","PI7kkyJYnIz6":"AOA1BBXsmf3B","2gpTnBYNnX35":"69VGYKGJRePB","UHbG1CwAk41a":"TKfX2YwOMXXV","CLk31sdskev3":"b8YWLpVkoqNU","kKzVAi7DpRn4":"SL80DtzFeh6X","p7nUNa3zn86P":"Ulsp16DPuc03","hBbF6nnuMxTD":"cDlfdBDZX1fE","4OXm4Tp1aAzp":"ubMPjvIyfy0s","JohZlT7Fce7n":"YON3F65X56E0","8lblGETOY5Ds":"x8SEYmoXZpyp","dvTjzFTyhKBb":"Lfe8D3SSRUm4","lEvuA602bECs":"KjD7ruIY3NiE","xM3zJ3ngGsGx":"fYRPDZsJ5Jhf","Do9JIKkw68yf":"ELjgKEtMFsJt","zVea4EtWx11q":"kupdaRnwZpJC","PWyQ2AgtnrtQ":"i6rTBCatmTU4","847OYTSoRJEp":"hIAvn1AXKAJb","wd42tSt3JwE3":"OkWRjnYrZg7P","swfBzT5gaDdb":"hrLe7dmVqlFJ","R1zw8yDzASFH":"NLedonlTnFDv","lUpFhc6V6EKb":"tGMLLChkw59V","wjRLKarVvUdB":"mBjlvfgzAaiu","jokxFaQ2AA8E":"c8WEVhfjHpqj","6gT49TtqPXCT":"nG8zQi5C5PK3","jszNnWxDIyZg":"gNEcxj2JhqNM","4FdJdD0rOqrK":"7AztUm249j26","Pg2TZboB2Ofl":"bpGBlLQlJ9JC","SDNaEfO435sn":"pxHT8TBBkBBu","8Oj7oVOvEokQ":"KX1lmFlrOpDE","NDMVLZf91w27":"TccgY0lgzlxP","NT9RGKq9OkMq":"OPdcDZWLJmuW","l6zUdFYhy8c3":"3d9ipQYcXAGI","G2cnqzC5iSwK":"Jb0oVqtdvPX0","ga1CYcr3MbCP":"mPsNACx1o2sh","nbZrHbA7iVvz":"NfHDqORZKBgE","VJzSfh13IR5G":"HLpDDjx4T360","hmQMnznY8ZRt":"Emi2roK7agUn","T5akjLNxF7Pc":"7JH1XRBYMlwU","ysPbIoIdzJ9Q":"02zx7iVL01AP","PtbxmrmligrF":"v5DAZmFgDrBV","FHryT0iP5iu9":"wNke6jFUyuYY","zzSm03q9DIUt":"aUDvRxc2tMRk","eISp20O31lgR":"kvgAQedvjiFR","9TK9ZTgiGdXh":"s07MQNuVC9cE","v4KHrSn7jjaO":"6YjRNX2mLKgX","dYmUSfzoS8WB":"59Jf1EVsT2J4","vtPNtavuUzXq":"tt7pe0BXyc63","JPklOt6ygIKQ":"oKVcYIZxWfut","a9EdvjkkByKi":"UXjllDsTMrLF","igPOL4DdiksD":"gxYhJcqx4DBv","QjiIreNrAptq":"rLZRauijmzmo","iOTzuaU41W6L":"8rmmSQtagRsz","mC5jEPnHx20v":"8UqY0pusVPVx","xHoYhjx9v1Jf":"WDMvq8WlmMT2","tA5uj1rowAkG":"0OjWgw5ZR5aH","ThGo5H1cMk6J":"j4gp9h23qiZA","1j5q1JAN0oN8":"sszTnuQxC2Am","f9i1czCTrJJS":"FAL6kICEE4Ag","JdyHMqkIawYV":"Ra1AofiHW9FS","0kPY3EOBBqve":"RkUg3LQX36nU","3ir3TE5zsZlU":"KOFk961rHOcc","x5rezj4dD5Fv":"eS4QzeFgYPQs","RgqKBXFLdckr":"Asa3GT8BLJbs","6pZdsKl9fsY2":"BZfFOJ1E9Eq8","2e1gVRz5Mw3m":"sNKaQ3E2qVTp","fDTzxN872poq":"xtoqO3TDvKUz","lMRhFAvKC9fV":"yjBh38hwiA7a","Lm2KxpixXA4r":"8FSvwTKXyldm","9fMFevzp0OMJ":"P2p3LBnztLVA","n5yIoMDBXxiL":"enWPGHmhxb4N","h50fyVhSrKAx":"Bz9dPadSfeAI","c8mvttEmvJN6":"Hw2URU1SY0Rw","vHcnhkQaSJhO":"bf9oQaTLyR4W","pJoIvXFK9BR7":"lGD4R0X5GKNE","L3ZzRLCekFCf":"DyU5TtQiYxYZ","gSUYoEUrz5tj":"XCzdpfe7hEHk","mTZ9TQE9D7VU":"1IOu8a7b0pfp","QqtcHANqQq8d":"qSEe3NiOGrqs","b95rZyRmpCaM":"6HxjCYkoSi0i","ixn6YmCQ0DMs":"pf7ludA2tW5R","VogiGP6hkcC8":"w0AZGejeaRWq","62JLgqZMk80e":"NVOZFmQ39Nc2","b8NoK9iwfgKW":"dnPr5IZmrlvW","J1ZynmMPUguq":"lr1HJWJCrLvV","IzSSuPPTJPm8":"NsjjyxfewKbM","1A2FkQTh3j4D":"yI6ZxczIntEG","NUJnCQ7aKeuR":"BVmA90l0evoX","RZHQIbrn92Z1":"mPvOZtLO68TS","Ym4AssfO4e2w":"owrnm767l5zd","TuqcGssHn0JD":"qR0D9aJufHuu","doFCELStsn95":"wAvBlUDXTTos","qqs1N1j3OfC1":"6eF3NujUfmKp","ZTxahdyigz1o":"iuOEekg4fIhn","vQIejEkr0tNC":"8lz4SWKNyVxr","cgvhmFVBWDMd":"RBFjFicyyUWE","8Ga2rrX9FMfZ":"40tX0f5tcrOM","SY1UJcF9MYE7":"y3Hgg91Qg7BN","EOwmABsv1pU2":"4ihSPN183bsi","8jRUY0xeGJxo":"W53QOSeFIXWi","zYyuGbWwRJas":"VUSF3eowDaMU","XoxyspSOjUho":"23ewFa4iRzQq","eBC9YmmdaY2a":"BeKQS2KsPYLq","bPNf5rr5XVY2":"d3pgDcRcPtlz","ixnUwP8RlwfO":"AhbBjUYn46ho","nlZgyRzxA9ac":"uuUQsoIX3TvW","yyL9MSuuDRSo":"oJwuaVc4tNLF","XOXZurR2hwdK":"jFxqN9jdkx1C","wWj05CG6mOnu":"rqJdHJ7pmIPW","RRDkMK6N2hp3":"kAFDa0KnNlUH","v2cis5CWVeMx":"TMDqsjallj8o","1Qm6ls4cmCei":"YKqtCTMEKBww","Qc4TBeHBj8zu":"ssLX7kmvz26u","y20JZpgxMwIG":"7KP4MZJFHPgy","7XyH12NWPCFB":"IkIE6v07lUlO","OohgKckUUCz7":"fJ42zm8XY2RB","WTyMFK1MoYHJ":"h1mVTpcgA8y0","KQeKWbTJMU9D":"KiLWk19hEcJm","IqHDC7GrXsdD":"4t6SIyjZre8v","LNp5BtN5me6a":"gFssj6A9hlju","6LZxttClpARI":"s9WOv2ThmMKE","IiYV7XgjVr1w":"jDOut9qcl8M1","5Wirsl2VgaaV":"kSSo36tH114g","0z59OvkfdIPt":"BtgtDlkt7IZm","Bd3H2Wtdx4VR":"gUCGu2EVE4aY","NFK4npb16x4M":"dSUOYiEUMVXp","OPtQkytOm95t":"no82dC5BCEbt","mVWOTVZ8gPeT":"KNHi5g1ZYGE1","ECHxehJre17H":"xhOyD9OIddzO","V2vWIxL9R4xX":"RWi9pgQNQrPc","W001TwVugzGT":"VuUFazkbskPB","BLusqU3dBfsI":"3zpkjsmP2aCB","GCu2rI30NvQd":"UQUWdcn1Ym4E","oDFr4DzxCBpC":"zWsWAJ6ocz3O","YWFruaboFSiE":"cklnDx3ClrFG","WScQyEqqkJjk":"FVCVan4Ipz2H","1TERbAkNAAVw":"dCjanT8p9mxP","MRnxY2XR5lVy":"ObroqqKZ6S2G","fW2KGI9vVFft":"n6txHbIbrDpE","IMhnDh9O5lqL":"JexoOeNZmwGN","yFxke5UugEUD":"3Aw0a53kGYRs","BMJxyJBIJ42M":"urDaDY576rvD","5KQr4he9Yg8C":"SrxKU1UGrKav","mVVB3iLYwClp":"m5gnfLyt3NMs","QaqIzWvcLMAq":"4td051RyneLG","eUOyMlc8JQ2f":"E6pSNqVf7QAr","0ITgnpUH5wPP":"n1f961CmdyG1","YBX4RtjOT7a1":"fVstVLiHZeno","5YZqThMUKeng":"PPVzXaRFxIWx","sU5WAolq0whz":"sfXxmuIwuvCq","3JHM883A6lSN":"WGGM4mPFXlFe","oaXxKMHReibu":"lASrE19Stdhs","9195D3R56m0O":"28GY3artAzcZ","TyDMpKIOmu7c":"gCIOBVr0RzPI","420FzEXWyHHM":"AUar00j80IqI","63mamWo4YQe5":"UcUER8AcaJit","JBloRfA2YrUc":"4nDywIF92RHJ","7YSR5xagbDDQ":"DouJuywrYtyZ","7w2IOj5aEQft":"7gd3q8gfjf5D","XQ3OkK1nkcC0":"hmqAT1gFtEHh","Fn6FxM7w38RI":"A3cYGohitzGz","GWgUT7jgdYM9":"7hvhKJqdRAEO","vhJDYB9VXhW2":"OHptF4MF5Jzl","0mISOuEMNgjd":"Q4RYwbKHD3Yw","xKJ0Jn9jsvyT":"xNBPkQJWvPMc","bPI0XRofrL8V":"QiLceGJrhucy","WZrDvnrlhQMx":"2W5RLHY5NcS1","p4KDqOKvkIer":"U1XCVHIYcQna","eS6dM6yXbdMh":"FahDo5vjtye9","nD8kWMY1B2iV":"F02rFnvXHxsw","2EleTFMq2bPR":"0GOf4eNBoz5l","Jq9k2WFI4BsN":"vy0MUmfNLhHA","v8H9jL96IX21":"UEddBOMukyU3","Rn2smPoUHd6F":"Cw1hueupnVBA","0v6i6MDk84td":"BtyTLoifXx5D","c07Uz8OHPZ6a":"psTYYYhBJQuD","NBJXbODcl66b":"s37CBFyKLpgA","PkpVvxGknCAa":"3eNro0eJgRM6","7ua8nQJm1aUX":"xyt9k9VJqPRz","KWwuzQKYfp8n":"atSc9ztNudmC","kYopheKpVeSS":"uk1FCuyK2e9t","trFfk6k2LhHI":"eWvh5aZx3RFT","ihMUZPyqZB4G":"2tRlfUB5dQUO","w1Ja8E314XZy":"C7vZz8e2gK3h","5Cn9vXKnktHx":"Av2kTsqyyvkb","b5TuoWbGA8qI":"AkYT7G3fNQ0d","Fj3LFHGcmGtE":"7anXnslyFFO8","32BjBpFRUZnI":"YVpz2UURqWF0","jBUThTyyQNDG":"lbpGArM05nC3","LeQR6pYjp0Jz":"Uag63oykEu5n","gCzEDCkwRHl2":"RVOdyhMKL2I7","xMKLVncafwg4":"dBwXfpkd3BpR","76VMJHOwfVcv":"tXxmW5QPTwK6","jzFexrfsnKfT":"7eYLMOwZ3Bnv","lM4snVZPBRH2":"X2GegH7TDZs5","wjkZxtlHtF1X":"3prSkBXdK70a","Q9yPS80rCTMx":"k6n5GIUTo86g","JhzevpE7CXuJ":"ixIcfmZCUw8Y","S6LTJUYN5wo5":"ft5AbncuLNRx","1Siuct9F8B2A":"eQUuNE3vm8OJ","wa6AK363unsR":"eVmtO4tvdkf9","f76aOlmZPts2":"v8DR8NfCVSKP","x8JRbYH1yMYT":"tBX5K6wljOfn","20zMmbttfHzH":"xwbDzIcfCIrm","tVnCg8D021IO":"ZubESj2GDrjY","A0UTlUUKCLoB":"BJZzbOPYKJOu","XBl7ccFDA1Z0":"kpQ3MLUugY9v","BrmYHud8UdiV":"vu8yV5sewQEL","iZqPGuaOuv1x":"koMVQ3jLCtQr","KEP62z5hGsTH":"DZdlAQerQ3gn","eNCFIDHTZc7h":"IX1lIMJO0YDJ","td8kzkLD0hB8":"8LK3JTH3bbdK","t6OokKbmHpZ5":"tUiO4AfHZcZm","jeTQtKrI9pbD":"qI09rEUR9it8","ZCtxzs6bAuv3":"onZbnQ4VAMxT","yh5dICJH9NqN":"9ikUP9AGlA6Y","RhX2BJEYlSb4":"MScNxgArPRWM","fffS9wNjo1xx":"bLWetNJUFjVy","djIfRgjOStm7":"kSmf1vJgTKA1","6EaFbIPtOvq2":"ZCAS6xo0AKKo","pnh5bj8lFd0Q":"vZaCpqVgMFTm","dtx9q3HrWpVU":"cPgfK8BtTm3Y","ayr8rjAFYu1A":"wvjVt5YLWJYl","F7zcQFgKcsPU":"j0vhe6d535jX","MfrbLe7UP8Fu":"nvRJyNbMlDj7","NLsIA6tA0rfA":"iV8iAssa43jz","ko8PsPrMiCG0":"gbEiyQlpGMIl","aC7nbHUhtE0U":"6Ptdy55gkzV4","yHODXgn8gSgP":"XZ1XkMTzOi6A","cYmNWll5Cnaj":"SINYVnkx9pcu","ppW9qustkR4q":"hi7vlJF7U4L6","OAdrLYvMzne5":"9neEPVK9tXnx","msizqcDOdpuK":"qVV01qSduECN","ig9kPZTdBSS8":"LSYj5DmTNKpr","jnHSWMoMWy9p":"H2o3RWrBAokM","XCBFmzWeF907":"IKvkyG41ec9H","dJ1gzwLIRCne":"jZq5GRrg3MuT","wzJmfMGQ7xwz":"XOSvQ3gjzSYF","cfNaJwhtOmJ7":"RMlpeQjx38fm","9N2r5fVqizg3":"vEsG1akf4dHW","BgOwhs25jYXE":"k6idzmnZa0nW","qZ8ac4PH6O15":"sBKcWBO2zzd7","5ExCXW6lL6uO":"64l2VuZYylLL","sEmuOM63ctdI":"5pdjCAupzzZz","9aKprkv3Uugl":"flUxUTaTscdN","kv3lugyXpks8":"SA3oAfU3HgZ0","tYJ1uyRnRlV2":"A5T0SMSVyJwF","O8bgKdAWFPAp":"alr1eTbfCC0U","qaxbsPjO3WB5":"aigyfqCZNv2y","LaUi64sJGN97":"qfdagiqkg09f","Guzl9R7XvYX2":"VqKEDheYv0M4","gL4jzyFeLCla":"JSESgB97pGNb","JyJQb8U2kH0t":"V3NwZruElOZN","Dp2m3WJrKS8p":"wogCyxbudjMM","ZzAFXlVhg6k3":"kF1YFcKyuhsm","mnld7W42cwW1":"K4O6jbYYu8LD","aGqFuyoE4xTJ":"cbYgRtKNn2I7","MtotsYzcRNkd":"oHrDMI1QzgYY","SxsH65AuqsAp":"kfDj7ObK1vwI","GOyTFiGhDcBw":"m62ozDZymm3w","Ccyx5V6WlPPv":"qfSQkUch9F4E","kisHlw9UuqI5":"Xjcxw3xojUxa","kQjSV7UIiprg":"K7JaYkUFFQiU","3gvw1vrpU3vX":"2gCfySKi6Ge5","HN0yNeTcUHt6":"yeopzecuxuaW","5TAjDDaUWNkA":"K9h6CVMinuI9","xXrZjGbtri9p":"J2k2Zh90YhIK","RvPJD0WUOyzg":"4drbpRAICdNu","Yd2H0CG0KzsA":"8SlUV807wojN","WVtYXybeIjHu":"yBgVetmcc4bl","3EkOJlDH5Kfs":"LiGt8xP7D2L7","l1Pt5foJCZoY":"Ns5zy85iKJNt","gD5N6dkemRNN":"Djw0ImV577D4","psHd2vffIbFP":"R3UCzA4ALh5A","IFh7waO3R6RX":"buwtsZbqLBB2","6b4tlgk9MHrl":"LbyFGlgvlt5H","nBoTHwDKKGhI":"U76g8iPF07oK","JNTGup3NgLuo":"6TS1oAayaMWQ","8gE3E3e12EWQ":"JDmfIItNc9xE","vcK6gQudsSBa":"IRWSb7D5vwJZ","bNVSqGr213Ye":"LMczzyjwgm5f","2XiKdyabrVGH":"uklBYYkQLWE1","Br9lRoaXeNeE":"K5UoGLG1Ygag","C6sMq7xpyPlA":"0vLZHTdHROEV","fJx38z2KFhtM":"qlXPtHWuDJvH","5zicBM51x4KY":"vRygIJ9V8Z70","RLaLIjVJkOyY":"JGZ80DZEyFx4","VOzVyakDDCUP":"PiWcAVGW7osA","ish81ePzVsJj":"aYueJI9KgMGG","TQJswzX0I7C1":"GTXU7sqGEL0c","CoWzXPF0LUGK":"Rj3gvblcHoF1","zQUKDsgdc9ez":"pir1OFXuZPKi","FbGEpSIbDogF":"eWyPyQp4fhDZ","ySdMhUWEJvwO":"t48fnoDJUkEE","tIXoLDULT2ow":"Qj671Cg0Qa2j","ZF1iRYB7zFqj":"ow0Cvp7yo7j0","gz5ZLYekIqn0":"mu0gqh94u1wr","XGDE9K6IlR7g":"jMpJMUhZchBx","WUKsagupHISv":"hBUTrNlrULg1","mahlnoPujjOY":"TbDxA2ltez4W","RT3Z2MVxAufv":"9UjUup4xNe6K","WeH2dB2SI6X5":"T7s29B3UEh6B","8KWY9I8WwNHh":"zioxwClRtDcx","rD4qZoIJWvwX":"A9YaA66GEryb","skXUfEGWJhNX":"qjJbZfGnja7L","VWOBgXYfXPK7":"yweQ7XFjiTu1","iHWX1wqsZJiH":"4DrDgNzUmHEK","BVX04BsQtm4F":"VtMufVEWgTqC","l3VBFL4PwwmN":"Vc15Qap0dtNm","fNrxs6Makm89":"yaqiPHGGaHqm","WLGRY4DiefPJ":"jwyF1D61M0DM","GuPayp2iTmjQ":"028CRVSh68gX","gKW90GBl1nA5":"NkSmtj8FtmI2","9UFypC91ah3W":"uIEmiAANAz6H","FXvOLEZeCAP1":"kEozdw6rBhiQ","sLjv6SwDTA18":"PxGiyhEC0Id0","V7eUfXcjWj2W":"wbewL3duaaYE","1xSOOYAOj0QC":"6InsjBSSzCH0","qRMak1K3s0ik":"Sr3Yrq3HELKh","y9G42OC7Arvm":"IpdHPZVqAwgt","EK8ZQoNVtg23":"PeKhDhwbybAt","VVr7Y9D7rwqY":"sEv118JUyFWd","nSAXIjbpgjtq":"DqYo4je1A4b0","L7quqStiZ3Mm":"UdXeLUlu5B2e","DzsrI0BygS2t":"TBhsVeOTM9nw","V9kfOK1fke15":"Q2GQcaPXnI6n","ZIfK7qEFNnDa":"E6VQaWudr0qR","WicYziB5z0qa":"hZJtT6kChsHn","cf1yy8x42i22":"Yn1j9rWKKfzR","uKB1TtkHdqzf":"4cJ8XEC9oKfL","Cc4y8VpNCT5n":"IANbrrGouOOa","5nCJIvNXnUwq":"6oWtNEsFtJNF","UK0Aq8V7s2KZ":"GjfWKWi1TNCy","h7zTbWMW0y2O":"DZNeuQznl9dv","SjnqOAOoaCRN":"fUr8oPykDyaE","6hAeG42AMbld":"zmOoy9kjBR7A","XuNVfeFTQvfZ":"ye6Uq97ZKk2d","yZKdk8ue4Djp":"KapPn3BnZA5v","Yyr7497hsUwe":"o7tsRGpu0ZHe","2OPAy8VYi42M":"dJNWWZfpp0Br","LLLZDQqqUT9L":"VNw1Y8iRtJNZ","nJgN8Q1gdDsg":"2k1TMQ2Y6Og3","O9fC0y3HjjjJ":"rNrQ4PLTa2CG","0s574PKJDrNx":"Of7Qy0EZgh7Z","LHV4FVZEsTVL":"xod2XxEVZ73p","hzuYSmoE3f8J":"VqTTCdTGsGsQ","LK80cOmIzTks":"1pNIz1UOVRxj","Njk1G4pL7jfQ":"PVhy72y28AJe","20I87c8dWTlG":"pV7oEm9zFo4C","IVK57vy1WvFG":"Nf0AdyJcICM9","6E3Qi1QJYc96":"chhmppTZuO5p","mhIml7d5jmH1":"d9VQCfSD8NBl","XmrKNIRDyABt":"LSg24nTwysd4","DU0UJnKNqe4l":"dArPMmRHkXmN","v5wfKx2gd4l8":"wMegnBWYYsZv","NolCwMzC2R89":"nBHw6jgMZhAJ","DDqppzaUio2e":"cJstjUl08Dm4","nb48Pl3dqDIZ":"mmHdEfMD6pEE","fvAlU76y2sbu":"ggzszjy4YEEl","ddceqMs0Ylrt":"VkhVA7VjW5lH","CUZ0YAhP76af":"50ah5ECoPHFs","wegceXpWormI":"4ifHmTpV2DZA","zrrmkpEEqKuH":"ZIyo3qSLZsW8","lq4ujNM20mzi":"nQFvmeHAhBMs","63AEQNF0bnHi":"cDNR4A0DUoXq","1ewDrP8flKYc":"9uUtRObk9o0T","5KRg2iLJekWj":"XKVLPmMDYh0U","rgOxr8K4M4CT":"MsTgaNuFBYPP","eqR6mbE6EDuN":"XK5jok3Q7rKr","4UjHSmcgSi0a":"kBr1b44bM5HB","qtTdLLBQzQSY":"9P5yo8WqzT8g","AVUyx2GRfDVn":"OGb6XSk2WEQH","NrRDEDlxjnBr":"duwkZEPCUHCr","mftm1qiHqJWI":"uRfXJPNeZeap","3R3RYJwJTsY7":"kWtr0WYbvK7W","1yorIws3pXs8":"d6HcLLXREWoO","tH0qmTJN2nxE":"hfP8AI98b6Zw","QFEMWzATxws5":"fUuA9O9YbVAf","BjfZWA3jPOPs":"97b3deVXA0dG","YWdqCjURKnAN":"oBId4JsLSRUU","qOsBnDy8LTnb":"lxFLvMsrpr4x","F0yzckkYKbmn":"qE9mpZKQp8kb","wjL2O1AtABTS":"NTXp3ISo9p6z","ypFIB41EfRUQ":"wYBotLeproVJ","MXV64xeTDhh4":"zn6YgqYbgwmr","kEyZHT4oqyKG":"udisKJcafn9d","CImyQ5pm3bWC":"Y077Eeng4WFU","8WVKjvrDSvMR":"qsgtyWzDvB3L","5raOZCF5aj5i":"zeZEzGUpYCja","bHzVhPcsYOuG":"EDkZumUHHtk3","rVjWXp5lZJdu":"BHEZWUdygl9U","hwzcbUcky2nF":"OSjgw1p3Pw1k","LvZ4eXO5Rfvb":"zwdLnhVmWXfb","5duDz8EoMyrx":"bSyw1Cs3Y2B5","SgrpHaSmURxt":"dfgEFnuTtd4K","TNCg8nJn8UdH":"oHKvBdA9WHXJ","Yp5FVhpgrq04":"1wR7UsAUpDHI","XfEmMQx28DWB":"BNeHvFIZ2Dbv","bvksAankSbar":"mQ8LuoPxVYwu","jMOpsG36xfAE":"lLo9FShuFHC8","PFQQDj1HfvPl":"mMtz6gLcS27D","m2hybBh6V7PD":"8Z8q1kP9pRm0","VMgbwYncd7wQ":"AXAXglCYMSql","KjKJjnhnHbBP":"p8zVmKZ4gzFK","G9YiPpvxxgZE":"IFWJ8AVpzvI3","uWqXrWpDNe9T":"ASkTYWdzaQuN","DHOojopXzxJC":"S42lqWaqphV5","NFUJERPgnGHo":"UNi37YDB4l4K","vGRPaEj9Go2N":"DhFCmZeVU8VQ","FRwUzywVevVy":"Fdh2yKg4lHxv","5DpfD94iLzCC":"2CYALM3D1n1F","tvNcMuGxDl5V":"bySyYMDrcbwq","X74EFKmrmg40":"Vov3ft0lzUkT","p2wndFwecPT3":"26diPpVloCQW","G7FAurfwNioI":"SzUAuFTd7p8S","44cXL8v0qo84":"CYdADWForWGN","xg0shQhGQKdZ":"djD1Ii4dz3AH","MmcS3caNZpJA":"bHyOZ42Fp0vg","E8Gz6GBnNzs3":"3VphIf4ScdDw","Nkpg3c2xkhy4":"ci6dfjUlaTVx","HGr0sz4uahby":"B4cmWzko8qNA","0Lg0P8oBDf6f":"IjOubOmLXo2w","MdxndjKMPgqw":"kmTXhubwLYJT","ywtEvBiBKcS3":"pxrFq1VBVgRY","huYCffR0h7Ck":"pl9NJHi7zb9b","UujopnrwAtNS":"T8Jjnqe7NBDE","W8vGuD6WCS0d":"WoXZhIJwd9CB","fqEoh9VFa6dT":"1MIeNISaDKBN","8sKQUSxhymO2":"XiZintuamhsU","XCZ1f8BKy4j3":"NFegkZzw4o4f","vaRv3eJ1kHWG":"QzrohhJvIyLv","AJ7D9uhytRJQ":"m7ed29TeWRcx","LKSgjHiACYpr":"LudQhYDBLB5j","SeQGJ2sHz30m":"BbsuTPDcubuk","yjl2J34Kybdp":"00pOBqhJgHPO","zL3YhilCXinS":"fty5MRXhrERj","OgzmTAoPt7n2":"C6lYUqYSsJHI","PmqlvgAwQI6V":"Evejxzc4BuZI","rehKS0b9yPtu":"rR2xX8uR7xJc","0NP3WAdQHPQ5":"x0VLBMQ1OGif","ewtmtNL3uCts":"fQZBodDff0zD","GG7KBY6wWfvM":"QEhz8aqC2iIO","TLBJoBisEUzl":"DcP0jODrzHcE","g3M3YSXa0TYu":"Hp2e03nHnHLS","178goPbHYzmV":"AcShs0znbakc","BjZkFmEsOPxY":"BNmlOMBnOJCM","j6sNZLABUGNB":"UrPoVKAlVUuH","fXNV9HF2IUIg":"4LQYYdITOSyZ","qTZ4o4okgXzS":"ziUnpSyDHg6F","Umcf5tW8b0SB":"YSe5emTNWZRP","hGWby4XIiyl8":"jCMFVuMXjMLA","FDAqqzzCfYsw":"x5UO4A4Tp2ZP","lhhcjGj8RjHB":"lq4KsUyVIo3H","RjLTrvhNjjE8":"hoz7kUwwSMgB","53sU1lhfrpkM":"SnJMqEjmm0rx","XY9MficxEK0h":"zxcLAsThtISI","k6VKKFcLlCfq":"Buo6Zt0AHCJ5","F2awbr5u4PTm":"Noxr5c5eH5tV","98y1b7fgX6ir":"tUBbOfbwP2yl","Zi4zRZx94SAp":"4EbwAEVlPdNP","3YrzZmnLYUBs":"DfCTARC7fRBZ","nSt5dC282oPj":"82qvCdkrpDLZ","PS8VACpsRbYD":"zgNvZLl4Wd6S","2OVckpEOujmf":"O5Nx9ZB0uuuk","RdcJjdcO8qLV":"ckFX30r0mShV","zuasa5AvziRX":"6oU42sh9w1ib","Gk5ALegcj93p":"LwXa2soS6leY","cAocdyYADk3n":"g9zxipUHaM8U","NONaAswEa4hq":"Gzulv5V4m1Ej","pS8HKO055cyt":"zeSWC3aP7QyU","mMA6wIhW0XbD":"Mf1MwOlQF9zc","GLVqm6TWpASz":"ipNMKueOY296","8ilIHxtEWd8Y":"0iy6syrx8XAP","dJ7hHpGEJXjU":"VbPOzegRxZr6","3FAnn1tML7Wh":"0LFWLjLH3oRk","HlPBuzi7Nig4":"BQ2IjWfzv4mx","I7U0MLcqwtY3":"UdQiJNvHrJEj","Oe8aZyO6LGrj":"o8XeoRUF1Yeh","G047wdwNBj0t":"2UuPsePAH8Ba","gHU82A7ofLUc":"hRK4SJ2VVKMt","mosq793ULha2":"KYu2st054tEe","hjTtmyGFF880":"UoZKLi4gLXc0","9Pno2Lsf10Gl":"D1auzPGgaqy8","xQHStcnj6Z7l":"1NLbYWZWasHT","7O76EU40GYQb":"Ki9KcqPoXLQZ","3TaeSmcKaJs6":"lfqklpLIaSKw","sU1QdNCKg9yg":"K2ZZSqlG17uo","obEXM6SocRk0":"MbyG5szmndVQ","ujRPeHetG6Ed":"6F0L9J1H2Bge","An1n3zKtPuuY":"LGMDdq7J2J7M","zn2102UxErKr":"WhqXRDpdfpyJ","9LDCXluMPXpD":"8XSsTrfWRfq5","03GwArPK9YK4":"AOHuWYhAiEjE","vbp9wRKB6jSa":"WRmwRSyPOSQG","56oiTXLkkSkG":"32fkxGsXPH9Q","OrgDgmCwWBa6":"hPhBTQl7hQ44","ZCvQh46jIDOf":"zUmyr58QBCBm","oDX5wm3ODe3a":"QrxKDSdDZYkH","UIr8dTzyaXqm":"jQGHlpKENML0","fiir0CndaSgh":"LCaPwdNZbQIB","jS36MkxEKl56":"Dcuopsi5poXb","RqCnLbscwhqA":"mNEJw9vpAIgd","zewdfRWIuRYX":"yNSmN7QNlaa2","9Oc5jlIegNzV":"zxjALAo1kLjL","r37AcnERUeC9":"cpAPC2GHPhui","gRHj1LndeC8r":"WGJpxBTRfEDm","CyPwzFA0rYwt":"DicIvmPWTur6","KsY5x4D8wZZs":"QSqe4wGDjdui","ixOfk2qPcfhn":"iyOLlJoJQOoQ","qmJbXedRkoxz":"YSusmuqFnH4h","isyuumf2F0QP":"GkaZwpIOk4AY","a7LG0CUjfjvB":"xT2Z6MNibnTR","BXGrigxx4vMv":"bSjWOAELWezJ","38U6Qx3C5Ezw":"0DS93m8Acznp","CzXrwE1EJTDu":"dxvzRh62EyTy","PRA4cSxL1zbn":"U8DvVP7SvVvn","b19pOHIzZ4kg":"BQsqJ6SkVjNu","OqHjHAwnGPg0":"HhlSNyn8mmbZ","KYMQ8JOPdxRc":"j5LaMajvR32m","zdZaS2KrH2lR":"KuTGwTLcXrib","l6tlW92WpKPy":"nmaSIYaktc23","PveO4WSXjr3p":"0OdtDo4dGGAk","R75JAAg1WOqM":"SogWysUTdHsc","fYMoyyIkf8Qr":"I1R26HO2fk3B","0WugShpKZiOI":"vFOx4RNr0zzU","iNCmgPwX8Ujy":"XQiaGU1Vw7SK","Xkx6oi7yOKBb":"WxohoLJpMiAD","cwF3jUZOQvdL":"G6JdDRQXHnrU","DLO5QIQkhScy":"QDERFiduIW78","Vq8NGUZ685sz":"haCMJAj2Deh2","1q8vM7AXUi19":"13YHBIGw4E3m","DpkBZL8g9mKq":"FyuIoO7YCqGj","MNgzbnPl3Rfb":"LJ0DWOxZcC7G","JZ5hfQRAcnYJ":"6sioStmTqRut","BJWUhn78HSzK":"p8og9pdMN1W4","nkay2soYAPJ1":"6EOpVhJNml1U","eAVYH9Comkak":"rBwWNaZJKx5k","4mZTPfcr9raL":"oeICCmybQNOP","CTv7RuRhJH0Y":"ZciG8YCFo3V6","O4rahHaiX4yN":"dAfDyFj5lkRy","QQdLx8uPiQ8v":"VRIRrEg1O0NU","BsKf05ploYR0":"1mqjnpO9tATA","X5aKWrWBgD4R":"KW4xtReOcILf","euEe1DxikjAI":"7XmGqUWThi0L","1YHdKUEgYcGE":"ywzHyl27JRhO","JxLOGZ9wP0kP":"1itJY8Byr4rd","sXHMPNgt7ksU":"8NqH4PYMMNdr","AlCPnZweYW39":"Dl6CqfZu1FBF","8Fbu3MlwW5eJ":"XvaCSZrFoLtc","bCMNAmzSdc3W":"WzVD79p1Fws9","FEkUgH7cXFcx":"jSS3sUr1W0iC","L9of2ZH4dlxK":"tqmOuE8IShgb","i65qbaLaRWDC":"UnrFW1UnYapE","RhVlqeHWlCsN":"j3u5idQ5dFKa","guwpHfzPTVol":"HDKeqEuIUXoy","S4efj3P7bQtn":"qicqYS5oaG51","LWnaT4jVe3SJ":"ntdqm4gzzIuO","tA1GFnerLo6s":"7Kk5BZbmezGo","33X1i616pB34":"Gn7yaYIOUURI","IdYcWbDa5rMY":"phHxXTPizdoW","Egz9q4n8D4WV":"0xkU4a5lcTPh","IMpxdxBdXNPi":"rOj5JmFvXXLh","Vu4IOcM647gc":"g6J6EDTkh3Dp","KyWop7arbCKu":"sAR11HBBsw4Q","C0sK6HzJbHLy":"gAGSDS7OLY3a","NsvxZJXlvxBT":"j7bs2D1E4W6s","4uyLKhzuJXIT":"gYE8UgP5RBZp","53uYfpQiG2VA":"5Aj6Wr67AOp3","YcSviUr2IVcY":"ecVa95k8ToY9","gnnNAPk0Rblq":"X0h15Srvmya0","LRjshu0RSwWs":"r7Mvf3u13Cd1","Fwa0OiSnbICW":"F2clKBz50xmY","X9RLFLh1hsHV":"yY0mTZz0lMTI","Ta5lhngeHaJI":"r7n56DCL6z6E","ZJiLxCZ7wchw":"X9OhLuM2aEQA","0JCgVPxGuJNJ":"a5O13Is6nY8u","hauUQhP7iP19":"JvO9qutJBGQM","oDMheqEz2QRk":"tqS8MGiXhE5M","2TjANU0YcSXn":"cPfvulbEMcZ6","oefqPuE7GS1v":"MQoSaQAzGfWO","l3Nxi2dMGe2k":"cgCzpkKQRbB1","kELWRQGnqO7t":"w3n9gopnU6Gz","rdv7xhMI4bdq":"IMgVeHlj92he","9KyPl5oM7Kt2":"aRBjGUFtL1oq","swojaVjW9kTC":"18IoOHFX6DM6","cdbvp6XPzlu7":"JMMyIz4dGT1g","OYLdbBoAPECk":"U5LRW1XvVcy0","C2a3jtgiN41M":"UxAiKy7BDuxD","2jSsVJDzEwSi":"lHE8t2HTAbFO","5tI2IFywkMPe":"YalXwJtB62S2","rG1fBqwOZcyl":"YwJaAjulf3q4","Y88DHLTezlYt":"aUXHIYGfzzNW","dmJOZr2Q5Ij2":"AxwXmvpLiqJj","S6xlS504VD9p":"Oj30wA2zxuMY","HOQPuAh2btsQ":"ibtuIkAuGdc3","mqbOoG6Wf7uU":"uZh6Wqxxyyjc","rIYjC7KIEwBF":"hMVgNrO24gFT","HozXkQYHP3c5":"euiayRRiBu2A","72gRlDaHW0OE":"4KGpxBK19fXK","dIQCOUygLXpa":"zb988g7c7r4F","ZA6HezIN7gjK":"5AltewLbaa3f","R5s6lgLIhazR":"M9J2Vpns2MIy","CFWaQe6z3Kmi":"LTlvT2O3yK1y","EJmGx1kBFqaG":"PclnXLy4yRoY","gVb1QVBvRhrN":"i8L4FumtRsMZ","RSGjkfTmrrkp":"iz6UNnydw4sk","wBWnIM9MButd":"NgQWlfn53bmA","lZk81VOWMsDP":"MZEjjqzFugDd","nXp5Pw1c2RIX":"OYFG0fotO17w","cuqO1VMvuJgB":"MTNKwApKUnZA","aA1nMrWgTU27":"CHPSS73mEyh2","HoT3E7t2IZFT":"Ar8hgatw90zu","yWdGgEAYfzE8":"gffInU6kfQLH","HWd5MGmq0Yh0":"6IlwguVJvnbu","XuE8NC3cq8j9":"mfoKPPtbeCzo","0506XSNJP4Mb":"HREPUqTPXRC2","basXceJGuhui":"TTo5XKtzqfjF","sG8eEmuQanBd":"HTmKluuIgOnC","kjFGHh38T5cj":"JEpyBxeSvx2t","3EmsTWe2IynL":"u3Kn43ev8BmX","uzhQXrBYbh4f":"LIOLR5vOtp51","BJsnT79glO8E":"BZxa217CPaar","DgtQbWUFKkFR":"Y34CXxILjPiZ","cUIGDMFEPPoM":"dwEuJwySbOgk","mX3PPyAJueGW":"8oVtM581cnii","6MGsGRk2PUu8":"F7KyjjKeguXY","Rwmqg45nCBI4":"91cOCliFzURP","GamsdaXBkGEY":"W5rFqxDMvN1H","EvjGGQElu9TK":"E2pBzz2OmwB2","P6dnEoY62PFb":"MmmzggPYAQAP","PmBqvtRW0GvP":"qEFbuTH3MxsX","43PCvlt4bLn3":"4dTDm8i4Z8rx","YNReRVRyzoSy":"N2QRapZhYoBq","Kia6jqwmIAiL":"6a7F5y2FDt13","qAicAOEcHTCq":"j8G8sVDBlOwE","zu6MO3DBjioB":"2nCrPrywh8RS","07n9FgB9c1x5":"bLE0SRgV7Aip","IGS1Smj00oDJ":"ZwxOGhHrDz1e","GiB2i026rEuk":"a1bor1KCI6Tg","sGbRVGeOTYxS":"rQoZvIyceXI3","eULRxjEVA4OL":"EAxe2bWn7M4Z","Wht9SVqQSSus":"A5u0H6vzLFHj","wuLQsg37WzRm":"3jEP09mjd9nJ","4q5fATfq9DHR":"Lz1VV2mAf2kv","h5YR2CJdQjM2":"puUVDxIikA3I","78HrPO2EHlF3":"wFizqx1Am3oL","YNkzfS6zhr9B":"UcxdaA9vf95k","PZ4vHRC7PdoP":"unx6tvfTlZOT","3VFkvbLWWvLo":"diKr9UB03U4k","PY6cnbgA6m9Z":"ANsMGzarWeyX","yS6pASriSc4f":"rqTQoklFV3Mc","hmDHEtBt18X7":"3Sxk5ZoGXJeW","CgKL7CO2zzzL":"QFAAz2YjTO5b","KylyS4yUZVp2":"6KaRKUe6Sg4u","bvjlxOdtl0o6":"i0UsVTurAaz3","riCUTbqLCX9h":"TWnlmM1ZmJjv","7YN9qRO5g1NN":"NFvVTCsropU8","DLpfpLE5nvm5":"JmqGYIIPOogy","9EuUSVFYlBmh":"p3LCIaEQwa6E","kgcDar19OTQb":"vmkJbEwAnxKY","xh9yyqEzioag":"Rpj5VQqGWuUs","OGOIODetpTIz":"v2Tp7bkCnQIu","9n0ppnO2Onoy":"J3mdqhOsrX47","DLgKpU7GjzeW":"PmIT3hgU9J8u","QDfGP1NH9mSR":"Ks3rcSo00Iah","V1pubJu9br0a":"uGIcrFQcrh4p","y9s6cPpF0MDN":"EABqsqedK2bl","vUkizbTq0SvR":"iIIgOOHBMdip","4i1FPLvzHjJD":"aIfZfmuKAmUe","LVHK6bJsqoyW":"ePSan1i5nl56","sgIryZIFY0LJ":"WHJnlwKRaTfM","bMD41xDIZldz":"2VwcqeqW3ELf","6wbzoHolGoCB":"zHs62QL1NNyU","SBLCYRXUeTLn":"CeIjC8aDQzrh","OT6pGU64ulhk":"IY77GP616CP0","HBLZeRhnbBSp":"rcpkdR8ccmoJ","uH90P9maDxkT":"R71dtSgTMkYd","TW0e4k0uOFDB":"IiIwdj8NFm6b","Re8f7Mu60yN9":"J434xL2XXqBW","LNL39hzgso6v":"2csykezZ23Qy","VnO7Nd1yTAGB":"9RFHJXVbHYE9","W4Gjt1KsfsXH":"E2Cvi2hNi7gx","OOML6yHTD0Li":"EvDb36FbXpPR","O0PbVxYRvtld":"9lOqo10pwqEy","Ck5wrspTsf7j":"EIQ5XlkzySdz","bSuLyvfquRKO":"nxyklFu1hi18","5teNhCROZ4xp":"Fd9vHioT1Bsq","R7cB8jbqznJ6":"BAhFM4Bu9udn","qlLaByXqwPyv":"KlrROJ4o7FDz","2QAj5v82kn0B":"g5uiRPhj3mP8","2D3ZyChcy3wu":"5thpJJghHLp1","MPS3AwgrXxBS":"iSL3UOYHhTd5","l1pIMh5AP9QQ":"k3vZzdgHopMs","GNKmc4fVr1gi":"5o120xjp40w4","6wG5DXo4zdmh":"zUOVUFClSXSc","HzsthzBYsPfE":"b4H9zIypLmKi","hL6UQcuog5n7":"rj1GV3OwVAJQ","7CGQZ89YT4cG":"lIQDMCMvp19Y","ZzhntI68JH2h":"G0iN03sUl5HR","DOJNYaQil2Wc":"uANw55kpTIV4","4XGwXRgNoeZb":"6KKcMpx2uMi6","vjGordj6CVlt":"IaO8YedYgQCu","4Wmww4eSJQie":"FmCEqRN0vt98","Hu7mONgrgqV1":"RWSWEU4NKNxP","yXXuRsq9gQdn":"fmVI0DXEWRl5","bwE5QKMvBnbg":"dU1Q9KEXLzHG","mVQF0KyyTS9l":"SpYjMhSSQgHz","54kLvR3fi4VD":"juZjpUGFjzqf","XTLM5CCVzoVo":"HbWgtlozw0cI","7urInoCSsqqd":"Z6tQhrlpGUiJ","tgsFpwANMjUS":"zQTDLCIgevKY","QKnPlGWfyn9c":"ifRub80IXhwN","hLBrr4Yv8XaM":"kMLlL2P072iO","ZUChqrgT0OCb":"rpiZjoInvORf","zQtSiAFErQMJ":"LLoEg5af43jX","zrzJqFrvZaj9":"ubp9KlranQSI","8GpaE2c6gIlW":"r6d0H5CixWUB","PDLcHDp1eei9":"mkkGvzSfyUK5","23qvifhZuYhU":"86gpNVP9hUBN","KHnhCIbt8nYI":"wuWASuVY5Oeq","RYCjEDLQhvAr":"LB3vVNyq9yUd","nDTifbrKkwY2":"Qnf6LYxEg945","My9ndfXtyjWB":"7Z3FUDVU426J","zmHrE3Q9zpSU":"r0e5NjQNqbj5","nIZMIEqmRtxz":"wkQqmsitNLyR","r9sfSUCRa249":"eRqc26M7BSpE","9uBts6bt6mvo":"cbKY4mHZdTda","T0XfXu49D4T7":"GM1G6cmwiyyk","dMZjMmxu8SzU":"jCdBgGXIshHT","FAEKLwCgUd7i":"1IjmuFSdhbsB","8Vr87ZWpHkxO":"IocUAYWqSzQQ","fhynJoPNwURk":"28xFlGCimBFL","DoUf3icWCa4a":"iAPgho4zVVSq","UPEqhrZijlCs":"PScCqJUdDVdF","mlQQ5XH9qFEa":"wAYDsnSvbvBS","ceUrhbfsSIyt":"hNVoGzNqeFyD","Zg4zEEV4sWgX":"Mmtea38j8man","gDobxlOHXLha":"uEE2G1J8d2lJ","4XfGwbmxiJ1G":"Ueqhj28Rg02h","t0vvWWKwDcZp":"MIK0EZVewQjO","FNBWIdi0iyxL":"iYBOzdYQT7fq","wWgxbZgOPi8j":"GhXdS2Kpa7wH","x91eOwk5iiH5":"a8r6QlCtMYUP","7DSTa6gOlUU3":"qIfLqd8g0Hdd","EyaIypsTYyGO":"Q1lnnxxWniny","eSA6PhEEUnV5":"2snnCpe8XgVp","chkoDVLUVWQP":"Z9zbIJXfauzY","z92m0YeXTEoV":"fZpowtOKXo77","gyd84rjec8SJ":"Ez17aRpJIBF0","tovJrrjpei0K":"Ue46jnm4tLD8","5VaMHHf5Twyt":"PgXKws12Snoc","49rvboYH9QLH":"8OkwbwChD0Jl","tZ3ROKutE1ca":"nCni9yp5c0ay","7dzxBkqiCChy":"QntthoKFyMTo","mUxISLaXQzPe":"rEC8viXMLG5d","aBQQKBJRP9WG":"jpKmuQJPvGBC","h8kvg2PEuRqZ":"esszRNo14XNT","TCYSPBbFL0sN":"4t2ZStOeNXjx","g8anYzyK203R":"u67s3jL54eyO","Po4ePl6VmHZe":"uELf1X6Quytw","L3kxPCjbScUD":"GFYHa7RIa6ib","wXNK7XJavQbL":"PiZkfRP6GLDQ","5dVW2GwTLfaK":"XKceJgB6hY5F","rLWHEUlWt36r":"MEkB7dUBhWvR","c1PWFOgMXS4n":"E0Z1PSiI8z45","2iw1ODmHJIzT":"AaNDziRsqSSY","95eiNe7qTGfr":"ZStVMBD1BaAQ","rxVnaZNB5htC":"XxxauKSxbkKY","jF0zjiFpgNTr":"Iw5HUfTyxfmF","dLkZGDNwvfB6":"LLPgn94j65S4","DqUJ3MJHtfIq":"ZDNDclIa6NPE","5z6rgaijcrZm":"QxulrfGdTjbV","Q7E1aGrK72p2":"JQwp2lOc0wwe","mfWSrVyVWhQB":"m09IgX2uRxax","kqUBv0BSF8l4":"Nbrxc29VbguF","GoHyYhRY2gt4":"ExeMy2CeFgmB","9h9lXjwDDa6U":"lXTUkoHrej30","r3AWRWkCgXbA":"KfpXIiHUd7bB","Myu05by3Vwzk":"SYnbyQUAuRME","nljQmE1LLOcJ":"Whw8VZ2VBN1N","nsb864plHnKK":"C8io7FJgSzgU","QSZu18GgNwFD":"v7o6JM19o3i1","XTy4ByXD4QgD":"deg5gkzQeetO","0Yg7N22kbRfI":"FYGoovBklrtS","mzwxRhtor2Tw":"Dy0qRYxsMws4","LD7UZMuIAAxA":"Y1OLmRi7ULbj","kfX22R3CwDqh":"paDArTKjJGIm","E5xXuqhtSTOh":"w660PsBBL4ao","fKCrL5pD0Moc":"mUgMwDAvQP0m","nxKlvNi5iR3y":"TkycVyAp8hYt","ni8YLiZN47ik":"h4VKixjUMJWL","kdOlO6Jak9gf":"H2XRSAvpTVl1","8uSAl4DYp4hU":"DukmSNpQKUkX","cX3cT6ZW5NTh":"QYD6Rf4lS5zu","8ijVsS30KddU":"jj5Mr7m0dljN","ATi4igFwFitb":"fsYdBgFx5Jig","kzOIwDAR322P":"7xqu8pVCPD5E","MOit5pe0K52v":"8McywoSOBIyb","FhrAgQLcvITQ":"wBDLHliDoWyA","Dbda7Oi1FqZV":"GIrHMy18ZAwd","89efjSQjmrsl":"CmUDZiYsQ2CM","qdThmXwf9L3o":"CPR8plqYnKc2","V4PX64nVDK9b":"SYgzJHakqo28","WQLLmNrRvAbQ":"JpXeJZJMCtP1","3wObCKJl8Ki3":"zVwNmtEdm62X","s1x6y3dCCxAV":"KGaW2TjIKnVv","MDqAAEE5zlSR":"6sC2p9hea7wU","TVXdVDNoLYE9":"AdY4m5gXQA7Z","TceiTH4MuX8J":"XFUegDXFzSgV","jygNYdPwMw31":"yR3ESWa3tjBe","O2R68BbLlIE9":"hiycxgoJjKMD","gy42RPCYe6fn":"0o7XDQ90amLA","kBuEpLbx8mUL":"AxXtqpj1uBab","q2Qo1D4XutWG":"9zeBFVhjSgeZ","jldcsFzHJJY8":"o8pPBchniiBG","kxL7Oa7awN7U":"lJBMJjhYivp5","V5AI5ziMO9bp":"Osji7LK0Z4Ry","fRBmv6RyYzeB":"yQpBHLoBCGMi","Bs1DyEi3UWZk":"hnm6BMMXl8Hg","5kOAHZ4CahU7":"QEYFe1aENUkb","ip2kdLzBEoN6":"K1iEm9OntcnF","NFJRW2fPu36w":"VC1MKdNLyiM1","YsgyNCKHxs4R":"TibH1pjzMid5","ww5BmeL7q3Hh":"f37nqpjG0OKh","ZJPioSz1iMDt":"qk1bjAFpwiuP","7Mhg6qPg3V4X":"xDdecN61mwMC","8hid9eGt4YTi":"PL1fGf74f159","E3AxZv4vjN9S":"NMYHZ5VgNrx7","l8YFsI3Ltun7":"ODmzIQiz8fG1","Di9bG0Xne75D":"Q9u7yS1yQOFA","yWUrwk7Bi9wG":"EqXWvmwpnoo7","L2bukELLpDPk":"fTLEfLxEH20a","cOH7JchCZDHL":"6aklALtdAt7s","jsdnSkEpfJtv":"kOewXx6zpnE6","1yxNSn7fORDK":"SQqzgM88szBP","oxd3EcvP7PRm":"pQ2xATiYOnPx","SLgTa1jlwjAt":"gQigFd3Wjt3f","420RkZrQIpMW":"P4wQoK6kVLop","vhoupPYTSn3M":"ihLx4n7FOeNC","584XaWSlcr9e":"DfXG4K12mMgm","2z1Je9RiRlVt":"wQD40melvZ8X","KXvdDnLZf9R3":"eNF7aRovgeio","XoiqezZVEp4n":"Cgy6uho63HaD","F7N9iFnlkgFV":"m6PI9pvL0vXE","mZdGKx4QHou1":"9oem9gZwerS3","EF26jHA61rIH":"2WBeRP6ZE2z1","pOw1tfqVmoVw":"aaJvQq2n0UBe","lWIgLTnEFDQs":"83EKl7d3d4Zm","zTTsU8AClCMU":"CsTY1bWjUVrB","AsqovOxrj5R6":"SE9pUsjxMdHg","CpOtY3v4uM7Q":"RqqKgXvZPOQz","fVJYimFhcj66":"fzMnax0FgNm0","o2W7SwD6GmLl":"kY4S7gyiBBYG","nEEj97eEkIeH":"g6eW9xGqe5gx","Uu5el8DPxudi":"d5tPohE1X53h","1HUB0ROx6DFw":"c5f0PhxA4N3J","MRrXubC7BqAD":"4fJUkAU87nnE","yjruCeBZ1XxI":"BfY2T942DoeZ","sxO9hheaNI4O":"0WrmVsf5uYBA","x3X0IVJAOBzE":"zkzAD8gELuNL","wlffj8OXduKM":"NdSK54uiQroG","wHFUHeXnl59m":"c6CS4sdLKpbp","WniKdsDlsShU":"pn0NBMIXoeBA","qe3Bep9ahrza":"xPs0WJtQKwQN","X5KvjUi1IgLS":"pLtrREZmYNmN","QRlMnZlO1LY0":"OeXFWOMqWgNn","QiVuqIP1WcsO":"Cbpl6bJNG64w","pfyYlSvTWtMa":"IKuM4eTQhix2","718BrHOzG1DE":"X3Ikky2up7eN","NngTp6orrgxL":"70Sn4gmmSbjZ","PTaIrGmnsUUO":"9izHRgHEj98d","QsUyHIrPwOJM":"MNDJ0xMaFYIZ","WVxknxxE2hal":"b1w9Unns1N5P","nNDTZDwtASrd":"mKJ5wHHvdJXV","LM9GL605iIkl":"krIDjACtSnzg","CuhnMBrNSpUN":"mmoDuTkOGpKM","lBHSbicDPYjf":"Zg5r2bc6jtOM","xle3ZCPxYqDg":"oiL9cpc8n8wD","MD9kmzqknzZ5":"N2BIXpMNbuBz","9zucF2AmTt9b":"yiWR8CZopPVg","FPy6s0rJO61i":"8olzTunVJqxG","rlJfHWq8jFl9":"i405Uv8c33cS","6avWkH1T0TaN":"GW7zhRFiaYXu","xA1Eh1W2GX9R":"oEBIvPNypelp","e7QfLwGyIWjG":"DdpM4BDkAECk","fkdZzkN6QbtF":"wpBFtCud2Pvd","OsjNt2Bpn4wt":"EmVIosg1mONi","9qPRQ9W01rzO":"IySOkXgKJwYj","ICNAr21UkpsF":"2S7qoucmmOqr","qDosemDlXsJ0":"jHcZtvcZll3L","Vqt5NkheLmTY":"XTKmiIbQYd28","UxZxDNzA0971":"weScdAPN9Ofg","9jy4SrncTjIi":"wzmjpzw0IUe4","f2N9We0Z1HMF":"vx59Fnp0KQrK","47jjwpACKZuc":"HJu6UtDZNJcy","2BDtVYzkVnll":"wbL5HbsGI3M5","pV7KokE15q5g":"o7RvTvbeR8ch","2EfuE4rXNQyW":"nSjNhxVBH8rU","6bAjUW9jfRPS":"N3wNSJSYBlZi","K0NPOfBUQwjV":"4MwAiRpHCagt","g7IzfmuHV5GI":"5hnPu6JfaHFo","IPdHF9bBoyTk":"hotsOHu1PMtd","M8Xk6dsjzGRC":"DfEgYyUciBxw","hrUc9RUyvNT5":"XerryL14J3dU","ttS1zeAWXg8m":"NYKJjZbmgAot","fQwikZUBo2rX":"r1FYOdtSZ1gp","0ki8vKzsBBs3":"H93az4pFujqJ","LCpfe4RWCE2H":"fqDEvTlSYZUs","nfrfj4BJI2rj":"qW1qktn5RBvg","i1MchvH8EUmD":"nsdknj7x1z15","bl4pFkhmzBWl":"8q0rRQLiZQsl","VXDnUIUlnlUj":"esSAegVtBVbH","BimXd7PhTfAn":"Q0KgYMrSNdJy","Q4etTRkDpDVY":"5wZa1UZeZRU4","1s2JjdKgKvae":"soh92NyS5Iog","2IlrRL0bIu9q":"KeE8zWScGisV","ZbawAFWV5Uhk":"qQtUtztNKBzu","6pMFS69gdcti":"5rLYEppDhu3B","BtILsgyKpJ8i":"tOk21ffFc8CO","2cIfxFlhKhcg":"vJEz7IYPQSPj","1mY7pzK3DZIH":"kVJTdJNVIWFu","UQF0ZzwwJSrr":"7OInPvNOOdhw","36jQHowcYwEE":"4BXI5vWdgauo","7qKdW4vhyZiR":"VkKYWT1ipwNy","27kBn7NyMfNI":"n4sDAKaYYJnC","mMRyrCbABHZf":"HGgmgwVJyfY3","AsVRq3JIqD8e":"pLqNKdaJD1Aq","raT6ND77Zmjp":"EruSYvsnsjX5","4HJueioeqLrS":"hsPDcYxIJ7MF","G2FijXyHcHIT":"oz8nDXzUiiCA","iOAxsLJwck9Q":"RkfSWrCXNhKh","ELX192KiMoDs":"sel93K3k1zLO","W75kptJhzTSX":"zmMk0dNb5nxP","OnWO6hxCib7e":"g0Dd6gVl9F5x","rnBh8Jyqxa1y":"c5FliuOdY2el","x4G7aWybNZFE":"CPHCL2mvZpyh","YB9nFKsfdJZk":"IYiQmukcPB7O","arOiM8jl88LP":"vy3sLnn7AqA7","xJuzTWqNE5XB":"SAV08suupXly","YpQQG6IinH9F":"Ruyl6pXkdjod","yDiAunZ7or5f":"VDF6ObGNdOEx","J4HxJwFKVU7C":"hf7ptH4sQJn4","U9oqsLM2wCxY":"pNm0zhS2xd0w","ljCOgAWmR9tO":"gheKpHLOwg69","JtsgUiPSepNk":"b1fO9FggsqQt","E5aZH47gvPUl":"fkED8qGsit6j","A9zsi4q9ub3m":"6wJ8ezhnMUXd","Azj5na7BmYxl":"DJ9zeg9sLL2N","bo3lRJYWo4Iv":"s3mrPnI9NbI1","dQUfqyph7Cps":"6ecWYvLHh9Tn","xJky6x1B2CE7":"OGDbemADrQtI","UAxPLGi2PCx4":"7wd26DL6oVJy","w8gG8AxOfPqz":"G6TsZvCVtcuZ","08wF04DkIDuM":"udMhsUY36shr","St048DJGdXyX":"qOuEGuUMYpkB","dNWwAK94CdkF":"rHgUODikxxP4","IQFRvfqC32Al":"B3HlDPoshJf5","X1ebkka6Md7j":"YYWRhHWpNhyK","s3ntEsP3HcHq":"BB7M60nxT3Y6","YFHYwkpFOVlq":"ImWJ4EKpJ1jl","3CjgCGLrYhXf":"Gb8DrAOk1UAb","pl658pcbHj8Y":"zOALE6KasYRu","dBfYC8hhNKGZ":"bklbqPSzu8hY","JN9BXozOy9Pr":"XbMvqkFAHTTr","DalUDK2c8CZP":"ZUP6ktsU9qJK","McPQ5gjIlEXl":"JofWNCqgjyTl","1yGCb7arUyCT":"ur37dsX21E2U","glpvRswU0j0x":"j3NJWoyDKROn","53UeMgYEz0is":"nkM2PlhDUvyw","rMtfVmmN3UOx":"l4vAKPirNPXs","bJi3oyvgamIv":"wh9Avxqc4mT1","turzzn4NvwXt":"ccoZgrqsPU6W","TeRTMJsZVC1J":"bnUlRgW9ZQTX","D0uB41y90IvJ":"d1F2nfGbIm6I","PdvOzQx86A7f":"DiG68oy5Gz7t","HsLvIL7u8ymf":"vyqsSxPhwwOQ","MQGbLmeqSCNy":"zzR7gFqXUsUL","HZNUmfcGgZvx":"ByU5XOfz76KY","unQBT1eD3EtP":"gBYfmCxxjRbh","KUJNBSOACyzK":"KE3sOoalVKcv","yCNClQQ1atPC":"U3rAP8EnxNkP","TjnsngPGbE8Y":"Dl0uzvoQcz85","oInOmwkiWcNd":"oc7lHFpi0TKu","Li3mTahCfQY8":"DMnZhvFg40BZ","rB8BG86hfD0c":"ATADO6cX7Yoc","htarR1OlJWPj":"kYfKYrIa0yT3","lYviyE6fMSf7":"DFwPJbUOef5Z","Drnwt0hzQZH0":"QFx72sLR0MDg","iSfLCNvHhKjl":"0SEVKhPDNg95","ByysvgCdAIk8":"CPar9Bxs3BJi","SpHsLYI37N5z":"DEB1CDTwXtss","oTDfw7E3ILMj":"tLyuKwj68OT4","IszjnqBQGYI5":"Z7iH6IlRKiwB","OS6MluSOVuli":"IHsFMVRmorT0","IyTjZAGMmmH3":"6xfhS7r83UlU","jQg6ohFECghW":"cOKe6SUsLdmp","nUSLAVS3Lhol":"48R9tqq6YLcb","21jBhOOeVIet":"fCEP99rdgYtD","GZq856K7YI70":"w10s3XBgXADP","2Pr7Oqpuu3mm":"T6Y8Fp0s488h","J52kBwyuWKju":"6HZqV94qZGEk","5Txq4NzYRsDU":"jsaVdyJ4Drdb","a6qPUMKpn0kJ":"qGe43QyJFOEf","TaDJ97zeRTqa":"PP3wTaDP1Bpz","NM9JDfA3qwHD":"F3i76h0QCpbd","2cA8EFfx68Bh":"oCA9XG9wuSH5","Xpgz4JGdqUpZ":"bXfkwClzCFVe","hKKTUw4kwBRV":"7InU7h9vc4va","tcf3qKLuPxOe":"EKys8Pzgf5GF","2ptibaLvE45B":"hg55xhhbc8K3","BHFT5KJ0uYyN":"mGNVGvQunqCx","eHhiB7NXnj2k":"C0UHxqzV7H0M","Vp8vm5pq0ea6":"3IgSqzoOSPvH","STjdETYkhPEO":"Tgc069dZv39q","udlBrvPTzc2E":"lVFbWggsd9DS","Sm1hD3P1cHRl":"wRXGWnQW5BZ6","8YfzfZI9usZf":"eswhpKWuh9Ks","6D4J6kxxzoiU":"Cr85lx6aNiNF","OqeIQN6nH513":"cJGT0U1HRVQn","7nuCzOctV8zx":"aOVRS8dwB1EP","s7hmBgoYacj4":"Rn2SCqca2Ykl","X8APqWjhcHvj":"IvnjCOXXElYT","icqMsZuGDnpO":"A5bxykEV6oea","JE9fqEOihOcN":"HtYuoo0fsUJL","cXMRAPXVQlWP":"t7VB1MBWXA8B","YqFHDMWGSwlj":"bdKWf9UvrWoB","0NBBgCflvwKa":"Hm5XzM8cTp3Z","3nwmuO5UqUkc":"BsKg48c7OSc0","baFBXYNufjk6":"VgNHX5S24Zlr","D5EMpPMCXnmi":"F1zl6aWxJQ7G","UxQ7VsXIW32f":"2u5mvfIr5Mwa","FziqK97Z7dup":"iMpJDzKfgQCy","AToQMKJ7xahZ":"xZaeXWS544Sk","seN4X8Ar2yYv":"ATxQNk8jPDJ5","aEJ9AEYN98KN":"PnFMkvrE1OE0","8kBKVK69DsJL":"34yTCSSKuMIb","7gDlOUstP0SS":"PUk7gghcrkqu","ur9JvcFzQvVq":"OLorJmL8BtjM","lFNy3LNWrsfn":"s1VD9N2Km6hL","0oq6VsJasqS7":"b63VLcH1Nf56","YXEelcvBbr9n":"4bKTDb6ygmSW","Aw7l00MGTfM9":"oGMqAZrKC6XE","Qgunl4cYUwbr":"m8BQI7V3E2dH","RHoFsN3UrEf3":"LQMTjfvY0YWV","OizZ00xwW7f1":"mnvs5wBzGlge","WqH0uQSuLbj6":"BbhRJiyma0fe","8sgsjGIYlnyy":"NULGhLkAU5Ew","5ZmHLxKXSIWf":"BO4waPch5xt0","6f2je8Rzg6Sx":"IsO8zj7o9KmV","g8iGwGp5ty7X":"RUIhSfzM16rl","aLF9GLa30QKr":"EKdi5jN81Avt","8EEdK02jDr0s":"PdnW7C5eRCRM","d5zuvXtnNlF5":"alu9s7bhVegJ","9gvsfX1aGnNY":"fBFkaB6Bu73m","b47QmJHlaUQ8":"y450ZzTFvkjA","Hj7TKbrOKIm3":"riR3Cc6NeZ7S","wEeB8ovVwbdb":"trxj5OOPzLxb","Bh02cOJM2Ara":"iRVYAGqC3vij","01buI0EHFFvf":"blgLrHEVjNlt","ViBo5qWSJcMn":"LoZlK66tpnen","idVxzeg7ygxD":"Rp8U0FZVRrAn","93BWOZWURFji":"gOOnvlUhjwmE","usUcuUmjgjly":"DBcHDCzDEbNX","30IOsQh30dzb":"JGQgyViU2RHg","dMXO1eCeJlvW":"RKfxnYt2uf9e","cCbr5DeL7VWQ":"1kYBIKp2kNpB","cXfNdzrksCf2":"fur1hJzafbVb","ETjxxz0z8ckN":"2lwVfznGkREo","zIpgVtFCBfbB":"7EqlRHizYAR6","OMhEjYSSglRg":"mxSVqNbXtYO1","nWEzpNf2oxD5":"kVD7L9yPU46e","lEWt3J6ZNHlF":"3XPKKV31HPVj","XAkctkOvsLv7":"Q14Xg1MweEWK","FZ1aWkSHCV4H":"TUhByKgL8eFe","T6QM1zXfHC9w":"jRAzJzTfffD8","li4s1OU6ILs1":"szpmq7u9dYKQ","CGd7cYcW7jTT":"ua5j2cgw3WrF","h6h4ldTxJ9ms":"jrRV16JBKdMC","DhdNOYaOqtYZ":"gGBO7iV2A7Yc","5ROHzilNpvKA":"yb0llo4L2EEN","Bgx0lvCF7wW0":"sfFSyZyuSrxa","9S9nfVds604u":"gaGbDy0pcnlx","W0VKzGW2hH3O":"mxIs5HZ8TWvI","N19liTuGgLb4":"J603vu3rag5j","WsPo5rISR4SM":"lLo7gy29cF1p","os5p2KmWh0dO":"pz1527olJlrx","Oi5D7cL8n2IT":"qmnjw6WOxLKl","4tuWmPSBHMlh":"DIVT64yPFDbD","XzvaXPkvSyNQ":"Ah1UOim2ango","eT16D9qTWsdz":"dNaRz3uY62q6","YPbfUamneTcw":"TdUzKMy37K2K","kv3NyHfd6TJy":"4uzRvVXZTkEg","lKE7u86zHP0m":"hc7R1k8FH8DG","2AB0aMSMFYwF":"5uTQQGcC0cA3","RJHAQiyQmyTA":"rlpUBhn8CFMl","bapulWvj4IQ0":"SX3gzLwgGmqW","WAIYLyMxVUOW":"RpIa03uU1wKH","LhnGp8cVajns":"TflOZdW94S4w","bBwKNjIU630P":"GVsgAJccRoru","oPrCRAeLI2jI":"Mvj5wmjFEr3d","TKJHXBLT5MHe":"j1wlEKW3DpZf","NST9lyl04uF4":"q471mhnHddRu","Nu8o7U5NmKBs":"F9xyzX4xhRIk","K6XWMI0h6JvP":"a8WYFBLeh340","qn51EgVZy5NG":"Ly9pWDJjqYil","kH2Y5PfebZVN":"Ivv61pgoxS8o","mR08zjygbuEa":"8rEOl5iuTtDT","oroQOZgamY1X":"PFjO39RuH1qg","a9r2K02f40qI":"2rUIdQyMvMsx","BJ7lCCsp5lnX":"ML5micEGShYb","TZqpdZgjk4sB":"nzEtOdGAlPIo","vCz1j3VK9fpd":"IgnmeUMHBAEX","TgvIYAmPwrLe":"ByNXtPEZG7mE","vTmEGHUFo3qM":"IVYOYG0asHMr","EO0xYIbnAWH7":"bsEAOBAun7sz","mI8RbFtN9FFG":"IkmzpKAjZ4Aw","nEYz3dP0rTuL":"Qk9IY35M6r3T","5IzAOGJciAEd":"LXfBth848XXC","28C2jBXNFQJl":"DfLvr2yOA7AX","Nzn6Hmgk59hh":"6RJrF1LCdpRF","1wOWmCPgNLZj":"wR1kqRKEzXGy","CDR0ebQ09RJG":"qUFEAY457wXs","q9FIlQMaWISZ":"gWYCo7H1UWfu","mIXGUcDeGzrf":"LKPcilK6Uw67","Rei4Oy9dbRqb":"F5z393CXDVa6","uRJUmrSXGSin":"dSEUEzfn5lHe","4JrXxJm3XGse":"5LEl4zhWBF5V","SAjkLV3W9vUv":"e5pIvRp35xz1","utc0dydreROV":"1NeFwGcTl4kU","7EDJJulI5CmB":"kUeb0MGSgtJr","vtJiso2oRjh3":"VnX9QRkZQbsk","QpgmfHkkIVas":"mEm4AN5W2etK","HnVKqdELkMhk":"bwPk2Ju8aRqO","uCnBR5Y20cbk":"A9xef26yJDaf","p1VqLx1qVBGW":"tWS9ZiSPCixr","mGRtBSYjBoeK":"un1NAHQvqQJm","C3SAySlnpbH0":"heWLj9N3rNJr","ckLY8uuNtVsu":"N8iIdfLIR7wm","Clqa8j6aibkc":"5UAjYrAhlc0g","MGcJJWGa6Gyx":"9kRfzTeRus3c","HxWuwgoYSyGk":"1tleeGM4PiCR","SQL90drCDRK9":"R3JZSFloEQ7t","qXpTXtdpA2Ep":"H5hfd9UVHifx","Pu4rD0YcCpE9":"BcXskXpls8Vm","c0Qsd0JjwYSp":"FP64Cy8IujCa","otlBd32UCGV8":"7ZWMZFNKUVOT","WqwtdmBprQ3W":"T0HtHYJcgvbR","Mfdtx8GQrho8":"y0jSCbvwgf44","6FKWfEaWGNBd":"itkkNA1zcU3P","kWLPzHVHE6gB":"vj4jTVcbOqAd","6N5m4PLlMJfX":"Mec0spvjTIlc","AGDOdMyEP4k0":"gMtCA8nqZOmN","Dd9ECKEemazZ":"wtwkEL2L0BsN","WhnhtQVFjiZu":"lAoIqMvOkTRG","GMRE8z16ZiGy":"dzcZRfo3MXax","px8NgtblsIOM":"tcg09StdzciY","lbY1hdv0gGDt":"n1U433j8jysX","2jPlxNIPvttp":"lFUJdl7YvEpk","gV1dtMr2uMYV":"psY2cqxiBxWM","JssWymmKW2UQ":"EzHvQzGW8MGf","JnJFoNWL4AzF":"dINUqtC2etCk","ncTfYBpnAb6p":"9cacK6ZGdgeS","bBXRaCY5s28U":"KfeHudxwPk8E","RgYnIQYuoSfH":"2cTyvQa0Aa01","Jjdva8w4dXZL":"CBHHpKl9N1U5","Axj5qXXI1NNh":"ohfihcUME4nm","Df0q9FOE0PJM":"d4kC04Sv5nkY","V9ZqYIjaThLe":"11CytZ9fbULJ","HZ6whtIh2DhW":"MyOAXFaBsO9p","vP7qXT4rbeDZ":"sNFQpmoFMMnx","2gerO5cKW5Xx":"tsb6qjQdtOZj","KwRYcZdtyZ8G":"bKS5UojxLaaU","1NWcNyd5UjEi":"wVp6M9NnYvK6","EY2Lwvy80J0A":"GXCX9nncFNst","nHtlRYmzv58A":"nNcaoICgFvQp","6pI61FhiBqMy":"Hen6lwgYKnrc","uxgLo1yFzwJc":"Sdlur6svyKBX","48DZo7Jywgld":"rncVgHKwt8EX","vrqij6f8Exzi":"jpTMfjtbrB3Z","HOY9Gk4YnlqN":"k9AnTEZNkVdX","MRKWHG0k2mDP":"9UylAFVWX0u9","45bZXd2toe7h":"8Ank37nmqWyg","FNqFollolQJJ":"ptaxgOQwBJLB","HWNQieNHn0k9":"2D9tG7kLK3ef","c3is4FRdoHQL":"uAtl0yvqP9d4","CL9CQU6AHPF8":"o3zgW6kt3DF6","8aZAJA7KLIj5":"XoqjikHTSdSc","cS8mDLaih5gP":"Zc5RzQdV3BDY","9hsfFD8GyT9w":"likkz3ilKvbs","x9WVkw1RU1hD":"DJsOQDnlO4NT","4BfiNkXwIraU":"bzpqmzp49yMd","kmff4l4dywTh":"K5vTxorQ5vTP","sxTBD6HycIjW":"GovGFdfrPj26","HcWrkBaetSrI":"znZkui7dhInK","IJtLzX6knNE2":"Wb8Q3oRvXgQT","q0oWkH0iEU9v":"dAJVhSRxbU1c","RVDlk8hHuvXg":"OGIcZNKPfuWm","rbr025VZAJkW":"0m43rfDUe9Qw","dLyOSKakqCZG":"OVg4w2g5y0Wc","s594H5sXGmqE":"jWOYtdu3HakU","yPVJ7eKtL3MV":"rXO4ohzC0TRq","ky2wvpgpSXlm":"pyvQwAFv4znc","IDRtN2318G3l":"h1zE8y5SSnPm","GHE0LmFBSKCq":"lRYJ76xZyvDK","rcaHMlEQ07Rc":"GF0CMdoDvN4M","v298qWU1jFY8":"qax0dqEmD4jI","IvJ2zettH5BL":"EYBHMXmqyvFb","9m2PGSn55ybE":"Aq7D02ajirCw","xMBnZI7lTsgg":"tpfGmM9v149i","UDyUhZFtVeM6":"ET4rcMM5k1R4","4IGgcjB7tg7v":"cd0u0eYKr7gc","tYws7pooSYEb":"VemHbDwUQPa4","y5OIpPWDNesA":"mrbUy2RsImzV","5n9MZytyXXiL":"afkLNRQWOeNG","ioGx5xspUSWD":"1V1tEoXYo3XH","xWkWXdYAFvkD":"mPlcKaRnybZI","aqlTCiVJfY7m":"1BqpALFCShej","VqllVSCugxyL":"x1H4rG1T5Lmd","ScRt3PmT7I3R":"8bbqCYhxNqbW","zANmED2ilvip":"NTkWO1nJfqxY","S8Vu4ESgWZHk":"iiZUJrAOEuNe","A21n3c7gxIwp":"sHRy47BMkidq","l9u7vw5fASyE":"CSO8LL9mb3uO","WdUAWJssZWQE":"yGIx3yugoQs6","D4Cmy27AXkSB":"qjWZaGdrlQPd","a2iLb1vSuCeG":"Q4cKLRD9yOU9","Z48N8ohf1366":"uslTi3SS7wng","URW6z9IdsPjd":"OVJFfxOtLzmc","4GvzLgEoyonp":"mItNe88yfKnk","oQx27CtcCuQW":"HAn4AjT1TA6P","NPVANjJYct5S":"5iismlSSjXMs","uPPFXhesDLve":"REJvz6Puifb3","tSebEAYlUCdc":"BMErxPcxATST","PuigKAxiHSGp":"QOlEMevvsuu6","2X4LCTfqXgZv":"1Mh8VyTo7YY4","oc34xTzIRx10":"tawmLCPgC138","vVt30AJYRl4X":"koI5N7qCydR5","IfNLaAWoXKZV":"SLPMAZW8kiGd","j0jgOAYPEowl":"HEI3WJA7njAC","Zk6EDmSmN51e":"79xdxU2Nosit","M0GrjgFqy0nT":"qX2g3TVBTO6e","StRCS8pX9D5f":"RFvVdHkW2Qnn","sDdi7RWPfNHo":"Zr5fzVie5yoF","HImG1fIbGQJ4":"Zxvd7Ltz4tGM","t5XYTTDWE3jh":"80oq9UsTM6jb","eWbn0TlvG5j1":"VnpUVa0f5vRP","Hutap4NCHd5e":"IIOGs54IYRN4","X3iMP2qBM6E2":"D40ZIMcM49ao","PMBotVfKJFot":"ierj1iqHp6Jt","B2hmefUML4kJ":"4mIkBGZBlgPH","f9LvPxhM6p04":"GY4HZjVFbSVb","l2CGtSEZ2KIu":"Si8gdHsFwHhX","GUtqwWwdLGVX":"tPdpUq1nyUo8","kPSCNE0Ezovw":"g9U1VBeZByKP","Pv2Rj8jyJv6E":"z0PGxNfnqU4p","rT4RT0jqWCoM":"NpdeDfOb774u","OrkJYsqv7N2A":"4MK0f0EM0p0m","icDZTrJNhYam":"mrmaBVv1PzKc","RNFJ6OWIrC5J":"NoTvvSUDYEvj","FVzZ1Cbvnni7":"ZA94ndV87WUY","eIvSaVl4VV7r":"EtToLDVfhrrl","GGkztoyM61Ry":"Y7QbNK7QftHS","jrIT5OXdLS0s":"caz9nx50nk4c","GVb4zqNZN3eh":"aKpHL52Yf8zl","Q9vNzfUm27tS":"yyoyuU8V4Sar","f24NVxpWx4LO":"kOku5kndpwZB","n9oAHp5PkXaa":"hPlwHll7C4jg","fk8EnNxitsUj":"sABKciqH5WcJ","Es6njbtCFhCd":"mbETasWI5x0D","MhJvoGBkBhwT":"ARoiqMBLosfr","sAjJ2hupqxG4":"32SNmHb3W3M5","0q0fP2T3f9zq":"UOZXzPwTxhKY","JGTyvN0d4pgK":"Keov64DiVx2b","KyzplzIkq9Vs":"FDzJv8W2PFuP","24YifSkeCcbz":"uBvyKzvUbAzp","CrGOjtwVMhqN":"cD9qwscJk58r","0xiXnkqvE92M":"TmcCaoEGaQ8L","h1PrxpLmdDrn":"Om3BjgLDfb58","oKZPuj2gjPzF":"UhjTI4Up7eEb","t6aCIAyqwt05":"JXBlLVsMsqXH","dsgi0LihAo87":"ZqeaJbeAVUqb","WhYUvwq8U80K":"ioXusF2tg3WT","U7YAWyuEATes":"3Gxg4OoQKc6b","WeasDE1ZKetD":"SFKc9UdxoFE7","spc64pRdJFHK":"G6XnwCrModJY","BYnXyjE8Ky7P":"5h4VT2n3UOo7","OQhcOxmYRWvt":"G6muy5Wt5T5u","Dskw6Nsoc0Ts":"GJHVvYeN0449","u8d5pDqIZnkX":"x9Alk503pc0h","cAN8jbn1CtNn":"vh7YaPXxUxT7","iOW5cbOESZcN":"pDAznYB1MNDM","UQKEItxC2HZU":"POLfYPhuMUT8","I85TaVxp4hGf":"d7azB7R9RBsr","cdhTd3gRTm7x":"Fz489vXprI4x","kfbgXLFKajjM":"4AMkSDOFO46B","kpd3gQX2XSiJ":"Z7usse4iQF7a","KG0pHxZ8HlNo":"ctwmydWCxFdD","i2sJjoANiKmg":"z7yBxNRyUX47","mdICvZHgbGvn":"hATcwdpEhi4h","iuDivygdvb3B":"r05L132703vS","JwPFYOl5pf2t":"YZcJjRPDYtf5","50YC0RJWlQoE":"ZJiLdkdCRvZu","KVaSFmS6MnoF":"I9JOU7Gzda62","8BBfGMTqTz1i":"sbT9ZWonGzUq","TgoBihViG0GU":"zF6IPQdU7w1I","IaJ7dTcKr1Xe":"GeZ08lvfPDQh","cBx8rwznUJx1":"XF8fRHWNVuMg","99fQvq0oIvvT":"P7fwtsFZon5k","ehsSMxdGMabN":"BwzScOyOQ74C","18neVVMbRH5M":"YTgQSkvNiKFD","q9KxHyxsvFk9":"eHexFU9j2VWG","w60TjpcoDJkb":"fNNox9LZKJeB","mP4PkeSTMBwX":"JxO8qeNilf6f","5vgtBb8Emo1u":"DxRxyqLcXi2a","Jr7r5WbXqNC0":"CRotZC6eDKJv","dW9UP7hfIAWj":"vgS8y15R8VhK","mnsdM1jnr61u":"J9bWFX1owDZQ","R6ffXnaW54kj":"3OwncCbn9ZJ5","HrVFWJak37wo":"fVGTcJgJNGYJ","iGcSPae7e6sK":"QVE6iho5bf5Z","IxcD7rgHRBS4":"QIDMIOOhJH1e","FpA12IaEZo9t":"xD8yx1Fe97bO","K2tCJvfCBKxg":"ofHe2c0sMNOs","sdTfvBLXOhST":"CQPWXXA9ZwmP","iYq3cvuKnNuY":"jql7saOT3ux2","zYHYN4R11rnm":"uYsL7grQROVZ","wBJcayVpt0Or":"FyE9SGJ5yZ7X","TIXTV8lWL6xF":"tybsFof01OI3","8i1F3NDyCrf4":"aigPzYTTH10C","omPFGqtncuWD":"CzClnzQNmsmL","sB8QdVqPKd37":"FXsaGric1UsB","oLDBBvoeSG1O":"bp38mdvmMnBw","M0Y7YRc3FGIz":"rBL6f5JTRVhy","CyskTl4jwc2r":"9tLcHsNSEpud","bvHKuD3tceY0":"kVq7xEPRaru2","IWvpUV5aAZvn":"UCbF2p3CKiBW","l9Bpq13oR9OX":"ZxvNZsr7Fy0w","KdiXbUijm24t":"jNTJE5IWnXfd","IvalBQTrFz00":"h02ZkxTKYfAT","nsjzy01CL8K7":"TwfCUf7cdyKx","9GZ1RXYSPsOd":"XBqWSg9NIn7S","9sNY5CpWj6vT":"d59AQ557ozLm","SALgZjb9yLrE":"Hm5DcTrMmBkD","rOsZDPIWsGQ4":"5K7DtSp92Nzm","dgPA3unofmej":"drqmnysGFqDr","LmHF8hxO7zWo":"ZpjakLrHCYKZ","dRoNCkiQpllg":"Fy7Vl5ATrcLZ","abgqbr2JPuj7":"aUy9CTwmVwbZ","uTpXgAUoaMIT":"71AMQBN4EKOA","yKxz5ieM7KEr":"qqehmNTuhB0h","HVVEp5pHM3xa":"1ctoS8xVCwhG","IWdFWKqB7wWS":"yKf0FDBFw0AL","1ps2humSP5Ns":"MxsWLq7bbOjl","rZa6lGLTifTg":"qXIGRv64OSoJ","TmClXdZZQEuX":"SJtKzLsmk2jm","Sz78wk9c5BBs":"jGU19JapJF6a","vq4LGEbJrJfW":"mIuXj2XYE9SS","e1aeWcwYcMSr":"8DrJlXCNlzQZ","OD0gqXg4orEX":"RwVFbQtfyuoq","CWKnjKEt0CJo":"WMlOQ5AUti1W","GSWz8DBp0fVD":"wS0sV4D2gcYx","zUve8900o7Wn":"H4A1YYv8sr5d","me9v5jdCzbjn":"ymaa5zXNgxws","VaN5yOpV1KGR":"834xYgrhV1vz","kJtf2hqAJOWN":"IbjqhtNbt3hL","QZif2XX0POLR":"Eug8JNyL0Qff","Td3FYoFlp0Cg":"GA2XqY9dI5fa","5QaXErKUKddG":"T6bKNJ6xi8Lj","RQTClerkPIrY":"ovtspRrdKxL4","AeIIYw585arP":"uE1VCZ8nNPsl","1wqXbBsGeE4R":"wU4CmDkRqHcR","1xivgSWbj3od":"Tc7ODmca9zDR","oZ0LPSxUWsTp":"7LqWT4msNKpl","lGq9ZATSyDeZ":"otjN6DpETsEk","nAtCNoEwzRgu":"o6cR3ijnwDz9","F4fdNGXRETKy":"91rE48N4CTfd","MsL0HzDzrpMt":"h3whfTAcGF4N","6xZq3hd0ojnT":"5qNsIPYUNex4","n6SQf6tYJVue":"bd77pEguekIV","nW4SLRsdjnS5":"aQSsPDGXzC1i","ZL85NobAUpBk":"6tes7gWHcHUb","TR2BaRjMiFi1":"4ECW3f5AeaU9","TprKyMRMnL32":"CaZ1WFOtChbx","1fRZdwR8PYZL":"ad90SjW7vseG","NYMUaJH5smxV":"ueosSIgvCKHB","O4Rse60kcGgS":"2DXSNEW4QvOW","654oAcktXKAr":"jYM1LVgsnTtR","e6GS311EvvmF":"xhd8NuAdi0nj","xa3SO105J9v4":"EawK7k12PzKT","dBDMAZVW5HD9":"kxGblXmgG95D","sDGVsijwiR9d":"DqBMGyt7lev3","tAPBaEW2UtUH":"gmd8ZRnSkXuJ","ng01Y5uH0nq4":"UiHnQoAoAK3h","583fOBoFofjt":"wfVtqhc8VVqt","Mf0VfK9zqKCy":"IhYzC8Z7OQnn","Nl2MoxeSJYNv":"y8tpnxlxyiBf","kwGtQQiIi258":"kiLYyagSwkvN","l5fspEkDGudm":"P7YDJpDU5OQ4","GKOnBgrOXgyZ":"gGLB3hEVOocT","FPyS9kbPFVdz":"fk0nf0smuJnl","4Q6ASqBe3oCw":"RfjemGSgpBr5","K6uvVQVlBN9y":"a2OLsCpYxxmT","iTlzu31cAT8c":"skxv24dbymAH","YodYWdro4wVA":"sU3UZ2TGcEXp","ddsdCSLiQnI5":"R1XnNCTYjl36","EwRzMKvu3RlZ":"lFnc59nJJMqn","pmSyfOaTgLqG":"XsRx4H5jyzRC","sUMWTz7mA5Mv":"wn76ZNwCJxxj","8wEOAR9Zq62t":"73I6BNBEn47w","mNIKi5vlW9ff":"0niol8ag1iy9","qUs7tPg2Nzd0":"pGvHg4MKkW08","wX2lSoRTa4e1":"6kqv48R8DdIt","AvdO02f72q26":"ugnmGRRiH9eB","hZvPMtSsBVgN":"24cyci2nF5ye","xBGBfl3Z3aI0":"JXU0w76x1Y4c","IE1fyXMdr9if":"JJ29Koo5tMSu","GswCS9I6MFoH":"ISnHymf3r5CA","wn1CY5JJQ6jI":"RlYU15VyG4oR","ElIqW5SCJ4iB":"4z2dn5tAnf1O","dXPGw7uNGN7p":"0EInY2vhOosG","gbafkcwJwSyc":"JIH8ZEowcxDL","PsfpBU2WOKiV":"HCrD5bufXZT3","tUZeh3ulizjv":"htQv1Em9IcHV","kRQ1lP0KLSpD":"hV8MQfDk0DWt","nN2pyMDuTODv":"oeHq5lzRI8HD","3K7GmQwhN7kK":"CWBgTnbUXJpK","2fiDJ9nwNEhC":"QCmagyhjA0YP","PF8jqnlxaiX0":"V4wIDRJ2uxZK","4bOlmnqjgr53":"z5sEuj2j63ri","IU5YqJEDvX9B":"pqWzfNJjgibH","wEQZu3kYFj5F":"HkHisMGODqkp","HRMmbXIDbtVO":"zOlNyftiUToO","BpXfZCsGd6zB":"tzhbLSbUXY2n","1ACSEqMN2LHu":"NvczcSXEEPTr","kfO2rmVeyJ6F":"uX4ixA06ksvw","WsJ9CDEj4cQS":"Tfj1pghh2Vmi","XDKwjDsL1kf1":"qBsSJRJ674rY","B7ZFFvhT3FRw":"RodPRwQt7Yq1","RP2BIyXoevwI":"zD42sIktRPk3","WbG0ozQQAzXt":"DXAkZRSdJARc","a2CgqBOXmodE":"99iqrsuxDbYa","6jJRuLbSoR19":"0M2v3K2laPVk","OlULXwqxxr6s":"9VR5jZYR93Ow","Lex5TflaM0p5":"o6EDcpJ74O65","VDdueqhaIZm0":"yM5pLsxXVmxN","ZI31Xf9YHEue":"uh5KtxilhUIP","YktcZQKi4rlp":"gv0cnTYGVV56","3yGf7RFpOAvy":"P9IkYDKn8WKC","ofeljbcwWJrP":"nWkphIK3pPL7","9DEkf6ld2UfW":"11NRXDaT03of","p6X9BYdAAllA":"Nzle90rX7wwH","QZuliKBzY0EW":"K060rPZuZFLg","exnE0oHAHKjl":"aJGg4o8C2N0w","KbVwQfEwnv7e":"iVnwNRsuldee","koc4DmaNNIAH":"AEx8a7X3whT3","5nS8wBijXyC3":"6KIIhwNGeGB2","7PNdSXX2kq1c":"tvFd9joytDKe","YRez1MoKLSQp":"sHOPMCVUKNkm","p4kNQpLRinla":"GsLgg6TFouYi","HFlvOAZxtQgy":"Ddkt1AnjReHx","CjnSAL3LGVLm":"SjywupdND0mb","RKovzUuk8pw3":"iOIQFXEPK2Z8","zAbOTaMvzUZ0":"4zEB2G0VOG3g","0fXojeKWRunH":"QURFaG38LwvB","cMlInFulUIhv":"HIq9MyujI4JR","lQTHhdhdZRGR":"3EhFn975ZRQL","43zFh7sm3UBR":"1rmj36Xl8pAa","icjHGefOeAKU":"yFVSzNAMGB8u","wD9UHffHe0xg":"iWwxgwFWapyX","9UYUTlGUlJim":"cC1NgtGPMf6L","pwhgVkHX8hBf":"gxDjEBtycMuW","9x6SW6mc5Yks":"24gQ4NnwJxQ8","D33H83gBUbAy":"o8jatnmsj6Es","R5E745rrwNhM":"vFAKJ2wjxk5L","aSzvIgChRGko":"W5JbKDAPjreZ","cUemuNvIIFgR":"2sGfvzAcQ8lT","z3ZuTnpODHM8":"8ZWicz9d2F4A","VXU6UnNGMCW6":"ykk5rJMDGwUQ","vaztwYg5a2PH":"ETMXy6he93Wa","BSpjJmoxf6jg":"AJMlI9A4iGPy","S4ccjZCNGwdN":"QKAn95XASPiy","f1wjOThGmbOf":"dcjkfbCDYguH","s9ROZHX133u2":"CfGIG1r3Xzl2","m8JbO5IOaFWz":"HfyiV3FlxqIH","UyV3cU71nNln":"9ERsh73THeEy","uvsd0JfRbtFm":"h7YeRkqCvsFZ","c8FzgUojweKi":"xxOvcwrzYuUG","QIhRpFRCwR7b":"wHprjLBN4N2z","5bZNHM2vCF0b":"07kIbLFFfHmq","5KoDJw3VvqsD":"coxUEqYYIi9y","oyZFS0njCgiT":"BmJmWt1DAN8V","CyobWKbFAcP0":"0rhYKrQ4sugh","bOCkBoV6oBHc":"PjfI9tQRTpsr","kovJSdmQLYHk":"0nNstOTJ0SCU","PUr0lAqF4vOy":"mdazirvQWiPU","xqd1lgmDssBi":"5mj0eTD8mvIF","Ibz4zguyC6AA":"FM9ou0chJ8NO","zl09jOqKGi7Q":"HMNKhhoejLUR","ox3lcliCzLi7":"2oKN2SIgkWof","rlAEHDf2dh4Q":"IeWndq9isSTt","KITNYOAyMVcX":"FkEUTkrOJpor","cbN2qRVveB3J":"6vlNx3DaeoLM","hTy8wrpWxAZo":"FbCsDTXn9ae6","K0jAfLeFeB1Y":"yQR52CbA43EZ","jqZyl8YHezWk":"kciGrAHFWTAe","nlc2TkARqOtp":"7q7yJevW48j5","tiB4BBsldKBQ":"bixC2rHpPLWb","kQiVehLNFucl":"X4aWsChMpj42","t450X6miIXFy":"6YFYrVg5Ui0w","1oXcB0O2eCmc":"yuux5eJRdLcb","QIhaudK3Bm7C":"7prvyTNhcdBu","L2OkmUyQGPv7":"qoElkGokB9DB","x2z9Yb3AVlBb":"OPkP1c3llQPQ","chl6gmSmWC7V":"G9ZdxMGf1n2B","IS3PFVSSCoRB":"wUn8MeYkkNtd","S5LzSPWHR9au":"6Qc11AovVZMi","VIu6mF3barnB":"mOoXiCmMc0z9","twKB7qp103Z2":"QMB1GrSqMLFz","qVi032ikacbJ":"dN0W9aDKPIuJ","mXQKwrG09Tj0":"riJPNs1ZxNP2","65NiRP6Htykn":"1U8islH8BmfS","mTEgkg4JaQ7s":"ODeCp0PCDzcO","YkcigKmKwZLy":"noweE7tH2RcR","XmxBWPVo5TTY":"PaA72R3xdWLo","qgeH2Iysxhv8":"ZzWtlk6e250D","Pf5N2BNl09Kf":"eVt93Yig4hg4","B1gl59K06bO4":"J3moPCgoYlUS","ahHudt5ctW1L":"epZjeQnGAQXF","YFsYmwTls1ni":"nHhIofxsYklL","SN2NH9byDFsN":"O0v25aw3kVsF","tjpgWjbYmZsb":"R8bLViFvYygp","bpFapmfmgg77":"KEWVUG0HFp0b","TM73F6vn0Wlv":"hn6hvE1WRWWn","b6YdmuElNHUI":"LBfUhGwRm5NH","YraDmDFprGTy":"5u9rM1JRvuMh","1r3RgBbVIetU":"fw1Nj1MCv1hd","WA0RfZHz0iNn":"oJSAL0YkgH2X","qZNYKgJVzyAw":"k5cdHp052XGu","4eWTQhYDJewH":"GfMdqdvIFcVo","Z2X1R5rf4awj":"L4ZuE3qw9Gk7","RExaKb03HyMR":"TDGg2E2UzPfg","WdWUMKEa9iby":"bNmPMDhmKD6z","KGwzbbxxdkzz":"9gIQfTKx6Pmt","E1O5WGzVrLEa":"D3jHIBBHCbPz","212hE3RcBWGS":"EDn0xdSSry9l","SYPWxJ1voQDh":"Vc1CN9l1wiIF","t3NUA920gEee":"peJlW7PMKnVH","gxjkhosZWKo4":"wRH9lnkEDdv8","37LfT0awODFS":"uW6mEZpLTPze","oRlY4fzXlQ1d":"mbXT2sTaGD0T","1TiWkpp6Oahi":"LM4ucRfxqKYc","zKKHIaNFxj9w":"iynBszccut1h","zFFSbux381xN":"5aJ2jDf1tUlx","xdPkEh1t8mLi":"V6UnnoSlQzel","KX77ks5zQPfp":"GaeLAVkatkpv","yMhheHRDl575":"kmhnQ82pKDUu","TwTNK61Cwvol":"2isaomSW1u6K","hoG2ZxFtFJ19":"8RbnFr1UcEFk","fhql81Gd4oRZ":"EGhbeC0jjBWm","wZmHLkxbJy17":"R7oUIZQzH6pZ","omIxZu3EDUME":"sUqYW8yWzL7l","eLxC0gAz3wC1":"hfeqG336gmrc","7ZeByuGsCyZ0":"huZn4o6FGI0P","8E7OBowz0nLW":"VrFWH2LDPctw","5FRxiTaEurBx":"vh3u1FTTpcEa","cYxqCyy7Ox2U":"k9XUT70BAnOM","ALhbdlLERar4":"8lxQ29IaXOQy","Y19n1vfH3rTO":"OLmDK0IfsviI","i7QdwAjidpqN":"6xz1P3mgIszg","crvuc4vTUqbT":"fSM0ELVWEWwl","rBLQ0TP7c95z":"f9bSd3HRaKls","EmiLmlBylMMJ":"cnwAIVUx0376","xNdTs46EPTE8":"kxnOsxKrnIlE","yXfasMHgHLGQ":"TKNOmoebYOSJ","UvkR77lmjyQJ":"C0SpGUlEIOsD","LOhlPBQDWh4f":"lWWphk6Nh2kR","NUvCGrJxOsum":"1fELyJmuBpSq","TIPrglLmj8Iz":"Z0g6RcMWjL9m","DsTk6PYn52i6":"u5YzvrTyZ8bS","Zol4CPneXSa9":"raXeeg00CU5G","0ox4z0XwNdzA":"MXKegM6jTxRj","sYCpqqOowJfs":"VaUuBXiaD3fr","O5Jz6dGwdZH7":"NES1skTclt5d","zK6SzsxmFGT6":"rqrHIaGmUIBS","EoSNEMmKnfuB":"V0ErcSNriKZL","2HZHrjOYE5lO":"u57JEuo7QzSi","hdO9YKLRzmLr":"H4CWAn6GvgSN","WjTIfo6bbDAw":"Kngqq8BaJzHz","KgPo8LLLoTE4":"LMRQMPEm7ZfZ","4uOUPDxX7v6a":"jbKbmdtM8xxA","jbgkSkUMrngS":"64zrvgUL4ETg","zjDgPm4mD0eb":"5UhkaLt7qaMp","2NFlkBfpxxtv":"Eri7cSFiBw5T","3SiePaly11YR":"S4p1sfhiyk3q","W3NNB1o6nHm8":"KJLUPtzY1egm","n1DOvojLLUBV":"CMAIQ79Q543m","hjzruBxGJwTI":"fVUFdHfd0EG4","dqGCfGl1UPan":"j2hexKydTEG1","0yRJXFV44tVj":"WioFEqSTThsb","CWTeZwug1Mb4":"U3AUWO9HJLVY","Hlskt3ti2W35":"MesowCVkioiL","9lTZAeN7nZsT":"rPDA7C6GKpDd","h4VHxj3HGt4A":"77memRHOggd9","vBa2wyTfRwwb":"ypF9oPXQeAvn","f7fC6COcU1HQ":"K6EExIg0ipda","2LFA8gyh9TIn":"bX680sk6CRAv","tMub8k1YMfBr":"HNEsH5PmFbzJ","d5ePTpsDYXZw":"QkORQULfGf5X","75sjdQsS2dAs":"mH6uXUW4hKUW","tly2paW5wTc8":"EaGemxDZNvbn","xMPRAWhw5RYc":"74dFctM9w47J","AXIaoRHhtOS3":"0v2yGJ9FmqAl","47ssE5uNh4Yn":"cS9c1WpwEmps","IwjTbtLdIW1e":"c4Uot9wE0Oh6","hJOiKoKhMd0v":"EhrkxZknYRmf","I0L2aa1YQtG4":"YXWvIcv6pPbT","QsfsKBx76yRW":"0FPzOg7zRUFB","mD0pKFIwuH80":"ertxXlXoSXJQ","MTnsBDbCAnLL":"rI0VbGfITRUw","y6HHiF3M0eHr":"u9ufKABLNUYa","IYUVo9w7VxCa":"g5ggj6VAlGMg","jHce88OOADhA":"02ZEZ5SB0OI5","4WS3DlRO069o":"a46etq9myExl","GqqTMJftEm3X":"dWhgKDkelGye","s0BfBp7pxLE0":"NOA8Ojb2AZP4","ssOrKXpJgfkZ":"5H72kO5fmx7S","JZV7MRFyq3BS":"4UAevJBwW2Xd","eOM6VuVrSunp":"hweSAI4wgl2D","XfWR53hjwlZe":"CcCilucdWqxg","oreg5gRkeYRc":"cF07CCoWqxMB","qBTQ9PoUvsw3":"vq6Uoaz2Z1ZA","yNfzC21GDJ8e":"ajMHKpTkzAC3","SrsrcwyA6T4F":"Cv0fRPYdtU4K","cRWaReQWvxhW":"Zuf5NfsXdw34","iovw0Y4mKqCn":"U8tiyM36QSFB","FZRjQQbyDbxC":"6Ok1Msb2zEX0","pE38UvtCAb27":"DGDAehTjBnDl","NmkETFNOLa60":"T6lto8WSsL5i","tEelwdcBv5Lq":"f0jE2gf1AUAs","dvwFzIEqVknh":"WVlRTVXVMS1W","J3ifQFqF1YVS":"xCANcKPHE8KK","X2xMMLoMbcXg":"8XEKZpEO04yD","LuxIRMy8Tnfw":"XBrIkfoSGVDK","R3IWDlI4lzWX":"9TTVnZRwfEhO","ecDGdpfobqEw":"gpcsY2WjKbXI","8BkzKLruiVpD":"pwhqelUZsVf2","CDOyfsVu0ueq":"bApMuwJRpN2v","r1kynI6ZLxIJ":"b82aPzrXK5VY","0uVrxuxCZ6s8":"EpSnT5HRAoIF","sDDe0LglS1kZ":"d9It42XhCZmW","DrIcBCJdhjpP":"B5KQiVHNgxQJ","kWymYI2G4AsI":"CFXteqFvCcl1","cxawJwofHwn4":"K8WoNwEXfCfG","mXHBjjELJTXE":"88aEg2TKZEP8","AQdROOh3hPc7":"qgpdiAZZDTRT","C8IuXyngVddI":"YazfTNDOaOKq","xbwohKAXmB0t":"Q3LLYnSAySy7","BdWDdYkhqRhn":"c0VzYh8NkWKt","2BzqykKE9hlD":"j5g0csqZnSug","jGpwe0Y0nykS":"QheMJPcKFdoH","fR3AtWXASqCl":"BpdhI6sAwi46","4YjLcJTbico7":"sePcGTlyXf7M","x0yfB6EcTgv9":"cXqgdJjVimlu","Xg0vG72gHzZy":"RgyOcx9BtywF","0gC2Fu8o3Pk5":"CwStGKMhbA3g","yoE7QUGjiRJ9":"Wn7KxVytluMH","u7YiFFfDidgd":"ecUwUVxTAvL9","QDdItsm4GeD2":"v91gKsdtiH2z","PAEkNQD5AKaH":"VoDsIcnDDDFW","c3Ir2GRuJ0Uq":"kJ8nVpaxuvSU","Co2zjpX4UFUX":"X8RhDEECbzl4","7tkcihZJEkUV":"46I0EvtxHLJo","RByQHaWAIFvq":"pxVdctpIjFUC","pNGOVqeE8hMH":"hnBaMzX7jZO4","JcTYGB3mhYBz":"5Bqq2Fitjb0R","N313cR7rftaB":"IFEG0ZmsZd5n","wtQJuu4y8d7O":"kvglWuBmOfhf","5ZXJv0Ur7odz":"t942WEHYvdk6","0OiOiWaPb7Rc":"GonUNAUT43cV","iDGbdqezFi7G":"vIaHEwCXIBvp","Jsf5sRDSIZw0":"GLm9QNaeIKGs","kPVpxkvAUsW3":"2hfkmL2AflYT","E0XCzzvyd9xk":"p0RPCw0c8Osl","hkUJCwqD4lVO":"qWDBrSl6pWN3","gM4znWLJx0Vf":"97QdqtiCtqqD","HPyJBIMhJegW":"K5S4ccu15cln","hQCAz4YZFZEZ":"IHPCJCC28owx","qWWaKBsQ6hPE":"0Ou22NbeohZv","WIVKmOvG0oCg":"qvY7lX4C4UI2","DwZptNn8dJ77":"AdHdn7G775QJ","2o4CVPH7xZN5":"Ra0r6hW8cHNT","vlYMJwSjuIIG":"PvRyXgcZrplQ","z22dYfGMRHkw":"4qqPJmQ3u1nk","uJeayFRrHyxO":"ZFi6NVcRefSL","yzCkU8PPx1AO":"heKIllVvb979","aRRqXWd0ezqx":"8dmg9u2q1WEC","M831ttGr0FTZ":"MCUSMARKVD1g","FTmdsCVOb6Bw":"hVi53pJIRYIZ","m653sN5noueC":"hyxejVAfYi4D","gWPDue4s7SfF":"AvO4Tqo0ox2O","4bhvGy8QqeDb":"34QgQbaDEKOH","qgSory8aJdwy":"qrzvPGu2Vqqu","dikRWRyxIAS7":"VUjIlDtVyk2b","hURY5pLDD8vV":"4AVQcDql3hE9","gLlP9syMQiRM":"TNNiM2xgUpcr","22LP4uEVQXIF":"ENa3cw29Md3U","CYTMaYDblG9i":"Gs3yySPx4NRE","9wdz7Ad44l2S":"YYDVZf3HzdeC","LOJUfWrx2LWI":"SEz5hM1kDAOs","KSwQUmsd1inF":"GiA950WAxIVm","ZmmNjSupxfnw":"qGowrb9UrtTF","h3HU1KCc1byM":"KZLo6rnH14yK","UgBf0mntwODV":"Iv46t8hXaKsA","qtUbaEy5Kgqq":"hoP3F7qyf9JZ","FLkzHbleoaE4":"MMUjJBIKt5LE","pYfkqaGQYEba":"cHnnRGgNyKzX","8cPzMdGb1VwK":"YexqIuh2u6XO","wfj8J1g7M5IW":"7WtV34xujadf","WaidW2osgMEf":"Hj2TFP9ziczh","8q92hVR8hGsw":"J2YibzAF3baS","12TiTcxuoTyv":"y1zL9MkDnl2a","6ZSPoRjW3UGs":"DZWY90CP7DH6","fJOeYyTLpe9q":"ApM7KraLs25k","f76PG6uWUWvc":"f5GdxYJztLt5","7Pw9wEXwRm9G":"k0Sd0ziiSnNd","vrQfyNGpYiWk":"mp1Bc5caw6V6","bRRA4C08Z5Dw":"Vkq2MQzFBwq0","prRJSRalAjdq":"IAr6rnbMfz5C","RUCqTUHPu9KO":"WWp185w2RCPP","7Sp9Jl6AyNrP":"eaIAejRjGdwC","9kQVCmxv0Mqm":"GcVM5lLTKRT6","9QAnc6PgfIwC":"Qh8hs9YimVeW","PkNhFRMXZ84P":"8zLW71HuLRjH","xcNWir4YQSTL":"IRluwYsXULXA","lpIOmb8iyu5Y":"33iMPS4NiPn5","jhnQywDWe6fL":"c9d5CGfHgW0K","OdTkkk7dc09c":"RWD4ZYvuiFRk","Ug61maikXsxt":"vcZtiB3vbi3T","3NG5q0CNMFUP":"cplpPJmwOKpb","N1S6T30HzaGp":"v8u7KdcjPQmf","pulS52b3AvX7":"8CxcLuBR09mP","sqnR3YZjMAqP":"mXZv9JzswMCd","lejzWXdKV5Pl":"WcCKwyaSN7uO","oCjn6awvKt5r":"eKcyP7dTNhB2","qdUaaIFVOtEK":"IZ1LErF0eoi8","cobnB8gCNt13":"IHLUNKHQFxno","SoZl7Raf4iGF":"b4tIakdW9RQ6","XakW6PIPsgGB":"uGu1P6sqsdJ2","Ktvcd1egSQsi":"UDRVGrm0G9BH","qeSTy1jkhcbX":"R8wgxUIt0Ls9","P4cn6quXHsMv":"3qjbPjq1Jr51","iLDeKAmEagjS":"AG4UJiSAdd2M","rFtzT8gFDGTb":"uIOzAFUTDUaT","0EsJ7Y3N926X":"QhgCp02DtMfj","CKWgArbfFTnX":"zQZOq0DRViFi","LdXG8viFMfCK":"Ix98St3VDT1S","qVjISml9TPwC":"ib0SJNxr50QS","vkSTgAUYPCAx":"K629LZoa8TDW","SY522FxJAUWX":"GV7n4UNewTDg","uC2assihnLGD":"HOBOwCzMwOxA","CNuB4ovG3k2h":"roQ6Ea7IBFE6","hZdSTwMYrl68":"NtwzEWUyDLZg","emF0ajUuD8bX":"waPeYopsD3QT","fvXPaRMbdrMX":"RMnsYWGUenhi","tfNVW7N83oXo":"KTF56WYGPtCX","roMSSKoQL0Ty":"3imKHJHwKSw4","NuvD81lIMEaq":"Brshg31grojl","sQLGPf3Ury8O":"Tqjw1CuGJLcX","bEk7pxXHJzGU":"mekEtE8urOY2","mGjcJvbsHZKL":"EGULyjyiGMQV","9RUjKdNaL0g4":"3TXPdpcJGcUd","ELYJgIzkSdFg":"i3hNFhjgCC1m","mEH58gM1MlR2":"C548BfnRxhLs","N9n8FVpbkesv":"HdYjKzwV4cbi","KL9TFdfMUbSP":"0Z7EcW3Rdt4m","p7xAmG20xK2o":"3zfHlpOd7dpt","GQLLMVYM7GUT":"VSqSqIfNWhfl","v3ixumOTzDWX":"olyRa6rAabWf","SsOACHbGqrdt":"9rUkRJGVSgFI","OjTYBWmdvzdy":"w6ZucVI0VMu1","DRQewH5Uw6fA":"TU22JYfgQ55u","3fKZGc0mAgXf":"oLIUJrEbupkF","XTKRXoTfnTgF":"4Ut4Yv6e9Sx2","1Z0nmhfOU0ts":"v7j94uowu6Lw","guiPSUPemwNd":"9FgzaSckykDl","boDXJBNbdtzR":"hEOnBwSY0cHH","c2ExwdpTZ5NA":"XynA0D3aFSPh","a5FJLiaplGG0":"wrlsJLnmzdp9","xZKQEMxyuYHy":"ibV5Em2iV0yF","HwqNczf00bAx":"ZCdRQPAFc9zZ","h6UbBTkDlbtP":"H05CRYAtO030","76c2hqLhzeFq":"AaEkjRcDuzjX","aPxSKtWkeEWh":"4YfE9WUK6amR","qYtmfAcvedzj":"MWDLd8DH39ek","A84obP0S1HT4":"6WonwjI4cCR5","gFuxKd0NABgD":"Dc8wtfOMYw3r","zWDEW9qPSnmr":"o9x5ceg83yye","pKTv2YbriYEl":"t39IUxWT5NMS","2MnHNq4yhFqN":"UM15HetTF3XZ","HMYB1Kd7gQTF":"GdWO3qhi7q6x","KhRwWho8an0U":"VIRJol7a29ny","C3oDrEKeNQd1":"q5XBkrZCkcfe","AtP4g47As6oF":"Ka03ZXg5fZG2","hvIzw5vEjb6I":"vU5Qzf69Flsa","1krNntaSJ0Gj":"cTUuDKo46XzP","Y6rjMgoh7pB9":"G0cHLq3QuzBB","MRvPtrErzYzx":"M32mgcpvaHg1","X2Q7YBDxP3Vt":"jOpacExDMLHA","QgvLJlhTuI1F":"4UW6aWgUeJ76","B92M0DQtN0QI":"NyWaexHxWY7b","DbLfqvcepBCp":"TjAd1T2qntN2","OW2MB3OGOCZW":"iRg0tfhHQs2F","AIJ7ljxWeCOC":"yJs5H80bsvoz","5NyP91njXlma":"QMkh7r0wUvxu","9IZyDZCoatxg":"iFtlw4RHu8pb","sNcyoU57xQUE":"ZJNWrmlH1w1C","9fHIxPsNJAHf":"w3ntJBxCnGMh","jgie8RRU7NDO":"ek4hzmK3BQ8E","FQg8DboG0D67":"JQfjop54QIn4","I4NvoUKIIMAx":"vpA2G0UOhhPQ","Sd2w2wOdCFYj":"mgK2GkqWCu3b","024smPKKDUTx":"hrAY2oYB7AgC","aBQsXYCePyC1":"5SkDRzaNrYY9","Oos24SPTi57F":"hCKMdDz5pgKU","6rUOUepr6Om8":"1C14ARPWu8qE","6XAUOpgnCP9F":"CX6PdWS4vL85","WPdjexthjOxx":"gWiaeSksARiV","PrjIHkvFWMm3":"3nmdfmUn0DBV","f2zddTkxwgA5":"OYnhbnD4Ntu1","2RpdHLzRaDco":"blWP38t0CWGz","NQJAZzzzw5E4":"bOnEj6VeErAb","7QQpTvurGvQz":"w5y9jWlUZfXq","RrVD62yp909F":"qF2ABP8eVFLJ","UR4KEfLLstWX":"XGyKUAzkVKYB","vlSHEU3dFFJR":"65otrTwWhx84","fmibpxLiwJXm":"kQqpFTE6q5Rk","WZsXAfdIOh33":"GGZs0vzixSKW","6xsD6M857vDE":"ZAteBK54Dw9y","kza5YKkorhEp":"PY50zyesGrTg","MwjnGnhWkHzT":"e5k8Gvn0D1aC","1Nr64vYdnS2o":"lAEtOkvYdBoE","nRCqrOE7aQUZ":"kzqGwlhy5nSh","2GIC4L8Ys5Yw":"RbAcv2VZilPH","pBXavqY6TkBD":"7GPGFnssJYzY","QV6lJderOZgi":"ce5NDu8gY8Jn","ci9oITzvvyKr":"zB2R0stdOVn4","25N4WAUjjzDu":"PksMjYxNf29u","CWElj6B1Y6Ob":"bwvMjlhB481S","i47vcnttmzQI":"sghNhzzYYGMq","o5GZ2C9K5FwB":"YnzHcuWVTDWS","qfMYcZfwaM1i":"KxyoVK4UTVnm","gZboBTtnDanJ":"2Sx4d7HlqYuD","tKG1FEo5MoQd":"s5yX9Jks7d3t","zhfwOFdUF4jz":"xiVEZNDxkqaW","JMAhpNXRpi4M":"2maTorJNi6CE","8PKh19nozR5X":"ktVHTFPUSNo7","sKtglWZAlAvE":"PjShk8D2IDtp","WVLzrUXv5XVQ":"sV90oPD1sc7Q","eJ95wB7NRJqL":"CP8C8Jw9J013","ac1IGc1atFnN":"m51r6rMjsdY9","Hzb9c4iT05kq":"GHRHJGhAcWIu","rMli2ZhdZRGI":"kXKEyWt0Z4NR","rQ31dE92Bumu":"SjcJdq5XDgxF","EeJ90cDSD81J":"AzFMdxaSUMzS","ih13SZCM78vm":"ZdT3sva13yHt","rjZCC5DNnesf":"EYXDHxfet8Tt","0WmOBA5iwo8A":"ojh778vDGxAJ","xJNbYqXgy0zT":"4fCwnkVHvoq6","JtHqODEqEUfi":"eIiqp4y4IEQS","xUYDdxeH7Xtx":"jWhdoKswIwIF","0OFC4SqWBzBK":"rQxtJgtR2s90","8RVtXWETe32z":"oNL6eTNShJ0V","mz2gHPJQXE4c":"lDbZZ5Vdm5UI","xMktitvp988y":"5HRiUGhp7Dac","dfa9Bc3JZiLm":"IUPMdgqaihfu","T1r8f6hnNvuT":"H4WvIOUEc6MG","Udf0BpmfwddF":"iEsWSfxR5RVE","YhammafA152M":"wF5pqMmkqDuO","kdrkI56UXRdn":"kzmzssv0xDlo","skKA9EzPtSgI":"rofU9IDHDQFa","PCiOr8kqzMC0":"CdO8ScDqkhqZ","r2sQYvkr7E4h":"3JCbc7bTfzOL","8CAtVsDljxhY":"0c4jbwh6CUbn","c4JeeCRQiYCz":"qJAQWomoNSsp","uAognTskuRd4":"68iRiVT1J8IB","Q7CNx6rmMupq":"Fm0yftiH3YVr","eldJYlTmbuZM":"N8yY2HOe61xu","UHHE4lD4O4HH":"xstyDv2HpvjI","upf44AFcONr5":"GeCtspY1XFMt","yffnHLNOSGvU":"chWg3QVaIQYR","WJSzwpeUhcHR":"osCuseYZ5TXh","SQVVkKNKceTn":"8lTMNd0ZmDDX","pu7Mejzd4HyG":"a01WApZggulo","425nLKJywGnz":"X5XwTJPhPjFW","B8ikkNBr00uv":"HoFcg6kAuPuq","u2nW8l1lLlLR":"KYG5NdzXaZ5v","An4Pu2EBHUgF":"yfLDyhjfhlNJ","5wumTgibsJuv":"IQTAbvTP7jlI","mwZ0WzEgTuO2":"NQTmDGQDdC8j","Z6G0WYy5mRDc":"TzVy6BdMSh14","rIFq5KJ0YloL":"O8JowqBbhAKb","ZNNBQ4Z6IS09":"Hor9aSHJsV7f","rZIEMQ1W3Nbd":"EgNd4VPIlgdo","AVCHjGoibVos":"1NrQUSK5aTrz","krS09Wzj6bOq":"NnMt1VJmwzMf","Vddz1X3Sxj5S":"V4gavcbu0i8K","ApY7xHjIO7V2":"64FgZD3kauca","FBgKY6PZieSt":"dIurBtJFfdtp","jmXHmvmQZFQS":"UillRa8Z0rZT","yVed2uH3gjts":"Sulsn5m51kxS","qMJlkqDtyR58":"D5BFJs8IbUil","wvrZwaa1r2Xt":"6bfits0YPDpQ","Fn1aO3QZ9P7x":"hiuK97fgcB6T","wTFa7NSKJJMc":"87osEnXXytbQ","ZgAvYoMIdV68":"Axg828Aq682J","OT0pXf4Cf6uk":"FsBLFL72EmaE","wDNZowH2R5vg":"D1IxQERitar8","raq8dxLAlHsA":"fNZy9vxPu9hI","OXwGPyt5p4Cs":"cmk4Kf8xoQvX","PEalPnf9YXSM":"rGroldOWzYcM","5eAl9OA6BFgr":"2rusT1nLnRuA","9Xl9NH0vmmhO":"WbRfEXUcNxlC","lCEizmUJLoxb":"KhXwEf5AtcpM","ZWigfDJeSXZO":"6LWig2uNdjAT","BxLI3ErQyF7l":"u10teuKP4ZUh","NGkLfoaRjymq":"uA0Ql3AV3seD","BjuZJpU20nEq":"dlEWeYuFeLRW","D41AaY6ZqXi9":"buK0cYYX5xkj","QIizXOZ4SOu4":"xG3jeOaiLU0M","vk9WXvs8ZBpF":"dxITWx71Ozeo","lfDP5ifG0RXc":"ND3fAqLD3XJz","8JcIAhckmsG6":"fc1lsQSPqcf0","Od0oS491gtz8":"GgbH5U6c80zd","7pIQEJg0VZWA":"fyUlO0RouKlL","kE81bukqjJF1":"N5Nl4dKfth0T","hhWxrLAEkqbY":"xCMtqqLVwMXU","vkaZjV2yDfbq":"mEDj3WClj1KQ","JiiHqwAJPRyX":"aJMaVJRkjM0v","g49H7QIvgaeH":"0STOQPYzOVp4","jDRW6fusl5zT":"hcw4AVEkNqxs","h9ZWkHOiwJgj":"0uceAzkr3hJ6","2PZwciyA2mJ9":"W5Eyip2hrmMm","j1sePP8uCyYX":"GgJMd6wfWJjI","YtSdp2wcLFLN":"3sjTsUKG8Sno","gs3vyMKZWyEf":"TYiVDaZoS2Ir","TJw3m4pN6T2k":"oOOE7dibrgNK","KWh8X3gZ9JMk":"R1GRhFQSPLTx","hah4L84R4Ymy":"9LJX7whYkfT7","eFbsz5VtM5hn":"Mhw3vvJf8tfQ","XfxgGvIXajWY":"zpiFk85AErQB","bGNxX2v4jQgA":"dzb1hFXpEhXl","htlIRMhV9JfI":"TOPxGeJ7m5v3","JzPHS8rmyzZA":"GDP3IvF19Ezt","4oGD1Lwd36nd":"HlresIAXrqe8","21gTkpDPTjwY":"QEHztsPxXv7V","Vb5L7UeXJQBD":"36dC3HP5fpXN","x1KHGGJThnee":"Qb9ukt5k3DS0","rlLHPY1EncJj":"w49lE7DKHNaj","ub6WrDzrxpQw":"jFSdOC7iSK7C","czj2L4a5wLxX":"rOGJs6iQ1atD","rWMMM5hCbBss":"ZwvVTCQeeRkd","LB5wFMHtmFDU":"MmjGAk3tZ0qG","3fVN6flmxtqb":"IWKXb6g6cDUT","aaOwqD9EOVnc":"nvKiwgp93G16","TyFGqSTkFUCG":"j0vMPmlOgd2L","y96KoxI6rwIu":"m79GJnzYgU62","aqTx0gdDiDoe":"JQ0I6u3i6EHH","4dVvsq5Y0AjH":"BBfDOFrD9oFU","OD2E5jlExWFF":"qPyRkn6NmzX5","fea3QbYchzRi":"IUYvzcsF5F5F","Ec5hnvCjAxo5":"wBg4OcwTICzO","LuMNkFG7W2B0":"wNqsIsiQ6Vwu","7Ob8fWpzGs4o":"z0BjrVu6Ja3L","PkEbv4DocYvu":"DKUWKROpzxaF","213NloyvSOy9":"jghTamucbufB","mYfofQkUlGgJ":"SKMVpXwnSXjk","8p7qwcQYdWm7":"J537Eguk2U3b","0awl9C2CJCVr":"2GrC5lPTabyJ","8sDjAF8muY1c":"61JPnJKFnI8d","8wikxgOiH726":"2984F1UkDCnF","GVGEfmG2AAhW":"rc9zKo93BTIA","1jgaioT5rpqg":"nM4luZeeAKrx","5M07gRgYTXu3":"X6KYr1amKvQt","gobQspMVpZ9u":"YVU8pmUiK7RJ","ypTmi4Ew4WIr":"jtHW8pYj5dHJ","a331ys8qRl7k":"UbbKJtqghMFT","JwmniAgc7UC0":"COV69ET7ANDn","xLwgq75T3OMJ":"f5M023SOz081","WXRdcNcyTRh8":"HjUAtnqofFOG","ezwDlpnDutyB":"vYrscRi4aAWs","A57dH4ghKJN6":"hyQRbVkaUWtJ","HDsoSSxQMw9P":"L0rJMcl06LDx","7yd0J5vzCPea":"lHfIzXO00K8z","ZNW60sz7ubet":"osrvd1h14MNN","bRAINGCQSHVP":"Kl6LssSOw4Ok","jkGe7YHDhUqA":"Yce4FOJepVOn","RPcFcD54wNXe":"27vXI876rIFi","vWrS2jhYV7aL":"jSKLYyoWscxs","ABF2m490yW42":"QcthZmWWgtzG","VXtt9Jh6GqeP":"PINjCOy59Cj0","q2Czbcm8Yacs":"9D7AvR5u3peh","JwSZPBjGsDLX":"I22upJP69aj7","qRvZNU0nUZwS":"0MEqdknZVbeI","19FBPdEyZk8i":"bZMAvH0Y5wBY","2O8G1KKJML1h":"fC2IJCcJv0LB","pjUrDMxPrOiu":"gzXOl0R092rA","cYU6c59qmfcs":"uUA23EVsBoiU","noIdmXqOXcux":"rE5Ue4BrksbU","nfkWgDS07Ykl":"5nPbEBsb2wof","irU5ddTRjbjv":"LyBh5bO5QOab","g6921aZWaIxZ":"sc5lstKLdBOt","Aa5jL8gqciKx":"lfvlAyDVN78a","wiNtyDHzDqbP":"fmIKAiJc4jBs","jZEIqHKvlu4R":"d67RvNdCVZL9","Ca0QINKg5Dv6":"Szquk6vIyxEM","Ye3SAhadshDN":"vl833FXfP52j","9VsnMn7IDL9e":"Jzep4tdU14tG","hbJk5cMvmf5v":"IX3JrsurgGs0","hWhpkjqERq76":"hvgKFgosLi5n","751juhEWUckI":"YjDI6KB1j3Sn","N9UkbXtvmfuY":"BwReLJZD5IY6","z06JEByKHAuK":"OrYIGeOVMnEH","15jCzhOkKRm4":"7zyaTFOny7Zt","SuN8yPSPKgyd":"Nn4CyllInnB1","lwHZaEeWM8ue":"0iGQ5biGxYG0","eTuiJ9xVZM5K":"X3YYyQIQgTUJ","mvoRzR6aS8pQ":"pp8T8n55KyYr","WnuP2GQdnnbM":"90f5ipWgbvSX","VwsAZVDfuEg4":"XXSpepKnSeoN","aiuATpwSG1S3":"3wZUcy78DIJB","rsYRn99CD98W":"4TnKdqETTexY","Ge1bEYKhVpIN":"R1026BGnsByG","0WIO4qSveVqj":"2ZCkwYZoBI5L","eFxvcLjahGi0":"glmKT5OjzPw0","ALaT0jUJNewW":"twea4DFwLraI","FvcNzDiGHs25":"jVqvNzQJ2GXe","GgGbi48x7gY5":"Pcv0r97iWFmZ","akV1NWmDnnRw":"wCvmGRNCK2Fo","IVbh7ZqS93JE":"8s9bizhIWXLp","cQGDTmMgBfQN":"o4oAzLEfVa1E","oYZkHED7moWh":"erA5kkUmV92s","OQVnUsL2aQNi":"FC73xOXdYWXT","BYt5nmQga8Uo":"WrEgAtaf0i34","4taTlErAhc47":"QKFnTQ6JDhKv","WJNSMfDcNXw7":"FVJod4YA6MT3","wS36rB4XCen5":"ztKlnvAKfQDK","rttrIIiuqFVk":"EEaw9RdOh8SE","SNFufRPkuLej":"XfLRM4m6Nk5m","vFNVraApiAsh":"B0wRzMX8UOKg","DrAfC67pzU5p":"s9YJzGI67N2q","KyMgU5HOT6jR":"sRhVb7LtVrPt","tECWZwRfX55D":"kNXXpNqpZGAK","kh2vDXu0vg31":"LPLCV9KkWxNf","okTZ7WtJnYGb":"5Lfm6j7Bcuhq","1acHyBhrp1Hi":"q3ReTtyrNaHC","Ax0kPwOVIrwp":"E3ZWu1ul5tTS","kImJ0nTbqc2Z":"amiAlwpGJd1e","4N8XuyGHBTJ5":"kdEKH1aAYn6I","giP2Ci6kLnDk":"hvA0wjaIJSj0","RijCf7swMuv5":"bE4lC7Wbnp9C","0acvTT1lZ8cY":"1u7Wrilgnez7","NSMTm6ybTd9R":"l10F4X7ul8Ev","n4VB9pslbwzD":"CkCgBaK4NSIm","uv1jlhNgi52I":"4qlBTiN2yoCX","CXsnxozKsrZ7":"P3MvavXHtVEO","5lOPNFOuEHBx":"jhDt5tlImtil","cnEKC38ArF7W":"c1cWRynbz3GX","Lw1s5B1derY3":"LEf6lMEwCWbb","eHMResfNFtIJ":"JXGpssMH9PFX","sWRCM4ipgrEm":"pqWJZz6O40TJ","4cHgie8q6hOx":"qg0ved961hiy","m3ld87DIfXO2":"rYwruijlz576","Gbq8tsxWGLde":"omQj4cpqjd5j","IXbbZT75C25Y":"pyltdzpeYsmi","T1Nq2BLWgoPT":"hsujtSW3HBy8","baHDVyvUC8RH":"upKbtc7c7EDh","CEAdzlXOGfwv":"o2CSAKOHpvt6","Nc9ox9Rr7XnZ":"u6ksGtx8HNtM","d77fyEd19dev":"fs5XhulTri5Y","5Qi7ukGMV1SM":"SmU8JatmY0R8","zx4YjaLJHxW0":"I2DrZdv6gz4o","hELA4jtNhe0h":"Lh5NqCOsjAjF","SQTsF3VI2VDN":"Tj06rLf7Rqrm","KYhPtir3CfsF":"RZvzBRzXMx3Y","qYDm8Bh0bP4l":"6EgcrIsm2IMg","qflsasEAb4LK":"jZxSwg697qnS","ib8zkwRkeF90":"aL4nv09zsTOl","Id7sH0SrFWHu":"WkMqn5kHD1Xi","v2xVHENQx1GH":"3w4Mozy8amnO","Mzw1LkAkckYc":"gDxFbZrV5nsu","kBQKmOdDcjgh":"54HeH5hNa3Nz","1wRI9gfBmujy":"ZA9DzkFnpXtA","uIfuaPSGUofO":"yLH37UozEGmi","zIZFtfb4duhs":"Btetr7apeg0Q","oF8icIK6afhG":"3NAQu4yUJpbt","5N315aYeUeBK":"ekVzNF3S4zN1","McZSxDQPLAqQ":"M4TUZ3IneHpA","iwtHX5IZUCff":"rkzzRX3TuOjj","VU724G9L2CLJ":"vCnD4aTLuJCy","IRfW4wBSn810":"z33S2He0YkG9","bnKaV42FSnhH":"fIL1LqxQRgpI","uewkUdgLvJIC":"URs1rvRBLNX8","2KMlffWauWax":"IuPwr6ZHp5qC","oV8BupNgHv6J":"PX5juAnpwfve","MbTVuxpoVRe6":"8uYNFuHDfrwe","17u7NpDS5of0":"0Vhepp12PthQ","Oj83lhaGX3Mg":"arf4cDqX46Mv","SQ0NIyk35ppS":"vVHW48yTYxAu","vkpd0C4UPs93":"PDYX8e4E83qH","tLQY0SP0TGcB":"SUFIzOjkgk7G","a4KwBveHMSoD":"voXTlhBQfy4o","hhxciNgvLOoV":"F2n69RjmvLod","0d5B9hzOPN9B":"cF8bqG0e6n37","Os98cncfICgz":"yZ3GHKokN5aE","xNI511S1IN7o":"OVyuFErfP7r4","Uhd3BvlOyslv":"EEP0ltV67fN9","pvxPxtSpaRZj":"5KS7YaZKHXGu","Jywap3otOwFm":"dOTq7FUtpsMf","uVFyJTcjgiqi":"ms72raWkMQ17","yzy7Q6i5p4HC":"hJ9zghXE8QvI","Lqf1YHGrqBlO":"DqMr0i8Udyae","d3bBaUHeKMcT":"8WXkS6SRDZjs","A6d0TOvhfOrM":"wC8w3qcPutMR","lj5JbnnGhSeG":"wD46ipXUaoky","FLJpZ4K4r5a3":"nyl6BZbWLcIP","EcpmaWSdDDVX":"8VHxUKsyuzlm","yRuwNLMngnNf":"bQVhyWej8ymF","y9HnRm8bLvlb":"GOElSA95XBu9","6p6RuBJzDTvI":"VKqmBpqZQNZ3","DlFGetLFI6Lh":"wGpZ0x0AMytY","qdAo9EVFL0wR":"ZtsjOCqjKXFn","t5gd29tuqQkp":"QwTOhiRbgBrz","p3v9ZsoRJjSd":"9RGOesLwtEGY","0a4KTYqKfCX8":"TxCPT7LNfF57","Nwk4JPPY0mN0":"Rd7S1nurg3kA","4XfQTigf7q5x":"6pBvz7zYaepM","9HrA4rnkM2rY":"S6Kxa6gINGvv","E4JC7ykwQm03":"XAdzYMCB1cXS","vZCaOKoC6JWX":"cglUPoT3cjkJ","Cg4LoZFiC0zg":"JJC65m9YTquS","V6FPNDBtthYl":"nY5odhIogQ6n","rvHg4zI75Qik":"8gkL2pzIfYUE","IrYzmCuuS05S":"Ju5goOGjSTID","ejC0Bi72GdgI":"fsFToUHc5lT9","ZHlgh1E9CT0t":"01FpE3wnTSXP","Kl38pKCvI5us":"lwkqDUQE37At","smvK6Qf7TVNM":"OGmobb5NIbxY","Slcs80HAgu8Q":"JVMkazeyJTjD","KiRK2ubLdzgs":"XGtu7fB9lRsx","dowxUwyNHCje":"NkzWACVkvD4a","f91f5IPcs9lr":"28Rfx5ZcEh5T","vyZnyl3rOq7x":"0bRPAZZTwI6q","5ECFZ81thFGG":"m1i26O0CRMMT","S5d32JJBpnlD":"kjYJ4T8l1dia","CJCciQ4Aa8IP":"rriXpsC3iYD5","fGxBH9ZnBQ5C":"ycSJ3iPozZJh","JgcgfYNCRYgT":"xRwpFGje0HuT","RlrTSTJlNtN4":"LzgKKWoGMqe8","8mWbqSqnDBj9":"EiNXxxsgt997","nw5vFP2ZHEjg":"GMPJfxclcZ11","bz0ksqwLH1T6":"SJmD0IQpOKcp","ekQmPcAtCM7K":"agXmwCLXo8GT","K4sQWuGn1qbf":"odSPRyOq7K76","kSG1BYoOGwan":"otL46WMzunMg","wthkznglZpkf":"KymFSO9PZFNA","3cFscuNK4cyl":"qffGXdPzx8YK","lx5nIZ7CiCcG":"fThcgQrYNEIi","QUlWduDCcjfx":"mg8WvJby26Wm","bb5tgpT4YOeW":"ECtFuIpccLAL","baWV0bxjp5Ce":"0QxVHSUWBCce","D49u9A6LITV5":"r3x9jnJ5QOjN","sHLS5FSfEoXq":"cXpQ8dw53sas","a2LxGRunEIs6":"euWVlaS4tEPo","rFSAvanGEU3y":"AR78LKeyKade","u55uHVY02xWr":"99o2YStR1eQ0","DTdXD06nwoKs":"NvEd4hXSzval","8JcTwmna18hQ":"JZZOTxfnSKDG","nOmYtpHnNYko":"g6QU1cwmzI7j","D8uX28FKVXs3":"XZctiYNJBLRQ","7TAC6SSmJqTo":"RqdKiJdExMIA","tojurxbNfzT1":"CmT5nUlzfkUF","H2llXSQVDqNa":"ZoWW9ZbWGe5G","kW7NpWulpldy":"NRHJNggkePQ7","N37h2BlDkYAR":"9qeWz93wXTlR","pgK1Cjp2AhB8":"ksVSFccx1Ora","eMTncudPmdss":"MGyNDcyS0D9M","2SHwj79UEwT4":"uqZwIyvd0jwS","lNqevtsI441k":"1iFGVX2fxDMJ","6OgFKOsbJeYz":"24z48O0p54oc","ApmZGk3CP8oD":"8XTAdQCkpmRu","iMOQ9s8tT26T":"KoZEcpA7dHtH","Jle8u02xEJbW":"qieRb1a6Tsh8","v8I1OtJ9Z21v":"NKLzZcfitRgd","6UcSdNEPqIMJ":"LkmQmdvvBpmE","Tgd741Rc8rXX":"mcfcKxGen0tK","64y5jQwEu5Nv":"cWomN20D7FCt","ue7ScAR4mFM2":"mep1TIg3zlOu","flsmUFXZt5t6":"IHJaN3cgjyT2","uKPwP92YUiEK":"i8QyK9vJL5uh","I9TDtHxBvuDY":"u4C4DErHdL4x","NVYitJXtGhtd":"vGAAJI6pfIvx","9rv2sTsikMeV":"EjDenUbrFZJh","6LvFiYoAVOJA":"xnZLGH1Mhyeu","mrd9GlOzrZDZ":"c0fHs7lFQNF0","B1t6U2Phu3BJ":"QEzQuCRdITk1","btnX79uGuHdH":"nz9IP1nZmGh7","ZFcsImaOyXN7":"1PwWLGSxq2zh","dsTBdKymfWCw":"fAbNlfW7GMTp","0pZozUGGcu77":"GNqoTZfpGROM","tBOqWYcQAYGr":"03uTNJ0ZpVe1","KsNyjeZGHB4W":"QEhm0mO4awNH","ohUA90X6brc7":"z1VtPF0n3W8l","ySpjC0DAotn3":"8klbcwO7cPZ0","zNOjNRrvbKFp":"e2bWrGg8W9WI","ZfH0tYFWogmz":"RgoJ1WeB4lnq","kFMVeH0JoebE":"2w197R2blIiy","YLhnTOPucaOj":"OdMv1Qa2Z3kv","j4uwCo0SbkVP":"J2zdgPack9Ls","XmBojTJZPw4W":"JbIR7vkypXDX","GJLhnrpuPktG":"FTM8sofQD3iC","1Z0CVbfVeaMn":"m14Ch4Hh7HSQ","EzZJufR1h6ZH":"XO4C1kSOfF2W","8pFGNOUjivyJ":"6n35GSnDeM1B","5MUUzRV3jqKu":"JnFmfS0XL410","ky4Bt5KhcKRg":"SajpTT2ahEBN","GgYCHNcSsL1d":"TBmQz02tEGFa","CFtXtmLqCGzh":"plws9PJaeyfZ","vXvK0nvJQfR3":"CjVnLfy6eGc7","3vjjG7GotVd3":"MKZAntvLoH3a","4prbVAVOvaLn":"LAS0P5LBo59K","3X3MtKirRsvN":"zaJsyA1zWcOM","heb3Kzagqs49":"sGlXNNWFdjXU","ADvdjfVcXbtg":"KIrIidrVXUGV","vYUFBeFX11VJ":"Ymm0o9DYLNER","3vEkgx2BYxRF":"fzLtWmRxuf9j","ifQr45WwyR6u":"5YlTik0dKwrM","fGEqt2pCoxGE":"eC2mJsKjFsjt","lPVFwwOQxgay":"a2HUpF5Mko5q","jdayNH7SwHU1":"btEK4qIvauz1","3DAD0olvKKiE":"PnRf1RCLa1KW","NNy0VtikL5Og":"Fw4Z1z6qX0aC","SRMetCTuIwFz":"mfUP72HMZis2","gICKaTVV9onR":"ovrASbQCHtiM","ptWrdgRYp9lK":"JuZGoFayEJe2","B2RqN6fxbX6w":"CVXbKuY5QPKX","uQft4DkJMaDw":"EcjTMaw0mvsz","dRBJVlgds2Bj":"jTemuNElrwbd","tgXRlPf7jmW0":"BhOqG5e7f4Gt","mN3QHqWnLsdu":"ksPpRNYK4v5j","fy2XQrGHUOjh":"x1FXYJJbG01N","OH7GumYrq9q6":"EFSIPaMGH57E","afQcvYBjuZgq":"UNyPwrj4yYLg","IulLRF1gbJDk":"BHA31d3NmDnH","Jna62dpYuNJ4":"UE8x082O5UVL","wOe5oxU7aduX":"HflraoXgLaIN","bNvMCDFqThDl":"HWk0eqXJj4WN","IAvZMoHudciC":"3Ob5aM0vnbrq","TS63mZo0gIh5":"hHT3cQrzCQpz","H855X7l9k5gB":"dzgezJQioWCv","y30nZTrgzjVd":"Vc9bkmgystW3","HMhWt71niKnd":"VAsvyF505NI6","vyalCqtNOxlH":"DIAHHUTGpMrE","ya5cZptOAq91":"zvz3NmLyCnk2","XQCsaoFwlgw1":"9X4wPqVgqTYo","ABVl0NM1dQt0":"E0bbVlLdmRGm","j8EXWI0tz6vq":"fupJqfuslsra","GcYOE2ZX4rzm":"gVLlK35GLSDX","Zwl3JoeUhX20":"eSLl9IUTLVPc","7jGWpjFhZBeD":"xs9LwUtQ5Bif","3PrTKNTsAiQw":"gzDyFnvy6j09","llHzgGl02NS6":"QnY8RZkJCBfT","b30xzp6ZDWj1":"ZsBsCHdFEHV5","RNS7uIvehSt1":"NTS2ynFR6zHN","bSZtTRLaBh1n":"dhIsKT0i6YWu","GJdL5kSEZml4":"kyucEgVyIdWl","5DOOnCOwMgmX":"Y1as9dsb65sb","barqNTPQfXpT":"YxyMrfTyGiqS","R7KNiIlrIkUc":"jEUiXIscJsUg","2emqrBR31oFA":"0FxlZVpODOOz","ztrsudYbshOP":"5usGf3XsLcj3","RykFZCC8YS2B":"zUOpEs0Fi94L","W6Zs8cdBSZ1Z":"waegCuxYxUuU","kZcqh7nuX0cf":"AKLV9RR7QI52","lAMYBL6ZH6hf":"irDBO4T9986Y","RrgctaXFdUue":"CMEg3qBJAg3m","kfmJuQgOPPmp":"1KKUBXdtu137","0yoTXyfSEbvx":"tpZhyut0uJzc","GrJATDmVGSjf":"jPAk1RyCwehg","MgEDXfzhO2r8":"zSkSg3b7lvvY","tVpa7sHKGYD4":"zPy7ihPLqcrd","FBgQHDbEU4cH":"1EOG4LtGgRsq","bjneuE0b7YnZ":"QADnsGXFkhbE","wiwzMWakhuuN":"VRXuHj01bHZ6","OiKowbFBNp71":"5pljTWWPqmjZ","Fkf5EbQrfl9Y":"X8tH0h7sKlmX","XzDF0mI4vJLu":"QzhtpUUdecYt","sPg56GThCSu2":"yzVKHgh44gD0","rbPizgvGARCV":"TJwwqX4mLVZi","URnjSYvH3nNl":"s3Nr5U0oFrg5","hmpxaygiX49H":"0s7n66rvagXa","C0072icOkhd2":"SO11Voxzrosy","eUC1tTjfYHaO":"9JXjYB0244GQ","MtvHF7A2WLHf":"RKH6y7gJX3R8","4CNonK8dB7LU":"wJXucOdgcZXk","k99SQjLsEbv7":"FoFsFCiszI73","GUQcmcv9Aknd":"nGVJExC1BXWc","64GPUvKcbyGX":"foCsqH0kvbZJ","SnfU29CPBrtb":"DVVXwgbtQVz8","WuBhPfT1167t":"Cuio5dIwI3Kl","sj0zojJTLbvd":"HcWzMqTvp4Jl","U8BmJTHEh5te":"zdJ4VltdnWQL","4urNcRRJmq86":"YbjmTgMrzEAh","6jdms358bJsv":"pjrpnAPHaC2J","ppTRZE8uObIO":"pZRdKEIYYvGw","2NlLNBmt3Ipx":"Rq3PZRPfUhc4","0RNDnyhJGpTL":"vcQ35qyoAALG","7NLDAJ5sJfSV":"EOa5arMSmCGi","BUKGzFbA3RcO":"uNHvQbJVrt5G","jt17agS3dfek":"VzphVxNvTf7Z","wBMqLTQ5ybiG":"QVJeuzHSdKLR","EO0DPL7gz1Zh":"8JMWdDpz1QhD","O7i7KXMFjIqb":"sOZVen1VfuI7","EBSwDa6xsIka":"6MFxlnpg1DGN","IhdEz9LtNMtj":"3orVCLcvg9H8","eEjpb8EQmZIz":"9O74fXNUrnE5","4AX5LgNiSxTL":"xXsewvj2aCCy","B94BWy3ZdQ1d":"5NDHPXTHvDK9","8pO07QeG6Ylq":"XaI3N0deZxGL","LnxO6NF4XflY":"4FlwYoxgmn1v","qscHedn9AOJR":"3CeOexfU2I6h","Z5IJzlcmp9RF":"zykJ92fEJtmv","30DfRqmmxFF2":"3rN80Cf0DlPO","X4cogp9a2UXz":"31LDnnSN4OTu","s0Pw4Od66knI":"ZR0e5iObIhVs","6HBSdbrwQHe3":"aIaExJYs0a7Y","AuKUGi2lZuy2":"nb5GCQQp3nSS","NGCH3TuWcxQh":"SybomtbMerP3","wHN41zxIa5BU":"RShX0qBEtNPJ","wfPCpViW6IRm":"dSNa3hlm4KAP","wxoapUD7qurk":"HO4YXdQ8FUEA","SGFSZN9YEBqz":"HOc1CKJSkRBy","luFcj5dYFeVi":"ohHibhejgVqO","sYvkp8dXMJRY":"KOKmQ4lQfO0Y","IXRtwlv2WPTq":"9v3rjMZO871O","4DP0hDFPVVNn":"VMKuRiLJybLW","dUJw0pkfWBTK":"oDE9YitAkhml","gv69Eli1jSmS":"U2dz24vOQq8A","aN59YNG6Ikga":"wBjQIq6OqKri","YCjpJ3DeFeux":"6hBPMS1JWKdW","Jt7jpAsWRWyB":"rzOYokBXfhWS","nJEvANHdpSGf":"sCSP5lmHkPYq","Lw7QIh2aV67C":"KSYVKyWMhFUG","lQfk9mEw2n0E":"uaWLJWMXVqgr","HM5NAfKZbsiI":"imxYMsnadAsp","8xUkInQUKqsl":"af5At2B47KHx","mHN5QAzNVy6G":"bXAAeD7CJnoC","s9xWe6MynwZT":"nxq6d4hdmxex","9Lws4GBtyMyc":"MulezGLcUZUL","4GZxHascREOt":"qTCsyPsIWLyJ","nUxjhosiOtJo":"KSbQQa8JA0l4","R8qikh4I5AJi":"gIdQmVjXWISp","8W3vRqdK7azQ":"O1zwGsKBe3Bb","6wAQ0MKlB4pt":"9zNLQUpnPObs","AxEOuWaZa7aJ":"u888ZAG7USjQ","2GpScMAdAMA9":"QXMvJa7F5TdQ","S7dSbp2IKVJr":"VrmL8yoJmtMy","slP8nj6DEaT3":"1vpjHRfrClc8","VKkuBXjQCTGs":"MaQD3haRn4f6","5y5od9UekylI":"HdyxuETNvMmW","yBqF0kEuukdP":"GDrMSvW6DLgb","GAX8BekDjCNc":"f3rb38FSa2th","Ol1bcYB4tDqI":"eUdVvnvNLiii","v2GXaV8xKQbp":"22TzFlhun9i9","OFEV8Y0yxsJe":"tZrKaOWVJb4v","bfkWbATVOT6l":"WBuBUkURt2H0","k5CasKa89uuS":"hmjvfRY5uiVC","EoEQR1LN5Zot":"ym1nYo9mQJ7P","V6cnIHxqi3ZH":"iQWJJT0BM0Y0","ZSRnwylS0Qx8":"ksjuPktPxgIf","szsbQeZj5ExB":"bMfiyhJmg9TC","xM0EuRrAon3V":"iK9ZzAlht3Ix","0FmP2kjq6vvG":"7mwVejSEI29x","p9SYnNoMsIX3":"80QhL3fECNUX","oVvPBRhgDIfG":"9cs4UPOqo3zb","o3BTkbIeewas":"gLhvL9SqbtaD","i35zRH9SowHa":"xzyJa7XoSUQs","A9DwqkQKKNn1":"44JVRl4lm4CY","HJoU4ocdI5v6":"Li5mrkyRCcuf","EV2CbV215BtM":"Q5muebHBhibf","XIrb8mz4gNGh":"ve0YehUuMUUc","3Q9544damSFN":"2tvWP52DX0Lu","WSlu1QEmuoyA":"al8sSZOdfrb1","Ol4EhnXnIYM3":"Qto0I9CqG6ed","D7sIMC8NPXBE":"VJg8keKdAVg2","z35WRzpqQY2h":"UZHyieuS7i6b","V4nu4S3yd9Q9":"nPKpSQ9yTQU4","yOGUr0nDEfeo":"limkQXx8wedh","vQQVVFslUn92":"B4Q7BEFaCcKr","K79NoErWUouc":"S7nHM4zUwLs8","GUhkJsFT80Mu":"2LWSWJz2tkpk","gBWXpPB76Yhf":"1YdONg14pedS","wnzpfDZ1NWXB":"pRWetDy1IQpw","lumAICvSixvM":"fzaXVOU8GR8U","kQ95pKBV4Iva":"TfSqI8JmOChF","psXluiWQvs3n":"MhDwXEy6nbMn","H3hcDtTxvxGI":"dCXsEuVwiMPh","aSXQ0ZzL7lyx":"adF14p8Or6B5","C5FNnGubrSVW":"0ZWajKPF7Cmp","CIMVr4iZLTbl":"6sil8viG1a8x","JT80hcN2jboV":"X12eVH9cvcDc","niWDM5ozgIOV":"j7Tw2yfRZjwz","dOB4srlxN0fg":"1iwMoDmOvP6M","3wtxxDBLn3Ek":"KjAEfbpm5AC7","yDooA7IwnUCQ":"qmQmfG3xULuy","yLO1CFYTUqjF":"LlDRnrb4JjGa","ypEyPgb5WNdS":"NEKBIRQDR2i3","MivsOyzFvWV2":"u7dkRBWw2Msp","yAPZA4kzDXOJ":"qAeqk2INvDVp","UBBKGV1zILTN":"mDvyVswZ7Qcs","Zx7e0vfSPdgD":"qzmV3bBB5u3p","xstGrt7t68dJ":"xkIQtJOdgIXY","iIl2nl913kK7":"ewYFFLhf59aJ","BanoLtTor7Ch":"tFO3gOBH3Xb3","38YzbWKhEIUu":"0c8rBgsWPtCz","ZzIHFy9rql4Z":"EuzuU5bnPJoB","sav8VFwEwqGF":"vEL1EopZAg0z","H5iPuwLxi35z":"K44QwsDnCbpv","Q7HYZ00K7QkK":"oIE1Uka4g8NT","GqNbL8BE9c8c":"DFJjtIktWLt8","TpEqTjeq3d6f":"JE2OgaDtEZJi","MCFi8tbfpbAw":"PiXNwMdNyNC5","SHqt5ASHM6za":"4X4p1H4UPSIV","gkFvmgPhvA0P":"ubL3RxWmgKdE","XGSKKHp8Dq3c":"oDCnW8ZJLiYf","7BFOoR9iruMN":"AdiwcJdqfGxR","8p3TuzHAXl72":"fcqovNGrGjM2","h54HQ2lvcpQZ":"Gmr1yOtU19GY","JZznSz1ZYCvU":"CuWSP6cbzwW2","an7a6Cf2Dfn6":"n5PI7Xe63GyX","EXxgAitJdygs":"rm33EEJOtNGz","TEhEvTeuQ5fX":"Xy7YiwEh4fue","DIP4dkORUfXi":"1Dd71rxiGzQh","oSSywE3CV8Ik":"vIyC3dSON10n","iA5p2MLdZ7z4":"iPru6JbZZcMW","oetjBk3s902n":"9nOM4oTgmAoL","PzVbw2e1V4tp":"qzgj1Q1Hd31e","joxD25Nny1Rh":"vjPgGZMIOmk1","IEfc7dxcKRYy":"mgJ09v5QkV8X","gGglrVd5sTaY":"umbvZHXbVUwx","RbstJ0Pq8Clb":"CwgOnCcKI307","cive8JW2qeZl":"xEN1ggjcjbB4","OPOnmiAxuYim":"bQ8FBUpdlgzR","hR0ikW5ZwAxX":"mHlUuDNO9uh7","Gwf7A4C91VC9":"efJqLZJAdpx3","y6EttHJ1lW1C":"vDdzkgzhEYA6","ZvVBkZgDN2HU":"eFS6QFleVXI4","mqr0MxhYHLwl":"TsOMgzphcyCQ","AZUfwUhfdLRw":"hhQx86gjCivP","yjAwP6EFG5ta":"aXT9jHa5clMB","r2Zg3cqReVYD":"R3yubNEuX0OM","xC8DuLDyVdF0":"9mU9K2QK4JAP","kJye6w0ABBlk":"H7k8iSBgBi64","2lRDO5C4N6Yx":"HqEbVkocOifV","wsLifB1aGpfw":"HN64Z6N493HS","mpRlphYIATZU":"y0NSicTeZJEY","j42mNfDn1qeG":"W9N2RHEk4nda","Ziad8gPQ43tF":"dXPF6nDjR9mS","r4RsVhrAOEEF":"cUQ1aDjPwgbL","4VBxRmpSzHFA":"drtZHQ3BYpiE","a4ttJTuN3575":"pTFOAFZF3HeD","ClsCorEQnaxO":"LX9l6DNrRXwp","4fogpHwoub8O":"k6emmvBBMkHZ","3wgE09mfPgDq":"TA11PszdGETP","To0iwId5aj9O":"A2uNOIGuMcyb","uPEyepivqrun":"gccW3usyJ4iO","Pm16RSZX29Jz":"9GrrtwpBvqya","TVBV5tMNRv7A":"N88C5Jj6QJzC","KF8HERe7qj9r":"JE2G8EgLpYsH","98HZpbBEWLi1":"khqzqkJPiynr","nfJchruVKObe":"UFf0fPzpuIu8","NNM6BBWKA2V1":"ffVazr7pNYUw","xsy96TuSW7oH":"p8yeX8MhZYBb","51pm3BpT0zBX":"P87qqjfUZIGE","6kVOAkyJOMEH":"6GdQRhlcy9bc","3Sv9EZjvFCzN":"W26jvFuDSzCo","bO9skgjinuLO":"TIJqWotP29md","lfSHjhTm9n5y":"JV9DOieVhjxu","EBRhTq9i868H":"b2XL98oO0ggJ","O8WmlQf1ILMm":"Tl9qmF3d6vxu","oJpowJyBLw8P":"xyWs8hagYUqY","gE3HJ96xZRWP":"dwLTRifbRgM1","macbadk6NcnA":"21LBjw4CYcbh","NedTsamgKzH5":"3Yye3blDEhyx","gc3Hc51lOGJA":"VA35Xszt4n51","odjSlo5Q694U":"3kNeNdi3Ansa","uJy9gCkviRJR":"lGG0SMhqDqV4","Aj6drP7s2cZF":"nhJbG19dzYSA","J6ihMUcNmua6":"UG0gOLdR6YRZ","28tuVaF4Q5w3":"kATN3mzCC5Ys","3LhGBlAyOuGc":"oTfp8qhg6FUP","eZYwmsSOQ8bY":"UDEub0yeKPNQ","Df1ukck02PzP":"z7KXNKdMXBhA","supKyHsZLPQC":"ENQcRpsAmvOk","dGtGTeTSg2YI":"snnvrpqSul1l","gz5WmrArjcp0":"gkRS13l6Cs9E","blK0H3dPDvG3":"AvloVHhZfOHY","ZVWvCqZAJMuf":"2aguMi4tFxYB","9hktZuYEyLaK":"H31zVNMLvRPB","IjbHjqjeUwMC":"zX8l89b3Tb4X","XoAcvdR38TMi":"Lj34dX8ehi3V","L6vTnpeAhklB":"C6bSeKG7WHKy","4oRLopvB4DZd":"uoYwQEAaJdWW","8vNsQpyFo1v8":"VwB8a6VBtokW","yZiy66QqgkOh":"zQLTZexALVGg","UtdUz4CrDjED":"Nd40KnfIg9sF","vEKlfBNtm47l":"JK3HDeLCPmsa","5YxKviHo6ofn":"rK0EtUQvC9AC","P9vXMEeuPXih":"ggJq3zakJiTD","pMmr8gnQ4KtK":"9cj8mOGvaygN","nSzECzA5dWdF":"SD79wiQ6ZPQM","ciDH3ywoJyHe":"RageMt9cLg3f","e9a2EXxOH5PD":"efY9cvtyXZuW","px2BRtlxD8nZ":"D8MDsV77QZBq","J1vhU307UYB2":"t9NX4CBJmwwN","BXWgjqDxvYXA":"pqiDxExJgaFa","lNyLCPyjZ3GZ":"18OoBA5SX4zQ","HNXJ7H6KiCRy":"A7n7xetquHKB","2vPWbiMEst7w":"B1NEw6KcWwmd","7Ef1FGOTnpGH":"YXFTvbrXrIsQ","erBg8uAmXEXy":"3aXeDKmTSSQt","KAmksSROqtzn":"xJkH9MgxmlZO","9k2OV396Z7hm":"2Dr9sTtTucDq","NUVcCRZHJdhW":"nrYAWsm2jdvX","3eoO1b3TuV12":"HhAHNvSUpQGg","azdqUmNXstGB":"FQv1XGNdtbZO","7MjIopb50MDX":"8MPwSu8X4Zni","VckNzC6QmTSe":"JpX2nQ362NHv","4S2JQChbAfqw":"N3AYtfaqpLR3","aqto9CTblfYw":"Ji9w38k3wgFs","NwvxemCNlZT4":"Vilz3v5sTqY5","uZHGbAbkVjW4":"6MjsAVl8nq6t","HeDnLrNd8WDt":"wiXEjdHTQSSC","ZIlkcOh59P1M":"FdvSx7hSFEsk","iCUUjI4EVFvP":"kN2pF7XSK82l","xZOQUPOB5fJX":"LKFjDPRYGDIf","E4EdnTAd9Aem":"BnMiptF4vFED","H3IJAYuKTDBW":"xz9EZLlgXQqe","gWCxMWEfvDdG":"4pSSRyr7v1fl","yqueRlkONqQR":"w5EEA4SGXpWP","lGEnk6EhuUsi":"JvMyqRM6kBu6","Yhpe1XFhOYd0":"SkCZDd7nq3HP","gPB8bfU7o5Es":"7CIb4DeJcjfv","mKuVxytjKgGU":"ZGUaWd0TAtIi","0CUmgmi6dMPF":"d6xInTGjuQ8r","4Id0QowPVBxL":"ZdoTcslJWKyA","6q0EGSFLC1N1":"vqRKxN32iNA0","yGpjg6OM8GjC":"pagoQUecmEpj","ti79LjvK7vvi":"HuXOkSc2a3OL","zOVILxAXdbYm":"ADIEdn9vbcoX","00HRN6fSY2CQ":"XAfqpdVklHvY","JENq0bWagmM8":"ApGnLNDFpZV3","TFF6zRL72bx7":"fXSy4bYZ6E6u","bUq6SSLLvVF9":"1MM2XFsTOKfL","ntuI0wkbvbbn":"9yWj5Lbg1Oyj","lRunzJrLRFqL":"ip91FfCEx92p","olulwOfsub8M":"X0yFRDBelNQm","6fapBaJizHTd":"ggLaLnhgwWQb","1IOfbR62xIzZ":"XF8gHrCLyVp3","CsFINSqKZSqd":"Y5NURpGa2XaN","0np6SASqziPJ":"zDakzXngFzHO","gJ9iL52Qbs6J":"8NMIsUgcoN4t","NRi5U105w7pD":"WcQt0qHrlzNj","8kUD0QzCVdah":"ZIxEdraURUDM","rJhG6fZqIAGF":"LtiHb5svAVoF","CsO56BanUFMM":"DuHjHGhnQR3Z","jM3ROVI4nRHg":"Gw4GisftG1dm","ao3WE3zi3ovR":"jglB1XfuZnT4","GVgHkwESpIGf":"ugvDh7UGqHWJ","ZgviDatFtfNI":"XBpTS8i7LA7H","ZKrFwoDOzZAj":"ieKbdd6pZppa","Ar18NddTlz7a":"Ner9wXGvVOck","BBGsxIex5eCU":"FRydP4rKiUdT","EX4arzeOBub6":"IalC2YucHwRt","ZO5EXZ7Fgq0a":"0SdXJTbQeO1C","C0mGy4upCqfS":"G4K6a8uP7U4A","KICtPtZpZIZp":"Yw5LDlp3925n","1s0egd1KDL9d":"T7Jq2nlvlSKp","uriiYoB1LfFm":"GDd4YpSaEqeH","Ps9LQSaU9CbA":"7BdK6x9JDbFv","Nxm4qHHfOYhB":"8IEV95X0RYxk","0txvNvqskulg":"DoaTiqDkbIfs","BR6iQe9Xk1kb":"c6QA5tsTkz8H","JCNnMitkxFtP":"HNnGZItNUeQu","AAGsQFWLJgbX":"TwXmE3pUmpTv","L0iUB5HSX9pg":"h8jKBaRJx13t","odl3KRniZ9Nx":"q0m2Ok6nYLFm","YXLvEoH5Sq58":"jhnXx5FzMZMm","ruaDSfCqaNGS":"O4Dt8uPPbb9D","4P3GN4pvpHEI":"L81RWfnEwr2T","uXX94mzkOAjO":"B4KIYFB3zfOO","fJ0l7S6ZAuHC":"DMeKH8cjXiIq","C4v2hPMZV4nP":"GasKnuZGDXQl","h8znJANzBQH8":"Swka5f2FFcBP","iG5fWg43lkKj":"92F1aV8IaMTp","WDb4JeuEAD8T":"xb0tPoXxusdm","KXYeYZx7rhw5":"x5U4GlRVvYZg","5j5kabBwE76Y":"afo4oOFeBJbU","9thj2Q6DrUQN":"4Sc2U4Km0vB3","bK8QPXVzw18W":"teOlPUe9j925","I66lWKyv41ZL":"eYUcKlJ0KCeE","DVpy4znIqtkc":"s8caiyQKftDR","WpKML1WIM6na":"DTA4AhJRRXIb","tqKv7BUDf8kL":"TSQG5MT8elGn","2Shpu36y7SFn":"l0Ej8hQFslPl","fzQ8z6NGS2b8":"S52W62vekbEc","X6I9BNRfRIPC":"Kw3OSqzJtH1i","M2yjGMdfP5Jw":"WecY6oRLOvZ7","ARlWPIRE82cX":"B4cnD8LSB6Rm","6wfHHNFAnB6c":"HG4IgsMEwEez","pMn4nkb2mfWd":"tv6wJVh1dmoA","MDrSRMzAB6E6":"5g6Zra0s975r","7zVDc3JXxraZ":"sT59auynrthV","YOind8ozONAH":"NyVciPuaNIIy","GsUWh9JHmk3t":"S69VCvUrcsFh","UNzW1UuletVN":"BGxXgfXfsuZ2","aBBLz2iOMyat":"fxoSQYCW4HWB","TQTHq0bBwAw1":"8dc409jvd9PO","wv5CXeOsQuoM":"Z2rkOyaNnUnS","btAxcQdmDdRb":"JT8iX68q5jWf","24SMRdNyeJvb":"cQJz4FAIXuLd","wO3kNTfwEuIp":"UwG96y95hbGk","e6HHWGZou3O2":"jr50aYS7GkP2","QGmM3Z5yiMxX":"iwrk59SMtnZm","kvj0ylGoXK5t":"btacOUpR1A37","qBbj6ZQbcUXW":"QeFxeEpy6tM4","rvZOFXywhnPd":"zHzSsJgCBErB","mHVZ0FfPgahK":"swZ6r9T5Nve2","z2i4p8VRBBa8":"WjNDW1rewukb","ry2QLz87TVZL":"dQnaOiSKlviN","kMS5TWfDfG0A":"8Bg815RnwbaX","veTC73wWlK0M":"b0RogpY8qZeS","rPIoUsWxOQnE":"SvxfMjKfl5MX","3pdfXeBCi7uM":"6wrbef1Or0Bn","KUpt5kuNtice":"EG7UWi95RmaS","cmRPOTXw07XB":"Vafj34vBWC2r","LFSeHEMdCkeT":"2pJDJeISSz4G","JXiLdNvm850J":"5fdluRAIzulA","8mvVaXDgwuE8":"l4Ij0zVsSxnZ","XPDh61NvYT2r":"NGugphR9YqvV","Rp1cN2ILcxfc":"lPUAm1sPkT37","QtyilgPrnQqA":"mqt75GgJWNAp","fzVh0XA2t9DP":"KmTWRMI5eXWl","ggk8pqkVd5CM":"7aCePhwHRXZ2","Ctrh97VhTCGE":"qK97e0WV0yrh","9IYpRpDSttRj":"PWwBYXFFx1F7","t9jBhy4TM24h":"puBjFGAQxTEn","N8u1YlNwSj1k":"xdNS5RdJYd5g","BGRMirfrNY3w":"e86HrAuRJf0q","D9HROKrUuKdy":"cv9buGdwsgIt","i4gkB3ovYNou":"jD0g59CDqpGg","r8z9BEMKO9uz":"ba6KnE4P3sgv","LKV5RmIxX5th":"UsFSFM1AQ4fA","KaOIzuswIPOO":"WN4RbpKy0r4c","cEtyO4A2Mebt":"oCzkrDLDa09X","oBxn2Wk1AKI9":"utWHw8nvowSe","jlTlteUgY0eO":"3uu3fz3IXqlS","PxFWxOlpkQqT":"hf50AaKd3vXj","HuS9b0lYeVSa":"DpIVwZXhScPE","M7qDYCJfOOaT":"kefhPmDoRJFq","3oEMo6JZtBBj":"Z2WUUEgf4T6P","m7mfxzkI3i0B":"p8IYsioT72HY","PpY98Q4tmnvx":"DwdVvaH4ZWoy","4bHV8GGHJxmc":"RbIu364vzxdp","ZGbpCq4fhgk3":"0JflTeDEeFQj","7brg8XxRxIbm":"GRLVUVQ7T0tT","S9jSPod898Zn":"wc7nmaJgsDZE","5DOMMwNX2tDx":"0XNvuT1jLXXi","QI2rep3urats":"TPiIn1GO6gPX","kK4zok7oVVoJ":"tUPxWxUOKEtl","Xx690HhBeWVZ":"wUeLZkyirk1J","Ajx8Zl5JvtCL":"fg3zua6ViKyR","YJCH4uoxoOvw":"TipLujLY2hwa","yeaGp1CtmLux":"vF4WpNfreVZE","L3QigwEAzE6Y":"WGWbvCF8CpXb","qSy7nZ9qI3NC":"GkIqqmDXP6RP","J3YGknth9VPY":"QZv0nS51WNy4","WBIWzvgokdfb":"YVVJqd52n1nz","rJvcUVCCx29y":"tdA22h8ihtsS","ekIj89oHB9R5":"OzNo4QjrOczc","Eurk8dqbO1a7":"1ZkcTjXjh5ed","9XA2SUyNeNJb":"M421aBXKz0Y9","xEn2gPoJyoD2":"Plzen1n8ZQji","mtfcqkOPTKtS":"j26BrBiJ2AKN","W7u44i8FMGJU":"kyJi4DiLc7kL","aswRuWj9Iodg":"SlfspRL2ni9x","rwPe5Jl0SLiJ":"CmeegHCYY7kz","ZTJizHgDow6Y":"iUxTrOenYE6k","ylaoaF5SeiF9":"kb2EZbkbBpcM","oQKoxXD3xhmZ":"qjcQYzDEXgyg","jwXY7eTkWbzJ":"KLKqtpLXOjKK","syLhnkWMw1Uu":"Clrcc4sXtfAQ","fJx5ylcSeAjk":"pLu6ZWdr38BO","KrSeo7tPXx0g":"HZ3ADKmEr1ak","FiZVicoBetER":"TiNe03fBTWVT","aC920shgYIjY":"J6hmV9VCWLBW","umoGERAGnGZQ":"TQDUCM5RbEwE","Ob7SuA9MTqSg":"YppeZ4q0WVf0","NWxzutue9P6C":"tQyUhE9QSgfG","PPk4mgTt0etN":"5zz4N6XgeZCE","sZQ5sx7bqyVj":"P02XCcJerWTG","J2l8VBRG57EB":"K0hB669vQV8c","lDyZ8SyjhxIp":"e4adHPBHo9Cp","Y55CxSCZ1y1l":"0mpSoFpaUL8L","cgUcZ55oEIFH":"W3liRrkfxseL","wfcwzbzpzhuF":"eEJKwutXiBGV","5O5PGrZXP5sv":"Nzm7SHMFXZzm","mE0oqg684qO8":"5FRZSNpmuEQL","gyRlel1NNk8y":"aeuf0IG3VnRa","z6NybaI5bvIb":"rtxsa86sUyPp","58oImT37Ljpb":"D74woI8rIE3v","zoxjTPnwhHCZ":"VY9p9BHpXvrb","4o4LZrQyyA0z":"MxyDPN26pCOY","t7eNQSGR13xO":"kx47iwMGht7Z","Uobyl06q5JzO":"c3yG0bg32H20","ZZU9hsBmks8F":"hAEafPY6onu7","r0clFVHHWW8q":"qsuNExcBcVr3","EFtWosZrHIuo":"92am1JmfSomh","BZt8HTj78x7H":"Zi00PLOfxrco","4XNDHEV8G3XK":"o0o5WALxMqXh","3XpjKtV3W0IH":"8BoIZnkHBD3M","Lsk6Q94JbPJE":"xuNo2qFMJsVw","ARYewMxQXtkt":"5yh3Mo68QlTA","GmcJmGfJgqDZ":"fMfSGpIbxQky","gkRqCx3vgzL8":"TGa8DnNzKnXL","ZLqtoJBzz6wi":"6wqg2Dqe3PNf","ervVTjQXM2Ze":"iLyqZyMXM5zo","4ABY58i3C60c":"IHpXLoKB4v6y","NFWPELv6GnPN":"7bCgzmlXfigr","iBjHkxgWrFXC":"B4zba9qL5phC","XV3Swv8XGieN":"sXWtdJLMWoh8","3oAbpZxGHFdE":"zw05qeeHTvfv","BFqvF0cVHIW9":"fSCjPFeaDMni","83acq3sbUtEk":"CAUdpRwP9OBH","wOBtVAKdtnbP":"wfn9BMTssZIF","XMZ4PU2DhVA4":"MVEY4Ezefifh","cjn40hSdoh4s":"k4LiKFUwlEIR","amh4gV7juVLh":"7j411SkMFl7Q","esnhimfxoO1o":"kGgjdUu8u6Cb","TXApckpeWWcA":"OM3JUD1OOon0","FRq8ywGfm44E":"dL9XK6neMRWC","dMa1QtD6scnh":"3sW3ePJ5FHVU","AiBqmilYAFzc":"03ttCQXhcWpR","y7qbKT0Rwmts":"KiEK9rlCmu2E","RV97marSn2Qx":"OUgrJlSTaKkq","xqiAtgoJkRzN":"NBcVnhqE2yrV","qhwDlfbmOAmK":"oy0vNANwUSXw","GxWffW9GdDgt":"2DazhrA4znrx","Y2RjO4sttFp4":"cgKIiKyxEPIm","4H3IOuD2wlmB":"FHclZOy0IK7m","Maue1H1wYT8d":"aFjAG3inbZc3","XTwZpJKCt6Qz":"TtDM1wlaScKj","Sa4Ff1qmYgKV":"mcJ8c1sO710n","2ZNINgP2QpTQ":"h81PzPKxvk4q","zc6FWhbSAwg2":"UhFOdVmsUrN5","RPvkkCuoBHcn":"fcFK92UmIKgP","O4xbQiCtanVK":"9rMhUPPyNmjg","1zr4O868Iy7P":"MjvVYgblaI28","GG07YTGqcqsJ":"dV95qysjYImA","MIA09ioP9vY1":"sjOFPpSjR62U","ANrww52nBO4s":"Y40Mnrdisp1l","EEijSpOPHzk3":"Qb2fxs6lwqIh","mKKYZHnwQky5":"VCpjzPFl3ZDJ","dPP0QHaCcdYY":"tScLQbAixMnN","GKmQWZJfmsBV":"NtnCdF4rwEr6","UpRdm36TGRbW":"twNURBQ1E4xB","HgT2SrmZqUNs":"y4kLYQZwQ7c3","vMwEL0aiiUnZ":"L4PijpNADjd2","vJQJSkuYuFf7":"UK7kT6de75Su","RNInUiUCOmh9":"A8pv1uS4Ywj0","baSsxTTb1uC3":"paTN6JWBpk1X","6e0D7xDj5l8Y":"Y9WPNYPmYNxl","0PicIvC3X5vN":"jRh0V0XVUkSo","9vyUr43dWlCc":"79nn4NjnTShr","gO9bCDknKvSo":"J3gg26SQepCn","hMch1qOr2gwQ":"I6lbUj1rrpmX","0X1OCYy3R14Z":"m2EupxQUpTlv","0wwZd9aY4vQS":"1McojwyNrWw3","egn7lZqiTyRE":"zD4WELItcTF5","3CESVP9zlLYj":"b5ND5Mgd9iNc","DUjfql7RFhoE":"rCf2cppTJ8RT","aGeAyRUuet6p":"ru84i3pO6DUc","sb9qVDE5LaaQ":"8M49X72sQOZg","ofpBjkLdhs1o":"IIeog9UScnFQ","FrOcSAQzGQm3":"LCuei1hQh7T1","s4cOIFsRK7cE":"6GvMvdfGg66S","LrFsYFwHr5or":"yEz1EJOBbegt","f9xdSHtlz1St":"jLlOnQjWmJ1u","06bGh9Vbr0zd":"0QAa7vcsLPD1","niWyRECqmzO9":"Nh4NOVsP9Izw","vMKc2oi380wn":"dGGqQH69InVD","m5redXf7XoZd":"RVxOuqKBxPtU","EksiEfsePmhF":"nbRxqReM9iTQ","nNeGLhsW0wnH":"7CJjxjcIoQBT","uF7hYu5jBOYC":"h4GpJomkq7UZ","qDAUYcvoIzlK":"sgraW1ec4rRd","gtTSkkn9GbMl":"RLdvmoaOaJGu","bw9DKE4cNvSR":"VLBzweAPzq71","XXoevMDru98C":"6qcmIWXkzEnh","PZXcJbmQUrvP":"RE9qbt7Rhzaz","2AVLtvzSzjg1":"j10fgJJSzCa5","lbCbmV8tTFQH":"faELu8pEZ3Vc","7cnP3gBNls2r":"J2uKwkiM61xm","NwGVjFiUjWZw":"gJo7HUVtWjO9","IcMKAoP6TlIN":"TyauEHjukhfa","eJymrbFn4pCo":"1FgcsSnGhFxd","j40C7GnGlI2c":"eP89rwz2KiVP","pR4BScm2i1Ge":"cL2Img5BnbIU","fJuZ8Qe7bfe5":"7utz9nGRK8KZ","JUSiltqEbzy3":"zaztTdTRPhOQ","kg3731OCIpHb":"aiVVegFSMIey","oFQmebw54nwH":"pqkYBZV4H3xe","fzpKfarkT4zA":"G9rBGWJPO7Uh","CgzqFmgNIyOz":"J1EmVmKegVT4","XuQLULe3F0kt":"sqx2UshRUcEd","RqnSWKv4ZSJ8":"383YM945DF4s","Pfb1XyCXVrcE":"yXyMxT360wC8","R9p57VhfJJ4E":"1CdfHXIOfppg","lzq3OOYHHlKg":"xJ1oeInMq089","7oe0NMuhKHdb":"1a3Zgd6q0fat","MkDbXhsqSc7m":"wcyMRGH95Sb5","oxIarBuNlZbH":"o81Np7OnoN5T","Lm7F9DSdrkDS":"6FPPxOBh2D7o","5c3BCwdz4XCG":"qjppeXv4hv5R","4LcnXZNvt3q6":"sRiCz1R3SniF","8EAR6IQcJHOR":"uGe2lPlphir5","ouER7q9e0ZFl":"UK1vv60sCarM","GsguRQuPmx56":"5y4RIz7kMydS","hRh8JaLOhSUA":"Uu6nwIhcv72E","Y18STm8bVuXx":"J4eY2NbK09Rd","xrq4jy1I04Xb":"wm4dER4hEe10","IkeCYx2LXQTc":"piuDmXgGkFFp","XNpdPYHpGflq":"ImfhLST0dGq8","3N7WOPSBA6nl":"lNzbVHMFGnQN","sV1IJcuFLWxk":"V0qlOKiHeu13","a71tgrBwX54F":"xaSww2R0FaY0","frL2ctbMFWFr":"oZA9E344uCDR","t08vjz0NCIoo":"87WXic4bywwd","KWKzhJE4g4Mp":"DkqRRQkTXKit","bmJb2cqqcB4i":"oR2hyNwoAuwY","UaCN7wcV9tuH":"0m6pCU7lrt3o","LsDXdWTYfXf9":"xOKlHEMxFjoT","7D5aSz180drW":"3qVtuXqaog1y","vRV91oh28wtm":"PjU4IzEOlldG","HwwcSHVgEVPJ":"rUPYZF4WC86C","7W0HxOmxNuc4":"nUsGRXAsdlXe","8T7ATBUZKQ3q":"sjZL4yLxctqv","Q4wB3JJNHC5n":"CSnDMPtHbkSE","aa9GDFuSAKwJ":"yeBhbjJYK5Zl","Nt87dJKnICE5":"eiWodKhlSlIV","tvsYl4rg606n":"FfPS9nylLjcU","AStvZFxOTBSb":"8CYmTCDYzCWA","bPq6i2CHj3lL":"LFurhZC6wn3L","7Q1ct81OR03U":"9FPuBsO1FiY9","NxeWb7tLxF9p":"SzRbTTZtpB1Y","SIAwkpI4QB79":"8rL2EdBj7rNo","LQmxxHW94F5J":"lZEUxmugBkF8","2th0ByOtQe89":"mRDC7gIUUM3W","TykT8KZKJed4":"lhYOBkDREa4t","NoafQZFN1ket":"smFSy0fENfzG","pVcMfsh3Nr38":"MizqrAZeD3Az","KapXkAoS3LDV":"TVFRF6ju0IF7","OBds2gkDqnlW":"UmVM7Wgsyl9w","CLrm0m5zhApq":"1wxFp0iGrDh3","jwOy7OKoblig":"ODsmqZdd3aJX","EdbnFW21AjcM":"TFnspURijapQ","KoUgK0nu6Sx8":"vk2fto4LNVRk","3IMnsvjwk8kD":"kCuuWViW1xCg","qhaKEKdQrAZl":"vD8u5UB42qd7","WIsOIOyp5yvu":"HAZAVq9axauu","AYcfV8bP2LjY":"xTgFHDhAAN50","kxb0SZAJoxPy":"HRCoy4NHbpy3","0jWVOu0rg2Z4":"ntOKqO93bOSD","bneuPwxgksZR":"bsehmNPGxzkQ","txGS8KtR94IR":"3hAoXiPQZNeH","54VBzC7QSPj2":"Gva1t2bPMBYk","1uUxwE31oPo2":"AzsYYBSTf0ZQ","kYpwju6JCrRW":"BOChaBqoc6Z9","Yruv8AS2N6Ia":"fLFgdNPQI6c5","yaum5u343RoU":"S3j88aYWQgKf","mcfJct4hXqrD":"DiEB6TRe6yD2","4E8JR3Lp3Fsf":"4Xsr1TgaOUtx","aek2eQFeWkIW":"Tq97qhjQhoVG","JPKesyKLmFdb":"cllj0gn729oP","6NBhqgIMoceg":"6CoKxHrW5372","h7SbbRa9iavp":"SoeKlpBHdBF3","zbYVn7nxzjcy":"NzV5pcQ9bEqx","NjBcKhSBjBPv":"QTIf3jtLcESz","E8K9knK36cyg":"y6htZc947jVV","H2KooJQiX7Jm":"7XmZ1RvECXCs","Dk5id7wAogbL":"bbtc1pHjJjkh","DmsQVFHpwXUE":"iK4I6iMVvKBf","3wbz4ezjJDXB":"7JwRXTqU2Akx","fBbvhQgrEq06":"uUY3JzJYkf6Q","0z8M9McsEc7P":"xvAhyg2arhgA","oaTF7qoE5NjN":"kModHB88HrNs","9rstwXfakNiD":"01OvEwXUJ532","ALdoVdUfx1sW":"kRQSuPB0We6a","HprREOV03YAo":"j1Gwe3r2EkUK","WuG3uxjCPpLi":"qwCsaQyKbxl1","gdoX67go1iaS":"DefrlQfoC202","saB0ocLLwzPj":"dj5AA9Ifp5ZM","sHaLsuF1mtWG":"i1MIUHTv8eWL","VFn0NV9zendu":"kVgXOmzTQgFb","wYXWAJpdWzae":"0lLGYJJSUkPx","N8oXCZPPca1L":"RVcINLlTqKiE","gPquDAOhazaE":"a9yVTQq9etmh","if1RkUL4OrnL":"cxtHQktZ6pvE","nplTkKu7c9S4":"4utiCWFL4oxI","tB385jH2AB3a":"qsJTk1GTWqm0","9xPpWZUOdYXe":"Cru0EN2XmumA","Dddx9nPPdf54":"e43ssFvwGvoU","j82NQkKoh1Yc":"MMfPzaxNhSCn","tZkD8IX0IJ1K":"GkvDmQsw0pFI","8WSLLRZ05AmL":"VZyLXRbPL7Xk","22moFkRc4LtH":"oqw65hnb2mc9","YqEwYpxGBIFU":"rBkVk9Ci19kn","u6RVAi7lZ0I6":"7MIC02mMSvJp","WzI8faC5cA0e":"eAFJpnKJmFKJ","5ye6UHgwRaGr":"rJHHNs0Nept9","ArCC1UyA2Qzb":"Ht0Bu6DShmkj","MpH8h3cmIMLD":"8Bgtntip9gEd","ORcUIk0Z8O8s":"TfxWABf7n3RG","V1vIOnfXnySi":"pc5vLQbr1pCa","fJJUSOBVyx6g":"vV8d7559YigZ","8ywIn9DObJrW":"F2L4ltb63vZZ","nLhHLl9OVbkE":"uGhdUqjMvW46","sk2I2MBjFdJl":"mOeLVsrunEqP","jJZJy3wpexcl":"VWQrNU4i8kWr","PsSiB96JjmUX":"UD8LDAsfyNbD","t4T1TGIqWnww":"2aW7H1NG8hH7","T8X1c2Z7rXTG":"9a8uudIhZaW6","NtY7MlZIdkbm":"DflcpglLoBMy","pLavhmedOpO5":"EQXnCTdtPNEv","Brb74zWqbwxD":"5DPSJJ4oe1Zh","p804fXin9KzH":"UlPzgRrDI0M2","HBGTq1oPXtGF":"ZyxPjQFXXRVV","z6p5sofQ0IyQ":"WNamWIr5xvJq","CPXta1pdyxk6":"9EkzU1RLLJKl","nOXZLz17rDSd":"LmxL3VMWDuBY","213raNVgp5lB":"xY0HO90zU2Ll","B1i7Wo8pccc0":"TyAVIMVSDpws","S6zPconYXTwK":"3RoMkHpar2oX","0zCIMv77eMSA":"oGCVQAJZL0dc","4KySJRCsUYWZ":"etwXIXROLeCQ","k4pJZvJ1t30M":"gxjtfVdohgi7","dxbGIp1Rf829":"g8AS6MOLEigE","R0iIVIsKuuYO":"xFvMds9XCk8L","uvNnsHP1bVXm":"sLDGBiedJ7Fq","JwEtckpF6xIp":"sw2e7v4VuDCu","BBVNQvt9oV6x":"vdySuzX8mPyy","VrMP9rirN83G":"D0w3xT6wUmha","uCcx47ealEfb":"Kp7O1iivxUFo","Jj0OgbKVE02I":"6aj9t7b3x83K","Hfju2QVWHXA5":"kjdEXMi73ZNh","5yAcR6qTZLxd":"iMBuwABI5rVJ","eDBA3QSxpzHG":"e9VQAwxd2EO5","KjA8LivMWADw":"LvDM97F23Tce","KvI7mnKY5nxi":"v36FElK8p74q","J4vCsDgRaDye":"2aIaXVdQ6DcW","I7wJ0nvDQZya":"y4rFdULsrznu","yH62UtCezxah":"MPzoqMlWdUwh","5gcSFpwExreP":"GiKUwYrzXT9a","xqBmBpApwMwy":"aZbmYNixBhzu","LJ2HOyfGSkCD":"gTzFUKYRYzyx","gFaZnaSquR8m":"Mr8skGcXX0Fm","KOWa2CzyYJ2f":"3r2qU95R4moc","haWVf0O6OR4K":"Drno9b3ZX7hS","5ZQr7t4qIDIB":"gTIoNuKrcz8e","dLWIcbp1uqvt":"Uyr2PWz46z2u","X8643Eq3fGUg":"eX4YEnBCXaCb","l33oxbhjgRoP":"g9UrVj8qJONv","wpqoOgR8jwAP":"GHkS83dX8Hmk","voPrXqowwMek":"cXVZ8nLek9WF","ELZDOuMZsGqk":"6m1yXIrIJ9GX","NHKXSPurr5aM":"74ibC8i9I0Iw","r6MjcYvnT8cu":"4DnDTqyJVmMq","rIzS6EAKFpAK":"9i8gPWeMlV62","Edp1yNbD7rin":"b6iONTohr5yJ","eRGPHuMErhVu":"EB8y5yAUgWYe","E3aUpKJIEHLX":"I5facxEaKCLm","bbWcOm5IXLTm":"APMmzHPC14G8","sbCRr0xJMkZX":"ZgZlnWvvU0AZ","sVnBXOfZCwMK":"pJ05Cp7lH2xW","qSqr6zpb0DWX":"Xu53uPOU1SDJ","eUAKATFOeXRl":"PYqdwoFrlNI9","pel9BKAlCn7A":"zDsmZZwpCNwm","1zlpTq0UH1Hc":"c3sApJMOi0Yc","EgVfZiMUsFeR":"Ko4z4GdyaLtY","SpJlGIxyqEvZ":"MVbIDPID38ub","gHJutXKWflm6":"U6DswPRkyeZZ","ILSxSjgSnp6x":"8gPicKKCANLZ","XF3xpPBHJrzY":"YIjx0pTqhe0o","4t4AHPvhlnu3":"icGGdJ9bAnEE","eW8Y2GFPfu6x":"0EEG14RJb67V","uB44Xp0faiUH":"5inNOCv2cN93","uXuzxHUaCPYo":"DRwodtV8GMfS","h03lwCzNmHXS":"KxI1rFelNUGh","N91QnjwlaLE3":"xfZyy3FPjgH6","tTUpNciKHkIK":"gJN1UVVnWiEc","LiOy9Sl57rRl":"VqO95rlhx0jK","IQzidTbn3Xqp":"ITEaIYSEi7iB","PHU7vvFFeyEh":"Wh5Jy3s8MwLk","hx7NdRdZI8Ow":"AYLYvHp49bsP","hHVNcjivCA8h":"e7GM9kPp6tgT","EMdQZJ9HnLTX":"gseFIlNuLHqz","LfZACDRSR8b5":"VbHszaIiGeLh","8HSj8dZ3Lev3":"sk4SnvUVoljC","MVYGLyUVrG5p":"zT9VQ6XrvngZ","zeJQqJHIfHUP":"xwUnSgMvo7tr","Qv71G5alZrc3":"dhxIBgx35zPg","3sdZ89u2Y4Ci":"n24xPnQwiyP7","XN9izl5zMvei":"g2yzJ2VwFBzO","bKJXEfHTIE0a":"4iPsyvmkrdTx","5RnI0120Q8L5":"BCx10LFSu3S1","cL74eAjKdk90":"6hADgILGNkx2","IeS1ldmQRhQJ":"Fmk498GTdpv8","hotz8AF3fOlT":"c4de9XRgNLDU","HjDxrVYJF0qb":"e8QrjkEzwIYc","xO01px54D0sG":"3TYhg5a7YF4s","8IeVKGBi7wOG":"6D2D0M9lBD36","d3MN5gQlUSbI":"hDsc0KurBdEI","dQo9YJPCahcQ":"7lVrQ0KHgN9Y","MQEPOTtTBIVN":"qr5y858d5JR2","e85zOBbiLwwQ":"zYVGSzxvdiAD","y0sWdxc4tNoZ":"pCQjR10SvCaH","VOI8NvFAqKqf":"yK0z9piHRkHl","1fLoqAU8MA8X":"gq4OkTWYDMd9","0pWydHaKi2jG":"SCJ7OdrbGYM3","ECl0CUO6LIaU":"izhvIfdpqy5Y","vzW8cQY44Dxm":"uDHOfRVOKCvG","RPTWLvgjC9wU":"7tECUmlCHuoL","VTXcJwT4eJdB":"xArKxeZtCGhl","d6RBHiNkIitF":"lF73GrMrEXIg","gCoeVoQ9ZNmN":"BitsdJGHmSBs","h4yxQi8DdLYO":"KM7ZSVROu2Y6","GqgPP3WsaIGr":"KNyX2LVSRURe","Q6bmrqE0RLMZ":"ertD5Yc4NG8S","t2eQFBWBb4dh":"AaHOrdxY6Z4n","ww7oU5oofWCj":"PUSsZnPl4WCB","FqSVL07g7skx":"BPavn67NOdne","OIgVoRdXS1AW":"U1C8CmfM7LdZ","HbkXngTLugzf":"mbsJB6g8mtTM","lKI7FCiVVHBi":"SSmrOu8k9rVZ","CI5zovPmLnOx":"eEptxpGKi3t4","KNbkdMDbGojL":"JkejdAChjn1Q","Qpu47nhbXcsv":"J74vuftjGcwL","E9yNfbQ8vLuw":"4g78qwQRHgOx","HtWnv3QAqlDV":"L7CuUiczdYlA","V9PlZrzlvGKI":"47kZty1Ksm4d","a3lyXgTi1HBZ":"1Aa3zFFY4y1z","1eX1P8uwNyie":"fCH6Kx71bS6q","cDInX8oJ0Bv7":"8380njSlcVtg","vvyD3W8GulLu":"LOoZsWo9h48M","gUA2TVxsKnbl":"jS7BRBRRj9Ls","lo3tpmDwGuqn":"IE2celiquqn9","xFs9WG4kauFR":"T5cBoQ6oBAcl","xIPXSdvlaYJr":"J2jYDMlQlv9g","1ETS18hxOFJW":"VnK7oooHIW6y","LBVYrGsxuwdh":"o2Yas61bQ1nw","UpavKok34vCw":"9Zvl956WoLVN","4OFjtdV39Zdh":"bGJQAKYaFG96","65wePgprdrPv":"JaLVjpmdhHBY","Bx0VoBAUwIWl":"ZnHmc7lm6KcP","yqZWwNE108pb":"wsbd87A1hviT","kgOv9Q5l7oOM":"ghB2qD7qgnax","PPGhzXvoCA3r":"wADJZn9IF19n","mBkAZEJEy3T8":"bTCAtAe1vZq7","S1lTvLJLLNbZ":"qOyuhnE7eHpZ","0ow4upqYsnFb":"tUlgfDl3Ijd1","xeHP2xgAqUxc":"wAOHOW3EvlFd","YcXAONhJ9Q9n":"FRbcI8tgmrhu","nWF8rFIQFZjy":"HBrfRMV2mrHp","iLcKsXBrWhoi":"9Gx0QkQzc3bc","k8aGB48eFaHw":"GcgfIhmGFP5o","kMkWPWExTcfU":"JGvavGDc18My","6q6uetnNC63u":"J5g2W1g9zlM2","09H5HkGmOZuE":"H8iMcOf2l022","INeqFIhPVbe7":"jdhbBIcF4Ixo","4MqvgN60Mc2p":"EQU5PjxfuSER","SxdozTMNHeDt":"8RjDK665cwrp","LEMA8Riol75J":"D08JXVjLD865","QKrbOGYEoNdp":"woZELz4Ni6dT","XlHuNwT66CM9":"0w8ZkxvN4Eih","wzf7uymcaT0h":"Lp8wGTpxSVVH","3Q78vTABvPTG":"wGJFh7whDp02","vQ19nl5MXQcv":"owZN2hmuMQ2y","ecmkUyf1d1xg":"mRH9KPwvnc5E","iVrEanQw5XAt":"4YY9LCieVDGa","c3uxjbivZfGV":"QwBADa7xAJly","G61D7ZzYIRp9":"GY5IOQ0nHUt0","ZoD2b8ehICqG":"N5NbuUcH92bT","szfSghgWsG4C":"kiC8uKiYxgcn","cah1jhpGncVw":"awgZLKaPtcyj","nnCOlN0uhbwh":"QYOdHYb0EdGK","UZuoru0064Wi":"2touJFchYrtA","aSHczEfbeMhG":"rTvpje5oQgp1","p53OW88eHo6q":"RTt9MkYfFaO8","72aHL8Hv6qGr":"WtzeKCMq4rRL","vXJyEdoFg4gp":"bfrF1wuJacbl","mTRsVFFNCqUS":"7ykwaDnDrxFp","4ZSMSe03M5Gw":"Xk4oK5gl4YOF","Jm68dTGojkX0":"WOj6rvXAb4Cx","kPcnGxcVFhdw":"5CRevchqEFaI","cYeJj43a62l2":"JyRJjnL8vCm8","8Fbol5dKV7cV":"96iBKbJrPJCm","cUoFlKrmv38V":"zrtJ6weDQz8T","VgORgzofor04":"SLgKLWH8E67c","Pny78D0eNHmC":"VD1gCWjMK0rA","Z5DY4ExbUAm4":"QraRSNXv84oc","EgFS0YDs14B0":"qNrtKhGcsSaj","WEZBDl0YkJXc":"sJSBDTmREcze","tlmZG4MtAt8v":"0uEwyBisBLhG","737KFOFHyL2W":"L3DX4LFDsKYi","sbHcXsnqLkH8":"stNDoekxt0cx","9wFy1n6yDLuO":"NKEvwalwAhyv","U1oW8OCpzVRh":"9niHbtbzsSh0","55VOKmQikBwu":"A1PHU5MnY76y","DA8q5T913DIy":"FcCldOJGaD1D","JqWBcOtu2AFK":"varIRm0odtcI","Ikrwt5zkCEg4":"xp1QClmnjehL","JDZnZqsslI8D":"ZFYpGZv7slwk","5nGTGmnuVtMb":"syN1oRfbHFZ3","0tDua1nFp7KG":"yortQeRfmgSO","QEX2fHmzcQNm":"fobggrSo49rc","44QCCPeiCES1":"5RLKQnSTEq2d","QrCg6rON2RJU":"ewcYxCjj16xC","c26NgZ9BGGny":"IJAXQdP9A5ub","INwtRc2cLt0Z":"cSqKyoJsmINK","hLeaJfP8Hr08":"w9UMns1Jjgb0","MLxtcrmg4Utf":"ItxAD7Eg0PYF","XIgj9aPQO1Z5":"c7HecFg7yyyF","IOOCDtBCkJCd":"AGEKXYAcmHl8","7uTGjDo7PVIi":"9YQLdNqPZS1Z","BAGP30nExWhw":"hoseafWGr0KZ","ODuXYQZLIULf":"ugWDZU8OcRd5","HEAFFVGiw6Ov":"j8hdkYyfOOat","XjuRrDO8XnCz":"uWo5gC0nRNlV","lvMrr2JFyfVs":"kd1EORkzutL4","U60kQ3nO0jgo":"u1p4CmVmzuyO","YVG3Xo9epWfm":"KcOg5irH9Bj7","1OU55TqiEQ86":"fuGtv9s1K5XL","VhnPLRkDAbyg":"0jMequhFWQB4","ga1qb7uW8V2h":"ognw2OjH00HL","gyVUgnMoXUCH":"vkdNiqoMJJFt","AEK8b5Uw3oYD":"7iNxLdmJqQcc","2b7dBDb26dcj":"xQpNDmzqS5SG","fcmds6dkV5TU":"1qnVUoeIstrK","mt4YJ1ahuExj":"B2DayW6iSb8F","eAWau17mRqak":"F34V8kXHBv4p","PzkH3mek5WZc":"BRQxSzwys0F9","lSAomKVuW5iv":"j0xjxCMclPmB","bWq8Y2DeAcqB":"OZz2DNSN34E6","rNyBI4k2pfsy":"MGaiVZsL33yK","sopn6GAmhJxc":"ldGi9d8kkyBB","E5PFpFTmSvgH":"6EOHaNv0aj4R","sljcaZEQXuyV":"M9UixJPMuZfu","bGfHr17pAd5L":"3VEWEkLxdWfe","hpLuI9Tg5ZNi":"8xAMPHUhR4R7","OQcDhFZjhX7g":"gPOVDEgWj2Ha","0ejzVGSE52Ge":"cKVAnFOaR6m9","7hyFdV0cihrK":"rli31cK78mzQ","rQuFuu6kkU9H":"SJKel6lfiOot","dSwzxc9CDC05":"MdCpS6vvSO2o","eLWFVZiTKKth":"jPiiR2U56MkP","vrgKCDHq5UsE":"POdU8iNxMAZX","8qyudsd6X3zJ":"3oAuPnRKuYj5","dWSWwhqpw4Gw":"rvsULAb64fjm","kCI9vQF2PbVd":"NXySq9n5kwgI","I54SFQtCuFSO":"Gv2vKlZHWVZp","V2VVvY4Wh4hs":"QmpPnZatkMgR","Wxbr2CCWnSKc":"zuwI4mbGrFGG","kYpOlJrD0TtW":"bev5gj8AlAW6","AnPIb13DECsN":"kFgoPOyrK9yL","BjsTp1qsnKje":"h8yvraaQuUsR","raZME9l8RjEC":"a1g4Lhjl4rS4","gyOgmEPEBwxA":"MWjv4g1MpCrF","sEAY5ylAAIvh":"wMkzGcUbOb1c","R4YGpJmoxYbu":"O08IE58eXfSa","bk9sfmlN9qTP":"KYCN2XYNlQfm","gsYExAES0cYb":"QFPPwTRhFbBx","2ao9tx4MaePE":"Na1mtl4WeW4u","HVIjQcT1zWKr":"CngmRy2kF5Cu","7j547HZQAN50":"rZ4UOkIOYMHd","KWhnUwaXpX4d":"vC2nlnlswfnW","dKUIHMx7mRcK":"ZW6q8YDzukNz","QjeOo4C8pN7u":"99YNBvU18JvS","X7CdAGbkDyWo":"88qFRV1tJbit","1LCOqdezhrj2":"1d27JO795L34","CCVfeQTh8xeE":"DKd7GhYnubap","ZHuYUSfVN828":"PEojniW6GkFd","H5Q0dTNAsShK":"TETOtbENT2uu","RA5Z5Dm0ra2f":"45eJyzC2HTuh","4p6527DfvFsC":"0hKhAxboshMk","mwESQ4QXj3eX":"coLnk2ahAFTw","3dmwPXJ5n1bY":"kp5YR3NF5W4H","dUl0FNlSbROK":"3I1nQNqIvnTD","aZEyO9HAuQ3t":"kQHMcmJymKXQ","fKXlcmzOaJim":"Rby1FhStnBQ0","6AsdPcaNvoPX":"bRhIgV6NKT7A","eMI4EbNX2HNZ":"xl7zE1W5NPFO","fYjJ3Y9ixrow":"TBSlYSTdO14K","XNmm5XGUXn4S":"QEo2swpn4EOJ","H3VjwzxsbSdT":"IDCeUymZDZRo","YQeGWCrV1uZV":"n17LjA1pwCf0","NzW8IdGIfoPS":"H477G4sdBBPG","0GvnBJvmLoLA":"s6uhdRO4nknH","IrmO7t2hR6p9":"XEZybr0dJ3Xf","BB7Bd5NPyed7":"zEabwcHoFqFX","C0jvtWP8smtt":"SEQ8JQF9b4At","AYiIPfXq0epi":"Kb7BkSowCV63","g64QFiuqQYHt":"rg5oVVE3LMyV","q1uTMrj9B4OR":"ekMjSJkHbkcM","NxvFnt5u3vm2":"KiRN1PTdQB2T","fheMjVe2GlVA":"xRb5jBkCYCTs","xdzi3nkQfxg9":"60KHy9AiZwQW","BY4LW0Fec8cM":"7yPHSw6aOcw3","LbvSs4cw00nl":"uoQxs0itG5jK","Vo3WBel6F7rk":"oDEKr7VYhOlp","XjhCR7U9f9jc":"R4LkD0rBI0cE","AHYNqL9bwYer":"yN2Y9jwK1geE","MfhFiwVOpTPy":"QH8MElOggfuD","BpyL7Sv1wh98":"UyDOCm376Mxp","edIFnM9WoY4Z":"0vQIkmtFCCPi","HXjgbRA9aDZC":"x48G9DDvkDXN","wu8v3uxTA7UM":"vhzjnWn4GMIP","jdcHCSZecfHC":"gmI3sZayBtgI","ZP04O3b6oZC1":"UeeztNARwzjQ","yCCOOp7Y4NUD":"lKLpsJ1YTVpV","UyO3Z3RQV1kZ":"l4O6jNdfM17B","SM5ypXHkqzVS":"1nGOEYNQK0DX","305kJoVgs3Ab":"IZKRsyj27fjA","lN02y7ywqxaK":"A0UlsZdvejzC","9QUTXWIUylCs":"hweNpnj0OiaK","3H4p2V2rH34H":"Dw3v5BGzBVKC","zex1Pn04zAft":"cz5urgkuMKnK","dOkRKRmU6nVL":"QfMQMWq01jpJ","o4MbD3kOX4sb":"IEjTJaM1JxJK","7K0Q9kjscIDx":"4RNro7NlvTxE","M3ecS7I9feJq":"SmCNaYLlQLX0","L3WUrbJBqyC3":"GchpM4CcfN0G","e7oCKyud2ZW1":"giGi96FfiGWK","zKFirRFIKYng":"amfMUfpC8pZm","gmEYkALXp6Xb":"XJQDE4nZcINt","p7KvMTT5Jlm9":"ml27qE91HPJ5","55uRF8wxVejn":"guqE4Fx2c4Oa","bHy1DgXJkRqt":"QtD1aOagfAuS","ZryoWS3k56mj":"KYM9LfQRKViE","V7MGxomoLxUn":"EdvUX53zf8XI","X8pxdZppHhh6":"jsubk5rhgxUj","JdbUJBWooJ68":"EBAd3Tkkm1FB","MPG0neOYr8AP":"UF4Olh0DiY0z","SOqOTzXvCrFG":"iDFknlRwaZrk","PtfnP7cpwmMv":"bM0oqjwkXY51","04uT9Jm3XMYH":"TWpNVY4wcSUC","KUfKSU98MtRy":"Yu7uIFEyONJ5","3nkIyFb6HTuq":"7vLlQgbgaStm","72XrRuAZPg0W":"GztRjWgRlmUh","LCWCNvcJ7AqJ":"uFKRymPSo26n","O1RHWG62LEjM":"tK80ojkds10T","lw8cd5hAn9yn":"3v8V5jGrcKDZ","v1FtoliTlnbf":"WjsT73V0HAnG","JwcGQVvPuJPq":"ILRIq9OiFYqA","5IVeA3lji95Q":"gMXh8oP0Xi42","itEzzhHhb99H":"RqTWiIDGLk5I","zkIYXnUvObBG":"6DlXUYAvZaRq","2reaswCHj9Cz":"y5N6NyAZXMij","L5bjrg4rKOSB":"vkhjwPMpTx52","FmeIHhOLSWsO":"8fiauyKW8Bwh","V4PhZHODNUVa":"45l8mTfOua4F","eRbdtx4GP1dL":"VItDW8IiZVK0","WffrHXZW0loC":"9FdynSf9ERwB","ozugVn8F2xXb":"RXoAHTtYfqFC","UHjqdjvemipT":"UCp2qR8amboT","NkJuhJJ9EjpR":"IddUIQ64xtbR","2fIx70ME3YGh":"XsvsEq74yrAP","JyfKYcIufJqN":"7FdjJWGnvG52","Nc2HALiq6Hg0":"ubtJyJiHb03d","aZNKJY7AoY1z":"JZOsWxZ9YmM0","nFeg5suoTsQV":"UqtrnOau5j0v","JFbl4gelfZmd":"1OMazzPWOw43","yuuwJAu0VtQe":"1I5S9ge4mktC","KOobIYuKBVa8":"TR4QDxDL6smZ","cYkhlbaqMr0M":"u8zuSYRH6gvT","WCRiLtr5zcCf":"iCrLv6k43tO7","488Wamtay471":"tLyaMBDm9FtK","crQDaxjISpBE":"1xaGfEO8pUxM","zSzD0otBI1h0":"ELsQ4KMAiRyJ","3hybQIERGS9N":"OvKLOUQrPG85","UNrGO5IMKHxf":"98woGSNkrJkR","v30xgdUI4YNK":"TBcsATAwN5gg","JGNw14A3cPIV":"uEvhvxGlBlJ2","lneZZlbm6I0D":"eFCI445yCWRw","gQfz5QSNzqDN":"iGqR9kvfUpTa","9HBemw5CDOzJ":"1OgkHbLtPBss","0DrvxewC8SkP":"AvwVeJA5W6FL","VwrqEQH5RVSQ":"4LTBvgFlc1bb","Bgn4PEMElRnk":"KvQlImTbdYgx","4RhOsT3hfo7U":"Stv6WDrS5LmO","Cs7byZNmp4FP":"2O2rL8fwTfSY","jxsvvQoNsJDc":"AMNbFM72msuy","p5rTVeNRJa0O":"GJbnXOLaJn3z","uPFqqucGhtgZ":"juKkvG0qKCGN","lDdHVN9DiNvq":"qeByf0H5ZlVt","ljYHrkgO5P3Q":"I9oOC6TMVKhN","z73xC6s1Q8dp":"JCw5x1QAPix9","PZzP39fI11Tc":"iM5t6nDBwDm5","xMLdPJJ8E4sc":"7n2dIIerJqZm","HSsgZJ9Y68Cj":"3wWdUxCP6EcC","Hvnvdex9rFlA":"cBjijxGTVVtZ","3zLNCQQCuAsU":"IvUjXhBD2XNS","IFYL8F9zQudU":"tAF2pBnAT0aE","PXAmfqWFZhJE":"htbHBA9uZ9gk","gDIu0t3gk5dj":"q9UBCgPXn2Ps","rMA48qifZT70":"TvmKjvfDtymF","ebMb1rQTmgDm":"2ucvw1wmJYZE","xyoFV6crR9MY":"iK0i88X1yLaU","eRRsVzD6a220":"KthtQFl4DpM9","2n2NAdl6omLZ":"vGbjVZPWP4XJ","jFgQwVb4S60O":"AoNM965uE9DR","L9XvD638xiHQ":"BNw7ma3dfupf","r7NckhovtaSO":"8Zue5Ihuz0sR","rAvvFtZHcfut":"3tIXlpfiZ2U2","IWEr2SbwfRVU":"dkaht8bgYOHP","7O0G5iEhtgyM":"d28wIYHUZCk1","mSo69GlQmVHl":"RffXWonmHvlF","azkQNW7Fj7Xi":"8pizecUxAgBJ","HpWf50gmL6Jc":"PRE0OSLtW9DF","hMgAq5Qntx0P":"SXqALMq6RJAq","XtjMfixUvnvP":"KOWWUWA0izVh","ISzzd4JEGhZS":"HDmt9SvPetb5","ABEy3ItyU7gA":"WTWBucLDD5x2","HHvzHSdXI1cw":"0uGvLD9D75YW","cgXr4qPidg5h":"mbPI9t3nAFED","CTaQmnKT2AE7":"f5zX4C50COs3","MbftELiCHXlu":"u6QmtPhsDFw2","0ISUV4Ll16Qr":"DKQBhjD7X9O0","DinYoFuyEcRm":"TVMe6e9cK54k","DRsLBNeFoKW1":"ftyoSHUdGrhV","dOO95WTb1Qje":"gDwwaycNcRLZ","4HbjnaiF788m":"fiXcJ7IbZzZW","J7tfv7YAc4qN":"iu8CVRWdBJ7h","b24ohFWsrwIH":"koqsXkbK8R6O","mLVHYLjZVljD":"52VRJHgONRhu","2dvPOqKKHghl":"laLRO4xQmr17","BBsTxEXEJkGV":"mqZ9dhI44HSk","mcMsOAhVb8MI":"G41BllaitL8t","zcz3utmvwuxb":"hZ2p5ua0IxPP","QrAwKtRM2Gm7":"sWu00ICg2s4t","CBf8vM8QOTqR":"Br1OjUWZqjY5","LvtnQpzgREIO":"2T3IuEtIGFdp","AKnxzqOMDmGr":"FHJmM3eYOWZI","ZBEaoiXdS1BI":"sFxnqRECldCz","8Eb9PIcTms9i":"6fy7dIhZtsQv","Q8TBdgu8i0z8":"XrMRrmOkxKyk","pitCAbrtMh6X":"a62g31gK3PgJ","ZkBimw7a0uKt":"gcKbQAIxprBX","AGkRp5qtbXp6":"pyp51HQEz6oR","dwDNq3XDPby7":"nOt8vXeowILT","l7ZTRoVP01aR":"XaYTNaoXp3Kt","qsl9LvP1ZUTP":"r0to45bCM8vv","IBLQ7e2Qdm2M":"0iVdvO6zCClr","5sK6NoUOhOGh":"gzF93JDhiILj","ZN8d7qP9i6tu":"3UXldn0461pr","OOyN969mng3j":"iB1YVisXf0od","BP38lBEepJt7":"fQzC0iGQe1iO","p2v0UGlAlSPR":"685DHzwljcZL","Jrf7hichJWpn":"9UyDc3oY4gUK","YMIPM5H93ToE":"3YKfM7A2nHd1","r9tgPdgS1yQS":"pvB1gEKb7duK","dzkHQwiBU3TJ":"U32HV3JpLXQL","Fp8T7j9UWnXM":"SyafO1Hd4ZNY","VPjuHKCICqCe":"E9zoJeWoyu0G","7X3UqZb8ejgA":"LhpcST622Nod","n5XcqPAkdynE":"qnPlCU7iSCZ0","mavA9wwVmPg3":"ktcuxX4swvXJ","C8GmA0g8gBpG":"cObohYQiIbNI","sUpa4MkbZbVl":"9q0n9uK8EYoj","svRvvr9UJ3go":"KlqVK0AOpavw","OcAVqnbI1Gxw":"TNge67lCjn6q","SIjVsS8d081E":"eRbhf5BqWiU9","TaNTuDAAjeUr":"gwzMLgBhq54v","Krk87qoQUgRS":"jxkNpk44Gtk2","F4lRmziVmPVQ":"mz3SQRxoe70F","5Nze3DFXTUXD":"edt6GKz0ezjG","nYFcgVNBFwpc":"46jMkTXWkUVe","OdbGKRCj3yMv":"J444Q8Zz5YpJ","EsUippZTaMp2":"r88hHHeF9l22","ngGMFgixTnCa":"9EXOXuKCABJr","AoN50R132Jid":"yNaMi8466pCD","sYgoPYej2sPn":"vgVMgOAdHQZh","XXLhiSBVgUGi":"9XkPBkOqd24J","RSkCVwNPQ27s":"HdeWytCyycIH","H6UAV9SlZqNd":"sTdUssRyYAvs","K8u2xoc1lmH1":"PCMVBnDidmWu","3EQXGuAEfisJ":"481FC49i3e0i","lRZ2ZQxoU2O0":"Gu2XyrmLZ2WT","l5daqedviAdF":"M1U4fFdtVD6f","KUqAql9ZgoxI":"7ZXp57sRLl2Y","PRdrLLsMVwMS":"PPSumPAewESb","o7m3qjzlWzhz":"mEU1gwE9zUIW","KrR7ksQjukj0":"JEHI1oQ5TngP","WxlHKB7Kwip9":"Y2rp2lbQRRMV","DZEBplvnrB8n":"urIRVnpCmdF2","jXZWBxY3219E":"N8woQ5b3xna0","NZvPSokI3423":"cCSHt0jJfjzs","jPqfG05lsVmi":"UWZCERMeV6G0","Tj9SELn2mwx7":"2MCaIgwNZ09F","hu3cH1nTLt8t":"BC9und5CYysM","DY8ouDLGc88e":"lEIDo1BAXYNH","Tby8MyKPHNTl":"5X35TN61eWMb","6Z3kPiaNrUNS":"wjXH85PIjp37","Dy3dXP5gfH8x":"3U0um8BHGcIV","swhwdJAqzPZR":"HVPPB4yPOyN9","FwdyOgOVw0cX":"rSD7VaPH6j0f","PiExZslkOc2k":"1iZT7WCUCIXr","UUWPbW3UBz2U":"7mnVCl02neWO","JbQCmorF7aXb":"UHax31h4Jsa2","A4APRO6Dmgp6":"EVxCkZsXuHWC","H6oiLzEWVxcX":"3c39JgkGOeF5","Mysz8hAZsvwy":"dusdsCjoJuLA","GLDRivFK7Vws":"m4vH9TgSjvjb","zYGv9DBArgEq":"uNyPnD1V9ux8","fTJz3AZnWTcX":"PpliDerR3EWR","o0164REWZlsV":"2439Q8ptLAk1","evcHadm9sfrW":"haXoevovdcqM","3n7UtCV9iJJm":"0RhZDcFQEz3T","VDJpw1iZQQX4":"bB5E64pJX9Qx","TH6dW1CL8gvF":"iFdRgqJtA0IE","U1w09bjExG6S":"qn61bIwZAl2N","WidjDso9tPx1":"v3uC0KReBmRd","4XyHbjf0xGmg":"3TTuB1uBBxBQ","wLbc5JaLfFrG":"qD76M5L0Gf2j","hI2C3juyVHgX":"3YHmzdxnUUsd","lXz3wp41zFcC":"WZWjmOJhbmzA","t28IOHUeazKx":"PUnzTpzedn3Q","FapJe08Y8p7d":"moMYyR1m4gQc","4Hrr0ZtrFpZU":"8a2UgPTPYm3i","U4NGvZmd6MIM":"xDqWLXUdaPsG","0Zm6tG8Yp6wA":"tsNSR79lbnbs","EcLCLJosYLVl":"opXxnDurWbiI","CtqTKKRCJNb1":"TsQ9d5xMDYWO","AUbgJggVUiDW":"ibms6xNfY9n1","YKcsIxfRRjjF":"mNoCnIaTKFDj","zBoyxbfEqPsG":"MQVo8AkIQD3c","9DcjmHEttvys":"7p7751dmsTHi","7zRjpl9bc2DW":"y0FSi4SjjDwj","rf6LVMaWuVPz":"uxQmMtnKgsIv","uJAijdr1pYSf":"PW9Hn5sxulbv","VeUxvkbhFgeQ":"ZMzgmcLUAnjl","7FOLtJJNoWfc":"RVDHGQBHY3od","nryQjfZzIXvn":"tofdV4PsvF8H","8yKOBlPla6fB":"ZBoiO0XAq5sb","vtXOvavwrwY8":"gZJNie6s3T2N","SObqtQ7oAxLG":"IWTB2RnItinN","6EC7j4nrKSZQ":"GLnNk0fMbUwF","3D3rrIx6DFGF":"KEQeHh9mK9C6","kL48e8VTgmD4":"vywTB9LnD7lw","gRxVdGQa1WeJ":"bPVzKhRoCfcD","RiR41d8WTcNK":"A3XFpPzwwz2C","nuZFIIB1JD5B":"0xsBHFUVbFTt","n55RyyCSeSgC":"l0jHEdTHWEaa","B1JgtjPKvsG1":"27m6gMrOJsEh","RPUTi0nf2wrp":"1ANf5oNHqA9I","ejItaGKyKlz9":"JSKTZi2Pymze","PNd2d7kUdy7m":"waIIZ8wAa3my","HwNF5fVE6H4g":"KnudKAIeIVXO","cfweVCKP08wP":"ktIcNdfFWJDk","PSztIak1Sk3h":"UxMsY9kknv2o","E0h4fpPtnUdG":"v9o6JpO8l4is","kJYRh0DY4CFS":"OdyUFD3qpOD5","G6nFxVbLof0G":"Ykd1baqGZAS2","Z4f4FcaQ8Xrt":"oztfZqNYiAuQ","AC6D2wMOoiHw":"5eydLm5uCt8R","adQiFGFWbK1Z":"yU3LPLoyBOA3","Qi1E0JpG7gid":"6dPSdgjucBKQ","KoqjUuOmExsK":"TFfqhT82bP37","PgeLGdAcf4YW":"fXCjbAKFbaIl","AeFyaYqI5T83":"QV7YrtoQPk5t","rDaxDC6vcWru":"RVOZQ7W8RVwp","OV5TSVMROy0B":"tLyTYjazxSXk","aM1cNx0vfPoZ":"DRpAmHNLBMUe","GapU6srwTQRe":"UIQ9bc4ViNAk","Mkyww8sy3E9h":"csE6XDxxM6wO","UOGhV8jz9lOv":"aGFY2e67ZpTc","l8qxxRqRTOLn":"15ATLZDxur0T","ZKoJsnJtKCp9":"2FU5gk6VJ6e2","kVgaZMz7LBTt":"4pZhZWpmHFGO","MAuVXlz0rHBB":"PG9pssAY6FcE","TO1Vsn5AZ3mh":"N5XzC3ieWV1F","1RUScoOxKgSt":"qGy3tYmJsZic","lvGUGHAbpYyI":"l5wQs5Y5VPxi","OzExr1I0bGlm":"0bomjEPISNPo","F2AZpxEmeqhI":"Wixs2Oqo7yab","7ANxh5Qwej5a":"JKRhsyT65Gvr","Tvr8vcKpsRlH":"YYtQEwDoKtqU","rElcsMxczgUC":"UTJobhSVRQhy","WgXASIrXugqD":"qEdCNvgtuR9R","mzAZLGHqJBQ0":"LtojGsSUwLnI","gYBpHjsW3sku":"I1JTe5KBmnxQ","hCm25ZpgvUmJ":"uQjPTej4EK9J","lgyYRLgY3vZl":"ERsIEnXeMitp","CVyJEAAQVdFf":"x6G79hEWmSXZ","X7NBiez6K3JR":"312qCmnZgr5u","dyOrADj10fzn":"nIEVjlYyeqM4","lTXJKfZHBNVk":"tiLzUbE0c0to","75c4NZDOHAjE":"tkOpI6pZGyTf","NU6PmWfBsNnD":"t9MqKRoNFcjr","rtZr2C1AbBgz":"UKFOKSHoSXVW","4LNoclMTfobR":"jOnUbj4s7sqF","PbYBmWSborZ7":"t2ea7jbiLvAq","or0wbWk82mV8":"uixp2TLOiW27","hukGIgQMaLBF":"SPWcRRLfklxG","WyvEj5XVUpur":"tUTApl19DIjk","2bj55FJlE8MC":"uXFtfPz8RoLv","ITbLHiyw6Z0l":"bAMZIljwQEJ1","m2MAiy4yI5QF":"OEEfWV0w4T0c","ocNKmG6nGWKp":"at3350Mz0W5q","EXuBurI1UbLw":"cl21jxymVJv8","eE8LdsTSIjSy":"nQku7hTHUxX5","T9VOupIUhVdm":"BV9UCCBYXJjD","al8GHBfUmny4":"AldUQrOkmJ2y","EPSyzsqPgQcX":"6iHvRHDCl2pf","2OvWF35lx0vL":"875BaItJ1WA8","hQaGSOLQciYJ":"dqPTowO3VcOC","NzyAymBCHBvv":"5AREhkbNeK54","77hwrGrhK3Ya":"Ys51ddFbA6wG","A0YFxwI5yrCN":"ckc0HAAL4Obg","hAdtdSRVfh6S":"bCkyYlpFkdN2","fY1AIZJKeSBJ":"rvtFeiJFo8x2","GtczlRxAxyiW":"TMdB4OBtuZ3X","q5Ierxri1AFU":"owbtb31KthAQ","2YJViJAkTsNV":"h0Yu3hBmhe14","lIwFV9O3naL1":"LNDdELrL6Oz9","bsdNdU72FiUF":"SkauhKPSY5gF","3Hn9S062xjku":"DflEHin19iEy","76445z9QM4YE":"P8r70erAAa6s","kxWOZ8mFVfzF":"rEUvcofenyiL","j7f0pmOX7W3c":"25Oc6bjhonpn","PgfDAPSglpFY":"9ZTmbv8hQOz3","AhnkETEtqXKt":"PmJQLUznHXZH","uAMukzHoPhjK":"HEsn87Ek1zgQ","rXqariy5iK2P":"70FRvld1dsUK","TkVfeYQDF86x":"fC5SFqmw2ikH","AQ7GWwlbE3yQ":"Au6AUoKIWXU0","kwforyk3BOdE":"AU4FmX2tFY6K","Bpah9J06DApS":"OTf796o6GM7O","cgseozyGjRZ4":"ydJnVulYnIQu","aRxpLVl7qGem":"Hf792JmMdFCd","YQiDvlOBnQWW":"l9mXlMTSp0g5","r2ntcT4gZJ1O":"Vand9SkZtFwK","vbrSQwdKMAyf":"bGnlLUhdvJJa","AweQ0XNd19xk":"Fo8pAQv1Z5Gj","YHMEh96zYqxq":"vPNvoyNKSPlh","F6dEOeytj2Vm":"ddJIwpk3Vlq6","GJLCC1TdtoBl":"TdU0A1zOoYvs","69fgnAZcy9gc":"JoGvhaGZPlOS","aA2mYBGEPFTK":"aOLVkTq4MHQ9","n1BiE5qlvwZu":"TCV5vSsPsXPz","WCm45PkWB9C1":"rHGjIDR3xfBQ","csYrlejdAKzA":"xaka8L4n1tGQ","cu4BSUfUaZYk":"yK4vstzYknSj","m7jcUdipNvZE":"rl38eBXK44sy","W8WV0IoQH2kZ":"CkLOWTMoVJA0","6CDsVLQ4thuA":"AjzQaAjF7gOD","f17PvVPfrihh":"GqRsc2xQiOfl","MqT5O2J8oarM":"qHgeYR0w8A0m","8V4oKrSxLuPP":"7VMAuc7AZ6mZ","7ppjLZ6cwuep":"l82uqxdOzOKg","S6FfMQAaqrF5":"TppHHFFOhv1J","4RqE7tyNrNiG":"WpEGn0Oa5Pad","5nA74tuv67xY":"qIfP9MX5OPHN","5kz0U5NFlEOV":"luvQHYPstFe5","1JiLH3VmR2NL":"4tO5fBTmvhbL","FAXgnuayLENE":"hSkyLBSevG5l","XOn2uWM7zI4l":"WIH5oYChqbTe","XcZBnQurdYz8":"1EpR6b3Q0YGp","4KZnxfVtwbYk":"H0nZhpXQufdm","fiPkknepONnL":"L0Vq3HslOdPj","30IUseKAvzOA":"ZegRF8DpOzqT","27a4LlBm8pVH":"x3WVCAZGtkQE","6QpTdsk0oKo6":"CqrvFbNYeSJZ","XL0R3IDjNjwL":"xOQlpAWfpwee","Lm87vzVy6jpW":"vEOQFFB4JkvR","NhjAva2GPmea":"8hVrXvlsJ3xp","DQO7kIeWSu54":"FR37tp4ogAJw","Bo89Vz9YhlX9":"0MaMSmJO37LN","Kbp6rAWhAmog":"kYfAjZmvrjVl","NesjFDVML5Zc":"2oORncaTOOM1","nZwlJ8mfISjG":"DQusZOTnF4xF","dymUIAilgYI2":"nsbGHOsPzTiN","YWCcfmy1CQGu":"BKwiSpy58Yci","N107W8Zzspr0":"T397i3qMHAql","WvTeFcAyUKZh":"PpPJQK1HNluU","ljlamHEViTo2":"Oik9zr60imLL","M9M5zj2NtUky":"AKkRhV5xlpgN","jCwsZYUldxpO":"gF4cpyzrgnRd","cghEKwJNw868":"MJ9pbGneK12R","2E7rKANwB513":"R3iiql40j44N","qqiNDYK9IOpU":"g39yk4x6XEZR","G8Q8aQ3hf97v":"X5ocQYErfaQZ","RjlVDbPlpIUs":"MgPXy0I7kZp1","mUXQkxKWiZoH":"mpeNjvmpZvdk","VRLq5uc6fspS":"UzDFUohJkyKc","5Nyi535nHrAk":"OSrnWxnJ11tu","NxLyjssXWY3m":"bz3BM2YGeTrw","9sKI4tmJbHGy":"IRwQW5bw2Vig","DB3ZwymHj1Eo":"v2qDEQVcReoc","DtXdBGP6ZLuH":"w82kjEPXBtNw","YW1rryhlizEY":"zdzWlNth9MQ4","XHQEtwO2YE7w":"CpIDny7JqMD2","pxryjMF7Zc4A":"c6r9IZucsIsD","RJweh0MSQjDz":"gX46cPwBPTed","RHn2nB6SXcWY":"njtaTABtewAt","c5tukUANEJVx":"dkjoDjW0jvKS","z10Lmo4FHqoJ":"bn1DWNHT7gBh","cEEoSCjicQhS":"HOIpqovX0CXV","u5wsdLv5uNOJ":"uXREgnWzTGks","nZFr2EZ9GIpQ":"kXtptbwoJZRH","o3S6kox4VhLC":"7acQm33H0Fmt","HE45QTrVfqYJ":"CZoaMD5okihk","2xTI5KXko83L":"Z8piFcxT6i9r","yo74Wg8oAZow":"falyGAfvMM7a","h1kcHgGPnUrw":"c6HEfXHUYYJ8","lUVdMPZ268lx":"3IkVQf7DLMST","CWvLw86u4Ho8":"0Eg3JomxHzD1","eR5sPxJMOpgB":"nxf1A4XpxTKS","DxrhxvKGTR3s":"mEFa1Rev6TwF","yCKXk9WKSl6E":"wPV0e7Uqj3Fc","XI0uqWXCJMRK":"TVSKTI5Wt6QK","aSNrAd4f0xkJ":"ZOsasBpa55Hf","PDRPdOz5bZb0":"nCVSyxjxD53j","A8XPWf4pSNw6":"mH6Qgj6FlH0O","p1St7zBAfDzb":"bk2XHOyS8wte","U4QcCMRTGLcf":"OBgpHoxC1qku","i4Oi4Y2QSeyA":"Zs4iEqtWzJvU","ML2P379rcW5c":"B3s9NsEUOqTc","cOJjdHpqayLN":"4rA5eCt3Ci3O","EWkrpJKlCnyv":"oVL1mjvLn5VT","92C0t4x2JcgQ":"hvqzhvKEtFyU","hgJMCLnKe7nt":"mgO5wACbMyZx","BhLiKFzowL91":"svMYf9r4cCqt","Fku7dP1bwQJV":"lMPFBgq150kq","Qwq64u516QIH":"S027UcVMRRPY","r2FbEreL2Llc":"IibYHKOTk7So","7c9dAcv2Zyqb":"UGmhoynVGNv7","678gCGSl5I4G":"OBRivQLaw8QY","j2SBydcCLvFr":"hYgI9UbeHRso","w0k5z6EDWUT1":"zySgnHIFxXDc","HE6ckyvSmcnW":"XVZOQTiQ0QzK","y5LcBVwxBG11":"calSazejxPHL","u5hGkRpijbvU":"IgiHPbbegf9T","2KQTR3E14NW3":"sK5DZPDYvG1s","jBBbgjdo7nxN":"uz8FTCFI6FuR","T1YFxNnN0jYF":"mz0N0etesW0t","E8petXd3MXOa":"OYaasMbSXNR4","zZzxPWdct59X":"5i3mKk5k1Ef3","NziFiafuH9qK":"43JvRH8Cf8wt","pvyQid6glGEg":"toVjt5dTobEM","tPgvaWwNx0VB":"NwwmnlrGDh4b","o0L6hof3ntcA":"fAfwSxyUfVle","oulKUiV29lyl":"7qLUllh38ACj","z1Yra6RIhGf2":"d3owgTECCoYR","xPTIbJAoI9s9":"i1mEKYSwUOOk","u9VKoiVSgGhB":"PcAhYIn9LBH8","SbLvKMXLXhuk":"J7TtX0dTdt13","SGxqtxlYFNIi":"r7HPP8I2JvFw","zeIaVLaYccRI":"KJP9kbpN2mpR","NpsoMEDlX8V9":"JYsROBcYTABP","aXyXEGBZdg2t":"edkmbmJwzlRg","d3aJ0i9cRJLP":"jHGqTrj3htl2","lWEMRo7xlHNd":"GoGmCdmWrnpF","XZrgHpFWZSKO":"whQMjXxoN2Xp","OCJwJcwF7w0I":"28MH7OWyc1yn","rs5tXijam9nF":"9DEHLKGf6LV3","19cMZs7l4J60":"RwcTn2poSFRv","ghkjN42uBUzU":"5NPFqYCGDjm1","ItuG0x7oulpc":"wmbqH59VAcCO","ZEY52dnyvj9f":"D00v3es7TE9t","LBrScJDzsprd":"1yaUcynhybPW","URf4hBfb8vwf":"THnzzpo21ipf","16jzzzQP8y64":"I9hrmTnqNFOI","b4ErCjvqToFh":"DaP0B2YJxogF","OoMPuWIUdj9g":"1NK6AVQUzZdf","KXl9P9tii0rn":"iRSoEm0SLc7t","nYTdCdZq62zN":"Mi2OsjnVUeWI","L9yqYB58kVte":"r5tuyV0BTDKI","ErZfuRJduMKT":"BihKjhrx77An","rlxhqaJyKFKy":"P9QjuA55hBlS","xsnlbNzOVbyQ":"YgcSXWcqurm9","lIvP243uqlO4":"BRsueXZHWWuh","z7SSRbjmVans":"rjc9Vokb4TR4","d8rERBiVcJxH":"PW3hOiB5l0K8","Ab7YDi7JMsMM":"snTzrXSYd4XT","V1mbLPlVQTBi":"06bueYM0K7F0","p5oRdK5JjxHU":"k28uzR9zYFQu","GO9O6EEB2TBK":"0ZO8mBTirRyF","liiDchQZwlWV":"N6whUZnDPvYM","9jvNPyXdEVfj":"al5F5H5LEMuF","pUdpgdiEHVTx":"ddIRHyhveITm","paDkOdWx2Ykk":"dxk0MmuDLgzB","8g5TU9fgdZVc":"PDdYkQpSpnWj","oBgMKby8dIgy":"usxYsjeoRJIa","5f2An0MTxE5P":"LoRheess2UJo","HgIzn2J46nYq":"yBTxal2mRTGN","wbQv5wWjH9IF":"ub7nZ1ffmIdE","XUhliZm8rGug":"OMPmxJsUHVNh","NCkQwfp8uOPh":"HjzPGnJgUPGN","javmfCVFRAm4":"GvrEV7up007Y","qdCaWUP7i2kV":"O58yUBI2Pl1Z","nHJVHTfqsoX8":"GHiFOhwmAcLr","U5qEbZkLDyQv":"1VAzKoSBdm3O","MXon14akhswv":"r7fRfXoYYvf9","Zj7lKK1f2HYv":"IzBngNnuRpH0","hfePLNO8ZNdl":"yjxyO0MLJVNC","npJhCu4zRAxg":"0FLZ9KIp2yni","efSPTMogHi5o":"edrcvIy5pDPv","AweH8cYpJI6T":"IoOOpWl1GYDJ","qUyl1xNZnuwb":"RI8YmniLKAUZ","6YHIi3Qftnaw":"beLhY78V2Lda","72dL2GObGgw3":"7INe4Vhuyx1w","LnzFoWBAe1cJ":"MfuEH0xTTCKK","Pb5DkbbcGb7v":"znntvODG5c5K","2BK7AnxNUdpv":"NkisY6AQ5QaR","JK9qVzUjoXMN":"RMBgu5QWNiqc","u2lEr84NjLh3":"JnL2vfRodGsy","wrrBuYY7tac2":"8WyVPppLc9vp","5sEdbmanGu8g":"qFowjF9ZtCdZ","EW95F9vM721s":"Xrr9sfSDT2DA","ch96yzbpUZHV":"V9ZxyRxbmNPP","rqsJ5rwxwBEt":"owWk2yC5RQ56","ah7ZHKxsO0BF":"aiTUYJ1sJObg","xmU0odD39mLK":"sTs6yVzbrkSq","3Vzq7uoCGxRE":"NJHVY5xwAIoR","S629x2LatqwS":"dKXYXKW8665f","fDmNP1ynhSO0":"4BEi8bsfyHXS","jDnkRnLBPBvt":"fTwQqlTws6PU","EMeAf9s1jACv":"1Kw1AazjKABt","8uk6irHUwDEU":"sytDVRbXEpvF","mtsk1bykR7de":"1BhAvUpPBumT","vBKi2xfzao7V":"6HU4MMV1EXwl","p32dHQUOkk3Q":"oKDNmETSEO3d","4T1imwef84sk":"U5Ub529gsCpS","T91kPiXbYspd":"ZGUGeMpiJqzj","1MUBAWC1CLGj":"AFvT8AMZO4nf","uRtyc2gW8Fbq":"WkROvep8xEEy","8ZxrFtClDusH":"S7RTUMYGPOHd","KoezUOjr3fuj":"mKISKJKARLnl","ZZeWwe6a3877":"lWdZI7xYeuqi","xX3CWnj0QKHA":"5YtwkhqOn39r","4V8XvOaFmtpX":"qn6xtxkHizkL","knfjAnjZzooe":"YpH6C0f966h8","IolZGhxCKaYO":"62eTbCZs5J8y","FLfyKKtxAoa8":"93kxh8Vm3bWT","Wb6apWVee1D6":"qPehoPgl0SwI","UYRfWKf74smP":"uOTyfSvPTPGf","MU6o5az8amhW":"jPdp6ex3xsIN","Q4QrWCYld09o":"caiUyjwHilD6","IFdGaK3dLk28":"JFuuO4fRKp2N","Lnbv3rZzEbjq":"WcCc15T9rPjQ","02ySUrnL8eb6":"U79Lhqx2btNI","eWo0iev2v6xO":"lXVPVzSWogFL","1GTYtRSfqCdw":"X3e2zlIRu4WE","9OLgcXowLX9a":"lJJcgUCQxinZ","2hY2lsC747Eu":"dtTiwIt8Pl7b","FzxcO15Lw4bl":"MTTMiwlCeKES","D4lXrIhbtyUb":"Qv9FJi7krP6B","LhJp3QBYTbmh":"HXjlTZqWdUi4","UsjmTi8AtzHa":"9VrBcNrcTszX","Yzhc4yW3soNd":"hXzpoJkfeOlJ","6itO2uEnlQof":"zxH7momnAvmN","GncaSQiHCeFU":"UZ2sEPIdiP6W","RcFGwVXyoXvo":"7Va2b9js98Gb","jTNhBvHCbuCt":"he0z6pR1yYGP","wpV9ibG18Py2":"lwYiMWaQksKA","wBch75fmT3eY":"w7Hyx8ItWHhP","QzjPozJov4pp":"CAMd2nxenSuI","6yVabKW16R4Y":"78bN4w0v2ULd","HKd3g22zIRra":"IcG1cCVPdgeo","lXfH1n7vJC7T":"8HNlbDIyNlb7","TOQ32RKRG5oF":"JHnpIJGif7wJ","hoCe4HwAQmWi":"WpSU3ekSDMNe","oxqOvKd5UDdd":"JNrKnMpdRBLR","IZaX7KlDziSU":"ClfUrGofBpnp","5gk4jIxjw1jF":"GPRZmivHj4P0","YduIXv4t7kWW":"89MBN7K7NB8I","tsr9azsi8vB7":"wT0RkCd4xwkU","1kGeHhW4Cvtq":"4Sgg0P72vLeR","akoYuKajDTiC":"BcxIhxDCJsDo","X7bl4w0HvESW":"gYgkJ3AgqOKX","c3GYXXr6MctZ":"L0ChDuQsTzyP","IRqHlsMtyoGI":"zkoVNuqsHiPL","y9fXr9jTLhNl":"ic9kcQ0xoM5Z","F3P8LE2qfpb5":"rhOaixpsyFKO","6TEbYHBaY3Ab":"sVYtb3UuZvDs","OBV7XGFUeSzZ":"bZfgrFtBhoCe","cLqvssZmfGLX":"k1eGdlS3w9z7","OP8Sc7lF3lGe":"CuvaBgHDsHhB","Z2q5kvVGb64y":"TpWtQzeHSrN8","S3YDKX9eU4my":"IpKJKuoCYXLb","MNgzvqgIoWwc":"obniQ6WP8SF8","g1cTGkmFveG3":"SPo5yBwV9zyn","XM1nCJvalfh3":"iXLr0r8FzaoM","wqxjnHIkeoL0":"SYLt9N3FKMBs","FVjd1CpeZxqp":"yBISqu8FKGED","ikOBWmvWBXI7":"10DhqbXrbXQV","3Z8QH91ST1J5":"ZEipREjTGbGs","4mdprORW7JTO":"dxrXqWycqgBU","yXEUT1z4YC5b":"67OpmnHFgB9p","0vpd5Ql5y2gK":"wKzIiVhT9IlO","zIQzriPb20fH":"90J7FKnyz3zI","urH37G4dt32j":"cwqkb4GMrSK3","3zaM9JOS9BuO":"8xjdDYSYjQn4","mXOSTMuYNdSm":"8F0M0MuvAVup","DMXN6sunzYM0":"hysyvMbf4Pvw","wMD09yHmuSun":"EVufvS7kJEKv","SQupLp8ggE9U":"IFV6jE4mQjkl","9rskp60RBmkV":"h4OLSeetotrZ","CmngIZ6LWwo7":"DeggcQkN4Ols","kercvG8AEtxi":"FcE1b0XKc0Hh","55de2R7wHdcR":"89tpszb8YtVH","cHoEFwo75vrg":"QV2qwI9zF6KH","goqSqA6QkU1S":"OrQg1bNVYD1s","yI00V7E8nLue":"ztLWcJJ3Y88N","6HIuCWBFZM0O":"0y4twYJppKcL","ZliosGopeCde":"2a1agQdi5KNj","1ihpB90HPH4G":"zJcJyZI2ww0c","Ycy6IFsVgcxp":"lUAHo27ZQAg7","WbgOxZB7Zqlx":"hvtAxsWZnOxi","XJbNQtspLEQn":"aGMaW9liqh30","1ybF9yWdJ4WW":"IRT9yfleWcgy","nkM9umEVtvmf":"O5vc6DLh9hKH","zPqntCdGOubD":"fWKrf4Nk3NPl","bXG9IyjjkLxV":"qeDvRWbnbDwR","FicQUZxwzSow":"XhAaCg1D5O9c","4JUINRQsb0wG":"CetT2DOW9gUL","or6KFplxWndZ":"kpHijmJANE8A","JzOyAY9hjNSv":"LGpNx3tfOsMy","4crzJbKXFeOd":"US6gb0UfS6DA","U1lKJmm11B8a":"mh5aqbJnoVef","m0b6ppTStl8S":"jMFTCFuc1xbS","5HbGvO2KNFHq":"HGurMcltkNJ6","kb056j8S30tO":"jRcOloP0AXdt","Af4Da3ChaN4c":"66tJxb8gq3Ju","TkOtxz1C5pMw":"NfpQWm2RiVqo","PIra0Zi7LACS":"RkE6mgrY5mHR","qsgW50wh7cKx":"wcADUQsQUbxI","7XP4hVStxtYm":"Q6hbQmO0Grkc","LFklly00mc0n":"cF7cD8iNfsbS","Y2r34Jzh7yTm":"CpIwqxlT4Pz0","z47Nb0twoTT0":"XO9ot16IWxJe","SL1zkCIl22bA":"5mqWFyGMjnO3","b0rFehYruBqe":"mUZm0yLuXdvU","vBgzLFHvDFdl":"GhhlefjwkijL","2uKtspsUle3h":"K5KUjmqMgrUR","nWFBcAVSwMu2":"quzEHGdh29GP","MS5EMqYXLzbS":"XxywJKuyWXrJ","9w8HpAbUL9UH":"awJDeic4rNfL","u5YXRMkNffXI":"Pot0DsAVprB2","lAVdA45nWblZ":"PxS6tgud57lx","Y2hdQYLwMxFk":"z9hUi8Vk2rQD","FXw9BO8dDU23":"w3H0asw9uLhD","rgApMBPi3DOs":"AUteA9DgSEN0","rqavMZLnH3Zr":"aDGf9GMY8E6p","a7dAftTdjnRV":"23cRaE3VYHER","fJcWOqTl8X0E":"OZv5vyF1WJTs","OPVsvMwEoekF":"XSO0PRk6VEEH","QUeQ3DulqKI4":"IxpVOohaERQM","RqAoF78Wk4Cn":"kRJY83B90XWR","zGTlRuaaoMPH":"vdSO9GoypMCG","BRwhZAL4VyEJ":"b6mW7BEwW35M","0A6mu8OfcqUc":"oauAxql8dh0K","RxKkGISSj6ej":"63oQ7zaLtTGs","QtU9v63KkVR3":"q4nCtav0BqlD","3uBv1EpwOqTo":"g3XOvGYzd16Z","9FhIVP1Ihh09":"CDJQBYMaWfEk","tcGdnaAtR5FK":"remmbke7ovvS","HGODtS0eRTJk":"1vvrBLTvEUHZ","JWd1VuRXHUAd":"LvfQ217kJL5r","NVe9F8dW5gJp":"zKJNOuJ1Pk7v","7PHbdEiEKMF5":"n4mq9gV3aEro","pVebcyVfd8hr":"Npbtmt8m4zP8","D5XEa2mbFOO6":"1BAYchQX0GM4","qhCKcjO9bZIn":"Gk82fQ6eunk5","fDGVCVGJzRU9":"oufU20az0jz9","e0zhpRDAp05r":"J2hzBwV2bBgK","fbt1L8Qwvtri":"MZm0BQSxkrqk","2RyuKQljLLPd":"pdcK2sfQXSQG","edvOmZYK0kTo":"uIvocqc7Oyxc","xWkQHt8XJw41":"lLRYDBP2Ofea","rgdOpaofczgj":"C5K6hA3NYT5s","Jm7I0piqpXHR":"crcn0M1f2z1a","Zf8nGZyO1hwN":"LxJxNw3YK3qb","kXKbSJb4OVBF":"CfnEbFvFhmPX","Or7AyaF5cX2S":"1v1g4ESbyo03","WUOGyrwTooo2":"YiOn0JlnQisJ","T8lCW62fsCYL":"ZgF8nlXq0T60","j1BP1147EOJb":"hj4qZlVob6JV","p2I07D52GgD8":"XR45tJUu82LB","CSKqbAdTccXo":"Hq2i1F39CXAt","RRsucYIAoldc":"mnH0hdmGaexr","MOv8qmQIiiOT":"962q8MXFWhpm","zzhRatFRxRlT":"ASAnwlNJpISA","CHnImokMF6gV":"itrwhBbPxGgD","YqbU6F45smjH":"OoRuT4FeT8Bd","3Ufmox26tMg0":"C37aDUTV9d3k","cBcHfpHrzqsj":"MpC43y5HsfBA","D4fFiod9sieJ":"L1ANhyHEBYYb","w0OC1ZAf959V":"9VCzW5Fo0iOV","vhUCobDE1MEo":"3esRlgHz8Pjh","cUwx27zFwkF3":"FpKPooHb4SnC","OUJVhElkqdCC":"KZvJGZvDKU77","Xgthuor5SZMq":"dTt7fVJfEpgk","Wpbv2sjwe090":"quhtAPYyCWuz","UpMOQkcqokfO":"PR8Xz6hMaUj3","fbL6u3jY78UX":"IUezHUEGP5LD","BFunE1gavfQG":"4KTWrujiHHPh","b3BY6ZOK1Lbp":"esYkCCyTcqRY","3NQ5BzKomJ0E":"wlTN2rKGApMn","KuxsUkoSXDdj":"hMbm0hzp27Jy","piZhvo2fe9ow":"QNewkX7WUBIE","Cv0LGSBqrHYp":"9Gplbm2RuX3V","SNriOikoKfmb":"ORqwJrniHMNP","AFrCaimyWOdt":"W1w9VTXh2v9i","wDa2TYpEk9H2":"3yawfKGkXNtT","wX9ItWSNUdkM":"bnKbYxLwAjSW","lxTpMsmav81L":"G7hgXJozl6Vr","BmFfznMUE2z4":"tRxlapL9Gc5u","YBYudW9DwSkp":"mSK2ILTKJC6Z","O7tRwAqCDTEJ":"CUyEX5i5U0hT","WnQGik1Pr63q":"kXRn2UVFKmMx","PUDci1NWH8Zu":"P3Joz4Myh0zc","15OEnklXNzOb":"le9xAMdHG6vU","tHQ47KEmc8md":"FwsnnbHW44eG","0xSTwK2Q5SpK":"8CtiqdSBhqNn","Y4FB9GwsNI2h":"dFy9UU5oool6","dMuQOdVS2XBZ":"za5ouj5pzaY6","yF6E2M4aBOCJ":"elYfBRF7nhih","8NHNV0SmJAAH":"vwM8qdsuc2h3","elGpIGbBxdcv":"P7nv2mrALuyi","GMEkWMGgmz4M":"19SsCPMhgIlC","lF32RjhEJgJm":"Yk3hiCxY9IGW","iXVVBJF0u5dF":"BEKqYBYVrMgI","TiQkVeF1tqHD":"w6c6zxI04l1T","JM26fKue51dJ":"O52VWavzy1az","ilUJTzvgK6tB":"BeSId3oDGB1r","ifxoYrx9vBik":"cIkuZhQDtD2a","YDIbcy7ucgJC":"gHVFa9VFfmJN","uvIhkXQvL09s":"MkadxoDuHhiJ","KHKYLeX3RCAV":"zx9zBeOfafvN","27Vy0vru4QiD":"4olzn5vLGDrg","1lFzDLBbzMlb":"a4k4lkILs4Y8","n4GyBD7cxqaW":"NTLSWqC779ZF","MmzC7awEnAJs":"BU7dniA4AYvz","fZiawX0vWybU":"mtUDCw66EEha","gZa5Rkyh3OCb":"23RWIe9fTysV","b0ZHhV2CuSuO":"38SNddtZTs8e","ccSvYhrMTMDp":"1znFMgBoR6DW","ZJJT3T5SVgjV":"WZAy8gwaOp7K","zOMwlJePKTrh":"3gi3H5KgFdut","L9sPxCsIAzfr":"8V755BoOy123","Ne4x8Xbpjs5g":"VmyKr8HZanAK","vyxQzRK8Ahfl":"x5JdOQvHlGh6","1AzlWfgJsTJX":"TmMgfEA7A5z2","n2smZmf57Y3y":"yNbRHZaJHXQ9","Nh7ufsflBybJ":"x6L7qUDsiwId","m3l2jBLHOpjd":"bDBr4INTJYHi","gQPzth31ViqR":"4x1ZtnB1MlEY","RFAYd3Sqacme":"fZW6c5MTdHrp","dp6g3eKtoRyL":"TFZXe5omMl8i","3PGx6heD9bhE":"kAaBgWfV99rj","eQ8foFJfOtmj":"FLGaGtjuvWJy","ab0adbQApKTm":"RPUlcI6HQA3K","TsEh079sKeTL":"8xm5KVbM8yMs","HrxlYCYfZMmQ":"47qt7OGQVaXI","EWndMYlR2Bno":"aEJCFe7iJmwk","JcPpUl5nIRiR":"eRr53R4u7QFQ","Uole6hWgw1L1":"z2sb65HMxiUy","AtwoRP7DK8z3":"NZ2ioJ3iETVb","TCaEhpR8JVuh":"RTagCjSMAQfI","WhMkoC1O5kko":"Dx1gXZVDxde4","SPpGKmINuWP9":"xvlFU8z5ioBf","wRhSUzyCtjPv":"YYQV6X3NVQUS","1nAtqib9cxqJ":"CrO9Ln3fWKoJ","GkFr1Dv2KqcL":"thJszuGQa2jV","dJu51VL4LwPy":"ktqboNWscxqr","Htj7jcz1MYmt":"VtBVfzZ9PbNt","LD9euwyMdapf":"AWjKwnvtVcQE","tvm8x02esYSH":"94ViIGeTi9DW","aQKKcwqNSPs6":"uB0e5P6Tsrst","buIddQEScjuK":"GCifJ9YzbQt0","Pqi3QBCLuyuW":"h2T9hpjyg8WG","MiGQLPO3jNXi":"1ta4bcKboanK","NEVvepOhtjqY":"AQMU3qYGiS8l","Y7pI47xAxFT0":"ugAAGRCMWEBp","af2Cfxx09CZg":"ToGzDuJH6DJp","QdUnSojIcKK9":"701ujmqn8Zrp","ES2sGp9PN39m":"6GrZRfA6NR4G","rGgFWHj3CVGO":"NzU92abpHMm6","0IWg8frPQLYG":"ZsTsriq5N7Nc","jxtaBosev4Us":"sVvjHGwppqHr","coE9gXe6cwNF":"iXOUe2auoj1I","J7ULJWxwjpBO":"310NDIstZdvX","obRZHNqeZljL":"tqXBoZ9ESEjQ","WqVPy5lL15Ok":"22JD1OZfgp52","Z81uVN5HAuXq":"o5FDuyo49GYI","12MJBW266NtG":"mJbAjO502kY1","PPQ6xJA0pMbG":"XJBfkhK5aIya","XaLdT67udaxB":"2iKcKMVXFEWu","XoEYkQUto1Bu":"Wfb7NcqnFrO6","uHN10HVooZ3b":"TWQWshMywkKP","JIGMadKlatuU":"XKqSZSJXiNsY","EQT6wvjZCTGz":"LQosnbUtzBlo","bcmR907LMjq9":"R4XfLGoyi5Nl","DAtMJyyb1LgU":"iOyc3qf7wYD7","lZ0hIhCalTW6":"8YWuv3SFOYBT","XWlbuc7xRmDI":"sMApd3mZ0rwb","hoUPntB0jOih":"ZDaacRq2ke8T","PD6BBuUJVXvE":"3k4n1lZYC5tQ","CYyivvi4Snz4":"X7uMpnEF5fhp","cNHQ3NPqEUJT":"LuKqtJ80fPhz","3Sk1X0Cba6Ri":"TFTaPE2rrHu9","Qpgtwxzafoqm":"7Nyyx9p6ZV0v","SP5GLo81LwWX":"jBGkr8nexbvb","XJJnXbEHVr6Z":"o2kG0wjuQkHi","QiX0XH2iJd12":"t5LIFi1ZFTLs","FZElOL9jDFgF":"5DbBqKxJ4o6J","ybeYGJvOExTq":"xxDIRCKTtQIx","tzO4N2e62jMF":"VgPxklGJOdt0","XZNV63v0QEl6":"bJv9tDvWnQZb","dwzRRUq1w8Je":"Wrw71TPesstd","QI3PH03VfDDv":"msn5bfCxXWB1","Ht0iWAydkh2g":"lkQjBn7Sddkm","jM6awrnq9TXj":"uWW5VTUYlGqG","OZc2lIivC4MS":"LWLD5dYYmTWS","ysERecOCIu9U":"OqvgC98wzIMz","Cd8qn8ClaJTU":"HsWupmHObyJJ","dADCRpGJ2aY7":"mdXqBU9rwzT3","O0OlqVqnQ89r":"TTnArvnJYCW1","rhXcADaDcjr3":"WgzDAOe5q8VZ","lPeas5oTtLyp":"8PkWVhmkapBj","j3hJ8fCYv6s5":"Nd86NjAiJgxg","1AleUfMeLqvb":"XGKPqHwttpGE","8GU2EcDA8ZIg":"N8Vpw47x2jOf","VZwAvB29pFOM":"KezD9jFfYwFI","aaIAJnpHkEtz":"MFPzCSCGgOUQ","cHWKXsWB654p":"rXFPKyg8WIew","Mn5QvgXm4wsE":"S1q7QStUQBWg","rRnhblOswiJd":"Kp180AIzsEPI","A2I2oUltXsFx":"8Hbc6pFxFDrx","9GQ8NoPj6ipY":"uQfWW0o2pLbz","AwnMXJAHASgR":"34SRiWreD9PZ","4zjTwDoMgMdo":"7XOy2OdSbrGg","4P6y5H6gHL62":"BxNZsvL7UliD","Zln8Qnq0A9eq":"GSULd7oyJ2TR","8S8CqkXLz7xf":"OJ2fWfRWITQi","e7279b31I0eC":"km3AqFYVD0kV","nT4qZtw2oxH1":"D5hMVa2o5q6S","f7MV7gNs2AYT":"1ZHha206uLdJ","MclLb90zCxym":"hy8qRGdOgW6c","yZ2owWxk2qp5":"KBeDM0imBdfv","a09GHSEkRq7f":"7CQQwfPHMLQm","QdWGFDJhc4XL":"2Un51bLY4PdS","8VdlBz9g7NJp":"r2NlhSgUyHb4","aLnOKn7dvdb9":"oZGw3ZatEO88","vTsemTqvo7rV":"tiL1vjvdBZGm","caG2J63y9JHE":"Onw87zM1LYHp","N3Tx8RYYdIhE":"gilHpoSr2rcH","82Y7q54vtpbU":"Z86l82mJNHeA","vxSgQZ3RgZU7":"aPsMEa6UXCX3","FSHTUZ81AG3s":"mnzAAUu0PB1V","MOzuEcrhFzNv":"wypJuZkYrz5y","EhfKgjvVDImV":"uyMSoHCksFlz","nYsyPq5VJ59j":"g3KeCxxKmcsn","GEkrdBCPRUff":"iGE9GW8rAi5l","b1kV6AxmE1Ip":"V0jP35p2JjJd","kF0UEwKN26xl":"5uhyoFK8eopu","7M2m9IpytJBT":"x3oikUwbqRER","FAQEfjpF1AQU":"ioIXgrMJGTFi","U37lrooakHyU":"DVDFG4SjF4tV","2vPt9LY2jrB7":"MlnxmMtGkOua","8HCUClpMUak5":"3LyLroThWcSd","WimAcHFYLgWd":"x3MFpnqfKc47","t3zLvTRvvde3":"Suj2aFKjvi9H","GmCd7VDd0sU8":"Dclt3JTATLvd","819q2Bzl9Mdr":"UxNQIsD4AkJR","ETNqr15tSoi5":"kvwGuxFZwjYA","KWiUZHg3RWm3":"Wduubk2aKgDJ","Qfz1wJ8vvmas":"T4F7ccMKKNoU","CMEbfDVNhUCr":"XyAl5bsGpxIa","LvWNmc3UFmLZ":"X30uAf8w4PkH","AOOJXrN6Jfue":"ipkZ1C4Cokkv","dJm1MOC5jP2m":"wH8zPxIQ10Zu","fYXUb1WrGWgP":"m7JsWXWv245V","LB8X1RTiVcyQ":"yAxgCaUaac6B","VjK4xNoLKrmm":"loKgktmfYP2J","TkE5Rjp6Xvqm":"FWTHlyYdjC2P","qTkihu2bRMH8":"OexDeDQDrDPu","BSHUh1z0WlI2":"5E0F32o9lOAO","d7Pg9pjsyOik":"49nO3KBQcROD","wMRnDKveBovi":"2kqSIrOW7SSL","QBCdWe5Cy1HH":"ciEV6Prw7EWa","ZZg00jHD02LK":"6qIrGet3Snw3","vmSx0REkk4CW":"JCSITtQdtTl4","BLQwco3wn7Nf":"IehuVzvTDDdW","jzmnEHRY1eoH":"7qRqz3Gd0alD","C9zvJ10LXqKD":"AXk5KzXlvkGM","AkhsT5yvMZUW":"MBbvPB7WjBZd","YlNdG2nMos5B":"Dx4MbnsKxlzy","T2urdgPeIdqK":"98GsovhfTSzp","mI6g9SNHQiu6":"jE3kfgOX5uPc","Le6J7nvYyIq2":"UNnhJ2Tvv5pB","EeBxrJdsuz8B":"29c6FCSFVZu0","46lpHemS7bBl":"egwZns0AB2TO","SaH3POHW2mUF":"m04XH0Yvskpy","wI1vIBLSs7xT":"TnUZkPI9Zv8Y","h05OWSE6Nveg":"SX25GQ08vUZF","sID2Mv9iS5m6":"SgkJYPohqCPz","AMWXbXiF3fPQ":"4s7wN8fftaeI","ycUchhLFLwx1":"9kCRepk1J99z","mTlp9Rlu3FBK":"dftQVRXxacaw","XUAtTyeQah1O":"ZM4vnO4MNdVB","UZh0jZQsVM8A":"ukz3diw1PJsA","8Rs3eOvLPFy0":"HIwKlsN5Cylf","5HzQUoeUDWEB":"xuL4qnnuou0e","vvhRLBCfv01m":"VlMRUXw0Hrur","KRXM6fwJtCf6":"il44AsZm2Obr","73PtULWFkryu":"yDChvPWM0Yu1","Ofxx3orVsSAh":"mn525B6MUFa6","1SVtm9xZOGgu":"vphX2wuG7Ehx","IdlJeX0xFtAP":"Jw37HEnz3Qpy","czevA5IeihWc":"Y2JMHs0uqMjl","Wm9YF0vaaBNv":"Myhe7JLfAKUx","DjijzAzYTStP":"TjlOhX55YyCj","POvLjbrNyCAX":"CLqPeA0bOLnI","C9QVHUiYiuUe":"N3q6b1NeEj1a","3A5rMAO1VrP8":"XDBrbEhhbMom","BiXeTYTVqy4P":"08Lseu7wf9Xc","I7Nl7RYtdNBX":"LIGFxhhNmZMT","XRnL8KqeRkyt":"LR4VCkOdjQKw","SQSkDbHbDhmg":"WGQtBBkJTx0f","NLlICv0qLwwR":"aDAtTREz0T5Y","sS9nGTWQJdDZ":"Afnf12c8DnVz","bG84CdsmyE7F":"2ZwRCQALJs9Q","ewntk7UoWun1":"VLowSLPQkRlN","fVyP7uyT3mb6":"FBeG1x4QfnSp","km08GX4gxqYK":"QL7idVQOkLv4","7uKERRDTqgH4":"Uhe9MH6xe7n2","WJX00EznU7mh":"rIXjkjmqt5Zv","9GTmM1kKjnj5":"ohFmeKzY0rHR","rugs5aC5zrbZ":"Ej2dqzQYHAdc","OI6HtQOe1wHf":"yQhuGr1sKCu1","hDoKHfOvIkwL":"IE5PMUa5G9Pv","Rtka2PX1CHTG":"Cn4xy5lXdOtK","nwnrJc4seAUx":"xY67cYi1Go2O","Kz66QmW9WBKE":"jA4zaKSI0a0D","Y4SyEwB9Jeqf":"UnVB6ExKX1yY","vJ2HfvKnQLw4":"Ts0bUaYGv3Z7","XY5pasKmg7DO":"NTcnhUaWMNcQ","6G1SBpf0DRKU":"tnwwSTQrrf31","ywLykZ0CxSXX":"gk53U2ZzlBjM","GZXTo42Qfjsm":"rBcYMZE70OO8","f9iyHuQZ4rJq":"E2dvU0btdWRP","JTRWfVNOeJMv":"WV79sjNnUPvm","VCxZuW9Pzwnk":"4OmqVg11RGaq","m2NU9gsWLBxX":"4dNvAew2sEFB","MRLotBhg9Yqg":"pttPhGtjteP4","nWrlgtBwZppN":"fxfgCkLkcPJq","lQnQYSiK6q9c":"gphsEcyt2KSm","iussn0CrRvVk":"IyBVMLzBU5I5","FnDyddm5ErhX":"qUCAxS9aw7J7","ZHC2u0jfckvm":"7i1j2t0GkE90","SvC19plonWiF":"TJyFq7nlVHZA","94QmlARcdG8E":"aN5Yh4fuDPaJ","dRwgbahNyQzG":"ID5zvrMgO4Ea","IeL8jgedBh89":"IVJZoBvlma0T","yI8IQ1MqnSv3":"my301XuoKaym","dhnUo4SPkVF6":"jkn6wSkPbPK0","jEYf4tdIFvzJ":"YU6OqWcHTwTq","msVod9i43qZj":"jAFJ814T7jcc","bvyYI1u8iSoN":"rFLb44IjhNlW","cvTzi5VkNSP2":"YZcimfiLrxDu","YcsGdYr5fAWT":"5cYKv7uaUIZK","9415zic2OI9w":"IKW5T08v03xR","loGpuF24Xrje":"hwgyGbzaPOGJ","krBLgBMUGaRZ":"Bz84WPY7BwMz","XkEsQCZY0E5P":"vreIXJqcghmd","pMk3XPpXkyZz":"h3fYgvkQIOiu","VPlhGpgeTujV":"LLai4CUoUtjf","56ksVHJS0am8":"FEtHxTGusTA1","PhxhvZKg2DDz":"vntNa9vJm7FE","qVeultqdpZad":"12OqKqIyrUCJ","695YXHGBz3HA":"fkNRMyOHjvHo","j3KasEq1Bmdc":"qWfxPJeAZxyz","h6BswyHgDZ5h":"j6k4wQq8qxmL","ejV9j1IHwpMk":"AKjmmQZf5GsZ","TTOH73WsVWXZ":"PHnHolRbJk1S","wiUj4KzsZtCY":"Ufq8FGi4xyvD","vVaZ0P7w1OIc":"vR04mx8VxFww","km6WMVZXPhpI":"LkbB18tX4zzZ","9AUsbsJZNGoO":"ARqZZUefn2Ct","tfce8QKpj6W7":"5urLjJfuRRqZ","ETRbahMPG80B":"ceFnRYl8i4fO","gCNmCuzzP944":"6bcEcyo7D8JJ","BjkDcinCBKNP":"AnR6QtuJdUWb","Rvgo8GRgFKr6":"wOHBZ0JNjvLw","JLla3VFyoeXB":"8LYjQtXCwJuA","OtpTPET1oRio":"yKbOy8KSM4Gd","IkFvhDVYhmro":"JrZ4xjJmUbRo","E4x1SqdGr7pL":"4m6J1RAOH22N","hlHzJTLoqCPA":"1hA5qYcZhmkF","p676AKmSfsCL":"D4UxpWOKh1Ic","5R1rSfRx7fQS":"LUrBwJrM4Lt6","F0LrueuLMLAu":"LVrS2QG3Irua","a7hpu7IPPQ52":"55xItj44x3KB","bgXF3MtiCTCY":"TYuhUc9L3RoE","OpPJdN2pGsPb":"DUkuNEWnTb35","CXxT2RU8JcBe":"38MeSU3yjNv7","vfyLRhacYAdl":"aaapLsJkVKYo","F1YldwPKyXpe":"b4fNbqcAoz3A","GPrqOllDo1Fp":"7OehuGJQd442","cVxuzQTA01UP":"LVja0YJjCi1p","kPZoHWcORCTd":"8ph73b7tLbuR","FDFKeftv7Naw":"JA6qaIZGgE0h","PtcwIhI8cgjx":"oVM8lRcve3IG","rZ3TQCwhYAJQ":"aqoxrJ6ZNZhm","DAFBxnSf7gWr":"8bkU9uO15mLL","88kztyFksauB":"NBBkLgvbo7mM","8cIqgs7yJRao":"qLlGmmfb26Rv","MCjOffHIFpXT":"siYODeichvwt","XXlqvOQxUq1k":"c3GHaEJ7bgiB","DmXu4SnxgUmK":"sn7sGCqDg4Rg","RjqWGg92T2ZR":"BIrJHF6c4QdV","yq2WtHxSGy33":"ONIcBvnf99qy","VIGKVJtBnUSL":"BF5lkeTSpaVG","fzum3bsb7scp":"xq3YtgDL1ep7","AF4gs4eJchWr":"sOEcWj4OnE7Q","DATZTSjECUCs":"xxPdf0UxwkQK","15gKp7AYieUo":"u1Qo9NnTDq2O","eQAseSj0ZVlV":"fpsndYykCROO","GoGWDncIMCRT":"5zehYnjQ208i","XiaEsUgrIaaR":"MOEzKuALO5Jx","gHZORMsF7GKc":"OV4gZ5qqVk1V","PFe7TaLymeGG":"Wka1lycg6ZPn","e2Z3vT1iI0ki":"Np6FanRV4Dkk","4T3w2pj1KZxC":"EaoYXstBZpkN","uZjBLchPbs4Q":"79Fsc7AZv8TU","BeS9xch70kcC":"GBl2jziiFxEW","VRIZlafPBqBh":"oxqr1zPePcfw","lJ1qoW9eJv3n":"xL3ddoPYJfkw","QmlkPcCrTka4":"YUBk51kyupMX","sSWVWGFhFnLl":"bqTMGfo8AD8L","X0L0ip8m70Ng":"hJDFQw0xUW8c","nXz7MbIinBn7":"HbgzMOznivJg","so0NFwN3SZzn":"X2s6RSQikCtv","cRk5aMwNnmgP":"eidVa24hUh7y","M6YjvZS2kRii":"O2qx5LESPRhU","QHAp3d8RFXtu":"819II1RhYxHe","f1CwF7qgR4we":"ATPlkduCOjfE","xcKNUG3lCAET":"9JI7awA2y5hL","oXTkBXVzGWHl":"0J4UNryL3fsr","yaWegi1G53mr":"h40eplTYJrIg","htJumX6EXXR4":"9ylr8UGG2LE9","Aeh1n3tm16rj":"I5U99U2ltGoc","GOyZ1E4AQ1Rf":"du7urWvhgg04","vdsy6MDpOyRU":"pEKOdnLllDJa","Hv5Of9SAxTrK":"arvxAqIOhebk","xQKkavXBHaXb":"lbPegAgmVW46","c62yfYgD8MM9":"KC5lLtjzikZG","TbR3eIqHrG8M":"LNrgg6MP48dm","udQm7INKkoZ5":"lr1CGvbJZg5f","18ScRzNYb6fz":"WCh4sBPu9xgq","u7Wi1w2cKYq0":"oxNLCycWAiY4","ady6etkGfB7a":"Ys6tppztWFje","g11LzZV18NTE":"HNPueXkcRcpM","cSc9voCeaSbL":"WkVMVUrhvXJ2","FHGPOX1pGz7Z":"R4qI5KVnbj91","0nAQh5yFtHBO":"ro305GcQ3OXV","dpf2t5sS4S8P":"Bz6cO7XU58hs","oesVE6RP3PEP":"MtIeQFKuoSPA","c3RfzdXFiLx6":"Dt8v0gQAncan","TZk6dqV8PlmS":"aGaWOImTRHYH","q1TcbajsglqM":"i4zlZTJW1jeU","4YC6lVJnM1qw":"3ZkaRBYY24jO","aVjl1LpgmDgA":"NYevR3mKEIKn","PJb3PqLCldlj":"ng95v5dySS5z","Dyn2xKFp5TV8":"qtmmq2keRauD","WKGRg9mH7JMd":"BY2hhcip4WYd","blfXSFOpxdwx":"LCkemqD6eifn","w5yQrnCGOZR9":"v3bb2cq5mr13","tNnvDK6V1WCK":"Re7orkMfGlZK","9ApmWIHAdOj9":"RUQ7n0sOuxRD","aN4zVaM22TlH":"msjKan7DlzlS","KAo2aztWqqbT":"E2RLh30uzJqT","m7trJ9xU6jq1":"sXL3OeSU3cG3","5ycvuMhXozNd":"ArH4OI0xd1VI","0mSETHcImF42":"hX6X8YaM8CU6","4ngdt69XZaHt":"7GuMTknM3ojM","mQBRPJO6naIx":"v8mf4KWnSo2o","9YWlbVIMgfCj":"YmVwUbZ6dWC9","eNhDuwX1UUNp":"VCV6YM0Alfef","Vy9Mt3RF6rof":"GcaQc2Xrnzgs","H4UOxSrkLBTY":"KxfRy7kJLRPR","3WKJHeN07dyK":"UjhyLc17WYix","rMNFXHQ4GFC6":"S6Nxt6qzn4fd","iCsnsPIglEl8":"FIJuAzXi8ApO","g1ERMnMyoF9K":"cXqXphQkyIGW","a4lsoVOIbUED":"a8eQcoujg2Fc","8bWotrQbFX8C":"RZ7EpXDrvb0A","zqbEkIWTwMTb":"itrGx3Yf80mt","qExhojChqdoY":"CZIiBJ3kUBPP","Ot0WrktgdUJq":"hiZNoRjCsWla","tVJVPjiInWEw":"Q9LxEKaA23mn","cNoFWCmUwTOo":"tTKQZt7R4LOT","LhIHfWjbLm6H":"4tOWoN8otSIU","2JPUtSZgzhsM":"gPQtK77OV5ON","gqqvT54tgiWG":"v0EylOOrIxIZ","GUExcZfHLu8P":"NBCY66hu9Mq0","KH6PdWXbG97N":"HWSKYF56sREi","IdW9m8SoVvnp":"PQMp5lVbJcOu","aI7MVTFSMncw":"cSRDJUivdRZZ","bdXOrM0ymf3R":"Rgi7kgc4DyT7","Qkz6nEC7l63u":"NvjV2e31dNti","Lx47G3SzlDRM":"gl8JgVZ6mYLk","KkwBmy5GFhl6":"lreVz99uquza","PLZWBoqoAfYF":"Mujp3EYDPEAw","qOvmXXEiYUZ8":"a1fPqhtwSb1q","5VtGkF5SnpeE":"KPZQzH6bhoVH","hblyTmjFy3eP":"F5s6QOmpJrsP","5SDBxyhiEf1V":"CzqsNlA4viRp","JL23mYOD9Dri":"Z71f3InB9moT","SugZhUG0fk0v":"LYyUYtzcEQw0","vhmjw8vazwpb":"9zKwjdS8BiEj","90DM3rwax3A3":"bxfFe4AJ7AmK","mFGpAAPpFPOl":"pLr9QTLftAYn","ury8k1cfcbIJ":"1ibPEwnt9eA1","sGLnvMY6vpKv":"QtAzZ2jLwOn0","xD7y6kVgmu0T":"k2Ea6cODLO54","KNZ9aCgaE31c":"1Xh3sCHk5uzZ","h9wDFQpKsx8X":"PGuwc4pK7DFc","CGF5Ci6h1eXm":"wQx3lrTsRUyB","j1HWvtLa8OVx":"M6YpbJx613oo","4c4Q0wMwAV0f":"KwRoKfNA0Jn4","yZVgIq0YwIQK":"IiielHe18OKd","EVX82lYXEyhZ":"uknvs8FBCNsi","faHlRhSAA4FP":"382dSFsSN3xI","eRWy6cglQWYD":"CpnSRVDGWdIg","4vTuVcmusZqY":"i3459zGwqVJm","Bg2Msf3p1oIx":"qib51qYll35G","46oIQAKFdrRh":"ULwD3j0rWM9E","8VnsveABZ3Nc":"F0tyu9Z3w6Hl","c1dyMdNP7gzB":"67WoU3jEBeWi","Ep3gI8xFCVYM":"VoilLte9nlyK","WHURYkQ4VzXK":"XZPmwk03QU57","kKxVWCVXfnBe":"spx9k7XnJKtO","72TMkFuv2vKv":"CG90A4tmYO92","uBgKjNPxeQ1T":"RcfzVPHOdvsB","3tI7ChtWJXO2":"EwUpA7Awbzgf","UA22Vrrv5s3n":"4R5BWjW9ce5M","AarrZCgm1W42":"nXV38movkrYi","E6A4LzTEoWMc":"JJojlWDVaEKQ","0z3emD33kIk3":"cHI0m6PjGggY","WbfsJyG1PPrq":"IFG4Fq56Tgqo","y5NXBaJoTrlz":"HBrC7ZrBqOC4","9d1dbzCp3riI":"0Tx33MQ8tDA1","mVpN419a83ww":"zvj1rQMVYt4t","pQnljEql2CWS":"v7pplT8nfcgX","Ls3Y2JcKcpZq":"eat2NcmwBrF0","dkUvmqe8huSB":"iCEVgM6TwOBj","ePOaHFEbXLDp":"wZ9upVmgc04w","zDZaninqbT9N":"Jg7RPk7wmgk6","iN6ELHGUExJ3":"ZANVfRnnJFXA","PMri3AOHQuze":"gIrnEIOHWOFU","kZrTSn8pLvmh":"dEWg4DfsYc54","aeax86KStfgC":"CXSBCdf4GvMl","JiY1AJrOizai":"HcdM0BXLJX7U","hR512JTMSUfd":"aWBXucO2KVRq","knLRUtt1UjUo":"yJUYSMZWK98w","tMvR4dCJGFaP":"DDZPbuVW0Olk","NXa6porvNhE6":"AQpiMEOJlrl3","lyyqqSRrrAn9":"XkQMfQLiolCc","EzuXPCJKr6a7":"nKShl1rZz24d","DteCQY0oNXCA":"EE6eP2G4sr00","9ywTQ4OfhTYV":"Ba0w3W0Fer9L","pFXHm9jKSgAB":"lI8OWXUdWqHd","yajrcFvObLkL":"kIiAykWkiwnR","HCzUl3UAdjds":"TNA6zaNTWdE5","s7pmAHOYIANd":"AOaPoYAxXapI","xVpXiojem0Hw":"F7hTZawdWAnp","oIIhfEuoDnCM":"MEaibcm086UE","k6TKUbm0SjUS":"3nT1X16sDRjK","0pnHc2LnQN8E":"6ySeMFI2XO6n","uIfJeEWa8FwF":"BvCrMPdhZQrD","LA88OqkX1TtJ":"yLtBXT0M1luD","x2atrRFqFyGR":"a5SHoLUGuhhT","2ijPs9INxhjN":"qfrd3hFUX96H","kxd3GmLhnRZZ":"k7fP5xC3laPG","duMdTkZX7jOb":"3OYC1uqXbD6y","pgbsZ3W1qEKq":"hmW40EMLe9VB","73Eqg1E9gZa0":"zJ0br54XZJIN","2ZnFi42I8jI8":"ZDM3Tnd4qX3G","dsMmtzDvJSc1":"MDZ2SiQaVFGm","j7s9sRIRpUGZ":"LklhHufXEV42","xZwPb9JQw8kq":"Pe2yuH7UXh1o","9BpA6Gwiwe1B":"odiOKqF4UKTa","5uWP3fXM0WEW":"NrCigVbpGUaS","GG1tpyKTH5Za":"fddsc5qneUc1","12PB2LtCELdG":"mf9VZjhqANGy","dUPxbDDWhfZU":"4pfK4yThjeFi","nWCJbPCSijCU":"08xSqymToPpt","Ikt4ofmmgWrQ":"DbWuUx6eHqyu","WFfBTS6LBVQF":"KYVoIXltrGmv","0niFw5NBWEg1":"D9UbCaV8C5iH","UwXi567UCaeA":"FYAB2yovdQHd","wc9wiuNkZRI2":"boeracNlDN2G","DN4DWOtuuW4s":"uUf2ej4CsJry","FhxF3tRwaEJI":"KZqIWdmWgfte","Knjr23Po8mZ2":"mpKQ5J4Iz1uA","7hXGFYYtsZrH":"kRo3ZciGClGj","9yG3vNwkL8Ri":"Sq3zW2VyXNyM","ADbjqfYoTiaT":"ZQzQZFS4NMiE","YZVPa154hXSK":"y4Hyzsvr0qJN","6MzwKR4pET2F":"eV35Uahfbz9G","HkM056lyOAcQ":"S2IgXBbT6NBr","KPjJYSyIOmLV":"8LVSbRVfjMxS","0qr8et4JgUga":"V2YVxIKAuOcq","RgT9URzImEVd":"iS0CnweBzp6r","cWStVGgByct9":"7kZ7C0JTGBO9","gkhilSkvDvoq":"YbnLgGNuKpac","PlUDtn7LgmPQ":"hmgGpsZPEqnL","3QlR4pM9LiBB":"lzmkmg9P1XUi","Fq5cuLdHaGJ4":"4LKCd0XUGTNO","BCbStEaCTtek":"diKMBByFmEoB","Ji1kI0LI6FKn":"uK2H9GOBluQR","zEW8QQjbEmbJ":"Vx5ko1p2SIPp","it8YY52dzTBe":"0evMuXGQFzbZ","1brx7KX9h7r3":"TNqPOyk4VDQ3","7NdQkkRrWcoe":"NPoVIDrcOj2z","1VHS0kBTfp8B":"wRJKAjm7Trd7","L46VjRESpB4W":"y7q5FsNdeM4l","4doTiQgxhPEu":"rUIaPNpi79Na","iY8C8ZY8t8cL":"JsyYozSJ0ULf","0nO1nSmcLctQ":"o218cHA4apTG","nO6XKisxSjhj":"cSPspXgLNrar","Q5nuWdxo8PbX":"Xjx4KVOBomBY","4JaZxfqvqzeX":"npp4HEAJ2I9K","OQ8ajarkVZ4F":"47I6JKYLzKb0","QbyVft9SUicW":"dV0yhFb33Cmf","VcdPywis4b9h":"qAj6vTm90cTk","JpJJDlWSUoPQ":"tjFNjUT9pfrG","lQduXH3GqhTn":"V9A5HGczms37","aQbjr1mBENOL":"YKPKkLxG1laH","4ZMYlL6RyhW5":"KPeWRXqaXZ0h","qvziCfmmsOdV":"Kas05NxogCmD","Dmgc3rm9OF6Q":"sHoR8LcIPGgU","D3cYWXRHkutG":"cXJE2C62SFyu","VN9r7kRWvVkB":"F2n6yGlXR86s","ZPW3zqUJPdcg":"3oQjD4TWJ5D9","TCPgvAJUPVAM":"jrZEyLRBNJG2","8JNcLckSdsxI":"NVo860KYut1o","7JcksxKSyNnU":"t4jYR5GGsPkV","WJnSgJ2Ob6Cx":"f5VUWMZAZwCs","2OZZ1eiPkmfk":"m60ML2LBgugY","MXWT9MbBifEF":"LdSo9RPPZIlP","5fp5zK2AOxEv":"JXPbozz35P5q","etJtbIJe2u7q":"XjSidQoo0xZy","4hggA01Xd8Lb":"Mbv8AP6Sbl9c","ui4FaoIKwXrk":"DdaC6O7JpNsa","mIMsCyYcQ7bB":"48KMyqHUeNZA","TJUPa3dQyByO":"jk20K13dnKkY","i7OilhDvcKCe":"uApeeD8UsnLE","Zj0nMuPP87fp":"X4ptOn4dlMSF","EDG8nDR4TZyF":"dyqmjXqHyZ1g","dznKF7tdbySU":"jU3v0EYjM8pL","1zCRbK4J9fiW":"MMFIRcjQpo1q","ege85Il9fWnS":"qIGJjXfZh3Qa","MJ0yVKMSudD2":"IX0asbSiLxXu","uE5rvug14IG1":"SIojYTjlKbL2","EFf0ixMUevM6":"hB0LKITJAuDh","QGnPQLCVUaNp":"2WTHeGEnnEQL","NTl5ncCUUKZo":"pujyZnujW7hr","0Kl7lljh5xCC":"v82s6RNf9yiW","NqPituzczOUG":"SLCo7HHa0XS7","FATjtKsLDWsB":"l22xlF9Mgse4","07vrZDIGoifO":"e6Zcah4nYzJ8","ffWmXMUtWZbT":"DeuWlReOI9x9","jkmSKuWs0I6h":"pDVwI7vYy2PL","HnuSLZJ1Iedz":"XxtOzIAUhMQV","ZyWLgVSOpmMo":"go6EbthyXVEf","WwqZLIcHKFQW":"HZUdAQUXZNtF","VZEZexXExcGM":"0mhkEKAU7WR1","J7IvVGKAOFps":"ylP5iqv4IfsO","dFjH6e0bnjQe":"fFx1rQuX9JQj","ZopvV12OTvte":"EPNFZm7fp8R5","Hmur8CavtyUT":"KIPhZ9RmLSXH","Zp4YRZ3IL6BD":"KsrxgmuS3imy","4m40uSSTu9Ks":"6GZOGazx5USa","gSJadwVqncNE":"zIs7Iw3es0E9","ubDAPE3Oe90a":"tvlQfXXuMhvz","1MDbqhOKZWAK":"6q9BQ43V4lOM","xdSG4U16O6Cp":"PlRyHXyQeSk0","OFXl50pxddqo":"ublvblSdNgWj","8MUrkbvJJeJQ":"oudjmytJke1f","do40ds2Uy33D":"IobyEMqcMob8","q5Pzjv7qrSOY":"DMm7RfgPhTVe","mGmxkm3anEED":"GfcJh3KM9Gwe","Y9dQbLmybMQb":"yQkXVsWgGfl2","FyNVoV9RAEFW":"ErhRmiC7r0Ta","qa0pYnCa729l":"lCxXhP9p38ud","AsEaLW6m1HZa":"6R73dnhLXhO6","GBAWTw40KTem":"WLywRYkfi6tr","44DGfKIjazYn":"QliLIe0x4B6m","Sx1QbC1BkTs8":"REdo8bifPtiK","nkl9K6bGrTjs":"pnwdWj18jkYY","xlk2RTmJ9xh7":"xhf57Hupg5NZ","z8UH0RFz4YHq":"M73D3rjgFOw2","MioRv7pRseFP":"VLC7KEeqac1G","oGpMvOyxCSIl":"Gf9zaKFLZM2W","aX1sgcQJnti3":"Z3uaqveY5q4R","eqJOZfUBMxEU":"SwNAQFP4j700","HFMK4InqmSkc":"J9rxnzxnmMOD","04erXyM4hvxA":"h86ZVzrQROSO","v4EUQuDwLsQc":"cZobGQ4OA6tm","HChx8k3KQSc0":"EaM2GSg6nPg4","XkoQc8vUIZwF":"LdRrxmB58HJa","CpqZd7ls5Z4N":"Sb2sAt9RiKnx","qArFtilCxv0S":"kBRVKNKLeRuQ","Wpb30ZzNIIaX":"dFI92jpoTDxa","nVe1Rocw3dQH":"hMw0wzV1Ujiq","vaMm5Shcstxs":"PTuU2oQBRjn6","CbDq8OQpsRxB":"NzVUSGthU1rC","0150TJ7gsxvD":"MC4YRAiWsun8","B1E47jFA2oHw":"NuqmolCcVRNF","82FlPnfC0POY":"Sv47Cx55wcJf","gdbEhx2Xs36J":"hyqwtvAnOo4S","hXPm15AvRQBm":"9MCSN1w1zCqg","LaSTNcEwxOq2":"DSTxA7np1jML","HSivXhjnqfrU":"kVtQZTAtn9CU","NYB9kOHoKfCM":"LV5j14WPneqj","YaTzXPdYY7Mi":"Us8tQvGXKo9p","pm9qxKQEyUDx":"bec6kG84FbRi","83c5HFVesNwW":"PY3hXiT2Vccf","tQajdUz5dnsM":"zJvOD2Gdm3NT","Ax1HuCZIBahb":"FGkbW63AF0Ok","ZYyBWDhxVObQ":"qiN1WhXIdMhj","6tWlO89vW5JI":"PIjKN84QYN6e","SbmIaULtTLxx":"33eOr66ZhsQb","kx2tZAQOGVu1":"v27R9Vxw64qS","2t7J3vPJeKZF":"aj0tlOEijTZD","5epIJ4DMrfxq":"7pUGu41Y0dPo","nXBONktcGwGs":"7hLg6Sr8t4sV","YxyGWgDFlFu9":"jx1LaCRNvwjm","gtbLfB1RPHoz":"pDxSM0nBh3Hi","TR2EFiWZMfA2":"jSxtbCeVsLcT","weHcf40mflci":"5a17Y3fh51lj","kmrLKnqecl2M":"QWyclkiWFbv4","QKTNHDXw9SJQ":"mcUmGoHjxP34","v0kCMzamRHny":"fbmy60Tv9PNA","HEzo6teK8pck":"RXC1Dco0pGeZ","yjZb845KVvA7":"P1qQp8vWd8n2","YEut8tul67o7":"B8s4hO9k5laj","Ru8ShkDePf6R":"V7LVYDraKSSa","ys7m5rZepvoy":"Vv4ranGpmLDu","FP4sc2QdtCaH":"asgS7J81Pqzl","zPU93mmzUFsH":"BwpVBQL8cI9l","A6a6rQtlyENC":"tI0tZSxR7AFd","A07huY2hmO4H":"0MlqVDoKKgT0","UozQd4d79mEE":"Gp6qp4eBcf9V","xvZrQhtq2a1K":"D9mvhkF9oSwi","pu2wGjU6LErE":"XzJCEuS85XEd","P1czL6xfi9bN":"XWoqQAfDjOHQ","AG2ubQQCGsqB":"Y7qpAj5vsUuX","FP22pYnh9Vuo":"TBltbFcQd4a6","WwK30AeS32J0":"NfLlrMMBnxAm","sah1RhoZXQya":"Y4EXcGz2YraH","vpJxaveHPqUW":"OUhtPRTaUNZA","JADekMh1qHlN":"pqVYLaJI31Lb","f8QUkWZM1K6n":"Ys2juWgMy33p","Yez8mV1KXFN1":"oVuSVDI9F1GS","6jbiNv5yaUQZ":"YyTxpAC8zlbp","4YPEFAebpRtw":"Dg51CzGuqTDk","tK4GaTxJqGmc":"wvH9gegG1j3Y","QDO88E4wEccn":"HRbnWjmIYGIC","VUElhHWgggY8":"EwMJrUhk4HED","XnQ2gZB4EJhJ":"0P2K81nUsRVy","h1oNGV243sPo":"ZqkWx8D8YKE9","ZErfVt0gd3zZ":"SoFObEjkMAJk","G2WIpcjGVoLJ":"R7GGyFiPl0RN","chRIOiT1OMSd":"EIaViIGXCB7o","UEVbxffUE9rq":"Wvmm1xeMp37U","I7WkWV1B3I3W":"rSzI0etS8YPk","JIJRYdg7Lb2y":"1Skz6ewzf7A9","yBftsWiokqMY":"bWYGaLhZfjbo","dTW9wkP9uxab":"ES6ajVh5DjvE","2IZDJZu4v5H9":"AQu9qrFm5Mkv","o3E24ctofoTc":"1f31D1egSKpv","fzQGyIYXt4Y2":"n51EGAFdGB1Q","NpAszupLU1D7":"tqte8EnEAV4G","0ExqLdZtoPTq":"7wbkwzS0DThX","jPwJzk71i1eJ":"SR82RijZC6c7","SlRDuZS4yO86":"MR3VHvONlyrC","npPPx0Tlcgvr":"STSFRTUU1GkO","wPhy04lr5T3L":"NHSbzT2C2PZd","VK3CIr5saJss":"yx38JywnpJk1","CSaKaC0qv0Hf":"oXvPfANdb2sQ","wtslrfVT207J":"fNMbn1DYttcP","CG0U5u0scdyC":"YZ2brUzfr162","9UeEIZX3g6hN":"Rbk1Gzt1fBCJ","AuVHONXMC2pu":"MSSZ9tB8YZAi","YhXw9vpVu0KT":"hIcnnKVWa0VD","BJWGtvGMj3ar":"cqd2NmU3i5UP","qj4k7ThMcGTs":"7e07f7tBXiLQ","Bx2cy27dbvoR":"oCxyhSSeudbr","NZm8ruo8FiM1":"2KNkY4qs0UKA","qu2XuRopvqke":"GTtePh2TxR48","3Fj4jAi4JQos":"F9VaremJDlDN","wZWZ6nDlR7KQ":"07Jwa6JQdXc7","z4iL6uX7d6Rz":"oqQl8GsjwzMW","2ToztcpRRx4P":"vK9ajtcrsBwp","0MRCyOYFNmob":"X6YFCm26hK3T","CAtb0gzfdSqx":"vUr87vBaKop5","596fsgyu6cyb":"hzzvNCEUW2mK","jcZ9RZ3sFDkO":"VcuiQrIRUA0m","1Bcpe2WBRjw8":"ZZDv44lpkFBI","beB01DFxJBV5":"L3eLL88ZT4mf","PLdFHbOb8Gdy":"5axa2GVHzXkL","dMdnwlIeseYl":"y9doD2MLDzl9","mZSZkNEbtcfp":"bR50kHLDPoV4","U34nlS9qINv3":"Qvpk5ZgxKSgm","Wmgies1pstWS":"BDwEvNlcRcAG","ZXaZVOTZxUKk":"SNfSzbzNlUfw","3gKaFUxBgisD":"BcCEtDkNFNn1","G4RIYg8Nqbo3":"bXpOCeKVZAXy","6The1gfDungH":"3p86HoCowcRn","zM3Ld8MoGxPl":"1PghBjWmC2xT","hGp4v7WnW107":"hXtGxZaK8Se2","PQ4WFu7yxtMS":"fTL0QQCRnUba","Gr2lokXxqYbC":"7e6ucAw2Y7oC","RimqNmGvuoQo":"4sB0cVUbOEIC","QNkRDra67QxK":"qt5KAy16gwig","Wmmn7vkTgJuJ":"NVZEO2NrIOTE","Ip4YAGTc0Feq":"ch5I4s50srGx","M02307CX1faV":"G3vha1B5E1PM","9MehrfmWK64S":"5m18OEJylWXv","YIoOwM72NYpo":"gMqqqRssoDit","3Knxe4vCWqBd":"xIlLL6le5Vsv","vylEtwz5g8F7":"LC15PlFBi1Bf","tyNwgnGBVVg9":"BflwXTmwBQgS","1NAhYUaP7dtp":"wgNRNmkffHuH","Krqtu0LLmHkp":"WDZz27bzzBkt","Ul68WtAcI0ZW":"j4n4itv5j03K","2qGWxFnYmgZp":"8fKL8q1F6i01","lDWK8bnUylhy":"T8wfu9p1M33g","SvrUI40MJ2Ai":"rNJShwAi3oeT","28ASOE6Am7Rq":"dSxSdZQTD5fG","fYdNKKWYEMAn":"9Tm4Jniq1OLq","MG7fg7zdn2GX":"rTRCCyi99u9Y","PECLmC9mXcIg":"xqEZva3bpnB3","Pr8w3R06yvu8":"L1FyNh7twADt","LlABl3XkYVT2":"01O0kkmCoWHs","40aT47wTHS00":"nW3ShVfzHAr6","0YcXcJdhUsnm":"SDcGC2RPsZFi","FOhORH2VDMBy":"K6teQo2m1nB6","5wKHww1zFI1W":"z4HKB8eoFQGh","THELjLiXVqPG":"LiwmCzNn5ZSt","js1Fd0hEkuuA":"Tfp1f2C83HzY","gBon9Iwtx1Q9":"ZNNQXmszDEPa","SI8JfJ57oxRP":"gXdOltfPZxt6","bSf8xDqlcN2C":"7SMFbgpk4Hux","Oo5DHQZ0F8BS":"JXLuNqpNg2dx","rvZtkj1oEjaU":"tO5lnq8kCeZ0","bRDAChHwygW8":"aD1vtmgCzSJC","fHP1M1YDOkla":"Q16CQCNCwSBk","SiUlJxBaAQDj":"bKeg9nFnOr9I","7v72KjVScZir":"M1Oc3Bc32U98","qyjZD2Q2KrLA":"bGgofhM20cgN","F1fVl8GqbN06":"Ga0kfonsBAnu","C5zdj5JHLEOA":"80xj44KXvo0w","b4Ss2jcU0UpN":"Q34un8jjwa6o","JxNl3ub0YLZU":"DtqN1DQ70tKb","qQY7A4eHaTj0":"pDexh4xCTfvt","TW3ILF0mk6tM":"deCrlU00JuiX","FmxaxsoOO1HA":"Z6gtSoTfWM07","JSzDTtWzpWhw":"NiQuh1SWaxeh","avwCzhb2FG4w":"U4GcnDF8BoMX","nyO7Pf2MLrpA":"sfKH2NyVXmRF","tMpIqjGa1mZa":"ChprJSBoVft8","QQci2f5IifoR":"UaE5LKFaI5Lx","ow7vw69px6fo":"BQvxH1VtEO4l","WH4ILWKevjZt":"BoF3iHztt6QB","xs7GZvOQ9Hf9":"DLuPC0H2wulw","WXnN8Rm2qjqy":"Ivj33WvE6581","GMmUnOhPyrBb":"Y7z1vKatELyI","zul45ENqaV7U":"y0cqv8f1waME","C23c7eaz9pLo":"3OMoriL9QPvz","rQsCsJzABl7F":"Itf8QmcbLGyM","vHJzBB22anEL":"A6SVyw1E7dcI","C5dTVciLpLII":"HNFC0fy2nOZ6","WbgjGX3ko8Hl":"bpmOEEb1tTkD","oHDliYrp4yQj":"1qUeH4dAD3Le","1Dr87LXdVLOv":"gbSpFoLOcPou","IpYwDYM7BWKB":"AhQ33vUAizER","SSlc0ZvjxQLg":"iBRQo56Nz4BW","AYkJrAXHL4XC":"rVPGSUL3K8wM","Jh4jPcT9NG47":"PN0oWsmAZBiJ","fJkAGesCyYQf":"vI9Yh5S8ikqv","g4vVKrPFa5M4":"sHAJ7BzAMIEM","MA1XjDL7yQzI":"rkvr1cEPLLkC","tMfmK1gZREhU":"DJn6C8svpjkM","2YzDkExmMbUi":"cI0Qt50QOr18","XAg4QEwFSNXg":"XXxscPdN0gOv","234vNihIzQfs":"Gn2JEbJvgRt4","GgXJfjz0EZLZ":"8QD1Zr9FfjzM","6bUlkeViQ8GK":"GldBrYWrkGgm","I2Ja42AC3Mu6":"K26FrwNcxBo0","RACOvQxkI6lc":"kdOPoHFv2fP5","n0ypbwOl7PB5":"kpOz55EDyngF","JPZ4BY9PbGxh":"OWcHA4Xh08CU","91rHfUytyOii":"lXlpJmaNRmEi","gp0BnG2NFMBx":"b2d8S23JOo9M","9mbNHB90y1EK":"GKqcGpw1xpZq","XiKbFfOleNld":"RHT5oWj47fI4","qCoRoXaX1OAD":"8am18dGwf0iU","vFnmU8mOmpaD":"wKI86w7dpH5s","hFGmuEL8FsJN":"I96edvSF8Ll5","fODPgX6ZUnWr":"tBSbBC0eeVqV","htstJb6jZleD":"Czs9dj9KdZp6","F2VlIkqjV8qk":"a1rGndNfHoRS","AMoDzwMUEJOE":"AriDWRSfJLIt","UfFaUN1ZyQ1A":"TpNdhdqUmjOG","tFffq8eXsKwu":"w2AfiLz49Wqb","ck5eej93AF76":"mhBFcaVEUH0O","r5BmKCHZa501":"FSRLwtZ0guop","uMK5esWngFNr":"sJtFy76ghuWY","vhWnS5Nwog4Z":"FR6bormpOlCv","gzUXm8Dg1ETp":"sGDkutcU21qa","PPQ9bfsq2Pju":"fri62wz2jtbK","lIR1kfQj5uWz":"kILDGTGt3B0Z","s813n46tam9a":"Tg6WVn7Ua7mj","r9b3ZLwZcyTo":"awi2wdvUpy4T","JWNl2isXopNY":"Hxa1qPJTJOLZ","6HrVEYcQlGGE":"IGLpxfXnZj8d","PGsZEgI2heHO":"v5vyb11JGjqQ","NDF0PTSneOrk":"v7lLAGhcKqX8","qtAtz7BH8Ie8":"1thJ2vxyKFZ7","RYmiKzFFJDJk":"HdtRvReKsyM2","i8pzBbdBphPN":"C6h9vZbQNggN","Dq6zzZ1WYWvX":"p0Wh7k7kEVk8","Q3CREQ16G6XW":"S34UGvp4lts1","WdkEYcZULleB":"T6JpM08h8HOq","tvczPhTPlCvm":"F9JwfpqwOHBm","ZIktiOqcrwRt":"e5jvS3BZa6ND","91JUSkATL4OB":"r1irTZ0TWguH","pddvpWeEeIuv":"Dg0yQFL1XGqa","IG2F4rUXlssX":"rWKzG2mTq0I9","H4hVssmyigRm":"G0G7aAfswh4y","9NjMqFflaz8X":"rcoATJr0aLCT","9qKbg1udWWog":"rDZbaLEUJnK4","eQmBFdpN9tiJ":"TG9YCyPubtSQ","4yJTOW5oKzbw":"LxPtIQ771xTl","CE527XKEjjlW":"bBzkDiTdqI0Z","PxXlXqouZZPS":"j61GHVkiwZlX","qny4sKUF4AXi":"z0PpfFH2AM4U","W6cTvyF7n5dm":"b2BbvzSVLPtD","Fkf0uR4Vm4gp":"ORVHirQJBUTa","Uu1WPxzw54d2":"DyuKnnk7MNSD","pVuZuTLahrVO":"gMfpteinm5Si","G54iMcuRmAqU":"1Wf1A9fwZKya","GbMzIXSwKM6P":"4olmDGLuQ98c","33BecCzN5ye1":"pN5td7rIAAEW","iWP9bIsMegYc":"p2TYw9SqHjOT","EmZ1y3cd0X6A":"oRsGYztva8YC","hQ9IXx1rbwEH":"VptnQHc3NdRS","1iVXUgqMuOpS":"r4peiVS5Ypbn","Yl57STO16K9S":"DsyCA83paRIb","FI9KKTicTVl0":"soLVoLP1mciP","ONRUk14vtL0v":"T2kmFDQOnRKD","lS43FRaYlB9c":"WRmNrYpSreTh","4zJftLKzCX3g":"EkYuayWRUNFx","liQcEaRSkyKD":"mSI7xL9U3Qcn","RlZzte0TkC0Z":"GzMOWwVAetC6","K1ePx2f2fdJz":"fiPSjjlTLH9m","n1WC3hC4lW13":"soPlc3kG7nzZ","DLreyc9Bt7Af":"4NSC4oerOS7N","kwQ32rHHyMme":"IIUESxz6feju","e9TBoqPshB0Z":"lJNr5D9bGiSj","WhDpaHjjXCRj":"5ywzGmnE7qJq","U4obKKCb4uuM":"lFUOFpvWAf40","6YNW1mSr6ySO":"48AiqYeTTkVo","nhRsczfsanc6":"29KJBfy8hKQW","QFrgb8xtWizv":"vFUKR8M66ENK","K1P2lduir1DU":"xprbkOWMKChV","1JHwoWGZakBQ":"7lMV7qwPQkpB","W8YWjw8OUfSN":"TQVElpvVmtRg","xVCIa07iBDYO":"SFoDa3EIcgGa","u1Vea18BybmT":"NuOHSeSHIIJ4","bvt6RfBhIGkN":"TIDq3umNG0gU","PJBkX3Rg3DIV":"greKZEhgIInr","5sYOw8hgkEvv":"cQlTb79Q2453","o7fNMaVkEdwB":"ba6dhnCvyRrZ","iwViCbH7pNdq":"NX6pWi2H1Jvr","DLoJFLtdsH5C":"gpT6KIQb1ClR","YXjsDsL5LN4D":"1cJNlDBdvBA7","ZxRSr9wFpMJA":"oIod6SdGvgI3","yif5PjxxKYAA":"y8j0j3rcw9Qx","syriuPNZQ54L":"2iVeVzh2ynDZ","wUrmexmUE8mb":"RptBNHuI2D6T","myEljryEwuJj":"udjU1ZgdR2kP","5yZE2zGCZqOB":"JaB1aOxAEtSD","QYuiP7EEr7Gx":"arAFRUeN6Uuz","hwhO2h8sCwiT":"ldlBfZIXyb4P","3VPuElqou4w2":"i065B7kwV1Cr","GdDxuu3LGQsK":"kPErFNb9kibe","71DBdKn9ifuf":"Ywlkx62LqEqj","dMszbvUUehFF":"FGf7JtBB1QS5","ev6T8BmLege1":"meEX66A6J6gA","4CH6Rp2vTlde":"mWaXFbOlcRif","OWWYN6fJLfyz":"RSuDXVi9am0k","tRZpEcbplFLn":"03kWM9tYq1ak","CmZzq4bfZOVO":"xgwApJZGFrHH","EqYpxHv1yZ4T":"2f9RDu0MShsg","6oEHJZ538wrz":"C70E0I2CyGkQ","WLjQ7f41IMhz":"nPKtR1KARAKd","pcgqN9X8s7Ar":"D7fepDefsfFm","ewJaCd5bzn1Q":"TVSriublPS2u","zxw4UKuN7q4m":"I6PHflckj6Jl","BvedOHgjmx4O":"Vhwo1cg7gKzh","WPgw4Zj5Y5Tb":"ajAJH6tIdNP3","tPKVZ3p4Whwz":"xSAtLXAryvSC","xUn7L4CdM32K":"UMeoxg0SzORO","lpQc56uXB4m1":"Caq5knQSCaXL","VzX4GoC2Rq7W":"KUaHSvsVVSjk","r72RcnOnB956":"mjQOilInZKxx","RNe9TOLo7CNr":"0aC7IR1HgAeP","kAEy6KF7xKYI":"q8SXCw1lu5i2","XDES2voap4Cn":"c0tnfAPDnlXv","UrCHJ0iO9GhY":"kFafdXfJzv2V","wTqZJgMBLnYR":"D98Q5XDOYL9x","mGD8wG9zG5Dy":"4639U9IlAq6K","dCaBuNZu1Jtf":"l7q4Hzg5sMYe","r882iDE2gKDj":"zOc3bV2NLAQW","uGYCGS4dxVUd":"Bm6oEUGyuw2W","YL1xYGpa4dGw":"iupBFSBJjhkO","4Q9U3X3v3CDt":"HCDSaYbuGvQq","PgHR5p71mqQV":"lqyrMKgsgm0t","iwmS1SOGM3io":"qyrQjQfTCbvb","nFLVPuS09A2I":"rxWl4cNFVWqE","8w2lJHGIyOtW":"fP1vxLLjqlvu","ZBOeNVYH4Olg":"cT0K8Rv5duot","z4QC8x8VOspq":"JWchVv2MA0o7","YoCR0EO3SfA0":"vinecLPIlnG2","K5xLaasEDHWo":"LujxfZdDfDuP","86AguvoBP10C":"y0Amf8tmhHkY","aHpT1cxulEUg":"sGbnJbLbYZHY","J2szPBaYcrL0":"ac6D60521Jnq","6jX828WBowxE":"pM2r0m1Z6ppu","REASFzVBSXxG":"881UPsU031c9","Y4r5ZWQGDibL":"0sqahw4rxQt7","xKloCf02sJdl":"eZWaIHzz76zb","9KCCtYLrOKtQ":"11R3LZTEFvEQ","NFxq16fTNU8B":"UKMVyf0zjp1O","QnOjbCkFPa8M":"zHBJxyvrZI6Q","y4wtFTAZ0X2g":"3tUk4J64qt17","uzGwiMi9zJe3":"U6lb9GObQMMt","v4Ao03WHxXZm":"vIeCOu0MQjr0","lFEZ0jblaiBh":"U9tAuH4RM7ui","ouDyWzRxiQpX":"FtG0G4AorP7F","uCSE4u45ecp7":"7GxH2ow87qG7","g4pj1IPGafpt":"jjaHWLVdiXJp","KIM2CmDG4Bng":"yon6vNzeVebj","j554VBuYG9pU":"RO1r77ZPZXQR","JqZbh57lXtFq":"ZPaRyOD7IvXv","Vrkk4rLmnhAX":"rasOI8HYIXOc","72zNVVV5cq5f":"TPCZU7qbNtpZ","EL1iDw91yq1M":"KPK8Q2uJ9yYV","BFtPsmelU0ZG":"d6xjDf2yhOtJ","af5ZCLuyETSq":"i9sX5pPm5VXf","Wdm5J4vJMSYO":"FROFIpWUVOl9","fBte0oGdc6CU":"LGC9fiQhAW8T","o4EmzmMFJPy5":"S77e4SGCNc7T","Fp7sDwjl8gyX":"leRCvtsN1NSa","c2fgBA1RQkAl":"dy1rhbrAyVTq","ST7Dnoh0Rt7d":"mCgYnn5Pss4k","VpPV69iCcxLo":"p709k2RHosX1","nJYnZpTnZiEy":"qSpXYGA2dPNz","g8LPJjlv93ad":"FpBLT1lBBePi","D0dIvXGKByhz":"Q6FlI69H30sx","78BB0FT1GEjN":"fd4Pb4GHLg0Y","9xl1XGXwbZbt":"7ZlRr4fcGYek","s6QLifau2Cvk":"BWYeaJqUEXqq","SgDwQKAFA6lY":"2ykWURoK0zGn","8ZPL3VX31eFo":"fmCUUzHHkZ8V","FDQCuGwwATkV":"B6Hp07D7jD6S","uA6LLfr0ytz9":"rEkWJAY2VQf3","ZZRePyAZPQKZ":"wLESh0xSQ4X6","sz3Oq2vXYjyt":"eW0D2vqSJFQ9","YHw1B8n7XCwF":"7JHLVHLmUHNL","9xPt58i76ne0":"C1ESLCQ1BiuA","u3edpJza6EFM":"TGESjsvB2AoT","MPreZ8YlmF5F":"7siB26YHgZQT","MaYYROKRu97E":"uAc9tgDArlz9","jhsGehG1FL3c":"Ihqr7E1g8wn8","sVTzVqU5fAfV":"bXLyIkph25HN","p0F0vTLswLYF":"fj3Y4qXBpPta","pm1JBukELI98":"DVpfIMSbXvth","ph99c3yWYZvH":"aCbVQYx117jc","cPKnfhPDMjeA":"Ao61f5fktHks","3XBNSOMWufDR":"YkQDJCxiTmsT","dmA0SSgEWuog":"WiTVTaLWqZNX","1Yb63N8kzoOU":"VzH5gxGC2x2v","3GrEQqffFIpn":"jCSf79ZHfWkI","xO4Hp6jITEph":"24Hca2QyzOBp","wtkEBSwJKzzW":"PxlQBDnyQYYp","hLoKMqF9eN4z":"w94aKIRVjFSl","r9x5AR9HZ1wm":"FAk4EY1IZ6Xw","6VI1w3eOtBco":"2MqTtRv8hKvY","yNw7mqMEYRw2":"QAV8hR2fvJCD","vgvHpuvw18tq":"gmzVHIwG98i7","JDhmTHdxjmU4":"7iM1Ao2UF9WT","7DVWjwvSr6sa":"bah7osGVpJfN","bxFlmfHrDQXd":"xXXjq7WLtD5S","rl3CdVd6wUSg":"kNnZY562UMZk","h3f5SBzbQ0JQ":"oy0FCACFws1d","YvmqaYVGgJ71":"43KgABcVDxUC","XZOiryraTDsn":"G5CdxhIyxH9W","9wACaHdxEdjt":"JhzuKgjWU5rg","3kW4nThRaHLD":"6JawMq4gAzpo","tutR5Vt4EP6i":"R82xuoZM3uT7","h3WkJ46Hsmn7":"LigWDKAoq6yS","8Xc0niuNzEZ4":"RzF5CQqeWx89","uT3b0MvFoeh7":"rCqaqVolSVV9","SrF3ZsZpQwKu":"ti5jVfdOe1lS","GPEJvgX4jUol":"rVuZBB8lKU86","11ICB4pWNNO2":"U5z2hhwizKxJ","NxpO1bVNaJzP":"Er46tl6jsmdH","kdvsdhDFrZrJ":"aycwR1nHiMso","iYlq9CeuVWvJ":"7gzb2sLhpTAX","8QAf9t71wf8z":"8zC9wWD06p3i","GXTbLVwahBo7":"urS9qiSUwpTp","vUmmq4Xrxvhu":"A9HycirsCZzI","075mz4PCkY4i":"SimEyStEULCm","VF7RAwILXyUe":"nJzPMBAyQ9AB","pIF1ty4WA1Rj":"ANH8Yx0z8qJz","IaWU4LZ9rO14":"UMyohTcFGBh8","LO4mxiFNMJ7K":"XmEZx65C6QAQ","SKKiUIv4S7nb":"nwfqgyfCTP6X","pXKwUItcRX9B":"KUvIUEi7nIGD","ahtWXvKn03op":"nuycwHSfSHLu","5C99SG3LgiPG":"WYLCjrpcuPks","g3Q68Ia8CS3L":"hx8wqU8F3wA7","oeytfDIfcWcw":"m0VtppP098EN","mhmrqIfd8L3B":"2YKePEirkBwJ","9X9SuV4ZeDv0":"EKy8ktBbuvcy","vFtDcorhy7VP":"dyl9WdGyv8Hg","g6i4r8QPHmeM":"ts8vweFhB63s","5WywS8iUKKYe":"hZrp34sqs4oS","ONmzPQPvdf8Y":"VppLr3CjVLfx","yABqfctnIfZt":"QB8CUXpUrZkl","3cbZyWPlKtls":"jaZe6Z1XVQQ7","CTZkvvN69XM0":"3JJ1IgEIu7Np","sQK1QGQF90ZS":"kJaCPDgFXIm6","Ym5XNue1upJw":"sKFHRvt7ir7r","kOHdklG7eVVX":"dNj5Er0uxEiT","pLp3nnxW32vA":"IgqTFSsCf0BG","96r2KFLaPaSj":"o8JFK3onfeCJ","7RB6zpiRpvq6":"3vQ0WjPx75Zx","92Z2pOT11QiB":"uCDnI6KhsJiE","jCUWXVOQvkVC":"XXKfPlignq5j","diGhAtVDVHYq":"I25IQsvnYdaV","lwcdVLR8yD7l":"7TAORi609iot","CP9o3pypOnPX":"z1NEGhvVE3gi","aOa72HBQDaV0":"gZqRnpFN0M1Z","WC1DCHo9oEJ8":"DwAqR1sijdpX","pQpeN8bttkfm":"WVgwx1a7sPvv","1UGiU0ZUfnPo":"hgdiCu91ScS9","Xkd0klRBH4CR":"t8MGcuXu1WbU","T2uYQaKZ3xZd":"DX09Pg6OFl8c","cibrC4Zhh10M":"vMrKwYzsbUUI","8AswvG7uME9V":"Pd8qLc8pBlKD","pGeoMtFFwsDj":"iJQGA8E39J1p","qCckVzd67ZaK":"qHOfPtap2dmW","kITbc33cQ5Nf":"rwrlCONqdQfV","BA1hXAuvjnNF":"VikTUCTHJi2C","w0FhMUH1tz5L":"34XxXmAb298L","2PAGnL0xyYuH":"L4Rq2uPjpYaz","l88b8KQRUX69":"w1eoI2hpHs2Y","yxKjmRRJJ56F":"89VLCvwyycON","PA1ScTjXtKlH":"kCIPnMUrNbHW","1VQBXKTdhqMv":"5abuxAg38DlN","KFNdcVtamQ2a":"AFGhn9cuTyFA","IbHHvOzRgIeI":"xupUrwEO4lEa","OtaMDtebUQid":"4IbvxJXvjjmH","3Vwvke0cAW5L":"etEaPxUh9S7H","lilcD9rfWG5c":"OVW0Cxnk70mq","GvWiGpKBl6g4":"2H2L1YVvAAJr","MVNCVoO8CzCP":"hpHuj73xG0Bb","GsoClgu72IFQ":"LEfbEdXmF0is","GtOhU4r8o99k":"sQWtW24cn9bM","kXX6HfDwHUSt":"xRAs3rgtrFdI","zg4o7LuaOVbe":"VVwrBsroDUyh","UxKvWRoWqBum":"Q4Q6gPJguqwa","w3TXEwhGKAx7":"M4LhRehdcvog","XeKjhpfXk8A0":"3EefKsZ94tez","qVx6ttya9JRY":"2zHe8OgCrivH","8sDKHqNhTNCK":"PdFd8BzaRYhw","SllhXTW8Osz5":"9plZ0DihLTOr","xYJGCpe9Hddc":"duIIeeBAkhEN","HL2kRTUagmFT":"8Ps2VeyVYqFm","oD5qI4g6fCLb":"L3ypK7yz1LXV","IdxpgIBEEmAe":"Xum8IT6dV9ve","07K4jokJ8fYp":"H8O9BVc0EGze","bgcrbYjoAkPB":"FqmvsSsau0lR","gTUEwDEdEbWO":"2Q60zGeDReQY","XUaBWhaqrNO9":"kHB21A2x0NeY","n3GENBHUqMow":"B7Roc2vCo6XB","UES7DktcPnpx":"obrRFgaZP631","S8kVPQOU0tYv":"UIfMrx4xttvQ","tYreQP79FW6k":"oHax7M4CzS6i","C3i8z003y7L4":"YYiX5KXjU0n8","19VohCmgDRNu":"0hx8yTSBJS89","ItJP4zvRCan0":"vAYReOarrr8g","gzzMOcDeJBBe":"vauNBPZJfAca","fEQyymOS83aW":"CWnF8TMyD0S4","3tVUIHqYh5qe":"TTVth1aSyX6C","867IHAOBXD5H":"xY8haL0p8p6s","qX4wpZcrc6SG":"VDFjuwyZuS4U","wTQJcRD0jJmk":"HCaqIKlImsx5","L7dEFtqr5sCP":"qcW13V977lEr","km0bYV4R6DgK":"ssNh8QClXAsp","dlhJS79onVSE":"Je0DQJpSOqkk","aozKKHeCMD4q":"eRt6EH2Zgjpi","W5mHETIE862s":"f9novDhd6p7j","zVINwAPl3rOq":"acNRAdtF5zlF","OPpOP3JgZlod":"rywRv7g2DU6y","OHiJ4AiCKjBE":"Vuplyt1u85Js","wOgoeP48dEqs":"m27NZOoOsQNp","sTob0h7CNC1F":"yTsWEMWkf5Yq","I0sLEA2zBD6x":"wVpklQAjKLfH","mdEKOzzMd7UK":"sTm6HxpcdENe","5YxsO6nGKsGG":"U5UCQiMMcZYO","pwlSIFY4Yb8M":"DvLWxK5fcPVV","rBdZE2dYDooG":"tyiZiEk8jy9Q","jQ3pWVCT9WZ9":"r6yJNnLoi72E","K37cXa2v8gvd":"BfxPpMBh7YXk","cmuesgOV5Ouu":"W1mqM6x9ZyAw","oEI2IfHgySGD":"rlsM5PMe4jy0","Q9BQ7pG8g7QZ":"Ky61wcBHi2vq","5EiUo5sV3DPo":"9CQCff10auTA","d0QTlySkkg4y":"YToOw4QhjBe5","UTHtoJbqnXKS":"teBOgtyiMs46","VkLqxTKMSeJg":"pywQTiUAwUSz","ie3xTZwDRePk":"ko6xnXUC2fO4","S0FKICR3EjKY":"Oaa0Ktmz5Hha","DR6KjdEh08iF":"jVhcwBoIXF8a","gDMW5G1f9ND6":"VzPppoPaGcVP","XT7Bpv1BjNeF":"YNF3lC5RokK9","sT4vnnA6K4yg":"YiRrukCm3nxG","YJcG495WFLAs":"uGxU5EqEPGit","STah08e55Kt2":"qrquYvOP4fK0","sMkzsdHaONcd":"SitXGWPJ58GP","szpU6Hog2Soa":"eiP1EcTJYABJ","AJJzfQt0r9v9":"BtWLuEAbPGiT","iAcFMRcundvj":"2gpE6FkcwvyM","2h6UErsv9GY1":"JdlSkj1qvaAm","g3Xk4voQR2ik":"Cj1Y3LcH8NGb","Zubft3rojBSw":"4H4eAzRXcXNZ","KZo9OmvchxZm":"B0eI5Dg7cARC","GnAugbIeSs81":"tSNoK8JcueKb","CpiitV8tWNne":"wEFkheQoVnnQ","LhVjxRdRalcP":"e1k1JfiPJmcQ","NzE03NyHTgwK":"tIj2PD4VXREP","tFBzZ1Ly1Npg":"DxJINh6wGeRK","cdzoEEs2suIA":"Al3IWY3c42WX","h24NW0CsygHG":"MTf1pxsgJv9W","CdkXivWEuyQt":"Hs5gHTVD2FMU","RKMaf0AgG1Kn":"eNLTQJ7Yy3iQ","flSfl5tfWlE8":"Sn3lbe9sO1gl","DpwX81yUgMCG":"SHH6oNqtVuMe","wVdRcygwQbOX":"46hezY6Vs3BH","3PRPwcpNZclz":"PYMfLhYglyG5","z3FIfep4bzpO":"bhXeGO2q2FMD","BA8JzWDBL906":"y202RQ60U9qZ","6IT9fsdJX8zf":"vOn2A15YsI9l","pQ4PsICkisiY":"ajSz8jROEKR7","UHNSKj3XOjJo":"m8ww5zShvDdd","vMSSemWknKzs":"5zIcXbVaOwzm","LKzuF7jClWcj":"nurRfpQn6Dkq","R8DQOe2Y5i4b":"XiREfyieRF3M","syyxzoK1WDcl":"kGtUbbtf906u","5V46DXMzLifX":"yrh9T37x7ld7","dESz0PXmWb2B":"JbeHbl6UFcSw","zxRHLG7LrJgc":"z0taiYN7r8vy","PuKwvEA077cn":"aHYFaYvNdcaY","oA5YI841fxIe":"c8ruuS9o6WuF","zPTnP0lbsP7B":"0U6NEJ0vLSwW","dMX08dSLFBA5":"kV8UXRte3kp8","O61WeWXJOdhB":"1USr5ONaU2nW","N54HyFZLd4OD":"NaldFWyiV6az","EW71H2p3saaB":"kY5rwInUCYHc","uKseXsA3VeqW":"50V8Ej8oroLb","Oqpm3SmWUtnZ":"Kzy0yfDrHW3E","NvIhx4rsZIrg":"WLKHawpB1j4e","8NbyOUE9Udtc":"noLVpfR3GHWs","bvzJghU8qoYz":"QlTxA69gIKdO","EqRINn3bsN6d":"p80nWjoQA0uN","jvwiCk3985pJ":"5qb0QdK97thu","TM3QMOUP3fdC":"HO7xsrG0cnt2","uJWenruejLst":"eAYHnMRfH54i","dOOJvKx0z2Yg":"35u2kdVr3Hns","9yabaSKI2YVp":"acFpC7wZ6jP6","QD3xjBFSi4PQ":"kY79pRFMfyjg","dfdEI3HMcdqj":"WYao14wZHnTT","XDMSojFppKTJ":"9nL9L3QNRyVM","13nF01kIDVI7":"RusWakODjTMH","ltg9WA1wWJi6":"jW0TOqQX9YKW","gLTC55WDkO32":"ZfNsoxMnE346","4VAI0pkUMmeQ":"qadi9ZQNog8A","NXyB0EbeuBPp":"JveAj1S67ACY","W6VFHIbsJY4W":"cG89HGWZbvwN","rYVH9B6NV5uz":"1vR9KLoI2sM3","SrZyuq91AVDY":"1FuduWLV3uh3","9lcQqE8Ztu0d":"hbrVlnbWY6xp","xDbpTn4YuzI6":"MOACG8hwKjYO","NIV9nxuoMd8a":"81TZHRTosyB2","yjAV1jbubzLu":"8aMKNfih7Tjh","llEfw7ECHSKI":"ep0CqSpEA0QD","1dL8Ry68dqox":"PDQnlvCbzjEV","DwrfzDAhKWIO":"uAbOCfbv8U9o","jHBVKeMXLsGA":"LzMSwTooBljr","gzzkJrLEuyNU":"7V6zX3m4iBvq","xdjlbJb88GJN":"TyuNzugbHJUG","bId8dnnlOnsY":"N8QXPn8Qx837","weprtgsm3r0s":"YVK8f5hCCSrF","IUiEdNDW9PtA":"HgL43jilVc6W","4XIep5Z8zr5W":"bc0K2uxNq8jg","AQ496FplZQOf":"k7hJkJpS3hf7","pjwzfGVfqWfb":"NtGURhkc1NN2","9zDWB20KEQjx":"3yi6ONRHHA9F","APDmhyxaoJBN":"3h42PZIWKh5b","kXYyClFa6MYT":"Mm8XTaXiacb5","Z5C7tZN8s8ic":"AylK3Xa6nos1","jD631oOJHHsP":"6SFIHarFHste","uPVQ2EJWJl75":"FJJ3RjEMmcR1","nJ05WSY1lONA":"LxT5wWzCCH7f","iFlfyovimA4p":"toaBYzEKtSRO","tGraG1PyhZKw":"h1zujyTFz0sw","NbMIpeWDkZSQ":"YvyuknS7TaS0","LEr3OC5q0vYE":"Sjv4QyvEeMMN","3xTTEoRA8byA":"N1pLqXzUa0a9","VDbrkw5GEsYt":"WwVAZlBbiKhz","pyWb3JtV1sF9":"X57loUuWNXPV","O455FElYswDO":"QTODxFHuTdPa","PFCooXD1wc0n":"926TYdE3ixLx","i6nWJ7Cq7gC8":"BVM3GQwtPyb4","mWVDVSRELkDV":"yO2dVFUgjXnQ","HUIzPFZov0sK":"8UWuZHBTorDa","qF68RJdgZz35":"hppDvqeXjtvn","dJRmh5F1B3yM":"R82c5TnnAiqQ","S1jrXwMh8T9i":"3AtHUNjKy2lv","dt0uz4sOEGTZ":"uFGlnjVki3zF","doQr3gmNajlF":"l9eCQbEGpR6N","d7nSzEMjqNTV":"Y7ajAmni716T","J2ZNefdP4dw0":"0ADMc6Iy11rZ","YRHijQtEeyeJ":"nPtNfrjOVRFb","qbk0zhy89ai0":"mcSVuuZEmOjj","gcV5ityGc7A3":"seyHNM9GJa8D","2KsjKADp7POc":"N2lLiFeP50dr","x1ALwFmfbBF6":"8qNbwqHcFC3T","zB3VYxhi9uaJ":"UhD3B6eSI8Yi","oLsRFFsgkDUb":"oCJBuIlL1tjd","DeGtAqpbvgf9":"uo3A6H1flAtW","Zgr8FP0r1B4f":"JpJHlC3ptGTm","fZKa69Wsq3IK":"7KkjEplRyfwe","VZ6fTVusvqYv":"CBpva2NibIuI","rWjh7oTrkGeu":"n8UVQuIWE6fa","ODJaxbWmA3JC":"fIOzpRroJnlA","bS7E36wRS2gJ":"P2lsihRFEBFE","TurWtVsQJRgJ":"1rsT6uamlFOC","CjVqWpDV0EnR":"dgElBAC3hIqS","dLZXrwiBgnMP":"WnruPI8i3IaP","BCMjUNOOb0rx":"Q8D0enRdrzAY","I7Sp0dFFJ3Rm":"CFcS8Qt4GAAF","fsOMXOgOedhK":"naWJRjmO85Et","rzTpJuIRJx3w":"EYoU7zeKFWim","5GfEDFklGxiV":"M2rb0GJnEGxe","NSONg3F30Ysc":"j6n3veLzjHbK","VV6WxBjXrCWJ":"Aq5b1B43AOl4","M9vT1km3chKk":"fR4UuvYWUN0U","MRhua6BhPkyz":"7b6Ijk0xwb0K","6onPLCelKKyS":"0pMiLNovqkiD","Kr08E1ayyIXF":"qWDxzPBlmrb0","3uAE7kdrt9qP":"KvmAR1U7EmLt","1gnzCm4Pkrfe":"d33XykBQENeD","vw8RUQXZiOkz":"b1P8PHRQMlse","5OCM9LN9p3kb":"dx24ty3jZEZY","WlnJQVMqDf5V":"cbBvHoMXRGSJ","C6luxmKbueyu":"xG40LEsrfQan","ws8vjqMD5qTl":"u6A22sjvxhE2","FWukDlhqlCO3":"X3VjtlqAa3Ka","2FkZVbUyLq3P":"vECXMGob7MbY","EcYmiF2rUGao":"pnXkG10C8BTD","6WfKPC4knJ3B":"WjQ2BYcNEyO0","NZjblrWK9Xqd":"8CA7LrlFneLc","WKgNg28s9Fhb":"skbeK3sLEQfQ","Crg5DtDmncPF":"hacrR7ndW8vp","iJEYA8W7Tdqw":"grKSxiX9LAVX","ggqWEPZlq1X5":"iTV8kjfx6td8","DWuRYqNSj2d6":"BWRm6FjhxzjS","3hMozMiZrsG4":"rxKU2cKR8PAN","x4osdNNxpYbp":"D56T4fNyyWAb","qj2vFMfPeB2X":"eZoymE6hMUHZ","4ePSr740JmxI":"oAmrasEDZucB","Pqd8BWB2l8sY":"0x1Hsgh2pbtE","lKTZRsDeYVK3":"fA2OcmchKQjU","8ekGUC8RGet3":"Nj34c5vKGynM","3xK056lHVKGA":"JdHk0q7YWUUQ","581zm1XWjA1L":"D6oJOuBYZ0Ah","PCovxE5MHUH0":"fsp8QSUwqM8I","i7zahIfYqS7y":"F8NCs5nlVb4u","yPnf8iuaPaO5":"GfC60mQm9E1m","0IUUmJoUwQGq":"w6Fk1tMHcbda","rB4XJZ2xjF6A":"XDuY2rVh6key","AUELWJ0wYlns":"dxzx2OP27f5b","2LP92tI68vml":"uQGufJRQddLY","5qPj0D1b0Xae":"05anXOjoEgNw","FonUcR2vCQ22":"NeIJdZyCU0SM","11XkxC72UvEE":"tAH1Lk1CmQ0V","VijlilRf9P94":"ubk1wWQDCthF","rERoXhBbxwUj":"KPAbskSsvNxo","BCxjHOWUSMC7":"PnmuQt5KYvnY","Ssc0XJ39i8Jx":"v2HtOhS0tqwh","k7BMHR0qivuI":"sMu5eky5OTAp","WEB24TB7Trip":"D91TExcJ39no","hHNOrSsKQrCh":"GCwOPE9hFB2m","FszYESbR0LPW":"B3Q4vOB52PC1","zUzltQGiIkAj":"NLWTNDMfLX4o","W2QBS99VPIR3":"2he7DVRBOezZ","MOy9TwltZDHu":"ePHdu9BR4hY5","m3DlWSvnqOfG":"KALdSZWF3I1L","aHndYbQK1eh5":"cJgDvWE0EeFC","VRrng0XBvXgb":"RtitFKjqnqfi","3qo6TzUSql31":"Kl7pProt2K3R","rQbu2xAaK9gW":"MlzU7q5NOy8k","dyhfp2pAQqqT":"cfkcetBqUzUM","mEJlpCoqfC7b":"Cg9tdD4Mm2nq","bXsgLwNtLBDg":"H1wwLduJcigI","Ous4gy9swLAo":"IcjUy12ddU5A","K11r1DKhMERA":"hHoeRT4kOQby","fj1rjcv0UDF0":"zlmsID1Laark","tbWkG6wuKi8I":"VmBAjDHTwRNM","6eLrzeRHicMl":"1Zjg5K66jrOQ","CqvN6kxUFUmW":"mhab6GLel1v6","xH8XBsGAd3r3":"pUEOwu8d0fNw","gzvDOV7yFJtI":"rsDAJGZPaeuE","F1txRszdlvY5":"qzATzn8aS6xe","rIypMLPnKaAA":"PEgGgvkBn9Bv","MvqCuDjJIxaa":"AQfRYgC4Y150","UBK8QgFQc82Q":"KtkubwnevDzS","44iQDl36IRRB":"ZT2GFpZU5pH1","Epf8L0b2My38":"0koplXqHpVEn","9aJgyC3kVGzA":"WNSxK87vGYRk","E9lfeddLPGvw":"VglYMbKTGriV","9GISv3vgmlRi":"sDJR4iBmARLU","vYAEgoWXGAsO":"hVRwdrBvE0Eq","5godPGDZ7ceo":"C4CKCnt1Hjdi","xmTdsDdMuAcb":"zAatwT5ytWm6","PeGGCR8gAxIm":"muR6bpZRGHVr","XKta2Yzfh6fm":"BfYqLZPCNKCf","yxmdyGBVATBZ":"DCQ7pWkmgTiG","5cyZThAOJrzD":"pP30CLEgzGzs","uRUgDuGWjin1":"6X3TAQej4Tt4","SsyamXfVaW7Z":"HPEGykMyff7Y","8vh6DWIZeOsc":"sofOCTqN1L3H","UBQinFxm8Bu1":"XaCiqx3VIoqT","zALRDZRPwj84":"iuDfX3ZrLptj","gFEir2kChbN0":"5lazj9MA9wpg","pbKQXuBZTsQc":"4wBh5nP87qjj","Ey4WCZMDSfwE":"SFTs3OlHy3MZ","OuqbfQMZL83d":"TftGUPN0M5Pm","mHEV5Sq0tu2Q":"7e3cHmzAgpmH","DlmO9Aryp8PW":"LMh5SDl42zvQ","XmpxhigN5o7A":"o2x7zzDcY8O1","nDkmDXwMPljO":"lCtWGrVWnYWC","vD3453Mry8zk":"LIHta4cpMAsy","FOgJsiNHsAC5":"fRXnFtJUtHJF","OoDjhzx9QyBY":"XqzaH55xO02d","MLXFg1Ml2oJ8":"aGPaM2sJvaTg","0t8AlOECkSbl":"x8TxWjWBwXFD","2vL7FeovuZvp":"x3vrWHGLkX2x","i7bY4URnsch4":"aHnoPUA6PALP","2nzEjh7j14hm":"WpDFjzgPpojq","gUMNMupCvwCz":"ilSvqP5PGpGq","upqTaBNFedTG":"bW63M8yFgJRo","3auWy5znQudC":"OPobom4kwPNQ","uGtGye7uYqW3":"geitOJ4v9hiC","gaI9dClJwH29":"dObLe6VmTZ3A","jtJ7WZbpa32w":"tEkB9nvT1bZ7","sFspns88bNp9":"ufdzX6dJhUuP","kMyA1QzKP4d3":"0IuK7T7PDhc3","QaH52f5Cb3bG":"HIptWHalYjTZ","Ihgaa0tnAgMB":"TVb2LGjyTSmw","su6XIBnRUc0A":"2UL7WmxTh4Oh","lJNOqkVxiwM7":"kTjq60aGmLj1","NIW3gyAYSBlS":"N4FsrafZ2IDj","aoU3TgKtx7PG":"rsBCcMncKUUR","rr4HZe9VR5AX":"Zh4UjyXxyhH1","rkDM2YF4gV7p":"Y3ujfS16EDt1","7mn1DV9gArii":"JewuiKZVaXpB","IdHrko6nqHhk":"oqpF770gtUUV","w3sOAsZNBiZ7":"5j7FOidsT02J","3gdyNAhrlmmu":"yXr3oRPPkUl8","kRwkjKrmqcpt":"c4ndfe7NlVJt","WoSOTO719f7m":"aTUpEPQzCQvr","aZVAs3PSWpD5":"vrv6o4VQpfQF","ihbIRh4aTZNR":"zY1j5gmok30d","cnAhxfKlOHC6":"B2VxFca4P2dl","hGDhuWC75EZB":"W3nXgWKwmG1M","nA9Nrpek1DdS":"FRG8s9Xv2nNJ","iAnyuq22xdgA":"AuiLC7mPSuYS","Tzufo6F9sUpw":"xihs4kSssxNG","L8j1kPjyJqIC":"qIdh5BOzih2T","m1mVJyOkYyj9":"ZzcJYCUZTOVW","UmUnJj184ekW":"7bLSBAFJjqjw","cRWvVEKsf3TP":"2EZ7OEgpZzdO","OF6kkB9d7blS":"AReJzJsYYRYA","pqhPy3BBfNQN":"D7UthrZx2igf","TrKDtadhIqoh":"o5f2a7SPmQaT","yI8Q6ofCopKa":"Im0fa3thCyj8","0gBEztdxogBn":"Ni8taplKQExG","Is6RYTlRjk9w":"0yRdzwwrD3wA","0fQS9zoSHYvu":"KxMSXWHvSBEo","jqZso1zLWopM":"k32U1UeDds5t","j2zoJThgd6EF":"vZvDTTQgJIRZ","JNKPvLf1zEs4":"nVCdRLXwTMtg","QepoZ2pPGlr6":"wNkGNlEnG4ak","ZSquoqNtYafy":"okwirRI0bfad","sQmt3BuuiyIk":"9yNDnOQz5PsX","XIiRvFG2YG4p":"GjuZZd7cTc10","nXSQZHeYwmzl":"gCWBLWoIH5LE","jSlPexGAyqb6":"cJEotN7GRTVJ","1DER2QnHX6ys":"BxC3Z5luC2SE","f5rUzjELVEBK":"Ur3N2JHhdjPY","RUaPIgRkAlJN":"gcRCcL6yEQtq","pnrjrW1IwuPm":"9Bcfp1xvz60R","y3w381QlceaH":"hRRrhmiTDqk7","4FuqVErHrOLA":"PsPNf4RnZ26R","8Br8gj1N5J25":"oH6AOalZM3Mi","941BayJKDNDp":"GuxRhOZRoh1a","nGoxOXJBoMli":"scDHKNeMsEFk","1MmkepZvEghq":"C3szkVnkCNfv","lL0242BGE1IZ":"3iLwaOgohRzb","9717bFupatzz":"qSQ5r7w34kAW","hv3duv8ua3BK":"ccF1VQ0W4FQC","115Fq9W8uwbd":"7QfZPsnZwNJW","7gwCfqYYgQ5O":"J95Yw8is4lNj","aWJCu5jo2Nsb":"VdjiXDCJfoWq","etbVKKOUBSmN":"UOTnPhtMaRKX","Rm3VOHnukFy2":"XhUBH04LgRw4","e8GfY4Lt4jLo":"kN3dmAyins7r","IDXcp21JIBFq":"xaSds4o4xKY9","x9HzatM2sPBt":"2TJxto5bBJdV","5clwC0EnnhAf":"OuXlwjtnWh8t","KiLFeWB46FfN":"ZkQfesrtkpqQ","8VitTNKFp81H":"JqDMGBRrA2Ao","dXiHvsuvAi8Y":"X5DSVT5m1GxC","x5tCXL7M9SSE":"0WxtEeYFDxQe","9kAvxTPpsfxo":"fggbJXdLrSk1","Ld3dYBs2FdaC":"mKb9rOvBYjOw","ittUY3hGfocG":"Az12qgN5mMp8","I6x8VzTZTz71":"PMb23i8c2jJ9","y96cxiWkcxxa":"pIS1INpGcQGd","8IPYKX31jXP4":"RLsEHcUrxjkN","I7YrUR7ofAYC":"YDbbrnXn1y0R","8CKyhAMa0zul":"WyHJaHAmkEKs","mzYjZTG6GN1r":"627bmgzSb37Q","f6ZTX240BptL":"zj0nxB9WoxP7","vnIjuUoLuH8E":"P5tRQc066upO","sqzVhpi2chVp":"pU1TVcDf25bf","pNKnONOLhFbh":"xmJIBfhYwesj","mJIIQVNtKtMO":"u475jnqaLyJq","rfkupG5t6E7U":"fZU9VnTd58k9","Z71tIwdn5ULM":"GEDbTdxfJXFl","v3LDOtl6JnR6":"brzsbHqDN0F6","OvQwnftHLHqB":"rLsqBNlG41xq","SZJOYuwYToS9":"ODUZVsUsfWT8","I9CYLkkGl7I9":"sFwQM6hv7EZh","We7x77CAX6sm":"Mh6fOJ21Sogw","A9IabfoptYMq":"SeJt0gacTSxP","9ag7GMYkASmt":"vgFnjNriP8P6","9QROTGZcLh6o":"CFU4cSB5WX91","zVSLcSIpQEDJ":"xtwSoMkHCvAB","uvmpA9GDdjGQ":"fMcaYKvk7pQd","UqvsWmVTSuVG":"cZw8jkzwAF6I","ALEgZ31fcZLz":"XKbwmkecx1kC","Og6UEVFAMp9v":"wy9dxk6Q4tKz","Zb55LQvysZyL":"V4b847b0xZiQ","o9QcmOTbHXRB":"HT8Q7edD19Qg","Z7KexqemSV3w":"7gF434H0TX1T","BHWkS5PfRGNL":"lezTFqn82rxI","bNUNYMLnm14F":"AQWvpPWAR3Se","XKaW3VdKXDQW":"m8xnceRldj4L","9T4v7pvHIvw9":"o6iGaSbyuef3","7BNifmW2h0UN":"wifUQ5GUZWKL","qRhDMKgFaekv":"1yle1CFL5n81","3LUR1U7v9ehx":"V8ADabeYVJPT","awCvDEp2ioE4":"RPp6mnF0yAZD","wAzCZ33UFiwL":"III6HvYNRDHQ","RMd9tVn2vglP":"gNswihRdVOso","H6ZSoAwP8veS":"uJaIVM20PnKp","AiZNNvbZIAA8":"9jhZfDlr4Kmo","h8fGRKYDXTdU":"YccNfEsPca9o","lfukuv6r8pLU":"qo1Yait2rFar","8K4seAnfOvJh":"YU51CYsJIgKa","NaCfQR14BJlF":"fEtd7373hbSH","XaQSKzWUFIit":"uc4O4HMUL2BF","pKn7GXQlt1GN":"ftzCpdHgwtqI","2haszRyBdTRs":"nWXIWN9lHAWj","hWlNpZkajg4p":"JG2beLvJUbvi","xyKAclsaUe57":"TQZAAoTmmkTG","peEtsHHKPvwk":"yMsfuNiB0b0w","XfOhSVXmLVF7":"GgN8C73MM0so","jpj5XqcZxcMc":"Cr3D2y0ZSOBl","gVtQ8MVhIkHY":"L4vTGrnN5TQa","NDT3EM8hDb8N":"Dq52AhVNh4Hi","xOB4fhrD9CUL":"LK2Rzxzbw88x","HIRDoI5w9LNk":"HqPWnD74k87r","aAZPLM2ML0el":"xjRBiLLEa1YR","gt55DOoNnwHK":"S8EhSA0o7kGo","dKSNInjl8Hn8":"YQSt21k20xOK","oqWIFdT4Civx":"Mwgu2i1IJPL0","YFbWdXJCvx4v":"7xFQGpvy5JVI","3X6wIAKU4oWx":"sD2AlgMPthex","Hz75ungiQQHb":"4Ejl66hurU9D","bqDWxfpBZVcc":"5STC1SpY7iUu","csITZFZkjV4G":"LSMeSkzkCUPx","rpKQTf50flHc":"yVnyRfZovobt","UinfP97Z7NSP":"oPR4x3EO664A","eUrd3pCcGEKX":"3ZIUZV3VgxVX","otkf7EZ5oS92":"NdQLKXLjV3yK","E69c5wUpyk9Z":"R24XGXj05en8","9kLXzH72tu7D":"H8lqkr8zyqod","c2EYlZZygu7u":"kW3ew8ZPvtVG","7R0dfJUGt6Zu":"WZlI48UyqR0U","kWy3VBLPAewU":"coVlYx09prcO","cnwdtAOsQrYF":"fEKbKEUByYwn","ZLQLEa8ymrwg":"NeorlemtZW4N","g6quYfHglpUc":"imJNxis340P9","LAwnhPxu034W":"NFiqM6xqnTKN","T3hbvuP8CjnE":"EFxyZXvf8r6f","tUNEnh5gpmsH":"50pUcf0WeOvX","6kzZjBWTjRI3":"co6zgo8hrAE4","a6RkJ5JbVBCW":"qzBCfkC2jo84","SLKhpaCkXQ2s":"wsZXR4EE9Fdu","lGpQJwofSOmz":"qDqqNqcMmMpD","TA801jZtVQwM":"G95YeEzBMR8P","kacbtsbvF8rz":"Rl4zLHZqiKLq","un1yHO9GYT0Q":"YvajPAvJukF8","0nWJcoxshYoL":"LKXMQoq7JJmS","J6huKk4JBGnL":"uS6kX0fzQbZ8","EJyAJVaZ6nG5":"EsAKRG6am1y3","7hdwiIWPkGJb":"5Cg29apKJPOA","kLK5Y6Z6ZW1K":"YmTFmFU9hgpa","5O3ylfdtDzsD":"99OoEAKvT4Jw","bTUrlIXoGFyz":"Ofr6D6UTEQyH","eJR1gPJLttOn":"agEJfNYIwXo1","aRdeQzTDh9Z2":"yvuo6yNAjssI","HA8L0khA9u5g":"p2aFyCXsNuBE","ZFDGlRL2cPgW":"PzwDKlloNeOC","656U2YbtiAnY":"FueKwou40eu6","io51imetnOMr":"BZPMGj10TBYT","GTf2GY9toM7j":"3Xo21SJabvIw","Dl9eZWRLdDls":"g5gpZHbhwcGU","twRgZIkSGAZr":"zZxqSD0uU7k7","yFhef2fVDzJE":"2ZhYUhkwwhxJ","uPf7juu1fjl5":"2JMoU09kKS3u","pNhOtpxbrgxd":"o9OFYTq02sHL","VqNx0wUAQWDD":"L0qwOowWtijo","XJigFIOEaFSz":"42jaIrVN6TNo","JFQ3pXOGjFad":"Cz8QJYeW4TAE","FEGWK0LULqDr":"VI4VkOuMw46C","cJLv93Vdhls0":"vOMuXebSaJ57","0hK0VkHUfix0":"G4ogq2fY64LJ","kcEocxeMGrpD":"yhMYhebWVlko","tpImWDGUISFI":"5VJrznGAa7yl","86Cz4OjfrCLS":"45lIvHvwPCBV","6doBvEns6Eag":"kyPJhbFykAFG","R10ySqJ2KtEa":"UoEF6uNVuPZD","Ge9mckC26ovm":"FA5U4lzCD4Yn","uhU9MdZ9xTVo":"JR4NRXwDcaR7","NICwp0us4T2K":"NITriDzDiBVe","LlvkBUr7tYSc":"QIDWZmX64k2R","BjpusxZOjSMG":"ewMMRXF9HnwN","BKD7Bw8svLdk":"iEBzk5eYITnn","ZUrHA3MvJwcC":"XWYnWROsZdFJ","wCmY1MU10mRU":"RL5HQCatKXWY","bxJhMKZOxGSO":"zk26URV911vM","QT6DmwQhu3U7":"KY1luNOEERBk","KbAorqtUTaMy":"iOrAJ5hkVq25","yhK9g4OQUSpz":"JPTAkFk8q5tl","qCE6ddUVUo3s":"432b0TvsBd8T","apEiG3VuHKk2":"efJD8p0tOP5f","3Ifwwz1Cipj6":"kzMcUvRihhu3","uZFVDY0imTGV":"X2eHf81ybJkE","jMdD62W7gPPy":"XyUyweMz54lM","N0W8tuFsyqSZ":"QzF9iPKhTBWL","DK9dgSxk86jl":"NyzUf04fdHBj","eYHW7tV02Mi2":"Nxma3socgJeC","rG2bYBoL6DSu":"KhxkZZ6eB2Cb","oPjqrFDjvfBY":"jr14G1HcK4A5","nVI7d0J48urQ":"thmrgXkrO0hj","NX7aZANalUqh":"Kx796NJuzVnO","2wfFz7JbRaPN":"UZvr0bDAbO6J","rwxDBmKyOPi9":"VoQj0rbMfBMA","UXSLA6K4EdZp":"sS6xyaQfZFRc","VPl2k6OepZWJ":"CdEHTNwOoNzu","hspZBupaEnBs":"iu3zEwiDrBGl","q6FmO0J1CTeX":"lDTbv7htcTHr","At00HApyYCLv":"ATP1wLmf76ok","aocervNH4ZaG":"XlDtMWOWi03P","kbbgHrnIQCvZ":"UZPvJqCPg1J6","K5AWZoigdTs3":"bD0xGyRL3ooV","gEfNo0GuGHto":"nUEFpSPXAHnW","Kz2XnStX4jDT":"OgCstdJOOElP","3ntakGV2Mzqt":"Lv8SjchNzlAm","RP4NCMBhiDHk":"Vuee3aGiu2TK","QBJF7X3K6f6T":"qiqZwhVxdb9K","ZHWEjjVB6PYF":"mp8Nb5JhiDTX","sv0TSOyCZ8Yl":"Z4uWfQ5ShErK","1yzlPt06a9kA":"WeTeeVFIpQxK","HPYZ9ZYcOXx9":"n9AjsJCF1DwX","p2TlEOmYiohp":"CwvFhx4FbVp5","p6I7gMpqvIZQ":"oAhKjRzTQcef","knH8Ojj64sKt":"Zbj8AQrzkcTO","xR9QVrtj855r":"evQ74nwjjHMO","M2pi6kp3kmuw":"MwdF9G3kmxgz","3ucW5ioyc29R":"HZ1XDtKDwL9l","dkLqFwmNojEI":"G2hZ1Jmtn4we","zfsEBlhJ2ABU":"QdieasAiirtZ","04NWTVlXrpyi":"gMmhevHrv37X","wq8FLhD78QgE":"ddERfF4rKmKK","Ai06igCHdcGf":"CGhUFRRqtC6I","4kD3XhDzB8Y1":"zKW6ExrRmkCc","NSfW8llxIGJI":"HX9DtgXsxbmU","HpTbtCPvdIJr":"VjIBJhlTq1cc","UgqLhycx5gSP":"5GkU0IKSftgD","aADjRHbMLodB":"A1f6YxvRu598","XyqluTs4SUbD":"EbAmj3TdRZDF","tP61Z7Pev0an":"FO8vzjWewyiL","RyjFIsIyF9gb":"WBRIqOgtYYxB","AEwuYXn4zjbQ":"0xg5cjIlfcX9","oA75jPHLsA6z":"uDgtVh2lbZbH","sO5or7qZZl8R":"WO976X4aoEp1","ftLvDnXrEGeH":"EFIcpyUgAjWI","8xULbrkq4ctm":"7TOORLftz9Ss","hLzLrxNayuMp":"zT75FIYZYkvC","FFczA245ZKZR":"U0Rl6OPAswoQ","XR3aTZu8VN71":"3tinv1EH5rXF","TTkgME7gAP3p":"z4SPSibXKa5e","JNn8Vdg25VMc":"AUwLtd2Xn9vK","3jpuaU4Ukyjr":"LNIB7Mi7xhsZ","ipNOEknzXv19":"TTUxTYz9KUyw","2E8y7dZ5OZUe":"Ms76t4Lspaob","HtITO2NlsC6P":"49UBv4U5Q32F","6XlBgY6iHZBP":"nbPmkRkGDUc7","iKQIe3L5hFxt":"SR2iCLh4RpzZ","F7APy6D4DKsB":"55xFTQ6M9nPZ","p1K7eyu1CdxP":"hbSOejlPapPb","eXiCT0QmnnGy":"7QY3GBEdLoMX","1cxIrOpyQyc4":"8jPRKVgrcoze","2F7wF0gffr2l":"X9s7d6BVRE0Y","ORlzKyCSx7XC":"Z9vLZe5smSbU","siaMgZEYm0su":"9lxv04m4Af1m","Et44b6nZdITf":"xfE1x52OZpO4","2ZFqmNb98bZj":"nwy3ifmxdsPg","GIcWgoBifXZY":"PyEyqFwWKBHQ","1mzCsFGXrjXV":"OAkh0SFEEmIl","7MLIKs5lRYPs":"fNyOTwkLxngg","xvqa6tKeqBgF":"7C1enwXtvH5W","8kUjdHjpfFzG":"GerMNh39TcRq","vB05kHNd3lxJ":"JaUbeYWjJaL2","fuE5P48kQHUf":"GegAJdSEL0s9","LCkuQrAGMD50":"KJ6av8RAWELw","JoNZYOCIqLCP":"o1fFaqvz1wUH","a1ktJEE8xeYF":"0PxQtlPLkAZ2","hnZ5DwFuiGMS":"o7I46rgAkvCL","4DxWZTrPErBS":"3WurQipDlH3q","bzwAidX5iCfp":"eBcGAo9eGvG8","ZsTYOmBHNvYQ":"ZDUZc517MkUZ","db5B1Cd12bMO":"tLEN0YZ59k5y","XHWQH1lmSweO":"xBOzVtecvsrf","PUuEDiEGdrGF":"ZihjtSEeXAUo","nkFBpeEEXL8y":"XSpr0JUityTf","WgSXQIDj0XZo":"sQCw5rZ2DKmy","AHQiP6Gi0d1S":"ND00tk68EVYx","zMKOWcYJYjB3":"Xul7ALTUOgfc","FBrgyp2xH7LY":"XjykWNrqTyPQ","Yqxka9yXfGAn":"3DMVaWmk216k","EJmMRpj9nX3L":"oTv90XalR7zV","q1uaZcwzr5vf":"cdPawPwfTXVc","y95IMbOapgZW":"nvGa4sTIxqcp","Iqel5SOzePD0":"kv7I2pjlIv9S","Cp1w1V3Qp0BK":"usYvSQP6MQu7","un4tePOz08oi":"Ff2kr6USK9tR","K6PNcM5ExcLw":"XIYOkUfQYHIg","tZnPVvQs1eCl":"huyCLNerynTB","5Xodic6ullaF":"igtoG7098116","58p0bhFeIQeC":"XrOBfIYU1sF5","VgyTnwK9zf0n":"hj0e2REOXoW3","UAN4rS4pOnJd":"IyKRhbNT91ee","5WkizQeYKBOw":"u3Wrs0g3jM4k","5ZQwXWOF0elR":"OJGBgQlevvOd","vmFMWhGU47lg":"LFNjAkaWEPrs","kNSZ6ix91lCN":"LdVYhuSrhYXo","HHUzEMEn1dH6":"swfVbPuReaLx","DqcOP6DMoQoz":"xp2WcHa1nDPG","xo8lS8TNYE1n":"EN52SN0CSUCr","OTh8QaiMCjaC":"yDYjV6Cf9oNG","6OUpBqRZh8Oq":"vjAXACMz9sGA","xMBsMwR2671r":"kKjSGSkG5g2D","bTrDUhh9ETnE":"CUducNxEUkde","Kzayi7imhUnJ":"WsTn6tfZzcM1","KH0Ngl1rBD0h":"pCDglhpzVPCD","nLlrtp7zaSOU":"KTKrewDUlmTP","Nd1DxMDQ9rG6":"NjK27sPljHMJ","XwXflqdr6eE7":"VpkoUaKwKUCU","Q1h33whecvn4":"owsW1kebgVxP","UX7gEY6WLKeR":"pdMt7kTCGQOz","4TTVQvQrCu0y":"MTHwgT5qvaTb","m2jylZ2AfZdD":"48JTpzJVjKGb","bCUqTYVtYVCu":"lGOV5Rdcml8J","Clm447SIyN33":"TrHeDpCrpsNN","GtF5SVVUqdRo":"QGkGostuJ8nh","sIc2K8BZuSd2":"KwQAZbuFSn8o","P8hO0Xq3v67a":"LsnPjvjoPfyG","36b3mkvcrknM":"But6vkAOayOe","OzQbfpZ06pfl":"DoFhOp4XvMl5","MeYtw9pz0Nvi":"ccZCuDKZ3v6r","K70T10fcGxHK":"siBYT2wLc1cK","OTEArGy3nJzd":"i7J683ifveXa","yymyZ18FVqdG":"4IkRcP7Jmt9V","iLkhEuVPZiJ0":"oJdZgCVxv4dw","6HTn4Zg646xF":"XlxoHtj36gee","2zJvAlPhCHfN":"9ATXwj7CJwDX","NUd7GwxFe9fb":"tEikiJykr0B0","jvSQ7iyithUE":"5zI39ikac3SL","cOsAw2SRpUPP":"aULfeU8bY4P7","9eyC0A4TC4h6":"OJimcIE5mbRN","dXSMH3LCxIIg":"ylp3gHt33Vnu","wlpeyzeL6rcX":"9EMLSn9qWqUZ","jNpZRg7clTTU":"vDb17Gytbs8Z","fOl3oDn8KBza":"8Z3f7uoWbedK","kkx5VimLyATY":"7KSdpTSHzroU","KmKxKo1M5mLO":"gLeMlkGMohFt","DVPCq2EFGFCA":"Evjjun69oL3g","MUerWgC7qO39":"df1utVhlcpkv","eGsqN8khS5ZS":"9AdgyyEsid01","eumgMf5g1eVF":"3oZbr152btGL","U0EWPLrxiHDK":"kROC2CKl04Md","KdMUEHGrmy7r":"WkvSpa8vc2bL","WQOlS0LomXDy":"shY9jbOSbVvC","45SnTAKE9oPg":"UkMD54e96uiy","YCaOz5BSIYnf":"Mo6G9zc3OhhM","3or7Nis3WVx2":"HNGeeCDQWJlG","RC5CSVrMjF1P":"xljqLk5JxeAu","eRElc8pbUgzD":"0lnaiyOsJj3u","US9oY39rm2qq":"csrR3vDtNH6X","AtB0jGEG1r0J":"d5sx8sZ4s12N","hpBgvoTELBSs":"pmQ50HREYGov","k9oCs1e90LJj":"w265wozvqqky","ZFUsCHAylrh7":"ZUINZcoHQaQr","a72266WGG3zB":"oQCfHg1m5GIy","flyb4aVAkXPl":"C2IVnXRzSfmG","cUOi8pA1Qxh7":"IXMnq7y21ibr","LjnYOOn2JN6Y":"xxZyO2uItciq","NAwsUKLJ7zPU":"WPP633RhjbQp","ODRbGISB8QEz":"4RaHSZIn67j4","sKDTTiEqnjc3":"iKGoQbetLSg8","uKduyFVpkQ7Y":"pYjJhImGKXQh","cIUTg4wbwwb5":"1zAtXYQzUD9C","ICrewNLadBSP":"Yl9Md3kePFdL","QChb4EzZsGYL":"4Qml76aQ42Fc","oDcXRvv5B6vN":"KZ1uOo73Y4pt","wAZ083vuaDt8":"f2TwNpRSdhno","E0LWJFTNxB8a":"dnw9CiuDDv6I","iApDqxvF5k9M":"mKR4sFrAly9L","3BwEYKI9IXzd":"iNTAcijAFO6R","XvjY3O874HIH":"9bYHlosdAZXc","PZ3zylcRg8CV":"hzo20cj9VsZd","MsiFx436uBbe":"i4632Gtg5SYZ","zIaEqnwDou3U":"m8ijV2NFxHHe","1vk7O6fvdI1Q":"7jntvgdpdCiV","hjLwwHRuUhOM":"Ba0jtUxTNx5B","ItrGlCU0I45c":"mvZWxsBVn7jP","sS6pHz4SC6lB":"zHToGQIf5Qyc","A1BcaZ3hRZKD":"SUsXkTZggUxm","hSGZqz0bxGeQ":"VFvc17tiuA4k","0lkMzqc0ClQp":"ylKbZMzhBXT5","lNjIzncgyxvS":"2Pv9clNadz4S","dL8nEZ3XWYEf":"Ijn7eYADCD10","rD4PFv35SJKo":"eQTVRWjr4PU4","Dy4B7iwW0xeO":"GD0uEN6LrQD2","MTAc9tiVKPkq":"AmyozIBe2UMO","yfrFC9S9VFHy":"w5pz1m8pl3QR","v3WtJWKKhJlP":"XJYhuatYK34Q","kdnpA4EnmT8U":"IPDeX3L8lCkJ","N4neVi3jGano":"ESJrkEnOvsrB","razZtdCsCWTh":"qyQGq2UHlbaB","Z10QOvp82wVY":"kZLL5g2IvDjf","2NRwKriz4a6t":"7J3sKxxdo7ni","rF2wZHZW7wGF":"Eemog3NF9ZxP","6KZLbSzeH3Aa":"IofTPDEmulWL","W1X8ig7ShOh5":"p32uMzRwHurw","pwjtH8q316pq":"w5c0at4U8Swg","UscsHf0R4kmO":"CedjO1Ogd454","DQlbaFLVgWs2":"umlFWidWCiYU","41V847M9slV7":"eRVB0e1GjQQY","nxfqtSqqPrEN":"x1d8g5loiomT","FfAiP5i4nbTR":"EW4fs1F9KPFw","JHetXPr3eQIa":"CKgM9CY0BDcn","tT9adctqxpbh":"LFrJQZAH69CU","5LjhjsUrb1Gz":"xz2O053xD6Nf","EUumRcxRg2K9":"w7WAqvdxRZrw","LuraE0nt0USS":"S2R9edx8zYqx","rgd36hYh4Ajg":"nJYTv81Kj4ar","Usl0PmKwGUTO":"37lv3fIxqrFQ","JL7g7WSgk2DW":"x6mh4ZyjTHyc","stjyRsJpZuUN":"2rR3DSUexsqv","9CITqnSlhsbw":"S4mqdUS4UzMr","7FumS06u0HZW":"4msaSaKDMjQv","6O2kx2kedcmp":"Lwh1IGmxUHy9","f92G1SPLEc2K":"flndhOuISE4n","3FTUA1bykzwp":"GKEQAzrTBRqC","7kyKF56TiVrw":"FL3tgcswAm1Y","F9hIbtSo9wWT":"pqqEjK8TnYLR","BjbhII20FLMp":"r82rjsKi2Sn0","2pxCudzPvVvj":"TF7SkBDwmmhD","hJ0GwZXlVcvM":"5yTRMhM1juxP","7NX6Wp3tMzJY":"XU5M5s6vQ8Zi","ZfhUCZCOk6YV":"LpojWa5WbyqQ","lpMMNirycd5z":"GmioasgCz7t7","BVFvz8HrKLVq":"wors307U1VcT","41JdfzxjrsmF":"DfzJ8odLmq19","5uGdEzvpgrkC":"shkdKO12D1HP","j6BcftZRTOrr":"sGvYB6KQ1h7l","NH4mjwEyyuyT":"l2h2LscCoa0t","WmB4z2tvgEnx":"S4rkvM1R0C0s","epeLO6FSCLDB":"qNpboUHb3dfU","IMLCrig4yr41":"d9wxjNIklux0","BjHdyXesquja":"UXAI7CrWkGCX","7Vt2nNAUcUY4":"WqTey0x5vikr","vQFfHGdcxSyd":"irbi9JskpYL7","Ov9g4lIg4SSo":"xsFXVjSh5Fwu","i2a3PFOPt5Mh":"wOkFqkJrgXj8","oFZxtVnreM2W":"rMf7bPAcJzcR","KTg2VelC7TB2":"33mztR8assQm","5xNf4iuq9zIf":"nlEsRJlndXzR","YH0SDgJQroup":"1g1yjErT7Xsb","i4R2MPdiaVU9":"D7TfyYW6kKn8","yIU4IBwCeZfF":"ZkV7WdRNmHSd","yB9GrA7ADd5d":"hGxoEFsehCXW","g3ORDZA57oiK":"EkqPFle2NrJa","JtqT9sz3L1DF":"J6WmM2IwvIou","Nvo6PZNkWvbq":"HWMapKWTxvWo","YmaxuEhwahFp":"uICMkbCkhxLn","LClLIdmmdmsp":"61GxY9uvX14X","hczLBsx8n4Ue":"H4XNYpNkGQXD","8z8TaPh78sD6":"b3mSJuzKNPwa","inRiEXAAG4m5":"tic1zh18m48H","30iiTW3WhMcL":"UEBq1Jen07Zh","GmbEoCFA7sbA":"moPZ6y8kprKz","8LG9DCwKaRHK":"UUsNVzpbhwMw","wr4JT2Ll2G4X":"RtL4NT8Uc2nJ","fG5wzAhkGSAn":"MZGflpSEdwdF","hBKxn1XOuxRV":"hiJcJiFVo4py","hOXZZ020gVvh":"CPN3yUtGpFD0","ETZsNlYO0b7P":"yzP7HNIutA7T","TjuZOHd8ZXjF":"dBuQIH0QNr43","BYkaPW8Nczu8":"IinCWdJZF9q4","o1wsc8HZKz2o":"5hDqNp8p0mVa","V7rIS3kMd1nc":"6sixCOHgGht7","8laG1jlUO9Mc":"McEHYpWrpeBM","WNY7OzCbDwfF":"w39O7Fi6PAdE","lhKHCoMTacZz":"XJiIDndu0LvT","5lhos8GUI0p8":"RyOK59fdeXuZ","k6LcmbT1VRdV":"Ejd0qzkTjygz","TppS8uBh2iJ4":"Ac7FVDsLLdqu","wBlaKDqYTt1K":"9w4ijh95awya","bnOwjvoavTOP":"89imNUVo3XMF","ZV89UX4MC7eE":"mDuCzFwHa9fo","IVdAQafGM3fJ":"9WKKwvosyqEt","e5RVq6uOvLrz":"V2bXDaFRnIDk","sqR7MosEKSHN":"sE47p0xVynYa","JT7Q4QdZEZvF":"blU0HrbjAIJT","uC1KUsQ6Q6eV":"RQYMUfnYUBAh","EXJbJtj9SXfA":"zjGE2CsZFtyM","hFcArKmb8ppW":"MgcHNti8cXiv","KICEHThbE05i":"9eByMldYEYOc","cyd0dGa17X2z":"BAUYDDtSiqhv","WCNfvCYpDSRB":"4JKsmyaJ70JK","bELFzLhZ9h28":"41lRuCP1dHCX","MBPwEacogIdm":"MUmGJJVFXeqT","R6eUNQiXGJXs":"EFgd8sAE7z39","xJWg6oHrhpxU":"t3FEbQ9Fadhu","h6KVISLhZ8qg":"WCw2rD0hDznw","gjmvGOUlCFNv":"3yxMzTd20h2E","xQidfrGPhBUN":"AjyYbj3f2Zsg","KscEdd6bqWwh":"3i0NExn0aJHr","Ae7fgAvMvTdK":"ui9YImvIKkHZ","TuWVXnzSqPDY":"g1Wmy0L5pcjR","hHw5ES9FjD6D":"Y2UV34rbuUTe","v55ko92FsYvI":"Q5GctZjUMlli","c5RzTgU6Zrdx":"N8cwRDdD9cXg","7Y2pZyoqEVIv":"dtMKqlLupEc4","hkhqBkipTJXY":"nkG8J1hdGdya","IPWjkeJylqbr":"4lLm9n6eFflC","S1qcf3HPeEzb":"prbzLFuhuY9U","qtBpK4cSeQHr":"2sTMALtm1XC4","jyCfxGDrq0Pl":"e3CkYlGfsunz","Cu40QRNdprGd":"RLT4kVE0jE0M","9W3RA6mEWXgk":"UrhBjSD1mptp","OgJkD1hxuV8k":"zRf9PgwEIHuo","zRbjH6d1HszS":"mLiRUZ2kCmAO","sYGsFZtcIWSV":"sAaSyrDD8zea","a1O2d5a2gnI1":"368jxe4K0ts9","VeLvWbNpVk4b":"lrqBu50bX647","P9eJnX6fO0R3":"CreQ9ZFA89EH","0poZ1uFtKudf":"164NVUpUfhrm","PglKihGMFCqU":"LKN3NiIejrMN","LhEBISgqkuzQ":"PS08FHKtJumx","t6KvkQAOT62j":"BcXsNEjSFiIj","xyYkGLnGwSID":"Q7CL6evGibfX","nve0RWwO4YcU":"GQ2N9jPVOFUt","pf0iUEtpr3vv":"1ZP43U7ipZCL","G7Jdvd198gQ9":"8noYuqFdYtz8","VvRfxkQDO7X0":"Wdv8BoMU1pb0","Tg7etxhbhDBd":"j5ttiWKefmvK","4ywcbWMGkk6b":"DnzalTJCdsMp","vrhmIUUrHgSu":"BvLsgw7ENgch","PkVJoX9NN4kD":"BurqZivkaA66","y67pxQTR1m9I":"Htxv2rAhMAkX","kPk2063Tv28R":"c5v4dLjFWUPG","22FN4X89bMKo":"fGU2yVSirWf2","DCoNpSmsCMtw":"pxut2RBcsXUV","uuPJIFIeALUO":"BHWWcjL8Hm6F","cSpnNFoZLBk0":"eYLV3qspQdEK","EoHsL819dNhW":"4MSKM4k0avCc","ysQvP0UfgG8E":"Io6S51EkwNnU","IORXUVDkOSfT":"0Q54Tb5JRo87","GBf5II077HyL":"HeChAzKQJOlB","CA4nJJrJ7VYZ":"vjUNfJCrdFss","ExqYzpu6oLGD":"oGfGGtlOML2L","bONdEBSez2xZ":"aazFQPImbbJn","3CI3hADkwEaJ":"lxi4ccSCGawZ","tSqV2uF9wPZe":"KHwmrxZVDerQ","L5lFURZYwWAV":"aGHrXqy6g0IV","QFaOk7B2XmEk":"FkXmexqRU8Qg","7DG5gPVlfODT":"HSq5FHMlhdDL","awcyJKOOkWaR":"PrudUYHbvzQ7","hizb4FcknV61":"TSY9BN28YDnZ","4s7aYcv0RZtj":"YetRtlB91HXn","sbRdaSE67664":"MpEr0nwDiToM","ivc9OAI7mpkZ":"ca6XxuXG2ZXH","pUtQHsZMDDyW":"UbdQn66LXXV5","Uf6yn2iad67b":"ELFuYY658sMU","dPHwiO97mXI1":"FebH1ciu7bDn","ge8Ea6yT1sLp":"Xd6h0wcMh58z","qrpTjwRkikr3":"ac5jN9XhKz7K","JJEbNVkVxsd2":"RVdHRFsgqGAy","GVYaNjQ5RSEB":"HI20tKmRgN1U","FlYAkrFlPnVi":"Fq0odfJkDIK1","fBC6jJTUU7tI":"3k6pUmCoSXK6","SwZ6T3rAi20v":"pKDZyamfNF4O","W2Oq1zCNMjW5":"pnigzDHKWHHw","TdmajrYMSkat":"790MOUzCt0qz","YR0A2nmHvObm":"k6k7hEbfSP9g","V7F7NNsvnlwx":"UvEyKvZ4qPfD","C6lJr7ZKrcsg":"MnOt27GRaLDi","IJCBlLCg4yEp":"tkc7eGgaj5Ha","x08CkrQezAML":"A16edbBIJHx1","ya6ivseMZhvj":"zh1gduVBgUxn","dDFgPwDsAg0u":"e2LVOCJc1rxY","45qWT1xS5ZXo":"0BZmwqAFXPg1","pUo1Nivk3BNk":"xofGiC8XINNc","Hr8RGjGublM6":"DAHYDK05c6bm","YxLvQ7dzZzqM":"11Z8X4nxtezr","Keye8MHNjyF5":"ofjymfO6OiIt","OZqfruevumqQ":"6tLePnruOieB","6pXeHKwipuTx":"sZK2xWN7sPv2","srCHE97uiS0u":"LMItkS0Q8hpe","5E6DLELZRm3e":"eoKrOijfDV1n","DOXBplXNW2kw":"C42mEVDjv81G","G3ZwHea23XoM":"3fd4CXrp5mMl","T2JQpdu41W5M":"fP16kbwAuLvk","dOb3fDS7mDLG":"elyUX4iQFmsB","vizcAhya8xdJ":"SA1VNCjRJoXz","ThYbbFCc8jLL":"3Rugu1k0Fs22","Lf6vLvTVx2AY":"KaIUANsLZKeS","6tAkGgJlKl0j":"eqV282ri2FGe","6sZQGuQKUdhn":"igHaAoyRemNa","ijVoIanvRwVF":"WOo28soDIf4p","PMAWCQvUwuBS":"eESJjU6dr9eZ","luLBYsUrlc8K":"QDx6GyxXEPId","4P5GCt1ME1EH":"phVf0w0jpaDh","EGzArpgY3Vsr":"e04UEVMQIsYh","xQ12hv4WewgZ":"T5ZnTKjZG1oi","sXWCKXE8vVGQ":"WIOQwtjVxQPM","VLwozrn9SP8k":"Z0Na8JkvNyQu","HvBrDnXDHc1C":"R7Hfx6lxVxb7","sqnimLKjV2zP":"eKzCSQhyX1O4","UKV6tsKBJHGe":"40fyj0YuveLI","oUEJmYN9DEq0":"mKymyu3YCGOx","NOLIkUIGOVaj":"OhOIWNNaln2r","yg4S4kuUTH4K":"1mrG7e9w9DTq","L0uDTuSMvfeH":"QYzoeWFIFenT","nWA5uGpGVKDg":"1PlwBuHCiAsq","4S7mTK7sKVKn":"9FDKD53muhrA","wlEiAjcGjWFr":"z4g3MfbTT2kZ","EnO0GI9CeKFC":"Jxxur8h4juHD","XNE4lVzxpcOS":"HSNBOhma0jHN","LFzpUDRWaHq1":"ROeKJrOrNCnf","ZZLR26Um6r2m":"OvUhsuA7482C","TJF90YYA2XMV":"Skq5iPpjH5L4","pjWhGKtusLd5":"1Ht9JWpk06ah","LGaLQsS5dzNA":"NVGwyQ48iC1B","M6qxfHRoFd6r":"fXiuHUqYrUFt","4ZNPbGlu2kGv":"1Q2rCuRP29Wh","6wnXupXK5kyw":"L4TsTyEYqBJI","ic3BLcUT4eo1":"edyhPWsytG4c","aBWzA18yjhEK":"pumsly07Y5Vi","hnCsmenrhFlt":"s415Ln7zkwV7","UbfKaQEmkzMZ":"zppeujEm7c5B","xfqGgvUAuIId":"cYImTsLTsYmO","Hf9thVGD0cqk":"Cr6UjKBnolXN","d8hRynDDM8uN":"1uMYyLLphYZC","uJY5hL9NH8pY":"JyEGxu4wldr8","r47NOg0f9cB4":"mwoHhf4wHYrj","2sGMCHC3bAGr":"CM483lGSLHty","SXNxswe4Hmcp":"7uWwYljNcgBm","r1T8VkZHOOCB":"3zs3lSzhzzx1","RYc6mPwEZHI5":"GvjbmXoIcZnv","BvejH42iFiiS":"BrKEJxxepwR4","WWQyJ0BkBhh5":"uMgAjE8WB0lB","zBw13uGKTCZk":"QJlL0SxbqJho","mYDxPSuDD1hf":"2PyEnYssoVbu","rArbizYVSbLr":"0tT7FOZBYaM8","csnzsQ1El2zz":"p4a6SoVr4g1o","8SYMHpxCuyIk":"8CaVuyESBR0f","mNfTxUzBWvGX":"58BogZn4I9zq","PfPM6hqWsr74":"JokXKVtvutr2","dweaqVOZR5GX":"9vyK3XupaQnc","gb56Ga5JIZ4L":"FqGkxG50oruv","sUZJA9LkoRxU":"hToaDTwGYytz","ZjfO2WCEFYs2":"Et1XXacnq5mH","SkTKFk6bUNxL":"9RtcQvdxVFyV","b2XlSvYKEBOn":"UvT3aWc7b5ai","enKVAFUraZhm":"8oDtfuEdHDyw","pPSV2kaL9ael":"GTWDtOBJHLox","BbRiWPvV8RBA":"98b4wuy2YPbd","UyRvNA7qRGGN":"MWvcFf0Ksabr","IbQ4iymm0Yk5":"zUvlggJiPBex","KE95tImxdiCJ":"g2kjiY2kFEdW","AwHNxmA3Uqpk":"8ic3PemGdQo5","HvcKMKSEMKXs":"s9BLnK5Ouva0","Ii49J8LMHUqi":"8dW9ZeQeXnHS","aDZDkuTXrgjF":"LuvC6PvgBBmE","MvUoEPirOUNK":"EZyJj99fsv1o","fEjEnYzzaR1U":"oFKhn41Y8ZV0","W2woUZNbWyaN":"BBga63EZwLd1","4cg16KxvYSdA":"z4SyztyTHgLz","XKjQHR1GXwFB":"XdWQ5eDwLkAV","sDLntGO5aJm2":"ZVH9AmYicHcu","EGxfSxpzH7Ju":"GbztlQQAhuOX","pqtN6LcthtCA":"0dchityaTS7y","Filvjsz4iOXh":"yrNdyqaEsFWl","7l8T20EI4eDh":"H12r8OXh0yI1","pmS6lUIhk70p":"bsDwJ3y9RgDn","iQZXKYj0zq9s":"IACC5Fxkn4Dt","mKmLpxpJKQdO":"PBdHTv6m9DMp","SRq5rODbYjv4":"1rkQ3JOmMJuk","LksZtjnAhpOo":"nxa7n92pGf6U","FdRAvnZHPHDe":"AG8aEPG7IPij","5MmH4sS4cn4f":"wGyCsKRt8LfQ","lQzbBOkoId47":"ktld9LhNKoV6","CmGCOqyVWK26":"qpJOJvWLDEuz","F9vffGYHCK2E":"zzVrAgscC8Ld","Tpf6exQiGuFx":"tm0Td7180vZq","fLFDKNwqQSxC":"MrkUu9cfSc2i","6nvOi1t6es5g":"PKCBw1eo2CGA","VTfGh9eJS5ac":"1LB0oW4KnLjw","pWLrRH3uvDNt":"3ZzYCu9stIRI","nA3pPGOMOCzB":"H99QgF3wX2tE","pjAPQ9DnSaiO":"SrR9bAXuRaRV","Shfq9Csx5uU7":"bepGF8lNl5qG","uXDpAajIl08g":"rpMXZB9ojiz7","zAsvYGK6hTI5":"jzVmgxBhvkR2","DNrwH9a4ZePV":"h1t2tCRvW1XX","5zF8fStqNXsO":"CRXfIL7xYq2w","cJ0MXMUIbxJ3":"kQKdC85jP2J7","2xLKWHiOBk7c":"vdM5KUCyWCYY","HYxhYeYP8hWJ":"ukSOTvKTXFc4","lDzX7ZPJGQvK":"zfwvXlbjt8Zq","4B7TgBJFp7y3":"ilO5WJNuSFLl","ycavS6iKePNz":"qmLDaugfldRf","R1dQLbuP9Nlo":"QGbbHQLPo1Ze","pdn8Piy8lPO0":"ZPQjLLH4MkZe","iBdATKThL1Lu":"wqdT9k8wdFwO","3PfIzyW8J8pT":"lz4lI0H43cSx","YQj6PfkDDLuD":"AwRqFgXFZE0g","v4sj2HucLEuH":"LgxLZOToNFoB","8EpUhJefQ12q":"W85sOl211WRP","fVyWnlAJRFE7":"mPZfrcrEqszx","IRCWcNWYdxjG":"DpwsdJZEP6iY","HtcS8zmpioa4":"ZylDD6ZD7sAd","Bu8JkPXWnTde":"ecFu2OKu56Ry","Y3aszGRppS32":"N75jKVJ3nHR8","s0Y7mOgfKqWt":"p6l1hWhSJWuc","PymuCNf4lTcU":"sARC39p0QmWm","bKQVHQ2TNysT":"Pc0YWp3yoPbZ","wfVua8960nWd":"FGYALq7dE32U","huQ9hJI2i5lv":"0ev0TjkSBagZ","fMosrHuzTiBC":"EhlNR1HIoCJe","D9xbpZmtqpch":"2xRUMoLrZlMo","ST54jLqJUTgu":"QndX68DXOWPx","QXwSZO8TwRl8":"Ztbmd3zTWAVU","XGhoKuvpV4QA":"twYejFtzkyNb","UEst6eC1ux9M":"0ZdYnYU9ubf2","zPNcv1LOba3g":"Mf9eTA7fv9zh","bvpGSiSnQWI6":"PtNVDhGFJbdi","79uNNn2FANmE":"FugZuzWD894P","KGvgVfjNnnc3":"ATogZ3bv4Yn8","mxVkw0qjSyC4":"7xZl67x1QvrX","85q6s8onIJal":"cWPnb3veOkCp","2GB8LkQMZLum":"vaBaGbR063rk","9A6gnWMTJamz":"KWDlPRmbew2W","cWCFVhegd3GI":"Vt371MroN2Hp","5WDawfRBQXNL":"nrm1psEFCpoV","P0rhD1v5BcQl":"iaGrwYf32D9D","pbaenE8goj3l":"7rIaYaiA75L0","gca67MBJ1Qok":"xZAX33U9EKIC","HX5exHk6M4WY":"amtikz6PhEjf","JYRac8vurZgc":"AZP1C2VDfzf3","Z7SxU73TUqOL":"RxND04ybvGTR","2Hw9MTqj5Kah":"XazEeBXtV0wb","b48LSblqpTGg":"gjfkxqMGvp1F","kokr0eyzKihZ":"y97l3mExPu7K","MMGlWWaw1OAn":"ir3OcEJahlER","P1VilMaXgH95":"Msaq5cfc10jP","EyIMXDG86V46":"Mzx1Z7KSSBwu","jM4PFSNKoutk":"zJ5s6GfgYj8f","uITSBhLNsVzk":"FlNeo4QPPKZk","QBL3vUVo1Ci7":"JqtjZQ4otUBw","CGsg5TUIPMOD":"eGANkiuPnpO5","mgMOWpiBil6I":"fswAEKmmtrJO","hZzIgfjOUrG4":"jzR0rX9cqhCz","hWWaBewgAb1I":"Ww339QYzCw9p","rNQjjEPudr2y":"hM0brEJAMKW3","opOwlCYdDNlg":"EdVaSRGBEvss","5nBZZGG3skdm":"zmKA5kh0iaBS","zTrZvNtKlYtV":"GWkZaV68fxZ1","tRrKAGqvwQnn":"cGp8KuZevUXE","juOMvcwd5xFZ":"sRRQq1eR7O8i","jDNJbsvU1NeK":"BINTgjLRmhaZ","IxCGmdBI4MSS":"FDpr999NJejV","XZ4Fbxwd1jK1":"GiQp6MHzq4Rq","v3mSm7KS505H":"mF20G9kQyR14","EsLqJsPCVcAt":"gCMcRX4F3rbp","quo3EsfQYXIt":"IzdSiuKdoFQF","2Mm7J8CLmHnV":"TQmjuv58vqws","QgChH29wyzfe":"yi0CwJ4awGBo","0Bl7T2HYC62Y":"Pp8AU44y5Oyo","Zcd2K92LNNGf":"mHqh8c7VMQoG","Au2kIh7q0igJ":"3mNhgwmPXqzV","aY7fhJjBIeIQ":"RYd9oH0kZzwS","Jcz0BCtd6LAj":"heSAd9zzyOBF","gJxLrL4QSApV":"NHxM9h4ql1hV","ZqqnANyneZ2T":"Y05jSA6BvN3r","QtewnJg66LP2":"5vbkrtNrx76A","Ho8XmcjDntac":"sbwzzOIkPUXQ","32WFecMPupv2":"96Zh0eSxpY84","nDG8xPxDLFC1":"4cVdxsuA44nK","GlqkJ0h2imga":"V5PZlAnQ2kXI","3mZ2pMV40qFp":"dOGOK2xiRbXe","DuXJqm8nzEpJ":"4vI7WB2PV25d","s6ka8aEVpPJy":"jmPwdZGxdB0A","BCM6WASzJf8u":"MSwgmMuSjY5Y","Z8ljNLZvBCnR":"uzSDdmEC4zMN","a8Sw2rLVADaF":"GnKd7TsRnhmK","HULnpzTnj7vU":"8fHqxyf4CClq","zy5dBI3s3uXW":"c3nz71i0PSkJ","rOfbb7GtZSv6":"HZppopW87oka","qopUGyExqFDC":"fuNBy5p2koyg","X9uxRygouZJu":"mfRrzpPhiDrA","KnpNIlxHVhIc":"8FefPyci2Vhl","VQCGRQ1pRvE1":"1qiyFIk79HKi","l0r3uvI80DvE":"hsnfRRgUfwAk","Gm8zPiHBW5YJ":"XHatzM76iVUI","OuPqr2HeqtdT":"9EkRQpfd8LrT","FMK5Xlsvma2f":"HS9mqPoj33SH","i3oC9DUmIf3M":"h3drBmqbmk3R","Fe8VcL9jIunh":"7SnPZtLtySDf","XCu5pBAFBkgm":"p5Wrg30fBEFE","3Ebwvjk5wB9o":"V1EYZU1olwxu","5pMwigyW1A2Z":"XyoZMMqykCTp","Gj4epwPZnzMr":"Sa1tQRqwodzm","j5sjWJ4XNX2S":"SfrOE5GZ3AIH","3vaTsaWb6t3i":"aRoTdN16mYex","J95YmmFTsCdt":"XSbcZbcoVgne","CQNySaZcGNaB":"4eb2wx0r3tft","dY9vufw4oC8f":"0rIuogNY5RSY","VTegtOGS39dc":"x3iQlmy5R3XM","FDuVGsvQEOzV":"biTzaxq9Pi5T","1pKeIPpVfzm8":"u2xN9LsNgPAv","5FJQi2qRYov8":"dXyukJXXLr2h","Lr7C5tTIxLNm":"uMmW8GHoARQY","dHyjIlHUjL0j":"BnjHhWQmc89X","7vn3wDQZ8bsV":"sIczemXFgVY6","yglX43dyzuHS":"OQuGa6rJSELr","NtqjNmuuoTeU":"io8Cq7GSMFoP","p8OYFipcXELK":"TqF2oUwZvPoZ","PKFbVD3f1vNv":"FDlj0L8jujLe","djXjcw0JYWRf":"eU5PHpEgIp1d","RlpDA6TzrLuB":"vT49qDcYsj3o","hsBp2mSQMV4c":"DNC74WmGS2Eg","s4rEZVsqgpnw":"alKyqkl7BssG","h2BWhtcMgK6F":"6Am02UfhXNnB","ytN2VkuHGj6K":"FK97TvNXVwcC","bFyFVEiQJGuX":"n9KVjSwriPM1","7gn3K3v7M48N":"1FCDndGkNWJS","jiPTcNNYA75C":"SomROSv7yoE2","8sboI9snEgcn":"KuYKt28ZfYZn","oEVqrHTugtfr":"BKoU71h0fu46","9kTAU4dDQda0":"3SswA3ZszKjQ","0jjMD7TVExwx":"XpRdq5d5EbHH","bO364R57kAWY":"UAmDkXDuaTCq","C9WHFjnuvGy5":"CIp9E76hoYdg","zvhSQgVSXsuB":"k0psc5NAm7mc","ghtgKYM9PCdu":"RKLG0q8AX9fP","Brs05h7iYNak":"7quT9Uw0zkBC","rBQwOeC7JA7g":"uumDaMKBgyvO","bfA7ayQlWtHO":"kWDdVUx2QieF","Vjy7CySuRQsB":"WTpqaWzCNOHP","KK6Xi2yr31OF":"waZR2axUCQb4","eYjjbF2hPD9V":"lD97MpkDhVNk","bhQ6esKARH1F":"2BClZJujPEFr","ZXRsSY9MKmFA":"ZO7FCiUCfDDu","JcEMdjZmYEn7":"rVdy6BJcUycG","YCRqvYo3MVA8":"h35MHqvSWitF","s0WRYhdanALi":"g9ELGHKMTutG","uuVr06M1Eq36":"0tCeqyuJZkQi","TYpBVyV8OsnI":"8tUZnkYCWNDC","VkTLVWtZuDju":"8U8XtJrtSlrf","LIwbgq5P8yiO":"fGLAzTo5HQWQ","brPxFgt1ncsp":"y9kXgG5irfZW","kGxsNLvcvYCX":"C0XlaGzZQGG7","TWcYrNVpyMLF":"JoVL92ksoU2n","2xy6h31HHR2j":"W1U3nZU5fL5D","fxqrBWO9VCZ8":"yT82uE59ksR1","NLbvhdL862pI":"xYWaLSN3gCYx","zDz7KMPvAkn4":"8TaRjAYUxTXq","d42eXSrbtRSU":"x3VPTYKW6kKj","ZdU3rC26RNJ2":"3bBpxQ5oAXqw","8yt1QMft6f6Q":"JJZldZvWdDN7","x6KqDRYBiyob":"KzPdZfC2Ydfm","XECnWrR8oys1":"EJeetlXPwBfR","4HYfxuEjeGrl":"ICEY6zWnC1d4","jpKsyYMxP7e5":"JNHtavlyOaPY","hhLJR8J4onet":"ttpIKUybYJPK","BlwchazR9bns":"MHxWHrXAXXpW","dII40AZPzFKc":"4JQEkHmDCTvy","8LceNFqgp2tL":"QVSpRd4niyhn","RyVeMNXcu4kX":"gjLyrs8Zgs4s","kT3rk8pxSMRq":"3flonm2DQ06K","ZZQXiKgCWnAi":"WkzTthelr9xP","MvfmHfAfWg5c":"btvFgLP8KmFA","mAZEMWU5f4X1":"EBbfxcTaINLc","F8XISCwSGTxb":"QYMihgQzQpZd","ju0QZ76NfGlk":"aLIGiBmT0zZh","KEEuAAvKIfLj":"P0DGsQAwaJDD","qxvXWnlgArsm":"jeWzoNh2HjJy","b64aensECJ40":"KeuftXuZfmiq","l3cwP1QSC1Wr":"Wa544Bi7jLpg","UUhGWO1y6znS":"k0J6tJkDIZxN","3GgyBHx5Dp4T":"lxemlADxMNDW","R8fvUqA5KA5U":"CvpAacrheOKE","wWT2QLuN1MGz":"2Mm1Q92l9bfq","Z3EohtMORxGK":"IUmcdkbPdzz0","TRVWcCntOBzQ":"QpifkHgHrvZv","qKe7tcl932og":"HrOkauEhRz8Q","2fBoXhlHul4f":"1mLSLp8SHyhl","iBcJ4S6K86do":"HZVv8pOS5f8B","EZUv5naRYGsZ":"IbjGQJypLGGP","6vsPbeStaCCN":"piQFQIpcil9Q","i4DIckBvdv9l":"mvpqe85fZYpg","rDNn6wV89d1P":"uMi7p4aUk786","4tnQdFYOcJjS":"65DeB26f2vky","Arl7AXCYuulh":"BotpAXCY2a89","SOlKs9Lo7PYg":"I2gaiPvdQg5j","vFCpvLGHiyuq":"WMIRkKIvfq4V","5pJUYYi9741r":"PTeFwKt1oADB","DY29KkH1duVI":"2Ob8n3cI1iEE","HMaIkFG9NGSG":"Cr4FUZoEo6dm","LIsUybdcaeZz":"XCINroJZJqIm","oHJy9Aj89ypu":"c7WNMb46iScY","duEOsdcMkj4d":"46huOFIXmNAO","VYmYJHzGa8XS":"xRs20TeSv44p","kzSdjxoxrJ6S":"vtL8sweBWhfG","rtezNfdt0vnH":"lPBiUAUh9Xcf","jelZMGAsYJoZ":"LAjfVJ2hi2zd","dCfdixFuHRtz":"qXtAv9D7ETg1","aILOIiIGX9e6":"q8ioufmINH0u","GlelvFGwo9pJ":"K4SnUsWtEY3B","4EhvFk7CQc1i":"YEaJly7qKO3a","5IMMz4efBEh7":"i9LR50XAYqD0","CK7f3jBcH4ff":"jiqw8ko0Beoh","5fMoiiTP5nKn":"0brviLOPzxMc","ZKmOJ9b8llZk":"XteYmkxoUJKs","IvxYIqPfmsq8":"7wnpWfPm3dKb","WQRxQ1kWtDM8":"YjfDMNeaULIe","3487tTTy81fT":"1RUrz79nVMwt","pbRJOhWYw6wm":"TSmBuXUsiZSY","uSyLAkXhq9LU":"4cBCnnh8ZDrk","gO76mXeQsUKC":"zZW8a6rSEuDf","FWnpqTg1UpTF":"S41QePNmZx6q","TAOwFuJWpIqm":"6KO6JvHy1831","vhkX5xjai0dO":"EKUDbQ3clsLx","xtCXqaKRvqYv":"8mOY5GdVeeO9","DC4jixJC0DGI":"OjH88HA38gEe","7nQ6MWST8hA6":"FpwBfQSq6Gp8","Eu2U2dWKO5DF":"dE5tSquSIwpk","W5KtyfGofgYJ":"8OBbjY3MiWwT","Pi9WZszbVtHW":"3nndCfCw2bWN","RccdOYXItlhW":"FTqPHMzjryJQ","9vzbQwdj8QKd":"vZrsfHexJB6E","zDVoPLNllhtM":"3GNxxcJkfenV","z6AFxInd74Oy":"bGmJqw614VDu","6bndA2QQTomN":"1R4S8lY9PBKm","3sRXGQPLzL94":"4OoyfTWBBplT","6H1u9hqxqU1O":"0rXTbpkYP1kq","6QMRfPig32tY":"n8eR4ECLRBLd","uu5m4DmaML2u":"TpOOt4gOHe2U","7rZMxv1aEf8Z":"JWyROOETjkyE","d48jIdTasLLR":"OWdM1fV7nHnB","dJJg9qelevvD":"aNy7tkP87tcT","3zbeDni7Nede":"qNjdwpPZbSmW","gAYCGxp14vPb":"rgd7xRhVqQPh","7Fd5zH7XGiwT":"XRViYjTM6LxC","XvuyYVME5DxK":"9bgNW2VK9dhx","m4Thz1bIVJr5":"kjevSBxpn4Cp","OlwDDaEF6fTC":"MkOae9Jbl1SN","GqhzjxQ9EPR8":"y1w8n9l4SEiK","EoCc1griDlnj":"iooU36BKEf2P","09QFCWCB9T4d":"yi7NKZu9C6fZ","BjksqM1AdJ18":"eeWdQmcIrnks","9DQ0QpZbDjJV":"5leEC32vhuqa","WUZBz8iBOfnO":"OwqNPIVEU0nA","SN6p8U6SqGN2":"iHfxLTaDfcJa","qv8150HZaLzq":"sBvjXY8FyAId","cUlAsRCmOqsQ":"bq2MPKRtNajO","OUEC8v869nZM":"30AN0DeKcYtY","wGW1z5r6CwBC":"blCvh4SG8y2s","GfRYcgt22C6x":"B37BNx2GQFHo","iufjbWiiWVa3":"7pkADgXe8WlB","zd7qrB6Zc6is":"1o113au2Hf8Z","bYRjdtGpRSrP":"nqGbfHeTbOgZ","wGrOtrc50qy8":"dDQ9O947p5NC","kzPHoX3Njr1V":"UQvA9iPFOVp9","EqAoRXssjfCp":"EPz1fttxSEES","wTTN62sr4Wnp":"IFBVx7DnxiMk","d4x4795CGYDF":"M0WZCFAoZaaH","VBXhBuNkESrn":"5sD0LySIq4Bl","E08UpaIrcSOe":"0JKxACaB6Fg2","cD10LQrzZgEZ":"lFQybwTuPPOC","Bo6HADlW6QKG":"rfJe0PptEJni","JB7s2hpW0euY":"x2AzHeWMYuuN","33dtEfVZfpvX":"j7addDWEAFWB","TBKZ9s1n2bnT":"OLJqVfNiO42O","wP7pruwKHnTr":"HxJhSLq97Rvz","4S7h1bLJPcVX":"O2Mv70A61KZW","29ySyW8FWh71":"iiK2gMkm7u0T","TjkcEAjUU5bx":"SYmOpAykBV4P","uCSJV9492jZ3":"111A9KdyWhwA","sohw3Pf68LgD":"hh3wIQz2zN62","u8TPcrsPqCqv":"F1HLS1vptrRM","4LcvXUkxLI54":"LO9KYFKA0lcm","wWv4XLGo6WXq":"4JntV65tE4TI","mLPggg0pLLyt":"qQN6mnR8AYQX","6F7qvU3oesOj":"jdf7wlIymnEL","DUXe0CP1mSi9":"05XmfT4zylL8","1HHtdcX4unUW":"snkGe97rriZg","GmwpXlLvaszL":"Z70qGIIUN1er","3ibLgBtOFgZZ":"WnjxuPhnNjZx","bTTW50BFXuN5":"VSYblQCJgi1f","Yk6bQvTXL3z1":"POjPDDvhWoyn","QyW1TLyWblYS":"SJc0ExdgSKqP","vZDl9EL4PoX9":"oMpqeDWpuV2l","Mb1sEaQP4D4Z":"RGUrFbCnjDec","583Kkpk1M6nJ":"YZJ32rGb9vIO","upqz9q0SANyi":"OEygCvpBVyfE","5yoCLs90yHQM":"X0pRuVGcLbel","iRPEpiDOHlbQ":"zZ0ShbYv6hPV","SPDQYlapCXFP":"ywGC6AhUuypX","6lR0U2vSYzVa":"5h8Br6u2COOe","qmLHVZhlJMDs":"5r3ruhxCzDIz","dRlkBrRgbaa6":"VkcPX8bg7cUr","F4o7NFk17cuA":"fZyys6AfNvD5","HlK76xSkrQiD":"Ad5spRBrHRM3","iX6rC6tPaaHs":"GgX1zG9mG2gP","sF5UAWxP3kyp":"rE9yBs38Sbkz","ZEpMdZydCjsz":"GYPqlIZtKNaA","lq6yeQSa6FL9":"Re1qezYA610C","n5yhqBjB64c7":"H9oQsMRRJOuD","HOXb43zn8802":"iywlL5dtEkQP","USkGzqkd5S8M":"NQkfTU0VHP2v","1pde0xzfN7hh":"yfS90Zvyx1gZ","SrFEiCbIH72B":"hl6Wd4jBge5N","YFen9LASBL3l":"2kMC8r3H1TU6","Y0gpWTzgnlPG":"pYdaiQEgM4rR","QlyXqjDNSbP2":"PjTFGwOW0R1j","sy69stKybQJU":"BKBl2orZuW81","lsqkK6tr6lMO":"IQ4jV2R9jW3u","1mLVFkJrHxUh":"74zeFx1hH4Z4","QnXb9i2CEcRW":"RLqsGKuGDkHQ","MbhQKnFeIlt4":"espg0Z5x6fk2","1A7nvDjWi1ub":"aLzTKhBmHfxe","ZoYrWTeiQTOh":"L8W7EYW8Yy6L","zxMxcQTbKgIg":"jqBoOXgx4SrQ","m0CZPX7djROS":"cPAyCZCyFuPx","4MXtwGdqjzKF":"Be88OZFCUe2A","6gNl3Icc2lNu":"WjVEArbWyQDq","4aIF96ahiMK5":"4JDWyuVK93Mv","PYEDJSdogcJj":"cQ9lrFxxlA5f","8obeyCkDE1Sk":"Qy688vpCJFZd","wki5b8HTUg7k":"3jA6zq2fjDtO","Dnik29kreFEK":"SUQhtNuMcAyr","EM8GuekUlh6h":"CigB3NJYWlW1","CozSV79j40rM":"fgVUjgUbn1dJ","PTX7FxiJBCki":"LqJtbKViAOkp","JG4JPdjEwv0I":"bvH2jQWGasaZ","W3hG12XEblGR":"N2elqndyeNOR","GVuYZN8J0TPI":"yv4U4Fw1IdJZ","XT7jPV53vSAm":"5ZmUi5wPd0YO","RrF7hqs2GDs2":"zyUea6YTeDga","sMDoMV4SDsGE":"9OHl9JXZ1FRo","UuZHWoAjef5T":"63pOWmB3vuGM","9zRUQpygl1Mg":"MEVmO0iMXByo","DqA5B8hunoGM":"hDbonwaQYzgs","ODp9HGqfgRD2":"RUteopSAQ4n9","R6vazzH0JYIP":"l05r9vHvbpkC","X0jLvbBRfqhH":"ywOH1MfRuKLV","Mb9ckBI10sfE":"Au66d8yOYJpT","XeVj3fGHKlGM":"fL3elFRwy0RY","fNwlO9EYsIbm":"ShAEcvtQwgZn","ppuQDvQuQVXK":"f4juslVUv7qg","VjAqwIelLb8c":"tg2HP0Ym62gS","yuCVObgGwFvh":"IcdkdN2PodNi","X2qMEVHrhkM4":"brQIAwljKcoS","mHETQBceB4km":"hiuVgJOh3Cbb","F0EBTUC6r7Ms":"mOC9hp4cZDhO","KClbFN9nJniq":"JULnuJnKQM0u","1SoDVrstldJL":"2J9jXXCRwuIJ","1ucWXr70DJBU":"ZUH0w01DaHDh","A5b2QySDInjT":"npxy1NFxu2xi","7FCGuZe3DBRv":"ClCIl2rOiDyR","2DxNZvewqXdA":"afj3zezuynEV","7Xj6blhlZY6I":"c18wAKjV7vPo","p8lh4q8Z0aAY":"gI5DnVeKSIk1","mtmCsHqbbKsU":"IU2pdxxCwjXd","9WfnTgvoBaQh":"Qvpb7Mallddt","FJo3Wjb1rV9F":"dAoJtzTqxSsW","LLEaZcaL73kR":"Dao3KRTDKjKF","0v69KWqTxkSU":"g843omSu4OYf","yO2WNixApbzN":"uEYdL1edAwtM","MFPkPY1UEErO":"Hg9hR57IWdh1","rOODb8k6GREa":"lpTVpIR8JQ8Q","7BlhusG0BRCu":"TbKeeuoH07ve","If87MFmnG7U4":"5Yges0ktVZHh","8sHsp2yEmPL5":"Vz2H1wvBjRze","HrLexbtgeUd8":"rqGOiOJiw0o1","5X1qVYQsHoQE":"BRHHhtzjmPIg","8ja7n79DOFOz":"0Pd0fSQDI9gw","GS63RV8YjOoK":"WTUoVWxcjPFX","TakPWLYrUbCT":"aw4xuU5V8Gk2","yOKKu5h4CGyW":"nwRaKjBOS5ws","6amVOSB41mmW":"bwhCM0dCZhSK","sVj7d3s1H0NF":"P0beq4DJrJgF","mPpMFAVTq5N0":"8ffIjzRIegLa","nWTGhW1hNIHa":"7J9ICM3B2PVt","u6jPp2Axk4Ub":"cdwztmhiTxBS","pCSfATd0yxUr":"O7tyk4XTwfW8","JmwftyAD5Cdi":"t7vs52841Iia","dSEENSjJEigt":"UK6Y7FOZnMis","MesYQMbvf9QK":"3U8WTSkxCjU3","DeqVzi0f8CVq":"Ddt0zdJykr4z","G2cgaENTAaGZ":"ytuKBOE6OYHF","sqir5bGxhTbe":"pcHMWQup010Q","dxUqAMXA6rKc":"vgKTBYA3d9Ea","2kdxT2ljRf6O":"DNcQWTonIcfx","eue36b9xntgC":"rDjyOGeh39ms","fRJHWeckgC8W":"wcQ9vYPyKJ4g","6i2XRZ3VvzPM":"uXbpSsAQGMm9","bBwqGLY9zP1q":"shM2mVmeDr3M","C8cYrokNUbsx":"h25z8i1QsyZJ","FuIJXiEmXuVl":"0mmtd1Lhc0xk","vkEtGSXqGy1d":"nVGpOAaornP7","I6Dz0Sc2zUJk":"ZTMk98YWUOAs","lEhPvy2NXey8":"Mu0ZVzaBRZpJ","4mMuab30r3lk":"lzQ5lSgDhmV4","G8phz2fYgEqi":"TNuMooTGtPCG","G2XL3y5nI4zV":"dVRVjwmGaiZ9","wyqX7omayYOt":"tIpAIDDPufay","h5GphZriZ0M6":"gVsyWq9IQfUl","pTqEre88PfxM":"usnNHZQNRfX0","RbgzlTNd98UB":"3E3wpjAWiNCC","zH9x0CffxEfn":"kdHZqjMICKgm","wSvMwtC6Fz0k":"6W6nZm8iFvdb","dUWhw2pfRaUA":"N8gNNSjU7Chf","WPGW5uUjFjhC":"xFfpQL23Kxiq","ZwzOfjRntkIX":"E3oDbnydW31s","Mf5bQIeUerw5":"WK6FEIWrv86v","iMxqb4J28uYL":"DS8X0bmVedIh","0WbqwAFXM3xC":"gkHdkS0CEaQh","EdjgqMMNzcjX":"NISy3fW5EUnF","tOQfeUi9V4DS":"jPYzwssmL22B","gor1YWOjtrR2":"NigTE2IZdsSe","tWnOHqOnwbA0":"pbx4qXgu4z2r","jQd3z1JY4bKq":"PfELmYe4gIe5","ZPH3LK2IiMAa":"3ZmCdriTd5Rx","1aI33UAan9rn":"vfzDaVZ1GdiA","zdsprXsrotLF":"RKpbIzpUfida","1cneihMm6CH8":"IQcYHSNDspee","17GYvQT38IxZ":"31l6XzM4pwmM","JK8wGnbwHspJ":"RGp6sF9HjTU6","i157yQgfQRjA":"F3IYPCH37aVn","izgXQWfkYnmO":"XyTfGfzpskGq","JiTLvZd70Jkl":"DtGt0m0EJB5a","ZIyc59fQdAjr":"2LeJyA2Ph6go","xwTCsRDf5sgX":"44xSOlucrTDd","Usk13YbaXT9M":"T8oqJM38kEbH","3cysRzsm6T3j":"v6rKuf6HmiDv","WlK7Cvw2TRUK":"cLiyTXCmsJ3J","FSyKXskIHxgu":"rfB8cTv9UxZx","SH3cO2VObhfM":"7zUYbqNIh2rt","fzaCrSj7Fknh":"6Z9N6IJyJYBH","CVAxFO0pUsYf":"OSm0yKgzuabe","UHUlkGjzEMz2":"lSlGIYf7nyIv","jt41f9G6GeDy":"mg8rS8noD0hY","GauPtDicxhwz":"5gHCUCoG6gkN","r2Z0xKgf5GIx":"Lf8zyxsrKcrL","MK5ggZHSfxKn":"dQ8sJj3ghxZ1","BkGUsyJr1pUH":"nQ56J3nKbIDd","Suy7qvOV6G4J":"5PFYrVnfZENf","ojAOVKWLTD49":"dIwViwODzRkF","yFAG7tA5Vafi":"U6zdKKGALohI","92uF4cMz1q1F":"4xzeoRxXluqQ","KXofnDDqotJu":"8W1w83FF8B23","nRV39x6xGxmO":"6PYgMqZLAKdD","ir3d1WGJmHMY":"Bn2ObEFmo87C","xjlwrm1K4dPW":"BoB9h9xlRRK3","6f59zoVHNomh":"k83KyTt8QbVQ","wEVE5FZiSodT":"HDfQ5e21tJmd","OZMNCVi3CMU3":"d60guKzYXYr9","9Kl33xSTR320":"OfYPhSwr7wrr","GFHzT9IPv3Ic":"zRgSWgHJHc1Y","D8RT0ANIxkU6":"5GQK2uXSjhFA","vsrrk3DrMmAL":"Z7zIbAcxdTW3","KkVPajxVlrjh":"7KQzPH6o2ZKw","Wq6xK2fTAoKT":"ZnVJ6EGnmfxL","IVwwmqXVBLKb":"pffCvirAf7Da","HpT5OOFJP1pb":"OWHkPoZFZCrb","Qv7fo9OJGaWN":"6MkjyUfADb0g","pJ57cbm5Qw1l":"BPTP3vPBnzJl","ohBnXdN3bEqZ":"zHcASjXIHP4n","ssWeCcmNJ43q":"bm0i7Tw2ovcQ","K1M7dfh1ygO8":"cSIaaHYeGPNG","EhKYd7hlCOXd":"hFzqzr2SMk7a","cDLne0lNbzIF":"o2d3z56f53Yj","VfB8aFNvQy75":"LToU1dPk4xPs","sd0xUQbA9jAR":"02pp2p85hGfV","CqeHF3XJv2HA":"wPRztTQVYZ8c","jSQwf0cj5DLZ":"8T1BN7W2bUU8","l8FnmXUHguTN":"tReZMB52qYO0","v9nuUdldOmDC":"23vGj8m7RRM1","c31h4j3VHpQ4":"WtNNgSt7qfPe","ZKUwV37EhmM6":"CO1hWmM6I6xR","gO1KY6nIXICi":"XLtEsnAaZAU4","Q5kUk2HMU8yd":"cqGnqjMJ9jKX","6VSSsytKq32c":"rcGtaliE5icQ","GljB7xDiFxyT":"itbiWY2CTcpX","Xzj01l7keGAL":"N6IAeUiZTUj0","NauGR26KeLfs":"ukntNSKggz69","EcarGEkMKlbt":"lenEiOHjlpp7","CNDBuJUipqks":"2HALwpSftRLS","xQm2B0huFeLi":"Sta4YQYfJsLi","B44xHCn1mT3o":"5L9vVg7iK3Vj","nmP93EAxy4Qn":"fhPZYle9WTrw","fnPlHn35V9Cz":"cKjvyWCHdsLs","znYCFdftC36l":"odzVro2w6Ej7","EFQjBLMj7I12":"eRqkVuQ2WHn7","XUlq9ahR6FXF":"ZDz0nOhKE1X5","AOBnMEAp6APj":"5keQpzBvQRtC","j0TxotEA0mhw":"KsZdA4219lKq","X5xiEAFSBzXF":"mw0KZZW7T9Wz","E7a8TTV4wauy":"jRrrfEadV5ER","aieoT01AxQ0l":"YcaB2dkHrSxV","q1yok3785oyA":"gEkWohsQwqsa","XPeU5DCQDMcI":"US1QqAkIALZr","amyAYG89LQpU":"S3kosTzrZFhd","hzSigGH7K7IE":"6cQUw3An7zdN","gzV6u0KGGRJ7":"rbBu3MroYgE1","zp1UvZWWMRcS":"FKxruRDQRlhY","Y6BzGjriJHwP":"PW8Aes868nB7","EI5I3qRyEUQ5":"EGZf4s2IjMNf","nbVDbxooWMsi":"KDTOSp0LyRjG","MZKHwBnsNo09":"VWjYz1Hrqjnt","HDljvkdnoAy9":"FQXr0FAGLe4p","NqeBfo9spbxx":"CK2PBtDirI1K","tmYed24HyXTN":"Wo4cpV66pRzr","YayxEjYfwatt":"AKXsP9pZniRH","ycDeisgshBJi":"mY2WiA0VKX32","fktXsclvJGMd":"80Z20NuZp2Hl","cJ6YD7gDhgFP":"rDLAIJt2dyID","oKVXOtHeOP0E":"s2xf1QKbKW7C","EBp3EIWtvlJa":"FfF99VMkf0ZJ","Dg4RtUtL8iYo":"DhFhxgZo7J5O","CsVZRsuRL1yl":"ZCgPbP7BMWZ8","8GMwWmVRUrr6":"3mLTzMlDTh9m","UFbUrYRC2GAB":"vso4hiDTGwvq","cSVSRF4B1Tdz":"h1D7pXAjRJ34","UdtuHE3sgpNS":"bloF6mRVdW7P","LRQOS24UqAE6":"Kv2ZPgRMtYBG","gq3nXz1wZAIS":"QxO6XOEj6HTq","1MEUKkLemGBI":"tdcle0OoH4L0","Lnf5O3ccrd0a":"ZaAFq0SEnF6e","Ph275uWfkHtm":"CMhWcQtlSuTa","ILJ6H5E1gvWr":"gR4XEPgzty8o","cjQRWT49bVJw":"H3AoKwy9wbOI","FECSfotR0VES":"g0ylS2wobZRv","V8d4seUychW5":"dQ1uNg785cnf","4NwOOEnQW6MT":"Gj3lpY2A0V3j","WL5Luszhk4Nv":"OxuN40UfwAPD","aaS4YTb2GtXI":"8VfBOccbrnZQ","zQ47ljTvBIkl":"cbzSmMidbnDY","2Au2AJ36boIE":"JevJzVEiRi4i","hzAFiiQSz4Lc":"etGfC738kKh5","8W87rPL1fmFk":"fvg1mhVvRmHo","RAAIeXiKX8bs":"77pAB0NeTpl8","4kAWYW5u0a6L":"pYJdqenu8Mej","qGXIZt6KtGST":"HQHfpdP0sc8t","QV4ASYWuFFAW":"6t8dH2REvWxg","Vmq35rtmKhuB":"tKnBzzdtAaHi","d7Qqe6urCZ7Q":"P24sEvEkYdBy","yHihL7BgSJC1":"WDs3rCJxkmgm","Y2X61NpKdxaL":"5afOt63BjbAk","uvwQz31ZPXJ2":"vRveclrqbAxl","QEM9G6Jdpswc":"33xDmg4cma3B","9JpFPKm2XFO2":"nfGjS8uV3UpK","WL3wIR2cFKsA":"zERwAelXm8xU","sgevRLTR7CAo":"EIJ090YeY97N","8igIXivODyN8":"mbvgkWrNmwt7","U1Dxi5kpQuDE":"OrC2lu620jCf","8KOHKAdyGw5h":"eqqgRtLzdpjl","Fn7SvexFrAes":"XB5Gnr0zNCxy","DH9DuZqYFp1j":"TNK1Gnrw51nm","58zaBFvCtGYT":"VNGBdsqjsIGD","qZ88juv6Tup9":"Fmqu8tW63FpO","oy6PY98xpRve":"vIhnxbJZD29R","AWdpC44Zlvna":"1LWfXYABro4w","OWxTOs2VxJdw":"8GijzNJs2uQY","sGlW2tQQMkNQ":"2zyL380mMQlm","7RhUnJgXArL6":"Lh0s83EMvvGP","gyOX83yB4Muh":"VSHM3dWUyGqM","zujqtz8QF7ci":"I0wcnfYPgi4X","MauSrQ1ycC8Q":"YstptFCc71Z8","qG9oViJAVqql":"wFHqo5BT0JyR","i4VsLMIOe3RE":"bw4Ol3Iz5uHQ","ZVMv3D1fcKOp":"3qqZIS3myaXt","Fj1oXHdJOH5J":"0xz9ghXtXF9G","D0SxUm31aGep":"bZCuWml2YDjg","kVh4nsRhQszc":"ByDOVkSSXUMJ","dPwHeFuL45S2":"lj1SCgnM1bAj","quXWtSXbvKR8":"g1qV7KYFxQ8o","AQkxQXc5k6bA":"VHbdLOi9Q5Gz","EuAxQzGsDEu8":"u1ZJjkLl3UR0","aOXhyMXUiFxz":"RFfE3e014vF0","OU8yT9ixb0st":"VlO7HJXNAwSi","YX88VRbSqey4":"k7NluepZJsb0","ZDgzSOS3kgmg":"sY1PStVX8QMF","YfkbecrYP4GH":"XCqdnKhkKnJn","IGJiRSp2t72B":"6DkfNSeYfXuW","qFLMrwEKXPdL":"MXCZnC5PYAW3","UnInPedulI3R":"xfII8bxpPtdO","aS39OeSdIPQa":"Oop0TRC4sCKO","GWLW5BHwPY9l":"SF3YBOuJkHEH","lzjuMvAGUOtN":"HRI76IsFRrgc","bziJlzR56iWX":"dyXFDUx8IYdj","HJLQhnMdd1nF":"VeI9MQdfZu8Q","EWFJ270PebRS":"CibrdhM6ORqL","SDut7E8JW40A":"OMxzalCBDnQ9","DhBfbRjEgKQ7":"ZrHaYVwCpKDj","DajeXHjPzI6h":"w1zxikbip3lh","pBIm1mcbqnxH":"XdF7niGvFJtA","YclkSFQqwzVs":"1SD7NM3MeTWZ","PdUULEHzhh9k":"KJT8GRwe6Dj5","gygPNjqbeA5p":"2s5MWpVq0xA3","eTS9zZTCPvAX":"bO0Z3qHhrNaN","5SFkD43ZTQWZ":"R6RzEQMKQQqC","OPlYxnwgmr7B":"KE16ONiiRF90","fAjtRu3hV9T1":"9CATb5Bb8lPG","WljPqkQg4Ipd":"VWQG1NDc0b3j","7iHfyshTF0fi":"qjREoUYXCPq2","jvGGy2yrjtaq":"7YXwdGQvV9Ji","6HhinoYLd9je":"ob87RLkApCtk","Y1sLrMybSzRo":"EAj0uvB5SeES","nb3YdjG8FxG6":"hHsQqoAsX0za","YxZK6qdQT0aO":"pVOtdsgOAdHT","dmhpxuv6WrLe":"XAuPL1BGIEmQ","Wvauf2C6LuLW":"G2ojeSL92u3e","UJWF1edwD7ts":"zqNi1UBYJazo","1a6lj0SRliEX":"mhEKGZPHHWIP","IpUBLV7f0eut":"7NnmhZD2ObnI","OIqIJRmcChHm":"x0uZCwmDbtEH","lH5rzcSlKua4":"rKru4IB08NU8","DRwXonTjoZCe":"a31qklEGB0Jg","Vn0O6hX1bkDp":"U5vHH6RQWJ4A","TbFtl2MVnyc8":"fDyVaQDCjCau","vvAdBya9OKcQ":"lpxvyegmikH9","TSzKRjXrKB4M":"IDRyD9siLwv7","HGhD6jllPlal":"nZn7AfmsbQ1I","NVF6h0lP3YCD":"8m0ScfBNQdHn","nWw7pwuqpWQn":"fu0mErJLVMdE","bi36oUbAkqcK":"de3wlOH1lgeS","MzF3SGLQOAuX":"h6k8Pm4WF3rl","7pqoDc4MsacF":"gA9yUPZbzb3D","CI5Uc1KRQIwv":"0IOLEmYqtGfH","8rk925Qlshhq":"8LHbOfKyhpiu","uqilD98xjjx9":"Il5EgZnX72tv","TbDYHC0htjAj":"9b4BZrfEYs3o","oOIJqn4UJjtP":"g2YBHA2EiaK7","xZvn4bL6wvVx":"x03dcnv86MFe","jc9ZenXBFuv5":"pT9aV7Y2iaAn","j4akmEP4SKtY":"cwRgJGy1dMpo","xZ2ALyXyDUvn":"dmnesXX3sX7e","yCUPqjzaL9dH":"12slQ3Jhlo2n","fCVZjbHa0apj":"RpfMXF7l6G2r","1u5kaksqwYKA":"eDeshYemBkEG","yOMQae2u5sdh":"REwZDnCO4num","VDy8mAb0APqW":"MrOl6ke9OgVA","eM9BrzlAS3FY":"ibNjR4DDBrPp","0Q2nYt6eyBzI":"Ef1hCMJKT1VZ","0KIL5tkXsFxP":"i2tM3JV4ak9G","rBgrKitzzk03":"sMZVlAgdOhUA","7g4ac3QbnUQs":"WqUZHyaf5rPs","Bpp2im8Gvrb4":"CMxQdbM8Cxav","QRXGGYSQw57q":"c0DuWuakGsXf","mWd3G2AOuLHc":"ZYeb7M37DqwH","L5VdGETZGc0a":"zM9QN3nqNMxA","hHYYp7D10Clv":"Ccv0vpnQo8Fb","Ait6n9rc9CCn":"HTD2PXXcSH1G","AU5v56ohwjDQ":"t2Ff0lOv2ezG","rmx2FDyIjWLh":"LCgDjPPw0WFP","AbaELsTNDIZ1":"drpjH6stAumg","mdhPUNPh42yJ":"TXwjfdnc7K9k","7Qd58kZfp96L":"zMw0KVoLWsee","szwZNlgNjV38":"3HUR8hiJTJNn","Ibsu8n6wqe90":"l3xbaJkOm9P8","QS5Uh0RbEhWg":"XnGMqwgnNDWG","9xLRrB9eT8Xf":"ZsMakITEfx7i","O3kB77kWan3C":"5xoKF3kjeJaY","j8qGXC022AzK":"8EVeZhwnG1uY","AZitNKsH2FQc":"9nd8sBnbweDn","X5HiZJLhySNU":"GAOpScTQP01f","CURCtKhRNWS1":"YwuDkWS0GwYj","N1URrLZNKbr9":"yQflc06yLxUe","HVUNDEjsVI40":"b3DHNeXDjUIk","EMgBsQofXMBO":"V4PYCVe6w6Xo","lZMXzNWgHOkt":"a78G0VShT9BL","nZ92usI5KSZZ":"8cIRVOJI7t7h","wtX7jYMnIWQf":"2Lwkhv8zRSJu","fJkCTj69aYHP":"nD5mKbeBwgZt","8eEUGb1eHz9E":"SbIifnsHGwSv","8TplhW86wROW":"cNi1jp24iTuU","ZGoufRugeJDg":"PD453hRfDHT7","fkCS8s1CU8q9":"gvehXw6FsGwV","RRp5id8EhhWe":"XIOOZxKrMOwn","wHkzd4fT5dV2":"aAhS5eHMBZOD","DTfwAXce7dSX":"rU1P7WZDd1ZT","6a7y7NDfSexZ":"vuzjpKVQLHHI","dKUxMa77PnAi":"bxKnBKcCoNxm","hSYtgQlJjvRg":"ZuvpACP9kiMN","z0cra5yieNCQ":"d1WYM6AdwCB0","q3wMKSiV0nb9":"XvuhESv2gJ26","9qbyq9d78Slj":"By8Hd52PDgMa","3iiILVpe0FE3":"7K1GYBiAVjtg","QTx1Vl7xI2O0":"lV3cJcqshAUz","OOPwu7847x8O":"rXtQtKikWOVQ","t6c2qH8fpPqa":"9Vynv8O6QYWp","Y59nMfdOfERM":"rZq6gcbL0ThP","d9ciDg7UNpHE":"lE6fPNWW5ybJ","TyZlpKqXMtFz":"hks0Mx6RGxtR","9NtxpayLdX7U":"Cp83iPrwB53T","mLYQ70Bgc3l1":"IXRz63dItoHy","lIrwfvN1YQVg":"BzqrChmy65lc","eMgsULRg5Tlw":"hIkStFRl6CY6","AqBXLc4PFn20":"u13aJ7lH8YBh","JajZQc9MCKNp":"aSXiYSjTQwYz","kVvlPdf5gTMg":"FQghrCtxS5MJ","xWLzQlyJ6E78":"TWmJ9gr6TsC6","jd4Os8CMRmEB":"KsmIzfOeSAoM","ufOCoeGtK4zG":"nr964rJmYgQF","pVehKy2HKpHH":"zMEASGqrB2gU","tNDwrCrjla9R":"m4CtfYAQPjH6","T2YS7njzJo5S":"yqGLO96czCGK","u28BVHisah2O":"fLtDSHryquTk","Bi6AZlGYdtT4":"ctbKEu9JUeJw","9EjPXpAI26S2":"k3pXpbafEgjv","h7QOWq6RWEyc":"HJMAa6EN3Vsa","pssq4awOXpIT":"Cpdic0TVtFPe","waOBh6g4IXKl":"Ao30sJ1NxMwF","IoZsdxdG1yd4":"5Oi63kW30Biv","pqWqtLrYLA92":"2nHY12UoY8Oy","5fvrxY1imq9r":"AbQvhMBbPmhq","R8XvtzbNQ4Hd":"2JagLD19qIFt","h8tglh7njdzg":"9qVp179zi30Y","PFitoyWZwwGF":"WHgXQVYd4QH6","dUoPC5vVTJ1k":"meDDxzYTTowL","ikjLTr8ldid0":"hXc77QNobvtO","AKmVaF74XLTg":"jCUH3YHc8Xu5","exe1PpYQ9ILM":"nRmirsqpKEcB","fk3pC6QpB1uQ":"qHLGBzAKyTBn","HZSdGJc0LrBR":"Aj0Up65QglPr","PtyPJ8elTrlm":"UGuwJpfQ8oS7","4TWRNgEVg3NR":"VxUsqKBiscSj","Oh5pjqmECjRs":"Dc9YWPbA08h0","Uf5uZAKBhwDu":"oYher5LDR2U6","e8qzfjVJbWhI":"Qu3nDEQmsDqg","qObDZan2pLAN":"WLueVmh2Q8t3","BERjQMeBtLy9":"8CbHvidGXvRT","c49HRuFqZnNN":"RGlvIDQlgWzH","SM2YYcJR9TOK":"S6Y1oUne5CoL","hhE9SiMNtMKy":"evaso1TXggJP","XKzvvAGpRggS":"lDjmfroSzfyi","hFcJa4weajib":"dUUM2jptyUW9","qNJSRZlOi0Kf":"VXiBJpLeM1xl","WvjhGIHH91Zy":"fZ8NfotqaKK5","fVUwZkl3qhuQ":"nx5VQOQfI6Md","kJ7NiFW7IP2i":"ps2u4e9YcMxc","N17DNJMyuJyb":"DTwLa21sYYwh","qxkQghQoEQMS":"wLG8pEOoJaEV","t0LE2i85dNwU":"yHfDei6IlIrI","103wCy8p9dLq":"iWKavhsjdq56","Wj2F6r8nuGjO":"imOqYXUWS5Lq","O0m8ia2mUD7I":"wr2lxv5VaFDJ","yGCZx9O1LcSh":"ZhNCSd1MkJkj","nW8OUZoD2xsl":"rtTEM77z7WKm","5dPNjVsbyWA0":"FOK4kdOgQne1","BbDUnGZqQJMr":"rwAu0c0ZQp3H","OiNzbDU5EBxF":"oGpV0HDSnZLU","61ro3QQzDOj3":"FGPtmGEOamdx","tZdLLVGL8n5r":"x4rbjQjm16rn","7DM42ntK0iYl":"rFH36JZyYY12","5KzJx47ZaGnV":"s0gcWTga4tR4","O8QsH3ZfB8OZ":"PkCsf7U6nW1j","EzXZkNrXuXba":"dkGwTg4rLWLW","soGEeZVh59zj":"JOajIKrMtPL3","NTWl61r31Rom":"k1zJbpY9RxAy","hGybbrCXTWKq":"09Qy3FdFjoCp","MWhsO2WSXaQd":"e2IFiOCSUcbp","R0pH4rGTSxVM":"QfaoDm8Ui6Mk","D4Lwir6FTcTI":"TaXzRGGWbRr3","zFvGciise23k":"cyjyBwu9W2a2","MWnpglO4vaED":"71dTFaCZEjgL","IfCTA2dSIsC1":"pEhXcauCqOcd","56zoLF0THHwR":"ZmgIo5aoiqrE","dTbwhl2v0rul":"jLxtH7vazwv9","RpcjwEs2w4xy":"OpYccUIufNOg","8INZHC9lii6A":"e0hMpjLk0vzl","nS3kYaKfosCt":"6TidgLL4cbGq","1Ojw6VxuJBYg":"ZISCYvEeiaji","3iVprjVBxkMd":"A0zwK7IdFEyY","F0aQtK2H80tg":"czRt1IdenFX5","jEUG6uVJte2N":"GcV64VQNC1yO","CNKhPSgjoWRw":"CR6QXhMwXrjM","ayd2bNb3Mrxr":"D9hPukYsOm1T","5ip7Ud4G0IHV":"7RLbB83ZQN4l","rH4DNdAO3fgg":"T7cChMvUqs3G","D61pfz2iJkqz":"A5jxQvzrD13U","DcRCZHIjk4jS":"tJvYDYg62zCi","5lNrDQQ0XXq1":"HcGJN3sWKKmf","up0YuN807Jvo":"vGxcgbapdgXj","q2f1MG6UGStf":"D05OYRMYgVWk","DdV6HXyxzVJH":"k92jzY0vwgeg","LG9nXSsjEhhR":"5aQcelyfkWo8","gpuikCyTaaKj":"7GSwXpZBqFQl","vNe35S3NOGjr":"LkU0RgK9dLId","89MgCMsBFM8x":"XDXA5GhzEmGc","cTAWov3ccLhE":"J6fA3AJt3lGA","hDDfZmxwVrC0":"09cFUTVB1oRp","cujkCwKgieCo":"lBJs3sQHk3Nn","ABFxSf8iPJRR":"bLwsvIe2Phm6","Znytq82oPVdl":"QylXspZBW8Gw","jM7X0IHbKswD":"ospxnV8RKhuL","cmSKZfIeWH1n":"wn63ppszQnxy","83rj2MV96uQO":"GAGlGeMD8z4P","zVLwMpza46v8":"ti3HntoZRjie","ECZaRwWO0koL":"uLhPJGcUxJY8","KAPETv3S3cdo":"20PFi82wFIVz","xavIiZ1q1Hhx":"T6pfrH8HyhFq","7cXatiCWAJK2":"vtKciPWBBPQX","CCXpgUPX3sJZ":"if2inQw6P5ku","4gKz7HsdEyZs":"Dcvb474vJWYz","Z1sRJie6s8wF":"SpvCFzTM0uAm","c2BRnOinHW3O":"8Lig2o3qWk0v","2bDxCaRBopm2":"6rtmg262CwXP","hiayVBX7dKkc":"57HJTFgpQm0O","yz0bohVwCrt1":"marHbEVSYo0Q","Vtly4RCZTJN4":"aQzglhuSa22k","i0qJkvORVviU":"elYZyna54Esj","KWJq6OH3KpUO":"u84nH2CPMiEe","z6lVBm9Mbg4a":"sdxX4cd7t28P","e26K6DcANwir":"q7gtMuVffeeJ","lxou6kxg63o5":"0JADfNNes9aS","SRk9gDqrc5WW":"NFUCNFTwfrVR","cPYKWIYvC8PD":"PS2Q5MAaTxTt","lsTKWU4eo1B9":"pksfWYTBgSPh","JV1JkfhqpeGx":"zCIEzCspZExt","a0GzeWRgiFEr":"E0ZpabYa7vte","oojx4fzUmfq5":"ECRNstxwAuA1","R4r1U5ZdS3Dc":"ZClFioHl9tZP","g6HkV9X7DmeO":"zz3WGTOqE2FD","9s5qI6mBGy2j":"xKYrZfnalGQw","YoQS5tmhRfnM":"k7OuxfbXfYWU","91vrI1ojBOEp":"zSXGbNon7set","0kYRWfosXX2m":"MNW3XBQ448P4","M1143TouUPr9":"vEnMNmC0Bpqw","HPtHZIpD1YVH":"vjvVG0aRnkZh","TzMBz3ZfArPp":"hmM919g8Eyx1","ZprF8FOmrcxd":"8JPHkz0vgIy7","VDbs4ECb219v":"HqgmAcUkRhwe","f1OWy5nla6o2":"ZOkKPYZ7mGvg","2KCjeyGvg46r":"ZkhZEb5JXxdc","RjNVjmjztdnn":"JAdhrPXfBTsH","jBIX5HePJHlr":"shatTALh3ucl","Cqlj8Y8NPmYn":"0XasvMAdoO2w","6j9hhOVP0AYC":"PkVzlcVYH0OH","PYFi5IbpC6uZ":"B4lDwyk2wADp","Md8bcqQQMe1U":"irSW5s8q4DYN","iYL9OZQrbEe0":"xzWd3Nmb0CF5","dUI1CTROUD59":"QasxahdwAaOz","9f8lkymUd2ds":"WjnM9RtjlO5a","6nphRDPJCvwB":"wj68FBpBJ5t4","H3JULSQix0YK":"KVNvMloHXYJg","b1Vutsahgi1h":"DrH0e8B4DxwO","e6sdY7GCTTzh":"ayZqS4cRfAnh","wY9QwjNjT9Yu":"fCC0NojqSrfO","yET2c9nfvCAK":"6AQdveAbzg1W","V5zimOSKkvpy":"mDaM6RLbsstX","Bq8zJxiwHfFv":"05hU5vwggsik","Mq5IwL0qr1LL":"u3Md6OtuIfJL","o5TXwWQqUKQq":"WeeE9DlqCW6a","HVVCBZyhyTNn":"lInCK98zaMdT","dM7hzA7irAtC":"hY8XZ7YKV0G9","l8EbBoQ77hd0":"strpQgirwBVq","z7MkotYKj99x":"asnKpBWFOKwu","yzgEjSIPr51u":"ZkaWNWy9jpMC","3yj80hMpVdja":"mKoaqH3rxlSS","0iQKCQ8Dtnj9":"syPyZtcnQMsT","5oGMHJif0mjP":"popVk2CWCzxB","2X2sDnVKLtTt":"bPBJoJJcEdPc","m0VTTlMNRqe0":"RpCMyV8p8Ual","y2VN2bfH9HmJ":"2qrinL3Pjfwg","0C7n9Wsri92c":"UFjpimQ4Egcg","SBE9rt5EDUDT":"Vv6jaz1nr7Af","0BULqLefeX0h":"gKdBa4tCwESl","zNgy8oik67s9":"7TtumahB0S5c","2xq4KUrrlXn4":"CZcoMa9Spcsj","BJwsjWkLzgOJ":"mmukYVIJ52Ut","3b91u20LRZnM":"CrQmdAhIeaeS","4eQ6fiHWaqbY":"zTQLYdbumoCA","TFDj5niXyGWS":"xrnclbHGs79u","UnvRaIndmqGn":"rf0zs0WsDuK4","U90SmyulJyBt":"9zoIUOyAYxPT","vFZCVSK4ymnb":"5ssuSIDdm6Cu","gRSJBNA4NDnS":"AlI8uw9Txn7L","Qaapr5epqf93":"obEPKumvNaA2","kYVkmYuw3dR3":"4VziIJGt1b8P","lve2lyD03s3h":"PYFXOAmMmW58","pUZBI87SGI0T":"gKhEEqImm3te","YTqIZKuC4tJq":"xnAKpMcUYeEm","VPYaAKGHrJSO":"LxzYH0XHagUH","94BJZ3EGkeJq":"HaxZkeecFcVM","uk4L4I9ioPT3":"0Kaipgveui6R","MiaprmmJnX94":"D4g82BzoSzDi","klh9pzzS61hI":"Dyw79gDeDJbK","N4DkdZjKwER9":"UXksRnZR2gmw","J3rZNjWYa9zw":"LldRbYGxapvn","AJFCPjCSngyH":"YwibBGh3pwx5","Uu5YlYoRcqQb":"KeV1DGaIdzoD","dB4KOwmbK47R":"wui5CE1vZ21r","jzWnNz4EG4Gc":"KoyxY7Fm4FTQ","7xzmfZQQCyJp":"GUNrRy5SQQQX","RzEAsIxgtYZo":"eTRJqe82Xn73","utOsTsB5fA8N":"aM9vaCFFRZBU","4dvLQ2HHh82w":"oLpBRb3Xnkym","jOJsYwAYuDyP":"K8Grk9SunFtp","UR7XJszs8FXv":"KAzmBJWhaOJP","AJCMyKgVj0s2":"0mhjp0ttU48s","HJxKgbFnjgAE":"8AbUtrunVnf7","CBuwRJCbG0si":"QXQzSGF5W6QG","4zl2u0MFY6GP":"M65BOBIHx4p8","tLFz7SxIvKLF":"8QLiwW4XanFw","GbB1eIrzvX6A":"zvrfwtkX4tbm","GFrVaFOlqYsn":"rXJ4B19FsqKX","PfTfN5UWpr1w":"W5OGB5OTG4Y6","wI1wQUSJpyYK":"eh0cyIYyVldu","UN4n2toeBz5q":"PBBT1UdWJ48v","SCMvsL1ZbDUi":"m1aw4ip2Gbii","h4IeEG2S3m95":"BXejw0CfEB2J","SGOb0s0nGQR2":"LlW86o0t5xGm","DERM0nUNYlen":"UoZFtxCgkzBX","cWXFZ8ZTzips":"oKZq1PAcVY0n","ojhqye80SIeE":"oxoFrGr9jGXI","iCDTAzz15sT2":"IFOjljHCOnD7","vDbqGbt2inl0":"uieGnKSmRRpl","JXSsS7GfZdjE":"0RuSTqyYiqfb","wVIvBxxr7rjj":"vrPLDTIzMaId","VARxJy85S6pn":"joMbYD8kqxMu","fVm8wia8Ohh1":"Sz6s3FmobQZy","QbdJTwznwKqo":"TFXuJFs7rYsX","BAkmwDdw2adK":"NdeZt3KgsSyn","YSdMmdlydEsH":"B3JV5M0T5ocC","WlcR9a7KX6Xe":"W9n9wpkpwHVo","JFXmGZvLvKFj":"DIuOmwPmGPbG","lLYaBQISUV57":"YxX1LFJBEaOW","vkbP1wONi1KG":"LW2sKHGpAzmf","ZqE7S213xCRq":"2JMC7nReZgMi","DgRqvN6qkYPV":"PENpYrbg6fKR","dIoNOdHKpSzo":"Q3BMx4mlscVB","fIVqB24mkq5t":"E9r2Gi7ML3lM","huuPeWOSgmiF":"HEvFJ4tOnXZY","cTSI6DE8kJOw":"lrIUEr2cGfSk","oCCFhjUNKwS2":"TI60Y7dDgWzx","XBNZbi4lMxCF":"W96jj8fkYsSc","dZtQk9L2cRIK":"59shH3q3YQIB","9rAKZWiHka2b":"lyW3IdTDDrC5","1pRHuck1xc6D":"zRZaHlDR3ByJ","csP0RjyQHX1y":"x9wkMkDMAvMB","rzcno4ydrB7A":"pJ87DJHP6MPk","vWxP2Fq5EXQx":"JbG5n2HLca9s","lG4ClyqO4vGT":"iRPhp4NfFFar","vYehK6NDqmIO":"Ck7a2nxUq92X","zFS5RQL0nEsq":"vGhr3PXJnvbr","mwrsLacmOoEf":"Yofv0JlWEIOz","eF42JdNmSFqd":"MMgSOvRkC16v","EZDsAWxGT3Gv":"NrYc7pcEMylB","WD9VGKXECQ4U":"TxsBZvmy17yn","bzE9PkLqybUu":"dvFqx3swXO5w","9Q5tXrD75Q8d":"FVASBE6ApAWd","icelikVXhSBJ":"oPkfxyjBXax0","PSVVWRsgG5Su":"n5ZDORIEY4Ps","j6VCDGRVxQbr":"cl918B2AdkeF","97hhaWDk9osq":"cI6SJYFSYip0","cGjgJihOK9JD":"FB7ZfLaVBoAM","lDGwBZj21wcK":"6HgFmDhyKUP1","gkDri0xHOcUp":"l5YNA0jtb4Ko","gO3ke7dOVs76":"GE7nW4SjUQ0l","NmaIK5aDTJze":"LgQc1I1RKors","W7QY4Oe4q1nG":"DB8jHNKRkWwU","pGA74btZ67fa":"ceqhTCLdWAyp","8vsaJTSkGFSp":"dCYzlSQICHV4","fE9L7Ei3BRIc":"8YG5e0kCYqfS","kJEm7zzVibQs":"Yjtv9fjJbYue","N54QnIpKyS7X":"xR712KggXfoh","AVy4HgoQLkv5":"WXcTPNbzg7Qp","Ml2Pkk5OuLVl":"epi7rUU1XsjO","HzTJgB0hBeXz":"GRs0tSQv198T","fBybEVUO8HvP":"W2GOLjiqISa6","OqBvu6MY16q2":"9ibdTRHAEXOm","wUfRceV0Hgju":"tRJzVEpQXBJN","NGBHWbTCca4z":"89HJNi9wCxO7","Rqocv3h9KSWG":"7yT59hmLro0i","DdSNV40iftjT":"5xvHJbr337w2","cqtA0pHJ6ykL":"PtPSBqW1mt63","wJfdAOgutH9x":"elfrhIMUBdoF","dV8vJXcXabS2":"a1zh7vSoMCxk","x9oIs8fcwMPb":"4bgyMTdpwcEP","Af1xhPJ3wi4v":"LNgwhmflK3ZZ","wh1VOJuJ9DK3":"fkjT7Fg3jelG","GUk6tLyW3up9":"mc9K9QabxEDq","OLv9h2oJjt41":"Ki4H6d5lnKWA","JsQaWkOe5ayi":"g2yH9lLkfooZ","AKR3d8YSM2mx":"u5KuttojNYZG","hveioDUEHaxW":"N01eQvoEQbJT","HzakuzA4Rrsz":"DUWSoz6p5nmU","eMMBLEHjxFC0":"Cw9SWyONpbM2","EU8ztvfCBo30":"jyWz8H2hnU4m","3GWLu7MOygTN":"trcieUTHsMFD","ATHsfTFADV2y":"VFwfzIkVzxwS","iktPl8vJvasK":"wGdsMzdrAUfw","EpOi8AdUenwi":"9rEB83QRG1V2","5yEi9KvsIfnI":"Hp6UdiglWfoe","1zk3yySAtlNJ":"9Fhk4IE9BnDI","4grvBFwXJqCx":"Keqo6yb6l7sA","ShixH0ItmTEh":"O9bjf3Z7183D","ISDLblwtxj1N":"L23N2IchRvHA","py2shgNyryIN":"O9cf9d8LY95U","18QxwaPDsthA":"jvj0vM6EbpUJ","aocJbhHRyroz":"dvhKhaNfKYZN","p7lgG14J6i2v":"16Q69fj63bMP","CrX6WyIaXkV1":"LmqYvRJXIIyU","yIfUp8I6ouUh":"2r21tM8Di1Ix","vXzNEz6saQwe":"euIHltu6R0EH","ab5yvUsOFGJY":"xiGeeLJXaBeh","tI7pMrML545H":"mW9unR9o3pGI","74AyqZRFkZgO":"0mWhvnv5oqIY","bwCqpuT4HezD":"avQVvCTuHkCz","REH9fudcYYB6":"A4IEDBzsph6U","FSVPIjJvdM0D":"m2y06MAWjUhO","5lUkz2bfejTr":"NP610h04Bc7P","tWS83wxFGuHs":"s2f0zpFbcyui","8O82ZGXEBWAZ":"Am8nEgnu3rLj","ytr7e83aXbDB":"EI44ic8nOJKx","inCYo4gI6HMX":"xg0iUoZfnQlJ","t5s6NuwcAiWy":"slJWJUJWLqx5","ffgBqXkfbdkI":"1aTAAJK2rNxl","DfTMmrNro5mF":"b9D5sKwZp7NW","z2GkZmJY5UFb":"jowYjenz0AQq","8JcJou76i4H5":"L7EPpTugQoHy","iVULq7ypatgc":"V1T75IAMFq11","X53cnqJoZkUs":"CeoplgvH442U","wO5HHqimZJAD":"dZYFzAi7L4xz","v4tDuKNmNYyN":"U48p9VhxAThL","RE5prRzw9x4R":"KlTdEzS5lenK","zQWH4KuQ3myV":"K64O38hQJtyc","zDQxxeLTpiOu":"RN74aoQtkl2H","3Ijb0k1rWH1q":"QcBmLL8FNRRp","Z7cNmolr30IA":"H3El2G7KDFbf","eDLan7cdhGvX":"ZwxIM0CVvjld","zDEZQlyFdLNH":"uRRVoEOHqIc0","YbjuJiEQsmZu":"FzwnDw5qWY4n","GZVnMztxpgPa":"i4x36lFZWIOU","WsGtpGttnR9A":"FZHYPnCVyf3f","JKBsbzhMR64V":"nExETE4PpON6","Zo0PCcSV1sM4":"U7y7PS59HMGl","RDauDrXVjbLj":"nvg8AkXA4WbI","qWJm9GwYgPfG":"ldaQU6wOZU3C","Ldg0mg1JV6ZI":"s0aFtlgSzzBk","3FqgafmMwBl8":"XXwQiWnPNU30","U99qOZJzw5vm":"B38QNEAAZorR","SGTlsZ8xhmwd":"sXBEUxXjFUOS","KKa2gpbE7Wnz":"9jjMFD8IrAck","acWsTNh3c0KT":"KBaehE1q9jNG","If7B4unm4Jq2":"oxUri8Hp2x6A","tVyuV6Z3UFxm":"cLTxpOkq0iXF","OofbO4sR5596":"4nhVt4vegAdG","UJ9EsHpbIQtk":"1fTrtjFTKMMy","SlsRmiHjLQwp":"EwGpvwQOMyiQ","j7Zd99mgwA2G":"C909eVDPmU9K","wuN1IQYKjH9B":"bJg9lK9NPLrf","SbVgDzgkH2zP":"lsa5UObsAhc2","ioTnrlq7i2ZM":"bULJ3Xt4azuv","X4ev91fWtXhH":"oJ1ocxag9ZAy","01pAJAZs0HP7":"ztKz3dCuxUsG","JciRy67bi3fv":"vXMiLaqIQl29","3EmX9o5P5Q6I":"6Dhia3BbEcMb","hsK1ZgubTS8z":"VrdVuaSUIObN","ngEB5BCHijNJ":"NJBXZ3anQuB6","bXZPi1HIWukY":"sEl5mdiKlgI2","ls2jwIWwdx6U":"wAZLyBcUtuXu","UUzoyU8QBD4I":"IABQna9j1yOy","n0IJQKQMwIQT":"PJjOvjIoASYB","2wgNy4iyaKTZ":"ZvNqUCHkRuFi","VMaAl3cgRy3n":"h6Gf3lDmrd7N","6dbvGpK16SJn":"7eDrQG1zMGZB","lyih0KzWkJrG":"NuxAHTeLxTML","Ik4YHkwIopOD":"1WUiSoTFwsk9","fmCQEvRkqex0":"rDLVl1BvdJIM","DiGxm5XiulHg":"q8z8Byexdlnr","9eNOt5VM0mxp":"99ttEXKOiBDQ","vrq9y9AJhuBC":"uvHvTHDLpYtK","FW2fyCBA4cog":"w0w2rTooCry3","JUDRgq8fqpvH":"DtKc4Kxkd6Gq","xcAyI1saeULx":"ncXMBgiqf2B1","KX0bwaotaAEN":"htuB9fnWgv4Q","scIOVpVpgBNJ":"i5aW2c2ENgY3","ZTkzKBEZEU1q":"2e69HSJsDAlW","YwZAlg38Hgbb":"4Hwede7v3Rdg","rZa1pIKP5wzQ":"KqLNi4xI5RjO","ncldVdU2eAAd":"Zo23gDaDxkyK","goygZo2mhroJ":"ou5YJJNhXEhJ","3wDpS8GgB4Cm":"UnHEMv8Fc1uu","DzPb8VZqnorO":"u5pilIqrw65h","ZfG2Zs5t8ixj":"7z7wVrdHZwxL","lKmfEbwA7GwD":"5JQb7Pm0e2gU","RXZ5PmeRQclm":"F4JmCKIm2G6W","SWHjcYBqkHuo":"n8ssmzbGbak9","uecW7PQt6453":"H1fObhcxhgDD","4JdyOLxDlP5Q":"NHwqIkuBgBiM","r69spYqwoNft":"hEywOL7NWNQs","i9x3nwsaRLRP":"k3Ha4dE7fhkc","kYLJzcq2irke":"miqDNApokrjE","lkGAiG4E5bAQ":"S9Qit8CRYUOw","zR70z4F0Yjgt":"z0luKQdkra5w","ofi19IsrsUVY":"XEx7uDTpf8Z5","03OBz9Fsuew2":"TXh51MjkFo5R","zixnCLEZpZy4":"jCvJLXx5juCJ","JlwdwsQxIPyP":"HywsmnLaVOL0","FRWaKLV9wpBv":"ZFhGXYnbPQCb","zeszqaZoImsh":"zzy93e3H4ffY","1Obf14LkjwZn":"sXYR34TkImt9","0fOXRKoxHlwX":"nxHdsziI2el8","L3n2H1WS0EDT":"MZWg10MBjJIW","vuxHwK0KtrQt":"8wZGfOEpDiAc","mZEiyfYlP5LY":"AsZZCnanucfT","G20BllQAqqXP":"qXhx8uqt5X2G","Sm6adNyWeyj8":"QsEDcMVju304","ian2VX0pDjfQ":"C1NJsAsaUjMI","KF37pWMeqwyI":"76nswxnUEInB","NiW57plK9RnR":"VQMN7SChdv4i","5OA8bFDvPqyS":"Cys8l3AHP2Su","CRnD97aaIEhI":"Oyebze1pw755","PRYTYwQ6XVdM":"yw4hc2HM9QGY","FhNt9zoPLAjZ":"hV1rEwVwlpJz","teI7ZLAJYfdJ":"Toc8XdD5cUHR","i8jfKP6XmOmH":"03f8xGTEAnfE","xpN3nKwnAqxL":"MlbWOMzvnRJf","9DbPtQroTez4":"E5A0xNWhRvF7","4C607rdW0bFK":"PgIRs6guPtUk","rp6T1HAZQ6UJ":"c1eO6rflccf6","Pp3fNe8Q8VrJ":"Wi3xueztIHkT","iszU5CW5er2Q":"BHPebEQzmrGA","ynpVXqkqWjNu":"xOob1coq8ehd","q1qY5xg3CWAn":"RbbHQypopy5d","v9qxcXdiJ9hD":"ZY4nEwPOXFbl","Ug27avqMUcRZ":"MkicKTWPE3Es","W9XxCnW8Uctx":"VauDAV8vDc5n","yQ5cOD8gTlQl":"bTfSCRVUph3O","vARg2IKTfMOP":"ZcKREA7LFG7l","PLrdGzDpNxFR":"2shCb0JtgeQj","mK7uv8epB8Um":"ZQSupa9yquGt","JrxiXmEm6WGh":"DaP3A5bz5ZFW","SCJss5onjZlx":"6BG1moNK8woZ","JY4SJMDzGCjW":"UfShPxhtgmZy","t56eTunfIR6K":"bzMHbENmAmJW","WJ7YdbPGOLqH":"SsRqnTkYYK9i","t5kB4sIhFLBP":"xF25ySfEQdu5","sRuI128WObMQ":"zkKQDaomDsEN","PhQ6yG6Mr1mA":"3yB6Yl53wwaO","7gf79Oeq3l4c":"2B6YwUjumoQK","eCD24TjIA8U2":"Zn203W3SYD6Y","D1UuO9LHlQCS":"PxDDVe1kXDPZ","bXbAchA01TpP":"9ID9NbQXam42","CV7rYxc0j60c":"sJM2fcOBOECH","hkIkqObW6XTH":"WKW6lz7XSnoc","uhSAVnBmY04j":"bfAAE6ovMZQp","sy051nWxphdM":"mzZ7eYnGseGW","ZJhLQ7vYbkyj":"RfB9P2zc89hl","rojSu2R0Lnst":"ui2uvRd2BHYd","JduLvuAwb2A0":"n7mtGWiIahMq","bBNgbdv5miVA":"Qo2IzaML3zbg","6NfRdWXxqvwS":"8r1iVUg3Ryds","pTy9X8yvXyps":"WTNHVeCVAiuK","SDsKppLFvMDV":"YKRSxOeWBmyj","veGvejtbc1yT":"fRqRIYsiU93R","cQZI0e95MMP6":"vmjmq0POZYz2","EGI07rx7UJsZ":"t9dOVbVY3Yr9","1WH0nRCHTBIK":"zbXJRbdRqjF4","Kmdt0pRDBvnJ":"dTmDrE9WA3Fh","zbvD07GJJ4Bu":"a2tX4d21Ffsf","KoITD99iPmyi":"uFMTzWMcOoaj","rExSpXBoYkfh":"OyDtgYXRDzVL","MfSUb40ngwve":"HVo8cK0xUb1O","dJEcejLQMhHE":"wGiPZtZCp3Ik","LKNUsFQhrn9P":"ATZpi8vkz2VQ","0vTjROvcksbZ":"KYWHtFeYxCwF","T3k7HSmqpXOs":"BEgqRS4Gxt5L","hNKyvvHDNmgO":"VfcKoW0h60Us","vZ8IXyuulE2J":"d5tx5IyC92G5","pGMb5cM3jES8":"bp11dzenB8hc","HIU7mXhVb7Go":"fTdqyU72VNUf","KU3JrbXni2pf":"QDm91Z8eJTVP","7bUpqsf7cZpi":"vC2rcYEXalpB","cPNhAikurMTv":"lG98uXJOZK3N","fvhR4fFUY9wv":"9qFEkFXAF9MY","0XmfPlVd8LV1":"yV7unQyFzHPL","YLCUZZOduAjz":"a6UZBpdFx4cQ","GF4KBtruxZd8":"aPWirt6tGOtT","kPOThDZU8AXy":"1mMln2dQefeT","qslLrYlQGlSo":"rRbYhkRyaLx6","Mpy0JALSyqZu":"75K7u1RE2d6z","4jgNEnNmIj3s":"cYyDxBqFsEAm","fLxFymmUW9qy":"mB53sggalrdT","2DEWXu04P84M":"n8qNo3uktw8T","eGC1t5DMpM1D":"V1Bf9KZl1lqE","szTId0TqXS8E":"yu9vu3rJQE7j","lr3ampVAxBTL":"JClvNWgvo2cL","92sr6dXXuE4f":"6V2nuh6YC6zg","4acEwjk5JgAT":"kTJvD8m6QjQm","7yFCpf1k5e1f":"5jlCqugHcdSk","ioemgfa04zB2":"Cpls9RCk1wO9","L5vzpCuVRgcX":"mnyNN44ZLKQK","oAy7NC0gij6F":"IhKjILCZv4DG","RkDlo52htwmt":"UfuKv6CsRwJH","yvXkbMvScdZf":"OM4BVFYI0AfJ","8hc4CzmBMlIK":"3XU6LSH9MRu8","QaZAwLXo3FEA":"HaaQ0xtL9jOO","7ufHa8KxxD63":"Xycspd7W1i9p","rUfgbYZhp99C":"6q5Qv2aRbfG8","PXN65E0Yr6jV":"TVwELyh5gdcW","vjLgTuwZzQz1":"pZ3zeZcLGJE9","JdWH9GdkdRqo":"ef3WutRYBccK","PgYcw0nDvsKo":"xRccpXrhakoB","EQvBQE9RmdzK":"1SzZ6ynSnY8H","ZHH0GlXcPxuJ":"rE5afTSLNbW0","EA0gzLEPvUL2":"hAgoRVS4KNVH","Nc5HKE9QqgMz":"IGlG2L8lL1ne","S35IKHBK9pxr":"SXudTUypGwoi","60yN7a8QaUH1":"ffBCxxO3lEVB","D5yrPrcPuD2y":"w6MMnRowhIlo","Z4Z88SbrQ1W0":"WDhvXk3Tfphj","pfbzSvFRiTiQ":"kWFK8dr0RsG4","GX8rbTkidChm":"dyxf5OvAt4Ae","FVUT2MXnIxdR":"jbFffvg1aozf","wzTjpb3ppZVx":"J105lirphddx","g8PaSgK9050x":"gAigBuLoCRPF","AZLgIZBIKhH6":"mTEwmHQEaGn6","leGZxVC4xpxD":"JrowvxYLOEup","78MZQIBJ885f":"UIKIHODOzG5o","3oWdEnoyUb4O":"dEX9o3MZCPc7","ZgrXuUBjNbAy":"hOmUY7UTlEW6","o6Xqyi0ehpGD":"PoO6tyq9nT5r","ejPKVeXmS22y":"q15Ro8EvUAtT","ADAoQehjSAgG":"yTPP1GfpldTL","AhWuifSUqG7B":"pG0SeOC7Fcxc","DaOlPIHDYYN9":"vkQV4hwtDi6U","Olbtyy5w7oLV":"hYDPKMTldfei","k5yhJIahXE4m":"Kko9AxNhE1Jo","3tfUqYIYvaAP":"v7CHgWESvpxS","DPRBA7VXsqLL":"4ivyWSBcFdOU","PclID6AaU8LQ":"CHFIdpstWCrb","MkL5O2wtZ8DM":"jwa63irSdotc","soYtROLPJmYL":"PAhgOVLgHXi6","y7frv7KsxE3V":"swztkqJgyHx4","bWCKR0CF2vyn":"Xya6NdQZWCiV","nLmPkA7L3k5a":"bVLKCBZzLVd1","WJDoMnOAfX7L":"gYztgdNTAxQd","8tkQbNIbf8yh":"BXcb4dOkybiA","BXMGVyiLeIdH":"yGtcYpcInAjb","ba6OybRDNuJE":"gxf1JvkYT8Cv","6hgDLODDXX4e":"3d2qyUwPyNaE","pNofHy7zbHL3":"rRv9RiT8NFYN","pY4LUojwrS8K":"YDjFhflu7KFO","30XjotpHpLo1":"nCfpl3Si7po3","7bpV1cms6kkl":"KK2nrfXRApGo","myzdUzRoVdG5":"mcZNEBkF1NMg","BxpV70fzJlgt":"SnmqWE8x5QjJ","HirZVZcuTYxX":"3F8o3BgKYyVa","lucpIbKxCGuF":"jVBrv0PKvrZ4","SrdmaFFdC45H":"FBBMF4rIeOYa","TRaDsavcfvZ8":"aC6NLl7vmPOE","0wkz4ZUnG2Wa":"Q26VA940ZOy4","NkUBuMKjGOgm":"c7qlKbPBuz5O","V3gia6hCtT2C":"wSn0NXNusTuk","5WKTx6fOLPvj":"EGi2vLQlyEEu","WIf9bR5e7Oyt":"Fqb88fkapIML","Es7hTbdJQH7i":"PgOEnNW6bab2","mYQo9AItK4nT":"20gKbQ7V3tqK","0aVdIHqKSGaX":"RQFxGNGbfXeu","D8QPMzZdDf6o":"jOGZ8enINowW","Sy3YUlDHWeqV":"wUNcpsznB1Qv","HNj0AfzdOTFF":"MOh0d33ObzJk","4zLngKbug1Ak":"joeaeUtETJGu","BesI4KDPzIXe":"1iXKnClhVVkh","gVwzRYeSkC8T":"4RoBIyR7ryNU","jA3PXRozrgaa":"pFKgmp9jpyhZ","7TZLdgDlBnCo":"arzXHZcm8bVT","SmUoXqtnj36r":"9ehmHoj76o9l","l9sUttIaPaG8":"IDrOV8iLMMpR","ZiOJDB1IJss5":"I8XPXyxskNsD","GtqoLiKYsUE6":"Q7unoPvYxE7A","NKfwgNmzg3Kg":"3qe3mNM9NFSV","YuICrOsMLmaV":"MqbQt1RPeOHl","c26nFdHsB1Md":"0HdrYjj08BUd","26LXMjB3QhYI":"qEqYNyh4OsuM","lSu5OZioI0L0":"OfLYirsmAGpM","D3asd3SbEhCo":"6pRitZL4EwZ5","zJYgBHg5QnMQ":"E5SLhd81hLMr","hADWdW6mVgLB":"Zd4pHCrs3rYC","UGf1XAFcpd5k":"OkvDNYXniXvV","Oew9Y8dLsnlc":"fWkT3owrkGJb","5ijf5IKMRstw":"VObV1qZWY2GE","p5wMGX3EL2sm":"zQJZwc66gWHp","WFqdOKgBD72a":"iu5lHEg0Oy3x","OlLyCpnVti8g":"n90WNuJzpH5O","qxrGQ3k4Is9Z":"kQc0ZvXf8zLj","fYeCqRQsmZBW":"lYEJfa0ITMKy","LxCbHtGe31gN":"k17WETj4Frnw","Oq5QYKWBy4gR":"39iyZwqqAJ8a","Ya3Qp9dqORlx":"mGFOWQViQmeY","cNkoxzcKuq1Q":"xmdjym7h66jr","WmxzccPz3f5B":"FeV1899osrTm","eggxiT2m0cHY":"HkhhLPibgVhE","B3ysbV0lcQ6b":"VHbnGloG8QG0","M10nc8RjyRuH":"huVqa9WRcNSN","zp59VsKv26Y4":"a9jVshc53fK0","3JHtx0kwjdpJ":"fPW0aGea6mbc","pH7GGdzrROny":"13zVHJy8nmsx","PrRTKOKDGEBI":"YGR3D2b50M76","vpsX87URGFf6":"RAiCAiIA0X4m","MDqj9g8azJSy":"A3qVuKN9t50N","EKmDRuMSWB89":"1sbDiN94zFJ4","cNi19wfPppAi":"gzqOCaGruryo","8h9FHTfBkpwO":"mCWtgR7DRepq","xXVVnvbbkTz1":"5B4IJKrbXze7","Pz6EfJmfT2Pc":"qDejtLy23ssk","zlhVNiMRoEZF":"ImgvkyIFfr5B","n1Y9VU1ZSbQL":"TWlrglzkUMiy","g5dLtrmdrvgZ":"CX5BWRhJVKt2","VSrKUWhfJyaz":"Agz5n09ZKROU","hVpeDWV9cPu9":"JcHodEZZ7FEd","crFy9F76pY6V":"YgyBRYqlB1lh","aTkhwvCGmiVK":"WsAS7WF9e0Bk","tdiZsHPK81Bn":"wYNcvAsS1xkd","eWCgrZ0WoszN":"hrrnU2yhWn5j","tYIy2jizmLBZ":"5SqCeIhyuNlr","0PkQV0k31rut":"F1V0eXP4OEg4","asyYe8ty81gR":"EpQ4rwMbQV4T","EFC1fzVeINkF":"QXTiFY76t9l7","SLgit23XFFbk":"pIwkfcmu88JB","wgvpftnYRrm7":"BW50NcsvPDj7","FyullIL93vuu":"BXfMhKZrzKwd","lUYBn6RvFkeo":"DfxUiJbHNIEd","k7Q4cObKyYRw":"UWTEhPnEwMNx","93C6UxpkPMqk":"3dfV3C7lPZxE","FUO5h1lj1JwN":"7IHHLt2Ctsep","WAug0zzx4tvT":"3GOmXXCXKoiY","WRt4rbN0qI1L":"0q8kDczQzylX","BR3A0ovZ60Xk":"fIalLgvONdmP","jY0eldxlrJOR":"deQgnMBISi2O","hQpRNSSU71hU":"yZpry4yJcUvG","KdtsaIQwRlk1":"JMIgSW7Z9iwr","VOit0yapaRgy":"Guy13qN7QxHu","n7T2aPAbEJtm":"n3BgLV6GIZrC","Ds35SerGc9Km":"G3DNkVW9Mj0x","8ZymX3TO967B":"RKLOpmULk8Cb","GfOGRuQjPkQo":"M2uXCbYxuxcK","ALpU3rG5aUML":"5Cs3ZT7YYdoV","6KU827aSBgic":"ffTHpZyxvOcC","8DFingEMWkVG":"MYCc6uJNnfk1","8F8lx8SADEK9":"0kq0gzdUj4l0","JMKl16G2ZEzb":"MYdZ02WSrnD1","uHIpHBswDcyp":"7GfhlrmgnCHY","Z8pwXeyKFwuY":"FMzZpadKxKmK","eKMsyVLB7wup":"kGoihY1gm0XB","6RQ93B8wGNdM":"TmWQ5YqXmd7O","4CAKdUaeSxvt":"O74ZQ3rRWS4h","wbzeGPkg9j0o":"5E5qCmV2l7xJ","B7elNx4Yy83W":"ov25OEvZ6UFC","QrZzcxer0Elx":"24dErp4Kaxsf","iEMODDdREXw0":"yyVICehnsEks","oc6I2RJNa3PP":"211k0U3hvhYz","VBxfSUhPEjCy":"fwLb32ERKCSU","QpeC4VPm38qo":"XkekLtszTvWh","oejGZEbfKOLt":"qY5ppqfaCzG6","X5vuCi6NLbDB":"K7k3UU5LPZ2W","N2Yw8F1w0SSs":"mrRh7qaYanjr","SwTFyWX2ZlLV":"J5dv708BF2eH","sAc5aUobXnyB":"A6bsYyiSAGZh","HDj8ZPT5nG3A":"Xh0oKwubCDuo","IclaTrRNpS0L":"MkL5CLKc8luG","ynH8jBKyTAS1":"IpAH80svgKpR","7jC0vyn5Mwv1":"DL3GU9797VgT","MAEpPwpcyHLa":"Hr2NP19ZyAB9","D34wcsvCKuO0":"cAuKqkVOPHUq","HaCKUdpz1DYf":"I0JtC7rleBp5","t9ZkgJ1NkUzj":"MhGvb5DqeIEZ","gh7ZfzedT4Hn":"7c1jcNdx9sLo","YHA3OCDaAGlg":"Y4ta5dYUsrnL","LSorkVgNsMvJ":"beF5cMKhRh0S","TVCT289gsSy5":"dSFx4vLlAD6C","GvRMThetqXp8":"c1VPtol5qeAn","HlUzg29YwDQJ":"mNaj7aoGZqiT","pokSTH3GXAp4":"zsA57PMlW9wv","Ngt0KWUtzfsc":"gWGbDYJ3OyKm","3T4RLcfR8vHB":"A8dI2BEW58CG","hYucrN6UwIXJ":"RLy6vvzivMup","jQjEGRENOXkA":"qNHeqWwAu0pF","CAFhWUh61ter":"pOiNVyW9nrpm","ae9E90RI6PEq":"GQJJtP9s4UHp","KA6n934JTX7S":"RYkyvwHOJ15w","2m7ZuKOIo0Dy":"Czp6X2Le8Oma","cxvgDU9sflHG":"maP8bDjEIQlV","96g3RO3kxuR4":"uwCY4tmojpT5","No5OkmcPyTV1":"zTVv54hBtHyJ","0DW0seC3c7LX":"veCslcPCHvwz","R2lmHoJXbHWk":"y0894pPywNgE","hbyrs0KSuSoY":"y3uPHDEVPC0X","hWoxAqwl1jUc":"I5RQKaph2qT7","jxycwTRty5AI":"Rki0gTlHUDoq","GA3GspbIj3qR":"qGHfF9XPUxtA","9oePrfXIAQf3":"sWrrH3SPursD","5XtkB3enTk4Y":"oDBpmvxVFNZu","TZ0FOlllNw2R":"DTZtyzvREd1M","GecB3g2M1b7j":"LuNXcVKsbuQO","dnrj2BdJ4EdH":"d12SzHK0a8NE","yIxH4b8kiLDK":"lQkYVaIJiV7Y","pPcTP4bbDXz8":"9i8ngOkyKFA2","qqzYlJIKiIBC":"Zdfb9qBmzogD","j6MiFZmrqbTB":"HFFfnk9HwDVA","P6221db4yjdI":"wFAmiUVJ3pY9","ygNFEbZAHDXv":"cZOHcacMDi9p","TiACyBBwnQGZ":"jm1RVWDlGePU","fkS8XCD2Y9dj":"XCM73ABFrl1C","udEVJdDbmk1J":"nScqCAGjtTbK","fMtpZbXdaMGr":"z644Qdzmldp9","13GzybDB9vHy":"pgmhoYz0G3IZ","OdoJdTnFq9bv":"NhVvwFGeVgvA","S71JEY4vW30a":"4a3lfZa0zVhq","SWoKVw249J8X":"Dy4LDY6xX3wu","qelGCVBib4DN":"K8Be1qpyCQM0","b2MdP94fByc4":"S7oKXM15OZoB","2ODHsGAKHrTs":"4iE0aOlDzxsR","cQqEwA98TdKg":"UowrspgGN2kj","kKq5QUJc8qS2":"SjfDGj7LPuwH","Kn3rk0uALp8s":"L63QnEfg95D0","WhNvzmPhHvTL":"78HvZJ1stxi1","iZ8OswKrgIDU":"gjmtmwxltQBx","5XqkwJduaOfq":"WH36GH1i2q5K","tLfgq0YEZHoX":"EWCUImjsFlo3","iMiQ52W8cGNz":"MW2NpRMfoHjR","4poIUypi887R":"4Ah5yUgAdPmy","HZPKZaZVqUuu":"ykHttl0kYGZP","hP7aKrWSLtJP":"3Ymc51202SZX","GeQeYW6PBz68":"u85MYH12L1pg","X6YTDVlc8NNx":"rZ8ewdJmX2To","NS09lB9PIDfD":"uDutmEckOZJm","pzPCf8puCNzo":"RazPeqotpHCX","P7lpe51UiEDZ":"iN9ZRDtdGgc7","p4JM32EwdMba":"ImHVlaaVB1PF","vP6umkAe3s7P":"bpPo7rKd9Uny","4IV3oLeTH886":"FqWiEj8CbToa","agiDTq1GG9u2":"bnb4dP4sG9jl","66PTzu5bVLqU":"G5ozil3x80E3","x1hYNPLmDVTl":"5pIcth2AAIxH","PYQj34tTMDer":"v7RPTslCuFZz","fhZP4pzGht0o":"mwbgchZtLufx","eAf42v0qTvSq":"AU3boDaQ4ySe","MR9N0sUO6JGs":"eOS9ioJTwvbr","JwhuGW51RdsP":"buAcBq7VVNxM","NF3khEuY0lIi":"bC4pDxIeesKZ","PaacSmAxmSr3":"PL22jl7eq8ek","93pOBS3Q7kBy":"RXmsetXXwYrX","kK368nR2VuKb":"S44JDTimGU1n","0LE1NBpSJgW7":"oVVw6TEwaXpa","ECbhEpBT9Rzl":"yKVXnohs6z2K","WcqlIYSwydfg":"NgYsTv6Lj6kt","8GXbZAVdTx2H":"f4toAZ2Z2iSa","mIigTdkn9GDq":"qLL9vM4Mg6TG","mKCxGvbDMlBn":"iVHRu6W3ooQh","fAXPK6w0Qev6":"PMf8Bu1m67Fp","KGcnKGMjNiIh":"fh3OzVFB4UzJ","G0lrSAYX3t8Q":"z1tSF6LlNxc1","0jQmVWeGbrAr":"w7mBUvmEYBMs","70OJhJ4IUn8X":"RilGChSo9efe","VIHbG3xkLF9b":"nXaKNU42SnSe","9qEDzP7b1HVs":"G1Eq3Bz9JdjD","ut1tCXebKMhm":"eRFZJyXdujI1","qnoKAKEiXOkT":"n8hMtkddYEbR","zJccg25m93ij":"gPHFFwQYbrHB","hTxEBDsT1KqA":"Cx9jun7m7Mya","RoHF0Vng6AqU":"5fiTB6VmU0Jy","vwkn8vSIR5q6":"UXGUB65Ppe5W","aHWxMAJgQa13":"9wpawnYlCAVX","jQZW3fa2WAxP":"ClspQpBJm4rE","E9Zn479g9wAa":"JANdgNUER2qL","LAWRExi63EIP":"9TIeTjHqGsVx","OAkflOqK3XV4":"lnwUST7IBbZv","zw9gqwFGlbcQ":"jjT0Hpc0ik2N","V0oiOLs0YLaT":"olK89IEm11h9","Xq3oqu66QO2Y":"spM2rvqw0IBD","PtUEVmVEsXmW":"7vZ8A70bfG1o","6DLMNUElcChk":"TQrf1C2YNY2N","HwTLdDiP8pZw":"70aC4Qfv86Bb","nzo3xt0lxflU":"X18ijUS1R2aA","CuCdhyL8O1h0":"TZa4V1zDTldD","dpewVssNeJJa":"txXUSqmDu4W7","zCrFbx1PfNMb":"xHTpHhUwgK7q","QlbWJxyY2w7S":"3fpwuneRiaGc","y5x1AYGnjTgz":"ZQwyiMo3jVOq","4jj5QES2N0SG":"jWPedfsPfyD9","OuSXOddvnOpP":"FCjKc6cTu0d8","NNtMabsB9ISQ":"DJO0WbRs7vHd","gBPSndoJeoN3":"K5mMyxUeUjyl","t3swa6IbmGfY":"XQZqPKbi4r2s","Zo4NajK75LwA":"IB9O6okFxTYU","xMv5pNV6OHFZ":"fEIhjAOdfS0a","8rhoKEpcuMyq":"mp9zORMN2BYl","rWGypoh2URSv":"qQJ7eX2mBrI1","mNQjCxEdDDy4":"sXk6qvTfdeh3","8ZlFTs36NQcn":"s9RpF4BaRbDV","nK6KWvwZnwu7":"nXgd4YYsCjyT","x6vJyt9X0Z4M":"6ejyNGGRagdl","iX2mPjuExv6d":"Cv8Q822mL5SR","otPHLwTs1xtK":"wyJTqC4CCm8l","DfliJfPXbXPa":"nuxl9BEAoiT0","gcbmA8Cmyier":"u4Ewy5afh09Y","dPUpXdUGcelR":"BWJEFxaWydhG","Ie0tbd9dRLs2":"qWsv56EmNTrA","a7oQyGfbPDaM":"rYekKdTyn3Gc","Q0pFwcXUVD49":"1wPnKzkJ9S4A","22hht0WsC8OA":"9fFvhNUFR2bg","CMcBRWQTzCyL":"zpOT6dZY0iml","JHH93cduutrj":"RJKzsa89Uwch","nIxE51r1e8G1":"mZPxAYzV6Gdh","WUKbit4CzGtF":"9mG8nqB36tkY","ER1lQJtaCp1J":"lc4MwilsfycW","8CKAoXAbrZ3Q":"h6tMDd2DLpJh","j0pdcmDV8FsN":"qXBY5JYFvSXu","qjNYxEAZDudR":"xVzI3luE1FDL","ZB8LY8B6f2OF":"r29Pa7P36p73","D34AK4QYYUpp":"27xrEyJDdzRm","9KRhzNStCT8v":"5YsLKe0lbSYB","YvRYFYpRe8YJ":"5frLe8fS0mH0","HFILMNSBrIKv":"5Y2GYiZXrgHC","1GAoXdi4LDuF":"mEUUoxSzpNzm","hoIELmUZiyhb":"OsviFf5k0Y1p","w0Vl2vXDs5hS":"9BED7mEpKnwR","mkARfiJSyXos":"w8fp4GECKLHr","eezaMn2TIGqN":"Yz02hXNauve6","SGn90cV2pHZG":"jlVBtiPZs41O","CRUSU1Iolo2q":"OrzQSDCLKFaS","4owqz5FCPPk5":"NNRJU6xrxFhP","LWrv6z1buqaa":"yR3UTjs4XBB9","c0Huajg2sNyu":"AMDAXz1QazL8","LObT4JmODCoO":"0IZnpbOBJTTp","NhGqGZ5MNF6G":"tu0BQeLxpToB","danZEbxr8OMP":"Uvnl6ymZJ4GD","VV8oJbXP8XG2":"HcDXJpsv6SG1","jEgIaABBSTIZ":"ORrC4winDFSD","EWEtrwVcIMxS":"sua9AWwmJ97t","WMahMCJv50E4":"WyoJHPilZWpw","I70bOGrN2m2l":"mEz1nI3IuwaQ","G5AudPI0Kvxr":"MnQxKG5j3zKb","brSbTTAgJC29":"N5gBZTDsMtsZ","20KqDtwlTjOh":"uPnY7AUryXrt","bTQ1CO6nKolb":"ImihPzT3gjC3","VshpfLc9xzO5":"ZDKU6uKRxKxv","L284pdgQQCae":"bFAddLbEEOZl","Fi2wAA68c3q0":"UAxWx8KWTIjH","hslpdrPB65td":"dVBy6BIqofvo","bivEKMsfkQ6B":"szhFjumzH22F","akuy0XE2QE4V":"wpaKyAld0qEo","T4UY2B0j0FoB":"IY6Md9KoJF9J","1giXSITVQguS":"vkzTFTXWbIpS","1JL9Qx53lRdm":"c7Q16uJn4K8z","kpwtbcQ2joiR":"4yXmRu60lKL4","VgdUEaalpqJf":"Xg07a1a6MhG2","dNh8WqxjBHNW":"bA1XVddroOk6","yoaV4CbvLh9D":"a4SKtp6elzSK","a1w6m6x8qkLz":"utdXEwL0h3jO","u0Y0yJIMC0NR":"EaWvGSIw2qJC","v8H79IBga1RX":"Ti8BjWICCTIv","phY8ZBrgU7kc":"Dyfzm3AXuxeN","ZcLO85GyS36p":"BfkDRhpUuLDJ","NSIIuE1pCSW6":"GrbrvenNWBWH","R0ZTzRwf9foE":"206x4oGQWSr6","2O67MzbD5RMK":"DdhpmrP4VaB7","cZiZJqnKqpw2":"gOX1wDPQSE5G","n0iXym9nb1hP":"0Bg7kz1Jk4m6","krwNngeASKON":"Wt88mO8YH6VD","BzEJLQzJCPsE":"NwKqAuXC0ax6","Sj8qEvXvVSvW":"iJAMkJTJmVvY","bRUVl3vvuFYN":"mBfpYaX7hw77","ehqk7kPZ8q1P":"M4zKtscWbgbx","In89uup0X3tU":"LFn1uCzstewE","VMNLOrnfiGtu":"5ZVfkgoCBgZM","YHJpVTBtqSZq":"fSE1NzRJ6Bbe","H4QpSaLdhpfV":"elffqpfystR9","fYwmbBoXX7MQ":"0JwPmjpB7OQy","fvGvtwi0xcGA":"jl8Nt9QfXUtK","seRzgBWF9Fye":"YhM4vwkhImLy","WrBRg6pQ5sty":"AuH0Vi8i9Yy9","t7Js1TiBVFDV":"hQe706LT7QHK","paHrdG8rCmFx":"GezuZ6YKCFz6","zaICXWy4uV5r":"yEL28l73Z2Dh","k2d5ywEpVT9L":"MZWl4vnWMbWV","tQUwX2G9QatP":"CsysWJwfgiNU","mpOGLuM559ml":"4PFJvcWmJPCz","Af1VriOaaGKc":"uOlu59Fq8Z9N","eXUe6E7a2KzK":"5xjPW29jvJap","I2r0cZa6vLoQ":"cvsC3ALKwNKC","4q53n4entJOp":"0O97FW7JRc9H","z7B2EiQrOH30":"c37nm5EfqCyX","FqBOhGd8R0Dn":"vIegKeDUvRrM","Ib31aNwyDmWD":"a2WZX7omk57J","RbFlvkL1eflZ":"rx18YqxW1si4","D2mTrUq5it9F":"X1WaLdOeRcCZ","RDcOKW9IpTrH":"Pi8NRvqY9yUZ","MGj1RKZgvpOd":"KKQiAbBlYYrT","L7PckjbFF8uR":"VFtoPGhuhAr2","nZoNNfeBt7IP":"bKmtldK1gUnM","7QNawyV6VEKU":"LJgZ6TluSQRY","P1W8BeaEKxAJ":"HhFsaMDKKeoB","pK7xn4euGJed":"AEDIeluUzuCa","C1wPwdTQeGgf":"kRY2IWXxeSUO","Sr1SsMzz64p2":"Ctfur1OkjaEw","URhzYS1Linjm":"SuKQWEDvh4Rh","uZQW6GhRR3no":"YRwCBf54x3b1","CFCrgg1KAuOC":"pOCCFGuXZVR7","bl7L16kzUrUA":"uJbdfvRKfSa3","2eJZVYmlSJIR":"0Z7iiCh8D4o9","nWGDqXSDEbsj":"u4PHVA7VyCd3","XJyPGmR1qx4j":"yPdJ0ltP15xy","2t9keKSwjjtt":"eWYU76JDoBwY","MiPt2WrPoEod":"76ZgfRRCjbJW","HLtsh9OtyXAm":"JBkj7hO180Oy","yuYBFtSUUrib":"VD8ThdJMH956","1amnYM9GIOgR":"U7cqO9sWfPzJ","0E20CU8DAkI8":"iC4D9qBWEccQ","n9IPERbqXqDm":"Zn1yaaQOsZeL","QfwMjemYyLsQ":"51nh2HPVnK42","rRox2dpli3L9":"PIpMM0AQPwOC","p4wbyx6cNTwv":"ItVS5mEUCTT6","70iX0tuXAONA":"dpPpiw5c7njm","KoanBtslLPx7":"LQMLeJQlKuxE","z2ig5K8efOgZ":"j8P1f5t46vhE","DRr4dh94E2MF":"0cIIeoIpBLQo","oANqVfjn9QSx":"1KDRnoiv1aul","eQoiEWRwmUE0":"4HjzdyuMOewV","3DZHQigVhHwL":"6cdF5TbuSeWX","g2xvDIrTxJxU":"G0isjLTQ2hcW","b7xc06cdZ2U9":"5mEGUTZKtmFK","oCAWqtqF9Q1c":"AVi1H7p9zRXm","zzvlEHGcFTsr":"O5ySPzJvGD1B","2Vu6wMBnU1Wj":"8JOTXMZEWfrk","Ja6Z1ctg2cip":"Dt1Mft7kMHjn","lxpIpuXPpln0":"tG5KsNwww7pg","xlGXmtFEr537":"bAErnx5UZJGH","kelo2EBJtngK":"Bcq7axWeupLN","75HqAoQwyZXb":"ICSQbLJfAhs3","B4nnPIoPowSX":"9eacJB8ZarIQ","Yq4apymZXt1Z":"2w1Ec1IfVFdh","35Vu0M0wK5JC":"1cQI2Gq5BTis","eP7mvVrv4o4B":"HXObXlwUsHEP","Kv2wvlQITVwp":"ORTD8SfcXE1a","UB7FUoqO3s3Q":"AyTdHZym2eDV","hBDydVCdwWLb":"yY6zUYIWx8lu","diwqgNB09mDX":"UmBxUZse0prj","SHg5V8ATiNtq":"Rg3eAEKx8cTJ","BBTp4ngaPBPJ":"2j9O9eFUxDzr","hW4so9MFwTU4":"VeaEJzltsCYz","q8xLhjkPXOLz":"DpYNkufQcx5m","ZwmSAz10N0HU":"4PfwW4j7YWS9","hh7LNUVw6BKu":"sUt2HcQe6VNo","vcZHv5Fc7jAU":"rt7ZkiLnyt5w","DA7mhGxlAmx7":"Uyao1u8JIsu6","DJTo3Mg0FO5S":"caVZgATG1IpV","FSl2jvsTeBOc":"3WoSZpjIFEzD","soGfbuVq1UeD":"KJPf4Sh5GZys","H1DXpquOQZzv":"WOaA0DmRBh2I","wgnoEfTyNYQg":"SBpGS9RosjbN","VZlncbkpTL5j":"92MbBObruhR2","bHmQ6QCWcAEe":"1BnwmXUq6TUx","bAptIlQpj5Dt":"wYygaWxFuURg","2uAhqf9G2Sdw":"HXXkPXZsv1bM","Yw9DD6ma9y6T":"R9hmdjCHmB4H","m5yQrsogG4uU":"X756NfSEtpbI","Zwgk9gMUbkY7":"ab83Lb0sXfOg","Bfwc6VfxknSR":"Axbi9G9n4Min","yBOWggMsRsKT":"Dw8ApKT2sjSs","1ma6YNxl97Ec":"MqHSn3hoUfbk","Yfj2V6R54BCE":"rNgO2e3fALS6","KNz6sUyUKWXY":"fyjkRpXqAKQA","KN2ubNLCQPoe":"yXpvi9Crxw6Z","aIbgtfpXjrBm":"3hgpGOANQN1m","F5wuzAsdwtZj":"N5gbC8arXE41","AiZRoJ0r4aJi":"iLCA5lgXBqDo","nDg3KRkvbVNH":"NBD8JiABX0tO","m0wgV4Z0sS19":"besWTiabCzLg","9vKTF7ISK4Za":"GAX7gQXan4mg","94olpEib7SJG":"EoneB2NQCdl8","zliopj1jfgrS":"7OkbyxgBLy5O","aXIryfzdQR33":"NWcr8I50dEcc","1p3VfugBknc7":"b6ZidCiyUT0B","0UJ1FoXRW1ia":"FCIRpoHsGkHs","bifDxzyoTPiV":"6LDPPxhkhRyY","WxpkB3aztKHT":"mIZAlg8wDXeT","nkJDjGD45EW3":"MqleTfTqYSOH","yIegyH37DQDe":"aCQ54OwuO0wE","EZkmkAVs58gt":"CItaBZlsMYCX","dTNagPICQ2Bn":"gSM222tZxT0f","3nnCHIKK0V7H":"NLOUmNjP9Ate","UInYBAWZzoWN":"NZZJRo6VbFk4","MjhGTxfYxYhz":"o3uKPza3ISQz","Y42wGjvl7ShM":"6133PLmWtuf5","1Cn7JtFoahaM":"0Lc0pcYWsDut","csNNe3w7EeLg":"HpGvzhItfp08","2fbdVXlyysaw":"rdoqGOblevLr","C8QEwOqE02vg":"bgCefyR9mGFV","BdPhavDTnkmw":"QiePqx6qiBEC","kwh01qPayvO4":"NPlRjcFLerJZ","Nt22YPlwuhdI":"7USrhwe9lmM2","5katcW8lOlwy":"LnEGNKz3UjTk","cqlgpiM6lAiQ":"h2QvslHvw8ad","QPYaNylfGl2d":"E1FG9l3qp9VM","2ajLrxS6EYjc":"zDjUcidCQYXs","pe1jx2qMOzNa":"Mw3NXs8tVxwh","OSS5RyQcUmBM":"G3IDPUbCuHmm","fsF2XW6rHAwL":"sYfPAz8sNNuP","f6mKxASoUkj0":"aOTSUjyZQDCn","i37T190DHmrK":"Eahu3JjFBu4u","er2fv39Dggf8":"I4HyFllfRoZb","R03CTR0z23hg":"CXlnbSXAN4Pr","114gdXHqciL8":"k29m6PMZFGNC","zBquoIVcvrWR":"BeJ71TQ0yzaC","7Hsd4I6rBogk":"cORUtiQXh30t","IYMP66nVkhDv":"PfnOMLTFWsCE","RHoUa9zkZNL7":"DJyA4zhFJ482","92R0McIoEQkx":"sCHtGvXuwIO4","9Wl55nbXHdOE":"233bHKE5Tqhb","za3YBuwDaSB4":"z137yrGt6Eex","F30Ze82ututK":"XuotiYAlne8t","pqnCXDkwSrtu":"0hthtTwQz4V8","UPWxFvDtQLnH":"KHLcX3d9zXOn","Bh9BZMoQuLeN":"YK9LqoELOLF7","jJk8m4EOB7MW":"Bc5A6XHN0Ez8","3t5NUy8f2x8x":"jHddTruEPUc7","5FBm18dhmZCV":"ssNyjUjroHAC","trNiXEUdjlgZ":"XhPausJFKclw","3DK7XqsJnfiE":"dCn7ICNH3vMD","XWrrMjTjhPFk":"fzkagQYDVqpl","lGKN0TstzUFf":"GfzZEDOCLtNu","FkOI6sVb7aO9":"03hF3qegPezO","iuM3mVip8x2E":"LRLw6eeKHUSv","GydwECH5aJWc":"S5nrTgD9XsY7","oKHbAHx38nyA":"Ssm8ERu0zZEF","sdXfZAWgUC8y":"hwaJ33q8GHNx","wYqbCJ0y83r8":"7Y9KtnbKtTw4","JnTYCMV5tLHX":"KcTNoUl0gIik","4ev2mONaRsYA":"N26gpRY8ecOS","Pf44DMEKEF2p":"KcZlI0Yg0kcx","CIAg5CmdhbAb":"YknpxTS5Ya6G","vZQzi2Cx8rjl":"w9eFSQgAHBOu","bi7SeZ8lpoHf":"vcqT3DSJ0wh8","LKD5F7hptuva":"G448Py3YNpaA","7Vf7j72oZJ5X":"hKODxSZdqes4","Fp1Fxkvwwt6p":"mkO5nkeXOogy","mpU3iupJ7ImO":"DmFmsYNEcvJZ","CILoiI5g7OTr":"OS5MaeFFkbMU","hHVaxQDWABVe":"tSuZZvBAHnpd","lhk1G6NouxWb":"TS5AJSzgSO0F","Zkjtv7zdtK0Q":"JXvjtfmzjcA9","8aEDMuuo3gA9":"Wrozr8cqjXum","mor1zKQ2YqWb":"fID5rOUAwgKM","WVhFfxRsJPGA":"JHLMhyr3DpQR","pBQO5ZPAxJPH":"C59WBrvD3SWx","yKpwHsEng0mn":"zNVQG2s6Ohf2","Jr1gejpqjin3":"pI3yHr6DDF5F","DFmYD6JIcrfX":"vzGkw0AgyIvz","m2otiqMo4NS5":"33umvm88MVwu","jtgfMeng0rLN":"v55743Vb2wJY","wByGU8zaA0EE":"mEyml3EtiyGa","2GshdYAlC8kV":"N4QsryYNQGME","meMft96DgzCw":"XAlHuZWi0tXJ","Pk8BE33gbhWu":"xQpHIOgUQHa5","XcCidyFlrjdx":"tthjkgYk2hTO","LEinDj6MTggz":"E8o5aWNuKBz1","imRY8clUXSCH":"LcaRYgPCY8Gv","fse8S3RrYmmd":"l5S9ie2lNdej","VqZpQKz9Cp4T":"HL76YCzip5sO","6VKEBodXhrAv":"PvHlVuHiY2WX","h5JDQTxj2dY6":"hhHF0eU8O84u","M9gWgRczW2t5":"iP2wMwxgJKGw","vb3PD7wP8Bqp":"PKWmiXdX89BK","uvDOV3mNrCeN":"skCTlB9oU9Nt","gvZ91Ke8M5gI":"pkRbt5EdQTiq","HrbvozJhkjF3":"ZU8Ya52R6KLD","OeRYsfWkYZWh":"8WgOGOu5BGlM","NuFxObIdKQK2":"x5EE1CMc2epk","gMJ5Q5fhtkAT":"EdGqNzNaWKxn","HVOWdWHsyaFz":"GxXGxp8DMQDr","W0Qd3WR7kd9z":"9wbR1YdtqdLN","XnazQj1YGeaY":"WuscwqcuchsP","IUge6Z7HLHJ9":"9MEaxlLX7ehB","scvHD5KwEaLm":"JnLREONbSm7u","t8z6rhXrAgGf":"1jP8UxyoYEwg","pMMAvEX7r2HZ":"dnHWWWkh3Mvy","wMgDLvF3Wv4M":"KDWbuy2OtGJW","4KAfHqI8R3Zw":"2nR5bs9gynmO","9N8F3SkvDwPE":"bObpNyEEbTca","QsToBZy4umKA":"NKZFsU9rmsL6","G6RiqdkapU4v":"nptCi4Pl5dnb","VbLd4UpDNZ4d":"40LjCX19StIP","z1qFe7pJR2Yz":"TVvLdci36kXn","ne6FEfN6hXxc":"JSCBZg6qTUV9","2yUcurLezytN":"OQ1F1y8WzBkA","8nyb5ANXBbhL":"KFRE1LxNznu0","k33Oy44Z1P1f":"ShoXIfuvoEzU","dZ6sS1mIPeqI":"X769DGOsATAm","RWMjWLkT5Rxw":"nAT24Ylfbt6D","OtqSp8eSGZkO":"XNeIJbzCF28R","31zkIZgz1jqZ":"z8NQHbla50Ri","MjegLtqa9JIe":"8T7qtscpFhzO","UAATE2yMzLD1":"EdJNc106dSjW","IYp1V6Gy9z4x":"ybrsTYcgJGrS","6n3N0nJlvoK1":"4ZNP2b2Iquj6","RdLBX8GNRRlM":"KUYuO5RPXwq5","EYi7kyd672su":"jEEKxGLcl7K4","D1ZRU9fDSiEO":"oOUXCthZt1UG","YnhiyTalZPy4":"jYKF8IbYXYgk","1mDviXFNktui":"qOn5MdPduiHo","TJgUAdXEKSjy":"PSar686eF9tN","mIrmKiK3MLW3":"Y5Ok6Lvbzcw0","2IJ6z5vqfdz8":"3SkZ9h7kykU7","GiEdxuThy0Na":"TxVRuvvSTLU8","btlheZpDGQps":"nXnD11JTY8jI","0ZIVkL68WgzY":"sz4NbEZMQWPK","oJh7jEXV0Xq3":"25VuENBPVINP","Iy4uYLnXERzo":"ZOvUDPEx4phy","jvwShsIqV03Z":"cggqFgDarK0W","CsGpc3dZIPtn":"0gQ28teuPEHN","jOVBSBhdQFLZ":"nRSBoDW7LuiT","9ac4cF3o0B6G":"8vBZXq0F8qds","dJVi7fUiK7Q4":"qV8mVBHl6N8I","02OSIDh327Rw":"eKwD3kvpl9rY","LWVs3KDlTd29":"kdHgmMmOgcXn","3ElOMijGPNy2":"is9baTFZ43SW","kcmLpyff5ml5":"sfsARjJxMz8s","LIptQoeMOGua":"Od5cdDdN2org","Stx0OtBHuZMI":"YkbRSXi2LU6i","W3COEzf1bbj3":"kaDrz1VthqAx","gbQB4APfv96k":"rHVJraLyNQSk","1f3amEwJ8zWQ":"idGveQGkkMk4","mufTLhWtzy2w":"Dwpr5kbspapl","XHJswp2i48bK":"oJQi0o9o7qSG","5N8QAV9WXUhE":"78X2Hsb7pR2t","ppZd33gEam6h":"AazMdOFzPNoK","atXtCzU0Vjkf":"5wZ97XUD5dzi","7oJuAH7LKzCY":"FXB5nw1tXjCy","L7t45oY84sUs":"eG45sspCy2E2","sG7o7rNT7i5D":"vxYmilDxInkn","4Lcbr29kZlXi":"oY1FrlVOY3Aa","YRGjjpDZSKVC":"eGhM06OhrbV8","TK8gCdgh7UEz":"uy5IhTLpTTNx","Gjeqj8iJsAFM":"MgHZPbV6rhVi","Ve1I57aBhb7s":"piElM9NgO3he","81PFoRBRlb1d":"YIx5g8b8cleJ","MVvNhtnF3Weo":"FGFD5nI4lJ3A","SRZtJi5YNoyB":"qfsblpDnqukQ","krwikiFFpDqE":"iKX9Kt2mdPk1","sN8f5pIjUnZE":"9SLj0sMJcH6z","dUHUSmNMWxm3":"ceztB0P50oN0","wpcdtrvNzyvP":"jxJzzVhAd5Kn","oCHM88wD8OcP":"NWbeIhq8eJ4j","COL9ef2ElKK1":"F0dg2IxbhCtc","7roKNiHKgJea":"2qfR3cGGtBJB","SG9poK9hW6Ea":"lhOyuAb2n8kI","xe1W2u94Q7Fd":"6qgyQm0Sg0M4","N76DWQXe1AKF":"6T8YZJBpnMrY","dUco8Fx5L9yc":"J9DWGSGsFQeD","15CkCDHn6P9K":"yU99idrevS7A","Icc87e0OHyjU":"IbAA0oqtfps9","V5U4v1FeaXPZ":"Eoy6DjhRWwVZ","FmjaUaXlgCYI":"7y2zaMAiJMIA","LvC96U1SGwfl":"x0Wxd2mjvG7E","Lzmy0BemMmQc":"mlfYYumaaUku","OllxJt1fa3gz":"MSGBl0rfcqnk","tJeI2QeXgAOU":"QbuiN4dvqRFd","HXLlv5KC1JS6":"Vk4J8E4wkz5G","007rMez8bKFy":"fDfB4UMoqdmO","yvwdEDmhkpJZ":"8Qklfa3Z17hx","LKAiq2jcVbhI":"0LoY9DNxSlbd","QTUgwGBRZMaQ":"rwW9L83WKGUr","nRfKLX5BJ288":"QbZZu6vQcDMr","y509m3jAmYKg":"dFKeN8jIklDJ","aGlcex0crcg6":"mV7dJR8qdc6a","SxRmTUXuwakg":"9Tv5epXuDAQD","5bX41FJZIkPs":"AK7JwWT221q1","TfZbi0YGIZ7S":"F54oVsSKRraj","LquszI8O2tSw":"PCE5RmHRHMyC","PSzBn0TECITQ":"CnDQGuGvovIu","vMpDvJTMNYxT":"R6hxkSFWWvbC","PkHqGQwuRP3x":"wcQJFZtYaarn","fbnQ117F7JWJ":"ZLw7ghvZZB2Y","TO7ZgLrBaCXZ":"U9pDr2AgO9oU","CemDoKwXodP9":"4Ch879vt3V2R","UM3v48sT54kG":"sGYHaYdgWb8S","GeawmdQXkvo2":"ZWYuIhRFY5G4","B47dyTjaJABV":"iUtV1b7Py3XD","JINcwB6rBIGl":"YPXK3VECq49s","ozI7oxO3fht5":"kXPHCCRP0NWa","EGzcGT6dzL3R":"lKHS4byVV6ku","rfoHt47qeAbF":"om45f1CpDgtA","vUpVoCDjN7PZ":"yKbEl9ufo5dB","197x4p9rHbIo":"wWIZEuIOjwHd","8Sa21W4hQi3s":"FYUdJOccGkxN","J2nGEUH6wPmt":"Hf2jjcrDIDQQ","M1HHmHuaPy9n":"BYzgO8jodj3g","U6YKWRJwrLGg":"4pA9lDqNPdiB","duwjJ180Un4E":"sVwksO5sj49l","jhK4AV0JdVKZ":"hgl1YYcFSBDQ","MPdAPn4Z8VUc":"yADL3fCLnUKO","jylcZlv6rC5K":"o2bS8i5ivB7T","pnh9IaGQxnEQ":"hwUYFaKpY8ma","JOdeNzBdJZxh":"qTajzitj6U7P","LJYnuzGQW7EW":"5whpemW5cg4B","X7lu5YPjUWHC":"hF61GifGgnqU","O4Q58I7FLWkU":"WzsTp4UH0v6M","6cv7Gs4wg2Ng":"i7xE3FUcsw5P","adhvycvHzYvV":"G1lXAhQ4Q4hX","ApWl0mI6LyhV":"o4C27bGnTyym","3JU9yDXB3aME":"yomG1c0Stfza","BTTzIf2P3xFF":"wsqwkyfk1Jic","y8w1Zgg5e74n":"3y3JHchtreOm","faffXIfDSBsr":"G63PPWXtGz6b","LB5lugwIhRbX":"HBR5bLgmceNh","PZsHM48RLbt9":"Ta2SxLfYm26o","UV9EyZShIeHK":"xeJC4MvLTNww","uNT8wesfHxQh":"Xx8cbpzp2tCe","bmQKEyiHPdmG":"3x4KxOn7YABI","97Vn6EAcTh6f":"eJTJYlnUogpq","GWEeAVjuA6P4":"1HEsgP7XTgYA","DXI6oK0TbW0Q":"dryF6qKNBO85","UmJp4QXfXiXz":"rutaFOajIxg8","fBfbUa65dKRs":"01Ye3ztvLZW9","aAbr79inwMwU":"nrXf6NSmxF6o","xNBmLlBFGO3z":"ioHkqXEoDJxJ","Ku8m9pFMafhH":"vY1Bben9vtFC","0Hukyh2eUrXr":"VkO25aVUrii6","yfsuULbvTDVd":"lRrLXLixp3bN","77sUm7UvOqZs":"r1lVBTctYhS5","rKYK15hv8yhH":"lGrHeUFJWee5","Dw8OuDr2aMRI":"M9uyiwbqSD0f","kPrYTXXFpaXG":"dXdBj40wNk9s","5h9Da7qtWfDA":"LUATKsb4rVCd","9jUmxS0OcUhX":"oRI19wnYQkqZ","uqIMq198SzlV":"u9gK3FmdlBHh","608C5ITzIoGm":"S3LRMBPUWn9K","FNZ0aVSGOtkv":"xbeSUBVExVkn","86dwTwiWExKK":"vnYSI7LoQGfc","T5t4KFqzu4wV":"sV5uVYm5y4bj","UC4trKiiAsi3":"BeI8CcIecYRW","deJgnYhqPerb":"tZ2gPiEYTodb","vXvUQ7OEtkw5":"gNAYBXs7VH2g","hFym0FNEoyxw":"GpOIc3rjA88a","29ueoRM8p72I":"47KGOVPsKf53","xLbljL1mawgD":"gSZZmlb3iyWr","KmrZpW3UGDrI":"BtU3M14jVNqi","BqlYbtxMWYHO":"WLP3DSWHLX1L","uOJRjYUVe1wT":"OrX7LD0evwGZ","z6bgs2RaANAA":"JfuMyY5v2dxk","RE0apn6I551s":"iB2AGkuP7RNR","D7rw8RrnVuS0":"GCt6OkAxUznO","hYcbFCxKkcq0":"UDoO60u1CYqa","GyMpwpv6W2se":"6mHNppin92I0","v6IumiFITg5J":"2S8H0L6Kemq1","8ivMEtajejch":"vnYEtTuxj7v3","84WHBOMo9T6s":"AtvR8at3dah3","m2pfOf1Ul5zq":"z0UEqWfjGDYC","Mdk3yRnfaskE":"k0f1ajhiuLnQ","IDtgSUDVeAjy":"juW8lhNayWYT","rFZvpxbyH1xA":"sMqCPdnqP96A","hZ34cIYuBDSP":"2l8CS3ObFWtg","3OxZ57EukzZj":"4BbyJD5mrrAS","4HYe6RoJbXbh":"ghsU11j4aZrK","rBc80zi16Kn9":"Uliq3GlzDcn4","D5vB3ke9SIPd":"VjrNhmVQujEm","K55De8FNAezA":"1eLZJZDjCwhm","dASN5CwNn5zH":"cTmediXbK1Bt","dq6qRqqIn6yS":"LcujgqKO7zn4","wBrmDcfeJbz2":"LBMmMRybqUbU","FTQHDk2CCdwb":"w1Ndzg8paeEg","DJcWX8e7X8hS":"Uv4YO46M9gg0","gNSN7ui87bvU":"FVp1fCDkiCDI","n0UKlxP6eGAM":"xrcigRVQhRuB","SpcPfKeorQIH":"4rtXsTL9mIFK","wbSffwrYJ4kE":"W24xm9TlfRCl","ZudGxJh9L3wr":"0p5BDn9TOCqc","4YGFJOjw2rGx":"MvpG1TVad7CQ","9413VbU2edoa":"l50oLRDwJ5TF","WA2Pt5ETo0Mg":"X3GyJ5wQUVWK","bcqNuqH6X4FH":"doOql9yWmuOj","81Egg31TE2Ep":"gPTbieG2ssn7","LctiuCIbZSOz":"LqlbThNS4oi4","fTLxZkcnuEqv":"WrOBMFbbeiX3","8RGskVIr8JCa":"xZhP53FB5sZB","RMqNXZAg8xZy":"C47UV5cgijMw","IUsygbwNPabn":"cCD6K0tVNtdC","arnHJSwpt8xC":"TrKDa8CelJzX","wOMpMte0m8Ny":"abmhDM6R88Jx","puueeXYFMqEF":"aHVXhw61OkyK","HaR61GUG1N1a":"hcCYq43Xl8Hl","4sblCYDSq9v5":"yLZahNr1MZ6T","7IXIGGWVOVXW":"zsdIIsGdjHkl","EVJPTUPVjHlQ":"PVpvF48gE5Fe","rZ8pZqOmV9pT":"6LEBStYBevXY","4xVa4UWCSqWR":"1CC60LMyO9jn","d6mKTIbC0Zz3":"fk4gG2JSLFGP","LeuWgR4NwQhF":"95OYYfNvq142","h4yoCmu4mdmv":"nrB1yavUVwbA","zdkq363gQA6H":"tJN9HbjD6y7L","hMxJ9kWnJJXW":"VT4RYF0g9xJa","ozfn6NIXO4jm":"iROuKBhQEg5J","S6W3H0vXRXv9":"NuRKzNae3obN","HB1JznVHaaws":"JlqqhLmpD88q","vadU3A82esSX":"Sj71x3StXwjo","enYSeUsfAbDc":"yinZnVOfIvj6","ezWQp5wxhArW":"vvvCNojjkBRj","wEEdzd2vuamg":"i9Tx02VRLJc7","bwlg5boaOSRg":"crpXtaICu3qd","rdwJcTNv6JOz":"I71JDsnBBQ5I","9MedRhMPwd32":"0dsqwPHSjSPu","u50NMjaTjdpF":"On9VP9XRr1yU","UTOqjo6I8w0q":"Limry2RMpr8e","NJY6emtxAsiR":"IZHzt6PtRHo2","8Jk80QYjQ3Mi":"89XYwJMtf8pz","GeInyKe5cvfw":"CC3Uq0C81qZN","Zl8mDESumCWH":"avQznxeO9yof","tXIQqqg3ODiq":"MFvROEj7SJvZ","5NHfyyKBCzB9":"ECmmFhc5tEDb","LBmEey1iBpjt":"879wdNPNu36S","ZgdKZOOgfKsQ":"SxwyjQlTq69d","7DJw1oOdom2m":"VanpooDKJTu1","gPrzOKwpcvvc":"3fckofatVjA3","I8oMLQzrz83K":"y6JUYbTyH6pY","EgICnLzZyeCF":"Gl10JkbagcOA","KXie20qXY9Pj":"O1JpvdzLJKDO","xTry12zQ2c6R":"blvIn2EVYgnz","VJrphiQihTVW":"EjwFzdpHZos6","iOsKB8AQstCS":"VO6Q5qQ1JLue","4mrrK2tSwED5":"IESPkSoDpQan","kckPil070OUI":"ecErbXfkdhhU","dHaQj7tbh8Ip":"7LWXnefH8nlI","qcrLdnS57pOE":"kVK0gqh0yGxl","89zvUO4sSe9N":"Up0tUaojF4uv","2dcGCmRhgjUR":"jxPz4IR0uL2L","oioGGTX2eWMw":"FdaUh791zxfR","MOhGJkCHNWIH":"SA0sy1PPVj7K","kMHJpIRhBAFZ":"UCt8loizCC3q","C2lUNmgDMQ4e":"94wdWvH2j8wE","trloHDCttSLP":"hcRt9CbCjUY6","TgX3GLTtqDI4":"0Smsw2COeHfJ","UTndI78LKEz2":"ermXuxA9n4jI","Yw3UGpN0K3pr":"maSlxN9BXXJD","GMjt3SWv8Iav":"Pmdas6dq1AST","aZtNZBtyAO6D":"F0kvOEh9rx2v","83oAPft7x58d":"amL4qwdwqV0p","qzJ503J1JWUC":"Qo1PKHxoMQE5","4CE9A8ekI6Jw":"0FMZcQEv67kV","SUlJsbMIsleR":"OI9agwvHyGdM","xiFOBceTrCf1":"d23d9gGr9636","C4RtO38hnp8F":"wr5aENpo8AcU","PuNjmkaGZBLj":"NwLHE41ofBrT","ZpU6yd3RC3gl":"5wYwWR6zQOMN","86NXQP44yjHQ":"MlvRvSjCxyeg","RAx6HLyFyWus":"yuFNhGN4lzna","fNSMTbFnjsls":"gw8jOhUksZnm","UFBnJf6psBH0":"4htrlZwXrFIG","dE9kqOz17BBP":"by6rET2R7XFQ","sS0Ft8W4rCdI":"GlYGwgRMDA6a","aIqABfJFGrjp":"ZYITl8FBTexR","Ida1GDaGLOQp":"wZGF0KT80YE4","YhPoGBQQogmC":"hol1Axf4Ptzi","k9fC3GbVZ2aj":"LjI49eYlNUCN","IctwFZ4PCQZo":"KqSp72vcgKqH","BNAL3YDcPlnZ":"IjR7y1ZnkSXI","eHkC0mjCdjcV":"dh3BCSPi2lxR","8a9r40NhpbFC":"JcN9vZ8fZgxC","muTc1GKEtUGd":"nHETuUxrkJTF","Sh7EpMeLtRXR":"NByIo4lc2RiM","mM3yghqkZ7ZK":"hKljJekt9IuF","V5yoGZKfTYxa":"NVx7pKTMmWpl","RkcUtO1kdfXl":"RWGwJFC3H64P","pLWNoc73nDS5":"HqswyMkCBfZi","B4tLff2ymt6R":"zmJ9mjOoRQX5","YGGPIIW2LB6J":"s7eSeV9wwdOm","0t7qq8PeE9mR":"9uNHlinbLV5p","FHDGabALwzMx":"NZbchjuRsxky","ymBz0zrzFuWk":"y2IsONiGZakz","vFDDMygsbVL2":"QRA0UYX4cgV6","DVQkQsmSkeqj":"bJn4d58sMhsY","cKycWH7c7MCB":"q1iZaXx333Cl","1cufRd3MIZbD":"tGBzavjLmaSx","kolg8G5Y5tn0":"k73413Ka67Oq","dbOvFlD6nsul":"vnGDLd2AehQq","GXCRrXLkefww":"Jve8dl8OapT2","MXUd073B0Xp6":"YMLFUcsNC0U7","sWPC5DU2WVYq":"J3VLo8qYh1Dy","KX7MKnXjzyqI":"mSNocxxpTLsh","5uImZGm4PylT":"Si6AIpiCwfKq","CoAft5zTrQPj":"XZA2mTTfFd8W","mOKgeR4z0xSX":"CBuLzoWyP5q7","3UQUNzYctmco":"XEfYpmWrNt0e","06M4goMPtU6d":"iFtY2cO07Y0v","mE3rys0HIPzW":"aqz81cjocFXi","Ve2rq7FT3GLt":"V8CUDLm2tB6n","xoprszRFtT6B":"reRtBuohZ7RA","czaGJXljGf80":"Ux3uWe1CQBxq","3KtQzRpgb1CG":"BwZol27V2smX","CmtCbTzCFhKn":"gPm5UJiiuBRF","x0mrhppdCRVo":"fLTp2MZDpzCo","LiRYGo6JOCy8":"RcFjpci1pi6g","9ycZrdaJU8eI":"TX0IEJPTK5n0","rOftiG9f8xuk":"KVqNKO3DcgxR","0JV8jMa0q5vx":"Q9Ed7U2en6gZ","cdCKpRETnMuf":"nCxXc9jn2x9O","nmukcsU13nyu":"6Ssq4s4sfphy","sMDtnfxKuMM8":"lN3ppOmbTuHE","TXWRrLOU6Vft":"DYIX4KdRLkD2","3r6coTc3ZFIS":"VQ2BANX40Fex","MMyn4VD4JJzX":"g7qSykuLCwEf","qMilv8CKWYPZ":"NEZn0YJiSSSA","Qb8FHrdB2BJR":"eBVUj9TsNZ4l","jae8WS1ub9VF":"O0w3RAJsGefs","sssjRJgrQE0l":"cGrZXVltvVBh","4wnF9GiZOKeH":"mR1fVHTyok6s","Gdn7lnyn48YK":"2XSnuWywEBMt","eMT1l3JGzv9Q":"wMGH9rQuW7uN","aSHFYoOzGeox":"XHG97ZJUINtE","dISWAuBMWrfq":"vB9OI0tOCH2H","Zkm7Qj01lRxA":"7M791UEHxlWx","RkN9WUyDKbAa":"ou6JjxOWxVj7","FZqN732aVUaP":"u2yE82XUtNPb","PTongfC1zpd1":"thrgqB4CLUmZ","0vkizSbYIqTg":"LaYEX0OxXeQ7","4jyNvQPR6b2L":"4tIKeQvgFS8O","yIakaheIC0GF":"5n3g6wxhwuN1","79iJ3G2HpvCq":"SuLIqXFINKam","3YEvkwp1oZ4f":"0BPbVCJ5FkcJ","0DPkYOJs8Xkp":"q9sc3EM86nXn","dLYtGsMZbcbH":"bcyXQJ1gUUtg","Zho97qkP9b8T":"WJfClPAN8yvo","yB6oHIv9A3oC":"v3UwSfIiYqmD","qg3tp9LL47pV":"mrVTSEymeOJO","8fLbygFa0AxZ":"zXTzbsO53yGv","nPvQG0t6zIgD":"505dkYqYJ9xZ","GNLXaebdTQob":"OlfjMCtLBVAv","p3VfT3aleHyL":"IBHSYd7BSx4n","kC84HylBWx4b":"4r7oByMS4Vf9","DnVBCHbMvXJh":"oZhX0szea4yk","fwwiylkdAETK":"pcK2UKgmZ3jS","lbMD6oDyQer8":"yVCj5N021UBe","7j9YkPsUFFsN":"Ym7tT1XcPZm8","A9tcbhXqv336":"dB1imv6XjLlU","BJZijrc5gOdN":"htNrGGuXUvDz","cOjR4cNTUjpe":"xGTcCq8Hx13I","gq9Nc5OqCOj3":"HO6NDnoO2wkp","paT76K8ssTZ3":"Z0aMZWDcI0sQ","V7cmkR4RAVJ6":"Tt2Axtg5Q3DB","PxCkCUXcwnqZ":"sDblo62JQDxa","S0MEfbuAcRYt":"97drdXUoA3Pp","sjzsgYzMc3bB":"8ZTwlTUBZEAO","ZrXyxAoHf8xk":"XaHbwg2v75iY","Ms4SOk4S9clC":"IKUAdRlo3c0T","M71kiUvh7iXn":"aAOtw5jdC7Lj","mlpBAAG227ea":"MliUaUXbiLld","0Qrzm8aNZyHM":"5IflYBI1RDPR","PgTDUqtYXFfd":"I7ZQv7sYFblW","Sfo3UhGwLJQU":"PtpmFKfTHqjT","XtThcz6yWBif":"6OttWyG5vzvD","WKea40rh0jtH":"k0iL49AIBcBb","GhWEeZBQHUyo":"JyGwB9VKpMn9","RwfEudnlcMGG":"ZwRTPvfSx8x0","HyyNVslQHvLv":"yAozHRajebG2","PUZijVSjsX7C":"WQlid28leYY0","jaMFCGJSKZaK":"r6PEElbxq2Uc","apVQsFYhyGjC":"hcCmau8LO4lu","yjvcMkLfoGTe":"7LJeCCsBvPRf","DEUvbjGbDFb7":"nHNVYU5zMQ2D","CObkO0dPfJvC":"9SjDKDENqfg5","Xbe1Si56IuxM":"wAj2SqGICr3L","xZmfvNMVSRJ1":"hSHPHy4cfvAe","xQeZgO9glvzW":"L7IdceyRdBfb","VkLSRSV30cys":"ZeEUsW4TOJBm","bsoLAC1cyQtY":"Ojd1us3GIwbK","SIR4IQy2l8iG":"9ihRccG1ymio","ySea1gtER8jF":"r11ekcMNl00d","9S0jnKn1ah3K":"pb3WoALLph3h","Abh7mZE3qWpg":"2cob4GFxpSWU","PPjDVjWvC9pp":"EwsvSfRBkO6b","YVSXULEOIX7u":"8aK9PZndCBY6","f5dgsc9TQCBp":"TTcLJe14jhsz","dcgw6LCyczzR":"uVhglGp0dKkb","WV0v0HWC9gOO":"BPiYvKZbXnaC","IZtC4jdMuQpU":"xDJTZvO3QpI2","pnlfFD7SVrog":"u04IBTs1c8TT","84nmt5Qw9Odi":"cLfPVjEppRDF","o90me4jCOf5I":"y6EXk764jWHK","jBaUkfpzVMWR":"6pbB7VKI7cV3","VzKdMybF8Ri7":"POyQTVr4pp7B","zbhBiOp1S24P":"bSpVsJ6udtnf","OJk3nXxNIYVq":"OtxsJAbHvWvm","KnOCN7ZZ1MI7":"IzN4LbxvdNIm","4iTtebmzSuBF":"gZPbf4tNPP4Y","iPvgNF9Pz1CU":"0kUY8DN8GEmP","QWIHjkIG8l9x":"GTs4Awxu0S36","kSpe7BgHb2wO":"BStMLWsyMCKB","Xo4NEfCAsSPO":"IaAqVOLuy7X5","4ZbYhGIQByUL":"12xqiz5rHpLk","5YDX4Z4MXwa4":"HDXs3qGgrWhx","n8Yaa9vJfc3o":"RU7uE66EEDyI","VViFOc4yeKNW":"CLXGYfZHew0o","5P3ogRGD1uY0":"XzKOPNIxiygC","jIOhRwPurDnn":"UBuEpyUlzu8X","gbGFAhVzPcwP":"UhnbnmfFehh1","8EXCQK7FWt4K":"dAHXExH1qoxg","K8dPC0M78FO3":"yWjXW7HuvD0L","7FEXI5vgzg5a":"rwebN0PHbkXI","YWyEx7KP2B2d":"8cxTIBhTkpdy","ZqO2iFrWs0Oq":"kB0JbZfngqcm","aaSjT0DrDzfn":"uv5OsaUIVrL4","2WIgdY9EEhiF":"mTBz3v4WsZ9E","cCCZe4OygPH7":"7ijPXxwoM5Ck","XM1IB88fTFUT":"TQS1CiwjFKS4","xhRv4fgQeZPf":"2xPfiOTau0z3","MJoIxmf9kmd7":"TbQFNoSSHLNX","ynAgwivqVh89":"jagyN3McCYMW","pNyz2RavREum":"xADPWNxTqOtC","e3ubvw5ig01U":"VNHBUMEPYQfz","geH6ZYm0rLEq":"F6UyYRY7fDeJ","8Y1piWH42lWt":"1ZDLz6NU5OVo","5wNG8cGCNAGR":"5ypZbLuKlMfT","bf3hRiwmPfyZ":"d62gAkRmXrmX","zdQRCbgIxCsM":"RATfVj6bfuQF","HVEUUfKMvXi5":"X40GwRG1TSj2","VmPl7yoYijci":"QLPiEmoZ3SLh","bWJGddLaXsJ0":"BWAHmnUVlex5","yUKOflAwGlZy":"wFSZk0s0Y9TG","Nu50z94y1mmn":"dRyGIHKsfFxf","xkosPRivJk6Z":"RYEJ4l9nifDC","Iscby3pN4G6E":"bLhFSP2GTX2H","giEqsUKEno9P":"9uwngDbYhdMD","U7Q1pC8z0Ucd":"CqfhLu6LatvM","mkjNSpWkidF9":"dqt2Hj4qXGcV","z2ZAFVOeMbYV":"JiodrDidsECw","xzoD8ETaWuNv":"iofLAZff8kxB","XPHg8J1cSkNE":"UzSLw7uoluAy","uPdkRBy1dQcV":"ds7L8s090D0s","HJP6uD1OgeJ4":"s5mIBuYGscNQ","COuDpWgwYkDI":"J6yGuPSehv4z","dyFsx20qiCdY":"3vOoSY1XjZs4","aMHoquj9X9pT":"q333wcfLie6v","bFlS7g9ARRPi":"Hz2Brrdj7Ax9","zKAbFQDYe1ag":"GQpJfca6G2yN","LWvgFIbmmLJ6":"FyHcEazSizAq","24jQNFQVfmOZ":"pfjeLkLvq7Qq","JCBGtcGwxsAm":"YYt6qU1QcRHa","xC4eKNupsb32":"z87b8yi4SoDr","Eqt3fmE5CrOr":"VdGeiVujTQpk","4qf12jjQWnWm":"FZj6uYN4bNXR","CdxkLu9pRND5":"ySKTQeW4n8CZ","s97PqbaYje1P":"10JNpCYmFcNU","Owclr0L5ttHQ":"nBc0zRdMinfj","EpTIuuMXHfdH":"XBfgDnfPQCQM","J5S5SLriTLV7":"2pzjJQHHAX6t","yIoTpbUL1YxD":"e9bjIsnMrGJQ","NlxdewHz8Di3":"j3NXFEax68wp","WSSM5a7BXVba":"owS1dh4WbyEE","jFxS92wKDmIi":"uuCWsCOYTTqL","o2SeavrAoCP1":"yMaCQ4ffx7OW","X6PtLg8FnBvg":"NGK8lQ4FbC5q","ATYK5f4sqCdN":"QpwtOu9Z0tG3","EvLSHOm7XwNT":"fHHoMpsLnZkf","woTWRRAMfYYb":"dwH4S5ogFCe6","DwNEudMGMEhC":"WR1uB2uGA3of","rGYeT2nExRaM":"xjY9BNcqCvsh","6qMxRKEB644B":"yRWXEROtwLiL","H2XCPFH41kwv":"hoSlGhhQA44z","b0KfMB61s5bQ":"ui5p8DF2AVUg","ipmA80rK3Bkp":"ot3KguI7TSew","gqdfpuqiQwSo":"YriNjf3lppTN","jmFDrKacdCHv":"V3AHTPpgz6TV","hF0kxapbZvYe":"G2AvjVXwikaB","1aopHoe4KVpz":"Kk2jXKOB7d9W","WIWH2u36zxbn":"JN7vEc7aX9Rs","dY1NfzytJhEX":"lSXAFpFPOxHm","FuSxu3qd34kj":"wMenzhMoseB5","6JR6hDuHsHHr":"bYblVKu4uEGI","9vqKxsw5zGmQ":"65CwTEPqqzyj","bUW6yT4ToCCd":"KsWnE7Hn1Lzp","0uLpiAfpVqEq":"FvAk9HDdZYWg","LSA3mgtcUxTM":"HdAHwdab669J","F1XMeEJB8F6y":"PYLhpBRix6y2","Ewwz8nKD483r":"4T66cdKUzkg8","jKIPoNZFbUEu":"97wV6YIYBn8b","UltyjLDk9h4i":"2Tt76QKFomOd","onUMraMpgzld":"NLHhdjemLDbd","UpZZcb13a9HR":"unubMml04afy","u3OIo01mb6Jx":"MhAVqyfK0aWV","leeAvpUPEC67":"pzuj4Av7Goyc","P4pvqZcINvN3":"NYYBkBQaRCTx","NiE0cJNg1dZD":"kCvUy3g9mLeI","xcbXnA31y0Jz":"waz0DUvmdj1Y","BYMCo5qffHda":"PM5YeKA7hC36","rGgeyoJEqVBA":"bmNokCRp84CM","xspJATsJosau":"KGQ4axDuSmUq","8Mu2042kEy4o":"kbucAGwLQDEQ","ozD88LXEmxX0":"YTKKRMo1YFRB","x3w0Arr7pRIx":"bJAqt8w38Efn","jg5omgA9nEix":"25kVP0fPenQ2","UNnYWN2R6HuA":"HRQvSVsPRQHW","86TDaXZcGTbU":"wpcYO6zMAumt","GUgS7PqJ4Nyj":"BjtNhgQZiJQN","lKWLx3ZdEz2V":"4HgooWpxbl0Z","Y0pMAYXxDyHE":"ztbGbubzpfJF","kmCdRZhf0ep9":"pW63elsB7eF1","DpHZDiKk2ONa":"yItxqo3H3vDA","RWkLH8OFV0CS":"skjHHWTvjU4b","WHjVmjRHH3wD":"oOFQdsL7DGGt","9mSfGRbwL2xh":"zG1mDGGw4ag9","d9suMUOvGFQi":"JsxX0RB3Pqwl","2GPyoGUO9rRy":"Z9u1eH3rxbeu","GQACM90RFqp8":"8zCV1DUihHZ5","InAqrBKrU86a":"jK0uZI7VjO4Z","PTe6iASShPmF":"ZAcSUTqbACCo","XhPlL9n7U7R7":"tJ3WFwr4kLhx","nUgIyaAm7GUW":"ryuGnm4nxhT3","awREi70DGmQQ":"9TBpUZ9P2NKe","900P8ZgqbJcn":"HFgy2zgHT7u8","Aw5w6HGxyXAh":"tg9VJzZUw1qa","JW6xfN0GshCt":"zfczbAMOD642","AUVGXgA1m7Z2":"L4PL3dPBZya3","at8Zq0sn6U5A":"ZnTrFfBcmOjm","XafqrlkC5XBT":"xG6Aiyk8oEiy","kANEI2Z5cKri":"EYokESGSETy8","cc64uUYt5ZXp":"m1Ih9sxbytsn","jbxE82BnEVhM":"tkkRZOBX1TiQ","0on4b19LlbEH":"k3x7jflialUz","CptbLj78nnXG":"9pw9BeiKidP5","Jp5cGQwfC7tk":"ILduWBPGpMtU","gy5ssm8oJ0Zk":"xxRDKABtcMQt","Bawo1SDFhGu6":"Z1vu8dORc6e1","fTGYSnaYvpUs":"eY4RDpssHoV6","0wfK0Vqp2YlP":"WjBr8hvNIwNk","L2ZNR2n8j8Qr":"gYuuzLJMJblA","Tf9Dk7xdJMlN":"Ix3pFjeYjf8o","Y3cio6j1TAT8":"g5emXveHGwQq","h5etPB2c5QuE":"l0k2rehsUIX7","2LhUtxI4OGA0":"VSIQNwYGXj7p","7XQz6pOaPlnG":"QDw3SfvTimzu","8oLlUgnmeIep":"kyqbEzPsWhla","ad13K34514Od":"86Rl2Wz4cgyX","71OnwHgClwyc":"ioZQdWXxCJq2","7cTPiZmqLPh8":"Up4obIeMgbQY","jz30LXNymObp":"nKTtwOc5n9IE","Jk7kmwZJGxOT":"G7UwKElRifqB","sO2kSPwqhN3S":"wJW9fxCRgPqT","Jb4R2D6HmU3P":"xbuRDYSWknXL","A8az7eLviQ26":"ZR9WqMX3N1ml","L8a38t5hKtBG":"RhUuKgn9cPbD","KvjZQP8ETmCB":"HcVhZTulrReO","lDFu2tKkM1aG":"4dHpNP8QyQEv","2E1m1R83HtMo":"4miV1COg68Ll","z0YvpRbbRHCx":"7U2Y23gkpofk","SJjMBeMHR8bm":"ikrBDHBHjCja","BV5ckSA6ane9":"7vnH0Cao1fuh","9gulwEC6G1fR":"slpXVKDKhdma","EO6CtkudoqZk":"FL1hwn5EgJSN","QsTc2Duqqay7":"MhGBiDt8LA1Z","w7ahOGfFhIw5":"YOiCrFknkfu0","Awhr8pmSqJxq":"6v5n9KuItFpI","iKxdokZGhYfS":"OuHWLSdA4WCx","AuIbGf7cxTmM":"UNazYFnm0nRz","l3cGBbfAM0kw":"nVhsfa5prQSj","Zp7ORn0RcABt":"y5Xbpyb1V5PY","HZA2b2pUXmRR":"oqSJFutmyIVb","16prqTkMyCPr":"z0q4mGg90IBO","yKvgHQUaqxCI":"O9ikNmrliEoF","qIbpMv4uQ9Gt":"QmKKB5MaOwg1","96xwhuqAAG2r":"68X9PUPBuWqZ","4QpSn23abeUq":"RWw772yUgRMW","WX6Gs7sofbCu":"fu6p0BzdDO6K","lpcJoCxMQtr6":"dYKDgA6eNKCY","NIe4pUl4RD6c":"RSZSJb7fEcPR","hvgF9nRKPSvv":"TGl9ZKOnFbsU","UAk5p2g1UH3m":"nHeL88fmxm53","DOucKSMzan5M":"k1fLBrkklaxi","YtypfDI0N0I3":"1xAbqRMrF5tu","6Iiiy9kNmQGU":"nsE3MZQMCcDi","uCF6DrcFIB2B":"YPfWi4UZ5jcn","MZO5DqQRxePE":"8hWEy2iM2Lr6","Dz95cypItQYP":"aNkEZpnkjCxI","cvbS4ZOUhZFj":"rgWU2ijyoZgQ","4195M7TGjO16":"79OlxYGnwcO5","mtZM2jCXoMDy":"EMXBFOS5uIIk","nn2SjedDfTlZ":"19nIOi9ZTA1b","jSErhfOolh38":"mF6JaRekZWpe","0K4T5KdXq6IV":"o3xcCvVG1b5P","7nKYtDQtk0HC":"0bvC9wzFQnQv","WghxKxcKwemu":"0a0fvdXJ3VbF","Dubulkraljo2":"WC2fNhSdgMwE","1qOvlIed5dTP":"yoRrj2qiEKPJ","pV4gy5iwUmme":"nuAyulXVHgPQ","dh98Xyi8yiEe":"CHKmm3tF1nKT","X5KeeoCoHA44":"L5A9d7YjXAz6","UE2DJsLHnNXb":"ZkHib4Zs0DHZ","JmPGm1ftOPeZ":"FROxHaRt6fq8","I2KqC0GpmKNw":"dyxK8v3PGmvj","QuM0WaxnaOi2":"O08HqDu9z2Y6","0rQuChmWS5uH":"c5Jsu31S6skP","ey1Cz1Z9wHGQ":"WlDylnNn7Xtd","qywV65SBAo0M":"9iSNdZUXhNkW","RBcBDJrKQOSR":"ftaIRqq10Aa5","2x97fpH19DEY":"e4Su8jggUc3c","Ua7mvfKsxfZA":"o2DEimGoX7la","LmGQfgvwC7HH":"HRGEefvQzZU7","FviLk8lafie7":"8FkMfodouor1","PnidjkXKq9jg":"5pYpIyjZJQbm","35AekYfVE7Bp":"uGd2JrsH7nJh","dB1D4mmMkRqs":"AoEjEpdFDfZm","te9krNBsDdTU":"GBSu4yNl7oaH","Ry9HpVWsbpBr":"DJI1pTSl1DGv","LzNzWIVuRXUj":"9ng9NeJHDZ1J","EfemTuSQS0lO":"FXDTrpJ7T9bw","sZXThntJjdwb":"Y0UvXKuErYuB","g5vGT9eRSdbM":"0XMZm77Ok0A8","VMsLuJdEcfMF":"qkMYyAWkRWox","m2MU1tLhIT5d":"4y8HrkuArBcl","Sz3ViKEpvhXq":"4B8WGdaD5Cwc","JVUG416GsRKW":"2BO1BcPsyJE7","lj0HqgZgd5nS":"HnKkwuIw59Sg","RbNaIt3Ebb7P":"ELbkbEQ6eppU","xmDCjDRSQ0Ex":"A3Rx0Nuq04pG","AqwGemErkvma":"OEmDmOSoEW6i","W69sNbZwgrCQ":"og3GwOcATDpV","DIavnI1RVCEo":"8CTK9ON3Bnft","OqAAV5Kud6e3":"O6X0SkHmPBcK","AC3Lsf2RsJdv":"MRQGTk7YuRBL","qQDT8NIXK8Ci":"opUDj5cUN92M","XVRrbKGPZr9d":"kevmt9ABRwNv","QaUagPfs9qEj":"WonVOnaoGzEW","zUKh5lRM1uoJ":"AIKfX2x2P28S","qC6WtjjJX4u9":"N6bEAyS7HXnu","YRN69YQPcdOj":"ZQwGEvx9iyJ8","0IPocA3pdPKe":"toQ50JndxbgH","XmBy0fVwr9TO":"iu0TH1w0Iy2Z","73q7eOcCaWvt":"5UUsX7vrhSdS","t0cP7BmTUmTP":"77cfO4T4dRlS","TSAVwI2a3OcL":"mR7mDCYVe8Y3","0Hrf4DPGq4pp":"xNQQcaZrWs6e","eq8LHnka04N4":"NGiAS3CwUDsm","nhz6zDSMMZRe":"LQx0FZeDRQ0S","prHzZHTHuS1e":"bGhFAR6QxDhr","Y9totkdzSlTJ":"DvYiQzolT5Hr","gevhSSDDdMWv":"Rq1EA0v5lJm5","8EjOv3GbpUsE":"snWqhrZCwGPC","2jjs4ZOEUqaZ":"8XI4MzsuVQI4","qZ84LqvEuwdC":"YVMBrupty5rn","xFGORbZ04glU":"0F5VH47Ub5bT","CAMk7JHjtNlk":"Mdq472hE2cum","ToRzV1ynIPnv":"ikdunSN4WL6x","fQYryra60BE1":"qVgin5E4esV5","4BwEGNpjfRpv":"5x6g5xu6WEGE","eEReQ8SpVbTl":"MWZaJxeWxzah","tZiyb7VOlBdE":"9tImc3EDkvwA","hjnJjnFfWpbN":"RNNjvnEVKRQ8","ycKNaQN2hNv3":"ilFb7g3fec2O","mpm78QBCEIin":"adghgy7d7OTe","hNCg4bD9vvfx":"PmiEojFov6QE","DOPcz0J1A0ev":"KlRfe3OF1MDc","pFHnOXGyuxTU":"Jt4pNXrNdjMC","4dXB6qhkd8KZ":"W5bw2zqKVvZD","PDcW5QydTs2V":"IBbeWfqmqZIl","tw97p7922C35":"GhPa5pygBjes","Ah3jahNKJ9BS":"302h0JUKlths","f6QUwZ0OooJ7":"OJANrGOhcddJ","b0iW51e0F4DM":"zO6h49lhhWNl","2NqV0BFYvch9":"xtaw3s8mQxHO","Km6gqy6VEa5r":"CPKNhfIgI0FJ","3HGIL4NCaQWq":"Hc3b73sg50oR","GSAdK7Dzapw3":"IiOsfUpy21v8","d3Qjj2DhWSC5":"2bGOshibZ8EW","8ZLPFW7qP8JQ":"jGZ8X514GG0c","kuyqrZul6gcs":"YHxDj82aCtUE","EcAZ7KcQQpJn":"vs2W5URsbcUe","APpjgrxhB9z3":"cRV1qNT7GguT","6xFcK6hIl4IU":"ZWmKh3Q0lOoi","wHbPHwAu7TGX":"02lUvIHrbfyX","UWj4MbrTb7gc":"tpyPRN8Q5jTK","f7npuE3iZ3U8":"i1mM5ExyuSKQ","7D8u22ofrGfs":"YVBqwPjsrX14","KUrdZXMDojEn":"3H99Nwu8uVCl","LXEzSJf19Xwz":"q0PCsNrxwtTa","UD9szK6h42Z6":"SXZ6qgiVcYZ8","SKVQjdldlFv3":"w2dAIhiyfPY0","3d0vHYTA9aYR":"lREHhAZwlrDI","k0spjYSCvXAl":"tBPVS0OhX0f1","2SmfJ5x46Gy8":"nlABoZ89DTsQ","XyncWG08GamW":"1JE622EFEUiK","FGKFTRGcpvdX":"cFPmZveBwnLO","VFNuvA9Mozg1":"xlzYYw2N4f5P","8Y7rJnD9iqxM":"F8LaCU2pEEBd","e3pFOkR1oi5B":"qLKDBd6nJx2B","hFFOSo0RC2jc":"bzy6eCCqBefO","1gv5FrXxOkhY":"T7G1IajklfQO","3N9L8YgMLM92":"tTyAam7XHIvo","DSr54RTL39dt":"GbTkG69bmMij","YcAVjuBp81np":"YCK2TRfIZFAN","ddwhqug3OMHI":"dyghwGhkp5L1","quBNiPG5zUdm":"3WkBFSS3r37a","zjHFqqSG5XRK":"Y1dfSud3ENjg","0YilnMk0XxRx":"nCeZXIQFoFnu","yct3F6jEqiqL":"PeV1xaavSwCn","xKJhmceXDoi1":"5EF7G5u8gaR2","qmgcYEfObkGE":"9JDNsLzZPItK","zy80BGJlvFuU":"UdzM6lYC4iPi","wtjvSgRQ3voj":"qhxezDxKGx5m","z8ctlK8aP5hX":"TPywnn5GjfPe","t17Uj4maWGni":"v1JUIPbyepVD","EUvAvGxTVrWh":"zqgiASN79IG5","DQub4HSRTWQu":"NB7l6I6IrpjG","vsgFPZEOcRnx":"LJGmgB7Hs2AT","tArBOyQx2VdM":"hvu4W7p45pRB","ndYCTMTgjSNP":"unGk7CtCtQmv","GtKy05wZA9YP":"CU4nk0GEvN5Q","Qejv03Da0E3g":"d7wnjmODHENW","50lKIRuHYwqp":"PyJEX94DCeFz","HHaOlqgSwMML":"yX8iDVOC50KZ","dIxA4E7c0WwO":"SKMA8ZXTBaGC","q9rqWHuHWEkV":"i25UkWLo0STp","a26JysLgdFPg":"QF1vinCMR4Zh","PCjm1HRKaJJk":"oKw5kjC93KNo","PFBTaLfPtbDC":"CWK4u53rDOFq","bJXOORJRI2L8":"Nh5d5sj5rphM","eHj2EfI42cE2":"yIly1pPjm2Xw","tMn8yY1o9yd2":"63pV0Ua1DC6J","sA7cSwdiG2ES":"euVRsbKKenr3","bDBq03QyJVkF":"D70E6F0V4ucO","429RLsFBTZL1":"s6dlk3XHKECR","avfvbkpGN68S":"NfEM9zHJ6YAm","INUTxOL1ad8S":"S3NBiKbpBMub","DjUkrNLEhEww":"APLMbM1TqRYF","dXXh9SNIrJQI":"pOtZnoXF9nwL","vYE0e0RdLQne":"vbEsiVGW4hKh","bzQhok601Khc":"J1GBVOogevBI","WCZgTz1r8xNQ":"9cmGfyNZmVU2","aC8TKNw61yrT":"NNOUzT4F4Bed","YAH0biLvzMW5":"1zCQpdcfnhiI","0KEhzR6CrgAb":"49jOdcWDbmQX","iLjgK4fm5DIR":"YFYDpH0fsqGm","SPofagYE9JgQ":"7JJiCiaJvWwT","apURYqaxTc2r":"MbF5sk7kIkkU","duStNYTb8hrb":"tSCrPf4MrX08","J3zsjoPvByLo":"Tb0jmHEG7Nmg","A9EwqtrIX5ks":"JPLkJGBkJYF2","ttuBsf6tn6Fx":"3M3Ld5IN1yoX","zU786Jd1Ortz":"spY3uh3rXPmC","7cnb28QBpd7c":"hTvrEBVMLw4I","PGo6Utyg0xdQ":"2sCiaDPg7yaP","igFU57H0VScD":"j0nTrWEfU2jz","eZQelFuBl9eO":"o2O6VwGWhL3t","GL1VT0CuPzO7":"UaeofNAacZIb","igIUobKkvdJq":"F7uF8llwwwMz","dfL2UNF2Cn2A":"0da8WMsiCkid","8Zbn16ey4XSh":"F6XYYxlPePfs","M0D8JSnQIvXI":"YYltp6FoDMdU","9N9OPUXXqgNY":"rzF4dhU8qNra","DQtY0kiRxCFR":"uGvRYCnGWJua","SKC4cO26SjIt":"gnl63ClUsVFH","Duv6CaXKUtLi":"ugawUU0kX5x1","wLrrRSrEPsqc":"yR0Up1xPSs7P","afcS29SVjrFJ":"52YV7Uec4Fxc","Xazqx0F5UR9V":"7lYqUcb8bfEW","atCQwQZ6l2wd":"ZSN3qXNMJbHc","chgl4dKzfbGl":"fnIHeR4ravzl","RDvl5GU1CEgj":"VXz7uVldvoHb","pVn0n8ZahfUy":"CouEzrVBY2oH","m42arGN6B0ta":"TypkJyspfG5Q","RoyYB4FukLOb":"GvlfLnvtc6iz","R6G6uMKbShxC":"WCcMVqdXP1Vh","KkyyemGJeXkN":"QBMO3XA2yEv5","jWFOYDUL9cyx":"LCBdYRMkNdAP","MFwiSFRVTCIL":"IRtYaeQoiFEQ","m7ge4HOE6WOf":"JekaV6TkIDS0","gMBb9kw8o72b":"CC67qJbmcxiM","zfh3o9n1ssgg":"BJUXJxO6ORiH","sNjYCwObHLKM":"uZwM0RSzUrfb","TP55JkNoMnC6":"cpg6vo3MJH8h","mX5S68yPsKrd":"6clcitYIjZ4p","qmKQpz9xOhHM":"mYZYzBKaEovi","u4dpLgrfCEDX":"3EIV37jdLspu","PeTGRw76sT9q":"7U8BJyBoQ4sJ","7Gi7Zm6TNWvS":"CZ9top9ZljrG","kHBtPosja0UE":"AkA4PeHnksqN","MxdXVE38b7th":"T5DQ4elsdqJv","GG4ivYN3xQcy":"ovR9ECp8cx0G","dV6CxoumRx5H":"f8qqUeCVdT04","ssOcgaD3iX0q":"NhoOSb7ul6DH","pcRWxxWRLtJK":"mAeTBROrPvSE","tLutQyOwy5QZ":"jvrm62e5jFK4","RmuBX6hUzc5G":"FF3Xu5mexncf","yJjnjEYzxvre":"JluI9wiP5AyI","AqbpKe8ePLBb":"jLqkqEchBvkt","d22jCNQzLN36":"xGaur8hgZaFg","3MHYFxUivT3k":"tBBlTK5YqK1n","8OWdg0f1VhPY":"fewIjOh66Ymv","h8msA19dTUr1":"rea4SMaAdH2W","GQFzOxNmTWdL":"YMu4qaNpCajm","EQDX29zSW2L5":"hU7TjbCsy7ii","B4CzVFP8mnHm":"53lBN3TM7g57","FC8idxowMxql":"P0GhqniIbSG2","gMpW8tCJOyds":"aZuVCD6yUJlV","jAFXSCZihJZJ":"XPiZ5vssNykg","UA6e6sSCOazS":"mnSWmGVBvDUa","rBt2Y6PXipIw":"zXsUzl0sz2vi","kZqBdZFKBt9Z":"6Tod9vWuDo5l","lwBAivMVER9i":"6gys4HNsnJVK","x9UuOWrTh6Cc":"uX659NDPQxNs","L2ukPljaYQRx":"bmBgZcytJNTr","073yv9Ng2d5y":"sC3EJ5pTSewC","ICqDOUs3IXhl":"AOkYDpb30Jo8","oLixMyHBI4hX":"FvaPniuolUc7","y1GSGW2KkI2D":"Be7ZKDrPnN4x","kIH55jgtnnkn":"W6IXhP7r68Bq","QrrOAoVbHV4K":"cp3sVls5fCqC","WRjXV9FCZ6h2":"QnusNpjwUwF9","1XDWCRI7X5be":"PE7UPLVQZbcH","OMWr8zRaVjIc":"SjmIwhr46Vu2","LDWG3biFb2o9":"09NxoYj4K35T","643JV7Z0WLo3":"fltcdqogrL10","v9e2u31KBthn":"nbmEubwFJc2f","YSUAvl8c58q5":"oIDGPoirSJfY","vOZ0Lxvdvc1t":"MBKvmaQHxnOL","fwnvvkVf2626":"CFH4bnaI5ZNM","8L5je5aodHop":"9z7lppvQNGX5","lNi53uSmp2zg":"ilCmSs8fRvm5","IowvO7WNltWb":"ElzeDVZdJkb3","RHjPTRkNPnFQ":"Y2s9T9khyl6O","W4MbJzTat1jB":"WRThNcj7GEyp","mrIypWWBNAVU":"mTaALy8iBmLC","Cf36DoQTHUGn":"SAq1PjcXrLLz","jhIIB0Qu1NYf":"kQe7BBQE9GsH","luVMpXiHgb1X":"Sk59RD02R1gg","kL6usFrAhlVm":"uXwgslU2uFOz","S7hZIHYT2IJ5":"9Kc0sl8E25L0","av2jWZDaZklV":"zoigufAtoVO0","9rSBPQTxA74c":"3XIccy8d1yOc","LlKr5Jvggcrc":"aXrXR6ssaxmb","YiU9Bhm2is2L":"T0McQgGsk5t3","99yzEEgZh02w":"2T31IXrMzKSH","xsSwWU9M4ilP":"tW0dLG0ps28r","O5gbvp7IcH5i":"aYrY3HXssX9x","lavHowWi5P9R":"Ts4WTz0dt5rn","LZYXWGPSshch":"NWbJy7gFM91O","BucWfW2pXhu7":"cS8S5RgKS5go","v2hvQdcwkpi6":"LJlrppv2ckuD","Vqp5d0ECAnl1":"JhIltXkjTDGG","4rTFNHVBvqwf":"tj4NKRjCLZG1","by0ruAPc0Fa5":"4Cn1kUJVFcED","MFsxaI6kBIiA":"VAXuwSZwJeCN","ur7IUXooKbnM":"hHcr2mjRUHk9","8MvAHFf8MByI":"536Wh0QrF0IK","pZsXYQD0bdBN":"KcNcZAabxk7v","6LmJTcog9pra":"bmeCD4yv1tZ0","Eb9gFK5gLW3r":"MvBoOc8yeT7B","nfYOGSCnib4Z":"Eu3RqkRa5Fmt","Kmll2KtIyabJ":"OigQb5mU5ZCM","RFRTxoxhvTPm":"pm2q8sPSkWD0","3lKkltkktx5B":"5uwl25x2n6Sq","XbBe8FFbwutQ":"Z4touvi61VKc","v458MkCHnRch":"m89xu4Klorp3","VNxG6gpgr0xS":"LYONpuDkafra","2kStZeIOPg94":"oqeZenplQcso","V57dVnqKfCZm":"FdOuCg37lODi","eQZnc6QTXZcO":"p8jKfBKd8fHW","19PvnluOOMya":"lZZiDto6cTj6","AJtgshCb9BdL":"MoM15B8mOKkV","FmAhMoPoBsqr":"veyrF1v66Pr9","7BUcttStBFf1":"rKAb6qspMO5l","WXpOj1jSnBSO":"67TE8KSPPsVU","ngIS1H5a2ruh":"c3EHMzkedDON","fmj5Ovnxhytl":"gA2Jao0JxkFP","WDAsXeqw1tq5":"z77W0O709doz","nkqngQRAt5QM":"1k1iHfoORy9v","0p5bnsWcrvHQ":"dtoeiEklQahN","VW2Dr5P4SeJK":"2ukR67xpXtfl","VGePnF3uzgXI":"kQoh2Fd2TT3v","xETBlRTinPu2":"ADVN7boskmkH","Ua2JDlAxTELf":"4EpUPvIVWQlq","KVIOdSKmwJHf":"jqdtZg6TPvkM","9bI8K77h99FJ":"GaClUaDhOdFl","B7FVvSgJUQtQ":"5mU9k2vhhVJc","OmPhyIAqhyRS":"yFimUmW4WICw","Ykx2T6ELMxl3":"gMYZmnWvjbS0","l79eImlnlr3m":"ANIk8ovU56sg","j1hN3rRObE0k":"GQp6U8Gpz0ss","l2QHLn7D4SVc":"Pw3YPiuIJsPW","PiT9c7YXkcfM":"hLAcKspLuBPn","eHK5TJvKw2ZA":"Ft28a3ZjJftb","HaoPefSkDn9b":"onAA7C3KObfJ","ZMG8qkHWm5Yi":"9ARU0xhyQRJY","DlPXzxztcvbF":"rEudrhQIYKZW","Fmi8TfJDAthp":"SpNsC6lR5Gxk","mDHm0dXGVaFh":"7w8nN9Q4e2Fd","djrwUpZFZCCZ":"kMr7vBD8Qd8u","vZA4dR37vNYv":"xDb3APj2Rjqh","AyJMrfPWoqN0":"dJuv87JjCIv0","vLf7GnIjV74l":"B9BlWfAQTnCD","09MZMBAVZT3z":"3hooawWfMVEt","Oz4PFQ3KOIfA":"L0z9CJYGMoxa","97BKmiQxRejI":"hviIEMtMRNOF","p8XU4c6wuN8b":"2c4cOacmzBCw","JTHJVXWVCjTt":"FqAsQbJTraJZ","U8eBaAIlLwVw":"VSD85QC9fTS9","UFkZVXhrIC4C":"8wA1732TzYnq","pVTSWPIHBNaa":"f1SA7ifbDyRT","fQdxwwHgUQDK":"Chthwyk6vm2P","8k0eVS6mgARs":"jCySH6xmy4p1","Cmw6owzF5u9k":"qZWJdl1DKyQS","IciEdjHKN8RE":"ghvbqhL0FLG3","yf1HfFBbQ0uB":"vXn5TrhpmYeK","F4vsHO9ejTDz":"hwAHs4qQTvSQ","nQiZQrxei9qK":"VhOe0MBCxlnn","mL72u3SIzSNw":"qbYKIXxSQzk6","8MoGsRFbbKi5":"vpeCHZ1XIARf","G64CqyvmRCJF":"VB7Pfmilgswt","awNuSqcYBSI7":"sYWlatQBpKsr","fxoqWkrK5NQ8":"zUTWxpKwBrAe","iQCp8P191kus":"bYJhqhIqyQVu","V0vB2YsLBpCe":"WBEKkGDFLJvl","XE4kSE1feQyE":"t6LnHefDqLBx","qypsfSWjKW7R":"kJ6IYfHhomFB","VJxL1cOi2sTI":"ejTFvCTfGu5F","Z8sbhBRH60yJ":"9d8p59lVuQDM","nYoAcQdRMxdF":"pVur9JpE5gtQ","EXH4SvMZmRJ2":"mATe3O1jH6XG","r0ZKz9Pe6Bfs":"Oada887S7Huk","ovxJ6CtdaHoM":"dh6MMeknWqNZ","v8pIhU5wfYyg":"tvXaMhcmcNeb","cgZC12DAe42w":"VKouilIi4omr","Rj0HP4FgHj9D":"Q1N441VearZf","HFzcT0BKKxN6":"TDTc5LBVoqGb","ZjQehPc17jow":"IQY6Ubn6tMKx","YBb9mdE9FpTu":"eKAmJQifbVYy","hgNGoThBEloX":"h2rIm6x1VJDX","6fHdoFjy19Vq":"NRrqzmAGP1TZ","lbAYk2eFjbMm":"Nyv0n6wTsLen","hT8vC2ZLnlDB":"QKV5el34D73I","nm1Ha8OiNImc":"k0dUOHrtcPGk","pPlWxPadAc67":"zS09aEaiFnOB","85ZWSILXPagB":"O1NyyqaNHBDf","CqzPiKuuE3In":"M93ES7Dz8m4T","dmh5A5Y11mbb":"3tmt4n4PHKJq","aGvW8RFSNtup":"5iqr0S7QMUxD","VIuvnhKVHg0e":"S2K7Z6pTb9TQ","Xqf2SU8TIPCX":"hT1pp8fWVqrW","fQCEZf079o3C":"jxw74Te7AjU3","y9iYtW9Me8XC":"Z4uTZvx70wlL","9lAUh1WORkFd":"U9AzAANT7Pwl","6dcucHvvb697":"Qxh43HxQ2QgC","DHKa3vUZHk94":"yixAor9Jvplo","0cNy4hSJVsQs":"CXeavYvPaZRQ","dYaU5TJoUt7I":"VPNCZN5ygo1b","SWzACUiv9mHe":"VS4kF93dgDCo","9LmjnJk5wi6P":"Ai1EGi2KGTki","jEHw6gx2Edr1":"i4dshXsxfU7v","NdQg6VO8ngmn":"oDET1KxTspHj","yxShD5LXyyjb":"xQFPNHIEWkVT","lAkWrpF3I4Rb":"KcdooU3z7Ki4","NtG1DSPHqbq0":"0rmLpIuRGvZI","lFBXqTmHu6Kd":"I6N77A5yFWi8","gxWWGLkR0FXt":"Zomq1FZikHy2","eBiNKpsRCbRU":"dG5ilPuw8ebG","9EdF7q0kMKWK":"bogHli9pkWY1","6wOjW1nkaHJT":"Ty3IhxhAjOZI","fEDyLihqFC0E":"LyPmqyBHnSuU","OuEhybMnkpCe":"N7tLIqOm6AHb","ipO7sWNBJmVB":"JzztOR3L5doz","mqLWi8fXopE6":"5Fe4TdrXOqBl","0laeYHmMehiP":"p6O6iZBUSawI","ZcX7FPbt3G1d":"xlNpCZzOFrhX","yH3cyBH75gyP":"FixxKPznkict","hkQzzCwVsEk8":"jsqQ08nnPqaz","98XNoSmETVOJ":"RDjTvhRioDna","hj7GcjhewpXD":"qqGOGc7L2IIr","udT8Tpb7Sohf":"4MrHEmhuxXAd","rT28TvjnksEY":"FDqFuBvO9SaJ","gEYKz9N2C01J":"ZPGNB5OjAjyZ","FnqwRy9EKKR4":"ggxvUeLsSp2v","BkmJeaFrSdNJ":"Dx0ZL4pgrexs","0W5A9EdoiZpQ":"JQ0Fy4W6FYmI","hp0onYlAkPXm":"pgIchY0Ctd9o","eg9mTtNww9pW":"UG5nyvEYfsmL","7OpfRNwo0M79":"4DZuG3o7PEgL","5OYKWniU1JAK":"7KR7Q1y0IqPt","tA1aZqIYo5UW":"xNU2pYyAk3Mf","kXItP3MiEbeW":"HUurg9SZngxZ","xYDlRCbiplgg":"hC9GL8yJVBas","U5NTs9xJc2Yv":"j8nu9Sx5g9DB","WWHkON1iwJ1c":"HBTQGsDX92kA","kGpEnz6zTXql":"5LxmjTzBBDro","y9PAJs1AFDNx":"4UO2cwFU6DXH","SjyqqQpMEdjw":"7Ge6Q0i0S2Au","BV2qbOEsHv1w":"oPIAmq4mhA9f","CYdtpfLY0VWC":"gKYN8qs7gx4l","6kJ682IstkHm":"bmHAvPuHx6Bn","g9t2Y1vZslfg":"aoqmOfTGRkOT","e7rHuYeZfYZr":"JJMGryCf6KwS","4JCUYHYNJLWq":"es1MAuME0yWa","RhO1F1g1wbfC":"YoJC7Xt8bzK3","VOz2KPCfSpKr":"IN23dkykaBnd","GqqtDOKkIxi2":"kDJJb6HjfXxn","5HsKByneoGAs":"LwmACYU4CzaW","aeDWmhKKJB0I":"4RB0HdqIHROQ","PDtgNuEQFeQ8":"vVefYujGqHro","q4LvWQxamQOe":"zjlwYIfZKEXU","CA3w7fTBded0":"j8r5kkdeapg6","Sv0cBRDAMal5":"UXuC9zN0baK7","4o6R8oBm4M6x":"NuYKXFV1Bksi","rEGmI9Asrp3F":"3GeyyOUWXu2E","i7AUlAfkhGbA":"MPR3a4ewSzrd","U6U7ADuuS3MW":"iDnS5IEgx98C","OPbg8fzaELIt":"hRSsttPAK764","tEm3CH3QH6nU":"hcn9tNOP92Am","ooqYbFdxpWaF":"9bIL8jLqNS0r","k38NIDIuH5sF":"DSemK1wAn8nY","lgGXZWebhx0V":"wn0GlNbbT7rF","ZJ0c5UOzhYDd":"CrayjfqkAXgd","fFdl9z2Ta2bP":"VSQWYb5GVFXz","3KBwFnJWoHgk":"ZHbHz91pci7k","NBzC6JRLjQOR":"ZPRqxRYiihZL","bpn4oScjyTSv":"mXLlyhJsoPQ4","pMIjN4sdUH0U":"V2nrqIjvhMEd","DWoC5clRvMCv":"EIxYaJqKTdBn","59fzKJ3sGFAU":"MxlSpfxrjYwY","a96JQWHvKJZ3":"2rDqy2ZCdBxo","FUo1TaVCgn77":"fb80Ee3rIY9U","6xpxxzVvDFHN":"XvHn8JsA5U46","gYbS54ZmPPt0":"RPpqPVDPxOcH","hj2vKVq3S56Y":"CTHS1uhuqtKH","icQhrogsOtNn":"LCV4EQj8PTLT","doL5yciMp49j":"BIj72zWfngFe","KwCZJ7mm0926":"TV4MFxK8tnvH","vHpm60Lp0MBz":"aopbBt0S0MlH","plfiLFqOBxnj":"iWNC2hJ0KpOc","hII2diEQ2qPH":"rxXUbmU28ze1","aJHT8knVHR5A":"H1Urh22ei4WW","xQ0br75UPZiG":"6MPDoWnDzFx9","ajspB3w6iusc":"cH3wJoGFUDbQ","9LMwE6uYH4tD":"j33KWfpgMDvt","tC2pQFGFAo3S":"hLYDsS2r7oLY","MFBbv2yLZqmz":"Pljw05sdoLZu","U0TIv8XSPjpZ":"3xxwsgtmGuMJ","7tw8zqcX3qP7":"vc1qKGQSIzmZ","m1vUL4PBHFKh":"JS4JmywuqGab","2upXxQPMgWbN":"4rAFoG4IRPLK","5pXczmW8ATeX":"cW0ZmzVagUI7","OOjzs6fqor2g":"cqI07hlYfnQk","eETMAY10TyJ4":"uq2Yr9boDOgY","f90OeUolEyLM":"5IP47jlasWAs","FRKDzvCLOCuV":"HOolRE862Nz8","12A6vS4A6FiA":"owAe9PseR8QQ","Ov0jujQh6W8U":"CQyrxWksQ1cA","Jv8VRTAwVJeg":"BLTqnJqZyX12","VOCbsQk2p0J0":"i7v8UEDPyj5Z","Qr9tNvRiXqe5":"pHFO3MD2FAWR","20XEE3gh1H1i":"9LEcufoXWVdS","eGhVFc6ua1tu":"E9XGZNLYhuDG","hnfDraW8T3CF":"P2TUgXMDQw33","RYn1srHVpdaH":"FzlflXpHfbDY","MhzOPikDKfTS":"J2eLbo0JNk0f","ZRzbKwmiYdPB":"afyEWr4lieUj","pd2LtRvr1a0u":"TuC236Z6zBi5","MFVEBKpOzJZb":"Zhnw1WU5z3Jy","1dT2DssRFaI3":"l3wrQhoqzE7K","9lWzFF5MutBf":"CItAduluPBT3","9Ejv3L387UiN":"Y1pkCGE6AenF","OPQLWuN9TBdQ":"TERZDogqYC2C","FKP1caZez2f7":"jqVitmiHVoub","hoJ4zkdOsEwJ":"tnhoIOGYU8m3","NEaiovur51kL":"0KaygJecxeTa","bxDtZFYwFswV":"PkDzitVY9n5X","KMuizycRcVAN":"KDLq9oWtVVKD","sFZhRGGyQutB":"RVdhjoIpoFcA","7QVAt45rkWBo":"xwT7dTpb09EV","24AHc5fM6Gg9":"YTCKNDra2GPB","gAk8lrHLv75o":"64fpYFVLqAR3","sXLcQqWYKhAT":"631yJ3JnqK9u","caRJydok5tnP":"tRkJz1xxzLwC","joPJNniEgGbP":"udmevZdRw6N3","B1yymTtaoNdh":"ioSTsrRwPHie","koF98cHl6FJo":"b9AojOUqjmrZ","vYM7E3WkWTBL":"cwi9icn2apUq","hQKhP04Hce6g":"EJ3MA3VtazoW","rNvWJG4pBBMB":"gWd8TZoicC5L","n2RYDHjcnsch":"Ukk01WOGrc0U","al9W13VsnGnG":"04IMGu911GL3","CyGL6n7va3hq":"2hSig5Vq9P51","j5SR5nTSasFp":"LaT921KVStss","xT75k7lBNclc":"VfLEkqDJpWmL","lObfpeua6hmk":"cKTSmu1tLmXb","npzESNXLqdQ3":"LmdCp1MLOn6G","x5yYEvSDQxzb":"i1meAVfL7PSo","C2FbG5ZKDr3x":"rhl6zeI9tmFT","zvdsSy2J7R75":"YnfvIbPaY7i1","IZcj1QWuR95C":"cOxQIVEbpxej","A88jaouD5HrW":"k43zEtBTd54y","2Wh8AT03VjTj":"oKmvztrDknhH","6jMwioxUwEjp":"6jwWym2o0Xko","Ec6N5z8JUuDS":"t4uqcsanbi9a","HZZ5OWDQE3yd":"mMsIppd52WFP","dKgB52fKxO1b":"fZfVd7O1zbeu","gJQuqhKNKG8Y":"ah31VNsRX4Zj","8DDPInAbZJK2":"D43fLZnftBcN","hKHQxXiRKocR":"lCbao8xJvblN","qVvk6g4ejAO2":"nW5DXr3LGALG","3RyUqyM5Wcpu":"hZAiXNnazKMQ","wsNgsttbZqnN":"G3iWharDsfgu","5J1JU2FSNF5x":"E91zpm0fM8g1","RKUnr2AB4bur":"B0rP3pXkpbDk","tZ8ur9BzSjhZ":"mRFtB51Kbtqi","CmanLK7T5F7U":"8dKTmtqChHIG","SvkEew8oIzC6":"FPiEBlalE2aY","CkjzVIvi7o29":"Il5h0nAp1sSi","NE2AbfaRpE61":"K0sBEJ6w9ehV","SozJoSSIXawA":"n5py6uW60iXq","gKg134uTvQi1":"k407Pz7HO6lc","gld3SmIGUqIH":"FidO0FfG0YxJ","CZN6hh6sufs2":"Gy5tSPVvYkRL","TMUIPOl9C26H":"RDjisHlAoxpf","yii4DPSNtDmN":"NAmSjdTTlUJL","QySbMyWbv5Fs":"7EADmDi97HWp","4Hwq3isNe1Zz":"tIXIq6kIV0GV","7gJcDJXyXBKY":"a0m5pTfWkH8P","13xu5gw4Pdd7":"y8ODiuMB0ivT","jLyIZWZWZYOP":"HdyALepZn9mE","FJnJ1dyqXsKR":"U19pCx4jFci7","ycKjosSYjyIu":"Nb021DrZoJex","ocbq9jI4pbKz":"WxHk55i6FMOk","h8TpCl594SvJ":"fsuvnIo40cbN","ke6Tlhaji73c":"4uFd6is5KJa5","6372LfJWw6YY":"YRAOC9XnU1S8","3PMC0z3ytfT0":"qFQe29nBLCHp","AfauuQvUQAWV":"Sou5URzU1BMX","laJRk2a98kc9":"sQkrRH9P2omP","qt67KDPUCCOW":"UJFloO6DUTLm","3V6R5DRIcisy":"9m2jk3UL73Qd","mdSbWs2RMU9f":"lT0MtYGZJXPT","XTktN8VQNLBf":"GJxHKFyBLBOF","bjYi8MmdhuUk":"jyOetXLQijOy","KSBwujYPi4iz":"ZnFWPjPHrs9P","DnP4aI0Jgg0R":"W6abVbtpmLFM","GB2rrOrEPhWX":"glLDbRFlkcR8","KlLkrcITDwUv":"T4rtc0ZgVQDq","wS4a1QX5YpzM":"fWrrqQv0gtqo","NrOH054mNmSV":"fnX0W0BCv4tg","vbSgRRPMGMPC":"RRlOifOUENFa","HLi4MCYwN13Z":"qePCedpVkKXp","89gd8UtWUSwp":"PyoHhSSZv9Lb","TCVFYI4D5eoH":"CNkztEbvyDlw","Gi6FToPyIxfy":"uAgrZFFr9klz","U8IqcljNvimy":"ISiY48Yk1xDZ","qzlvSkM4MRSm":"ndyeLLzTAyjA","4JVsRAzfCc3g":"X4PQsS16vMvv","oTnwCS00cvRn":"qkSrjiSS6eVl","BqUSkSsgRpW3":"2VUH9s4Ym998","shwQFnREOKEa":"2uV3x8N3RHxA","iSqV5dkQX5P0":"cmFqAlWl345J","3lE8bLC1DZx0":"Y7Dd63i6iR7k","2HPyqnUlhk6i":"wbbTSHNK05xa","bkk7ttnaa8oZ":"Wtz3VITkkel8","BAY9VIPlAvoS":"D8mRWzyPNQxu","ODe91T7bbx2B":"yDpAjcTF8VFq","d8yLNAiuNHe4":"XmJ8EnlFzHum","Q7XUHpmXXWV5":"eLZae8xgVWmQ","Oaj3JBgFapFM":"TB7YCQGNpoxA","tP1a6v7vo34E":"V4lLtObXA7Pe","70wVVfPPVAMR":"Xm5Yter9MBGu","tLBKl6lY60JN":"5PIfasMdoaIN","bROrfnZhxhKr":"GXut8c7S9HDn","J883PAP7zOiM":"7NHR2U1tMsvg","vcP2x5Bewi3r":"4rL5TGJ1KLV6","WPxF6TOulPyO":"IH7nJ6OeUYD7","UB0YA7UPu9KL":"SfRu6psu1pAG","M1EOI6qDEkfQ":"LqUhJn6WuOMb","fTRr6RWPneai":"G0RlgcKO8IsS","C2bzkd6yodK7":"xzcFuaftTn7n","98sOmwLoyZTl":"EGuzLed4kuWk","kKZObGdGqR4J":"ZMvckbzEBSJW","yy32Hl7F4lmD":"0pRw08AT2Q5N","L01ruNlfRmjz":"hNsjbAkBkNfJ","81nwo5yIKCf4":"Iw8PuxGLUOjc","GPu7vuFD1oSA":"gPuvqjMhjMsB","tvsQcnpYMcxQ":"y0qWj77HXaCu","osvdJ7buEXD7":"N6J5BasPwIBw","cPqjJunI0M08":"5zc5i1zOZfPn","aywBGq4NFpgG":"dTtRqDmTWA9n","MdNIydNHlLll":"h8s3zmCwX8bN","6sE1YfHpyusX":"yomQHgAXCK1g","YwmQCsB8gTrv":"AYiZW0Gpl55k","4giCjS5i0QXl":"bSo0glC7Wv3Z","uOGhVIanOVSB":"tuePia0oGErN","0viyI01WNEjp":"ucXL3Ly64Wys","4vt1OKBEBJMv":"V8oqJDAWtQk8","SZ1I1v6iCIZj":"uXZdf985dKBq","QRnVdrG4AB3N":"HwP365RDiJo4","dePImJfexuts":"ly8NJFBHtw9Z","IopWYhYEeJWi":"wfK6z9WSnfIz","MFJcxNlWcjnl":"puwb9QdjnAuH","0EtNJa2NodZl":"EeHDMpmLhnM3","kzqt0XNEh9aq":"tn5SIPDLvmwh","hbyG9nDKm4tj":"ic5NQhLELVwA","0ky9mFOLdDXB":"oUXVmW8zhxPW","vZ7zY9yUyqh0":"uBC65E7FjnjT","v26xr1FJUWXA":"EPThOHl1gt26","rV12nqhGOtF9":"9Cv2SJrAgYGr","uxBHFkZtILRj":"pm29LFhYrFII","pSZqlJPdt9Kk":"h1FiJ1ihyC4F","shspKagAdBN6":"pg2ui7d33yUX","bVtBF854gu8X":"eMFAOdSEwBTw","AatwpT1b9g2R":"MUtp1QmKtvkv","RnVQBTmaErQH":"4EOmU2DiZatt","oSCPPsgTmUn1":"kpSeJ14BSt0F","qGrVHVXX1Ypb":"kgJZg2KYsDIe","G8BBW836Zfj4":"cUunIvbNRvV5","jlRR8d1qgNSJ":"FiLXmFcnqLtv","6A1QXVcmyMER":"swV6Z6ra60c2","p5rLF6Fo9Qsc":"rafwvUQ4ve3G","0IIOmZ18fHyG":"bdfXlFQ5pmfG","nKQMwcUigOGk":"dElt34EzVXIb","sQI3aG9RZXea":"dGVQf9qRIcxI","L2u35y1dJJcq":"0KgaxWQEIlEq","LeLD9RSscbzO":"hpbPgplXAvPr","0XK2LomUjpVY":"OK7kp3S0Ek2V","o2JuSDffR6gD":"oCppp17VKaHs","waWMaUl7PAB4":"C6WNJCxuOLeJ","BSanc23hlpRz":"JirC4sJFNJV8","kh52hSUIGvHN":"DSD5c5afsAOY","MQbmC2vBdtfa":"Qmp69JwwltUI","oK6L6p0qHUev":"ogvq5RoHMYbr","qhGi37Y22xTS":"vw3htkxp4uJm","K3WVX9COXsv9":"Tdk4qPuOd4Vh","VNn31FLvav2q":"Hr9fv0PYW3vY","S8T8OXifJJaq":"mLWusmZ9w3Nq","nOzQzgdnf8HZ":"bC1WgXARf188","bOLjwTh4wRz7":"dpWTkeiPS7l1","pmhmCSKPpdXu":"A2y5ePqEc5ZD","hkqNbIBaxpKb":"lbISCAdwn8Mp","kpSi0Xm9oonP":"EFtChhYkjZZX","MolAIOz3NAZA":"AH3yzNuNYOJz","ezKD8YN2aykn":"ellUXhOv1vob","BIElfugfEBcZ":"urTyXKu2IGP4","YDpN13JxSZK0":"A4kE2W9o0ODP","t8FWia2AxThj":"1E6KxT8ZbcsO","IedNAJkde68I":"ocFoi1Dzv6Na","TvYuevxZy6xn":"p1QstDaYbJQP","Kc2pjuJqYA93":"ijAm4UZxXpDL","9hF7Csa4DpiK":"u2me3IZ5o9Ul","OnU9JfqJSmFB":"ITjeoJJVShtf","FjWOxg3rs2XJ":"rv6dOuOjwhCc","Zbp94fKC93w4":"XfbZSnIcAm3n","EEhyidw6by37":"6ckH1VIKU3Hs","3aYn24YcAzjP":"8NX81pRKezOP","g6NnrPr96EJu":"wQwAhd9Lhc26","R6T3mf6s9ugY":"whM64RtEqsjD","lRHCfsaBCoYF":"2ysawgCpo93C","9LsYFa7Z2Bco":"vts9Lklg90O2","bLTmt0UH4LXD":"xKLkwDCqLk0K","NMEAxshcSw91":"euxANx91guyI","sXJGTDvIcFJZ":"hbBDIwzOmVoJ","3Hf3ZpbQzBoL":"7YRMUIBPZyMb","EsvETeHJOs9c":"Ad2Snve5dQ5N","iW6VLSqXmny4":"dDN0qNeDCz8G","CvqsygYhrRO0":"m3ghYYc2NkyG","VDVNk3LqOyN1":"9fmp7n0I1C2e","XyEyDckpjNyB":"QgBeq5ZVGEy3","Ql0vqGae1kYX":"JZFcfgEHO2ik","lWGnh5uATkfT":"G7JVyCjRBAWS","1oKhSlXhiJtX":"bSMusXyWgZks","iEuleSPtuwBJ":"krD71clsqc81","cX7hxZzudYLs":"4f2CMKcyd8vW","pmjjj2AEEO1M":"8aAe06TRGxvr","TDomaujwt6m1":"RFzmovS3s8us","b3iGCBcS2FVc":"tN5Wlo9SP3dU","JAfRqhLYXRMr":"KPAeO3CNCby2","YN7S4uLMnCyQ":"Bx1uarMjsdwF","QPyjcwUIxyW6":"TzlnalrDlMUT","po46dRtTvFG8":"YqQmfKAFnP1r","xICFSUW6TKtJ":"KWfn1IgiixL2","JAa6O1w1XxoL":"yXZXdiiyYdRA","p3Hg4RNX6qrW":"PdIpGEBMa510","n6k2kpsR4BTj":"aQO9iCGZCtoo","74aiU31jNh0i":"Qi9k8y27d3k4","tcU5tfrkFC7Y":"RBQevTeURrMK","ltMto9YaPqy6":"nlzOdCjTfDTL","0kyzre7t5yOd":"fTQrU832ONkk","ydkFBpNqQoXw":"4QKZFNCU8kTj","qtuXJHEknzHW":"1Jqrml76JDau","TcoXgkm7p6Ju":"WvVavN0s5tjF","c5WWLMrAZUUv":"31Txca9vh4EL","KW5FPvjvWDPR":"IJ9TRL2LPL32","FtIRnQ5OD01i":"zDoEiXOzF33z","AivXWBCyqJxS":"56jci0qoCEdf","xhxtWvktlBy6":"GGPEO1pXJtvh","Q47rIxJ6Ws5m":"4hd30lCBUjQR","I1ttFCXyOsIO":"78flGIA8xxjV","drHmy1GLQ9ex":"nysGml604171","YeqQxxclON0r":"NOwvaHdiTabO","fjsdwTeTcxV6":"nG6vzXHTORnu","mINhpGCDiY4v":"dn9svCrkvF3V","p8N9K5Vw8tV6":"Dyax79qQO2jY","Bil9E6MEVUJY":"xF7Q3DePywM8","ohFDhnmiOfqV":"OOzUVo60kVGG","vFwAXt3fRWKp":"OZyUTwiocTmI","NQ2sExLivjaE":"7z8LMgGCRev0","CEb6St2cTXPD":"tavLkXcX3Qi9","tJWgl3RlVssv":"i3wMxJ1LCkOy","G1dRdfwqB4uQ":"vX8KM0vRFRp3","vQvgRJtaMIsI":"RpuXhxlaKvG8","ohG4m8tybOnH":"8893nEv7Gweu","aWV3NfT9sjYo":"e3WUakjKlQ3h","ri10Al5wJGiJ":"04zHCUtSyx5M","eciZIjq8Xzv2":"vbX8TkuyhCDf","cAVsGAKTKO6X":"Sj2QOqs5El7n","26554dwxOGLJ":"joVkcOl6mJ5e","Sy2r2VFWpU53":"vJZt7Wb4O7z9","VCoD5lUfsJDs":"nEWIqulcMqjn","2GaoChWxqxYy":"VcOFPervD5S9","P2GfYedckO64":"4FKwcNfACeSq","Rj5hSH1PoHzK":"yaKOWe0hMGLP","PFYZobMYKoIy":"4uLXOtqPwTLk","LeNKB8pUYc1r":"qiRwLtzkcl07","ziWGRYSJrjBE":"UkzWEYO5u2fj","ARPp0rV3rSM3":"hc1AOlblUMmR","sObDUUxgPOt7":"TYLYPzbOYwUJ","gYPeUiKGJb83":"o0ifgrXt6Kf1","KLT96FxzCaWm":"LtEF4QAvlHDV","7TIHoyNp8Vwv":"gZSRyKZkz9st","ZlZQ2JSdzU7r":"6A8iy0r4xXEe","5MjmZ2fXuFbm":"XepQKJi1lcjZ","Z5laqMtcyl7T":"vh6L2rnpP3N5","eU0QccmMJWb4":"KuyAgLfex274","XvWh8lU6Ga0o":"F8Wi2VBJWxIW","1cIKviwMzQ3D":"wejrSvYGz2Eg","fOvBP9JZAGlH":"le58lMkRntUL","fOeEv3Uzj5WB":"fKxV2Q5RrM85","M0VGn8Mrt0ws":"JnUAijQ721ba","H0jHf7QK0JYx":"yK8NMSd3yHkK","PLgXKrhNFJ99":"728syNcw83ke","8kGpNhDkXzKf":"bNa1BQ7R8oSH","dxn4Pv5taIkT":"OKOJTfSs3rFf","terBDWERZvbJ":"Wr26ljG373N1","33ZEPhCLdFAU":"p1lCpIjyvytB","KdOJuP9uJvfG":"eoHeGSSPwOuQ","AqlFd2PH7Pq4":"A262q4NhpoQq","peTDGCixnXfP":"zugaxa6O6Puq","n2RjEQml6kl2":"peLKtTvxsYcf","nfhIFXmbWrfG":"BUuiGQCsP3OS","IJnn2XUl4E2q":"65pEZpSu84AL","sNwrEm2WJ68d":"fIVYdhC3PnUK","0GeYo0XJVBGI":"aQFX9PjXdKC5","bSWXDhpBk1Ti":"AfMiRRBM4J9W","PfoeI8SPHun8":"nLvJdnC27ETv","tePXpmr9Ne1x":"u7deaY6HalUP","SnuebyLpvIDC":"Hqa0p3u2YjXy","JBOzrpOqBbkQ":"8a1cxCNjXILW","3sSLYgEHXNfJ":"3EApHLWNWGQl","ZLirQVRpx7X6":"VCAGNujlRZmr","BYZrhveNswcS":"hVtdK8LQYnVS","HNuE6FDgSzm7":"G7yCnvEhUUY4","QqsKz9oqAzQH":"DXVUJfuvVVL5","Wqw20AZZgzsr":"Qx9Z2NLbWokl","Hkwbyxh3m91L":"K784QgA3bgDk","OmV6J5bSlEQQ":"CJS5rHXxKARJ","hWgFobY89Miz":"0FklRtrBnV9T","CvbkAWGr1ELn":"JDvsIW566XFZ","sRWhwo9hqDO4":"viXvFm476qN5","HxVyOp5ZQCXN":"SsVdsd31lAzr","99EbMq97AaWp":"Hb2PFoKNaZI3","00VSpdoQVQJ6":"IMZHkbtCDR6K","ZAeIgsVuhJ1T":"NxV36eoJEJUp","wbLvfzB17Foy":"u5RDl0bp2QH7","qQ9ZxOPWKjpj":"QRpSJunCGX6x","ZuOuLCaDvia7":"FARe7ZVVOEur","sDdfI7dHHOIH":"QkxRgM8QPfje","207EKNMsV4OA":"CTPgFObNiRQz","kYLKmPNvtL5D":"5jiFHpE1IT3f","TjVA91JDVZDP":"KNJoJOr0fPDU","svxmfarjEvS2":"Yd6iGeSyjCVT","q5I7INrip23G":"5BneSKtCaZF2","guCgADXtDMMG":"mlYRWz6qhZ6R","YmwAavIkkGhh":"6lGOmPWAhdZY","Oc7LGqei2O3k":"i40TV0AxofYr","QaLLb7uoWWTI":"X7Hn6hsUI0t5","96eFyjiDpXSs":"vxySoWG3YbLW","aY5g9f9bklBu":"oqrAjZQkzYOi","9xRM2his2hCi":"POXpATLuJXQ7","Pbr7zzO910r1":"1RfncJBd79ax","Fz13a0EHLolu":"N2fMfU2v14cI","lcD94m2GXX6u":"2t7d18auMe88","T2g4QXaVSwgN":"3SEb6XOgmJ3V","jI1JftW1UqBB":"E7EtmKdWrQG4","Gjw0AeKHIhWM":"IOBCzrSREPH1","mNRWOTawHT7g":"mYxHsUT5SddS","YDtDMFU7vTHa":"hIu06wqlUPRX","Al4IIlDjaZxF":"SVUPXml1ev20","AMZ5VacoNSyT":"Kf5ctlhMp5OR","09X6NOu5Yq5d":"hgkHz64BrTIH","ucTeYZ9vBkhY":"evTeI6YgwPJ4","pgD1HIrJbQ1N":"rfmy3eBDzluu","6HrkhnepdSTC":"XNj1kDA4NZ1x","eoXIukzvuHqs":"S74szW8fZYsN","KgtU9783UhZk":"0zYtiPUvYU8f","xBnPsQQrlApt":"4F1I5FSNGQ7K","mag07zlT4KnG":"bUI126QAceJQ","cX3QhnfSPVIm":"w9d8Q2FgeKS7","rvZYkhOtF8N5":"FAv7iwiSag6V","vmAmSLerX2z5":"CCo8aBnZwoj2","TZoSFhTTqLYG":"rt1RsavqBnay","6DeiXOlvoCLf":"K6NDLUoDXIHs","NM5UvFDMudKj":"64tyZS3RcwaP","mDFX4BusMugF":"eMQis50aPiPx","Fp6O11BZkair":"jcSyCfakckev","QCzm9bIPiHkA":"d5VJhMvVVvR5","9bTjCg1QeOHJ":"83Vo8EnPCSTJ","CBXUHg0AQST6":"i7OdA3oosgfz","z9UStQQzSYjE":"i4z8JQgBOrTA","gvWq9O4X3FLO":"ZgIVAbTVtC8D","sSevgExq6k4O":"WrJYbzDKqLet","0GUBPdhuSRCn":"CPuAHaTZP3Xp","ggpHY3xt2Eox":"o1W08TL9ZPZp","CiyMaci3Jbb4":"4y497NKAkdNt","pPRZNg8xSFGO":"vwj6YEfXMfSY","UlDut4aJKi7d":"orPw8SIk4jVQ","71BgbysI9b61":"q5JgjEv14MuH","dABeSzi492WQ":"4lXSyRn47xBb","R7bK0ExEogP5":"c3jIS6fbJqpG","PGwYER9RCDQG":"FnHAWs7oeci2","bRKhg2twTSi8":"S0dNDJsuiKPo","DkfdY769HnUv":"Nr0SLfT0JnMt","jUvufKigsxqv":"6SnqrGZNhJR9","1dJeyYXE7raM":"CoPKpLpRLbGd","Bx76VkJWhb8J":"PrY6ClsgETrD","vkYxy5O2cZ7e":"lQ6zdWQJxTjN","lMH3ksQGqBis":"UZ3bKVLZQZ8G","S1aYjNNkKy1f":"zppv8Qjo0qYm","B38MoZW9JCrW":"cQIYMSZ36Tce","669MbqwIm23W":"AfoQVjZFQYqU","ZlDADYeQUXl3":"uq2aRVMZfZWe","14F4NRJgZSuU":"jNy2Zcvhfkrw","JqvE6E3AE6eZ":"t6duWhFmgLSk","izI2FEQN4DgW":"STLBz8cG8P3v","jSD6Fp82dlUj":"0RRw16MFxFW2","AQyHgMhsEpe9":"c7q6iX5GiCPu","5PHfZkv0n4BZ":"vvWOaVFzncwE","laBoxV8Wu8e9":"7fY26xLM3DrS","ugco20KWtVdV":"X5VX1Ku4FbOA","LkNyl3EbluyD":"hzhXutFBGT8b","IkxDZy7YlTHp":"SxT9QL2vtFoT","CrL1IdEYyrfv":"k1HBPmNFHURn","751Ydjb8x0zV":"DtNe0jBUG9gH","iu8kaJuH71QM":"DQD0iHxYZKA9","pGhCShnt2hTO":"bwn29uttnh5D","fb6K0eGbh0k7":"QTBvKMrXwW8T","jSKWofrmPnG5":"FFSOTmgRAlBo","KcL5W0fPqj4c":"PBKybzEO4uYO","OBfOuse0ugYB":"9CL0gs1gDfBJ","jKJjayDXycZd":"oPl7N3WwGFoA","ry0bTqqsx0Lw":"3AKrwQU4kea1","Rk1UOyDAXWVf":"K0ti6GWBkDZv","rGA82N91Zhh6":"JgBmt15xmT2M","Td7KnGqF4O4P":"gGgPhgcWDmC6","oCkwiCskuXau":"hwmE4K9cCCri","IqqpTzfgzoEN":"lqx8c6AdeHTZ","RTJrBk9qYCvP":"hjMMNSSbFmX5","TO6fLGKz0cUk":"wLvXC5Rj0Nzs","rU6YnDkHURR8":"WtKcAa7GAXGh","XzNmOrhyUQTx":"zF9Saj2SD1nG","ECnKCoJ5c8Gv":"XSrH1CWQELro","awqFh8TQQ11L":"KP2sKQeFccZp","hW9kLQLuJGhF":"INJ8FGLC2QmQ","qH18oX3CtmjF":"EjRinV1H8tGL","SbF5AdiqZrtV":"OO6fvPa6loZG","927gRRFjzeW4":"L4bvGA3EQYzp","qQu11ly93yft":"mnBJkOk0uP8E","C2HrdhWVb4jt":"bR13LLvPemkC","61FWO2zAxRpJ":"pyVfumThhZY3","rtRdg2woSlpC":"q6kVqPqPeWl5","moUBHrsdQd3H":"4eu4ptGKWtBp","7hJ85JzDqTsp":"RRbHo5BNkVGT","xjfQIq2Td2zq":"owtm7twKdadn","YwFMaRRdoMCN":"HgqVR9aavirB","Sczc6oeiTqji":"P07fti0Jwj7K","9ZNVrgXmYY0X":"dVXRPfkZBX5u","IS34xA5utSxP":"CUI43B0XLB7i","KpHUSv4TzlKx":"GSbiYVj6WS3w","bmyB6G97nUxa":"DaM0A7EhWK5F","2af9RA7EVpKp":"hIuKIZ2AjRKG","9tNDd4wANjae":"ceXYTur2SWaw","NsK8hmurVo4v":"MWE86r9F0cQK","Zfyzx8HY1tLL":"htv9QG1c2PvW","VMVfS8oG9U2s":"8pjp7krZ434j","fMRJFHRmLq1u":"lWRDtjr3EhXG","IBsPEIRtin2e":"LQDhEdbJxISu","vTKY68ggZRFj":"F7yPhgJwKKE6","wEoOB5Aay7S9":"yGswQH5mrA9K","GxPTL4oE7pDS":"HQRmSjzjpWwP","79c4uXrf7Ox3":"pYiw78Ydwduc","whYTmjTvtvKq":"HY1fuuszBGY9","t8AdUnIExwGk":"iF4kuPxJFDtw","3JJgl9wMnE1B":"qyvtIj5vpkYB","4adnmUrfIJbe":"nY4ogIQIafNw","PURbO3fFWRF2":"wvcl5HjlXF7E","Kw1JQbSHs9m7":"dky3BXvTbSxX","5MC5fgmUL5Jm":"m3jAfNY05HGn","Uc5K8iGs4BZt":"56T4fF4pVTpS","AYm6BqYAvsXo":"IB5zWGtvZbZg","tqYnWTcO5pyw":"jacFPCqLUGM9","Zzba2KxXUhZg":"iwaj89siE6Js","XMNgYyt8fCTn":"IrbPirZ0tXKY","EeP7KFCQBRoi":"Q1jdxps6Y9ey","0pXX7Jq8SEQN":"SFg3VbuIkeMw","RbCdOtnezZfK":"qb3U77UpdIj7","NwKjAv4lEvDk":"ctJ5coHsJryv","0BvbeEA6wIJ6":"wL36CPewzASe","IA2E8v6FmveK":"UlCUg1Tu8UVS","QfUcO3R61gO1":"9B1vOJj45vmA","45gNNUXooiJq":"GlLCIMGciHoW","lRpaTxtwwqBt":"jdtVXPvasUvZ","RvAznwixEasp":"IDHnYWevDY3e","rRQVrkgJvww8":"PBOVTxFHuMEq","llha5APsPFbd":"k08TR0UVa329","73sUUcaPZb4Z":"nB3vtbWZhMRV","EaDTnW2gXRHs":"7YQ0heaBTyNx","AUdYN9DYTMyw":"M0tQNrsZE3Hh","xffOCzYbk532":"fJ2x3mWTgBDS","FM7P8C4E5rPb":"JUbyIwdzBCkS","4fBYCFUaGhAU":"kQen0K9uhXCl","4G3CFPMNTv8g":"rOkVdD7L79xB","AHLld76xEJpb":"iKfiTikOpKrV","VPqccxxMBgtb":"fmeXy27m8lX9","CpxEshIhjW6i":"5I7mGfL4aUq8","5X2GQer0iUA8":"YV7vnk4OlcKf","FpIGR86U5myC":"uIJpEz9ZQQAP","XWoGtEiL2di0":"KTPQp5OppDyo","BitOXa7eHDvm":"X3j9tdkyvZO1","514IV6PkbcUc":"cGsx69MhS8xi","kEOWBspB8pal":"QgcUPIFUNtXV","wuOhYk8OBhzZ":"rDSNNw65fPT6","VxSm8os6q5Mb":"Uk1GjHQipvNp","wrNkAHWPQAHn":"eXpUGf07bS2Q","3kYqiDqdgSlr":"cQLIyPxmQEaB","cgwTk0Ef8pzK":"F9p4PYHDX0ov","4uPLHkQ3Mux2":"a8aiqyKO5Znm","zGqn2iMMyArl":"Yx5miJ5fY9OB","XNSnNyI2dahR":"QftfTnWmLsfS","Mdm4ZBZawPhe":"md658ZmCEkLU","YcW3QRdAlh8r":"mgibRHWfBmfg","tPF2IqhbAxRi":"tMk6htmZNZVi","d6lJuNXKiewG":"hWPqcqPZ8PCl","tqOO0xIVzz40":"gmDuwQtde4bl","YZbodPjd9eso":"2YxIdYLUbdvJ","GS2G8GWbG6nu":"iAVPVySWWEmK","FF25kwyxYxdJ":"o7hZlFDElG3F","c4fYvAZXmqO8":"gg8sOBqyTdxR","V47gIPQGkNKa":"jrQz0u5PoagO","DzArTTT5FE1p":"ZM8TPbKkcO4k","kpqmll6EnldM":"hucqxhbiOyWj","2kO9BxZqO3gZ":"Bk0zxjS6lQCP","en75whUeRE7g":"Axt3S3IOgegM","OXVuvT8P7uLy":"d86T399dP6PR","bP9ooZCPOsZa":"yfUJXqSZn6MF","2l4D51bxZqB9":"CxInIuwIRG1X","Xv52KSNEf20a":"wf8sw3uEbUiD","5zmLvmkA6zzH":"gd8J6ZLupp2K","aL5KLZE9ezGc":"KRMFMeeppjg1","R00SmfzH6ohY":"y3R0d22IXxoD","f5uqAzTD2Yz5":"ho6veykkPAw6","It1jE2GGWS2M":"hA6Aw7HZEPjM","lGyUCPCLrGdZ":"UQB2boS8eeSM","qd1vzjG2oiGp":"mYJv8l08MIyJ","1RN1K9sJ7zOh":"wyIrDkYBIocx","m3Nj574EqldF":"IIbRSajSPYGu","nOMP5KQCUBpE":"o9j0ch2ZAWQl","PdHbLfDKfqgE":"B31xyJiZ7XJT","UtWhlkXwRVd5":"BWNkeSmKW5Dy","oMGpwHTIvk2O":"tTWKDnI0iw1G","rzQWUxlDbmwU":"QsQWhlYgnwzT","XTSH4dOZ2REo":"JRUGQpwQpnnp","HYSspZxWWbG2":"zCkR1AvtxN69","gpWpwszyxQeF":"jFdPTRjPPxSf","mOYge6jXTp3D":"RueeWSVXz1bS","e9sNawr7VtVY":"ut10udQsQPWN","dmp1b2g819KL":"nobLt10dTSJ6","0Vth6rZyzd2v":"moGSjBR1kQrd","fQBGi2gJ3mfI":"VpR8j5chUWX9","ejMNPaGy2FhH":"7IzHf4lXqO6r","QVYHGwxLYJja":"Hl3MP2cweA52","AzORRHyOns4W":"IYzGFzBfilX9","wrogRSsdxim6":"JmnGGwM3MplT","qoh8F1DXo8YA":"xhZjBv87Wtmj","WomrpfrGYjF0":"vEAdG5sWbA1c","YQc1fJPOeuNM":"saszro4ff8dn","wcnx4iEq8lAj":"pUPNUFpsz0Hj","g1XM3S6Lo44f":"UALS5hIgcDaf","AaSBktSriHA5":"ZYebqr7vQrhQ","LouhqAL770rK":"GbCtaECQQ2Ct","NYqGo8tBxghI":"c1Bt7dIodHA4","11HQ0X8BK9Wr":"yxNM7e3NMMUr","bO0mJILGDBkH":"oBzGEiGARQNm","f5WPd0HybZve":"JZzKualwiKYD","yp9ZMlzPO9Pl":"CectNfrQ0KfV","ZX40N5XWdhlw":"UA60Ti5mkaa3","kOdMVxjSGghm":"SznKaoDilAzX","Ahp9EEUUaAKa":"tq3mnzol8exi","BxX3w9Bjg1zg":"2RCP8JPrLGnh","nI2Vtb0cUY6Y":"QR9LE1mbKw9l","8R2lUduTq0Oi":"03nlg5BihAXw","OLwGGQA3dv4j":"7ntlSizKt1eq","27Al0OKNEZ2Y":"e2kFixejwtxH","xfUcEzul1GOh":"64FVQfyEvFZP","MmoK3ytHZ81E":"ChQPczCmkz0o","5XirnENjHHRs":"pWcWU2cCD3or","5XDucvhesYUN":"53JB0MqZ6EOv","DVKRKRN3hfwP":"ld24eMC5cxLN","YKaezBFU5DxZ":"z2VFvf92VC4d","9cTKXovTxpt8":"GwIP2IaMLany","bmz7uZ1694Nn":"R7wDiFUcVh5d","dzCHk46a2LgE":"LmgdxIREm34e","V02q4Blau6Uj":"ssDtnBMydfOi","HDKeKVoSdwvC":"BDhahED0DM7R","ghrCV9WwOuEJ":"w7A5MTsop3p3","EMRarnIZgFN6":"MmV8QPEsbKGD","cBPcTq6upUys":"aC6OMKagzpeB","kaEK41idkqNz":"uyP9j85SzDtZ","g3yBezrWswmJ":"xuCKboN510ti","heuh1LJZDgT3":"USYoHVHhdYhn","L2idnBpzLZBv":"iyTqpMEz0Ulo","en9IYIMH5hrU":"xkAbqUWBeicw","jsNlKmLbYz33":"Sh5aMi9SGaTB","ESyY5tFAX64W":"yu3tv0pBXbU2","xdU9QMbmdBbN":"jYvVdMxPQXWx","GR9XsizIXr0L":"rplPfjQRuVaW","MeQO6vKWalrY":"wM5zsfe6sbRr","Pe0VF648lPzc":"pmTkZ0NemEZs","HskxHO07SYyr":"ugjNkF3rZRH7","PY9eGbc3gR1A":"fWmRVjSATYom","JvAHiiiZF9kF":"AYRD4oS8QZT8","UphKOvK7MTAP":"dCpUqGrJ0qJu","YDO9wYEVC4fN":"rloCWPad9EyL","UE5BSyMl8RRY":"OvoafyGH18X6","qnNkDerYNVkn":"FU5jOniWAsu2","vLKyiMhccKlu":"DaoLKuFTB1fg","804bAkugFVQF":"R5fEWhUQV6Nu","hBWLnGQIXEbG":"fuYYjcgtaKDH","UTNf3cdY6r1I":"w8jZDX5zIVnZ","7ctSUIx5JBpE":"KxoFVVojY03F","3CZWPvwfZN7b":"6oMvjRqCXsiH","7ce04SnYcKrZ":"KueJToqg15uY","UHkmFdd9PyRq":"nlKrHrpf0EHF","rylBPs3Rz0Jf":"oiBntcTTqQzB","ZGArCGENjJnQ":"RvtcknqXiHvO","PgxK6SJC5w55":"vF15A8Rzd0Zd","iJEdXyWtB1sj":"qXE0vVQ9nu3C","3b6ZhB7iVsfG":"w2SPpkNlr7Me","SbyMVzE9VQp6":"7k8YdRVNOuZS","LEpEDT9TdztB":"QHQRIVsQQKML","yJHkUYJHe3PV":"C8g7okd0cssa","FNDJHI9m8q0X":"ppWHH2yPxLVR","PlaPe5VdRj9K":"ImuPvkCvHbhW","pB1qy8qjQj7C":"wWrluZUpIQ35","BngB1A4n3Zrm":"7J75rwdO2U2k","2j2cBtncLDXI":"jALhcAKVX1rs","x2c3fJPVsUWN":"ZHvOrTkujpiH","FZ3hT97Ai0fs":"m2BuJyqalz54","N2MLn4nSoNYH":"I2hKvP5L2vzo","W8OMrL2Xhjsq":"z2ZygnvZ3k4e","puOfifruhzSO":"WuOIkj3HhCwt","FNRcLppprMPp":"NDtLzCh6OSGa","Onlr7IqVVZ1Y":"xR4A2qSrsrFb","WtDTVG0cuO5W":"8x8AEZXAshha","KLaiWapg0IKc":"CDy6oYKTtYuV","5g38lY2eJVdh":"1lSZUl86Z8iI","wEHMGMzdjKH0":"oJRtAbHLgt5P","bzAT6mCGwa4k":"gH5wSlKaTTdw","28zeNUiqYkLb":"Ck7rNsU6OrdH","ENIfEUp05x2E":"VC4M8tUnSrXC","Nhiw6ifFx80l":"LwTqOpgquN9S","bUCfr4b9vhBf":"XibQdwhFRhAk","juw7H5VEqLgs":"M8CCj9gXZIGp","UHZF2WWNVDX1":"dZBKygvRZulV","VsdhdaPbdH1b":"EhzHk2qCHYnU","XgKQCfc4KhNO":"M3sqLkEKI9l0","HRtI2p7SJGyW":"OJ0UW234ahcz","yE80g815K6MJ":"qdcHrDvL2Zqg","BMmq1J1ZbNbj":"DDiVWOTzXGxl","hk1QXZCwFiFV":"ErNCfluxhYvk","t22bPXElfpuq":"Nw3gfUUxJml7","0aYpwyjfhenZ":"lC7n1U4LOBn5","0X4OuEHmrKrz":"zmUD9X8ZN3yU","BbKLi4BhOYdR":"UczqGSQps5MS","9LU0xSR2SeP0":"vpLexn6HZlgv","wWwGRlsOvn4r":"4IZ9EeqtKBrZ","333ji0kT2Zhw":"QIKs88TNJ8xd","OuSwSFgnT6Ds":"gGwg7C8WdRDR","Ay7uD6Gcrqiv":"cUGCpEaoC54B","CkixEizPEhxB":"b7Q8si7d9mje","IAqoxhcUYZBd":"sF8PWznz4wkY","qVd6bacBigtT":"Lgep6JpWcHeb","RK0MeOyvgLpI":"ztntUXxOQDit","P7CBad2269ac":"6JQ3NU4vSFaJ","zT3FpKIy3Emu":"ee7RuORMufP5","lOqVLr16eLdo":"1vAsRs8hsA7e","rPmlkxvq4M87":"LDLCNxnEeT1U","wFJFbMQ1daFI":"zQAm1f2WxODL","BNVk6mHsAj4c":"Xt2Q4Mb19EVI","qf2HNDgSGDIF":"VkIyp8XQsOMS","zF0q2ySA9NHZ":"YO2egq9RthM3","x7rUmMsHW6Vo":"BDNJRfoK9WYS","b2NsdHzXNP5r":"E9Hb1lzV03J3","IcjgPEe4SS6V":"gYpE6niQlezW","YvUDp9FZt78n":"iYqx0CoI3wbw","XXbLjyAGaO7v":"lPfqd6pU87Yd","y9ntdDC1IEhl":"tuY8RBxbtVOk","hdoUxpMjJDd9":"HFfRP5QHhXca","cp7GVET2WQMn":"q0AaykDm1DLo","43Wp47PDvBxX":"AcT8gUNKazCU","FY1KwxkRjoRE":"IaayP6knCUAq","mDVQ5aVqCHXv":"BaMfLpjPeV63","Cqt7e39l26DU":"BE3RQJbGVW8Y","y16mxr4LDcOG":"aS5k3IrMOmHS","gkaCeBv0i1MS":"ZStibgol5HO8","x1ICpmgI8H7p":"SZ0XZzaDJnAv","z4UHXQhTETrp":"D5mOYf9ADe9T","XAoCP4pt9ug9":"A2mJfXltA7Tt","IilwsEO8xadp":"ILKjZ66Wm57M","ljuAuQ5cTDhx":"Hle7Sghyfjxv","cJCwKALG4GKD":"L9paJoMDfSxL","5PsgKQskIIuz":"PWxWORJ8lTeY","3oogYhjXFEbo":"ejFOulHdeaKZ","wnLkhdt9hbXu":"RLu6jcRmEtrF","SrO5FhDsYzL5":"16HHT24UHgEb","C1kulJxDWHqy":"ChIFcKTmZJhy","C4qtmGBqL4bp":"xvrLx3gn631e","ldd3mqYMiVld":"t4rUzZmnjeTp","IDB5hfTiMj2E":"rX4MpCSI8vUf","eWWkiCN2LLcK":"phg3O2X6s2MU","NO1PMcsFozl8":"SM9ZVafWmnnw","SEFoeV2zSa89":"vTx3QZWz41x4","gvXnRDvIfv3e":"v9xkjCWnyoWZ","ihZn743p68sp":"tYzS7fmBWgI2","qJtq4UMnZgZe":"9aqPBATWxU1b","DyPqJrmg7svL":"yw7kUDi5XQez","cVmhXY1OpCh4":"ExGfvNaMDAmM","A3BjtqjzKtqE":"iVRUFiG9Zxcv","85ucHq7iJCJX":"q92zsLeDvlC7","tO4j6bSoFxui":"Rn7nRWtvWKdW","KRHQmgCUrc6B":"Y4tvzP5HVj0a","9q3gkbaf22wn":"Pu4AJwLAk3JO","NBCvWrsKGRNi":"uXfThh5YjyrG","5LMcWoMnbZXw":"PYJ8p72e3Cvf","k9Kj7UdWupJz":"YYrpEGNU0DE1","tKpfKmlNiRD5":"UgWvlEf6EOu7","3yevHEWlV2Nd":"F2hV8x1tIb2q","vXGzwFPTsxCd":"KYGbYW8VyzcK","k7Oxc4LaVGeP":"BglUvVr8Czut","r1QiMQ78ph7U":"ZRAtSlthv71z","bRRzA2oKXeZs":"qfCDk0lAmkgd","rqfT27MnSqhh":"P7nnXIXmFWMe","PZCGfDqa3zTH":"g1ny8BKPubb7","mSJJLmP6g3uo":"CHY6V0QSn8Op","lVPrRA5hLeDn":"J40RkCJpPauU","SziJSnbwxp6s":"gXrfDezZjcqw","y2Ek65Rigi2o":"PRlsFU4KkcvR","HhwKx9L6V7Ys":"FTJN7prDKKi4","4mg2KFqj00ND":"Jo5omG0hxzOh","a73hSKDnzx0M":"D8SrXOL5JFqn","UgALNi33ee1Y":"2Q8fYqNeXlLg","nJgXIohUaXB9":"gmsyaC7IfVK6","8yul23w5bioG":"9P8rZdFIjBEn","kjpMeg1VjoI2":"4HkenalSQNeK","ru6H6bL8IADd":"FLGNxKV4VuEl","WaV2QiE94naH":"LKf7LwIyIOWA","hj2SslUvNYBt":"l6Ra3g82QE8u","huHW52RnRg5p":"tMCaHvuohZ9O","o4Q5yigTAEOo":"0N5YA12Kf4Ge","7U9VO4ovIe3Z":"sQaj1ctzkZr5","tzCYFa4blCV5":"jEgte96IEYih","WkPxXwiJIqGi":"JFC2ed4tseaL","9l8pqv9QX6ww":"rOPmtoms7haS","E3Mk3LWfvQ2B":"PxzvyFS6fJRZ","y9srcBCGAbaa":"yqSSD6hjNK1x","0qVhGnxw2UWb":"hFIhGgXKYd2G","sEmZosNV6Cgz":"q50kPVSHzf1u","kH4yv5H2sAgp":"B3XaAz7Dx2vi","b25Y2RGpxqvG":"imm5anClx7Ca","9dvL9XKaHY0L":"HBTkb9IiHkRT","66uBy8Eh1XpM":"bVMKUGDJ4UdY","lrVSWpdUddS1":"TmvaKUgDp5Z0","1ukhdroa3Oon":"bWgM5grGdiAK","e3D87FLHh4iM":"hpxfFXCCfLzy","YNyZaei2JGsb":"BqtY9Xv3UkGz","lrJfdJ9Z4edV":"sktxmKwPgL7t","iBAU76QuEVLg":"LovQptfyTMqf","Uiv6i777hXk9":"Vsh5YKxMsK1b","ESzRGBSbEZQL":"OIVdGIPJXnzQ","3G9hPl90MChe":"4EM2AeqKC3xW","NhltjpPeXUHQ":"X3XzARhMBlv7","KgFxPUCzgWra":"4pxtGBXle1f7","rV7TOm4czBz4":"MaD1SRvT1DJ4","yQOhHDWae4BI":"DRQqGEVMG8i2","DxLGSGHn7cJy":"PdKMWEJtwC50","R1kUfqdPIkF8":"TS5oStcexw8l","rh6Br2pzsUX9":"zN85NU24G6nk","wP3gHUpyZKc4":"wSoMuMaffGSo","DNgkg5fgGYqh":"z893cDUst9Fe","vW6U26YtlsbT":"DA6FyfjJjN7L","CZ1m2Gsq0Vbw":"HPTqRUFvDbKt","MItttjiuy0Fg":"KKeSVQ6zUGvR","aFqUkfDcltB4":"TI2zGY1HPI1y","QEpiir192rgG":"kdkEZ2tGan9L","BQB43RHsE1wa":"pzahNAgEoCsq","NmR7OhNt3Jm0":"ysOJLZfKHWQa","hyZqovwqXULu":"6vmlexduiqJA","9VFMdL5fXwDX":"7fKi3Qe3eIgE","CCIwXkAhJA6h":"jGzrKZAsliww","wFAforRdLVri":"ZC7FNhaKYVqB","dtXebRwDL3CB":"iEAGshiqW0QZ","DhKE3kEPpU6m":"zGFF2ccImjIy","Jh1moIF7KS3a":"uwOtd9X7NZ95","9iVRTQSpt4qL":"zcsSBQ5THPRq","82XRQ8Ahe1a5":"5Y0M6bLVRKa2","Dr0eB3BRwBlP":"4I8JQdCTch4t","eOkskDDtoa2H":"1QZqotQZYD31","hEpHB2Q9eRre":"vwLt2C49tBcL","xu9hI7pMw1g7":"sou3zIV136hJ","OYdQwORMyS71":"ZoR0Y2Cl0XO6","a36PTCtfZclN":"9nEsIvNA6P0E","LX9zGKx51Y9j":"rkEgEdodoK0g","srS88jsR526S":"jQR3kQTZHNfz","k96FNeGtQrht":"db4V8u8DMlGr","otDQox85uQwv":"G48dH7vqE0UR","KKqwEhtzkBdJ":"8AuhFXdnHL5W","QcTaSfhtG2pk":"LeSPL1RzzRPj","CeZsKdqmggO7":"tB1Sjm04btfa","qIvkI0HtFdkv":"hzEfem6No2TE","vEQvgL5CXXBq":"2bJ5gPzJwXLm","ATtB1j3vrtQF":"BQP1sfimTwSR","82jilquG8506":"bQEWG0bL7IAq","PYTX8CfwZU6c":"nRFPqm1fXncF","mMpz0m3uMtxX":"1i7xCqQeISgZ","tf5bDwcoVLAL":"KxFB4ycpzdfa","oe7XUHYj5RpI":"8nemm3LRLdjy","nx6KNOLBbdmz":"Z8dNnuarceWF","L0XqTA4OC5vv":"iN6VdRxzu4Lo","F1KnrvZVki81":"85TcjcQIM26u","wjRpPf6lRpN1":"2jZXggISMpLf","0rayEUCOvZxb":"fOZto4jPkew8","dVx47GoxSGcZ":"CLTSaDxiRnKv","vxB95Volw6Ag":"xjohmFiI63ZM","gIZvtPUJOsta":"mHNXynsactFC","NSSWnho9Gcmr":"Tq1LejkQIHls","xlVGOJX8tQPq":"tJpEKqqQWk5f","IAfv0kWcdOf8":"AXuRqwHnPzas","3ND305lCPtvl":"efUPkjVGgaZr","zsdPkAGm5d1m":"4ZozR69gjxO2","Z6cZcjwDYGYp":"2eLERIyB5J8r","FarBoha64EyI":"NSHAPOMDQJWH","DQRIOknrPqG7":"3HIIXCHCNA7E","ttync7hLXIK8":"M6AJ1UzhL6MJ","I4hT1FNjulSa":"yBrCgEAO7qGw","iFGMP9BthrUc":"64dKSG16xZRp","CSMsGH9zgre6":"wRBKQUdLomgD","dSEj7VO4r5Wd":"BtQOBmxGz7xX","ZKFvLCMocHee":"npzBgdOVhzAF","f545xbXqenC0":"7SIUFpwZiaq1","ZX1ZGzRSoIFq":"iuj3hnORhEHp","cTo7hJowBMG9":"WUgSac6fLWpF","RpkDJFIv5Nts":"Y2aDVJ1W1hQU","LAmF9MI30smZ":"hry61UQUsT6K","TAumeypmjLcd":"XkqiBf3bk3uP","xl84lGW8buU2":"NEpqzhGzaZAF","MPZXTRtxXM9x":"9gRUX7ylATVO","hcOzn85rJDox":"hQrYHxa3s4Ci","M50qQ9eNy0lU":"xGWjqpC2M5VI","zwRdkvOQSRxo":"4qn8N6h7m1ux","qUi01eoNgXkP":"vaMFnpmsSK6t","0tA7LLqhe4NC":"QkI02NdsXNam","g7LhfsnZfP9G":"tmwrL8bS4dEH","9UDrFxC58u4z":"d4Wfpt8twYzl","yQIyGk7iov4k":"TkDuGIgGCUVm","89fuP2SlE8rU":"rpSsUhvPnYDr","uIn63QfQvrO7":"Xpoc7hIGwCIS","Y42tJ8wpANtk":"lC7qHOKFpSac","Zyi8Xv4R72Zd":"OnzRj1DMj9fQ","c0p7iB3dnPmH":"5CT2OIXvT628","7jV3dWHGafu3":"TnQye6SCTrgV","HGJnVhbXtQjk":"xF4CPKvKWwXA","nUZMWO6GDb8q":"qKN32yHODgkl","oSvAYP8S4zkA":"lMYTe7EKBH6z","cxOfvzc1NfOe":"ttFdC8dr6sTt","88dKz0MsPN4F":"lh29kxgbvdfH","CRV3b1qzWdHo":"yuZwmRrJ2bH5","LYqyK1C8nWBI":"ePMh3obdgsPf","YtuUM62l9Q7v":"75uR3jpk2l9y","lOKfm7h35mKK":"4EVHUgHuOOEX","pwxab64TxIa8":"hMCz3MxT3Xpy","bQXN8osDRpW9":"c1AMLyAn4fG5","XjKh6ZNv7ask":"Lg4LJ2yJWnqr","zwnTLPtxef59":"joNGUpPbiRQY","EfAYbxMdnbK0":"9F4Iupb4KIPr","OYLkOobcM6Hk":"Hl7D0EMt4gVZ","JxJdICNe6UhN":"U3IB1DqcB2OW","Hbn49Xv6qGRC":"53vO8olaJAVc","pUqTXc6HVrsv":"iHz35ilTQvpP","X4bUZ0OoInS3":"0BgTALtWo27m","QbbHges4CaZZ":"a9lkKrb1mkQk","WUBmazUCRNsv":"7LbR7TtlySDz","R6xMUcrXFiDE":"8voGpzDWshT5","EEaGidjdzBzr":"dZCz0AjaYrsg","Gava7WyJZCMb":"L0QO4AYVsgTU","zSRIdAkPlWTN":"fBB3J4hqgvQ3","RG92KyulD122":"h8FBXhImLrQi","cbIFhrOUl3Q8":"RWXmTfG8Necs","B2HYs2Q6eHEh":"oIlEDhssuJa7","EsAAWBurF8gO":"u0vSiX5BQULB","4VlcFVRrSuR6":"peRSFIwTZUoK","Q0UB3ZbQ7wqA":"8VtriXLG4oay","hgUPYj0MykOY":"LOsBB2kveKS1","mdNY79boGvuz":"yvnS2omDO0T2","wPioTtoKn5kH":"pWktTecFIg5c","meDy4ZSNPEhO":"oQw4xhEClj67","Gx9YvhPD8opJ":"SQv8NWXNf4PN","e7ai4pWcs6q3":"VWQVwFryGsfU","mPUEcnQ7ogCp":"ExUiOUZ3RwyQ","hke991RBSNEm":"0mnjme8ylWAC","IwBHr2CQJcJY":"jSA6e6bxwtBk","qah0glgVG8IB":"mNJhsI2C99iL","6EnQRTUEf9sK":"JAzJ6MMZuQr0","F0vpq4S9Z9py":"VM4WD7L6BE3n","Y4yBHe5u3OI5":"jGglhxYQ9PLW","bG4q8HAQ7UVm":"sMQYi1Dqg7zb","LahFzVlC5g1K":"7GxyWdZWPxMd","qaRi6VLl1gmy":"gZcEE7RThaFo","1kPcAidH4sac":"VTQZWGdWwi6m","9g7vmgsCt0iO":"w6ySpwlI7Mys","uXrvUlA8fuwm":"LyVdSZ5Y4VPu","qLqoEZFQF0NX":"ewv6r8ZyA0ne","RpQvNtYZMPO5":"UlX5nIv1iH9k","qUXqGuVTZm93":"ce5ApDn8pSeq","dSXcv1JopZlV":"r4P37hdcoZ9X","rtS2kpNG0dic":"LlfDbA8S6WyJ","aBpdJNVGq4tY":"jrv2kuIpmSke","mBa18cwtzuR5":"Kw17HI2A5koC","Yg82TrRK2Ozi":"TFsmiuLAJzva","V4noXb4kOTXr":"EKgpTQNBeF6r","k1TIKGxmktUi":"WHidwbbViMMJ","nxUTeNHPdG3T":"x90hhPbB4Psk","EQXqRmqo2DUm":"iukw9jO3wE6w","YQGFblBDuygU":"pepAS7pIKdEF","oOAip6elnggy":"EQ0CJ8TG5CWL","A1JnD095pP46":"kginMHQV6OwJ","Uf5eWlswH0KX":"jHrDdiBoHkG1","bl73GeN3t4LY":"bbu308WrvDcw","uC6o2RYwXqAo":"OlXNCzQFluyc","31g0xblDvcYG":"SOC8MM7Qef5Z","LZ8MkvAUjrv3":"hHMb0Cy8tPsc","lrUStP8sWvaw":"ch8pQXfpf3wx","l8bVCNkpMjBv":"W9v3sllDMHhI","rFEhRQJiyCUJ":"CFaEHhb8OTB7","ZURlR8IFXl8a":"yKza3TlKp1pS","mbeD1zpfMSP8":"P18czmQUcgYL","9JCUHuOavd0G":"ABcQeX6FPN2v","PrKiwRhX2896":"RIZPLizvbZ53","YKoVER0NrnYk":"Tp6o6HkDAc9X","DOnZB94rYqMd":"o9iLEWqHfWS8","hRxW71g1c4Gt":"Facy3QV0ZalP","9kVbxAl5X5tx":"pY0ElfVMvz1N","OutGz9QxQ3VJ":"OxSIpn33MOm8","n8mjG7kBNwjg":"CmacWQ2xT3MP","lQGqcFbnIAHW":"fyioReoFlM1G","lK5ohP2fG8GS":"JDJHrFQdEG42","S4OLp2swFHga":"nik5ex2EddB2","V809sijE0mUJ":"HrzrFanV0PZv","5ySC9QAiYQNK":"Hgg4yi9l8DHG","SZlclSJfqZJ7":"N8H6EONYnPoo","C7pcktdFY48C":"LRGba11hpvat","tyd33t4nxPnQ":"LI0Xu1EJAf19","YlHxYF3eK131":"TWXcL3ijjCSG","k93UXypRLn1J":"yE2c7MkrnDPc","USiGLjclrob7":"oXBQbnpT4003","FA1q8nN0j4Fg":"VIGn2x5qJfgJ","bA2p4rXqLftt":"D9IbbU7r7Cgg","dlQUQzSI73Pj":"dgTVoHiNgShz","fg6dyzN4wNkn":"mLOOEbldgDYD","nS4dh6hcNOqB":"nMWOjhoyBijb","RN0mRXQUiKGo":"abjwm4uSIKoO","qMD44byegI3u":"mKYHllNN5TiF","O2cu2omf1HrD":"f4RsslBCvbRg","3C0zeFppOgE8":"xk45OjTe9JIG","FHzTDlV4yzAp":"6Ck9xCv639fA","geEHWB10B5ND":"BgsmxRJs4nyr","XPOnTn1vviVD":"79c0JvjkZHmD","x8rE6mqqXVoz":"fUIOIkGcWtzl","zwy4MTLr4a6M":"4a5JoeB29skv","ogLopOexN24y":"eB9JqYybMTcs","9GUv38EZaQ1h":"rEhNLKeznRQb","mdnNBt2L9b75":"uYuRTIMA2pi0","30PVyw46O3tz":"sQrBDjCtuVD6","iRBU1PldFZDe":"rOG0F1UMrzQV","hkFxiMMv1Ev1":"7hLlmPcn6U0H","wuXulU7OzaW6":"pWdw7hotIhY9","Q74r3MvXJfOu":"5DOO7oQsFKLB","djfIs9cur6hT":"25KRXuRZe3DR","vlMGPHKo1Zgs":"z4vCpLWzrbCr","IQfZdOWC9BqW":"yOYQvSDLYjZj","N29XZGizOl4A":"9GutpPjsmfel","qntWd3s2chVY":"6h5wxlAGRHnX","4EmJff4Fw4YY":"DevhXkBITqW6","dOLU3aOnuOET":"hQXHWl6O7fq5","RGFXu8v82hun":"ZzDW75iJ6haP","sflE6ziypPdw":"S6Eq6IVu0Zkc","FXRrWLKDneiX":"Es9amCpVdF3N","HyOgSOto7prw":"4CdxvI62ogNN","tO5z6tRt1jVl":"k0eeq8MfrU4F","5gbfkzrvYv04":"nrfoNSQqWzS6","ofVFnhBraxHE":"8IVPjxJSytrJ","QHjlXXarn0ci":"9HebknESagUk","VmpiAtiOOKuK":"CA3AsQRa9GHs","8hOnhXs1GTRR":"zh5TEEf3W0nO","KHNfmFhaV05c":"xfFZU76goXZz","EPGItOTg1ZF1":"OpwpmRNy9t5o","ij4hylOwhLGC":"Jow5ac5Xlxj7","X8dWSaUvw0iC":"ArzwJmQC9TKM","3P8GHtPczO80":"WqDdGCY9e4SC","scTKz7mAGnD2":"IiTnPWmJYFJt","BqIIXxsfrKcu":"TkZ8TiMjYN8a","Tp0F3m3vB3DC":"9AeaBmy9ePCZ","zxBWPwAwN2fM":"FgFLD4XBAPVq","pUuAUDIjgjIb":"s9JObEg6V5YD","Zf1R7vkQ2jl8":"a8QDBotlShi8","KeK1zRYzoD8P":"wDlBMtayDoHC","S1gk9sV4YFqx":"hHoyGSMczShF","LUgOtFylo1iI":"CI8wTT5OypWn","cMZ2TeSMGCaE":"AWFTyXm0ecGF","YeVzEQFUzQxY":"4ioGofzR8KGI","weuLYsl6zxlA":"FYQBKLgIjGeT","kxsTggSPqmRJ":"yEibnUm24uGS","0JkC7mFqVHpX":"TBJLwesGN98c","bupHRN8ApxBh":"W9aOTCwjmUBa","PPkApW54JIae":"fW5cfvaxfiGL","0bGwPezxicd3":"9DLkYJuWDTrD","0nsCC41ur0nb":"srAC7QblLA43","bwHKFzzFCRkH":"FqaSS8CSswzx","5UWyqaBhbvIv":"b9xZQ8zAVv8m","tsm9hPnKyuid":"nT4d29EfChWa","3jTPbneJZE0Y":"JZpKSbnvO5Nw","k2MtPX4DFU6y":"W2fS6QFuezvM","u9eh1Z1iVPVT":"lWVNpJtryX9x","LByhcU8DRKgw":"uaeC8GTGrdxz","tboZEZcz8Tj5":"Z3XCgpTvU1PR","hwVE1T6RkNos":"V8QjRSIAnH0c","xHt8M0jgsWFz":"EbPlT2Qax58x","SenXXnMJjCpC":"XT5FVjpXISyY","xSMWjsqHsunO":"Cl0HsA88qlOk","q3cNiWUINpVd":"ggg5sbqf9wE5","QlHWFuDbZZ6a":"2Iv4F6pmDcoL","jqbo5eStgqqb":"596mnOn0HI03","mBet6hjtbKTZ":"cKog6ZHwYsh1","f67NHnd9dVe4":"Pb9iHLf8f8RO","ugC5qZLlooGa":"pOssEAMWSoUg","SQHMaw3ReqiO":"5ElYR4RsJ3Ff","VLNdlFh6BNqp":"YqdSTqbB3UAn","GvII2xJJJE2L":"o9NqAcRMdBlp","vuZBVePxeyLh":"PN5FAFvZvH0n","qdjD8RthPcDL":"X0cF14kCbIRV","AGqaAxVtULuG":"B86UPQnvjpqN","o25hnSoUoI5i":"b4QmyOwyGVRK","Xntg4YOe0zKb":"adXgRxaqOe6q","wShVxMPYXfDY":"FJOyJ2tCOW6t","gA5C1QKl7HEx":"InUday9ll817","5FDdM6ykYUpl":"zCSXMgjx9y3z","hpLoABk9BV2C":"7a5VEFeWuUnG","KxRoT5FTzwH4":"rQbLP5gqBeKf","PR4mIhry2SDR":"p0xhnUZqrrLf","QJQ10QjtktOG":"IQmw1vTLF0r4","U4BNkbhpHIuY":"tfyYPFJr2ecT","5kxfjfwBBr9h":"oB4CrGLTXRRm","aVJrJEAlst6m":"pGGvOgersOUn","TDoNasoOgoQZ":"VaZdjAlCENNI","KuCTON7vmeNH":"rQWOcz3UQiWC","fa7FWPzbyk2m":"AQWV83WgppP5","Lhd1xroXQOPG":"latDqLagkwa3","3bsi1C5Ew3P8":"qUGwZsfUDslf","9M39JvLpXE7F":"R3XBdTb4z7UY","AZHs5YkEAmSH":"u12Rw6OJduhe","9Y67S0X7JaKm":"HAmMUcMa3QRD","y61YV77teIUP":"i8BPHcAJq8ZP","dKBsZYc3wNSf":"xIoTjhi4BMSE","e6MlH2PFiEdw":"E6bKFM0gCdQF","sTJimRhyQjYZ":"YV3v1oUH5P8D","nw9dE9ioe4Cl":"NFETvvbWoC7X","KvF3SxCZormR":"4gCeiJKgvv2D","bc5wA91Aveux":"TUwPOov3xkZc","zllwPHnCGpVf":"CiYH1C7XJwT4","cRkXQyzgjoFf":"Z2H1rv7O0FZi","NQ4K96WCVJRn":"o5VtTcNhyzkf","GIFd6CZsGoaS":"lKlX4g8Y5UHY","JmI7g1emQ6YF":"wkqQ0atnTHcK","R9xxNflT7nvl":"x5SzJjP6sRJd","rBhagEQZnG9o":"beWdWBbMIaGP","23o7s0U9gKSe":"FlIKuMMKdqaK","otfIn78OkugJ":"HrfWZmbT5x1l","n12AAdmilptu":"cH2EAJdKn2hX","KuNh9qo9PYBV":"Sokj5YGbRoNo","mlHe7sBJXIs0":"nHmtIIrrH9G4","lyGDBWr2rakF":"sSItEJlcjahI","ITUkQbSkbl5F":"XUEVBb6w9N6r","1kV5YyZoiFB3":"H8sgp0MKdzvM","hVOqdJpvTCTP":"zbd3oCMHT6Is","oJHemQzR6IxS":"rHU7teCEH8RR","cUKq6JkAaxMW":"EjivUFEWiHlT","Joq6yq1JonDd":"MQRve65hqtaW","mMkjYhKGDOSI":"1UPQNQtuWwvF","37ke6Bqt1YYE":"Bi4DCeyarFyL","yraX1ujPW0Du":"q44oJjx6Spbe","UkGZHY3Qddvj":"W1gGRgdoPLM0","ONbWwrfNzivL":"RLK9JocGZhX3","duJDDkFOgPmD":"JA3pVnmLb4JR","gqbAOEX7jiu6":"fnxQFPzgzh0Z","Twyod804GB8c":"qULYhcH6zrcw","KHlHa7mnth2q":"sCmJL5jXa0rW","7USUxY9cLCF7":"FcR24YgB4jKr","muGpv5nWfEVB":"9Qfp2w0NIRJ6","SEYXb1j68yBR":"t0J8f4tv7WCQ","JVIu0VHuSfFq":"DtFoCMWwzkT8","25yuL7z9VFVd":"0a2TzB4WyVO8","KKWLZwS1DjrT":"KLK2W0599PqD","BUzZiuXtPZey":"aw9fw0ckQQxv","ZHVqI0GUvBH0":"40VRbo2geMqW","o3deG1lEhoeS":"2Q4jbGf8mC5J","BegbuhVlYD99":"fHDIKxaoKjuz","49lcC3Do3xaL":"IiQjWIQIHg1F","MMj14TgWlZfg":"LFJuUP8aYLjz","SSb8Qh1B9FKR":"U7ev4ufrZ13O","NHLS5YWz7US6":"OJ36DOgnzJNo","iEzXQa6PuaUY":"eqQ7QdxJHihX","bL5pY7IJMWbw":"dRkqXSirj6Pb","Iv9OK1RH1zfX":"fGs6oQ9JXkkH","vh6NGPRuvLGo":"YSEjhvMvHBNm","thVjIBusR3aI":"WogiSCgpGBeT","Q09ikfhCPa6j":"5AdtJJkdEjiH","PycIX4Th0DqK":"z22uf61Ggppr","SINefbp9PWuU":"1rzINHIf0JMJ","4a1E8qpyXMw9":"miSO0vDMzXmR","zGAbq9XBafZH":"hSUatVI7I0gr","zGVkZWgtyK0B":"Jtxucgbhtb6Z","oc0p4YfuiaJt":"NlzJYsUi8HUW","RWCJ1p64lmva":"e1a7tfomPNws","YZQbPaoB7d3R":"pyn4eYfWJqdP","ZkcMbT7xNdrh":"95F7PaNWwjCJ","LdvP4tj8DDrF":"auGmewBA3xM0","K4h7uuFJ6f6t":"2xxZm3xrnjnQ","seIwDpk5YhDO":"MEpmCBSXDrYQ","SPTUFSwOxIJ9":"YkpI1hiXZJZX","UBtofqquMG2T":"X4hvrtbdJw26","RYtjH9p7FrNZ":"14QoEVUssbY7","YlNXZitHHB2B":"Nk6sHDhCrLjq","JTjg5efYkK7p":"afOb0GgRZWGE","wCYDA5dstfUL":"Ae8t47bwPDFY","Qdwnildx7RYR":"zZi5OrZYBj3s","lFVJx6alfJRb":"Zo08PD36Ck14","Zz4VgOvPtam8":"TMgH6OW8Y1FF","3dQdO4OiYvuk":"NDNtcqMQgUJ7","oahmrWRyGpNi":"TPhmd7xmJJ4i","oRxxadQ2X3zs":"RvuKxxvIN0RS","K1WZXKhkuLDD":"r3xWuPA2oqzX","qkEFXBZ9qFfb":"MFEQRwUFHTrh","l1AKdwrcK260":"8WNhRxlSReiA","AXyXYlav6obH":"YYuq6FTKLoCg","asKViCFIn7LW":"lsYywaaAySj3","YR1z59gAh76O":"YpeOjcDYmINQ","OU1Tu5CfHbWd":"8V2rbmx7uvWf","hiGhH6Ah9TpB":"OO3B4h4F7s6q","A3gGQcImd2lC":"g5A7zaxg305D","lpsDVtiMGlF8":"5vCelZPzTsGF","db5fFzTnwD6F":"JHMf6ZZeJvwF","Bg3hRJENTeK3":"LVh55QzH1lFy","D1dNHYdB1LUj":"Yt68bJVdzLZr","hSLazEfA8iX9":"MUgZkfNoPWLB","MKdCjTeOtkGD":"avgx3aZzZpkp","l5aDiR0OqMBE":"278eyWwVZ43p","BoF9ncS8sgSz":"pE9kt2gYhMjc","VkZMchjW05cE":"d8LcTxWOiLUT","EXjaN1KLgRVx":"DebwV60KVLGw","XoIoDxBgYzpY":"i4ZIBRjF0WMK","5gdmlAMHe8hR":"Vm2DQLT7DTDY","crx5zsRzNf0U":"PGKoe3TUQI2n","zjZlggx3Eu15":"eNjV1BDmb00t","qMt2ZnFyXK9p":"ERQk0mED1Ir9","YqdUxaY8XNvz":"WIjXB6pROVdW","exrDXQyGpJnr":"2XsFU83XcavU","5YA5HBJG1w6c":"VAeTc7LUATRJ","w8knTa7rHWXE":"b1oGEZHET2Rm","TR6dogFwFeuw":"npIQ6lPk2oUW","LjIYS6FK1ET4":"Il4tjTbQcb5g","fczK4qspESH7":"RSGF6Dca6AFs","svlcS7slBoSe":"uIwOppFhVigw","TXm3I1tnUNTD":"ntF6hxQLfEJ2","zsG20H4sbi9d":"bEY3qiFijlTv","TuHf9FzjGBje":"9JtxpKCwQ65Z","SJRU5pSJo6uy":"qwUkqzLmk1DQ","kPV5zYlahzoJ":"hicFQtSpx6zl","J47xE9dRj57p":"La7l7uK2xfEp","PRiiIWNo3KbZ":"BkGjhmFJEt9p","feLG3fORr75P":"nkvyHbNI3bWE","cNo02UrEKMAU":"Rkmo6nPQPqJa","rVMEJzIC6pzL":"Q1UYf1sNb2HI","xEVyu0qI0Wr8":"ciXixB5lwGwA","ErjMIYHslAVK":"vnP95BFnPNDa","D1cV62AwRGIm":"H14s4uUeGyyS","S0t7o0hvgvpV":"X7CDbQnPKR9v","JHmumQJPJQLG":"CUMjKLV7KAE1","ZBIhx15GadnC":"jA9s3qhjYy99","aJAS9yUMrbmk":"HP3EObD5sExA","tdL2FdZ8FowA":"bzxNoeSFIKuS","Lue3yzY7GI8x":"B3ascrHYEt3Y","NqukETj2woTP":"itqy6XzVB6z2","058zs8U7R45S":"cm0OAD8sm58l","aSSx9FrtBJQH":"PX9kjYrNMjVg","yOzdi0zpDgb7":"4xxji44EFuRP","w9FvAcjKoAG9":"PLxP0tvR46gi","zVcqhkNoZCqS":"P5O8ycYapL0H","e8FATQxWokdf":"Lc3nSc5fDt4N","XlVIRCnG2q1w":"WrQfM6Br1ZIU","RWNQ4phWVoVi":"2EpwBi29QKLi","KjLUMe3mIqyd":"pq597zdgrWmM","OSXU14flRYoF":"0zBbPJJDZv1K","WpzFKfbO07c1":"myKMvAnDXRns","DeWtberwduLH":"f0OYstio9LS9","Phh2squwqXA5":"24h3Sn3ImlWe","pW193wWQyt2C":"44bWnKq4CGRM","so1gXHQkr5KH":"dmEuYYksFEJD","3g273TSVEZ8F":"n9yxrxZLyG6H","Gfeive8hrDZx":"JXEc1dTfUHm9","7Hbvo5jVeGtd":"rhX6jRjbnc4r","JJE7gVEiXBo7":"4ytZd7dY0Kiv","rLLodfH8M556":"tnrxy0WocQtf","2LGcMoHmxxOe":"hlEDVBpCraBj","a248vYdnuLrp":"1ikA4B4f9CIB","pHe7jNt2qqI8":"skfqZ714cC3D","dUcLLICZEOP8":"VMvruTwDT1PF","606P90JhUSPD":"xPqYKTaLhx6x","UCdoEr3m79mr":"enw9Pk5q3Swu","4gdmzHFfIg5n":"07wkx9Cp2M2a","w5Hix7QKCumT":"1XMoM5E1ZVEu","ISZCZ2g7xj7T":"wM8BqjY2HMNi","E7eRaAqS42TQ":"804vbmUP2bJu","sdztk3r3VTFk":"sXoROPjCrs4b","w2rZnkJZugVT":"OPiYkD2unQ5P","QDkls0F1vIf0":"JDdH1QjeQyKg","QVkcA7db0Wkk":"jPMqFQd4o3rY","4y26ONQgj5Pz":"zMvMEKofbCGC","Sf8NYz6sKpRM":"M7V7BY9q51G7","ppZOMyDt52dN":"XUMzt390zZ9O","e99xTc07Y52S":"fPdZTJqPJC8Z","9mucHTRCCyin":"JkJn97Xxa3uz","FncgbXfQvKZz":"wOYuDZzbfu87","4xJZLr1xgsku":"Azur68PnzeJ3","FlSn0tNiXaYX":"jeOMxGRslJsE","fLYy7JufBHO2":"IwrwDKQkopr1","ozpN7tutQGap":"MPLoFWpJRGBz","6hXIPrD9MYD0":"nu6f22kqAmZT","GoU7KmLQ4Oos":"RFiNnbddZRwP","lwDw1LzHwCBC":"eu8teoweOqIS","q1b9RLzpu2HA":"jHz36tDsxtOt","T5uhLTONTwIU":"bMGiK6SKaEIw","m41iWBQg1h3n":"VAgmvVTdcx4B","nUNJbSHNZoeb":"oWu5XT54NWl0","7BT722u61CnP":"ZoBRx7Zd2Lak","DqMuwBVyTvk6":"UTeORjBXGEj5","UW01I6VnBZYk":"t3S6ZcoIMr9V","cqiKhYmxqrPr":"HQGOfFxd4azT","BsYxkLm8w9Jg":"v48OAPK7Jt8R","ilwJeq3DygJ2":"gY7fPsmD7Zfx","dZZ0DgaPTcX0":"F47MNJW2oDQe","iEDGyjMgfAUP":"7u5GGhYmtuZP","TI4bo2i0WuvW":"UKtCaRxlYj4n","K67zbl6YVAx5":"Az7PslOwjpLn","tuDv0Aa1d3qw":"tfCV6KPAt8LK","1HgRYlgrK77Z":"oxCZIvnsV6vj","LVMyc3zHZZ95":"XZthNNrVnQyK","EUrfVZR0sTET":"KLb3IvAJqCx0","E6VvFMvP1Lrz":"iOuHSb2WJJHL","usOHOMJ1f1Az":"rLCcbZNc5eeZ","Lz5bZ4W0JzAN":"APITzzKRmZEX","OkWLZc8qgWLr":"j45gcpb1jLNd","9cmW0nSLdBrl":"eL0q7HAzmIGZ","ySGtbe28c9XI":"CsR4AKU4xrYF","4XIAzsFTZqU3":"KummCp8dOoSp","PFuM0K1s0NFj":"Sweo0g0a9XAm","hnhPqjjoPLun":"oyp78H7abIxq","Fub4EkRXz7Po":"TkW15azooHR1","vhBwGHewwQ3D":"cMoWGiGswF8s","QXO2q56IUML8":"CLLzrFzWW8VO","Nu7flhSn5k0A":"1hamtPhuCf2b","CKLdgxpN2zU4":"chojRP8lIWp2","wiqkeGYInaBk":"b5OHDt4sqO6R","cDCDeAuqI6sr":"BJmFUGDX5ODV","O8qgw32rdTBQ":"d3uajBTzINs7","yB7G64r3T1XT":"JOScbyGf1V9Q","h3WFRRNfTyIP":"G9mIW1ZUFC5B","wXuE8HKgf4Sh":"Hi72vqiUVwCa","vw0tqzrZ6m1L":"rROU2DG0QGoY","QF0gI1l3rjmM":"rEFbxjLxGHXK","kdMmNHPD1wHg":"eugLXjuR8GR2","Snovnt6OEOFq":"HbFf7fIzKtRK","kGvQrAl4WV9w":"IfFjUyBN16mP","0a0KrAwqFxQ1":"DnIQGdiQ71zw","A04w17PY1FIg":"tYv490Q8OS1g","v8IrnZuoMI6z":"pl22zlcazMYf","gMUIN371YYgU":"pKfISDKn1NyZ","ls89c1vLxbeS":"ZbaYNb7otNVN","XjBvoVBrxPxx":"M6dxXoUdO3RT","F7CuamZwjQ4D":"bO9yaMSEo8dC","34a7ex0KSqv9":"DsNFLXgQE0VT","9pprcfQp8Zvj":"MfV82LcJG5BG","IGv7Q1giLa0p":"zyK1qdmge9bq","TS2IaXVO7J9Y":"ZwqqlXKAPWea","nHS0lIwcxNj1":"O15UYCa9uN46","yOdqLRfuq4sI":"U8YUzDi9WpaB","9qCIBHbfaYWc":"6ytLVG9Q3GEY","FAyx2EuixihP":"TGvijmvmNCEo","t1ppVe6hNnYB":"dMehdhL5smkg","tKAbufKYvvhy":"rmSJ9GYwNHGm","tYQt4psPK4qT":"6Omxb9jmVYDs","0JJadPBQ2OL1":"dReGYin1dx8p","6micOtwDvU1A":"74NsxK29bYt3","suXIRLHa5lYY":"bjBAMegYWx6D","SCIxIWXbWxX6":"XSgm0YESnNj5","juwRjAo66XDV":"zBdqvtmw7qP7","5WSM7u8qXUAZ":"VPB3K93aciGx","2ihYnDGaddao":"owDv0xaO5hQ3","zbmP1lRzGSfl":"cUum5n39H7Iv","0aNedTfqdbWn":"lYny4xbSK8iC","9xe3yNxjCQ3I":"xSRuHYF8rjRz","d0pn2OS74F4S":"xs5xRdEiQrjH","DGXdMuPynbP8":"mkgbI3cNVf1Z","ANTsZhRaTPmE":"WWro7YcqhahQ","U3nOoG3d4tBW":"CYUEm1StqNWE","gTxbtpwACbJ9":"1JyMJ3rbZnQ4","WDzJ4PMCfcZv":"BLhYlfPZ8yxI","tCRLdADYqSOZ":"YgdzO7C3p3SA","F7GE9hFqaDbf":"q4rTiPgnKYmt","Oti2kgPXsDin":"4DKMHDOjR8Zz","qWwJ07yq8yeX":"E9cur1z5VhVK","gkAVia3Ng0Ds":"EQ03q26MXhZ4","5FuhNPUkI9pg":"B0ymYYKokYr5","SD7U4iwnUKm1":"33Ov5eQ93hNg","RMLo8GycqrWD":"MdYoKq9EiaAm","dfcLmgJT1jU8":"zaAaA6HwUy52","zUso12Cvt6Fo":"c8LpWsfhLaY7","yGUq00YTYec7":"umGs7eurvi3O","Ixd64L1zAjhM":"pHpTt6uxxuWT","mGreipd24kPl":"K4jCq2niYXZ0","vyRQI17ILXRE":"r9O8qJuPt0Jr","j3rpT9B9awQA":"MeYW08PhyGVY","D5NJhlsTiHlH":"eW7S5WnLGQo5","q4mkbdMi3Yf1":"DuOcCmAw8gVP","8DgiH1sjtIfZ":"9QKmCIL2oQFU","hDnaqtbsrfhW":"yie7CEtfmlS6","geH3PZkybuL5":"KzzVkgUBMe4M","jYzgDlsiS05f":"eIrKbSSIFbOp","jCfQQi0RBTpa":"fZLypZSYSn2f","K1G4iOguus9A":"VapQzHtu4QI4","klTRumAPTzfJ":"6XMtv8pwfmKt","Wwjcp9FeWBlk":"RTcagyBMa8X4","MoOn4Rz8kRh6":"kRtYkXdwbr9H","kUG71vfzMNrd":"oLfgr2DymRSW","9uNULAygH9hN":"VnR8TGDTREIc","1XgPBYSv8grH":"8bQKcYvmC3IQ","LQIxdP5GP60H":"CMXToNyuYaXO","G2ExblWfNHAR":"ptgHJ4CqIvhn","COliU1SuahFL":"XivfQw5u19lG","ttbeDq7taKfa":"MeSsJOXqrZTH","UKu5U7m6JAJx":"8UVBxscxWpXk","f1L9mcFtLsNh":"ZUxtoOtZvoS3","KpMNZvo2RXF2":"JwuDVSrGaunN","EVbAGQYUOwGA":"4iqZonX9Wn72","UZrpqvJrvsFg":"lWTHjG1qZvTP","4fBiWTovY73E":"T774eug7kojg","3aTG3AZKo6zG":"vUlFlOyjZSZ0","yti51sQgUYpI":"05YF8gDz7fDM","FQVBdr2i7BfU":"CFwEUE2RE7ND","RcrKZ16eaK5J":"HSxHCBgm11bd","FMK4BDH7NHBS":"LxavBFlSKTWV","ugR2NAFDwqBC":"bCVHxsnmxVIf","pmyKxectEJSq":"XfTN6sxG1gfo","I4KbRDdDIPKa":"NyoIvtl6ulla","L4iZfORUoN7C":"4CDU3fLCsnFJ","m4fH61kKh1fx":"23TwHVeccPD4","thKPXFI6LKbz":"dGL4M8DZWDQw","aq9AjGJQD5fB":"jXGIg05FJT6B","hlN5Qsivn0b6":"PcEEsq5TVKs5","LOwSMdRcBrRi":"8p5NiXESbKVw","LAULI0CA1SDN":"IdLcXhUORZYr","oLSYOa5pOF3Q":"2f5hqIkmtemd","RQrHaXozNCbU":"jZRlkAUqzOxU","Gjl5DSJx1VWc":"UC52kQwQ8hXV","jttcD5TUDZ6I":"TCvf3tgLCdCT","5yXxg3P2SxZm":"5ahg3av7yJ6r","pTUbuSMpaUW7":"hQ27rwL1BRJK","CGRtpd1APhw0":"3lIyJVbl3HPP","AqIA1s1drD3C":"Jw5jK7S0j3w4","nwsAzAIUi2rU":"FoGKe2QAXgcy","gp5nEo9IzBUq":"ghoW5ZDDMbxc","JZwSRJn3oGIk":"uvom2nqbr7da","pth9u4yoL9Eb":"vuq1RhUoELQp","SUp3nIazzu3m":"Gcu0Hb7CP730","j3DK0FGF2KSJ":"4OovfFVZreIW","tZJry0fqyeGg":"S3ClHUYhOHjK","gSevwvPjoA06":"qcvag1CveZoR","bfxU6aczfG6G":"whbiIL8Nf8OR","rUfDNJCDh1jK":"rOeqtEMpQYV8","8b6SlpIqtHhL":"x4bi9zbA0Zbq","3PE1zGJm4rSP":"tIYfxPnVodbv","6pNVAuD6M5xa":"4XLjKQ0B3mP4","9o6pyezlpCle":"O8Of4kbdqjtO","LC5IiiY1MzMz":"fzd10m0OShqP","oXAXNpgdEQwb":"kW35JZ1030Yt","cer4lnG9wb5z":"5qDktZdxHhIs","NFlVFKdJjNG2":"8FxKEqVe7DQr","RomQ19zSdLY7":"crDV4hHFtZb7","22MDK424AzkQ":"Odl3lbajkyen","WqxGrxWQ54XK":"GYV78rTTwc6N","aEaZhFwHvT6n":"o7i6y2I6bnI4","6aWYgWJ0mUMe":"uA93GJCeglQ1","3vRI0rsJc1SB":"fBkZbIlDHWWu","Qzp4NUeFXB61":"Vz8PlnsEcYpr","uTr5X1b4IHlk":"vEXb7t70F4yC","xXwuTLLHF4gx":"ZIALICAm9TFs","FkSPsz1WRia7":"5yTl7N87p36C","4M14isOq10Uj":"Vi7EgwNdLeRO","eKVhpYQI0apg":"0XjEEa5KOjHH","4Xxd5FKsQFUK":"46WdGFCPXq3I","faFSt7CDaGw3":"jgyxXdUETyu6","qy8OJfeK483R":"kKvAGdQFOCsL","QWn325C2SWXo":"YYvoMvtyM44e","Qg69PJ9pG7Cz":"XCLS16eC0JSL","OHTQQ4FRTY8O":"oQpLBx6IkNhP","PxXfvD8arYQo":"HmmChkYwuq5h","VzAlVPvBqtoj":"n2V0bWLBWljq","DqUzEHsBFOoV":"kr6QwLqvEzVR","uMmxqG6nyVWU":"Y0ZFTk0XYBLY","al8RusRwUSuf":"IDIiXmMwazqg","PVxXJtGbDhjS":"FgilvVhNYjPu","0XUUxLVGazJx":"4OAWThNmfhA3","hMQOj6Uwmofy":"Tck6k9bvLJ5O","Ey8iHIclaHAm":"zecJB14miVZ5","0hRCmw07u4qv":"GorhQwCgxJFB","Zus9Pxl2qzIb":"aOZzo9h5ZaBP","79O5GtzDfN2e":"9mVgsxatjkry","aaGeXLBpEPbh":"uONBldiVxKM7","rrg5WeEYKwkC":"TykDsz2WV4Eq","F10qqNA1trq2":"RsdDrucidZU2","c62jdE59eJl8":"i8nMvfSB5n85","nWKNE2yPJUm1":"H8Zc8TWcxOCP","QsEhnPKXS8xK":"nUPPf7x9Q3M5","ZRHKvBaS4u6J":"5kDxKSCRETE6","hXoFgPQRnYnb":"9uywcsA4kpZE","lHpzVYd57X8T":"ICHhjxD0CiEd","X8e2FlFrDN8v":"hKTSAL66q89f","SQnexRrjEBQ2":"vac2rBAmqlXI","ww8iR0wqiGIB":"ecoX7T5jc0yr","VXcCQGNK2DYN":"dP7EOcUJY5kF","fnY42WP6F2MC":"0o9oI2ZZjCxo","vLxd9CXoWxM8":"Kt5iWGc2ZloE","BwvIgC19j7gq":"HHrCnGzhGQL7","51bRuMxPZkvG":"htgT8qt6zFtu","hvIUtXJCGc4n":"9nGVO7tiLTrA","SExzj0tg2ZoC":"aRMWSxAoG1vV","2QgGuokzRTFP":"tQ9BrRwSC2QB","I5lVMeUO7w1t":"htUF4D22fnRb","49MJyY9YB3Kc":"rnfV8uYR4p8F","tuzbSqFVpOow":"QGSoXLojn0cA","QOwhEFevsTQt":"7aYxCayRUPVo","1LeYOp7k2NG3":"Po16jQ1fjvNF","CACFTKZ1YGZf":"AAjfm1MhxTEv","Klu7xyFPOF0B":"izur1C3z1FXk","KUXTJSO5lSyq":"T38ciDcrbcGH","T4hKD4lZFgM9":"ElbwaMqcmAtw","nPAtmgk8RqCx":"DpcCDqIGZ1p0","PxDD38ommS4U":"NZDIfY9Bi3sy","WirdMvrlyJ7E":"ww4WmVS647yk","1Y5aqPeKwC7V":"9bz6EnQo5nY3","54ZPxSsQncCf":"QHQib0Q9AE4d","RI0o0QLbpQBK":"kXDb1UEpKL48","XopH1tp8Kh9Q":"VA1L9LTMwEAP","tHhoEp5GjXPp":"TIP25BR9vvgS","bq7eRrOxZ4Ti":"93QmcOUOmPVG","BIj7VYYz5YcI":"ZDgQKluBOI19","EDlPiVynrotZ":"0MUZ2BRXczaO","tlEBkd2b2QY5":"Z5wqBC7kkSw7","D0rpJEKJivN8":"v0oHXDXIOmMY","OPxvwZkvAbkq":"eRLlIn0INRNN","crWen3Fd5gc0":"jq776iAyiZQE","bGOvrePQt1LX":"XzBLKMLTRS7s","JnWVc7xg6faJ":"9BCmcipqHIxd","acaiCRqAIuaD":"LdVkuwAWITuI","5qzgwaSzmwMq":"WSSe8XtUTyz9","6p6IALU84VgR":"uWMG2GjM4ZKv","JytGE15uZx5T":"ZkVnn1HLSHoq","USufgRMX3DXK":"wjyY6xFEam98","bJMKLFIhtMOq":"uQrBCj0zLC9y","00vFFdjCrAu6":"CpoRaEktMWIe","d0FMquPmovMm":"er6smlYphd2g","mswGOPdP5Sel":"yga1BBSTp0cq","1jeFAide4zbx":"6dSRLFEUVz06","p44E4qJ2o5Ul":"kQBg6ErOv37j","YDZQ7PsNjvEy":"86NS0geDpQGT","5FZkPHTeiOXA":"wuAIaSzAKMB7","TGGPzoRvOpXl":"Wj9cLg1XIpYI","Rj2cKYN5fH8c":"Z9v1RzDmLuw1","5UsVkitU7JHT":"fjYRUO5jYGBk","FQEMuDD17oQE":"SfQ31nG9zNYI","E2Ia2bvQUtII":"Ho6ngVysBkVR","iRC5MOcPZ7pt":"1BZ8sLztM88j","sIP66lINIF5z":"pE4KdDTTj9UL","H99P60DTxYOQ":"UV2R1KcWXlWK","zC8aycnHKaBq":"lFmP1OUqi6dU","bHzfMBxe4k2O":"yqlxtVlJuLLh","yD8cGz0hkfGk":"tPW1Suv1Y0Dt","TQagMa1RIX6r":"R75mOFjMYFQF","NgTcsipSx6S3":"VhunJsGohVtC","4T62nmg8CTtY":"4E4jmOvtzoEB","fSF2GIfsydNu":"xmDZay86fmM1","yKyRu7FWL33y":"ZWtTWWY9IY9U","Z7HaakurbzEz":"QVQFUi9G1oAj","rQjeqL7aD8Fc":"9TqoaCM2f9Lv","HWXN9Z5DfEWb":"hxphmi68lWc7","NrbMapW5jQ3r":"nDmmmPI6x4fP","YvqQjOzPrilm":"S53fH4ojDdPn","sGVgojiqpDCA":"NoBFQLdWpUg9","KaLwHOpfRWsP":"n7JpQKYPfOT4","HsFtQEJ5nC7c":"ceAD5oihnDED","7vC5vrUxfqKq":"OHhVTE3PYIFh","9moQLN8O66G8":"V5Czc0Uyu8UF","Ch9Al3rmwU0l":"dHfiUJGTFW2j","jUaBSys1y5p2":"pEnxvGVPp2Yz","SvajSE9e73O9":"JMiiEQZ2BNzF","1zuVGE5bWS7s":"1BQXA0lyW2lV","MmCpZK0F13J5":"aS9yYIHhbptU","a5hh6h2ttBbp":"DN3nYtmyrrh6","Li1w7zMrUa0V":"TCXrATOcyV6Z","UQo0BzfddvIM":"o4hFdquHWMDS","3t2eL6zVwzNI":"fQlliLsGyRA7","uqi27HSi4FSB":"JPCUP1wfd0Su","nTCXEp0Wynqm":"OqDwh1IllagQ","0rRGVXZgm4Wg":"gtDZL992Owbo","ns167QOaUG7z":"NONKpQvY2cjS","XyfoeTrveNjW":"IheLVlUcU5hR","ZNk2oNuXXt9t":"mbDXWzWvcJmM","QrudZNfTNVVS":"LVRarD7ywM5F","fpZmn21Pc24b":"7c39bfT2Xq7L","wHt2UXGxr0A8":"8qtpxUFOnFtH","nkrWwDL6Fl5Y":"BUBnFTp74zu4","Lp5CwCFziRrI":"fuWFew1vdy44","wfRWgYn7jdZC":"qOB1UMi33aF4","Qq7lelPYSnfx":"KmFcOhW7nxj7","L8AWr3TOqnui":"saDMUM9qjeaD","iBb90BXmY49D":"AiPSXlduF0FF","55kLDhgSNVdm":"NhqG8QIXuOzO","7sQY0M7YkC1r":"GOfyOjaiIyJ3","7xa5u9mvqXEh":"6ByBdYldpg9c","u38fQMQF4WFO":"WgjhXl44ajby","XTqH35S7UBDT":"kAswdpGK0tGx","ghz9170yH7oz":"OQPEDHqFgyU3","zGzF7vU4R1nD":"QQ7tER04fpkF","j2wcmq3dkvvG":"0B596dG7puOF","YxXW2rnAKNRx":"XwF22a9JKG2P","pfnyJf0fdW2L":"HPsym3IkXfBr","7QYVxEB7kZol":"kOei88Vqy0gW","f1pfgPd497Mf":"GTowwtoxIV9O","B7jBfk0ilUUC":"5RPxqqG0GiW6","V43owPWicjVY":"yW7UHJsXGhmK","EyLAF4kRi2vj":"A2xAkjKda12Z","Ti7fhn0CvOXi":"vuEnEpt4BLGY","qlRiRdrLpSsb":"fQYgK4oWeGA5","vyjE4topHAEV":"onp7nWXiVVST","pQV1zS8C279u":"tnC0DhYHHKad","axfRtdcQSw8e":"jndBBWTMazyG","BYlXjdiKpTaO":"Eqm68MYQXuO4","58dl7EF5ryxu":"Z87Sm9qxSHjh","A9QDwoe4OozB":"bnW8ROwCs6mf","ByoYvaqOBroI":"8zdVrYHHn00O","JMhp6oyTyAmV":"w6y6KBgTHKTJ","T5ovxXFbEPtU":"I91YnBGT33lL","cJYhCyBpwQzr":"ZX3mDQKLGyyn","FA4zA0zOheG8":"sVhw5c5pQxN4","jw8AHO26XrtZ":"W8tEnUXfhjIO","FNe0CFzOIHsb":"GnnbGuZN1E11","MEfvSuENjaE1":"VdHBBFen0yL5","3XwHpkqSQqtX":"SLXCyTqedpBq","Tso8H6uJZOTS":"OCMtw1isJWFp","KbMLkPCsbbHA":"w2jvgI3lFugI","55XtdE2ZQwss":"PWfLW56UN325","UL3VIQZp5RJg":"mnjBd1QG0KSt","6wGOUhb20nR8":"ZAxPAdijddiu","xGXZAZFQJwmt":"PSggauE2j5SZ","zPSJ0hED7gO4":"Bso013eF92wH","NTi3QyZC96Ag":"To3dbBVIvXvz","rTHnWQXZi2ln":"92n0YytiKxEK","0sd0nC64Sks2":"9NMf9oNJC42h","6LbjeYdZx35L":"UlvzY9gHmv5C","5S7xNCM1BWfh":"C2lm0fo2Hu6W","jw1LOoaHEpeg":"rjjV6UQkoV0l","gHfUdXscYr6K":"7y6QGkgYt4cC","PveJk4NtjwWm":"OQiAxkll42Ed","z9dM1YvX2ptU":"BMYna8K2xuK0","RZiPaeB79Bxl":"9Fc1r5Y9qlFJ","0izFJMWDqdMR":"Zv3TQRnKNGwX","QaKHd2XWBdZY":"DYkwPrOdQ3AZ","ElNtzyyXeBZm":"GDhc7zARCkgd","SpcalYPgSsco":"66LtjDvv935T","5fkWoaxU54Xp":"Av41aXYZiwvk","wNfS8PLGBhb7":"VmasUvXjc57j","71TV0qqaikrj":"sYuipY11A7IN","144cJl02vuL2":"JqFui9cZcxCz","58oHRxWaVUiA":"IviITohgK6J4","o9fWIzQVOBZ2":"FtJXsgvZZu2S","ww50cuPx39bW":"n53lFEpQ6a2C","iZj8JQxWQGev":"TeZvX8IRx7UQ","TUQ4wLN9q3IS":"2nsz9k0Nm1Oc","8nk7PgEWUes4":"8SJjCZLdQslc","BO0OYhlXvYHM":"9ya04vqHEfXh","DiaDJCPKdNTI":"BgR6bEvpDBPw","89pZvD0U5bE7":"opREpGIAtFTW","DlNNZyaUVWUR":"J9JhsMQJxcRs","lUzjBv2DM7fj":"UyAUtcjHicYx","npmYiy2r5sZy":"pQcXJKXxqIv8","gJyUCFVnbfxw":"OfsQqWH56K2N","3BBmOVIJAYXf":"z60GLWKsJXht","U8LXxpHK6NPs":"lf9mpRMlBAOV","7rmaxjAhsbjP":"0w6upm0Ps6Yj","6JegwocojCsE":"QBwilzWFj9yo","Q2HMtZImWOb4":"FjaQp2gwGVcD","qkJnXpj8NeLa":"yYqCBgbrCtCL","2hvAyd0ZgDuf":"HiTJZCIRuqHq","NljYYXiR3Pr4":"LAJHVZHNWq8A","Rr7VCjobP03C":"VbmAfZHDTsh4","LAjEgIpCIp4n":"elEaAUL6pP9T","cNJoypinO3iO":"quDX0r2bzSKW","asI3Vm6Nb4BQ":"oxpCp7vZOM3N","rLYYJZlrtKjv":"pkeqC3hX9cKE","H4N5xV3FmYta":"jusS34zNz0ny","GMCqBuPsMxTH":"FL6k6c6k6mbF","BnGQgGGwVXLS":"BV7AtT1lsB3J","8vG0rDIyOe46":"ILZDxIOJYDP6","WhbFL0xVIOmV":"Vm4THjOnB02q","YbR7H0UNfvcd":"FCWTmzWNERPJ","R2vkL9RiKMMY":"cmnPqLEDfAi1","vxfchGLi0Zaj":"wNpFi0dVgNFj","X6QfG70sdgw4":"ST4Iqg3lIbsu","Kh1TewUpXdMk":"faWJq0Y5lRME","MZJRuhRpJGiW":"YIO7jMNBZu89","1xcCQy9HoP3G":"NDzPXUCAl6Lw","APShXCFa11HP":"wzn0vCwiESEE","gTG0CUjPQiIJ":"LmABLQJVGq46","qjtqxfRRFbyV":"lRllet8k1PKb","qcIU2M1lUDmV":"lxp1RQF3hrMt","Bu7chtyCMQoC":"dMPPz8Qd4ojw","6KuAMSxmzqsM":"OU5SYXmjsnB4","JOL5IOLCglXy":"KfU5j0sjxCGD","1h9U0kPxrk6N":"fpAKb1l5zKWT","HX0ZEVAzrxjB":"qcy4w9aFNiK7","K4SaAJKMnnir":"LJyJ4UFpnjo6","QGVK3KOCBniI":"6tMqvJ3uhBnB","n7dZmokMhoY5":"zIKG5LoRVoDN","dXkofH7N4luS":"PxvZ6ARVhHmi","rTJZLCAgtilq":"gqhs2kuL4Hai","4aaxWFGTqYjX":"GPE9YALL9h78","rw913ZyRQrOh":"pGh8WRgrg5oD","kgHSHWghAvhy":"lbb8kkJELT8o","8Gwvv9PqRdie":"HmPbWkv3ByOU","KE7Wlj3MqT9M":"WbVc2Ca4AXVA","CO06Z3wftkNv":"z8OrSLQBrlvi","x8DdeXDnYc5d":"4fmrLqYGUwrK","L5FHwLA7HlLs":"lSpwbJRpOsDm","WdRGEs2KV9gf":"LpyAzbdE01kw","NXAGgLwkq2xC":"Lnmb00p5IFQE","mnJnyHpFwrEs":"ttn2w5WRcvoM","ojbHnWqhAJhP":"rw2kGTyv69EH","v3wlCJVIbu5W":"xoo2VFKXNtIw","FS9mKCnnywC9":"pIBI3bKOUQPk","Ey4tCdhcUr62":"BWBdk6M69yQR","Eh2u0HA3Mi2R":"PIuGoACrF61Q","THMEr4OYLtwL":"EJ4xXgb5hux9","O2tHUh0SJ86J":"fs3YMJnTNyBJ","I3BgRcCWvNMF":"38i9IfcTNhBv","PNAOqbmRN9zY":"f5QEBkSggUmo","vkYWtqW2jszj":"lk2eacEL5Sp2","rxJ8c98xczmp":"XkepfRd0Fzu4","FFH38GdLxA2v":"RqHdGSKfrYRj","RZpumHOFC0JJ":"jzP24BsIRvMY","808ejXW8IFXM":"tq05p4K1WKD2","vD5POusqruo7":"tHMMgWfyZ6aI","hd3kja0NQDVp":"1D7vq7azOKSg","jH7SrGafmZRh":"fZdmC4UGxZAX","k2zieMcSAnC9":"7cXp60EsRkl7","8Gowrwn3Ynqk":"2csrny1vGhy3","KChpMWT8OSHm":"kWKhwZUJyucn","brFNaoC9ZqBM":"xCwm3KRN8XnT","nOt1eQsp1xjJ":"yUHUjuwlKnz5","YVXCVBk0G9H7":"ZPzGarqQzIvD","nE3LnbWm1sKl":"I62CkiMjT0DC","a1vzARlCwC4U":"mbChJFP9mip8","O5ieuYnmRLk0":"ONzUkRtKciGE","62ModneIG2T5":"Cr8HOpdApgmg","QNeWERxyVRGR":"In9gxX0wdhUv","R98wlsMCqPhi":"zwM7UZlTfWp6","jlw0p6faBhA4":"EnGKIu9HcROA","JuFv42cgvQjF":"fk7sUCbDMiG4","1tpKoESreZeW":"Giyw84W1aomR","er2craQAa1C7":"extvPQQqeMd6","6EtSouQb9JA1":"63iUr3QPRg8w","jZR8C8459nF6":"VjNq6o6bIhyW","TESykHtUJCD8":"CNALxDOglgM4","pcDn99YKfIFD":"6iSYf2ketYet","rrMZH7gI9Wio":"HEoKeOgfdCjs","AgJOKihjG5J6":"5CePgpZzgzW8","fck6eWLsmpb7":"bvFR1M2qdWz1","nb9lGXPGiLJ7":"fO3ZU88KQgn2","1st8csK7CNQ2":"HpPw0hj0lWSK","SxYraFLEb3hG":"JRs8klvoHh3H","Fvf2vnwlFq13":"M7ZXlTxYg7BL","BwNg5uBGRFnE":"IiAa49MM44dw","XVm00DIWQgXM":"59NUFHtc56ee","JZwconiV8otx":"bpSvH5itzyPZ","OWJWBYM2hUvu":"QCD72Uiy2t8s","FaZIDm9roLpt":"j0mQba8NiaWu","Yne1iRADb02a":"WIKAsssAD1p7","Ovcpb2T9ShBv":"TOq0l9IPzn8U","DxAhpL4XmFfI":"QGQ2r0x2ygLn","1XaSfEaUUPcV":"OQU9J6bdWbog","ztexp8fyEs74":"EKc6glACcrFX","ksBTyBcbS9p6":"QwECozKLf9Za","VxzAE1wUaZNL":"KtND87b9dg7e","TVYd2aKOI9A3":"i6VBvz61i4Kg","vUR8QQbgV1EL":"XvOtwvW1f7Hp","SjohE17omKIl":"4iREK28B4VY1","EwVQJqhdYb54":"mn2jGAl0njgI","YBus5CrMndkZ":"9ZN81h9zEijj","MW24NoGe4aXE":"W8VDfdcM5Q00","H7zHPINNLe7a":"LoGJdsb6TvOy","QvZBB3GISrK6":"r0HmMol0wOt9","nuCm9RciRlNA":"4e2TQgqwNppx","zGGeBcDpdt6I":"oOpatIKu7Lfa","Kmur7r3LflGS":"FvGm86PWX0eu","zV126B5ll7T6":"omWwDAusds4s","wPt1mwZ0BIHp":"rd1F5ddFE6CB","Y9fFywwu8oo7":"nwqlmg9oBKg7","iU5bliDaoHrP":"t56MSO07JSW3","7SoYOmT3hSSI":"oX7YJw867lIx","VHTdJbO7hski":"sTsPDIG4qJj6","FokESRanatH1":"5E7i2vMuo8VU","lMd0FBtuTaaA":"jwDVDcijRPcO","EgNv9oBK2uLT":"FYst8jZbtTLG","VQqXbPySO6Uh":"yDvmQyqVgRon","7i75eSlTApSq":"vA8SMfp6t8Pv","9qyZILhftfY4":"fKWI8PVDRvBs","667tCeswUTHj":"ysWDmU4ZVeES","QqPKiRqnQKd0":"TY1VXxhecij8","8IwfuN2cbPBl":"D2Mwnz6UsXx3","8O8qLYSX77RN":"Lut6gvCPG5tU","rdye8bBw9jwf":"IR46EkJu3KN2","9uaLv9zGyTU7":"a3wfoaXSLvje","MtHnfU1NcM05":"LH2yJTA8oTDF","FoAWuJSPjsjK":"chMnqnGrGRyk","9p9P6dkE26QA":"BFXvcHxDIcIW","wKdVtlN47Quz":"rQD6ZcXjLCwm","VIMtU5ZgtsDn":"zCPCU3YGvrX9","0O9tJvqeMJlX":"ab75HrwkDIrR","LeQTJx6Hwxvf":"zxsjTdwQyO4f","WZpnRSJ5pCQH":"lDo5aY56xffm","GvZSaEV5BKA0":"tGOX25DPJbEA","3mBU1dOwvUYV":"1rJJml63YIKV","Ob75GYmT3MkG":"NAUXPpUhd7VN","E9zGL9lc8jlX":"1AQYazOcW2Cp","mIJ4Wdf16SZ5":"KnHDsic797fs","AjgrmkvhMpZk":"P1vW1hx94Mjt","Sf1knMCS8I9l":"rp2o4ZhUAwI6","wmO1AxRK8E0e":"eQhi5lZa7qKV","XwJm3uDHdlDA":"JXKPkin5ptx3","pKtBnTtuzoj6":"OtLz6G07dWWY","xwbKwsAxS4nd":"25B1wJ9nSRKt","NuWNaEMBGk7N":"WcqVCXplA11S","AEQLxLCQNUYo":"8EJ6l76eu5T6","gC3wcaZERfFx":"EAIvxeVpFsKW","sZGJfb6Lz18v":"trqojv5RayuG","aCQwyNR09zUp":"QKYKaepwoEfp","C7iYkV5SHjsg":"dvsw9h1IvdoS","Z7nhuhTje9Hm":"iekSVII9Oagn","2kfkwSj3W7Eh":"OFSTUNI1iiOE","UV0dNqlNrGlC":"uzA4FugJm9zV","UxhNIF8jPUK3":"ukJ36OB2OfmU","Aucldo7FZpPa":"2atTX5TO5txu","ipgN8edxqY6J":"TxWF1GGNSx1y","Jvc5CI5UJzhX":"q5N1V47xk5Ro","tlLHGAWUiygY":"Gy0DQmmmQR5Z","eMdEAzw7rdNG":"wVfBA1ANevLC","CtwrUaTN7rIE":"9kap8aN5vnt3","kWM9uUmt5SsR":"KACpAKtPXkAO","nw1n5DUtRXAM":"ZSiVq8pa4H0a","J8LwmOK5dS7o":"KZXdjtgBBvIM","FTijFEi05g7c":"sQDDEyO206C2","FgMD8hc14pg4":"lnqteuxqv6uy","jpaE2rzHgzK9":"KoQptFEcOp0a","G4YHn1WWrXXU":"yoEWXkCeFmpD","PAu952sDBbVy":"5oFZ7nwJizxM","90cVKabjsbNz":"ThbwEd04ICZZ","vf2a7BhRdssm":"pnTPlfw1oSzV","LJKNRf2m2XSq":"l5MYuDADh70u","hhJGJbPM3xGd":"VkRwMWp3kNdC","EFEunOcXwpoe":"C782WbHZkTEC","m30YpyGFfue2":"10qNUkT2V50B","yPmIeIU3DbT3":"4XE40LWA3jUT","JKsQTJkTspG3":"6khzmurkrjtI","2v8YrAFxd5ra":"hhxCXPhw0zxw","TEemitdM3Rno":"hM4jFcQ6La50","4LoZO3cQLpsU":"UBTelJbhxDZA","L1qTeLLt1qvG":"rync2R6whSmo","PXBWbtoDihm2":"Fedi3JDXG4LZ","ewDuIkdS5Wrg":"HGuAbgVftxQz","6vCuBed8iZ77":"qJoHzO0q104Y","1lsMuJsaEtQn":"XkSWRGOGszFy","3G3gh46YyjIf":"OUCIVNbx5Wqg","Wl1nBngCq6Zg":"ugE6yUtYbn9e","Obw5S2bMHRuU":"lSeSh7w95yT1","egIkBg29admQ":"io1ZwqzclDii","sAcTQkHbs32b":"lfHOlP7eMcHa","jLRQhEEOef72":"aG3aEsPd86v9","a57qDAc1vt6N":"ECekR6bgsD3f","WLibYsCCnfiG":"RdyZMix5Z0Ja","62kOqBVEJIGI":"9ubBtABGYbRH","IAjxyGC70idn":"Ncu5ibxL5NZg","KWCMK8DoCPyO":"TbXb9UFmQsjx","iSqw3IeyhGYK":"XnHgCv8QTiiC","yaIifYutADsJ":"zJDsCQ5OKp3S","bpBp99syPnAg":"59jShkTDu5pm","tBSndAQhSS1X":"EzZnO9dWZ0Dv","TQVYrlIQzHVE":"DoGS97mO5DmT","gY34vHn94I60":"kMATro48NBrA","cOlFUX1fJS76":"sTRz8C50JyEx","glpmN99Pyis6":"2WFhAqikP2BN","79HaPtGbqDEI":"PotWJpVrddM9","Ldhg95dT76f4":"ZjyGwx09np1w","toKxXqQ38rxH":"R9nIJkrKloxD","16kJYBdTaX5P":"jCPJ7XpfS8Qr","e5aFvpFvuW3i":"wmSGEoEMFhh1","ZcOuQwiT6AFW":"It01rviJHslz","5YM0SBa61f5L":"LaWP3smWsB0f","hUMNXgYTYwgN":"6hoaTYSk5Zws","X4GJGyQHDl0w":"9ianPOJ4tDUr","eB0KcUX433LL":"dE5a1F6FsjHD","jmezhmBPSy5u":"vouzjhdvfiO3","CEtIOQ26RTvP":"BNI97OuMRNYz","JrpbtON5TTrS":"gUFSeZlZjjKV","wq9s6olk0X4L":"s4oCt5av3oBr","YTtpkOWx9ygn":"ESRfeZOf2M4i","b6aZ7ZEqUNUM":"e9pWHvOlMxxd","b14RKe2rkgYZ":"zvOLNDV34qr0","c0ZZkmFgkSAx":"q0XzVHNvIPYO","cV984S8LuKs2":"2LOLWNbQrIXr","TfP8mpGY6uf8":"IAtGvsBbtut4","9LhkwAX9S4c9":"I1XFXGPFmrTN","0jT1ENFoPYtF":"MiNf6pXZKaZR","tLpjOQLSmiBY":"lxOPMolYeWgA","bT8Ig0sYQIxI":"rgySzkKkfR1z","Gsy0U0V2fpqi":"Sp4QNHA45mUj","hV7ijczfWpKA":"zMPVoridCQC1","6NtrN9elszvn":"8bmXL0UKPH4K","6UCRWt8CWmRd":"18zmMBthMw0a","QDGy4iFDQmTR":"D9yhDZak1xCv","sKYCl6IprIyE":"LHUt4RfJroI5","XDsL2IsnBSRj":"hQw7mwx5tZks","1Qd3caWyscKL":"MRWY1Hrgtynf","RQTDLEF9K8Z8":"Qf5vlsfSpDCx","Oj5ytLcVXXED":"y3lZBUEznjUJ","9NJxnmlpe0qd":"EYGmYghRoK5d","BUvjxE1gpU0Q":"eX2pmZ1VvVEk","lZnHSKvBgO9L":"lJ5hEChPxOw0","ClNCDx36fhQY":"tGcrC6RHUn90","xi8gUnys8MKd":"UTkBhk8oSHRp","yCuPRWpsgoW7":"Pg75uycl0RNV","R5gWlaRMN543":"72l0dLiHj4E7","SdyGbLZzA37o":"eoSrwTgWruBb","1ElWFAgSufn5":"DNwOAALJKLzc","liddpSgQZJOX":"wpQ3a1VVggTv","s6DdbQ9ZdYWE":"gd5KU8UzZfyI","aiAkLOqyNz1l":"Vs4V6FBL7fKu","ULeRA8DVKFsX":"qWEnvTl08JuR","1ptHuwJ4NY7H":"YbSdEJi5Tomk","68CFFGSoVsyI":"L0VbKbnsi4Sp","U4rz14HRy9BK":"X2NDQuPedVz7","rn96nhCW4jYL":"MKgujywb1mAs","X1fSAf7DVk39":"fHH7Ki6dQoRN","NLogzLZOoUkm":"fmFJ0kirGEhp","VMyOyDurSAwv":"HkfPNyb8r751","xAWTyk5GC61F":"r5U8Gylvvb1h","LBwSa730l4dI":"1YmA3WqQKmUo","ssd1EsFragzA":"9z0ZfxRzaqd6","pUPuCeJ7u1hm":"NRLiWtRS8Fli","n1MSoSanRwbb":"sCQ6iY2Rcffb","tcrHfHxMVOVN":"r87adgLjybav","cKWoP3UYujad":"Hyb3wxttSPiQ","JUWgYDQKKhp7":"psRJB57ywBYj","hfKjV3KeQsp0":"mFvOwO8Ph8ou","nDqqZMFjxJwQ":"2WcaKGqU867X","zkPNe7WgIu08":"GTVIBlNsos3e","4HBhEVSXy7XM":"m3uFHhz9nLPv","yppYieT7iavw":"C1E51GSf2Q2i","qbWZo0ypfQfZ":"aJBmzeykSaQ3","wMc2shMc5cXW":"FeFBtTdajulN","bbODMjpEV7DI":"WrUpBXufhFrC","cNDQ0W4gIKfm":"Go4nyE0FNy3E","bkqpMshc0hiI":"1x9HUcuAsRAu","CbNZlwGOjcF3":"u7GW46cGgFIy","IxxXAEqyRn7J":"VGE4MrFcRX4h","IeY5pTuMPixz":"bVRj4MYjTS06","huOeiB7eJsI9":"SGwu1MjhwLZf","GTa7HPLFClMb":"yd54TY8bHiND","PAVJJ8rGaNG0":"WkpL76TVszai","yETpRRiWyDIs":"5IppFnucZdJU","3WckC5vhiFOX":"4UNhEed0mVNx","4pcOsjF7hsxA":"IAwqxUvi5eAA","4i8w5t99UPwT":"64BfzZ94SyDE","SMYbOXXGdRYv":"xoTSlNZTQHA0","7IwYG18KEoLC":"6dxQKZKzo6N2","oAqOwRDqtver":"0zcNw9JhmQ2Z","EIcJeSjsGXAt":"HPlPuNvb444j","1wm125D6ljul":"uvu2op9CWhzr","NtHeHxo14VJg":"NvJuMMfbjnzk","9DOPmPvy76nB":"LuFyfd2A8biV","jL3RmvGRlrrM":"yAgcUJwbJPJQ","KGqRkFlRTuo9":"7l97MCaHMiGC","lAa00Fqnazmd":"S5I6x8u0DwaA","Ngdr7npJGxt5":"X7JooC1YFpGt","8uiAjcZlg8gj":"pGTCQEejxNtD","26fVEoeCYDC6":"UvRe4k59Ea82","qZeE9QQkUECa":"tNWX8NvKVsKS","GvXgurIQKBJt":"CdngaEL5T5FH","vOL6utViIYOH":"GnW1ryPhKUHb","Z3Ne3VHAGMT5":"i7JmoTYYXx9D","tjDZb0X2FQm3":"1TRxPOQE8BtK","XFBNgNKGRK5x":"dRZcL4QuFjz8","zA1HxcpLNWcV":"Vjo6gkHvWcSc","Y6lRZyCjZGiy":"sBjvGWCUqGfW","8PLes6KkmEi6":"rmTE4YtP3teV","TXbFXOwfjvuQ":"by9ijmJMLpvk","uiOrkEip4HO1":"onhypQiWKn5l","WsxqUniD5WoI":"IwTGIJpUEhvl","Bk760kTIOydS":"Rian3nHznAzd","bZADHlyIyIn4":"nNAZ4e8WmT91","8m2wui7wA0Md":"1opNlIFQqt53","8XK3n4g8wjSa":"QQon0lzQ9E5X","PrVXgoOUfvXe":"Ibrd5eJERWnE","haIOIE2ChraA":"pZYmzs187jaB","ZDCM1auUc6a0":"vH2egBwyTM8k","qFqb3A4rXd5C":"inlJMPosRxYm","CxvLGKtLcegp":"6NmuxqIHgZCU","zEri219obdhW":"UyTfk4V9HpVx","7twdxwzwBDrD":"aSoupQLtpnqZ","aGTliSY9i6HW":"v8wxwPNy3k5p","ZhFRzn92dX0k":"XoCC0ze35I6q","gXBt0A0IfdfC":"we7gRNARzNZQ","nCyHgZoc3VGG":"ILEGiF1wipmL","16B2pxhGKNzu":"9O8VCnlIPltU","ZjgY1SuazJhw":"Q1IP8fJD1sMA","gUyANDVTOlsr":"dFBta0LcH7TC","nZeSNqtd7tA4":"blV0JaOT4Pyh","uN66zivJ8lS4":"ZdV1lrfIUbOr","tlporJLCW17F":"KocXlziRa2il","yxp5cNWLTR3u":"KE0uieInJUcY","JXm7m3MSEl6j":"gmxHzOpYTXdE","EfHc4Im2s7WV":"lmlgCxWheYCF","bbPdGhcdilHi":"InfEzO9U9dR7","nHa9JpNRmap0":"fxaJXGZxhnpb","vciiO7ttD7h3":"QZJoUXhnTY8Q","hpTSsdVzi7zb":"pWIXREAkJtpt","IfyT7DkNLSKS":"XtaPzCK7CTHA","316xpWxZs9Vo":"KSxuXNBm3rGE","yHT0ByHB2oYw":"cjH9KE77KmZ5","2CU6YsbVmCuj":"CLt4KGwdQfIl","dLXKimg8MyxP":"PGcNxaTQQNBr","MAT64u3PDJj3":"GAjUl2PjfwnW","fi16EYqZL8Zd":"yVlFkpK4Ut5T","h0x109p86CBT":"x6r5DP7KMIjq","jp4CmBwyWW3e":"L5UPRVIasyQB","9TZxcb0YOMtV":"Tk2osmhS6ucF","MXgHwNbW7iy1":"v1EMeECop8hk","snCPzKLZIFBQ":"UYgVu5HsGUrl","al0SLNftkHKC":"fWVkcwyfkZC3","WVi4LxuI4wY5":"rRnixEFAPVwS","U1Dsrf6CEs1T":"PcpY6fCNmdqG","YMJH6ceYevdu":"wjzctqZbLERx","2okwyCbTyh1F":"DDjHKckZ02kY","KPQ8AG8JYZqD":"twGwzf8mqX4E","qYKUrcJ1TDp7":"bgFARURhl4qT","d79bNEpNxQqd":"71cb804qMKGi","RdEiiVHag7eT":"4SfTMYZlvxts","GcXmLUR9E0j7":"TCYBsKRFP1uz","tiR6gMLV7qi7":"tTCFvd85xFcK","oxJdBLV3sQxj":"QrRjV7unDXuc","aakzRsE62ne6":"9dh0iQhAzdkI","To9ejDIvKrxB":"wINwvCmb8nnr","EObuLc1xzhGr":"8zB7f8hWkhUL","fR0lGFq2XFu7":"nKn7Us2zeSQm","9DmKiYw5D4Wh":"2RNZW00Uskjf","nueFV5rDY4rx":"MqrB6UeBhgHV","akyxAZ1wfw0x":"M503GtrLY9Gl","ZCdmRJV1WmAp":"9EJ6zxN6mLrk","pwHv9SCVqy6Q":"DavKuCHP2BRA","gJ6dqigoDIHw":"RE4vkmR0KhOF","J6gR0yZROLps":"fcfPpwVqSVQE","HoCbeAm6wmK7":"GnwBzvh8p5C8","8ByYN9JaCHHZ":"BmYMZDQLusjD","tY9dGsXH1T8l":"iAkeKIftyn4r","LJbzP2FuS1g2":"bttD8lWNts4Q","lqfAFjAR9I0g":"JCm3f4NZfhNA","O6J2NmjE1Cs0":"gqURe72lzffX","lhbbvYEs9OnH":"9Rg3aapiXBSw","BhTszvpFDrsx":"ZjSuJIuDh9Zv","IKfg6lWSG2ee":"28tQtaUy5E7v","Pspwq2wlYVX7":"qm5yPVlihV9H","gdlil6Uw2rUX":"ww24XHzYNZmi","IrQWNL5zrPCF":"oz2yy2thilW5","fviqLJuv7Ap5":"QGOLvMCUy93W","YAO0Wfd99bic":"O1Fwn67wzoxc","HBlELxFdFmza":"oOOYzdtHHHXu","AQzL1il0bpR9":"a4ZWajVwP6U3","Zc1k82TWrsQE":"E7Mcw4lvYSh3","0dKG846odWPC":"zokBffLCR6ci","TOgSadVITL6T":"wCKv3NyH0SzG","OFR9iv9SCTlX":"7Kf8kf0IMj9P","TGbk8UUE4tHV":"Nmey1bRBcMus","Ixr6NTOjKfsA":"mxtCXZin5pFB","dyARbwvvEvM9":"09Bs5Od7xwVF","2KIiLRpk3dcQ":"a1d7LKUtRLRk","nV9jGyOluiJs":"9FnT2BGKYx2g","ogy2hYQVc2eN":"GtsP3c9Sod0C","2gZrwFbVsOxL":"hhdjzaaNOpbK","BnGeRyOi4WAI":"8bn3beI7HO64","Uzfg8sGOzrrz":"kChOu8aOkiJm","pvewFFcMZ7UJ":"0ULd5NkNlLOG","Au3U6Qevjy4G":"0IcpuhA9jcXX","df7CNR5Lrq1V":"db7S85vAjmHV","imywrGTpM3jF":"SvrsFGEG2LAK","Mq8L9yrCW2kQ":"JztecczFznW8","DENI1FURzkqB":"MbvGXNP6a5ad","FVmfU71yD0oF":"OMJUcnabJOIr","ZTBv2R3wDUdU":"478Cfx7zv6py","kWon6YzofQ4J":"iLz5TZA17TFU","ksofMMTIKdO8":"83fwspqx2bwF","Y6IzZZ7OhC54":"FK9DBtyjAmmu","mZltaERnnzYQ":"2wfyNO2cVgms","Yn2HDyyqBGjz":"fwyh7JwbBCjF","U3chwwU0zIvc":"sohRA5cvbmDc","4jTd6UIPBbpo":"QN6wVyCD1Wmi","ux2Ox2TEyERD":"p5b7O3mscz8m","mmYDDJWKkaG6":"e8jeKtmRf583","zpxWVsRvTIWe":"94LQl42KGFSG","SWMh6Z2rKzqJ":"h1AKVXd40MQF","uoawCKfTU5Gh":"rAsbzLxs8HQN","LWIJiUrWVPQu":"YT6EIARuCAz6","Ey5dQpPywpdi":"vZjLZmJh01Hr","4vVOiYqhfapA":"9wdOzr9TcF2K","XQ9KcZPPbsav":"TwW2LY1pl9tB","ztwlj82G4PYg":"B8RIPSf99khI","l6L2hrbKdWLv":"MuCWkwiB9uBe","n58AFayK0uac":"FysWwz0COFhD","0V0zBeyyfGij":"FgM1utdUWor5","8UKPak7pRfR6":"HPTsLybC416X","fTsKyShbusTv":"p4ruSraVI1Ys","hTUXDX3PpmDp":"sdeHJJreiukL","THhoMkRcQoCj":"ThDjtGwvtdQm","LZ3GCUU90M2q":"4YXfVy8VoKtw","xgssunrK5ffC":"7I33csPKOUVR","H8Ahv2y9rGsV":"7MevPUVqPAMY","dJJpxRBgdeZS":"SyNhDId32XOm","FYPQemU9oH1W":"2PgkTGm5VCT6","jvlP2mnN38mz":"hRXSKC4YvxM4","SniLcZjwNCZm":"m4F9gb5HS6Cg","EWRRQIyJSgkg":"eSa3mCfpxuhT","sQ7rWRHi1dlG":"Ti6J0WmmjTAq","VyNZPP8AlEch":"BMKQXOoK1oH4","tUmEmr3iKZi0":"HtD2CjHOpLnM","r4iwl9nClppN":"iXBHupVImSWU","og0LkuTUYC1p":"4oKzeylVcQQy","cblXuTtRh0B1":"Yd31ZikY2wXA","Q1cfrP0QmBYH":"iBhokB8tk9m4","XrFdPGNPF29u":"76FdhgUYa7DU","LTKKWOvZ6Kvu":"ThY9NXbgEe5n","94AYdxC396tG":"bMqTa739mEXv","qi2OiDQye48g":"vr2oUPxTMVE5","tSaHct4auCaj":"z1jX0gGbx8cR","5rKMZ6Jh6Mc9":"OC87HlUGYPAv","laTKpaQpvAeu":"FR7579kWFWbk","ZgKJLghmQpa2":"KDDdlKK28vRu","fbgxgEssFbf4":"ldkKWb9qvnlk","mqDTrE42lBVZ":"TsKVlPtF448m","yKVoR0Y7SF1h":"QTmPIUZg8kLG","pVb5nDQIsX6n":"D3oSSpDMTwRU","vVLvbfYGbxpW":"1y7gd2VdT42s","KXephhX66Nwl":"a09OHPVNAxL1","lzmH0Uo1A50T":"UiYjDdsx1EAt","AEoxMBK2kuw2":"0UVZvLf04tVf","UcryRWHRaEBE":"oISAUmrACp6i","iPeKjBjxSlZl":"2vGF5Z70UN6Y","4QKZtoSiRI54":"tlRlGsWp46uJ","IWpnIoQbg3eg":"qbeR5aTdmthP","kqXhFUoDzeTl":"VQ1Li77mUpFc","uPfcrRJ7HefF":"XyYadx0L4hsQ","pnC9YhUCfLPK":"frChbABlr80X","jPMZhulrXbDQ":"jj1Igu9ebGcJ","DnnnjXviIMeK":"slUBIKXSlLXR","Du3XKacK7ogW":"rHF8nsg8rqxb","NnKWxf1UDEnm":"XCrudwYCnCV0","vn9XjDEdgBas":"FL6lyFob6imV","JilFXMNPdY97":"ulcPJv5TdJYl","ovPmkm9xAFE8":"83jos3FzsQf8","FfXzBVs0AEkM":"2GkYRaKd1XhD","TlZyQBZjXqi6":"rsO1lWIC4tkS","cSypPt17tQNm":"A7tlk4GqyxEJ","jafcePDPtfR6":"dONSE1mwNrCO","1VzWnkICOyf8":"qAONNXNTQNdq","PbKZwe2t5q3j":"tu7L01d6iy5w","yehR0VFkVUae":"51uZrHQFISg8","x3oKF8rni0bv":"bknbQvIeYOtG","8GsDy0muOQOL":"D0rOw7jIZXfX","fyHM6XMbrh7p":"WbQqGmtqAY4d","vwcQrLJHEVdh":"sk4MQeR9hkaD","rhxc0vI0dLty":"gPM4Qn0WyBZG","08zT8XQZdsip":"dCYXUiIXs88R","jHeZEBnqQohT":"8T5hCi0ji45x","wzr3OFniRifU":"tOaTBRZrKHwp","AduBXp8tDCq0":"HozaaCRtf6Vf","62anfmixRJuY":"MOajWSrd10sc","Y3sjcbGwZx78":"6rRGkku9wuxV","YgF4QWVKbzc4":"QkD8et6csg3J","aRQrdZ5w6bgT":"tGuzzXd03sbx","H0JyxmSvrxnK":"4BsubVIm6ybj","lmud3IVeXxUG":"hMXHr07wLNPR","tiC9U9tiDQ5y":"YrgByD7B4Kxh","EWRMNCoMXo1a":"0czSmcju7tgu","lseBBysdlC0a":"wO9F7LskQgdQ","pIOXF0n3kYyQ":"DEw5usVVPnuD","OoqWUmtJWIpP":"QuMvFQPgUtfv","YZhO23gzEFeZ":"4gjQptjl8Eyn","Q4STaoeanKlS":"Gb7A4BNYRGCS","MdYVSsaDPbs0":"y7Jkq9b73zbW","nOu09ZusEnK4":"S2SnESDgLFzx","Ce2cnqtQRlw5":"BLJ6ZVQmaPfq","cZEohMYE66Ky":"jBfl9pTVyonn","Jj2BzeuDJBc2":"9l2k719d8kg8","bA9wWAF77y7s":"pI4SSNeJ5Fj9","Ko4M06wh1IC8":"XMi4C2dugqfn","bXHqEl0b91BR":"nFNc2wfViXHq","FeQdxazSvSVP":"Vp871ReBb1Ro","fiTt5NE3bMbR":"5Fuwxs8rROie","EGypT1PD30KY":"O6WbtIObIhuH","4COMQWBvZnPP":"cePIEb2c7NUM","Mj2w7cNe6RzT":"ahfj0rQTmV57","FA12OVfmyxB2":"pShgFIkB5zQy","lnfbyl4aGUc7":"OCn5jo5sZAmp","ogjk9XtsncQw":"XmLcVhPQGIA9","mn3epyF4kzjT":"Uyp6jgas9AA3","bp6M1ufMZGCZ":"Vun5tkU8dI5F","kJXDiwDQ3nwj":"1jwJ8kSyVGtQ","IUNjq2uJT0qs":"G6ABlrwVUMfs","Xd8n44yMn9ZR":"KUnPpA9ub3A8","ZSvH70aiCjXI":"n6VBm8uwdns1","HxOHZK7OllHe":"vkGo67tFnhZT","s87X0mtQcqD7":"EgCr4SMbkwfA","wm6iDCPJUUoZ":"532GAFNsTsVl","SkyseY3zIgsl":"s8rmrwta5uOi","3lmqW41vr1BX":"pBwf84DLR0zB","I3z4tp9cJ2Dj":"PXmHDoACWZZw","ukyZ9PjsNvnF":"r88jivQ9fb3I","Em455yg6Guhy":"zPnx2vf5uGfc","XL8nGTT8sSMU":"jCX6Qu2oIlnD","tTd4Dr6hIJEN":"DtO3qnkDxAtH","IBuSNzwinNzu":"6XS2P4MuPS0D","dMjCbopLGTV6":"gFgLyW5Vse6x","U5nw2QZui9NW":"nspWOLpIkUgk","IFwDglge2lyv":"TpF3heh4CCat","tjU5kgwpdc6U":"dntcCmMbAL3S","W6couqwKh8SC":"cA1qwyp5nFwT","9DDvwewFSeV7":"o9v9xme07MA4","Vw4mxCtEyZHS":"JVT1mAZ62caI","0zxUp8OqQwSQ":"NQAqiskJwCNR","1rDu4BRgDDDe":"obQcKxTba19c","wkEqpbaw0s6i":"vDiuLeyTQlHz","rH7XbVfwngcd":"aFKlExavsADQ","RsLQjZ4aZq4H":"DMMFweXU36zo","4fQW5HkRcyBj":"9FOgkFzsWFlM","o3DlqNlziMQx":"Ra3JF6K9xgmg","yRlGtDji46fq":"i9aT3TxLw7bi","5DBbQYUCFUw6":"XTUr8eLUJ70Y","htBI3ImuQpqz":"OuUkZQHtaeat","vlv7vHkugIjp":"sdX545xVgRmf","ED2iZXyMBMW4":"BXRGFI48KOWj","JeOm9N3PtKyM":"HRPHMvy0ynEF","FyeCbTMkdPEA":"Mc2gpggrcXbX","NZnBdRiNbySw":"T1Ac47mSZEuy","lebOlhiLvZpU":"FHZdumKmlpKo","0O33Ls9ZFOLo":"WLDmRjoWY4aI","pmBh6ynBuUPA":"ziVK8B11RIOi","Nz4OjoE5UBPg":"L427phMlSASS","MLoRWpIeir2Q":"2dd4Ot2VYM2E","Ff2YpEs16mzz":"ewc3WufDayfm","69lTmXgmX8lq":"Yt8w4sv3Y9yo","GT7PgF7NrY1X":"xKn2hyaYExU9","RHtSZ1Rbaa7V":"WWXTDOZ9qRlU","3DOkLIwuLQKD":"dbPDmlRsqgHp","EcqMjQikTsGz":"erY6mCsLIKiN","D1SNi0BQU0U6":"fSFijInYyIiv","WWEHdBViXela":"zIXabQXWa5X1","4MYeKEAmiavM":"h5LmAf4d3lek","DcpxWP2B57NA":"n0Kc9L4TKKA1","wpEzaDmQJtcR":"HtG2Zl46ilBR","P6MdqzS0Jt2v":"BVWFkyOboRcn","Tforwac9XZsW":"gMrUl14wj0w7","lnIwh8gBx3PP":"YUQY4gg5Yq1a","JA384fIzL4q8":"hdLJUbkxMJMt","lQNfKAxVGCXN":"U8Ds309fcLL4","X9LQN6jMmTLd":"QLiRsR9a0YuZ","DT6nmOiSmos2":"eFScfpAsJvlG","VS34ILeCVcEB":"BgdBnnTEwXbw","77t1O0eMgp6U":"2iGQ2twECG5s","ijrJIIiUSsHK":"Wb8tXItrqB95","TuCyc1HKmNWH":"Hp5XEnlkb89D","R4PBWRurDdBx":"XCWbQqPpa8WV","RRQcFiBh83AF":"mhhFeS5Gxnon","dUKALb28nvHu":"0E2s9gIcEBcJ","wTdhwmoADf9l":"CqXbniXceSSk","9IJKv6zqYEgD":"jhK1PlJ1wIkB","Tu4cvK4h4pfu":"1Rjypou4P96b","VNNtMPLW9iD2":"CsoiTao1tBYW","Ejh7iVIMUxEH":"MstKEGnklJku","9K9kYQk4ZrX0":"9lGHJCtwdKrK","VZz8cguWPge0":"njERyPpx3KeE","YcEAq9l00Vhi":"ukKMnrd8Unwu","1n2gzwIOrxb3":"XTXMDcEM3BE5","UBUNyVkBGYqd":"WOqFmOFDpQA0","3iconp2KV3eE":"YT2EjwFTVtG6","FO9ezHViz1ul":"xzVfpWfxGIvg","OyKeCltUvXiH":"jwwudcZKXYpi","ijfqt9LvD0UF":"TVyNiFCAAs78","OPPirWffTeV8":"DkDqwt3uyLMP","HYvGQaeD26kH":"on8snYccUlEl","PctSVgQEywiH":"SL7UxyseBdbJ","fXwFXG6tSlc2":"BysilKHdUrlo","2hCU0hJhJncN":"p6UrPIOesXb7","7uu4dzGAEGOc":"6L8nWxLkIpSp","FtfAtgot99qL":"QaDHvmB3eKzH","IPd0N3Btmsbt":"1R8X7y56bk3r","WIOebah0fkhO":"AYRVc6lrkqpa","SSlmGzN89x7g":"P8BxmDIOZC3L","M9FufZBFTFMD":"4D9WF10XMl14","szJpK8h8FDma":"ujxy7j6vLOqZ","OhCaKUPzeP5X":"n6l2JkyrIC3L","82SqLVUB6BxQ":"6mbMD7LLTa72","jsFv154wxiPL":"LxLZNujNTahy","lJfyXgWxCF3R":"bYUclQ0LOuJp","9ivxN6KTw3dl":"LcYTeNvPXCzU","pVDIPWzjRM2A":"sUl3GiHvif2L","SgKkL6bgHFiQ":"dk8jJRxhIW8H","sw9ZcQsm1Peq":"ghS5x90tjsFS","2bDmrdm1mRdZ":"t3m6FJgBgaON","YcOaQJ7oKxTP":"cL1IASL8lrab","g4rKKH0gTSGQ":"UfWCcVHHtTfl","QihgNqk9nVIT":"iO6nrlwiCUuC","zZG3rpWDXXuY":"hlozeiyFaKJ2","OusQbaVpvp6a":"oKm2c2cbB8EV","8nkZ1ZiRAB4F":"0ps5xRVGCgp4","8jJkL9r7ctTh":"JV3PMCtiwTOl","3L4N1DKSVkgM":"LWPxoJ69k77L","pw7lnKXUy4sg":"7rv3g4kLLMdU","nGW4xGChpy4x":"2yQNQPUtyJWg","uVv0erPtMoMm":"cQXc0Kdmmvhk","O30EbClngmMa":"aVamL68rgNxw","iJcMqXEfX4Jd":"4B0CKzjjCqIR","DKoHDEyeDohB":"OSSPI5x5mBDp","UI6pyIrJsVJs":"vsSXSGwCL3JE","2jJYwXCSXuF8":"08SPsmlGNAcq","mzczYSVozxWt":"0GmWMzJuVhVA","VOsPkZzU2b6Q":"GikRZOL5JPnp","LqmQYqiF4YzJ":"jhaVViPUeQ3I","mqGGOTsb8eIU":"hpzg8B8c5Emw","xclLYoJnZnOl":"9W5fJoqJwpOD","WinPpMZn1NY9":"4oyRnnVLrmWC","AhkM8lM00Y62":"gXreJzjXpwrw","kyapN10S7XSq":"kfdRQ0ncfNpB","SvxA4Ne7wQis":"J9JlVprAoQku","WdVqOsERq6ui":"EoBKa7wWPOU6","oT2Oz0QMTtsz":"sLdfz53baddn","YkcLXm128c2A":"SHQeI04ebdCP","AWDdx3O7OvNt":"KJqpNFpRRdCc","5uOxnNulgDxt":"P35TnXzwH1L3","kwWCY1Yug1pB":"6P2lgdMHxXsO","ZHcAGD0vrb8U":"hU29eF6zw7Oe","Wvz7q1WQVo2E":"5Wn58VpRTV9B","fAcB9MorgsFL":"wF5cfTUsbYEk","NuAQAtJmDZsc":"A1NCEualowSW","gYsoz1qe6MiJ":"TjpVKYBuSvBD","FBKT4YGQNvI7":"isbSWoQ0cPM1","2bVxnP4oWifk":"QLFsvfbjFgpp","KtgLYQ9aQ4Z7":"Dhwe93k5gbum","oL1kYXHj7deG":"MU9aAaHRG6KZ","QZMXOZK31GMM":"bHMWY0srfaTb","6xgazLKSzic9":"fh5HHfW5Y1NS","pxcTWQI0cS1p":"t5gTdjcg8rNZ","IHkbrIYBWQHN":"XYJ9m8bzgbLS","7cckICH1ZPct":"wydphEySCug9","5IJqfQ3hShj1":"C8CcSwM4oZtP","2cpaIx2SMBGv":"bPLTH74Pj8OC","qpElwyzF3JXQ":"oBeKkYlmryd7","3TScxSaFilEv":"C61AVuSJGNHc","kwRqfwQSPiXN":"CS8RtgTnBFgz","fCbJTNcMEdzV":"pcCjHZFQmywC","RIo4g2NqFLGz":"ubXKO2uySO7p","LhVBlwqImgGh":"77spvpcWez98","kS69QUVQoUHJ":"1XfZSYvVHYRB","mipXS9VIC80K":"gD5WreyYxh9C","D2aAm52LCaFq":"Qka8NeWm7EqZ","oQ0yQzG6jh2k":"0XbBZVAycvY7","hdBHoE3OHT5F":"UbgQOcA2N1wo","bFsukq9Z8MBI":"WttPEPnRzurm","ynfJOVe6EWGa":"JzgaxVttQbPj","FkkJF0Dhzg9C":"JPupnXiSRAw0","Py7rj8kvvmIG":"73hqY7WnnfDD","e56o93mnzyUD":"rc9rQW31b3DF","sm4RjD549Pjg":"gXE0MdO865hI","J0e8HNXzDFdS":"MSJGwPnboPas","biXmgyThssbs":"WNnFxCQRF4kQ","ikzgiqGd1Nw3":"qm271wQqT7l6","x6PEBAE8V6J1":"3PWww4lM04CZ","V0t0fOOkwrnx":"2sZOMfdAeQb7","80AXynUvCZRX":"obFc0E3ikx4p","DIVTt33ZnYYJ":"AgQNZKYvNZUD","A5ttwr4PNvWt":"IV4oyqIgqOpd","PdSJFsYhKdI1":"kXMm8xCa48AQ","7B5AiROin6XC":"sxVfEW54Faj2","e6PeV0hjkYnI":"9Txtx8mUDFTL","3Xq3qZHLDJtG":"pstnc5f9fvOh","CnH0M6o4vXui":"Z3OmTtwtMhLZ","C9fDJaXDZcEU":"uF2nKyT71u36","JAKP9TzNAZzB":"z2xbXk0ZoX3W","ouLmHRNlPuje":"IgrfJ6HPiFSR","zXq6BrjUuOHp":"HVrzgvZseiEj","X8uKDI6UUgbn":"l6Vqeg3Kc1DD","XjykG4eVtsQm":"qHOb6jjUC7l0","C0joW0AdwBpY":"p2bc2CR7boZu","VzfaEnn9eDhy":"MrCKzOq0TtZp","nNw9UqowGZ7g":"C7fB62NB4MAB","N21RoIXMaHMN":"F8AoI0ddIljX","b8yLq588mDOf":"UgfEeZSNZYVd","CVkQLp7D9YRM":"surcOrVN7mKH","Q4y0aOosPCTS":"QvgVuggMVyxy","WDKgRtDDkJgH":"RfNDRFIbUqJB","Bjm3b5JH62JS":"8IwlhIehhtvt","OjzBedPcFhvK":"WkLdZXdjtCIm","B1WPdR1A94U8":"SdJgTjmauFry","6iLbAgnLplpY":"l5yfI5DUM47r","URzIxPmOKwN0":"ngcYL3XDNATD","JBO967Nyf4RE":"2YjhykCVeUWa","Dg2LuopzYDdV":"vjNGESiAgPtc","GK9Jk9avHDaT":"0FBRwMUJBzJF","RLfn1Xnw1pKH":"b7vaXErtnaSj","reB84HRn84hX":"IdVmIoFa6v1t","ctFhPKtcrow3":"hwFjwpQk96PV","c28Jeq9vaIE8":"W7DXEdridWJP","o3Ld55JI2IYK":"9AghlfjAlhs6","A55jC9X7yTwQ":"96zTsWYFN0Rg","cVS5p38r9RYN":"YqYHFF7YwJX2","muaIYZzOuZBQ":"LtOi5DaDJ3jy","zeNtEahPOntn":"h003s8CTDUcp","JenOK4Odu8Ka":"cNKAGH6rsYaK","2Rq1mKBTSX91":"eELknhE119uI","1G48myBDJ3tT":"9m8Q67ORwH4n","P4VCy0Wv9JLV":"0n2VpYdAUw47","PPdqTjfgejyt":"7mkYxVcux7Qx","9gYqfr5qgKfl":"D75GUudlx9H7","LEHE94cYMAjL":"bFbsZSAusjcu","a8uufAjXvKmr":"HbX2Ja7fwjS6","j1EMhwAkHJSK":"5E5CZGrMmhd6","MCTOVY2ka2mJ":"KynY1pR7EfhI","EVj3PT4aWVoh":"B5D9nK4Wp5jJ","JJor6VhaQuGH":"Tpw77URzJFqn","7nkDS5qt9Xaw":"l3wUe2JzkJKu","8bxvtbC4WcGl":"cWR6Ii8Ot6OH","Tyvgxo6rDLPb":"2HWTiL5EeuJK","wNrkcGaxL95X":"Sww7MezuV9gY","zQXCV8F9NH7y":"ADzZCnoMznoA","eGmSzuLwAu5S":"UX5CVa0lEtHT","tOuCyf8cpcq8":"YCqTAbIhRh1m","G17XBp0BT8SC":"NbMlY5kGa4lD","5KBy3elqrTsR":"dCA0EgP21BMa","CtQigLyy7P84":"ivmG8vWnjOEl","rbZSZovMagqX":"qdT2KM6sBZT0","1TuiXeyCYieA":"sYksCYWEWbyu","vihIWH0dYFvV":"lMwB1IGhz4hM","dQAsPmDYerbS":"qGtaqGgVWApA","4001EXWDssnl":"GMyY3OOsQqYo","o7PKTmKFUDpm":"sMqgt4MRNSmJ","HquUECw90Fw0":"GvlNEcj03Q2U","bTUd4ZuDj6et":"4Glog3YXAWMK","zv3vIgssiw6o":"qoe4J7huUlDU","LEGhK7r8CHeB":"nQrRyF0mPqRW","foR1UBYXU2R9":"oAbE7YgHmzOj","tkCbAAp7WiGA":"kPBFZ0LnAvqc","cBlDSGn8R7uK":"ngJ2KjHMxrPG","TrDnLnBoagmw":"UPW7wksc6s9i","Noz0M0VXGC38":"wmpXR6gsIGff","3HoXo53SOx6E":"XPtY0CiaeCyS","GTx4AYdxNy8c":"T3an0pmaybuc","JqvmLrH4n77X":"JZWVH2P8MjKs","qkmqvMsmpEgp":"VsX6OUu33cGQ","tnwUznKSruIZ":"TzdCtWimaMjA","6LU2dbkK2QmJ":"DshiEOQ3t3FB","sl7YfmM6txp9":"zuQvYLRYEUD9","sN5JcHr63x4q":"hGITiOdSMyzQ","RvQX82g7ZeAf":"s4llvISabVeE","OqT67jOfxNMj":"qoymtDWqrJCz","uAIBR1gPsIBU":"MhXiCl2ch4iW","vupbusGBqiic":"9tt1LTZeWsMp","jNQLAbMPzcXL":"dg4mERSgSYZD","BDV6hbKoJV3p":"1VkzuLF4N0z6","lAxErCfW5ylG":"MxvoD9DohADB","Gbjv5sS517jo":"ZG2MXhICvQic","CJgrnoQJKqYS":"69bgVIjJwPk4","3RJCTtIi8bZ4":"I8HOiOqY47rh","yDfLV7lUt3OD":"Dy5Gw2tY1Jio","iFtX1XdQKiBd":"qwAbUlUquuJt","ViGSPCtYWert":"SxhoEeEYJY1i","UxL8v43meX0n":"jpNjGoudi1Vj","jYEkhdr5LRzJ":"7eKpPZNLbhjZ","uQkKvDo7M8e2":"qjL6EVfEyxej","E2Conlw2lwIY":"CJNjHbsQslqJ","hX0rRw6kx5Ph":"WZWkM8AJqmZ2","jBgQZMqkqmUh":"TuanecSsNrS9","UsWe6JDNzpj8":"WSuoA9wRIp3E","eoTNY5uQAdj8":"kVWbceMPxDrl","5g5IazsAWO1u":"OHTDtFWtttCX","SV1T561GGT0m":"chiPQLYDkVKU","i5GvBNIxHd5H":"wag0Z1OyE7w3","ErbRZbMuR6VL":"ZriOINiEzmyf","8lwFQXCES1UT":"R1JT1w0aE7ua","TLF8fql6M8sN":"h3W1pObWRcJQ","alvOiOIZ9K6c":"BveNDpFltWeG","0ZsuCi2Z3Y5r":"hh3cVkvHdKy3","p4s2Tpjl6IaW":"SfTKclfcTp3N","oriIvWdDJtrZ":"qQIAMwJLSqPL","Xjqmm9ZiBFlU":"r65UnIBCj3N1","jxjdlAsbYwXj":"aMwCcRSXwB9P","7gPuhoEA4qYL":"xTxZQg0Qf3io","2tg6tkxDBgeT":"945ADYLaUWk7","a4isvRjkJ8K3":"jdLF555DYcUK","xWTDdKAikUFM":"qifGRJXJeGAe","IBfegIHrH1Wt":"MIAJyw1CNLou","6iaxUA8TSF8r":"ypYHlMwzL0mf","FJflcEtmDxB0":"fjZMkq2NZB8U","u9f9fwpzuYjW":"qeiIsdnRwLZq","nBBgHbD1pqWT":"aFqjET9VKTBv","8GFyRGGw22ip":"OvA0G9Sn5uO5","enSa0MLcPhg3":"dcWVu7rVAq2o","p8nA6p9YNAA2":"iMUYIpju25uj","J5eDaWfsFLGO":"c21dEY4URfpR","Qv2xE0QI6sr3":"4kJ3USLKlWJf","Ss8KbbJltcai":"Z3bFjJbgYWib","MdDvF3zYbBcL":"Ip4k0EpHTrmm","fYAXzpc2q4U3":"TvTtGx7hAbTQ","1ay1lPt3BJVK":"QnSczifNSaF7","1Zi3uhDaql1a":"RLnGPg5ouRxe","gMAAhi0ZiBdz":"vtPYsaET34Iu","ezypEgfPhIk2":"jpwscPq9JuDU","KSBhTnlOWIhU":"VuVKZwAvFD2N","Qb5CSoJh3CML":"8Vz61HsfmxFi","J1qSpoQfCYKA":"LrTlETil2g7X","bN1SGbjLEh1n":"caRx1TD6ynRP","VU3h0j00aX9k":"ZGPcfLy2TJAF","SNlJgnchscMU":"gZFszwb5Eab9","PQWBYuoEoHvA":"MvhB18DBaztd","z0jtC1LfESH0":"zepM6rCslvMe","gAKCHTXoTcgb":"e4hPzsyzYUbH","3QGclTtBkY4F":"M3Q4vx5uUue9","o2jPPcHfy6EI":"3Z7dIYrMaFlL","K7Lih1Jx6ACk":"WLuUsGY5KuQX","xJ5XLLioZlv1":"p7WZm7o4YlmI","hwQKL8ZM0dyd":"TwiKMQry2fKq","eg32VfT4m3pk":"FAKhwxuwe79b","yMx1iQm9Z8XU":"AlQn0JTZlV24","3TzFfxei0rxs":"M1DLYZt0FBNz","l7kBkl2Novio":"pzbBVx5JiXV7","mCJdcfmEHkCE":"VjzXJcXNKcrh","VIJGox6zgmg2":"Hl9FAgtPwPef","3Vdc75DlYmHs":"lmcBdOe4yQmx","DNRPresb4JRR":"x4kESAT2QKWC","B3x6ZszkcAAd":"3zUzzmWnWVWU","NiEdCUvk30as":"Vje81wpuzCB3","Ve51lBca5bVC":"p98iBtMvktod","Bz0HiJA0NVJo":"vrYH9vXXbQiz","TsaMIbtYuWBW":"dTYvMPteLx9H","qZOEhbb6p5Zl":"N8bMulTawUbQ","nfSsYC7CU4u1":"GkOE1BGagWjM","7GMTt8lA2JyN":"MzdCpl5FSvVC","rlxrN7upL62B":"UpqCi34EDZVS","OHkyQ7SZMw3j":"hr3cc9wHSkA9","c3mXdvWV17IG":"knOHTHymraF8","alBhrbx1XM00":"M0EaOJ0je3ec","zVQFMPafqmdf":"SP5medRZzRet","ZZolkKLVNggQ":"Nl2h4176pYCC","0JoCK9zHGB16":"5nrtfetWxftd","spVbw7SZNM5g":"BLFxSvugVZzw","SI95qBoCC4AO":"yrHdkR3s76Ok","4KKz1hB5yDxu":"braQNWH0Qfz4","85y9DjEHntwc":"Ma2In7J6QqZ0","mjXrq4qV2Kia":"CX0fUAbwJzuA","xDgKbOUpA2WW":"9Bcn1wMNG9iX","eKfGbMCPSmIT":"Qvt7evcXjCTR","QlQHQkxx6Ixx":"nf5eiQhHApWl","VNaYiVs4PL6h":"s6GHepB5MNih","tLBa0yeAQhd1":"sKuj0MBPqzHI","57udQwjsXFsF":"OlAm0pVw2FWL","x70HLF2mlm6k":"RAZpjKsVUBn3","yUGT3c9tmiHK":"P7Qq5DYAgnZq","9lWs7IcXRslB":"qHogNozcKs2z","2gvQLhOz0YQ6":"Jys2VpTXYFg8","uzLvep2ks6hh":"xpY2zzxq3nm1","IbvbuqcWlifW":"kswsIj2XXlh2","A2XUAySHXoBt":"FFEDXOLue24i","p5tTFsXUgQ1D":"X7tgwFqHpAmp","eedTbCNoK0gD":"ldns9bduPFxf","220twRRwtr5V":"rRJ1mdiBwbdT","WsvI1v8vYEbX":"du1N37vJyEYx","bHkfTfY6Iear":"NywuV2PMyvS0","ndWANYMFViYB":"Wq5d9aP7uSu2","4sEfdCxsCFkZ":"lAdkGCLEy4jV","GaIcqQMpUPEp":"ey6taED24PEh","wYVdjNMTNAkt":"xfJGVKE3KYCq","1f2EaH8NC9My":"wzW7DlDohx4o","P6kPKLfGeflk":"v9XUxENdny0Y","2rY94WOp6gn4":"5o2L7sRfedxR","Cs01ks0dQWKF":"hj2SnDHfAesl","ZHa9ziVxai5s":"r72wLI0SVMFg","Gmg0pJjcAa2D":"iarrCaNJnhoO","22PYB1TSYKBB":"ZMzgAtNSjMcl","l97Ot35qyA4J":"KXaHCKKVP2o0","NaSevyNZK3ty":"syCxU4gET4Ss","edFZolGzU7wo":"xAlWdFRAibBd","xj6RR7qmgxYz":"sF0WYhrbqGFI","vPSokHUnEBJh":"wEwQbQAIq1J2","TOA6vyLtldNB":"k1KHSOB5fsca","zKrbjs3DK67E":"6H7mP9ckf8hP","1TRTDNYYkFUu":"LEcukJD4Ncg8","QtTxrHpJoKCP":"mA7lvXDVWKuP","V621y1yOzYV0":"G0HyronfyxqF","PvItYNSRnESs":"azwVcVbld28k","LQ50cgjByVsp":"1meUqO5HBaEg","tYYwFqtg8W7h":"01Y7meCA3B7q","76Y4kooQrqdk":"RCJul4LpPbNd","8LQxF31e3HKb":"UVddzXzLpTT3","esgxA78EhjU9":"p0v9xR1vXz6e","OPNKyhxek9Ab":"niBi130CLlVl","QzKaabF90hXw":"IoRtgI3WhrX5","ckPTRNxM0IEC":"VK6cE8wi41bx","AZdjxZQz2A3o":"tXWRn81LkYAG","IBfdwUjx7bjT":"ZVHDTHWdSmJ0","hXqW7vXNqZP8":"VjiVpz6s3doZ","8SImrr4Ecqp7":"HgfUcDh44VGa","QSvIpgd53smi":"QKZNm4U0UEsO","2YW9Mv2dBF3n":"gENpc2IZZEK5","tMhmmyeHW0Dd":"JCABmcqUcBwa","3nt1eylTz6GS":"1eJHM9cDFXvb","I72F0s3zaLqM":"Kt25Zkuln4xf","zxfr0QSB6iUV":"mypaAntnvJuN","ezkmzAtx9VzP":"nZzrMdH4gkeg","oDSHevZHrfda":"rWiTm5bmxdmz","2kTSHwysw8me":"MQw09MESUQ9L","XykLkJEFV9sK":"sqG2la3QxiqQ","unJACMpOaUoz":"JSGAdFdmesgr","kRLxEAd5rLEh":"SaSJaLjo8Gen","fTJMF40FaerX":"bz2bneRaHuNw","jnSRmOrq8DdK":"N5neqtJh8EFX","URl7IRcBsBAm":"YHENBRQI2Pm3","WDWDBUoQvq7a":"NP9NZOL6N7B1","1mAsdJ1IjzGG":"rVESIljnFPlB","0Boak463j7D8":"9bOJQty9zLGg","II9GsS4w9K99":"KqTJkv1lqjkX","s20YAEGozruV":"HopO5Rup1FYm","D64AIazHWPmd":"0szPh300Epl6","ki3Ydayu2hhn":"hyRbE6b3nYFG","9V51b7tU5P1s":"oe6NbDcy7plS","Mtd0gTYAdxWr":"qwF0sDPIiTV8","3J6sVUst9NZW":"Wt2bAL26Es9E","vxrW61bH8ILa":"NvOBrbqPRMmW","yWqvKdr8cMXN":"7YOZ5eFH0NUF","Fn2UzaLXaKW8":"ilg5vAratxRh","1UBP4llr01IB":"ArZq8SqQ1cqj","tOWYb9f3rZJD":"y4G3Mg00vriK","Y5mKNjWcDDOG":"tyXcqYYjI1yS","KpsGI97VqlUx":"KKJcRiWnNgRR","gcXDuopU0UKD":"wX5fgFqkLnow","9UEOrdZYV2yR":"gdHlSfU2cn5Z","j3YDyZaQBWvU":"0ahaAhf8eyn3","uf7087RgJxdI":"68RmtObUu5Oa","21vdh7ND8IPj":"Zv7SDnWMVRJG","9kxLrlfMM7Kd":"R7Zrk1BcF1HR","FVWkU9Oc2eKS":"CH1WP5Fh2V4V","ipNWtIgp2Kjt":"ojLpICMVCu8D","K1fFByzSrmkM":"fgt5VMq04Vsr","tFX2KOHAOby6":"LW4V1gfGi5Zv","9TQL53lbs6HO":"MWCOUaBIxLfg","zMsnyOuRbyVn":"bg3rT9YMQZY2","UoPDel6VfYoD":"nVJwRtMWRVDS","IBqrTDimdWx6":"oUxC0YSx3fMS","HIrxBr6U3hKh":"m6G5xhFrtKVD","x5dfSAmal22E":"MDCZmaaYIxOq","RaKjCwJlrznl":"IqgNN2mZhAKy","fBGYDjGdeP87":"C4fojc8prgP8","MYPjbqik1Hh0":"OnE4TCbPlpUL","2lfy729dYWRZ":"lYFC7Ghdpezm","sVA62WB3CW1I":"IV3czNWj1LU7","zddJe3TvrtNB":"tAQL9ECLYq2j","LcJMNZi0m2Fe":"FNzTvq3T3mcu","m5hWoAatkfGP":"QDhO5BqMczFV","h9DX768zMb30":"oOYQ8CwWfPOs","ySomg93dAP1X":"EOwHFAjLvJ0X","D2JTSU7iPzW0":"QWj9a7cutvfw","pixi3b6MtdMv":"KOlDNdCAPyvx","5PgW8j1sLKke":"tcP9kDIpX3BY","5GyBzx7YiVOy":"FNBiXZr1zqza","BVuC4WAhkaRE":"GWhDHHAMTvN9","X4B30SqbBpXG":"J193E1lvwZXq","EyYhMWjQwKsi":"YvEZogTggKiJ","nbQtyJG1GnLI":"H4bcp8vZyqCi","e65vBYa9AKQU":"A7x316IZvSbT","RPJhAnvZQniG":"WYQotwSOXY98","KwcmzCleeNKP":"aSo3gBVApUCK","2IeeLTEowveN":"tM2KgNm36pmy","C7pmdU8FDVHO":"ot3E4rO9gcmU","sVhTVPEwzrzi":"Dx9QHK86Z3MF","BBP0iVhijE0J":"fwutXJukDwfu","HDd28RtRgI77":"MCA1z43nOTMw","axNr7wbnObrL":"iRWW3phTjqUY","cqCmf7VVu0By":"h7jYiFNnFwKX","wtFvog7zlhNE":"TlqvGrYtCOih","UgFj7M2CVx34":"MN7D5VhJp0SA","BnKxB6bw0ClZ":"s6iznj87DoQS","IyqwpSYJE6zD":"5TCTyoOcLUb5","mZvkZHUazgbc":"j53A2A6YPB0X","D2LBrJ20Xamx":"uxVqHuWPnEe3","piHnKYXgdXhX":"74JJ7yRPQz0P","aKqYnyvuus7g":"jjUGkFKofbYb","UZN3dBZn9tDG":"40mbM15ohjDU","y8fTJkqH7f7o":"EZocWsuvsRhn","mLi5qqOf9nw3":"PbL1f2TXRuWx","rc1Xm6H1PJv8":"z22l9FGyV307","lkX2azeMSxXY":"su8AEOYQgGc7","x86oiBEZuS58":"eZ3fjLenB7Zt","HNVNAeWrImP0":"Le8ZpmcgaQXd","98rXK3Y2gRJU":"Ta4yMFNF5wo5","ulY3bTDItTBh":"oaWrH3tMV2Af","JU39hWjlILgt":"gOlsmK71QHk9","Kpp09fflfN8F":"djfulJ4Spgpy","e3felowMyb48":"N5yV5IXsl8xS","LBgkVKrayNKB":"kF3ij4YYfvjS","ZH7tatxVi2Hn":"oIQRWqtwRpOc","n5tiJof6rJaf":"UR6AnjsSclxT","veYT2lMM3P5h":"iUFDFu3jj3UB","mlHQOGY1Zm1H":"zL4YjhyMugoa","73KofcniChEL":"oZUrUGQnW115","Jo6U4SrEZB3w":"4FCZfMb27PJ9","re0toOhR8gai":"7qBjjuX8NuLv","dhfa8i68p1Yw":"jOZZgUT7gBnE","O1KVegaqCELa":"Ilw59w0kMWFS","t4Ryvahft9kV":"5CGnx40dUSHN","1rTAlNm3038d":"OIkCZWomHMB6","sed0naqfexu1":"zbpOcP0xcJqC","2wZi2HNqLMkM":"GZHzxfhOEYZl","LyKfOhradUW8":"cVk9oixIYBOI","DRmVtjiB5wN1":"OvOB6Tg9xFYt","2hniiRZiZf8N":"ha63UNjG9Str","pfTMhymK3dzM":"omTaWGzPMzSZ","LxRWzKlw9u2G":"4ozbuK0uOdhL","sFDnVICCiSCJ":"le1g1ZhguVGr","mPBKW0ePoOAX":"bI9kNmQUQUZv","uzn3wB2EhW5m":"KoHB0QaEIaod","Xioa9tuXMaZ8":"977Iq9HRPOKD","8Z1KF1mLHjrZ":"c3QzGzUTqcGY","ebSFr3lyrVIT":"xkXgsVflq3wR","KInViNmMSlFa":"zYoiKX2lvW49","LS0cd9lHBYtg":"WEIumDCEZHwC","4X0KId0PPWVR":"GXf4rtitmijX","nAQ9KrbNEeYS":"kw8bp3CAAXBh","6uVuJjfImCav":"p6trlh5PbcZy","EOEblzZH0S4S":"EL1xTvuF0p2g","yHdY8UU8P1jK":"nwlvjI91TPdm","XqpzvPJB9sdk":"DieHwaEf3u5c","Qbn41mJ6ikkL":"Wj5oWOjiaIid","TqKN53RNVp2B":"V64AofvgPru7","JbIUlek8Gk3t":"sntY07WpoQrY","IhxCGiTfhFnD":"NqChTc1RsSh6","tLCrxbdvFDdb":"FyzgpI2zp9ki","EOgPfcByVrIp":"6nZnLGTTRV81","DQ0hUYCbd1By":"iLy3s2H1nAZ3","RMkQVnn25YcQ":"XvYVgDaMnD7J","TIwcgL7EfHtu":"yntpzGvc8LRn","fnACwUZp5vVL":"lse4Y6uXcYok","aaE4rpvPSFzg":"A4BTpvRbrhPM","F6P2KhPipqRx":"aGJ8lhwPokpC","Qayfy2nGIfqa":"ezYwksIicU8z","fwWF8Kko2vWH":"HTjPijBkqX0T","a1uDwjrficyZ":"8RHQZaAUbIsA","H1XjPrEu3xnV":"JctNiw7Ow3lV","LuICwKVuRu68":"ctUMaW2yPWzp","hUKp4zGvQ84A":"5dtnG3zqojiZ","u7zwLr6VIdZT":"oQJhHL2cg7Gx","G7BTfeisd5LP":"7t96WeeeAQai","axt5cA8xzvSP":"wOT5yFHrwBI2","2OgTJdZBodaZ":"giCXJCSYhvVY","inT9iyWJPQqk":"d8LeSfn9a5YP","HIOxcxH3kDP1":"H9WXdmvqNd0E","1kogf9iEufbx":"8W7DTKcok9EK","ppMdGAPH3yv0":"0AuzNVijGS66","cJu32WPT92ht":"kXgCV3s3I87d","LrZtpj0c6mDA":"UIoDu8aCci55","Ooq6h8UM2Vek":"9a4upoVSstdf","MrUuznZftcEG":"ABHSL6ok1eI3","D36TFrpYJWEO":"68BDgLG81hWZ","2aowE4CiizqP":"ndM0ByvYPPGP","0Ofo4XMhB0a5":"UCClBZOA5yK4","WzTJ3ZeSyhLl":"1n23t5KA44XY","Rz81ikl0eNMU":"cvpwkYQq6uh2","fLIVyE7eKham":"xEtaJurhAYOn","pIGpv6HupNrz":"2VwczsOqguZk","7kNXh3ObPJJG":"GJBY4Ddk9XYa","JBLbCTQNrMhP":"UmOOc0JOaYBb","h4AoMzEfKcSR":"hqBP4xsRVIoa","2Wa0b96UP5qt":"7JUpQ7J1AnRu","yPD8VvruXvj1":"bT145TGVSDve","RoWVbxgz2XfA":"ml8JMK0GjFCh","OAVojkaXa49b":"WQImlwMgiryb","UAzlxJXNIo5k":"tDiietJF1cpR","UCwjHZWf0Gya":"Cfpud3vVb9PT","EzSnFwgXgbuC":"MnyAe18DQhKN","aY7A3pPUYqcl":"7mwQIb5MnTmf","2Vo0mWXpkcay":"tSOeS3mufH4W","vZNoSG57Sw71":"ujhHcWEkdVJ6","XgE4eSmghvB1":"G6rfwNBcyrdu","sZBLfQQijQRf":"uIUAVycnUPUP","46ZoXqB9Tyk5":"W5b2ZCZbLNoV","O7mEyuTDOo3Q":"jQW3cj8VENx4","K9rLg1SKgLhf":"Pvr47MMXrsAq","tbPPH4czMTC8":"1yTbUoWs95yl","MeSNCrIjcpJF":"kxck07hSZNSN","phqpI7mwuCiU":"fuLJBLvelmx2","fH0UMvX6AWrM":"puq4MdnynIT2","uhxOaR4JrndH":"l4HOA479OphL","hz5kGHFgBQjM":"ev0IuJAANBby","Iboix4vIxjWp":"PgPAOeHKW7WO","5iV1W6EVyr69":"7ToUHZUxT9YE","anetD2LOoyx0":"bZ3okQTNUhWi","9WvJCnmmA0zQ":"UEeTSFDG7Ah1","jXys6hEPljex":"GLMhvjagCSLJ","xBffEcZ77XtP":"V76zPDhCl5Ot","R4Y9P1uL602h":"IflZc2YtoIpI","e97otofic0Xu":"FMRl0BUtZUGE","pHfmnQ4Mb2rX":"vHRKi8nyv6uI","EOcyaUQm9S21":"VoIbqyHdtgkn","zVxP4UesSdif":"cvjc0o1BnfMj","lOIQZjGIb4Vd":"xlPVQtHz1VvW","6TuEbthM4cyJ":"sbLxMKoUqG7p","lLactGYFXj5v":"DN7wLAOnwwvD","hLRNyBtkUos8":"otzPKRMu2GCv","xw6YNBJ4m4yH":"3r4KT5EKp1Am","qja7zXu05aD9":"Pfr13kc7CK2W","ZJhSlhgqvMqn":"8sX4iA002nIk","mBxjey7s4fYh":"8V1QjBVCGlGX","HvFpeHN01Cwa":"nZzAVfN1KSpY","lcUl85IlOj9B":"rq7dC01xIIQ3","q6bNRVgnhdB9":"5ESX8IrfBlN7","3jUh5srwkDcc":"a76askPZi8Yo","TTIFt4vHptbT":"rTAOFB2G7GiP","32qUT25RA2Em":"14s6YALzJJ2W","CiWonIX0Q4SP":"x95EhuXdRuxj","XUKCPHj0MGMg":"m89uue6vkU7u","ZoPsOtqfv2a0":"2rlw40cCeN8s","IVf9WQcJ6F4t":"xxNAWnedMtJ6","CO9GB5VpmK30":"dvtCx988eFPE","MM9bBikzE0iD":"F93NYszktxRl","n9HdNQuENsWL":"sxEbDi1YQUBB","bqEqztdJyQlp":"YS1qsrGS3YRS","8d55e8L3K3wq":"tou1IrtTjJHt","W8hEYjBG0mPT":"bnEHDeVJUZwB","in6kPzm5sldW":"ca2MnsLTImjh","57Tk3PkXiCcn":"l4vBaDk4yaTz","ArZ1zlM6qJO5":"aMRGSi19P8Ro","dMTBR7hU34KL":"NRccKKeyvxIl","lGdKVjwss8fX":"yLKzyuMop0nz","xMuiEJF5QmOt":"GXcNwGDslmhu","E1or0yuLSWLw":"DKrFsAiWjUN2","l9xZ1uEvi19T":"FtjAsAbc1Pep","LIV2VnDi0SS2":"lobLGUvHu2Yb","J45PJsdqWqgz":"6Ou1m10VFt5m","OHTq4aVm4i2k":"RCnU7G0tPgWD","CWc2AR7gejke":"FyZlXEtOb6kl","Lr8lOx9bLXF6":"u9OW3pIBI3oc","ditKlV9rcAkO":"asJpiQftxegV","6k99evSv6XYL":"gYkMTnp3TNz7","rPw1JwpUdfmh":"tbC5AsiNUayo","ywZIiMnnA7KL":"CZmSnFEoFau3","uTLmw93I67wx":"4pbBU5IYJjnl","KBWU8aTBqxNw":"hkg10Q5SPLuq","kpfoWO4mTBY8":"nYvE9yce5aKK","eyl0v5V55ZbV":"6pObtnm2sQ7r","uzZ4iTFrG7WQ":"UCC5A1jQAVzI","We3AKVyO0MlB":"Lu9h0tsQoo9u","cr2B42HMUxWW":"M3cOOygiI8HN","3I0nNx2AqO3v":"zyf73t9IL4t1","Bh9hAPOHsUOj":"9rnUwHKYsSIX","gyqw0rk3kZMl":"EikIaN0kh3oC","mItWgMOdYNKn":"DAF2lRl1Fr4d","RQJEUi9iZ2qK":"5cq1MHdSa77p","IPgJH6MCXMXW":"qngstJL4WKUN","l5aiKeBQveQP":"Ww7FEdj7y6bd","tMmAgUjaqvi1":"NXKz1514OFZc","d3cxUtbLdQCl":"QTDNj0LUFg9v","D8zekuyNpMSH":"TBUI4tzbTih8","ZEE7CnylhtE5":"zgBRXqmal0fD","i9yQ6wjZLpl8":"r3ByfGjJAXWp","32jBeiGmeLUU":"MLOry0TFkYWS","e7EY6KxCZHq8":"l54rVQSZWvqJ","MTRaEsUecvLK":"MUebxVJEYIye","jX0PCt0eVziy":"TekQoBtCVL6h","tYf41rTlPeBY":"pitb7EM5ftsb","RaSRl7hW6WPd":"slDZY8ZWkwIw","LPtUfuJrOD72":"Vy0jzWl7U6y7","EuaiaGoqpyT4":"cxbDwwvtP2uv","EvlYHiqg7Vyh":"rdwdPz6rMP6w","oEVoNxQNPAXd":"8y4J69QlSmDH","ZxVNxWcogwAs":"V5NONAxKiTi5","t0SInxl7GjzN":"n2OtgonchR6c","yGoRaHtDzcQn":"W7OLW4j93plg","FAKLACeCW4uw":"5qNS18tFVjB6","tmfJrIuymNAM":"Avc6DUzfpfnT","cc7JVQRcRWoN":"Sje5dZyRedhs","MVpQsDJpMVFY":"QsM19lq0zrlw","EvQJGyoy8UrP":"21reST4XWzGg","wmJqr1xbv1Ft":"zHhohxR4ZhxO","yTuTN1demeIO":"8w5x19xtAYpe","HkQXVpLT2eNn":"ShJQXSMnYqGO","BT5OEnekwUBh":"JQzTN1zkaIVZ","aWpl7aAyfst1":"oWqoqvbsNIpH","NqJy3vF2MDIu":"URy2IzipOiLu","Bfxgu0XcuwTk":"x6HmwKSdi7Ao","k3dVGo2ST1Qp":"5cuVRDfZNWyA","5Uy4NczhU2oT":"OlNEFTB4HD68","7hpVQVPfifpl":"jDwe1ZdgM0uM","yHodeWPx6WXZ":"Fv6ktELCJsgb","SsUs6tn1s76o":"coWPaNBE2hSG","ot7MsZ5YPGtO":"lOVfiAr9iNbB","vt7FAv0mR18z":"nKBmzd95J7Tl","S6ey8v0je5WK":"PHpL2a4boevc","Dmpygbklix2I":"ZmiKAIBjNkLs","kSnflWQ9e590":"M9vhcAYqxXya","XW93DzAI1CKR":"IqdEBus8e40L","4okHoR7J7piE":"w9THQVdh9ob2","pyFXQJJi5h7M":"eBH94oULuuMB","P54QfbbXFiUh":"FRjmBTtmeFK3","FXlWwSXTMOQB":"v6N8vXkTFXCO","tP2ynX9GLhQX":"XwRiQy9FosP4","My2wf0HhbKJS":"jfINnpEX8Zb8","ciIMfO41JOiw":"j6t8Ac7V1vJE","tXk3tUpwCe01":"KgwoXBzxkLiu","cC20AuZXctc1":"xSSw77Y7bnZA","KobQC7H4UI8M":"W5jFNdOBDJQN","na6By6rvaR16":"8MqvoXHLQAcQ","uI7UeXkfqkQV":"MNBZdDdGU7on","Nilm0nQZP5cW":"jR80eV5T1IV7","TrmcsOVbyOLt":"epESaFPT1TYJ","yqLfEAkByYnA":"nBZs9NcZmFSu","CeWSIEGTreWC":"dOge7h2GY5Jj","Sm90a9nejRhc":"Xxailvg3gn0i","2Nxtj64hNZ23":"UGbx1eapeMRY","LOkIzefnwzUZ":"wVTGR1Q1if7d","HRjHOKzqJd10":"53UWyxeatpwT","Zc634dA2UoLZ":"NOeIuwafqq7a","w2gTyLJB7ojI":"vlAqWhzdIvo2","4v3eFb5eZFIe":"8AB6CEMbOtyX","mTg8UpZevbws":"KN0HyzAguJAV","XhBC7opI9DJ5":"H7o6jRWWcJyw","dPIXv5sapnNx":"6nDZQu2Bs397","MdZDK6ygrv2c":"BSub0NzbcCdq","3c1pA25bAJSV":"I6DT0HwEJiNl","yLjxkiMCoaVu":"EcfibgdrMACp","nQIlxVJQlJfW":"kn5lhgHCGxvQ","4lxnXWZxYn7t":"Jj4Mfj0wYfQt","msrpNZ7tnctX":"ZGoMKx4C0B4y","9wQLjCYL42Gl":"vDdjw8SncziT","ao8OjhV82N1S":"8h8sEnohcxgE","geMmKxpyviPX":"g9LpZiSmZaRq","EQlNXB0qVCvq":"62pgWGESa98E","80W029OHfizN":"wJdHgaiGsNEw","SFEw0mGg8PQP":"a1OWG7pZ3UBB","T2ZkfpGrTiVO":"9nmdsFRb6mOU","kykWCd9CeH9M":"dq6fJ7cPKL2A","QzvvVwO6osNg":"JO4120DkC1v9","qGebSnXyTsxY":"GPc5Jva8iENC","nfYovDpj7WHe":"hu4mJK3lajAf","awvfbpWmjGBc":"9EdvXcQ4euaP","RAnR0L6fQRlM":"0cwpG9sEbJ4O","DHissz7uPZC6":"jqovs7af5qDP","lWmvj72txVZJ":"TSu3dpu4tKH0","h6GjTWIADqWe":"F5SznZ0BamUc","qv1BX2Ilaeap":"1BLVTTHco1zQ","0Y2DjAj5cFZu":"5YzBveHUgdT7","uZ2KGH04VfHM":"UUZzo0CyDxP5","Sz0nCy6FKUsY":"qDB593wtYIPe","v2mUhew7Obaz":"kXRNjEXO426e","CsQFWFsTPVfy":"3RXR6s5U2Sgp","dXmMW2RmA1d4":"de9HJ6MZkP2i","C6SrUFHUjL9W":"uqug0ZI8ymJV","YtNGUJCj4FeN":"6ly2LT6lqWgz","muSH2CWAEasF":"5vBiZwLGaXwm","otWYAhnEZHUY":"1wFJuoLQqyQH","LP4hftWygRdv":"p7sDw0KnfQdn","Hee1Qff3oKpF":"y9W4xDLU9UND","eKM43dlubWHg":"kfAUdYwFi5Nh","thrRNsiHWrt5":"iCdmo7b1AJlr","OqklxbI188dw":"mu3FHlEoVXOG","qO32ZEbQAOs7":"XPWHiRE8sRL7","AdVDkpCgA6aI":"6Rw21lXUV1Uq","9Ff5Rgy9HdfP":"7aqd3cM8KALW","smeqhEyfPwTQ":"ilmwcoyHIIBu","2VIkEsDWRCAP":"bPLNQQ56Qiz9","wm2o4nGxzwIi":"5RGLSQTN4Muf","xVxKuhFFLLsI":"uNuBAZ4v0k0U","HvDz4jz7RpH1":"6KiZfkjQfs9h","0NNRQX1OmvHa":"dSdusWILEZhq","Yj4joXJ5Q5MW":"9YtXz7lsX1TB","eSa7MurjSEYP":"v4JT4jxZsTYC","ucWIhGN39UYf":"pO90x9FA4bmb","zlrQWtDuUVTf":"NCGntiCPlZzZ","hVCdIjMIwM5L":"O2golhtQ7t2z","c847A4CiZRgl":"tBvyHpqsF5Ar","bVxZuH6S5ebW":"EuIXeCKk5hOC","UygS0LnbfqS1":"BgPjSJEmva3r","6gRHEZCewLF2":"Nuqb0j96y6yl","NUhEe7yzpw4T":"hP2dpVHtYv5U","kq9PriFpcjqH":"jnK0Ms49X95Z","4ThUw5m0e4FL":"F0yz985VT5o8","IT2zJO5cGXas":"HY8dn50PNxSS","pupwsbL01yNI":"oMa1H48soT7X","rvYhzdAyG8lK":"GXHX4iA3UG76","VpoHpNBhphmG":"5L12StPRpyoJ","Zn278gGo3kRB":"DCs9JKHRYGAH","ar3RV0npymtP":"f1uQQjZtZXPZ","44cyCnkDIwcN":"waKuaKHCYdFy","Y4mta17Muxao":"c9asbKXhDGc2","fPjy2X57zxtR":"vHRn29NQ7eWT","mvrwDp06wprT":"5ol0v6WQdyNY","BQg4Pa2rW2YQ":"86UTxTalopR6","nj3GF2riOpZz":"xB0zj9Qrx80x","DtdlNbU76bAr":"PNTs1pgDasy4","Q9D0ywrz2Pp4":"HkMlPg7X9wJk","HdAl8oLCZiXZ":"61lOrkMRhAtp","fYSLt4cNBdZr":"UZ3KyCjmAzmW","7mSF44M1bjmw":"IDp13Fcgw25B","wwKIEY3dRqnO":"wjujIeYmnsCH","iiGTuH4HSM7F":"n8duwNYUtsvU","iJ4n1phMUSXh":"UHnmiPlAYUfW","pARJM1bRVObL":"L2rfNMRJbWSK","RI2Yap1PI3cX":"xBuXDO8VioOf","GDb2YFbu3GpX":"ZFFxgAJbIB2H","KBAoRrb6llqx":"kxAQ2Suz1OGq","TE72cGqHmrPf":"iQCjbKXOWyvD","E9l6I3kiRoQV":"3zsggz3CuYo3","IEwHl9SFFuX6":"tuWs1BOxCObl","o9CXkz6HUDjW":"ULkunhIGrzzU","7Od4wZkSUc4H":"Di18GMjFjFFs","bnRblF0CMLiT":"0BtIWFqESm8g","8En2ThtKkW3y":"cYL4lFMC1z4r","Y833GgsU1fDs":"Z4sMJhyEcJg6","WEtmOjxbB1Bj":"qG0K7VIjVmEg","jvA2r7TJpeXk":"16q3tNb2XURu","LlwjOcGW6YmJ":"Q1uFMhSF6TJJ","XHKJVFQ3DXuG":"DOdZvtr1vJ19","Ax9uxUyju5br":"0To0jzFoxBpT","dPF7sbOc6KYj":"td37ZPMTbfrt","0P2nhOwogl0W":"tDjX3GV0GNbm","bYoo7Olux9uh":"1wLNeXiHO6xc","NlKKRj9pmjEK":"JpBGj2Z8tK50","AOf50vlsu14W":"FBk3BaRM5cC1","ZBOVqMpStzXc":"yFrMcr1B6BzA","t3KbZtpcYlcw":"d1zTsDwZVIs2","fdewFXnKW9Il":"DNwmmyB5sZ4W","qoKw1ggN54Jl":"zNCRXylMNdUk","hvunvuxsc2DI":"2J0CqlabluHR","81ZZxqQ2mK80":"qpLTLGui1msN","myoKmT0y2xPs":"ewleiOXqnWam","s6CfnT5kF3WJ":"OpNmT2A4Ueqk","q5uxQpppjThX":"amrcRPpW7l1c","IKhZZM2Pds0v":"7b6UA9B2tpzh","90gVRb80P8ld":"TThWKNU10mYs","a8f7h0CfbGm0":"ykY4rTb3ZW7t","ZsfTOprJz6YL":"nBknUGE3XDZP","AzuluARzTrSl":"fOBqblTWYVho","ngJKXWHIDWLk":"BOdkiNcpuYyx","9EKlxg8Jw0dJ":"J7oDcFbzdOBu","o3XF38f8ROai":"aMrkzKAsJyQa","fg9l8Fr34ZMz":"vnXT3At977dm","kbP3tbBH1Jrl":"3TGZRIv0k9eq","D9d3he00raYD":"DYVhqjQxPi2c","AvIRodVAi3yQ":"kiHgJSHznaSr","T6NxBmdSwXGx":"eKGBhKeBSGq5","pGvM9FyGAyfh":"yCj4OXuav0NI","qn1WnBaxxdHo":"adIzYzGhb5g6","MKffLHEZWi2E":"WyjFVVcfobUM","OEm6vx9kGiuP":"yanjP8LhRcST","vTQJHiNfclOU":"LW0sJoJCX4Xb","bzYMzaaALKMz":"fU27GodVgprK","9u12Nh5LgUMx":"qGahxlw4m8CK","KnUmaVH1Ba4E":"XAum2A005Awp","0KIlBrUDnNWw":"nMifEoQ8Ofhc","buLNUJDBAZeI":"CRgZ00QwkTKE","G30YrunABZtM":"5J2dKowh4ebN","5Lr8XYP4Qrkp":"N3ecnWYxwQZF","uahWUCreLwKq":"6X4EwzacAHP6","PbdoaDYDnffe":"wNtIZmfvNpnC","0oCBsXnBipKW":"W4lhiMcT6Usm","SlZJ1yXJjSoM":"TNPoStANKn3U","pljULtUGwQAM":"gQjrvjTULUp9","n7927In6YFIt":"bhdXe5QC1iGd","cC5jSPh3DYB6":"5YG3RS0wFDNS","JhrPqPmGfGDi":"YldWuRGnVrJH","ndNl69Ncx3Bu":"B0zSMrkEbFxM","WyJduhcH8iVc":"lB3KQeG4FKoB","GJV3rRIAnCX1":"LIKghBgGoYBG","i0YgrNkCdoad":"Qnl7gXDpgWQN","uwsf5GDiwHT5":"xbcKwUpvmHyu","Tmz0wmpANXcN":"ePCwIV8s441i","dsvr5DXelZA6":"DESWER4MQZik","E501kj4LZ6DS":"66WYGjYDjTZD","PXOJWtC3VWty":"5bXyzZYsFHzT","uaS27W7JE5ag":"MmrOFg9Cnbgo","ATORVs5E4Dti":"qBAqJJnyCw2J","PwYA5dq136dL":"BOJLisuCIExz","imRX9vFjuoYr":"39ajEASiDu2I","hqByPi6V0psQ":"MNCJOdV7mZAX","664xN4W4gSnL":"GQtaenamRjRv","3huDkknW8Hoj":"5u0pKToZLcJr","eWMCgnyKF5Oq":"2V1SYidIKJml","24gylqjX6EP5":"piwhgabW5qcr","8m8BiEZNFnDb":"gmavKkIj1Xsu","AQQuMTeTrzX3":"QJvbICosalxS","4947OBzi1cb2":"VAffzrWIhMhC","e8tgOnYLqZgb":"2q3abDBLnbA5","u7KkMitDSsfr":"XLxa9mhVIuJf","Wq6adD2KU4JY":"RUcmSRjcxDLl","uhv5utITSnZP":"95bhga0tYi0T","3H9JayDg8UqG":"mBfwUY1j3Qgs","s4KSuqG88DTf":"7mGxPYXV763H","M7ptepo40ynP":"Ad9c0ddg7ArJ","mfAcTowwsCTO":"B6Uu3XIO0Zzn","HgzCNo1MHC7q":"qIMHYES3nnwT","lqaHBIxczJjY":"BURCse9ocxuh","M98vB5NMRfsD":"0bfsRUEq4wWn","cjinrcuk3DtN":"sRf9mS6Eo1Nk","z5Cn6V8ZLrz1":"yiAT9qjEQpfY","E1e6EzyU8P6b":"miHvGcmblGbg","3zgQWKZMNXWU":"SraSvqFBmnRV","bzq9Is2NFBLb":"Z0tVmAIWq11k","1vnH0S7t6B1K":"oEgzkvcVlQ3z","IQlwDoMzg0G5":"ytRlW3ZXHvUK","pZQErnAhpQvH":"bhbQLtxNHFef","o4HhQD8b8OJd":"xm4iKjZNxxez","AwHxhTd3JW9V":"3MDMFIpDB2sM","AqIjU4oHnQHh":"Lv8HJg4SeKMB","16qwlZOLiwg7":"jQOru4Z0Dulj","LJTfbCYuexTt":"2Xjv5GgLOrlx","D6URzKDALNh3":"TkyJdAm7J22O","8dFohl4ukmZd":"hoSydM5jzlbc","QIeE742ojaxH":"LgZL4DZB53Sa","GGjFZ7yZR2r3":"w7f4sLUFgk6H","E7fCnky2anc1":"Pa5wJkmkE4pd","DiN9DWtYfByT":"PsPTcSWwFJrv","ZmaUKBqln78b":"JRyICb9rTdcq","golDVHQOny6j":"JyEjNjaALosO","ibkOCxd9CqhB":"mviYn7Mcg197","pcI6f0Z1MvhY":"lds285o2gDsw","4YR86XxfzcSo":"Kpzthd6dNTSE","ilKxfSj6ifUD":"s0Kzy2C4ui9a","doCjHiNgJn7u":"da8t0ggqdIoW","fjIOyPOUVUlP":"NhHFsyqI1Xe3","v5sZyNFRLa8R":"aCD07SFmLnTL","9xBF2euUw7cX":"udpTFIyJ66uN","wfpRgfrl7sTD":"mdZJZek9Fn1x","gitxB89ShgW6":"zz2YO9CkEsui","W6Yqqx65klOc":"o1uKcZpbEsN1","SggPehJiUYVG":"UgWZYZfgF1Qz","srB5D0Z7DSHv":"Yc6Bivkv1cdL","Sn6r8Xs7CaV6":"czt8otDKajNp","LBNpRAUcMJNx":"N5ikclu1SYMb","GLAOUC1oLyDC":"tJYZnVZlGKZJ","02B4RWn0XAFh":"HqlTHlU1KbL8","HSQeHiobnQbg":"GWqy7pKwfVTV","UiKHdNj6IinV":"P9wHiq9eW2nY","xb9PeE66W3NK":"UDF5caaV763Q","EVo83T1yjrkS":"SVzZcRJdayRA","Jn3H2QODZGPS":"0XXaYI45z3ju","OixHix9nZ40L":"RieuOYjRqcCf","S6s3o0JP8NSk":"4zzdw8OjLHz0","bqFv891CxaA0":"P5SwKUfOwiPv","VlprGbcSqlgm":"xLFv0r1vG7DW","dM4clMvw0DTh":"fg9K5fnigdBe","k9nUncJWE6c2":"4J0t20V0kMDQ","abFIygJSAnJu":"0vbfXbai8c6A","kA9pDLXL6eo8":"mD7oOLPrEbY8","xfnhNnfECbvV":"ELHAwmzMHXkn","Jvns5RTK3LvY":"tA6fqAKnTwWq","N4qEOziUwPIj":"8w15GJfNZlBN","0tUFrjKhm3kN":"M08vkwRPcPDL","Dzm0N11SjvHV":"zRI4tHIhP9zf","QZYYLftHrQIV":"KoHO5igOO1zy","nJw7mj5HFMRJ":"TytszYcQjBsP","q7q3oOKIZuNX":"tkwB5dH9ZcaF","6sfmQCGOGAU9":"yLwiMYR1oeDq","BmnGQCXgtvta":"IPeVjJ5jxF6q","vpm6KioNPtSD":"6xIEvDvekotS","77RF53aZK5A5":"FIBDgiQidMdM","2E64AWst30Lz":"LJh6wA1raEm1","XG41qRrqhWr3":"H4sfOc8PZkjJ","iLxY0uiwFNsr":"uajUksNV5w7a","XTrZy7KzGliJ":"TYX6Auh3tpjJ","kxhTrxFgcFq4":"eufb6QAWYvxK","uofZVYZ59Z0U":"Q4b5LGy1j7Nm","j9NnkZ64rpZB":"SWgxpxEG5tnV","GAgfSWukotqi":"LpL7buc7iccD","unsONPQu1Krj":"4FarD4m6l1NW","yT27cMXcraom":"hFSQcRHIOtUM","q2wb6Xo4mDtV":"7DUCoLCctxMa","v5FNe8EYTnlQ":"PyfYvDXu2ihU","Fa5HVfc9Ttvk":"SjMyyEOkHn8C","X0vXaXIeLfac":"mEgF55sa1jNT","dCN9aP6LvZdT":"cbaBqhuA0UD4","hjHAyEssXjTG":"Qp4cOccHsPju","RkuQVRwFE3J5":"y22l5VMEcLLC","YT2Ag0XEbE1a":"OLzzh6wUZAuM","g3l1dPHTN7Ve":"RlK5ti2s4g3E","5p6Pp2OsaVyp":"3Rf5WITz6bs1","yCQtqvk3pLdK":"2KYToJIWG1B3","qZVGDqr24MhV":"8TrWLV0Qy0rP","taFLEB7ArfrU":"iQjGZDlEZl6U","6cQ145oY4A4p":"5xV8Ue8AXadC","nZHS3iAvoVSo":"0XizdEsgkktX","hzFdA199px8t":"EkRFeVh5mfYv","F16sn2Yozum1":"JAKDZLtwFH7n","jCr0neYPuxmS":"NvfppNkLK7zY","gKSRbnhhLkn5":"N5QAMasqwoUG","G51M4Q0PvDtC":"K98oVyWFiVjH","6WBO2grKdkic":"C3fgMtK4C9hK","4A3Sk2HFkNd6":"qTfUhhRikLTo","MfGlIGysKYUL":"4k6nJCeoWy8V","KAAKUvNmxE42":"EGQIFlANBmY4","IPsT9M9iuHOs":"qwrqWL43dk0k","YkN8Iw0LX5tN":"AApptQ4Y7LUF","MA9kvrpjpYuZ":"jFZ6QFiYm2YS","CPqcAqC6hiDV":"rD941ueBfXIz","vipbSfL43vvL":"Y9V2hex2jYW1","dpQoyJxFGTZQ":"81VEnr3l7gDQ","1Zfgd0qQylai":"J0tjaEZtupos","tVFZxfW8C3DZ":"vxtIDul3UN2P","sj69zpe1BMum":"KRpcOxK0Dnex","17vTIBtmhNYX":"K1Qb3DZiGwk1","sMaBuvm9tahr":"yHvp4NbVkypR","51nrytdrxcfi":"FaWSZ7U3xoyi","eaEsq8Cwsafu":"9KhsAP482kpX","nuAw1mh84Ifo":"AvuUUCK1w1Yb","2ZPKJL1gkFVY":"1cXyVC5k57mk","5vovD6fYa1HQ":"ukrKugLso3NM","lQIdCoS23quP":"LBItzjztMOXq","CBKw9ykFT57M":"r1MQXudXSrMT","WDww3mgpikzB":"ao9RLQNGBdpk","I4rsdBLakgNe":"gR7yzGBPwDFR","Xj0480xJvWyn":"frnLKJCe10aA","SkR5EYxpV17B":"KHvPuxfgW0bY","icXv12FRodMj":"jGMUc2P7s7uG","d6R7EkaunqzI":"5ANoqQ0AJQ52","Pwcuec2Tpq8n":"JGya8BYqufUj","AGkGDIwRRvC6":"Q73Pz9b3u1Du","qTnRWKjp9rb3":"3OSlfXVo6Icw","1iB4iCfJCnXa":"rQRfHycpX6RO","jtiRq3qdOhkE":"Nq4W97UVSgpR","g8DuCHYT4ZvK":"rQGETU2a4JLX","pA9B94YTS32D":"w5TNg7Emgfvf","2QtvXos6WGKe":"pwe64uEaZlr4","Tq0RhBJetFMb":"ksyRsakEvA20","cPtUWBABAwuQ":"AsPDydKqaesI","L7IEyzeWdM4O":"Qa6otMJRPlKA","NiDb0Y8LOpBC":"yGEW90nQhHB4","BAgZ2b6tnNYQ":"d7ib5kFNioTf","fZqTMJbO3zIj":"MBXeeZFCYykW","OHL9OYSNVAwy":"2UgZsDrqab7g","GanfnSIX75hm":"hgv2ZiVQ3RgZ","QxJdWBlJRwjg":"0N0SIKU3hzu9","uZWTjqCqQie9":"sOxS2EPDs4P1","Xb8hAlDoEA2f":"0Ngka0SzJWTT","IuSDhjezLplp":"nN3JfNW6xYuN","FAX52yphHPKX":"brVv7RnrRvEj","4NNRjMVYmwl8":"fUXdTYB2YV7V","WdnU8RIo3e3H":"H1oPQFimZpaz","bjNHTuNyjIKV":"mr2E42R9qZEE","xmBxHsxJJF7Q":"44xtQ2FxFSAE","20HDk1M1IMFI":"dJ2Uc3ylDBz5","0YQysttaIPrm":"zJn0Y0WQ2Mhb","R8UZrjpYlmOh":"hWV4rjdYf2En","AqjPB2zfVXLm":"OmpgHrjBoPpw","R2dUAnfdpmin":"672jLoj3CzzV","IDTrrtZ7X1vl":"Y2Y9AHPBJVvl","4lhktc31Y01o":"81qoUxe7kIrE","GduYb650aRtm":"hCC7GwlZZ10P","9dLydKaNr7qz":"44yCHWrAsPIn","9vGIgVPHctKI":"qDRqPYUZl0ah","7vBoF4WvjGrM":"9bjirCAYmXZ7","R5LACJx31L6K":"f2kEXaK7WGn6","M3kiKkTGRpPI":"HMVLDO5uyilG","13aVxQweEmXC":"efkweexbLC3r","5FTicKBnRpcZ":"Sh1t03HjgUmN","G23Y0lrqunXV":"Q5fULK3LLmvF","KbUcZ20WBd0X":"Vv2i9Z1bZqvk","2IolXRuc9TMk":"Gv2cGWOnb9xQ","FQqJHkavCkkG":"MKq3sfdj3LPe","MSCVsQclcEPC":"Kn9tkWZKQZse","Oz9I5a7xd28b":"DQP54QJz8yHT","pSFJaofUSHla":"imAPa5uf0Tmy","KTQIAEtvRHsR":"znqG639jZcRc","nAQenoLiLt6a":"c3UyY1lO41HW","bLE6vC8Y1YmT":"X3F2IK6CqOKk","ReDMY3gYgAix":"fOzziMIdU5zi","Yuez59GyxsI2":"k58z3TqnDbun","ktaGaOWUoNp9":"EKYapkhlw2AI","6wMcV4li2wlV":"BGV9qBSU23gT","A3WGQofNNMqF":"kzfICSygLBlR","qws4UBH72nka":"JdYVBxlHrv1P","lLo1tdlLKSdG":"d44cDHvUClNl","oBBurpTGkuD0":"UTUANyC8WZaJ","uT0K0hI1FBFD":"xFeKjZ5Er8Sn","VtcZnVevLz2Q":"taz7rngciNrh","9k8rSrJ5ljt1":"sVKbAbiWX0Kd","pAkpjWCX4CoK":"FDd1zHg9fXoX","a5nwKrL2ZQw1":"fadDSTWctwL0","oBErGTuAQGPp":"sjEY2HUVF1Il","AYrHuHUQEjyT":"Kj21psJlgfn4","DJvtI7yJD1BR":"4cUbXqS0yc94","93iDvvZRWYZ3":"ao4TFZ1rfqRm","K94zCBHNfFvI":"6t5YXp7q3wXG","5q1ZIQQ3zzDV":"r5KRymeNWzE0","88CQBRWOc3kM":"XMa736aDEahU","zU9gk88Qw43g":"ioLbZwCngmSV","o0xtIfQ3vhU5":"rWJMYeNQUb6l","kdVNiodTJalw":"yTWoCU5R4eKO","MAfSFT5vT2sv":"PDoWUQh76Ajy","Zshf9TgierHR":"ZyP13T6anVPT","JdR44Zuh4ioZ":"hTwn0VXALuzA","3e4D0LNDzziV":"rtL3cbsFtJjs","OU2La7dVmGEe":"LNTTYgrXwBkm","4SwYKTt2rgBz":"vlzCocjZxMyU","z8RxxYsPWY0Z":"EXKYMqEkqEP0","RBHNNRxMkqRI":"aXk1WGjlephY","CjusuIZ4k3hF":"khFmjCrHMwbz","zR7awrJEhRcg":"0PC0YD2agDP5","ljg16WcvTovz":"DvnjhSm1tNz5","y0K0sI2dXp6d":"2AH9Y6evStPQ","Huhk2lWAi194":"bl4414HO0Nka","rA8R31sB6zYo":"Pxw7zsdTl4JO","DODDi2tJ6GRf":"QGy4eLuj3NJl","3upV7RMpPOR8":"rU1lfw3729jg","tWSTU5EUanyb":"YGmuuTkwBj7b","kGFTRkVmY9I3":"ZaAnu9od8Hhn","BhoDoU3m0w2L":"Uve5Lqa7jdi8","H0oWwmydnHj8":"RVz4ysLaTcaT","qfQ77zr85jR8":"y0l7YIIPKlN3","qI10GyqD0zcy":"fEFMY3sNgsTT","Ixm4BeyRRACX":"tZQzPje2PqsX","EsOCSdx1UbI5":"2HDGESlW1GvD","QqrOJQBdKks9":"LJipretdCLpB","gXnc414Pds7C":"lPA4OICDNOu8","e7FLEfjL7WtH":"exkaBZppaNaN","XirvvbJHShgJ":"rOYucasKoSTA","No09avD6ISpF":"w2Q51dYH743f","igMEeoqM5mIF":"NCfTZ8DtnBXU","TYqgBytiayHT":"Z76i9VjhYEfq","Wx1cQTqEACqT":"oGqP1Zlgk2wA","ndt6quTNvIw3":"UmCSO3xSdkKQ","DwjswRhcsR6n":"m7pcKTQZ4B0T","u5KgDThaGEPt":"4s4kdK9iZGTv","ZXPzKgDeaMPw":"cKbMNuWtJDa6","c9RI6136FvUA":"G66AHsADB85D","w3wrHmTTdIbn":"Me9uCsKTN1sJ","syE4QBMsynhZ":"Makxbb1q3BKW","OPuRd9GezjzG":"Sjwell62R1Lh","ZcJMg5fe1SXg":"VuzqAywuFAqy","ln7pv2zXljKI":"YWPKam1lLu7d","R6bIVRMoKKew":"kuy4W3a1Wq93","PcP1iKgW8Mod":"I16B25QVhw6P","dNnKPgNqhZD4":"wyHRww2EmDmQ","4H7QhofHDNIK":"a3IziZhFbCRx","3ZsgXvbqvOhv":"z1DdTKKFn4nn","FVLc0BOpOzQW":"vVcLvgFKAzna","2JtY9PAiiYfO":"CUjM5sCV3daC","D0Mq9vQQldFE":"xzM1I7niQ45W","ZJXikqhXhrQU":"jNRsg7z8PaoM","t6NrbmVzbMZx":"EKnnESRZYvJe","ETKKq4oFKh1T":"fgj9ji9qLcyX","aUpwrp135v5n":"plhENzo78KuK","Ht2A89PnH3QR":"7IPZQStbkrRF","WRIwlG5KISDh":"YiLAxlIQ8lNJ","wpGIYGXNU5xk":"AMeT32c77yKO","3h55kkTdrUfv":"D1RdJbLtRBpg","MtYWXhOZl9p0":"TYyQXkifUeMS","4I9eeSfhUPHS":"vJ5cZt7wdwV2","xisrKvKFObBw":"Kuesg5lJhQGc","FwRcDBidcDTs":"6J3xbYnAGrkY","yeNNWx1XvLRr":"FwubgAcz7uzG","ESIUjips9uB7":"yIK8GIcHsU5Z","To5qXsl8dIXh":"v7uSmkQXOVIo","xlW2rzHErPs0":"1cMcsF49kGCK","QFrLKig9VZdS":"YwoKDa3W699h","669szo4UsEaF":"VX5t9DdxH2k1","4CBIBYUYN9nJ":"CjZrxyWMVNMx","Q5y30InkR1Cj":"rLveg176LovQ","G1HDc1JplwOS":"TJKkQdyDWTAY","rZ8TqJ6NFPAF":"7jl7EuoS0IIk","fDNT6BSbqhh2":"AsUz0KFsp4xm","EdZBq4yz9WQF":"UQ4uE4wh5ASE","kFtAY6cPkK3d":"ekaAwzxXhGvJ","Qr8aTNzzQneN":"Od1RoLReMpAk","eIXSrGGKYLJp":"DvxK6csklLWp","sQHeEVHmNj9d":"ynWHEfbQKdEf","kWU0C5a4ORTV":"dgAGeXA7G0cZ","fK4sqWrYajWK":"3rIdpny0DL1F","3aiV5AudIH7g":"xrCXHtU2foUe","07dsFu4COa49":"IUhRTiTv3wIS","LvFIor2VjyuV":"f3Hek94ZWkSJ","TKAHBpeHLhmf":"MJqt53g5KIY4","nI8E2nkM4aEh":"Djyj9eCrZYPi","K7JnIX6tQexI":"Dh9cY4rQSDuG","QUfdfse9S8iX":"GCgyAJX3ThFg","9Nxv6V7TTIQl":"TAoE45b9r50J","UbyQf4nSEU6O":"FE12PjQyA2z2","VfoO7v9QqCl4":"jbG3DO1b26qV","kL76QwivcAIw":"4hWnSHdnhsGw","rw4mTMMFZbUE":"g5V81tnlKixn","Z3ROLUBUQSdR":"GogDZwMW2OWK","81VDlhpuqAcd":"5gbEypEW80XQ","eVrOyhmgrYWV":"BoCqssjk4JHH","n6V1RoFEvX8a":"evlr3XDQAkFT","6HzIpYCcZ7Aq":"ZuwyJnimJWwc","T8RAMTWVTrVf":"ZsZIqMCDKD9P","AQqcXLLBix4l":"AEmPxyzHsjRo","EJSw3WMRPaw2":"vlzzQwXnmYgj","fmskIBh8WofV":"pwBX6hKDFpPm","snUzQ2GqZrOQ":"gVUpYIWeF4Hu","HUlOCBJfMHOq":"C1Aonu3Gqvry","A4jLfTwQGM0u":"DOgrfCKYkgmX","p4vTVzc4yiFR":"Rds6jv79jiIb","iCh6c5U32D3q":"b7ORdU4VmDM7","dDN1SkmCLW3v":"xrYMyjD9D2Fp","enJvS0kkfjqY":"cWKdyGEYfmCR","dSR2nq5KHNst":"R3qHh7N1VtVN","zThLqQ4etdqf":"zQ2XMRBDVPXD","37ebvahZUyWh":"FP2lWJfJtz02","gQqrQgFK6Wrh":"Cg7FFzlgDckj","D9FPuryceioa":"HErHnCgMtAXh","MR4SYDc8PjGg":"TVe8qAGldC1g","X1SwuY54RvEK":"tPxwO5XtNcqL","TSvZBwnO9Bct":"eqXgAOdtXyYG","A1zLcM6k8RDH":"E5E9y4IyLJkM","U9Bs0Wh3rC0I":"GidVf4UUps8J","UfEKGZjINfUZ":"IRxx9GEyxHV2","gjSbHNn79xxd":"DVRv0pmw3msw","qwBBw3egUbUS":"tr6EiF1fuh4H","QUGVCudO1GzA":"l7fZDRPtODps","rtEwDtXumkrc":"3m3wRinzIGom","TrS3gLQ2P5uF":"B1atqdOmMniG","0esDqwmgCHuV":"cp0eORiCbRQu","sptSATngolTj":"k1gHQUVQDy6u","Ct22mr35u6uq":"LF9krxFPUdKZ","CbxOU8CqpHVk":"jaCR3MVDlYPo","vYcUyupHcAHX":"wTvb5OQOIXyc","FZB1MuNF6z12":"Kq2p3SIgxQzt","iog9eDOb78Iz":"rkGsd3Lxvme0","jLFGJDeLgdzF":"GdroGeipzPP1","UwTddPtLtMvp":"e2DKBJIhbqPn","q8tSDwn5CaZD":"jSy3C1gYfv85","4iiqviHmAoEP":"7nFg0y7OHeXj","abK6Bj4hAm2q":"ipIFxpJmeeYD","t7TKf4QSDEZU":"VewrIn9OvVRu","8IfFaGo8KjpZ":"NVSxjlFVTtoD","2wtp6xRBJ9uw":"mEDQXxHIX9Qf","pdifSD3eFCvN":"3P21d7ZmH99a","e6iveJNGLtfW":"LqTZarZGDRO8","F0RmtqG4qTVN":"QPrjIx5Cj3OI","DqILmobvPq4G":"PcWxRCWVfdL5","rxR8QsO38YsK":"rQ4d2iYb4gT9","OFXw4AG4EGX0":"LZKPrU0u8QVm","PpN7lAZGJi2b":"3xBEtF50V0YB","kWXa790C64LN":"GnUU42IoLOQB","BVkxP70rgbia":"6sPOOrqFT356","J1jbvAGOPzmI":"llrNe1rPe7yB","SJboIpp4uFr5":"LU9GWugGhDO4","YENTfeEQp365":"pGqeGUKoQseO","7a10yGNHeiZd":"zTcfuHNerxXn","khKU7aNW1OTk":"iQplMvRVI3en","XY1asYJJpi0z":"4O4iXZ5e4ka9","CbsdcVODGMFM":"QQYhYBFs9jw5","HFnpbcWGfnOF":"dGAWJd3akpIM","g7zlsReeFDYZ":"RhzqWIlpXYF2","4oAdPasMd9cw":"DuOr1NJq6RyE","Qb6L5fdBMax6":"5FBLgxP01xUl","BYmugvjQFuaf":"bJk5D77TK7Ay","lGWvtbxazBjb":"0IJv1LUj3jhk","Ejv5MhRVQoad":"aexpywfiMitC","jZqYheM0ub3K":"MuuNm8RXlMRH","j3GEog5UFIbO":"sov7I7v8wE6v","DBsrcbt8ac4N":"I3JDkhbGKMYx","74Oaj9S7SdLO":"K9vfURmUItJj","yqbE2yD3yKcm":"1X99gqU599U2","4QiJogPw41s3":"6fQ7i5Aez7UP","s2gaofHLwwo5":"bx6HRLrosT70","8b3NIC6YZq7b":"9U5CRdD15Fdq","BiEHJrqOco1Z":"RzYWEZeQw9Br","ronICJThdGBa":"96mI60trqnjp","SEvcCzcqzmGE":"tIlQFbIsrq4V","ojjvCgjQIQKC":"HhNdnReEErXk","vlHcSOryh0lk":"mnEkcHSnFvGx","jO88fjG35Hm4":"VVjD8CQ9DZzB","8ZzVtKt6G8VX":"Z0bHu0rimwfQ","RDaoyRdofxVI":"d5kILvJQm0BG","lorE6uJQ3mHR":"nSuxNJBOTcoy","ZVCvVhiEUO9q":"gR6ocrsWKc1i","H9OnrchoqEPJ":"sUGOP3hCy3ti","W4YbGo9kj7A2":"EFEuytJ2Vmrt","l2B1dpAJ1BRn":"OHnwBJLk9TED","s9Vtw7wZQjWC":"6ltxZPiLWIkH","aCPHmj1B0gHB":"KbNsOPewOhcL","VxogF8OsdhTW":"wSNIAypvmUNN","uzhXPq7eGbWd":"aWO3WP47sgc8","d7PvLfSeWjF1":"uzdNnAuhnbQH","0ZZ1YGW0DlHT":"MzlFdtC1jt74","ZcTP5uLAv94t":"gv8UblHWFSp9","ZRaI7l1xEn1h":"6ZfqNfrfBZq2","OIfgUSUHajsZ":"r6YsHfqWtoTm","b8q6FvgnoFlI":"z1IU1TX55RIU","rTFfspaztA0X":"l28j6lFMB4Bf","fGaOvnDEktL0":"Pa7zEAZ43I9g","9YHXQBxJtSqv":"dnOeUb34DRoB","IKfctAnjWFSL":"Mfvl0T17eRTn","NvGaKCNOaz9u":"YhMzvdyvVUyf","soFsz0F0YP4M":"TgNdUcHiPpCW","PZM3bxOhbpn0":"DgOZ3OBAjwgx","hC7g84KCkYiO":"AAx91GHkQ2Tm","vqhGjuhgrAeK":"B16z4w4Dhon5","XhrXjPUwkczG":"NTo5QFuhhjr1","m9FU8BDuIwNY":"CZ7x8aBq4muE","kjwxk3L5N3GI":"VvMeSXG6ACVF","Tk22yNJCg13s":"lhyVFEPSghJs","K79AW3JiUdcM":"nVAlPljVmd2K","EhKU858q7tFC":"lXpvY4nu5S7X","AxGlI1UYtU3a":"6kDv7soaXOZ2","ebXrGjGUHPsl":"UzcxPSYawboH","RxZp2FX6Tcp4":"mIV4coAVFJCb","1YazMjHMKY9A":"Feg5kHpCaP9u","ToFRQVHwuRtz":"p1arrRx0zDIK","dgssm7rbX6vq":"QaGmJ71PaGa1","Dr8uTH1EJ5Ut":"0Hs6Pf038ycp","MTHZT9MW6ART":"nj0a0RzHGN95","5Wqqcw4tLBuU":"2bvSmaza0xMI","cirhQ7YFyc8B":"VX9I4RyiUscf","HTB4PSiDvw40":"CcGqruS0YPd0","e1R7jaibHoga":"3EFkvxDLeJwm","RUIgxgQiJro4":"FmVwHNdDle1U","mPy80EZvsWkn":"CeAmBiLxbKWX","9G4KTCRM2t16":"mwpwSwccln59","tMz2AL8lSSGO":"RT5wanlOLUUX","HSq9ssYXvx0l":"APhbbfljusfn","fcaEqHPslO4L":"HObeDskJJrN6","qpdIFL0rAb6d":"v1QjSEeg4xxv","Op2AdI4tzZph":"ke2AlvBS16Ir","sIWO7v2cIvKJ":"xcVoqcAwiDM7","2hurFY6TyQJa":"m6iJKxGzLFAm","4933RRHFiK1G":"e8T5MgVKXXRl","Lch0V8fhaHa3":"9DmnSbeVMf6l","KH9NV6eyaIsq":"RBB8pBqN3Vlt","2RHmYP58fa6z":"pyaMzX2j7NXk","3uQf1untjnsF":"PO4irWqtp96O","qx5fxtiv7981":"rH053fn5RYlx","cVT8GbY1fqHG":"EmrPoLODGIME","LGXfdkBSizau":"6zG4QPgqk2GF","POpOSwge6WnK":"B0r0Abua6QAr","5BHHopvSgXRe":"4SM662eJEzmb","dro4XHzTvwwI":"dIfK7mf7LvCn","eqNR7FPdu0C9":"Ymb7969xaafn","az5mfI2cKkda":"ngaE4l5X13U4","6WPtcUF6DoBl":"kMKeW4Irkq21","a6yehUKP1Ttg":"aqGtjX6v5a1x","5MUxw6kufiOC":"uCgKXfnBMiAB","TY5houlJpbOX":"osytjRGEYthY","eXFEPLQ6QUY6":"G33B0DKjFgBn","zmKt6VVeGo4H":"rN6YjXzb5uQt","ZwFqxnbao6V3":"rlWLojzWVVfD","nB5kFc1NbnUv":"B50zkOoMJrNB","hoCCsr1jtcNF":"JoqErsS0x2xC","Ek8M2tqyoOYD":"lxtENXEqzMqi","h15OCLRULcmu":"Q9GqRLPD32fA","33Sc9bI46rip":"mgL5HA6NudUN","klNB3yUXNk2y":"jcWqLakUS1gS","3wMwCRmaKAsu":"UK6N8s8ZPLus","AQK7dt3DsdWl":"zlFGJn9cdwOi","kZZFGZBJ7XDU":"gTjg945hjFy0","nCLkdwQkBE2i":"r3y8e4LiCgZm","kywnkxZs2OHL":"1iOGRuKaXzAo","EUR72tvedZUU":"0BwwaeKnBNGY","yq2DA2kopYnc":"uOgEGnaBHrbD","IqB4tI7VBdCb":"IpEjPSXBae0v","DkiHHiFDsMtq":"S2EmiQqcQxSb","Hh2KDYYNdnGY":"PSUrgidIHwvH","JYRhfYdrwz4C":"UgmuoSfU0WvN","ZCs5BVXbBBwp":"VttQupUxVwpn","9q2lDL0fujFq":"Bx1zDt7kN6oK","JW6e9amVkQLN":"8w1vdis5AOxz","rCLyjqQngDqF":"1j3VtzzmRqCj","XKAwIkpqNgMN":"E3PY0bD3RDdK","PbXBssU9Z7Lm":"P8elWkymhbz9","DGF41yafXlZ5":"HNZz8qSsHeWj","y243HpNgU35Q":"z6D1YpbeVpWM","NlQBVJHG1nJw":"uP5sP1eB8O8b","nSNqUNFCjScp":"OaqfeHSG7mtB","mtMotxU2EXlL":"IBJa4ODFNPzE","pthUfOum9mwI":"Yer1yb3o4INJ","x73SZb192J8E":"WTd75Q3XPjKr","i7KVIX05TYXO":"6IWpiDK8erOb","rsaLCWyu7CpH":"MXt1NAL4YqBu","P8PXkZpaCRff":"zei68d8BWIE1","Ifco8YR7Bb7H":"7RRNDF1FGBYN","b7HgyWrGqmGO":"hS9kxW5vNKN3","y7dIR2jxkZl4":"ResATTXCXoyN","HWPpqLaykzsm":"2a81hCojydjF","ISWXXFsjWINV":"96tUZaxPQ18f","2naIVEqVPPJ7":"cUHgVR1vxe3J","ND0v0AiNwxam":"fo7eW7hSXW77","lo670tFLFBw6":"xxY7HM2Z9nNK","7QxJm3jmuQT0":"cYN4LsP6z0GG","T5yhvzom1pHL":"XkdhQpTGckmE","zfyiO5aH1iga":"67zHwwK9nkRS","2smiUCgzyO07":"FeGKZYlgAtkm","eBY5bGOtI2ql":"R6D580s3NYMB","B64uizW87WUC":"zrddbt1q7kq4","GiqFis22iqYs":"bCeEkHnCrNy2","3x7bbhvgCGH3":"btPBmeglo0n3","SBgkuABu09Ub":"4KsrlQ7cSTg6","VKWetKB1LXBY":"vLmkQcpoFa4S","v6vbePA8KDxh":"GczEw5ULE1z0","ODW1GFnH7u9e":"WhE8Yd5Ia9aF","sqBu9DOUW9ER":"wTumwK1FIWFf","FrMQ08hOI2Qx":"huHbnmpIfReU","9AVMo5ChbdeC":"McbsOzRpJcXu","agqNeCsCUysg":"T6CosJL6Sgow","KW4GYiuE7Id3":"McIYoMIc0d0r","Tz8aZ7lal5mQ":"LfcFp5fg1Y3c","5YLGPaWWMqEN":"BXCPPsLHqTVc","YYNYkAQNrLvc":"GaOssbo83Y3G","6fxaO12A2u03":"51wvm8HHOmTU","iA4URxU2zgyl":"aTLgj8iHJpiF","VQD3IS7YFpha":"4op6duqYqU6u","fQue6kKOemI7":"AF18r1RwbROX","uxrzUkzJuKko":"nCOydyB3zo5Z","DxTu0Ic0nEZu":"U0QWeFLf5T9X","CWPm3FyXaZD8":"jEQ9XmBDltoM","yIlBVzuV2NzV":"u9dq11WZlTQm","NepGU0AOomlM":"aAUcmuR8v7GW","afeoEXzs895n":"EneSaktWyGxq","YpA1iZrab1la":"7ceUMbokO6P4","YMBUh84Hmu4g":"E1iNIOlAmelR","hnf3W7yXAPz7":"rV6IjyCcmrzM","cVEZk9UThv6U":"zNJjjP48qQRq","iS2h6bfuX1eG":"wDW1q0otB90y","ARoLIjeyFoIB":"wkRNNpmg7v5I","N0V91fCKXcmA":"t1fr5P3HSuAB","mzmNrWOjUXR3":"QScw6N3Nwr8r","MuicBXDt64Yt":"DXJHXZ6dcM0K","lGEHy5iGyBko":"bwFbA5WLa8mf","FniJ7vYzsycM":"cEc4VuADrK4g","4L62bbiVtXZC":"UkoPd9YyDYLj","SImC24Xth5u1":"vZ1SfSMPM0vh","y95n5oCS5v0H":"y9oZb3ENTHkc","dWvVnCcTHkQd":"DD11QPanzmWJ","ab6vShGxUTbH":"l9MkxjUKPUgW","i3cVKVbyp2VT":"BbGmDDFRxMN1","lmsyZzuTQ95v":"HxcuJBwcUMw8","ORkPTVqLMFhZ":"1va9YTtLp0vy","1duzQGn5r6lD":"Q2Aqtcqx9dWV","Yi8NtXy7QWe1":"D91FkFJERxTz","ekyd59Flgu3r":"CNCLfbYvPXNp","zMzOHL8tPhNb":"y5RODTEZfAtm","ol5hR4aFWXD8":"EAU1gpowDe7v","UVWg5eunTlzH":"ixC9CgzbsPVi","asJA4ekA4CXN":"3MMTyGK1Zlaa","wVuWt8zLWZfa":"Gmpr1sst6i2C","m9QBZ8ZDdmrX":"zJK3E8cWekev","KvFOMlIO2YVh":"r495ZwXElSW0","TFRZoAFtAxV3":"dRJhEeF6xtYa","UpcINjCpN0X4":"tARrZz8VkbS3","Mq60zUsVTdyG":"9tK40YRY7Qwu","Wt4lqrlEaOsb":"S5QTW1gF65eT","DNm92dycZLN7":"DVxyoVJp5SnX","3tCwtAnCtdoA":"V22zUZTUrgTi","55tugay8IH6G":"xPAqgY4RZ16s","l74p1SNLKkmM":"U6AdeluZgPFo","6j2mG5dYaACY":"csbFj4s6WORp","BdfPk9Gcpwde":"tXcVVbWprnwd","uWjv9gkmgWJz":"6r7HwEsMggfR","iGdcSgYfj2x1":"IEtkD8Xpk8Zk","agObIqQ8Eppf":"xIyDINf7pC6y","wsbYfCdkpze7":"yAF2SRKDmfGG","MIhH9XvalW2j":"ExEQYo4XplGo","yA3khkVbYyfP":"xQdE3xuPPKFt","AmrAum7uB8RK":"g7LvBKQHCIjY","6wIzFYq7FDZd":"gxKadyHfQuQQ","24XkCDHBHVPm":"dlm28GYalPdv","fne4VCl19PHw":"geyybH1zFIMx","IaP86dWkRQaW":"B6edSOm1ccEs","qut2hRgSRPrs":"lTMRtlqi7vss","UBUhhadRmHFH":"cNSRehEZOSXe","5tU4NIp6JaTu":"f4V4SDVZ4neJ","56d8S8j1K844":"nkJzF4fNXoci","pDrm1yus4MQH":"OLFrbFOc6xun","NrcFuWEYew4r":"WMvWwqDcOWpI","yesGqpEH2lGG":"sY6aeBUsZFnU","k1RLGS9qSQqm":"i1FsNHJknNJF","SLeuOPsxoVOm":"MVPF1b1dMuvM","Ce3s1pczVnz6":"rMSo6OhOGYwB","DfKUyEWuDMIc":"uL0jsuBSvTPO","VwZasseiTpEH":"jg07dg95nDz7","md7wKJxETOKq":"samM4QhTYumO","BcbNTHisnKhy":"H6pV1JqUziDL","GQv28vgaQz8H":"KyzedLbYm9ba","6U5RD2D5wMda":"OUS8SpdCtgtk","YkYgs6ttY1rM":"Z78bofIYVBUt","6pPq59b17Nzi":"dkLisp6Jg66e","YSsy5CUgH0e2":"g8ooWI63cO3T","WIdAzjDfzaRY":"dlJeWKpzLzwj","cfdf4VI92bZJ":"9oP7MSDujv7E","5palT1WCVGnC":"70b7X7d7nDFh","re3Fhm5h4sk0":"T9l63hTQNq8O","8PW7MFeiuyBd":"ZY1g5CzIcYGb","G8oSzFviciYr":"0jDLekMt8guH","vSnVxtt7Rzdk":"sYKELFCi1xvp","54jQ5JeXPc8H":"medyUXtGqVyz","hvW5wKMpcsek":"yqtc9co5qiH2","id5JdakWKtDR":"XJdax7lOshFH","0tXVKTTofGaE":"vlQ8Ucr4ICbA","5QFaXtG0mS5L":"bCPQjxTwd2sV","wTDxqPZor5bE":"0ZYSS1TQJK2j","JCzhOHeY4qwL":"85PexhPopf0Q","jMVHUSBHyaZ0":"TEMNs3g1LoHT","3c46MftzqLRm":"0fqQlbFhX58y","69m7cLyiwI5o":"MoGZ789PAEaJ","v1IB10kKc3DL":"o0T3ULRBmyrB","MyplGZzn6jFz":"H6JZR7Lr9aoz","QJALQLOG4iFn":"hMFeOgkwbWTo","KABjXx2iEmK7":"BlvCciAl7GDY","ZvcdBzygHWpA":"SNWFfTX5yTqS","sGDkUwHBYFYb":"EZs2OmpBHqtx","qhQpoo9UqPEi":"saYYGZB1PxrI","1V3TK0q8AEIp":"XuvKcXXeoenl","2SjstBckz4P5":"Y1gzUcCczrpw","IGrqq0nXIHPR":"eQWAsZtTiy4n","IRw7DXjPXdKy":"Pp0Yt3Edj5Rx","9NNTHe9b4P2o":"rlsrP5YTqeVg","p38PfJEwMjjH":"4zyao4mIcRq7","NGfURa5pWaY3":"uD6npmWKyXvA","UYfNykz1T57Q":"N60IVekuObID","sAPGzwd9BkDz":"O721BJXHWUIf","VwxPn9odiDxv":"9pOiI6gbx1Su","7R8nlj9Wlr6l":"sO9pdPe3fohq","Vv70vgrABkqb":"Uqy9WU6QWcRB","mFN2YdNqNyeT":"IktRSzIV8yaB","zaUFwU24h9xv":"d9XOxo3iYB5V","wCwDiZtWe4yk":"KAUtWD0Me8hP","5nqX1YJzlw7X":"ZdMwhKAHDq3s","jRkOjZhwaJnk":"DeGpsdz7kLlG","aOkvtPkt7xbO":"VJhNi1jfKaae","KxZeaKE0JkXf":"aPkI62FMp2JA","dAYg8cgkpSDe":"8io8hqD3Ewpo","0k1m44BpS0kl":"2wzp55s4T1b3","u0HS13fa5YnR":"aRnxPnq9C0L2","QsY27r4O65S8":"kKf5K7tDyZLS","ZDeBGET3wjT3":"CBrCsMpOowAj","8F4nFdT0UAZg":"KFtCVGvZZWps","kGGe4gRaah6L":"HRPFird9wIEX","IFaJN07EDVGP":"lrASm9tuM7vj","MBEOzclsMoYG":"9xUK4oygPguI","fWh32XhAWi0B":"RiD5IAh0EmoA","YlAJgtj6eF4k":"8BfHOOoPpqk2","ppWk9x2KPE4u":"uxVsQUMuVFdJ","cChp1pr66NeS":"MRqEYQnnw9Hm","atsOMa9ekNVm":"EplWPCIByKQ2","h6fbqVuEtxNI":"CIi7eyb87x6o","NgNIcfLMAjfR":"jJbYMUJq7zrT","yElAmo18AYPW":"R3dtwKWqD5XJ","GXx8ugeHYVmk":"yvjcUiRIqDP5","tgoyJPUyZo9t":"iYhSQkdJttNc","vIIO6kIDa99m":"WDPpusL4EqDY","sjKujCSwVp3e":"SgTaFAHdICcF","GLKhnb9RpsPY":"2ubfxl46a4Vk","jKePtkSRXUFC":"WVEvCwD3UNQl","Klxy8ekh42L9":"5D1e7Ml3pBS3","OjVNtiSTMnLf":"DFt8LKVVLujb","c2dzQuQR9cER":"KlLiUc2VWXvj","i8GFcHXldyAX":"oqSyaiClvpA4","UKV1EXQyZlQ5":"fJpg1Wj0YRxs","MooyZRkUeMh4":"ZpECJr9TRIQ2","35D1S2DMBucW":"lpZFVIdlYMtr","eJrLOjFBFpRK":"1TqcOvTG5lvD","Zs9IfKFZyMjX":"OAPhKnHnX5xu","51HL0wGMG5Xy":"6EfxtuWtkyBX","sOg1oTDGIHfa":"ikEYCviEU1Mk","fSdFY1qTrvDB":"K5W02FZrNEGp","Ot5qukdXA42w":"y6BczT4kFzpF","gIx99MU1savf":"WTcWalYgwdsi","as32VYoywUr7":"zm5Yg7T738C5","lMPwVb0hQetv":"0jzsfU5SKhmA","NLr8thisiNMH":"5wODjJzks4cw","wmVa5krMGuMj":"PM6N2qkhoDYn","THrBKBjh46xK":"sTZ3bujZCbaz","8fmmgVuEt69o":"gS3Ar9hRCKGi","bC8FZSDqpqEA":"RjDpsPJiAksh","sfIlbE0wHbgl":"rXjxZQnkcXkw","3IHdyAPdIBVA":"AvfrjOtAWwGx","ZdNp13QSy4E1":"ax3Rj2vnaC2F","ExS0vttlt7Eq":"ZjFCdFGOfMay","0IXXAr7mTMpY":"jxDs1V3p2qPw","fNArdTaIGLQO":"of0veTBHEPTq","idv1JN13RwSi":"pJBmTIVskxEJ","PHxtl6NU85Ov":"E9wjZBjcw4HN","OkTBUVMJIrgs":"OlawhcXoCcvr","74O4gSFE4jMG":"0j5dd7aN9VCf","B2hV5aeVpqOR":"lHIxtwAklv2W","mDai38kZtv0q":"Q5SHjpANnXIn","jIB1Khq6DtcM":"8cGV0jwJdrSK","0s9kfDn6D4Ty":"GNg9Kk0c8PSO","XVQhFxyJN9ya":"AN1FBQ1fIbNs","QD4wxndgNFRz":"uKkaqvaGaDTt","OUyur3FGSz3I":"xkL4gEDt7WV0","CjrVzZQsDAmw":"syKLJC1iIfeR","UhoZp0hRJhGN":"Jvu7qdo2tvq2","YC8GOqPWwMVQ":"ljlQYMnqdUsV","7ezjrTUFrrw8":"AFu2Q7oG4vHp","OrwIb1j7ixQO":"UIG4CrHbndpY","RRdipXDdgkA4":"vuvnPgYa9ECx","0aSSJWjBIa65":"oWofXb9WNalM","rkHIdLPOzmJu":"4J3KqZpyf0Ko","d6U4gMfSodnd":"OPFDMe14aw8q","4sS9i87HU3ya":"BnAVDvHmvNI6","CAmOGPQ3Xetb":"vdYbgNXxA2GU","2CUOWM4IkRt4":"sjq0f5R5hnkQ","Am5i6rWMLR6w":"KuynAYXtS5Oo","3eaJkiE2tOdK":"evfviYk9rfjk","0nZAT5hTHzS9":"R6w7x9HX35Qp","lGENNG9f2XDV":"jZj5cleB3gx8","iLjhxHM3eAgh":"lh9tDr5U6sFL","bp9hqYRVDZkj":"v66vTGPuJomD","rOVVOaFgCRgp":"AEpMd0cDWQbc","ZmegydwCgphq":"2zQA14q0hQzZ","lPHKGTYqTx68":"FKiILFlkdQSe","CQNaDRY4BTJ9":"U73F8Jrdwv4R","owSUpKe4pLIA":"2AOZ66lCEfYd","cThKk7C60W98":"mWbwBROPUobt","BkK5cXcJ76Gp":"558z1cKNGCSU","xEhHgUohCbXS":"ED8R6rJr4B2Z","hyNiXEWToOCR":"sPRF7GbRodA7","Pc4Rfsw1c7VI":"YyF7I93VCH8O","naH4DOJdFB5C":"KqZFxVZyX5U5","sG4lnqboZs7p":"ZMEtM5j2itl3","X297oySdBXDl":"0D6x2HABKQR2","7znp11WAAoRJ":"OUWV0rY3v1FJ","o39DpHdTGxU6":"yhBgXGxjYzVt","4TKWESf1mXrN":"Xts2GiZuUgN7","WagWRZmgunQO":"8UKu97NMwSS0","jvei8ubof9t9":"1MhSpy2tAiYU","QUbqllS41B8o":"k2oBnmkTSuQl","Wv5pt0t4Wrmk":"72H74pL9iwRZ","gsVJVPPoV3YZ":"fS6X6sFsXy3t","9vJiXcgMKhtS":"70socRIAJmqD","4MOZohjjLAfl":"KBS7MXG3JEWB","H9CovQ1nrzJi":"9H8n4PcdBcxL","x200hSdTGIXv":"vwsu3t6du2bu","shWyqVsBrvHf":"zw21aYRMUemw","0d7T1SDNZ9y0":"kQYyCVDFADyO","xChiEiEPNCJg":"lIH3PnUkNMAo","LpCiyzdvdzhl":"Nn1HcUillWVM","md3ZMNZVHqz4":"oi6f3Yg4Y9SK","f4zdHpkYZCTR":"N04LLTjvNmii","kaySz2fWn41M":"YdUEtFAcYLy0","RJku8jTiJcP1":"YlduAq2H4qrv","gQ7tma1VWK3d":"NHP9EkcoxfM3","RnrJpyQRUFp1":"k2HtvJiBUj4R","Flr0Fx72yOeb":"bT5M37ch7BL2","UFESIaecROYN":"MEhbE8poi9O1","N0ZCoLDkpmRo":"7MCQ15hgQxeL","XAYA72N0ZHy5":"WjMSysuIa9ti","OrxbU9aAyN4m":"3LPPuJsQlckJ","D4laT9CKci6a":"KMT0fPVa4aqN","yzjaSI8d3vkj":"QRWjjp5EtgtS","zPwTDTmTWeHe":"wbUPRbZ068N3","dIYQyXaVVKF6":"JASfqFnNoWeQ","E9C4nGosslqN":"r6tWspAFY1xO","z3T720rDopbf":"C0qSOber8zZc","KFWUiq5phuVo":"rn8PW4Lq7Jub","3AeIvjJo5qzm":"TpzyGc3W3Wui","xy0DAlNgAQ7E":"WpBVAopuZUjX","HQJsGnBmncqQ":"Mhey7ZPSM1f1","cVQECSt5p1Y0":"w0jBBaIPnYuJ","0fSIIveRk9ZU":"4sPByCCtK34a","QoW3pFQezjhA":"zBl2xIdNEHGO","plAPTilck0r6":"HHMjKfzOaboi","8kcULibp9S9G":"xOkOej2JyPkp","HHjUvBa8HA1Z":"vQRNZ44Hwkmh","afiqN4atH5zK":"9BQ0nnjkPwuz","AzxpuScHX40F":"wqERldRO7gIC","e49IFKEBNDFO":"1oVCssQZIRIj","nisnkt72noFJ":"cpGhSGRoy8h5","ZkhknZlQZBbW":"qUqgh09GNG7s","q0CselUYTiUm":"f44HWWgvILXi","2qUEX9Ifhvi3":"fgaB26fVPY2E","DPiGw3FXSXw4":"eKr4WdFFMnZt","qJuLSw260XvM":"j6fYyyUEtnfj","4sEGmGZDTDoH":"EpwCLNG0tzEQ","CMX2PocgZaL2":"uQkzVmObivQI","gbcjJJeFq7H6":"NsdcXrf3tEx6","jybgzChRnBI2":"7RMNFAg3X62z","zPggHI77597i":"ZmZUI9fqg6Cn","hzjC0IIRfe8E":"ixnupFkbixXY","4h6kSD96x4a5":"VeNChd6AiNq8","Y3HluvR3S3cb":"njgiIJRvoKq4","20ONbWEpTwi0":"k5Ij9lzHyaf3","cgqIxDOQOiQT":"Y5RaMpgSfjnN","RdFh0HeZ2QUY":"dSFWlmEiTwWN","jlMGHhs1OCEt":"g2RtUf1IpD5G","e2vs2DMIis84":"xlgLlLSzYPbR","ybGRXaFl1xEn":"8tWGDoHwfv63","mtoY9MDCPAoC":"7YeX78LW9BLG","p1VOWvbLrp4D":"YucBEINSBGpp","kEttEXc0VbdB":"9tspuJ7aSqqF","igAPOB5U8N3J":"vZ4l11nAv91e","2zBlazETbNz5":"lbARSktfd7PW","Xm6zzNGeM1Fi":"g9MdIZc0zI8e","mmFsmhVfcSJS":"sJvOb95njsSZ","cv3sMUPlzglj":"QvfKlv64LeFS","lEhxs1dvuEQ9":"jIk06BeNxbRD","Zo7ejFcV8JiL":"bI1Lbu8q7QHL","q40yueeoEZIu":"Qf6LoNZrJy7j","9EeBWeZWbEyh":"Q5NeqDR1CkZ5","nL55k1ALxe3a":"mY1MMt68JVNc","9CpQWNFr0Gxi":"C10RJtLggFwW","3oNPLa41HW3k":"FgLcOd2hTuNi","hHXOpk2NADhx":"0PiGajKyoqI1","VmoJbDq07pQ3":"dRzhpycJEcLb","hBCpv4Leop2p":"LsvXEwKqv5Jl","wZliMSGuMhHb":"RVxAIVsSiCii","1DqxRuhYYhob":"1edYYaZljnNX","5SlE71JR9dn9":"MDwREhcAfbxS","wHEGDvojTgqu":"QWRd4rqPrhfx","pw9u3Wa4hQO7":"7Dkw1i0O53Ez","qLeKFk21gar1":"WbQdF0Yyu7XE","g9zkcdLhNNlM":"yRvI0av8YxIV","AojoYtuXMdR0":"XyVw58Jby74G","RLn9KI1fxkpt":"p1bPNaBdVm7u","WpsK4x9fRJnP":"PKCljpMh2ocT","lYyhpAdfWLKa":"qSViMNNXmSYy","fuX8qc8SrFhJ":"jOvhV3I5Vju5","Ys2ApedLYq4Q":"ApJKG4bR3lgG","K7q3nuyRzGQs":"CFzxQ84ln7qq","EGZrskfvoxHO":"ZDrakAfvbHGy","U9J1TY3ordA7":"u6IIDstzlE5q","eLjc04k798Q1":"RMMuYoHkDExw","oSCvmKkaZ2em":"Cy4pSZdAsQPZ","Gmgpxj9hJCtf":"3IEASXtqlfGi","xwJVw8vfjjLA":"9EZ4ACk8I4Y8","n88IDSRwjjV0":"2kLpQnzrYM7w","fkAWW1wH2PjM":"sWpiLpWy1MTx","bL4tmKZNIFBb":"wMRxbaMXVbyS","rpAMJ11CvYDu":"CKw7qPfNYOfT","iFIXY2sQc6lk":"giNNG2KBgobP","LCaUSmMOcvfX":"TGzQW3NVSMEo","zLmVBvzLUlNm":"eYNhoPjIfvdW","Q8aIpISzqeWs":"5zrncVYA71YA","kEwIDzl5ROsD":"RebOFT2xVEAS","iB6TvnAmmG5E":"aHwmXIuYDGIu","Wdh4jlPb1nlI":"G3scjvCgNKTg","iveqmhULFpVh":"qoGgAZa7x33t","BxW8kX9TDAo2":"t56Q1dYBafJY","1k5ZaobmP1JU":"MCd02utErJN5","JFCniisx4Fmx":"A7PmB2TLbno4","XWHKJeEUcCgl":"qLydK4pCTJmv","Dq4LBVTlIfac":"tHqD84hoW5fg","tdlHhwUgIvAM":"ms3FFhrIllS0","SV8pIa8PJVkp":"NsM3wGL9zwpm","P1G1Pd5mE0qt":"ZEVW7WOlNYQG","lDxlI9Vr9kkp":"jICTL8B4TuLu","AHXDTEU7sydi":"81G51jrMcnnw","dTF6iEhwahZp":"QaTgPzu5lkFP","Ch55iFTEcvBS":"VWciyKsud97A","EnwpwS5fUrXu":"oQCo7FFWY2Qs","Xrxfi7sUMa7y":"dSbqQRRQcJ1M","Nfyyh8b0RGlo":"ZbiAdFba5BoV","JgXyr495Sjuk":"ugKELVGFy3tZ","jCwqTAdrExzD":"GEzN6jaQ4TLI","RdEkjWZCfgaR":"lxp2AzASfh7E","fpQUKYVLEjyL":"QEoWCv4Fx3KR","RSeySgj0VU75":"Ht9d8J4GqNvh","A4jGh3aS3Qpd":"dhnLsV6dfNv3","BkNlXRSAkpcf":"0hKJGYbXVXOu","ALrHuhphTn8b":"8bJ0Y6XODvMl","jKjk4O09VAqT":"3fGpkrXyvyoI","0ukWmeKFN4ca":"uhydLfSz8NkA","05jsO1tYaz3s":"zHKy0VYy8EFc","jFh7wxAkd2Hz":"ikno67wDcObr","0RjEHmtnDpWs":"stc48LzyKEgr","6RdGCygxWj8S":"crZfqadSIbLu","bZxa74vjSaWR":"ohHkS3ETTYvy","LVeSrIGQRqEz":"UpG7gsNfFrRG","v9r9LLnLR5in":"pgDLTvOtR1XA","qcNZLPRUGL4J":"JNiR6tt3wcYR","vn37KBXGYLhV":"Re2xEnU3iqm3","v2yZRhWpK6ug":"7PZUZk3DGcfm","yeXZe0LIN5hD":"rN7PlP2kxlwa","Y2ebYCjZiDW4":"cCh5V0KRjCnh","sStYw7ozLi6u":"DBKRAsYuTh1n","544MRDd41L7B":"Iamyvlq6Yo3w","9NYsDS1POntc":"2z76sAHJFFIp","XjtJCf1PAnQx":"ejfxP9Yodvug","NeV6ui4ypqs4":"QyL4eTswukzv","AEhlY6gyuvZS":"l5MEQm74ELSs","gZANkIv2M8As":"yhhETwdzPp0g","Pj8m227Qqy6F":"JaRRZ6iQZndP","tyScI6V33Igd":"6lAZT2kPwFEw","i8NtTvf2HIv3":"nkR7AEYB6Do9","9CZaYtHPOsjU":"B2DiCiluYHD5","I2Am6YwH6k2w":"1AVQRZlhs6nj","VDTMJcU2sq4M":"iLmO8dzDuuMU","bs8bo5T9gupO":"aCHXLB7RHuKG","AAAGVTZHdpV9":"IYB7FmnN42T1","k2H19rQtrVME":"1lAW7XCIebAQ","zWJHT0d53MWb":"A10zq5s3Yn2O","1ORvCNNczIiU":"dOBqNU4oCoxq","TeZNrrf31AtG":"5VsnDDdrkZLm","TTn6K0Mprvir":"qsR4vkoDg2Tq","BCHRJFxQLwE1":"N0cmDzjF3TK6","5frFJt3RR87F":"0Oc5xwVmu4Kt","o6eWB3TFjw8Q":"xYEZpFX8CKfD","3HO7sRiXlD5O":"NNz0lSIQawQg","Kj3niWTRX1N5":"ckds8jzYwNcx","trU6tG30gG70":"fGqe0IKXcJOx","GjwMSQ0B2tjN":"KgG2gKoe29hZ","IDlhhEvhJxxW":"qkDAKWGRM7eK","G0D13XZsSSL1":"4VMV76mxHiS2","wIv3qIGdb4Js":"Tei7OfLDOqhk","4KTBbl8oBNDg":"90ymCgSHeBcw","MqSx9vcL4n21":"W7ish2u0nKHR","feId6sOpNTWk":"0ytsNGIl4Z0S","ZvdnSKqIQ0fE":"Zth5TvtID2z5","ONQ0A2adoPSO":"EplbxFMS0T7I","BPOyoj6eOOhS":"ArUgvTvqVeil","ZdK3WawfJ3aU":"j48Z1JdsOuDT","QmSpfINvZV7t":"iRtrWkcyi3w0","aLOioVTdHV0U":"ZeiET8ysxwwS","WKKDJIl6Y3Fm":"Dugh8KV726n4","9XzBaoPrE9p9":"eN7PmfE1C2PK","VASAokaTCp73":"EqqBKrzkbT7j","Mmoi3201ezjN":"HbbpFC4JaxVH","N6tLEUx9fBK2":"93SeYjeqGvvQ","nLODhVxi9kGP":"x4fYXTQ7Dke5","hbLa1QRj4P2r":"Jm07Tu4roe77","fKD8ws9ABPyH":"fYCTym5aCGJX","kGduw9ZGKBJ7":"X3Hj1DdD4UDb","W9x4ivzkGNcM":"jDnULGAEORhj","e87Sq5B8joWJ":"t2Ydw2glV3rQ","An2RbPJztJXc":"8ZoYD5gx4ECT","OtP3g8TyzxhV":"nmfYvQhPZ7yw","ZzcFg9ffLibO":"qkMXh9FQlrFD","rSAm2vrsUh2n":"nWnYuILDnjaP","6Iwe8V6IsVWQ":"mGZlhYLlcTbq","SRNArKe0sacW":"WkCmbuh7sEPx","E7nYMJRDxWhT":"fBzMGgqakZia","AjD4SS1HIoNI":"0sK10Vvt0wf0","ZfCvAhYHFjDa":"VAepLWs4Bpyq","oHI0O2rcu3qz":"OxOqUMCzbIQg","sKRcyD4S4dcG":"aGOhBEydVGY1","WCYXVkUAFahR":"sDxdRIstURhg","jrnR3K6hHTQU":"p8N17U1v3YkY","AbvDRjJyAmj7":"H0qVPa6INNiS","O7Ls6yBcYgQ7":"JkzLr0TRAvqN","oBvp3U5fJX91":"5q4Fu5tR4Vvo","t17g2pAZGuo1":"H4erSt3ZdIDd","2AgjJELuktPL":"ZtmU9R4wkfwA","Vfa5n5sRYWo9":"ZBPWBk3N79x8","amha04NeBclq":"ykwXGaphEadn","J6hVUxQSrblc":"d0KUVdqBYwNc","Fz6kqzNAy3ch":"ZmRy6ixmFnhw","c9bYYhjjGw2m":"7aMjkVIIutUW","1Nsaal9tCJSQ":"oktTbSguRft3","bbzJmbe0L6er":"51QEYTasb5fn","JmEzNA7tMzdq":"21T0tMt3r4Qd","Hp9GrhiOB1fz":"HfKXdwT2HXKG","vB0CbG5muU3S":"9swNyOkiLybP","tjfIyyW1gOXD":"ZQWu8OMs0vin","uioMXineNuBc":"jrkylVR51RcM","QYymn6fK77gw":"OFSrdPFDiz1Z","eh4cLWQwe2YE":"lljBqCBfkiMD","qmGcv6DBneHT":"lk0rXNoHdZzO","OYNEWmM0pIKd":"6gvgEvctaRzA","mmy2wcPuMEUa":"HMBCzoDSqo6E","ftL6kZ9OfNkR":"L1eyA9g3gTWT","knib8B8nc6yB":"wK88ZFquQV2D","seBzPfmaPZgu":"lulqTPz31vvB","RZmcPhHyP3Ko":"btLDC0sDxCgT","Fmk1tc2FlbtM":"vLaG7J3ZKRGj","TejYLl4BXqVb":"7A5bWL4xthxz","S7SFlZJNaGhA":"rVFGxHaZYcFh","OIBKRO8aR78J":"sUjvdjA70d4H","IoOmMHWbuJl8":"jh8Q9x1ZPZLu","v4G5VoK6sdtQ":"Stx8qlzJpxNc","vF8OFKs7yPAb":"30RAZlOnIWo1","4o5jz1KKNrJP":"XRGaBn3pXPRE","SWVKTohd9cBC":"F1DJJQwtdWxn","jYOaZf05B0I9":"WoePjGGKdsWH","JtdkesSNIMzn":"bGC7rTYqI4qk","Qv9I9TInJGG2":"cvJ5PtDly0td","VwtKjgToaC5V":"5pmXU6QU5VMm","rUqUmxngRbHz":"dgQ67rTd5geC","sTfdfMHrCjT3":"KtgnEXRZC6N8","uFP0WT4MEutO":"h3IxnX03Rg6f","ynkPEPzSIoBt":"F2k8SXfO1mjd","Okg4SyU6l8O5":"ZirmOQunMHM1","yJgHzSW7cxcb":"4mdtSJJTae2q","84xRcGLjhlzX":"BQV64jOjQ1u4","vIAx6oz1zfaY":"ciTCdsYQXqQR","1wSR0WuvIcdt":"ZzX8eAYFDyy0","s0NWFAy2rQaI":"b6d42VSorPhX","wFe0fXg9yS4u":"cl6gsQl9YRlZ","WCvVy8TiWKdg":"aCy6FG3xo1hx","2ZEMYzH6nmug":"eW8nF8iupF7t","85L0Z7iVIQlD":"DYFvBhBJ05dV","lpTTuId0Pdg5":"KoQGb9nbOKeO","XyoBv1bhc9ZA":"6BL2L3S4zh62","mWWyztqpNjNp":"qidBoILh5Lwt","RIkZrqXvjLZj":"gwm59SX4S5SU","GSpkF2PW63Gw":"LcSIDn1jXpmW","xpBQQ3tyX9Rh":"05VO50JgPVky","nI9gi0w6xUGA":"IhdeeDaIZNDP","H0qz32ibHILt":"mGsBd77Fcvm9","1uwXPeMunkTl":"Zifsu2lHcg89","Mtx7Qms72cCN":"EAbOIye6qIYi","mCsry3jAknNY":"HJHGH5Y7bU7w","8Us8sutQ96sm":"INUtL1bYdACR","86lGPPmP8FkP":"ZSTEtcffZWqe","hUWt3yKJpqqj":"b4E7rokSUod9","QqLaJhhGDDGw":"NMLE6NwEqlNL","3p5rc6t2v4gH":"luaBk65OlEwD","oWGIAKwO0BOw":"D6upsTEbE7yn","VncXVawJZmQ5":"yAiQe7iJMdnZ","Vm9tFJdXqJuZ":"svNIZCt0eNXU","lQDF89HUpY5X":"NvakIswTBCcn","L8WOElwx3Hx8":"4XloUIKoEVzo","YuYGdhZxmw3M":"z73WKuNgWXPm","BC9U6RZmANs2":"vHnTiebusJb9","NtTweXRp3BKb":"Ggf9mgaiOkLW","Z1VZooQ5ax0E":"RNyTo8h82Jzy","HWx5JmzzBY0T":"ADhZv2t8jpi8","QxC8xZuPoYXo":"Z1kdc6YxFQ4C","6LwuIUkbJRwf":"qkxWa4KVsIBo","9Ec435WncmLI":"lUCZH9nslqyP","r6TN42oW2LGP":"rURaFdP2ZXgy","E0zwKKkHxHsn":"CFzzgRlipwD9","8VDl95q0UhiK":"zbHxc8z9mvFP","Q5NYXQ9dzhBH":"lYGpsWJGT8k7","UMGqBfwvtFX4":"i3G0gaJuBZBs","k4GYzEXv3llw":"XqDt28RGFJm8","CzOupCUlJ1IB":"XHFQNRwepYCK","1cnm4QGuxHWz":"QWVwDZhTfpTH","Qat3IHrEfyto":"6gtgJFsYTPNx","ipmb1CYEeh19":"Q6zCFwkdDP0Y","f4qPqGkuvA8P":"5sZJlBy0ACWY","R4UQfXC3Pek7":"Iq5VilCoViFW","8jeRNTfLuLa6":"Mar3iCWo2pN9","cLEMpKAXqACa":"rF4o9dDyGWXf","sBnycaPLXmcY":"d97pCyUVav30","E1qMhiGyRjba":"mCqYdwyfy2At","8aJt3FDhNvSs":"3UxCupg1eTTj","X8ql1KCzknae":"yA2bFHwNFuwR","5qHKr6qoUXCK":"Grl8hOGT12ut","ySRavR53rE4F":"uLHmbtwj4PvY","yloGquF1wozm":"2j968kirxSbH","pZKkJattMfvA":"kbvfPZai05ee","KdPpZHn2sBZv":"zNeDyuLuJXfN","AquZk3jJoztl":"38OzvXKFxIm3","O3oTnV7U8iAH":"n0Zr9lbDiwLy","JNnVpWzgS5xO":"h0PB7CEUYtlQ","maWAvYiF73e7":"REASMUPQuMjA","YwRLzZwI2p9O":"r3DoVx5WXeog","4dzeTki6xlYn":"7fkSYNcoSIKv","B1I2M1bNH0Ul":"u6GVgq6fHBKK","MtIWay18YmGX":"U22Ydg2vG5t7","CTOITWH1bg69":"x8d6xiROIpLx","MLnGarsemORO":"xHCD6XwPVZwZ","6mYL4axQf5SH":"iyM4jXWH1uBP","4zXTQ1kzz83H":"NvLaPP5eiKiy","ur4JknIybaxe":"8YyAmdBw4YhM","RnYDxbWvMvHq":"8E1kwK4lCIvM","mFhvUnw1EZlQ":"2QaGCWoyQPPq","71KT3d5BnBcc":"P15EKvHaPwvd","PidylzFTTJaD":"FZokNS3PfxrF","LW9aWfjEcYK1":"c9wS5CN8k4ER","i5OnPmTmMfDD":"gPGHwaDUj6FG","OeijXSOmEvXN":"vhFNuPJCcKk1","1RRlQXbNFOjq":"6bAvf5dyH8Uq","L85ar92Zye7B":"4e98uLThS7JA","BOc2nKoIWqgo":"B2CDdtH1N4Oz","ya3lwFmLa89Q":"IaUbinVqu2s5","QNHphQ3H3vbY":"yNtL2lAtdGBW","9dZHPuXoFaxz":"J458l2mObG5e","vWx1NznR0twF":"CXEoLnEnAY3R","kplSDKTwi88d":"HJqJsQ7oEa30","iuQSAGyem07y":"fYrg1fkLBX8A","Fv6qCSRKWagJ":"hY6dmlY3btHO","iu3YuD5HqPFb":"y3vxtHOzzYYb","XYG6ZXJviHm3":"eUypEcQWPFiL","EhhprrUclLPK":"lxVamOXxmquo","YZ2WN3V9x0ko":"s53ec4HgCaBI","KP2d4dMEmkQm":"zap7Q3LtKEiN","MpmEtJw66hJ0":"aZza8EAEiQtR","XsmrnG6J3utc":"cRXuqGeAYDAi","tKD1sRLvUXsN":"NbFsak0sY7If","1vxcy0N5Poe3":"DacNti4yFlwf","EFhEiHPFgCKD":"Z5eDtM7oyAl6","4pVRjY48TaaS":"ifVylsrEfUrB","zNM3oYholMU2":"xRwpujPpEW0Z","YeOtoj1XXnvc":"HBZ7I2zlyGLO","vymTM0o2Iu8r":"MyY1qhR2bKtE","yIY3a0VfD7qb":"6Ga30617ZRyV","WTqFBKgXxjKb":"E2BIpYn6BQqB","a8JKdAdY82YS":"eqKUKUSOQjTK","YpCGHDvCFTWY":"FuHQIQnObuYi","xHF0UFan2RAf":"e3XKH9O6Nrc4","nMB8IfXVNVLp":"rqKD8kF7FpeD","hWIWsRlXvENd":"e2OBBQMHu8yf","w74wGwnK6DLK":"ucGnQxwhCSSK","2xtutdExYu4g":"hyhQFcCi0YxW","z1Fy6vMXOEIs":"GFFoBbVjVmiw","qcl3VYqHNpNH":"gSGO5sfTtYpY","t5vXgJbhHFxo":"wKwvhmSlAmGh","sVH9xyz87r9k":"xRRWnlGgwRAX","GHMUMMykgYiC":"yzsIEBPdE9ty","D3eVOHAQP5VZ":"HnbmFbWQ4WQT","V8ExFFlCut2T":"SW5UQamFg73u","GY6mvwDoe9IL":"BOKFaRClL3uS","57d6HezrE6rT":"Z3Q5GcixRFsE","qP85LuZIeWnM":"t6d9pk5VgnC2","TxHYXIboyCcZ":"XejU4iGbwly6","qC47LhdD3Bag":"HP8QOkfJ2Zj8","tThtshHHeZ58":"r3kwAlk4REUY","HjIJB17XK42n":"JmAHdf1FBWSw","Rlqqyu78iia6":"18kCAxuRzkhA","OsXVlHSXD4xw":"Vxjp9OFqeSJL","x31kDvILEBMY":"wRoP67Hu1W1l","toJLlrk3MjAw":"dnjietbEpC7d","icYsO8ogYzOx":"vL8iAZQbznEH","oiiOlAe8GtSx":"Fc4TGfzC91jO","tGlXhNuBW6Jq":"rDwALhzyBER9","6WqYMW4xoASC":"mDVo0qNWezry","1IWLFUY1T1DS":"RBcd5liOhH0o","avKlEEzA10HM":"lvEEGktudVlB","fYp8OItkCJMz":"RQXwugDanRlB","f6AOEUBhK2o0":"D9Z4xRSk1cZX","nl1K9qgjT8a9":"9MYceimbemUc","246VHa2YiKaz":"Bz2PgEbgjCOS","jdwgECF7ND9r":"P0dTvskgIDG6","gvBQU5wPbHB9":"i9Xfflw1Buqb","R8kEZk0R2p1R":"MUo3FLG96PoU","E3YqIx2OEFVJ":"6dfiIOYUVocv","rSV6dvWdClU9":"jNFwYZw1KCF6","qlO8GUqUH4CE":"4i0vd5xTPLah","6r5uhJqBSLP0":"JFNG6MlhqiEC","idE1wvhbknpr":"ZHoUX5jTuZxx","j2k9PaWbz7mu":"SLC5xqaIkbhh","xvZG4u3QBxgM":"W7MUxHmpu8Fe","CtoXRjXMhO6o":"usiydvubwt6Q","YWAvxGISVnPT":"iJ9cPPoYEfRa","JcKgwjpHaKxF":"0Wjrc2W9Fdo1","uIO8qeIqx7pA":"5RZQUtlZbK0j","go3V9H3yKeQY":"WrkH4CgXzmWG","RCBdAuY3xQsQ":"mWf4fgxWgIsj","QbQ3drKHn6Bi":"sj0yggNMKBaU","qDQEsk1huOWM":"wznnxsFzHYWR","l6Vtze404FqV":"4PQpabpg0p5f","NUs6d9AMpTGD":"h6ZoF37bT7vx","e9VGQEtr8Gbj":"rhvglsKmTa17","0Fs4ANVkd6Vi":"9xYzUS5OSZxD","VhxtUfNWAxYF":"ienugKGCsqQ5","uneSrRTgPk22":"a3rWlliOpGRr","zUA2kMig0Ekb":"SZbdkwSgiN19","WrXCLJzvb0cl":"CACx9XgGjg58","FyvXsZrnVU08":"RYWhvvEZBr2J","0SC20EbDW2Xu":"TmkZLI3SjrvB","zUSCbatb5Svu":"4M2557BzEiDP","jY3R8DzBIF3x":"CezkPSRvzPHP","UxCJGuu3WHrp":"JUbcX47gqkQK","ZXz8IKztSzoB":"qElxMz7JSHON","NjVjAsoFaWi7":"zTmd2c5en5j5","qcqxbfJzKMKg":"3MLd0jmIrUj2","ilLramP2sTd6":"J9R5yDJxyQ47","saLePXAAjqWN":"THJc8l6RvhTO","h42ABZXjNOCl":"hJ8GITzfgPm2","zSV0BXtSdLQ7":"ImfSnJSA34Q9","LujJ6rq9KBFm":"MvrgIImS3bBd","GiaWETYXiatJ":"5cpl6ZJwbi84","y15o2x4hwuLi":"LJPvfbWtDMSK","GiVZ2JatRFPt":"z1qCBU6CW68r","rFzN7w4s8jSj":"01JngRJNSnku","Qwv1KNeZo50p":"z0zgDv4Nn9fu","BZ69l1kxAZqI":"a5SJnnH1AtPL","qbuod392tQos":"YAiM0P2GIU4Y","qG7EUeL8LndS":"gx4aIo89sQUy","OxnV53Wuci5T":"uYo0MIWIsKt8","sPqygETAvMwX":"kyJwc5L161DL","Tdl4d24IWyKy":"gZa1mBOnILDV","KzwdkTwJPNXQ":"HxaC0yeA7TB4","ftuwnW6ijc4N":"QMATvQKF4AyK","rna7j1mlGxiS":"KiFRFINVPmwy","OaeL3bCnWeHG":"qLxzLEOs7Xkf","6llbwIII2VgP":"RUY2d2X6GZD1","i5u9ZEz1lOOu":"J1qGDhGXGWTW","59ahjqQGYlR9":"6w39I2TGRjSE","uvDxUOAFzh6d":"bhEaeeukId1G","8zr0WX6nQHF2":"NIZ2xull2iF7","gAWauUlGDKcr":"SaAEmYKJOejF","mPaOHt6eKwj1":"kHTQ8n67Kgy2","sNrL0LAtlH6U":"FWwuBGjDoa6c","LLEVskhu7fhC":"ZRLVOh4UR8PC","65YMDqQkRzR7":"bipn8hr78s5P","gWsxPU4FmjGL":"WIvzynDkXAY1","IZIZ10T3O6L8":"U3sHXB1O5lOh","PgWdpZmrsFQC":"sRs3S1g7qqoq","P7qjQH33FrGQ":"EylEpyv4zSxY","ggWKmfzh7XGT":"LRXtriK4XZ5g","2uw6mGFY66YP":"6Q0lUqB9i2KP","d8EGYMBcUQHY":"uPMMa5q5mvsF","lxdi2oa3mZdy":"oNl4hnuVzwHE","eCBuRZ4sR4F3":"PxcEy5mvGwj7","WvkbXqw0IgCY":"nZqSlwGWKNGp","SIy73oZ3sYfW":"8ijAh7K0LV1m","39oeJd7BnjsU":"uMWJ87XFOKTs","yQuIbgwZZHvq":"JRGrlZQ03JWg","d4reAEMX0SlR":"3I74iwZ8TUET","NpJQwWIhP1kj":"z3mIaR9kqVvk","t01vcxTeFFAd":"LDvGULTzVI4d","R5eoBUosjtPw":"KwfGWXS0ciNj","obxsZBFmmcGL":"fUHS6CreRJ0a","GJcfNNmU53dx":"ZSij20g9GcHs","kUoTrl87qKxi":"0O4wL0WTfRY5","cb6hUEFHr6Nm":"732mScf5wRmS","w6Vu4uMjC5bX":"mxIEGoewvD9V","QTiPjlzMdNrg":"KfjHzkRzeB6X","nqEQ8L913ksV":"A7a8MUOkGBDi","rpDbMin84VSu":"8UxhLETIAMOE","kJgQKhKg8Tzy":"2ZUSxy5KLEho","CU2BzbDy33fW":"XP13Eb7aH7FZ","CEXG41vXqDlx":"NlbqDcJJxiNU","F9sc5nLKCVBc":"7FtDV2tzYtsn","4Ymgx4LjWiNN":"rLzQ6owbfm7I","Y7dqu4iINv2J":"RjP7jlXupcdg","542ESVTZX69V":"CHBvLhbsEJNn","ObhsYJBYanLF":"KfhzcczII28G","jqFC19R82uV6":"eOiRb9mlN960","yk15RsPGwW6J":"tabUYVTvbCiE","5BJAdyg4t4nL":"9MpuzVjgeSfc","iDXnNW45VXkX":"2J01uZXeJv8b","6AEABwihD4fz":"CPQyQXFFLd4m","w8ZkZJiukglx":"mW037PIeo5qb","PBwR4tiIM2Tt":"kgjJqE8R1gtP","OtYf9UY42BLC":"V9N6GgoiBuiN","xpfglZKii4eO":"1adw3FFSPHnZ","3RBz95y9oabV":"O8K8gDuGSCfW","aNQuS1ejcEbZ":"Qg2jHUzib4qp","MoN04ZJgaoNo":"2oQnnkOABhuy","WeiAj31o0TjO":"CbT4PV6VbTip","qWyk1clkhFve":"wA0w3A5jZzF5","x4UMfRG39GDx":"0BZtgIgamVJ6","J7EP74JewuEE":"m2wPDCvRHaOm","5BZ69ITJDuRG":"D5N40jq0t5z3","ioDYkLd7KcSW":"AvNhtGY1J4kM","yI3gdepTLLE1":"3Ukbi3PLicQM","G8IDQFnnoxTq":"pDBPbrupYjoP","uNse7RnZatKI":"J5h5CNMhPv5m","pC6qqX52iB5V":"kWC7LYIhpk5W","fcXpBWJgBcuF":"hXfDbsJ0BiBc","7vfhvhAJCwyO":"l5VE7r3vmZGG","0YZiLlfuV9DR":"Bss0SWoPMgpZ","Ig4CCVISzUDK":"94vqGWYFJ45q","MaCqTsc5yi6c":"npWaQ4HcLDDt","t3hy1q3Upp0n":"quNA9YKTF3Em","lhdh6Jw4zIBY":"6E830tPxBm97","A6IBmG40UKnz":"oQTFMmlfp4S2","BgmA4Hxxz0uZ":"bSdrSaOgKNx3","vSP2Wzp2NaaX":"wy1D6mRAaPYg","dZK1yrLA4Z2K":"cR7yDkrAYoes","QrMBHaZWXksF":"tdOWROeDIQo0","HWKPLIZoy2LC":"lfBQmfHkrGXt","a8out0BYZAEc":"Y37jXPOmNwYV","WIKid7GO2Znb":"RNbyALvpQ9BY","U644atNjLJcg":"EZqRorRYXKTb","Qo7yropH9FxF":"ucoYgUPTvRii","2ChYrqkY1dRK":"aAVPcNmSlOvZ","Plq8qNjnAqGX":"0uoN1j6mGo1Z","SOutOeTddokY":"RhmCC19jPpJv","7fNtbnzIiwy6":"LTlSoM5evq94","IMCzbqKn5iCm":"xjqpX2lVHurD","trz01pnhmKDj":"jvFr5a23y8aA","FYmM0sBzWRpJ":"9B8ulowXHdTQ","GKHhP5r31gRX":"RFHMzmSNX5nb","8u9ccjiSIrBO":"reBQEaAFIkBK","SUfYZYyR3Utp":"FvaxuLu9BNdz","4ZJdBANBodPq":"uETH96Jx8pCH","ZFbNt2rM612n":"I75QiWrCuC0M","ETJpph5gpLLI":"DIma3upajpGL","2HoP7YVmOv3B":"ZHyZroZYFv13","Kq0t8JUxuxeh":"AZ3c6BB16NBc","SIIVlIkWY1a2":"ITPSR7v6ubX9","EbqFCdp9QS62":"qYFMwfoafHwb","BG5hPOoZdfsv":"bJFBUJto8oNM","FbsVwcp1QD1n":"zRMCgYN230B8","3G5nvs2WXlKw":"RiaAjmJ0Fj6T","T6n7vtNboBLI":"0Y79SQJEk2OH","Sd6jAyvBRfBL":"xGB3lYkbItiP","NxoPzwuPQCqf":"bcPnmijeooai","04G3Xokjlowd":"zE6S3Np8oODR","8dpq8z0Avunz":"zEbGkOsSiFte","qUnIHi9r9YAk":"wkTLFpdrW5uc","aBESJdpOT4r9":"wCVdceu7YnRe","tgpiMxjJ7ucV":"7Z6QJGTiFJki","g7nIMcZtV5BY":"jw6rSEhqzWZ8","PX1cl5e8j5q2":"fPnwZZibIKCi","DhxGETTQ0lW1":"Tx87RoO29t6j","89txoQdwThVj":"VRCyMVNTZtih","FgRlBhhHvogG":"0aQ8i6L6sphd","1Q469fe0ZgEp":"fgVSD6rEZOvx","ajaMa6F1XaeG":"byJ2Cjsf9Pcr","2mf6WkWjPBNB":"uf4TUlV2RM1d","q4VQDggoCdcg":"aE4GglZNJ67h","UzXdz74GzbRz":"ZsZxMd4wjnoY","rxFw9wSTIA0Y":"A6SjrGY8mILe","9vfRJac3mfzC":"8pk2X1IKGvik","6FE57JUXJKXQ":"UaQ25yB81yGg","RCOPakh0Hucy":"cic8DVd8e80a","ig92ISutHBeg":"9ylWDlWRUENe","l9wwXPV4yXt5":"RY31NZV005Ev","8LhVsaSa5HXo":"Wl2jh5nWJsBw","yVeTgnOmBZqJ":"jL2CecYdvpf5","H7gN4mSIcQHd":"7oTWQxvqHz9J","WpYlCdUcde97":"fyhbaZSXbdlg","QEFMrXx2vdY9":"x0u6SaPMfsDf","ge1DCkDLQi6n":"zDRdrcwtMWQA","uLV7oZnnII0e":"D2PkNZ9kdDtH","Ji22t8DSbJLt":"9uLmOcjory2h","DUNMwMXIz00W":"6r1ueBh07tYu","kgyCamntiDoZ":"f3qTjF05oEl9","jnP7dn9DyKWP":"qn0PVCFgsR5O","r2NNQAm8EmnK":"Xwgd8bDDuPk4","CswYU1vBDN4T":"hqtkXDgtMHLw","4qFODIaGIY7x":"pP8kDVxfjai4","qolup6lSP7CU":"EJOF4PmzEjOV","E8kR7gPJHPQW":"SoSlLCKczayw","R13Dj9jJYaOQ":"H4bbhGZrwiZQ","xnDMShVYkOTF":"gzVhbk8kcYl1","YjHn8uS7kf9t":"CON7gDJ1MqJc","iYf05vL0Ppx3":"3tIgtS6VAACo","XP6aQnPaRfi6":"GvAHoN7FWjsY","U6JjTdyM8ypc":"zHuXeWysLuxE","ufOshJPfaXyI":"uKZprrDZELME","v3LYzSWTnsLu":"nduGeMjotDxi","2TvQ0dG1PIRP":"21nazqSXD4OD","vq4Uxx8dJXVZ":"1hcRElkLNagK","FleAt7FsklYF":"XQ3CG3ZHS1R8","M8mfOScvS8Ob":"402vRwkyafJG","hzFkZaXNAQ3q":"Xr6xA4E3SIu7","nCYE0ImHpuAX":"LRliTbS66zTH","q6KALKJZGqMN":"YJfr7aCGv2gH","tdXgiAteJyp5":"iOJ2imSVR89V","TcdWN8lk3BH7":"swJpy8gi8Bcm","C07HpaWB7xpl":"85PD5KXvMVTc","1bKwmVDolrHO":"bQ93pttNPrVU","tgPbfQd7Fkii":"q0Yvf0qrzcTI","CZIY1jJSrM1D":"2DV0mi1RMmuY","Eqvq5CgIvknP":"9pI6k3g2duor","e87BccP6bfHI":"1FcdAZ8crRu8","UH5rZtiIS2lw":"2PRi0QxGMqmm","5LrlO72MHXWh":"WzVeuox2LNEL","MWLcyOEndDGQ":"TvepCyUgalPB","gQBGxy2z8tuB":"2ZJqpRgBrnW9","cVwfN4WV9C8v":"UiPlZf4V7Md1","52UCAR4c62xv":"tBTjxDVawOMn","zaKsWd3TYlX0":"TdGx99vQqN7C","sLU671fNebXq":"jLIIbvtJJBSg","o7VFplB4JlqC":"OKeR5mjKijPD","aWVGMg3rYi4T":"YOjzYN2JMDUa","S3Eci1qDNPJT":"1QUwu0eLst0R","7tC1qOhRfYRu":"JDWe4Y85HrqC","KyV6WpYUt5XS":"EOBzdMLpNajm","wtfBu9Cjb8DY":"DT5HolyWzq64","5EYzlcF8G2zT":"TzAO0SlpCcd3","SRK7Clw5cjcX":"4BJVTHPCyhAp","gWJOLARL1QTF":"vGdHgP2oT9hE","0TbdjEeal39Y":"R1NC1KUm2i7g","mDpTLBdUUugJ":"jTMb5heq2S9i","mMSwKe1WlmJi":"4oehq22ZKWT1","moupSScJCrgh":"2SOXihZoRaYE","xrGZhJ30sKhj":"a3gzcOKJPEL7","rfapJOUDK2lA":"yXhpzEUUuf39","VQtO6bAxvTbp":"Vnj1fDNIsKCM","VAEGN4LgY8fi":"ORmVFJ5cEawm","hSlTtYmE4wbc":"NtCXuNh0tcIu","FuIwqqBi61IM":"cCKNPFVAst5e","adMWvULVAJzE":"SSkp2LKJMVXZ","GFs6ufRxoVGE":"oXEG2UneBUFv","DTNJdHL2lKQL":"NowLndPn4Yo8","62xjAazWRN8x":"Ozkx59Dz99SW","0ixWwRSjC5p7":"nTHnUhLVFEMd","2TQOgo9L6lp4":"Zxn8l96cmFN8","3FAhyKcLV0hl":"lIPMl0RtW6xH","7RfmQSzuXLQY":"CZILQGoxKK2E","wxHaVHGHCx46":"1LBiKODp9qo2","GtXgKLa3dxOL":"okHzS60UwZbW","6DmBMrirXQoV":"EfTDN0vn95BC","GEx1ZqUNvRGy":"rtydxKpQ9nft","l0TRseGg5lqW":"cixB6UeGzlpu","9LS1sL9Oli9u":"EbxV1x05bQEV","kNp1RAJgY5e9":"pntbl078nI23","wmmkQJxs2q3P":"3d2qPARHDbtA","YNwIoARLvQGf":"oTSQReOBzgzZ","N1TsE4s5dcxf":"AMQ5kfCINksh","ZQuY4Jdae1hc":"ZqYjuA8tiz2y","06Q1gxzTVvlw":"khKoW5oUFmAH","S3HHNHjfIfeI":"zQDWtb8L4e8A","7uPJzIk1hcdF":"MYyUaKxi2FTy","WFNRzy35y7I2":"fMCPsxrP1i5w","CCyhXjSmKKp9":"FDoMrREBmuxO","eEE6qvDqBXuZ":"jK9qMyNo3VsS","Je3dFpf5ZL6Y":"VgHecDYYGv61","1Yx5m4pV61ZT":"FcF9cHlN9S0T","qFLMJp7LvtS0":"1NxmolIy7dLX","SPd6aByHpPKu":"MKY0VVaCG81D","oaXrUvfJO2En":"3nGC2UYLmHrH","1Gvt3N8BanSL":"XrStNhiXsihD","9EA6JOtrIoxg":"3BrpyGRLYwY6","ir7v7WF9vkp7":"D46AOCBt8cxm","hC7ioMjt19v0":"RrIEfg8iq1rL","NfSzjhSVy5xw":"bcYQo4zkPYAZ","SJ2h3tYmzzSs":"YNC85kqX9ixv","p0EV7hpEVCiA":"zPaiC8Zryszn","nf4n9vwvl2oJ":"GvhURwNqcLFA","vjOO5NGhuC97":"vK8tyh5eOPJs","gmDy2nA1HgOK":"NAyoNUr0XItK","ixKOaGtbaH8E":"VW3pExyyqHJZ","64dhGMEpbX2l":"GYA6mJ862JaH","hnZhoJ2jH5nO":"0URA3pqSoYlX","A2Ge6LWTuWpX":"2M4jh3g1v0pd","GJ5jdqm3A6SU":"uhwBpv4P6x8U","N6fR5Ipk3wg9":"BfUySCQoAtDI","mlYzyyN5n8iU":"MCx8cNJnFrga","SvLIihPI6tzW":"cNgj6FZYB8lL","AaVZCZ1KpWlk":"U8CFLZHWNFCJ","30WVs65soaG2":"HYnqnCSc0dBf","tFxEJ2ACzC6x":"60VrCd9cxCrs","nNmpB65seOzo":"2aHOSfaXJ07c","JlW6jThJAX5o":"dgTi0iKyL7Co","TtwiWB7D9bwr":"7UyUzVidPpGw","gqXhshdtwMRA":"8w6k1REBQSq4","MgAX4nkUbBh9":"GIN1gg60H7su","TXtff2rz3O3h":"fGSL3kvSbLSz","EyajKGpfq9rh":"XfUsQQZq3FYO","9i8KctKH7uqY":"Iqkw1Y6WiS7P","yG0Ace1t06SN":"n8FVq92wT5cs","Hy3FbQygqbqN":"JbnHJIwSeRqB","6xRg0Foc2RaU":"QIxUfXOQMs96","Qre25HCgY0gG":"GRggvIKxizCL","QqmWqaT7ItEa":"ECRjYvRywqhN","OvsHeQSd9RBl":"Kv9i3a5Pqrmi","s2E0MZBSAFuK":"51WXxvh8nJ3i","EsyBA88eYN9l":"RBPngee8Qubg","nS1wxOMzTmw2":"0eMFR024VTIH","5Jz5UqryOVsO":"blksR27ouWX2","liH37zJq9OQV":"3nKAcWlGaRar","B4pMaToaxdbk":"hDJrNRoL1KPd","5ytS0aIRsXGo":"iygqO6bfog13","isJAhBGzapRn":"mkWJFtPLGDt5","jcLgP4f5z0Km":"IdxJPKyR9QOi","37SoTZx1hx4J":"to0MvumUrAf3","PZFPoVgCO0TS":"dBHOVtgQmDIf","Vj5Zqi81HTZr":"wpQAZZpgZ1l9","XOyao7Iy9v6e":"ibHI5F912gVm","gKuh4j9VB02P":"LzKZUWPQNGwQ","WWRJuhefTk3J":"JODf8xdfYtlL","Kfhx4BPi5lu6":"HFgDC5XkiEj2","zqZsrVg9Z2nd":"Y2Ch8PtWrNEM","rkcBiAlogqUu":"ccEmKDdlFVXf","LFf5jDFikygl":"FK7yTIX2K7I8","Fl9Clq8krII8":"kuDSNJ4ST7XJ","fMg4D7L14Mze":"GoEjfCU5i5iE","bsq0yVdIYJFl":"t14ftq6Loo9n","vW8K2NRcIJ3v":"ZVF0WGQD0WIy","eQTAhDTTiBff":"Yy80MqSd1UYM","TZjLCvk45H4z":"3E1hwkUgpDJl","bz5jSNPaCAMa":"uDSl4L2O3cp1","M6DV8YlnF7Lq":"FrhaTHD0d5yV","jBJAF8O87oj3":"ZRZmHSw38D8b","L6Oasg679EDu":"S1pUmdqfkVMj","uKfM9Bf6Obxe":"Pcjn0ZAuKjOf","TGPB0M6lM9fT":"irgzh7doPVAf","Nc3oHm19VdqT":"HzCUvoTWqEh2","PiJhOpZbdeBR":"Ib28YHpVIsnu","UuZVSDvXUggC":"Qb0fuMDxzGOl","TR3UpaDqU6iO":"d3OKMC8UYXRM","D87xzdEAO6pk":"5HjEVHddWixN","q1x5nWnHUz6F":"DjU5nkPXAmxl","nDEjqDseXO7q":"9gTbwFVfYvht","f6A6jq2D7WTq":"Xdrblu1HdvLQ","6UMPp2OAz9Ne":"CZkxKvTvmVFe","dSfDynF82rRr":"tllHkEFcjHTR","p642V5B0CqAi":"s2skgdOYb8kN","ViSSRgrfMscy":"VFx1y4oPylKU","Mm6R29w7IdVB":"q9RpGxef0XAO","ff8kFNYgkCen":"xkr4ELCyJeaI","vMB9em2u35Yu":"pSeJ19pHE1Pb","OZ1bCl3w4R4L":"2RuGJBHoncDJ","ypEQAVVdKrRs":"Zx8UB5eItYuY","TkyUFNiZexmT":"9Nmh4X6zCQ5e","dEcEArCzaKks":"rYvDXcNfShDf","1yQaG11xa3hR":"ZjdIfpZWMJxF","EQry2lMu6Don":"OdpolbIPRVTj","fd6Qy1z8l5At":"UKg02o6EiVog","9Mx87EhvBIf7":"VysW16UdxbvD","9B0lnAs0j14M":"jlEyu0Hhwh1a","HStgUF1FOjRJ":"pYpALynP5PAb","WNm3E4WdFQf2":"2gX2y1lSytsJ","Beh6AcpJL5JY":"GazdrDcqTDOK","UzrV4ZFylLul":"MGIpzmxpq725","QfFTrhdVnm3R":"LcXIDtP67Pj0","DJGS9AzAJd4e":"sdnBe332tIJ1","zInlYmzhBw6G":"naq9yVmt8Ibq","PKsqUYSLBaTf":"Deqzd3Zahjsc","oBvN6sj6gCPm":"7OdhRcpIgiDT","M4BDCckHhOxe":"volScK5r16uX","1xfU88VUXhMU":"4qBf3SHb64U1","4uKooSSzDDnF":"SE50GFmgCSBw","2wfy3ZXueCRb":"aNexhsYAgiYs","LQpPWEXnBfsm":"OfVuF1ClawMv","vinryPAaxdVO":"EhwsBPIsCRwR","9JLkcfgZbARd":"StaG0Y4nYMRY","nATso0lerCZ7":"aIV9IjpD1KII","A0fHEvnHNH0H":"FlTDEhCqEeQk","PYaJ2XDTsdCj":"DSW5gIZVJcN0","6xSMZ1YdtMTf":"wnQyS9P6V4dZ","5hlT5C5ZiXMw":"m4ZbBPJn37jG","8G4YOXdXjd7x":"y9i2gWmnCwVm","3e7XKzofHR6q":"RuskoegzqtDO","kq8NnzPtinXT":"eqUYpmh0flQF","FxyB0khBFtLT":"KS6MnQHU0o4L","ITEUrAaStM1A":"4QGaj7vYtwqy","Dbk8FEidrQGy":"EkPvmt1kERPL","lm2H0Zu9eKQv":"yjSOuGQMNPlv","EyGxJKpNJLtN":"lScjKW8TK7Vx","ROOWNcMesRDP":"WMiJUt0AwNTX","WXvaPhJHBCh3":"kUFQHlI8Npqn","AaMU5hgDrUHm":"vAXfRSbaArvH","mjyIU1tzP0e7":"8FRbR1tm3NC4","hRGfotDDU6hP":"HCOZnkVM6cgv","iqwnmg8vuKOF":"2UKiiX0tXTdo","8hyRa5YF649j":"xpyMI8q0I9v5","wGxqdBuCd6FA":"DuNqQDtJe9dR","RuYwwOqkI0zu":"CPx3oq0LMKxw","exkqzDdxXtrT":"1pQt5x5PiW8c","K8R6RLtuH9TZ":"7FPAWJLOhvYr","e21pBtdYqfGd":"iwHLugtiRnbv","ioVnNKUn1k6k":"GRadiaCs5r5b","BSI2Mg4bdTuQ":"gp55B6cBNlab","QZI4RmQvHuID":"8DHVGArpi1zu","HXuFInLDPeDw":"8cEW5n777aaZ","6R6vqdppmJHg":"Zvk1ZJvAryYX","srxZkzssvk3i":"QJyrdMcZVZHI","TUHb6jEVdY9B":"Ybd0EgzJubRY","fj16FOkmAwke":"YkPHaixHhzw9","CyEyqsG35OX0":"oufkgPqieE49","5JAQPjLGCWu6":"RD7uHcINFY6X","DJhkD9JjZqTV":"wIo1yRHO8xv4","QxwulvNd2QSW":"hS72BkDyZIje","hudzg4sXOkuw":"gtNYPJNBjLXI","dUG1sYQwMzyW":"yh1LXUYvQ42P","nT0SlpPqupJF":"rlu41tifgpyb","X4ev1dGEt5yd":"qnOa0WqK6V77","QZaATuMMPwbR":"4gtXRzjY3a8b","AMolG8othjZR":"fuSEktH8BGkD","ssHohQWlZbyG":"MAFBwUzUn7jO","o9PFBrrVwh33":"RoOVbiYPStXW","pWv5ThKo8dml":"g2OTUZ7MrCW0","A9s1gP8M103B":"naMiVv8rmLM0","fZF2KRvO4RbM":"ZoLIiBc260F1","1xdZ7tDAP3FC":"vFt7hGHOQLPa","2beKeMdZgCeZ":"vHrJdUZhKSax","hMm2f5DAvfa6":"HjCNgl25yGb1","fcEAcJh05FmE":"yzIHOJUfcSSq","MgZulXfDVnDF":"s7Ms2KP5aUz3","xUnKqALeW92q":"XXjDuUS7ktte","gzCnj6J6uzrN":"jzPKvhVTenWd","y7Eod0FVaxA9":"7h9Bjoh0ynQv","2cKPUS4wTM1f":"xGECbs7hzJhf","Lj73lmx4ACtP":"Vv3rxzpol759","5P2cTihHvBWX":"CNaWBt2XE5WD","u41TY1IS1kXg":"Euwix6Mrrp4d","ZNIduucHILIq":"2UIbN4ekvDjX","BVlUpgFWdYiT":"yU2jTeqpAgCi","RvWLj7rp6T5l":"YhG1c43rrbuG","LjhoDtbtOs2i":"g9sKbtJyFWPw","y62qH5B6zC4S":"fEmB0DLevp6V","GqfYMrbBC8sY":"R6EgzEKM1Gp0","89QfIqcIOOwm":"mvtBPkL6sTzJ","Pxn8aibxzlc3":"f1PFOUBv5u3e","Xh9KWQrP8JFV":"rk9PERCXXMBM","XqVhXv0xmhVm":"FmKs1WfF3zqL","ZaNCxRdvHWbE":"FgZfmw6bvpoe","EFsw0qjJAwl3":"8j7JTNeD1ixI","LWZeDswuEzt9":"kb1atlKu07M6","OOEp9fbfJ4uA":"IxmRINkFNwFX","uACeDnMqkzzp":"Ac4XL8SEbT2P","KRN7AkGtArKc":"9BAr4uvWRB5E","32Ai6i3nrfmq":"yRLx32bwMiwF","cXCMpff2e904":"8fTxG0SQBJkV","PllUEQtp517j":"wwiela9sou28","vz2zZWnn0Obx":"jMsh34jEpx3l","7bAZkLxvgAEb":"gBizEr5tcMK0","sqF90MbmCKWN":"0uAl0DtpoWLn","suodUKnoH6m9":"sbngAoS7YhdP","j7ZkNHOkszsR":"k2UVeUxjXCWl","26YYlY9fuXAa":"3PnkDiEJLDv6","idoWYyrCJx0s":"mlM0A5aDBjVe","XEONyzOQNn3Z":"5uU54UlLgfbF","WcI0cx2IdmjA":"tXTsZ0CNCk7P","FpXnf0cpK2jG":"fLyxn0vebiDG","YFBCzacHNRFY":"u45ssCv8sHaR","6Sff8ntWQPYv":"zcnf779w13HC","UYWQK3X0cAlV":"ZIKSzNMVgDuu","bn7QepmqcKwL":"xQCD5ATFCMbK","UUK1RYyNPGdi":"X5losHeBSfoR","UC0hL7oqltO8":"y4kgt2RfggC2","ZpKbPjgE5jaw":"FlH9FohMLTde","IvfTalxyzKx7":"Nz064Y9KcSel","onLkEOVxClUz":"pyGuT6371Mxs","THJFc2ovUMBc":"azD0fwNKz8bG","Xco8G2iSf3Il":"UHY59dibDjT5","esfwFLD01S9k":"fFPJzaZvSnIf","kOVazEB30Sju":"RLA8QyoZuEok","MFIrDixnBMoh":"xoW8UG35gFJM","RUXGasfkDYFI":"6kA9dxYSUgrE","GofyKUvtUden":"ENAC7NdCIHCF","Sm7ZZBNy601a":"DrC6xagW2NAv","ShrN2mgQxjPC":"36S40bqO3bni","gsQDhMtvuLjn":"wdLDOSpCU44G","mfs3Yt9J52jW":"badnTW5kOjDR","2AnTdq0Bmi4F":"vUtvZxfly4zn","7B8BWtsa7AvJ":"e5Tm7XAWSgz7","ywoos1Iwy6K4":"7y6EqIZaeq2l","ZYfpC4xDkXJV":"8VbMdsvlEhy5","COz8QrivUx9p":"dfM6PEzl84Gf","u6itbb1O19iI":"jNit2yLRvR1A","d3HFQwvkUUWE":"snDdLxyD934Q","xa0gdU8TXiel":"qvYEzXnWYSSU","s4ovH2kLfi82":"jM5umfxLKUVY","YPRseF2rWdqP":"luF8IKlXuxY2","qE3PUB6SAt8j":"59k0iD6cXSNB","lXKkVVBblvhh":"iFFVRT4GDwcD","Hn2WcN3l5vIF":"JGBX8B6J0nEl","d4V0r0leikH1":"70354gBHAZND","GsuLjsA6rhdD":"XhJcnlbEJhca","JdwDoskafzPK":"EKl8gGQFRouv","lnha6STKkNn9":"S9W4EWkll2DN","Y1RwSJjekH3s":"cK5eVsYrvgfL","URcdFDwKohg5":"xRBjuTYGg75x","hgIba9uCGX83":"gfBvsFLnDTPc","Lsb0f1n6HOET":"m1dTzSK0qRuX","5CcFIGuyHQvJ":"yg7fd3ZeZWpm","YcS4meYxrEgI":"TC9Sr0CKkycD","kG9phwsCEgrt":"SZ8KYateM94N","WQiBkAxg1JdJ":"sq8tec5Yf4Qx","oOgaSSRuCZIz":"sssjOSUviujA","ZDxSCe7veoZM":"83H2xu99B59T","MqTmj3KGPUqG":"R4NGzMrNz0k4","3jIGTShUKPCC":"5qQwK78QbrZT","AIuUbfLwi9mP":"hl2a7xoawl1r","5PwiSAUcMlB6":"7L718o4jCieL","joOkTEwQEgYW":"k4k21BqNB8P6","2dc3kJEYDNzB":"486gjbfwAYJV","RbSAQ7Y2gZu5":"tfKLIz1eh5Dk","ph9tdKbhra1F":"FOeCl1KGyNuj","v6cHEvi4Jh6F":"N3utsDsm5wf4","C8iV6eVYYORR":"Jo8FNDNaaHvH","hZVZVnptn1Bz":"3bxnEEkhWCcC","S5Envndw1jWh":"3UOSEJkAQsuE","h84Bs3lz5uwh":"71NShgYHIntY","4FuSPMKfC2Pp":"wR3aBRhtcEm5","2cWyJu8z3Aw8":"Jl9IoAkI7H1l","2ru6o76roVlE":"BOQD66SBSXF7","wxbNKCCwMN4S":"bUuUpD7SVcR0","efER7NnLcEue":"nSklOW15aKmQ","c1muixsPZ1d8":"zGtDbKtM5ngF","kCGqZaz8V04N":"zG0RL1L746sI","6Rp48mDSF5oW":"okakDaKMaMkj","X1whm5E5h0EC":"sl9st8GRpPGG","A6PYavukncSV":"8kJXxJ0gV9vq","KLYg1fnCj26j":"06MXLJJXJ2hE","PmhbtDeUP0Ju":"aN8fM8oxSrsO","q2gFyCrwTE0i":"YLXDPtycErZr","pbupVdhhcYs3":"71aqk1mY3CNf","Juqnmw2bjWEf":"tXl8b4c6Gfw5","3UktExO7GsIx":"T1kalrD6bERJ","6P4NxH6d8aSS":"aaHcRJuB1Meb","SV12NvhIQboX":"P54dzNjMHR2o","lyK3fhafraWy":"Xt6C7vs1pLpl","vcbrSHbn515O":"EEhatrnanVJe","UMYFs795OR8h":"Bz2odbVTb5dK","r7dTPMWbbRg6":"bEEKbsjHPGhP","2VRNJAO5AGaB":"vc0AO80aGmAX","ICWCa106oLWN":"QNffmvNUOMVL","wQy63uB1v0r5":"vpeFMhQcpRtr","ux65S3xdlCWB":"QL1WNCIjjCDd","lPmvMXmH8Sor":"IIFchtQjEq2n","ndt8Xetiz0uh":"EOeVwjC0XNI9","SUvV1nsnI8ea":"I7pkdC722UyI","8JF3BwZlRRmj":"8mKNeqrWeKl8","mECNgFKtzRVd":"W6o9FDKPXBde","F41QbL6BCcdB":"tKjNgA5tgUTP","IR6nSPFWtWt1":"f7NKfMKRhwKL","niBuW1HrVoBF":"stQABLSligIM","hhsN9R05dcDf":"g4Z7tJGWY0cG","zKQNPh1UnBha":"ut5Dxj9w5tno","hMOo3ik0ZVW6":"OCeRosh0qr2p","x4kMHj9MsP5W":"cBw7AROvYReM","7uGml1CWYS7f":"p4F2Tws3fk1g","cTQkJlHY6cTs":"rtaYja7ZjX6j","Z2n29CPlRF4C":"3EUHIoF9Ax6D","SIONJVS0kkph":"TeiAPh86zPvz","1mJ06UWTgUP5":"wl8oJlEkQIEy","psLke8LPDiiy":"o3xChjAdNko5","Y2mSkVpfqWN0":"GaQzgDNF28ef","Cs4to8v34FWy":"GtILOzWwxQrI","RodJjXGaFv0V":"SzGVxag9rYiu","6Oiue2pdy1zq":"3PlKBuKJ3grg","qGxIeyWQ8Xhr":"xmHuWCHEWg9A","mG6GwUZY1C4S":"WTj4rYNC9K9a","yecwgqLoxsTP":"74K3619ta0w5","rG9cBE0kWkhl":"fWWomxxJ7fbB","cNqtiw7lBC7l":"94XtuzsHuNcM","lkeMOcG5XMBp":"rKtHM9frV4u9","u7B3A3JLfvES":"XtpTUiQX0UE9","fQ7ttC3SciIh":"yS7helT6OrMd","rk0AOPjuzdJK":"Nhtd5TLdXmpt","ab7Mf7LLPlQm":"j81hRwHxSnNu","37SGSLrdHbAb":"iRSjjX65TOEa","O9TSPKb4HM1x":"FeM6c9h3fZ81","C81C3fEv6Z6T":"FbbV5KAR9jSW","7m90R7gnQnm4":"vb7IqmNtbmhN","K6ZGw7NMmMde":"6ibMDVmkAvkD","qYO38unbIYzy":"fio8KnKaLjTw","0Htx5hLoH6hg":"dIPkfBeo1CPg","1LYeFLj2KsVF":"LWZWvaTUfHVA","bar7siSHLfWN":"mr5uUcepkuto","zAsyHiB8Zhbd":"TVJ9zYlntFq5","ECnoNEXZTs6N":"nW4iVTrJLXS6","wTl12Wow5mEo":"9DxtFq8BQtmf","YuQMxt0Ff52M":"gwNwHpUldxQO","mIm6zE5sqWXC":"NYZu6HMi9hpG","BbxKNuDFfT6h":"OYZrSpjijVrR","GbobNcOayyKo":"UfEKCU7p1N0w","ptL0DTgongsg":"QqXEd94aenP0","ju1vHrupbZjN":"d9vjVSLG1mwK","rsZnxGl3zjhP":"9jpSjurALygR","4ZoagewLf6MA":"M8S1DboCojsK","KcRs1347KqhY":"FwvQ8wWt2Id4","6qa3X3Yn1Ncl":"QazMsyZORi5k","L72tj0ooq19b":"3CsWJFTqUukV","WmdhORGSi9MD":"Mz4FGiHThcjj","aVNGB5G6WqXv":"P69zPylso8fV","poRebio8OiLQ":"5AF06Z0z7VzV","8KezCLFYyOkB":"M7byKquNjR5o","XQtu3kmtuYCg":"iSrdzmM7oNIl","gxJ0vbR1P72x":"akcsJxQTxj5w","otZfUbnua4ne":"cf8nXuIoNrg8","CXGK7KijK15U":"UT0m1x0dyFo2","vx09UXd2gPgN":"p3yTaItMbpHT","TTYjYmsXnQJD":"alEQ3MHdb58x","ERB0YdlmLV3b":"OV7MjUaFJrDu","J4wrudAHbrbn":"yPiSXgN2IFWP","AtG2CbJ4lmh6":"5xp2tMhS49bf","r1F6ndKgqDiT":"IvNIsa0rfLVS","gE0B4R4xM6J6":"2SywguoBIkIf","Afnzdge28T4Y":"754UGUB8gZWk","uOMVMCqHbj5Z":"2YDPJb62GQlN","Ik6pr2uJgvMu":"3ExtnUYZBBj6","qb0aRUo80c8c":"MtnWnfDu6m3r","eu3EnLuk3HgR":"br9RpYHN3OKk","9h7HpqJ37rLM":"AtRcQcsKwPAk","rgguYR3nUmZP":"YXnmZAXjyp5u","CiQM2XzkRbjo":"JHaEWUZmr7QX","TOekTctvkf4O":"292eeG6KWZIJ","7KDOfE5PhyuF":"PflOAEtgUcOY","Z6yy4ZrOIbuU":"GMF1U86vJwzW","9yoDn2zEeOf0":"YOvroyAo98LE","AnCLm0GMpHZO":"DJF1GuUUVUgd","SscB4wdLcAeA":"xPun21adMQzX","OfFLKuvKT91O":"cCqXesWuvnPV","wN6IhNBOMjxz":"3Cm4xi4vg4ks","7WlBNZ9ZfxXd":"S5AVfwTokQRv","8zl2xA9CSOs0":"ywTNbn4BxKUl","db6DrlUFnoiM":"uM3iSCwsAnZq","oNOlvCuTz3sT":"cq6MSLrNcQTi","8XTOupu55SVP":"i00PCYo8YSiX","IAXBReKbQpFW":"vmDlmwMH2Nyh","MW0OJEy5xzI0":"EhEwYH1beUvx","JL7fqyfdTEhg":"31FMSu3aXSN2","Mby4qIZi6XWZ":"vcsaarNJz0LY","ikF7lHw7dxXn":"qHjLsSkvvAMz","jOoUebHdggWT":"PlQxGgJSEKyb","8JtTPqtqCOh6":"fAX8HwCpzBfH","DiuuHehPqN8c":"9AV2KVuL2WYv","tLlMwrKwR7em":"QYGXza9hVnlL","bN99Y4w04aav":"D7I1j5w3nmWh","34djvpDZgkm2":"FoCUM1pYFQ5I","yIN6QwLxbgKg":"L8fYH7j3eeX8","5y3S6oeEN0Tn":"jNkMIOHXXHKb","cRjB7Gmowt9R":"HskOkuprJ6bi","WHXgDZBlmhDm":"5Jgn9UOGrm2C","O5fPFmwp74VN":"uSKWDzmqQ5J7","c16IKkP6J66Q":"8DJe5Q76DDMi","fSvzMEZKqBCr":"MJcjfkgOdS4z","lwm5Z9ozDmFS":"R04crPuOwB0E","NtTKULV5mvdi":"2xassYyHKlEv","u3bzGDgxOrFA":"pBKGFB52Pkgb","0Ry81U4ov2M5":"L5Ao5r6XAaWO","My1thm0s8evP":"55U6VPkqU2a5","r4p8y9NAENa9":"TmONVoqR3q5D","oaBzC6fVONmR":"8W6TDgKtmJZR","uGERlRBVKKEZ":"Cds1G6nNFTpv","15xcr8rOsqzE":"nnQGNHNbPo8w","mQwM0he0E0dK":"3dkViKSdS1NJ","kiOWRYUACQ4e":"N8De5j3IaIgx","O6u7DDZKndkA":"bzyZTNxqr7qN","C1oOxkTJKxtd":"BGcDZVPL7zxF","6qsu5sULS9SC":"eXjgykgnStBd","2OfTWyQCAwab":"KrYRZMhO8Tc9","ckWikvsXusJ5":"OKyZz4bkectq","nWS8LwwdteIK":"5YMxNmC783r9","AB293cw3QjE0":"Yj0McblcLwRR","RoVMBWBCyMJU":"h1iMK6mSSrDD","U0hKHl1vtTDe":"3XUh6R51TaHS","wdyEyPtsxgKt":"9Z7H1gwrFDoH","ZYQxUhytktQn":"qvOoNtouvyan","0Vkwh4tOxHUu":"0kOl3JbLY16w","Zq9fMe4f3YtS":"CsGE5WLE9Tde","WHaINsJkzvj4":"GdvW7frC4Gha","XPHycxb6QzU4":"ANxqlpMybeTr","eFfCORR5g6Wi":"kgg3cAUvGd8d","96Xlm3QzP3QB":"bmgKUvc3FKAI","KATDBdzLqcUC":"gAyMdUM7A22F","aci59dCMdMA0":"Z6hD8i8ESiPA","70sW2OMZvoTs":"HKotlfN20fzq","anLVesn3uD8f":"C9doGSrT4CR7","xOtSzsjF77Hg":"caBe8UkHXSON","6JJCZsvtZyn4":"igQGP9KtXqMo","9TbMWnUPqj3E":"PwD9O1k3GXMo","4VIJc8mhLn82":"1d9Cpa2WbR3U","wqrcuCxKvnOz":"PoXd9GNg1Maz","s0xhJGntL6Ct":"2snSNsfCBnOq","M7nHzvdvGlaF":"BlGUDUtWuvm6","Wtu9UeouwyKu":"iathYb6cnEeC","a2oe4FyNPuDE":"OeX7xWMbU7Mf","q2wga6dKugXI":"6scxDJkEHV1O","fkmMA2aXUq1R":"MLkEnmTMKJnB","jCn8dIh8R6bD":"V6EQEz99FEWB","Y5vvMxtJkGGP":"CG62j9uDkdEv","W5ApWkQZx2qK":"26OBjnLne8PX","CUoLsKiSclWM":"d73aD61NXyxe","vJD9Unmmf8Cn":"T3LLkCJQDzrN","OSf4Vcx30hUa":"6kRSMxTXrXIM","9RiYsP7xxctO":"gaUnTsQks8Fl","NbYTdANfpEul":"rvQlYIaRCsVw","zkVJeeLJx70D":"sprXBix7lF43","UI5yLZXzTZzp":"0li3hLMVz3Ja","rYDuMPrI0Ekh":"e3KKorIMUa1c","ax6quPxumkS2":"15REUYToPAY8","v2umhUTUpGt0":"YtVCwNbVKbXw","ck8duxlFH5IM":"lqDpEbGHjmbM","r0VurmpwFB2c":"EZlmiWiY0Bdc","L3H8ag5Hu3Yx":"P8LTigpdgobb","psAlntE417r0":"b693nINWq36q","ZRxOpyDgYZc4":"HTvKhvVehSrP","szAg5qvrVXYH":"aeEq3BQ8kk1g","9XZe645OJ6CR":"QQ3Lc63DzOIF","0cQWBV9EGlab":"PF7zhH0iCsuW","tZnV005k26KZ":"8qwDiqPiKVy6","EVWFKSzb98fw":"hOtapA2rvS8X","bDPpu1eL1tWe":"9Fr8QYd9Q494","zjbx1sAuZvCD":"9gSDwBEr8YPu","fGdF092RiNUK":"Hmlxr71DPlU6","nl0BkCcpxN4g":"ywFgf3yXPZtc","JwnlnLxQeNXu":"bG3W2f2ZjSJl","zeWlof88NLgb":"PooAX9Xlm5LD","ZlYd4CuH0Bde":"TERdWiTnHbNH","uUrCdLsEbLQq":"oVlsvh8pxzFQ","vSErJQkTFzJL":"SUTuVlUvF32W","uRaDZauN0iIr":"TRSDOPdbAsJ2","xWyNqgJLRA7E":"gsJ5lkifsfRS","eN7xYDC27vi2":"p3Lk2UZ1gLcr","6WOGo5AFXO7V":"Tt63GT7IVXXf","8EtwtHLuMHG7":"Sx2Mjsu4nIB1","f5s6DGAFP5Fv":"PzqEetSBFCE3","Gcm2cUMbn4Qc":"IQxdf2mQFEx6","v2CwSI7GkO4x":"eX3wgPDEyfzD","OKnigBsRmnqH":"oE5NYqukdUIz","J9JUsOoLNndu":"PATlh29wherh","IrIvvZzLe318":"t6OUFtRSX5xX","UilEIZYQlZIc":"8R75nimXR8fc","4YavyyFNZF60":"XRpq6xwFg3GS","quEwm365tRHJ":"4GF9CNv89BXj","OcksYOdQvVoS":"HPoS1q0u8T15","rwRMOkPNtmnj":"Vd0y6H8BALIb","PYhSyYS7H6Dv":"vYI51m4UITM2","Rd4TCdMppJtc":"VYW3qHWEYwk5","YCtot7b3jQDU":"gq7G03cSsRLw","pGWLgpuCrKYn":"PjPL2r4Kvn45","40TRQShFnjWF":"dVqb5z6dpvNj","7BL1aPviFNCF":"uTj0HNcBMErw","pgqEXoBqAgZe":"6PwgkoLLyhpK","91wvoKRZcj7b":"siBQuahlY91Q","WhwyLk7pxwMh":"FFQ1LjoBoQ4i","r0Wlg3haapHh":"LLzIWTkfiMr3","SJn74yVwFVqm":"vLu0Y3aje4bL","3l9PkeyEiY1X":"Kqnb5FNxk8lx","YlmQa3X63rys":"rOwN3BP56UqY","hNB0bewzPWxD":"ec3THqDvM1DH","lxVEpqyRrRGU":"R6WfVfxVRLB1","fDDYo55uQgHz":"wGQnsSS2ObuP","Hc8vwSGTpo2p":"Z4WS4B4ggbNQ","Kb9EQisQs6xc":"CWeFM3yJa9kP","AuhSClwfnSQE":"YsgxnywzgtLk","SGfi662Cu98v":"cxdrK5pRhjZ7","7tbQ1JpLUvQl":"ghCqsGmAyDet","66SKKLjVSIKl":"H9nYkuNRDiuU","yz3rUGu26Rt7":"HPwPhzB5ewIm","7rwNv3mcKU2W":"ixyrxeG5k3Ir","jnzo66tazTxc":"0vTBSlFYZnAz","iNMZryEeZ5iV":"2u87VWRzUJ8F","bR7Fbns0i9au":"hPINnxCixMP4","f8kBfhyJ973t":"7cW0IJlPY6U7","iVyYEAZx7yRI":"p7QMHsMXbSt9","bz6rKIt1YFqG":"j8pBnWrPILie","2kfNlWPgOQZu":"UpjXysrzrJwu","8LfRuoBv2U2d":"4HpAYKQCCvGR","9Todjrw8uVZy":"rdQaYltLh4QA","vPdNt5WdgOMw":"VrZClLKB2aRW","PDdbzYmQwYi0":"kw75bNU8VDjL","DOBAlp3Dx7OM":"SmNciXGUw7hG","meyo4q9UBVaX":"0Q1oRMHw7riu","VUbEGEr7F2Rc":"QMvRenoGc4IM","lphOUFT9vcFi":"DrnVYQ9CAiKn","sSqz3rlSQMup":"DzWeJjQ07QuX","S1XNkHkOfLxq":"xRKqOXzJY7rq","vpmxo87m5C58":"Vq9llcC9CySr","dJSoNGCyYF9v":"6b6iCEe2WHoX","AVoDy0GRmONb":"eM4yPSB8nY3U","lga067PR4pk5":"NKQ13z4cua5P","MUZxXDotTuyl":"KiLgx1hwt5eW","hUpwRELbirdr":"0zUGPgxg3eK6","sRbqmPCsRBjk":"8rZoake3r1JS","GUIO7ur8NOl7":"cyEh5DeWAChj","hOYicKXLupgE":"C1HSJXuO5GFo","XHx4UpMBmoki":"DJtIN3HbWUjr","61PBs5LVpT4J":"pd5cNxmqZRAn","xrUZi0Y8bcMs":"enN5ar3FKuwc","4RKQ4EMqf7au":"hCJqus5lyOti","gR6ewaWnOnVM":"bklFJheIGqe9","KETvt9sclJnG":"aGNQhfAjmhJ6","w00B4b7KHCAB":"DB8UCZU1Hkb4","KGhns5MMxrnE":"S1BzaUWHcsyf","6YnuJ139DXET":"BmgUYdw7q61R","IM5BY7oFwgz5":"HU7fHB4hzDMc","8lJgLHbDyYFU":"yFEkP3mzS2A5","eYv6YJsRaJNc":"IS2iUsK49Ozi","Hezqg1ZNTcvr":"3BiKVxM2zJvp","wbTb2HRT0jOE":"qld7tyz4yXFO","YTDbcTGf2F7v":"U6xRT5OseWKn","bwbh2km166A3":"Q4zrnYI4O0Mx","QX8F3yer81xV":"RjYYo8AVbRhD","umnV9tPAO2xE":"TAFkteE5iOCr","qVgAEptzeDjv":"EuRrklaTiwd0","sU0g39vDi903":"8N3E4lr7ldBP","tv3dsbICfvpZ":"BTATHizWuNT5","iQi1x67XuFP4":"f738qYH3WqBh","JPiNlWsNSyun":"eSPk2VTubwxq","22m1KSAn2MMv":"nWn20H1Ys4l2","kf0UQvTUlxva":"2rocUWPah80u","otQGrcjEysTZ":"OtKLJUaHh12A","0Zt4h2aPangB":"nBha8PgovIvh","HcdpjAGt2qG0":"eftDPuZFrsQp","eiJun5hBlXGH":"CY0yWqYtR9yd","eH7zM63MkNcx":"tkzT0eLCxTNp","8jvNO9SPAu5f":"eIxonmhP7pcE","qm4lUbZWoSjU":"QYW6ccdKvfLv","1yJjEOBZ3F3F":"V20UDxtNtMBi","DBLungWmbAI7":"31mkPBsKU170","rzBEeG2wOxzP":"gVqQaocFHhBv","Iq6VhAyYEgdP":"U3oRLG4UXFb6","ZQAxoDg0hDgI":"w6134HyaIWYV","2ZdHADf3fwnN":"K7bitXlOaG2Q","vuvuNA7BgOxk":"TlPRRpx5xU1K","0ECmLGkPdylS":"VZMbP03mjpty","9yy395ba6eVG":"LimoIGeG1Qxz","c0yMhHYszLhH":"7Ru7YMcrOtcI","xAhadOcBvcOa":"rXJWnfyVrq54","C6F5KzHoZT9J":"NpSUEIwFmcVM","VlZ53FjVB41X":"l3cnidIqagKP","K37Hr6E3vFSP":"2LFpucNU5yT5","DDQnUBDy5H3G":"LmS8XB1zDnzT","sy3hhykT2zlb":"ONAtgaseXXq6","KINmnVN6YH0c":"Br02cG19q8Jm","N1QWefnFzI9V":"M2tQHlOV0Fmn","iHQRUj0e0QdH":"8bP0HJZf613I","4LRCofCbflAz":"1AXEPPjDhXCW","WED3j9xhMtfk":"rmRHOn5iCIJF","KqYotQtLJG1G":"s7a9cPTsZh5Y","9yn5TvQxafbS":"O8Nj8GNWHdzQ","mkessg9LJ99l":"WjaNSqmZy75g","98QfFUQSzTPU":"eTUEwuh9M0bx","FsLXZVrHqsJ2":"EP45QrI90rWn","mY9uspIlBhtq":"xX5pO6K7YOZV","0LR20LW2bOIR":"hGpSzzCbuDk7","lKVTzSuN53fV":"mkN852MJarpK","hLHRwjwvjEhL":"V8YM6muWchvF","8NE9g3xQGBr1":"SuaQOt3bpWkT","Zr2DcXFqXBGa":"aZT9N7oV8WIV","dEVIrUwCYrRQ":"hVFVRHM5qFJl","Sov8MLbPuXKO":"V7dcEVOjBUfp","fOqYrDSnQs9E":"ZQgodiLXyhHS","XkAaT2IuR3lC":"zsmLF5kzDDEZ","5ZK2p9X6AKQ4":"rW7W8rLpg3Oq","oRJ255NOhbH9":"DPaEb4QoW6Xt","Di9WgCZbARp8":"esB4ECrzjAlL","GeSaDTltdNku":"p3BFShElClSX","qb2BHVPLcYuw":"vYcwVUdl5vJh","iBSaxBKZA3SQ":"isryj7tjH0wP","mopFvp38QwmS":"whjWhLhPXAZR","hvdnF3CXRuvY":"di6zWaugIouH","rj1gXnYPJDms":"MigAvTx5lPgY","SZFvvRvMb3YF":"L7tU4Y5sGgZb","SAx2wZkaw95F":"EMCupqKOBpta","v00m205zKtFE":"KkDzBUCJpAtq","vWxTFcCiSPYR":"rrG9afQpez48","DyDkV21OLtj2":"VAt3ibS0TmeH","DBQVimSfIajY":"zhTbeYtloZNN","9cb1UIpQXZol":"tyQq7oFTKvJ4","Rijs1rrdtmAt":"VUSZPQUPIxVC","mZ8D0CCUHaMD":"T1emc04KoILT","zGvz5rN1JF7W":"Rk4ZD2gRdd6L","EjJEKeiiDdvd":"77bGEEPBfH8S","XLQfbZ0stxJj":"nR0LtJBxOSwy","SSDvUFgFoRwJ":"9QAh21nWjw2u","42QrpM8U26Zg":"b4lJ5CkAlPeH","1AUOLu2SJYkY":"zT0oWJSg6m30","x1udCXMEIFWf":"O2mQSR2k0qov","Ghe3fVnh0AxC":"qn2ripU0hEVg","FFwEzkI3Zqg4":"N9UB1vgbdaK0","NXaAVGE5lqru":"IRgNBcK8M2yq","dtWGME402Qrz":"wL8U4PAyWkca","Ha1I6XvdQWxl":"HLP7mvSytlIh","E9OxCr0TeCD1":"aNvvXDZqMKmZ","PjwwAozthrHJ":"dEl4scbM9ApT","zJQbCSSBRFUV":"flOo3S36rmiE","1JkYPnJefe0p":"0LbeZrXpWwaK","VRKFHVpS5zbn":"J24vEXrl9xjH","ZvQB4MuMwemd":"HhLU5nw98ydR","BvcJX11Vauq2":"D3zIG2CTF9Tk","m9pEH9csY8RN":"SaIocqyKRSyL","CQ6pycZfgSHs":"uAIPIVHY8ERU","WAVDLAfSid9c":"PoPapAcj43dn","fmkCK9VcsaXP":"KUMQUbCUjCLo","jpUqKZllnuws":"s4olsJR4yrST","ULaJKTg9RezO":"MoIxSGxxZMH2","Hdyqz6lxPpxe":"EDT8ybvfYca0","mfWwFEL4uAM6":"szRDgDPEt0LA","sUKUPf8hveuY":"48OAi6wYkXuw","AS097okGrAWp":"OPJ4GhYqUMRB","8eoyZL7SnkEi":"ihVa695yNcfm","h3gddwuQAV7z":"AcccZluWfu6w","zRNMUa8Id4zH":"JWQrNquYxSo7","vMYTLXIDagt2":"NGfJKUXe688z","ubLNI5016Rpx":"boIGMjmKJ66T","2tYd5bCnbEkL":"tZXy6Bwa3LGI","MvY9JBO7XJas":"DJoiA8TH8XfF","cd9HEM4AvLCU":"khMoB1dmNcJE","mmbJe367qrEI":"kLvVJzN7Vu4g","9wx5LTBZb70G":"Q7KVS1OLBngY","nXEGwWjKnAxb":"uk3mFdeUISKh","Q4hiYUxjlVAx":"3gxMvzQFt8s5","B1LISNDTahLm":"kYDvCM8324aI","ug7Fd28dgjOt":"HN4Wn5cOHnZz","fuYSEwJkgfc5":"MDTIrLtqlaEC","z8UCJHcX7mWo":"DMt76SSAMaK5","jdo1X5jjViAD":"1UYu1FDFhwxb","4KpbxmfoQBtg":"fOOFEIknyCwO","M8sKUsAqMxAB":"SK7MXx48jETT","EXLn4yvUEYxt":"KiQ916J4I6Z3","YhtUQG09zAOd":"FC5tjWTuxf7W","yNDviRHQeslD":"D8qkDpQc0g0Q","ysQeJS9hXxtT":"ORrSSclD13wO","Bkbtuhhoc5vA":"f1PKnNuJp18Y","mN91tE6RlcK6":"4x64Vj5Z6OzD","SqFdEHdFpqdU":"zAKgnTt0wevp","SrVrQj6uHQ3F":"mAj8YzFTWOJn","kfNKvw5YJjGO":"dhgyya7x6JTC","WJLzQkCmVar9":"oRYWASRDwwP6","QWpEgbSWl9st":"vOauYao07Hyr","uURR12HtfWga":"hA5Xvujarzqp","jxymgmPW0uDp":"kcSHUdRJcEl2","gMxFVsoERKXQ":"ERKEmqQJk8bi","TsXV9NqybBBi":"HHrwHtg9Sk5y","isVfTIDbgbk8":"HxwNeACNPyb4","20GzMLE4S1IU":"0h9oIT24uRmm","TZspXbi9LFpU":"s8JFa3127nsk","KZ0veHTmARAQ":"lprnEq1JBcNM","g4rv2P0bkbqF":"rTPAkHa6Gw4c","rkWFXfDGlb25":"QK9bKHOaV8jd","A7d6mU75r9NV":"KSE8UHE2zu5H","tfPGccAFMlPu":"8QUnCxcuFFPE","vuncviGOZDDb":"ni9KhaXYJi4A","WoEQxrWEsFqT":"ap18D2UbY4KP","PXIJwDnjLocS":"DeFoNy3ISloM","Z4dfWYuJURf1":"tmKTkIeKDvM7","XlYCTUr1QwTo":"6K3t2AtsJd1f","NUjhYEJydctu":"1Eku1aGvIDbV","1bAnGjtwDSQa":"FDpbd8aRH327","WepDHsmxgzHw":"ilrmP2xGTSjh","axakqgiSYJeL":"veqVVLlXq3fa","fUGne7v55qw2":"V4fYV92207VB","DauWoKB3Gd0O":"hMUc6uhntxVJ","ZMhlH21ZBMtz":"xJ6K2VCHO2Vc","PuwZ9V4ntXyJ":"Xqqr47KbsmDm","9BUXcAANeinD":"L4gYICrSA5TZ","HWdgJLCQw3zp":"AsxXZYvYqEUQ","u6IiQrPgq9FB":"mhvYfLTLe3nA","eZEJA5V9hnpS":"zXO9mPD8Y0sy","6EtuY6VTluNa":"5YBksLIV8dDU","cw9HTe8mycYc":"pqtJgbdU3z7M","bJMxarlCtsMw":"s0QCZRaHLCty","qRGKqm9Cz78i":"Ca83IuqvmcI5","nl8WoEPwN7Sp":"k2HEgS6QWWgF","hXAjKCTgHHsW":"Y81qJWKBQMF3","LRtTU9ztlbjZ":"EQSwr2xQ9mw7","r41uJCPPgegD":"w6eGPqKWJ9pz","QhKByjPGyakX":"Juj7gRrmQQPm","rRB7rUj6XY5n":"Fh4cV7tztOGZ","1g32IYrjSrlo":"CqfDZzjBIMJE","ek2XptCiT8cZ":"L5ukrkmX5UPC","coyBUPPXkPC6":"I9EzqOGurMbL","4rMSYDqeXstF":"HIMIiHfnc7DK","5RLd3odMwdfE":"IZ5edn0lTHoW","dRpETK9DwaXw":"YbXUuzpbEK6u","xRztGJZ8B9jI":"tfdxHCTQqp7t","oBHlHt8x3sp2":"XKQ2DkZqPdJu","RyL9EY01A44C":"iyRBX3ypYrh0","LJGHisE7aH48":"Ls4WYw1JVnkO","mYJ31AIUrbdF":"l8Kb5qspLyCl","1QpgEzBBJRFi":"cMVwUqe7z3Dk","rc7sFtBUhwO0":"gR2PJQU8vlyv","jt7TbKLfRGTe":"QADzJy8g1W3D","Wky6I7AacEBq":"af5WhMXKQVnA","5Y4RsOij3RKV":"87OLluTvC0YD","64D3JcoBw2Hr":"znjbE9LbVtwQ","F1H6jRUrVxBp":"ChBCr4Oye9kx","jnGVhjE0Gnff":"FHrkNg1SZoZZ","3II87xocO39q":"3wcFft5TaKaD","IBEMlw9gf7ze":"FAtf46c6gotL","ALv3jpC67Q1Q":"EcfykVTDY5xI","K1cL3nmIiQ5e":"v6VyMBfz2V6z","V4lzaxGCG9bL":"2UARFVAEKocN","KiHuGvUO6SR2":"vobAVebnOm6w","yQ0DGioOirBV":"9eycaCPp3PNv","blCh89ChpYoG":"JBtBbvzhkGmy","iMrc8Gn0LUkc":"6EXpQuywv0TM","JglQc4NLlupb":"HlhbVDThMri2","c5ymImsc1BiS":"Y6YeDPRGYCww","4OPTTtxMXEoX":"0rGPAyQsUdLk","Bfgp4IUBbTvR":"pGuNJynnFtiZ","IfEN8OPQs8u7":"nmXY4ppjl8ei","RP4LWWjSzF04":"4Ji9aPtWDUYj","aQyKapcxGeyS":"rQlxOvUdm60r","xrVWaaRT2MLg":"khLmWraAfdxv","LwA3AHdG1NqJ":"9sNAoRF0ClAU","f7yoD2KMqmHZ":"dXaCWYmsxL9Q","TcjhLMrz9OBs":"0kqnOrvf0m6g","e2uFpGSr4CRf":"F7d2DaAVkwmn","LEiYwfdZgBXo":"8km881xT74vL","oNGzKdB8b8va":"L92xPm5CxxwH","FpQbCyfBCGlw":"MENAI93ECG2v","WBP7Rx6CjN4t":"cbOwGv4Y1oFY","D6XIidm850T7":"hlsQW10lFG6q","Za3arsfNslsr":"NY0WwzoOyH8h","uh4KG1kl5qSW":"lLcXBhd4a6Ie","dzDC6y13GYJm":"ljMrgUty42FV","H27uQs0cWg40":"OjBPp0Vid1JO","5vxx4NnG2DOk":"F7zaLyWf6Br7","xMgmVTGt4cEl":"gQQQw9OmN9Gl","TE5GWka7PCpf":"h6brrGCf1qST","3lgk5MUTZZiw":"5EkldS4iiE6W","coDbQQZB7hqt":"MMkV5xJGM8as","AgLQbdSKTVoM":"BHH0O5SFZuDx","KUP1sfhQtrzD":"QP7lVEWqRfVE","3UhugI0enkBz":"EX2Uk8ixZslM","GaupZaJhOD8P":"0PUmhAhatcFZ","Ij2R7wfI6mVi":"SEFX9pAfvkvt","qQzBqfmnRid8":"EvP10cXtxqEf","hwAUapo9uj9z":"2uPkDCWEui2U","z4gufKhkb6tA":"GoGBj2dQjhCy","fciE5Ht887KL":"mfL1yM21illv","sNPvYXMIRB0Q":"kUclVfXhz8Rs","RsXywW3iJ76g":"XSWMKLzt8xF5","Vpd1YGQUNUjh":"dd7JsKaLhd5G","laQXGIrvglF9":"ItwL50YFTxx2","4I1OgS5G7hcO":"phM36RypSSLz","xUHB4hzqa8hL":"quJypmcCQPHA","8I7UwUmcizYj":"d0xrXdqK1bn8","fbvk8C1BdPI6":"NMfMZDMyDtl6","LqsZOiRFNp3Y":"wwoA2sIDg3n3","JoWAPweiPprT":"7GuDlNC5yMZm","eBJqLcuN25QY":"e9v9RuBKEeRN","2uK1ZdDnMLDT":"ztrzm8hh6Ive","wk0eXKBufHVC":"yjmBQftk5l0t","nVxFpiD3Xzo8":"467tJjmjoO0r","VVMDAqPSdFOB":"bZhIBH2MEDxc","W2SX5m9Kj3PA":"4HVwFdOT5z1k","OA6ibr20Ljs0":"hO15Wr55cU9j","JDPbM71YxOpr":"NcyO2jzgl8zT","J5o2FqNSAciY":"XnTITnGqIu4x","5Ya8QU0wdA2G":"30LFoeHqQVFA","ThoMrrnZNYBm":"NnhSI8e3UALt","Eu9EXINc9xK1":"Nm5DEgcJfj6i","s1LpLTk1bps5":"ivxC3g1dWtSu","yT5cbppULZuP":"Lee5TnxzGxYg","vSrmthG1CQ4C":"xhZekEXnppiB","eaZUmVnY64t5":"1zszpCdCCSbX","pwx6SzKWMMCv":"S7NJCxZaTzzk","BU8LgvtD0Z5g":"p4CdHyVdPqyh","FKgQCngrmqBc":"Tej0wBKJXHzD","LBgncLqYXzPl":"iz5JOtSHTme9","utKXPjUgjD7M":"BOyVjXgXdDjs","swb2cJDkLScr":"cLDq1QCGmjOP","25Ujly66lmPU":"pMqmq4RFsv4W","Mpqd5lOJLeQ5":"RhR1XFCpD1Tx","nw1eyZhhkIIX":"TBSrKXJG1chI","pQXcFs2zvKVY":"CfzO7vrHV0vD","cDaT8P4QMNQn":"01JkixqXFF91","qsdFLGf9eoL2":"7CqfPeHByc1W","aDA4lxxsHQs2":"hZQbKjOBSEy6","rMmAJrTKz0cj":"Gvw6kYKuZE2s","9YvzoASwR16N":"1V93E60T21sG","GgK6aL3kkzMT":"uKI4fvk1aspo","RCt1F4E23PHx":"Bpm9rmC2kuua","wBgun9R0wuXz":"nKQEN3TbHHzS","tAWW7XB2MCyP":"IiSlqiiDwt3q","DIrvZTSshG36":"NlC7BI8H8oHY","GyFcJ73bmgyp":"MqORJVynnCvk","5UhUOjAalOGz":"3ShRjxj2IqEe","ZICzayFD6jfl":"KzVg6yvCAOIr","8OmukUnr8zOh":"78BqgLSCJ8aX","PcLmAzRuomdK":"zTwJNBU8S4Hm","fbeuZ0ZTgSQl":"y0epqxOIxEAy","U6eaTN19usKt":"H8QmKbLdvrR6","NmuMk5cJ0myt":"4fI2tkHWy6Mq","cPFlhLPC0N06":"UiH6vb6nWQxg","LAkuyg5RZ69K":"aMVF8rz1CFVK","57tIwi8UXIAF":"92aNcdxJwFkR","nglIUChEK3Hb":"bmVPPpdYMcbX","hLt4mnBeTbK6":"PMDDslmAqhx0","HKOwAhmP8LPd":"vQjMVENZsilu","lt24i1hEaub5":"efwqwpBXEyU0","JOiWtaKtjTxJ":"KB3T77avBAFe","cYPvyl5NR4GO":"7EAOQLVx8xBL","yE6Y5Hzdts8x":"MCMpVZn9CiwO","abZY8BHJmIGj":"IfrUA1MZVPst","6J2lCRrVG2DI":"GnhPDsSvnZMg","Lv8w6919zr0R":"K1WPJ44wXkmu","efDm9OohBnjO":"SH19qbRCmSe9","pP2B0pQAuTJl":"uE0KehO7CpcK","6u6V2yJGazvY":"CSi7L8FhnoRb","umsmq0xgOaM3":"YNo0Ek1cfoDE","UaYKoWeNljzm":"jymcMr6RmUdH","vCtyZ65ThIAc":"G3oPjOs3ubts","gflsHUUa1zYr":"YJFv6e96oSW3","mppiacqpq0LV":"QgfoXxuWxmRR","Yz0ogpxvcp1Y":"08zPEDgOlPy9","x0QZjCnOUlvJ":"LxrBnt9VN31P","kcsxuptSDJn0":"TcUdJqANa3aw","cXXmay9io9k2":"cjBBxMUU7QXi","qST0uqhv5e6W":"qOIAIJV8TWa0","W8WLk3R8EZzx":"VTWbU4EDrzpq","H5wBqAszfIQ6":"39eKFcjGx4PV","zHXSBFl5NbPo":"cx2bfeszN8ek","s4iH3Cp2YX7E":"JXpAyCPlpruW","DF8Hl3AzzJwe":"7n7tWmpMhwp9","z29U1WMAZVuG":"tsKmEVoyVfOm","pYtLIuU3wz3w":"JsToJUciGYaX","sX2wytVBkgxd":"mM5ckeb5A3at","PUgRTrgU6hlX":"qEX1BBG5iSIp","Vr02zeUHZRdZ":"MLEhzQWxGIzN","xGMmMbWVAQ3p":"ZBWXT564NJWv","jI4C8aSpQdLH":"Po7oO3jH9tmb","hUIVhgeVqFRP":"zVm24lLnIsAz","d4UFWlkOPW22":"sRwhXxpWuD9M","eYhD787UGa1A":"jrctLxgghmpz","fyl9icvKcH2m":"NjgzSBELBb0I","GQY0kUmWUHNu":"A0DcUJfTiv5V","LgAGXRHdzNz4":"CV672vCax5fG","VnLC6FTQ8Evu":"T50GlTUt299u","cnnSQdkUoaUo":"3qh58kWSRaxQ","FSBgIBwbj0Dh":"Z8llDFJxoypw","uZIZNp4nZfJe":"fcvsTneqsDnz","p61uq1tnDRgm":"puPI9F3WrJtG","aIK5dZrs2k4d":"uWRS4HkegBbI","AYnBJ8TUBNlE":"eAYfL3P1YkyU","XlXTFjPHHGOQ":"ZlzV8dnBPVoo","ho6FY7W462CV":"0zr8ZBM1YdWE","JBect5LoLauF":"Ke8a0e1lwOYI","zCjDTzNdmrwV":"A2STx80PjecR","L73g5JHRPDbO":"QKWldrgXvU9F","sZF70klwpzlo":"ww33Mr9nG96F","UOtMuvKwvBXD":"UICjmEnthmzg","Jfvvl4MKujmV":"QpvIGfkWcKLQ","Eyn4Q3cFf5wq":"LBu6l3Vw0KGI","B1tiMVFlBfgE":"cJDIi36eqUAX","qpCOnoQFZ7MJ":"bdNG4C22Ptak","8Hbwn3vylvJY":"DGeFCXJjlLpN","eXH7vBKu8OKb":"M9BqSHrMAiWt","Aht267NiZCWh":"n3foaffJfwT0","F4pLYOx0gKL8":"ZchHSucBMk9B","yl9LxApiZw4Z":"rO9Ra9r9B0P8","Cfyx4hYcWCNm":"44NRpDB0gr2K","BznIH6mqMbec":"4zLwENz4sagH","kvM73qMg6zCO":"6zt31to3w7Fg","36C0dax6obBc":"C8Cu8PEB6W4o","ORIutz0KgQIi":"ILlxe04rY6wP","vsfOydL3jm8U":"hso1SRj7vMde","M8RC80smHmo2":"FMfr79Qx12pT","hajfVEWd7lIB":"zkNfu1meglgy","rbxe1114GmYW":"Wev81p7UdcSA","MvJhHLlz2x2P":"PTFMnQC53JWi","fFOxDY18bP5Z":"enh1aYMuLVhd","4Xr6ukFqDlad":"4UX8XlxFgU4U","ZwieTFxnsTie":"Sa8CAwoHtEAt","heGkBCCetWrc":"1bW52xafR1b9","xSp29wpkV8Qr":"YJ0uceVVKO87","o2NuSjI6up91":"TLMVr1qwSQwd","mNtEnFnqNL0M":"Ge71ucZCGxTp","bsOZlsra0opg":"iaCGL84uyc7i","eYVGCWLaV8SI":"fsqX57Bujo5s","j5bq9CD5IHIc":"5ckEwMPWarDM","1XtLlTf5VJXr":"Agu3BZrEnlAq","QATAP9d5MvI2":"VU7ohKSRmyBu","0WnWPsYvB6fR":"8OTBfKzEqtr6","dXNNsLKCx5uq":"Qu3IwVz1jsGi","ODahaVsMrQBj":"8pBdzF7DeGZL","jAGSoPbA92UG":"TfF5ZD3W0zVB","0hFG7dkzYrcl":"9ZWjUlX9EpL8","EcJIkhto00ub":"xWfAiDCNykwS","XofY9q4MzpOQ":"W6iFDFNwkZrv","dk35QZls4R2F":"GWi3yYGJjyti","bgYFHwilcTKD":"9UgqN7WA6ZQt","ooSge77GOOXm":"81Ts9cbJfPKx","1fuvJmn0nvMg":"mxCeZP8tUeuq","isds5h46Nm1f":"8lP8CeBsa8we","1B3RhDmzLNPm":"68lml5zUqJXK","bATfCEKh401A":"ZmRizoesIKC8","LTaTGVCB0apf":"AQbHONMWIqrp","Xe3j2oApMfOZ":"FIhZl82yN0tE","K7gMVYKpee6V":"7I0qKfZJ6Fn6","HHI1DJs8x5N5":"5FOrfGojDm5i","8BTE6mSBXubD":"9EVu1wFi9OzO","WJzWHFmNydjN":"31Zh8IN42QKp","dUOXNe1t3m5V":"EsfU89Kz3qPT","isiBQomlHfTE":"aI4YhIdTfYZK","QX31GQ2g6Qqh":"WIhzRrfyRUWT","ClgSmK1ndSU6":"qfhqeJ0ZHSJI","BxtwYjZLAcMa":"8KtuxyngABgo","Yh1FgDJMhHft":"YbiMKfpYoMzE","JV7SPkg2Y6jD":"wF0js42yG8sC","CGIF4TIMpzNP":"OTZppio8QbyB","poD95KjlNZLA":"mkwrR4znQe3q","BWv9O31D34sq":"UMH0DNoan0ET","6d5AdkSk90T9":"oSn0u684r190","5yuz52YqObAY":"MYbE77KqffNk","ekDL5mHRztH2":"2NsIzQMgEFcN","P1FfDqJI5QMq":"lnchOHS9oes6","LESgriM3CjiA":"XqG34TtUoLf5","fBvtx49Ifjvy":"v3EXVmz0mhHd","9kJBnyaE03Fe":"7r2sIbJhmYN6","JGC76Dq05tIY":"upi2fDzplzRz","h96XlkwaJ8BA":"JzIogJmsEHCM","zKXrvoRsNjJV":"xtZyi4S9ji3M","MvFW64YGU8zE":"mPMeBGLTrLpP","qqfOJXELiFh1":"7EuAmlv59LSY","p2vktnwv066O":"2lSG1YXyZZOK","fX2MRSXK1lKX":"iJ7MZdntu9fI","xn6df6awSfKO":"LXDmXAcwLd0A","HxuzfDKi71lC":"9tXMgdx2igjR","ICpceA6iCsrz":"ZBLebcS8ppPd","hKvVSF9lN8s3":"5oqjHgZZlFih","ZOgOzU9kkEkj":"QrWoSjwVq400","xxqGzxhCt75Q":"yB4yOWwc2ns1","HGSO7nruxoXE":"UiSJgPOKGTTt","geZgPpBnGhnR":"gJVy78XeUJq7","8CkzWL4MxVJX":"Y9kGu1JUy6mP","gR9QpEAiGhTt":"6TSgx6UwvK3B","xiR9MxG0KqHe":"szE7T5GOazr9","bNXCHbWpDdRa":"gaFTijgb7yQa","stszLixx0d0G":"N1qMGKBPK4KR","SZWOzqU22dcI":"xofyPHlx3D31","vzHbwaPdU9se":"SsZD6y3TZhE9","hQSpmYD2muGN":"6UIqTSFxrQB3","xQ5MJDrwpd21":"GqIcpx0BeuMy","b6I9nNNi8UFG":"xGfpcfYsBqil","phMUaufudIgR":"dlAFEfd9ghxE","0pgTw0HJLh3L":"GlZ0VXAywW6a","uu0BwEwJjdTU":"lnfCr7sJPaLy","jA84f6ZlQ9fY":"UYXdal9h7Z3M","HWIDDGUO8R3T":"rfKkk6R5UUiF","VyD5QDcdsySH":"FLt1mdTCBJX8","Rjzxnm2KQo18":"wwSKDkfqWAed","fiMbOx9lXntx":"riWxDEawBGUD","ESVy9a9vd7KG":"k3maIadU3qS1","egjRb7HFmYDW":"MbSbgg4fIVYK","HyVsDMeg2mJ9":"vNTGpU2hHzVL","UFgMAxYvvOmp":"3qwdFhJUdcZc","XLYfv0Ia38at":"7TA6kRjkgHud","2OMrDRV9u2GH":"VtHDMHiZaBOq","bsiaIib6Zc5L":"OmHqLCFFyqLp","XOayTU4M8exv":"KxSx9PoEOTJA","oBuTcaUnRhpf":"IvFMCI2kS2mG","z4bHKQcw5OOX":"9SVsnn4SHjal","tWoCY9r6MkXz":"2f9OMlU5lAhc","UP5m0Khhzja7":"I71paeLTqOLA","FBYStwqBqQaY":"vzBRUa0T0U2T","MoFG4ESfdCoV":"ckJ0ROjwiySo","9MmgXgQXd5H6":"nGRxXvHd9KKk","jLdDitAfDKP9":"Wa5incrfMSkU","L9IY0Q8kihf9":"qxvLKq2kfe7j","pbLjp5CFMGTo":"jNdObBddjEQJ","9e0B942nTDD4":"hetPV4RIkzlg","WI3n5wY9YyIG":"v5Eg5rfax4ka","vUw175701c3W":"IRf4ipj5hj5q","16rbcDDiGDLx":"KpOZXsO6no3T","LAAL4GBWWJzS":"EE7RRXKEXbwS","fyskP9Sf5fGL":"BEZwwY4oVcSA","DARjLBjp6EnC":"j6l5tqUEimCk","7nW24ECnpTJw":"CUBllJMJiJEp","d9tvna1ikJyH":"ACD3B3oWqBNS","wwneg0Eg00WY":"2BBjooK8W8CP","GJPfZVmrJJUx":"zfejkPGVwNJV","wx3wAZGL66DD":"i7BdEKBTIX5d","If7gnIoVKXFM":"6s0RAzvAC46q","lhqVuTwiPFyY":"mfDfKhJwo0l6","oKiJ8xSFqwDk":"Hu61aASOYcLL","eLIQjDMMFC0Y":"Rde58JrhaLBL","op7AQpVccsRC":"UeLhMWz7tU0j","AnoQwssIuujJ":"UvNiLT6aWF1d","dNDsRUJ7Q5Xx":"fqbDcwPBNkjC","tT0DtrME6S1Z":"A5PZPJglTMNk","KNYwuHUgL74M":"2DsMx3VTfZpm","xezBsDaxJOju":"HxaHWQtwwcCQ","bmb8tKNAj0o2":"qrwjOajNGO4c","JLdICRjlQTP4":"Cw7vUrJcLpSi","mZgLNztBydam":"DacxDeIUODky","7uQoggTXcuQ0":"blR4x1REFy7G","Rh6TM3jr1lRS":"AbFNODdzvf9E","ZIS2T09c1CAM":"TEmHNqwsSaub","vwTErdFBDUl5":"VQTPBlwwHlhc","tGhtk5L4UU0I":"7GjvimGY1R5b","kY4hHpNoD7Yr":"sGahwqvQD89q","cyn89CssGaiL":"6exCq0Mb8FzH","ACkNzJYWY2kv":"Y9wyGEv5AjJd","8Qz9TFVSPEXF":"gYigD4ymf8zO","PlFpOsCPuM57":"we3Q44nW3Zd2","jf7OEkV0253p":"KriOzcs3nuju","rFLUjuguX0Mo":"ig9bweKwfXlV","0z3HzaQ5O5QB":"ugLnKSfxezMf","x0r7ZIUSxJ3A":"mcTvjDxGV9CK","QJACgTmbBiCr":"0zEUKE64osmt","BGkjgmBhF8Er":"nBtxHRr47W5J","ucudWfBANNzm":"RFZyIRc8ERdJ","oK5PFgDIGwDD":"6ZdokzxfHMqV","8BWFFZY8NzY4":"hqjFkxUd3iI1","lhaq3EknMvly":"W8OInMzibHjz","seL8bbxrrvN2":"WZKNYBvgxVSC","V1ghdyMNRjmH":"E9YVkBR0zfgg","SyVXSzsp1lBV":"7eVYidx0S1RJ","4RvyNuhod8Mp":"DWpQsAJpCz7E","kRfNThE9Y6zu":"Cf1ud63eOjZz","jrB5jzVFc1nl":"6prAUNUNjDjk","uyJp8mvxiTOx":"D7gyNwtb5I8p","fXvoiomrqe6V":"WJlSbaJbqi2u","WmBEaWduz9NX":"0h5dMdXOLjDJ","YyrF0Tl5P5yO":"pd4J0jrCIUT4","Z32DlOsNV4Xu":"pGgWL7zhN0zz","DvyCX24MFHKS":"gXCKLQAAu0jt","QUhjIw74vUji":"x7Hypzi2vei2","2e2IbTUkArKm":"g3qkrQfJpHRv","EcIC7h5XJfbL":"JZXxvN57JE8T","WNXt6n32i82i":"fngdV0jUSJVi","yq8rw8JljXY5":"JMmmZEeoapLE","CPgP1oH7ZPsA":"YZuOJF0XkK9H","flZXOZVTSQO9":"KRPqoZIXDLJZ","J0wLtFMlk3nM":"7GKfSrS6G2m2","U4H5V5MHq1wG":"HxTRhbFZddCR","8br843AvXJBb":"QnzE0uCUizbF","TdSKAHRsGuPb":"KLBYq37ddqiU","oROjPvI7fkma":"IKLhybb4RVmw","QFEmeQg5H1Tx":"01LEFwl7cUhQ","0UtdTy05UyY7":"K2IspgTZQHlx","dBZQt9DnKU5z":"ddWExHb5MFW7","8C1axOCKs2u1":"4ztTXAWy67oB","TaynT1b6SBEE":"GoYcwmfxFtlJ","rVxR7FXuyc1N":"UjKEzzli94qN","RpcN40tAbstL":"bJM4GI9R4YH3","txkHQ0azbz62":"hbTad09m9twn","yYHBNFcFC0gm":"kqccxU973ULl","zTscAC6ArY4G":"YEynLbnQh93g","x9ODJhCUuA7l":"6ElO36EnPPTE","uE0vHSURQlRy":"5TrieyKBjj1k","hxAfxit9JWuv":"calEy89hbxL1","X0Tg5D4ye3Z3":"x1s3Uy2CjhD4","ZTMtuaLgFuWe":"vlubGfIIKCop","Dg4CIr1qr0Ew":"5XiWpwdS0HNg","96asUWkBGY1t":"5lIRu29v4FFf","hykyt47aqP9t":"UIxwNTlagdzL","3FRpAl5gqwo2":"nmMAabFaxqHk","qMyPB4c33sDG":"VE0pBFnDvuLt","X27ikruIg4xD":"10LR5os7nkzn","Dl3D8cpssppW":"ZVEDiMLcZbYV","wlQQTZBfBQUW":"DQHHTPITHtlx","AqtOG3EmDjG3":"RkIr5EMoOPQa","kBKCCslzY4ZB":"Tfg3eFevErXI","1mQ6hdWgcaoa":"HkYfDswyjWIF","d4cns58OzsQM":"tt6Rt7ZM5QrX","mZ9GGjAv9x0g":"UvH8GdUvU6Pn","XYhGqGFGIyAn":"LwEuzCkUo0Ey","8E9cu4XzmVCu":"o54AjDn8aGfQ","w9AMRmmoMO8j":"dVTGoYDEK1Hx","fcV6tSQbnb1M":"VMmm3chO9Nvg","0RvzUpHlVloR":"gvQXMg6oUyCi","proGgNmo3I39":"CJKO7CEvPYYV","o2Nsqwzr6hhY":"QItIXLvJVNQt","94DMJQKnef4r":"mcb5MLHU6zey","JacXY6dpWKKz":"ZVMHyUvLARuT","1kZTztr3ie2H":"8l6AXsidZNrj","l4KxxafYWEPC":"RCcQ9cBVx8BA","inzZhYk5WQ0C":"eCUDPxcIwnPS","LYHazCmaMvmM":"iAt0k3FEQxDM","V0uQWL8e4tSG":"VpKnxwLw6SCJ","IJ2898kIkcWC":"eNKB77WTjcR4","TfzIji7oQriv":"hm7ISm2TYcbc","fptDmDpNJ4Xg":"uqbWoCDHweQT","dmWbTIOXHhDj":"ZqEkSjRGnNUw","pBxMhwlGl8za":"7X8D1EaXXgNV","lta0ShuQ3os1":"erq9uUiS3pGH","k7OSnDZRbXd0":"ijB5sKwqgqsV","5VeWSd9SEJpT":"zLLfvEGgKoiS","vGbd03sj2Uja":"kcHbCmYpC6Xk","IEoJLkd25MIR":"jxAYFOcqKPN8","89cs23PBTVOw":"MF9Lsg98SSyZ","pxkvl8Mn3y6o":"PMpAQHbz5RN3","SWd9AtBVjxKV":"U2mhu0P07u9C","vMqE4iTQdG9U":"jzNlJeL9gYI5","xxqGeeFVxw6W":"cpkTM2BPLb9L","sLrjE0PLU9Bt":"sBYgGXg4uBpr","BJYWbmpCEPch":"3aWK1GpwpLmW","UwoxnpxGkS2X":"j7QTxfgSrHgv","xPKvY9e9wWlN":"b3BTBMHT5Ole","NaTFIQG1xlE6":"d8tOT5TOxJBu","7aAkFNNP37OI":"at35xoGEHPnD","kfKl8YvZRAnT":"AvYIWQ48xALJ","uuYROfZQeW4U":"sWNzQccBWDTR","gcZGdRjUXpku":"AcM6JldKyqtV","IWn6UKiwql5c":"l0HrTRlpjiWU","7Ri1OBhsm6jK":"qau9w26LdMux","ImWlnvPgBAIB":"QzeyXXMKnE6F","7TaBrB09MqDs":"eFPENKj5cVs1","XDChrN8I7UL2":"kPbNz4BcPRaX","pETyTv48YjX1":"gJdmAipnhxNj","J0OF7p9j98oG":"8H0bDpcxaK22","qoz6RFYmqpIg":"ExsXow2JLWgD","UEoA7IOg02DS":"17518SymH6bY","9M1Wd4v5Lo0z":"83uXdhkMeQJP","KlCSkogKnrrS":"TihrnSQDN1gf","KgDlW0Pp9Skh":"8fcyu5cvS4nX","nhPdW10YfGG4":"FWQ0KfqlpXQj","j96N20H1NEXk":"jiOlcr8gT5Gs","mdpvJtfmRdej":"1wLXyP4oah5q","kyzm8nRI3VWx":"0eN4LcePzKry","NpBuOoWHprCr":"aZKab10TJWLG","I7uUk576KPn4":"BakOIn0YdkEk","hf5Fj9rzq1Qc":"0jyWnwDW5mB4","IG1IgvPaO8Wf":"xmHQopw32zBE","1ATbZKabqkvs":"yIK24fXTXCiz","43NdUXtpfIhB":"mgRIC7ZlhfLM","UHJCPZffXzZX":"x21JEz11LDaS","GTncf1nupoWF":"IqqmeS2lxdIW","lPYjuhCKSqfX":"HA5dED4KZpEQ","lNDJeM697Nvn":"d4aXBA5Bk8ju","J1GFQRtNP5CH":"kGTAE5RcVQn1","oaIjp8fsyGDo":"yg4kX4UUNkoI","367LDTpWZ3Kx":"fT1F7QiYURiS","cTWS17WLoUjH":"4etKZ9UHGe9j","auyc5PFRZAK0":"ExxlPHWoLfkJ","mD9lZtmp0m01":"yeyNfR4JsEK3","OHH62XPKnWH3":"3FGToq1Dx7ol","9HDd3POrKVbr":"do01iMExSqJu","7yIDyi3P7T1U":"QJ2d0YTsg8tO","wByHGzetw2Yl":"o4jVQkniAkIr","yMFEfxOyD6M9":"9SV9m3b7jhUU","gEXYB19fHgvU":"ZdpVxVls6B0z","bJZ2pcZPDX0P":"SXSKYeCZOI9c","HCWaY6CAxxvd":"Bi1nIrCgFBe7","3dwHHRkIABgj":"P9FsHeUgbjpc","Hgv8LfT6dRX8":"5S9UXBdNt8F8","V4DZPucruCHP":"yIdRRf4jium8","ji3AdwmrVHbV":"kdmQNwQ6d1is","uOYG167Yzsu2":"6K03jTR7SFRO","hqMJgbgpLxoM":"ITb3cAjBW8Si","rdFOawy2cCuI":"mIePFutPwzfy","SPF0de1yDwJ8":"T7wCoCmGO3i9","SDE3eSfNbo7t":"aACbPRjR4f6n","HOkp1usKv0iK":"1NgJKi5vsS4U","SgG6iWo4tKca":"BvZpDC8JSyVj","cCNZjB9kJ0i0":"6c2NekgauTHp","H8e35eiHBnfI":"LFtodC6zG1P1","9gXLO2ML7eXh":"6kDzElONHs5A","HN2Mhr3AcUSy":"3zipjN17rLYF","zGfOjiNoA5Gw":"8c52kEhCaIDq","RzoNnJghyNCS":"KoMMiamqJWuP","j1BZjDO3iWpX":"THaaAVkBZS6z","MvzBBfI5KCP5":"ZQJptDLpEhRB","g84jm3nPDlNq":"PHcUNP402to9","SaOXzvAOzqns":"zDbLanCiHUst","mYVeJuB4t7jp":"VqU6RqJctq0I","MCcnbQwXHUsn":"JeniiyppJbTL","uYYiqRJWAJnW":"NX97woYfHqOE","vHEMTEyCJ6z4":"wl04lCciI9V5","aKgv3BFlYBzr":"n3XqYjqyGXEg","ukzdWQsabwBQ":"dQytGZ0ipE7a","XL5OvNrX6NW3":"C1zaGEOpE3Y0","zuNTFfORIZEY":"cGxA1SIbsvtd","Id5DrgvvCOFW":"qSuIGhpwpJOq","Oj54fF5M6CwZ":"6bOCPxzVNoSh","cwjColOURXNz":"AbVE8S9t98iJ","1fTQP3Pqv95P":"u00puimeBYFd","nYgahUycFJGh":"zzkSpI0UlIwh","0MjIU6gw1lkj":"wocgsCTOC8I4","PlOuoO9vNtad":"MDQdUSZwIion","W79dpfKgfOeZ":"CCsZHk3ObXmL","mBQPwZMGmkuk":"qIpxypdnQb17","8ewO0e6UCTLo":"IRZt5vXxct55","rlvhzaoasT3Y":"xtqs4eJ18DOg","j9g9TrHkUkbA":"vtmOh9ermn61","ZMrpSBhSPr2A":"bNEB7qyZM7rH","8Sa6PPwvF5hx":"LNPF5ruYyHRu","f13CssZyx2I4":"IQMDbKK9aezX","uurvoYFyQErk":"SRWf7ZLyqrOR","4MmypYFgSJja":"kcz9Fe6kXds8","2d4I3990eLeS":"ugZXpoYGQNbz","Aei3joW7b35v":"x5H2MjhF3n5S","SSlaVYmlZjiv":"TLXHUHFTa4VM","J2sZHyu25E5C":"NvCAzRE7sOO7","jBDGUKZ6LgZn":"WLugxGJUbYof","4fp7ySnh7PMb":"IMZPwfqsBYzM","fgKTl9ayS6As":"7r1mXuLeWwQo","dg5fJJxdlhJ4":"30eVp0HvNCgT","e6ZMOHr85mZ4":"lwi4qjFKtAUL","YNmoRWwjvg4s":"iPjL2XToH117","emlCS7QzqLl7":"sKF7u2sKZFjL","op6UXfxWSOsx":"fcbxDQib7yQm","WsHgQBhFv86U":"fuidPsmuySv1","cloaTvk7dXLq":"gV1A6FQJ8rID","njqudmaKR9xT":"vSWO72Js1Bey","E8LZPorKQLeh":"e20heYqzJv1y","eBEL4Mri92CY":"8BXRYSDtNi9P","YSxqmFn21ojr":"9uHkEAn2DcCb","OujxjftcFZF2":"9pToNeROMOWF","EO8aQSdSOw13":"iydX9pkghXSs","XdjigTtFvR0B":"i3mUzM5Fvdq4","cTrX0Z2sI2uQ":"KB7hkSbvpFwg","PBG1oWtJdEpC":"eLo3LohJe9tG","isddVclzJ2gj":"GtuEEFUBI4tz","6fNW9Gbzegfo":"c7Ki99ArJBYV","KVW2ANKhV0Ya":"jwoGZ7q62pLz","lpB8YPeS0jca":"pl19FIrCbF0i","bckmB9TGNnSr":"CbXiG9NS0iGC","FiCFPU6q5aXc":"UO9CFX64IUD3","JgPQBRoFTAFt":"9urSQoessYol","sSjEfmUvb5aS":"QNGlsoivAjPu","woVNgEDF5VsR":"TKoLfJX2bRJR","rsgtIZ6Rtcsc":"092GxxPCQikl","zPkAxDzooV2f":"Nw7FkdKXFiOO","9AaFVi9qA1aF":"KDqjbRdbiBL6","oRDvrjOJdUFh":"QkJ4emkUKO7j","HYg3JRNPUtSp":"3kAIZsAtys3R","15KJmAMfNP6T":"6xNAKJJWE7R2","pDLDzNr58zxj":"hAlkf4yd5MTk","1sYGJpXQB4VK":"6Q1KV9ixmQmn","5qtFCbufQPiV":"UhUEtwzKgQDs","roeqd18FVJ7W":"LRgyL4xVqvtw","JxZG3oGzVi8g":"MQEjqck9iqoM","XFL6hzpkLWJQ":"8xvBfOREOIUM","Mvs7k4RVBQQH":"b5KOBMXSuCu7","eYMSfL19a9M5":"cE3MKePwf1sZ","KlBbngHFo4hB":"F6KrKs31I1y0","MqBpXF08tPuw":"VjnD67u14qDA","cmaxm1GqFOp1":"NoC7dxmdll1T","C6y4NFQOVQsK":"Npvf4pyOLr6K","b5tLwRG8ypuA":"mURBvWY8GTx5","d33NVfYajv7B":"QK8WpSFIZ91e","H5ZkA4JdAW5S":"KyurVlJkpUHc","oTBtTzO6Xdt2":"ieR6FZClf6iH","CnnbgnBTW84H":"BXq4pKrLt8g1","xRs3ewrzb7aX":"34yV2u1ZdQtm","dszpYzyUeL2N":"hyptoF2iT3eD","6tKFGXaUxUMS":"DYPHwfQLEiBU","8NEjHhtHtti9":"Ev36fSHN13Zz","tN6g7eaGR745":"GacERo0qgG3M","b1hHTKQ4XeYU":"lysWo6LQhH4g","0zAr6Ww806W1":"W6ofgUiyPE12","ZE7icpHW2zGX":"7CsScHkINaWO","C8XIsrrBb4wY":"w5m2RZyQ5kEp","G0OPwxgNpwdK":"5D7Az5d1vxdu","iflEluhkbvMn":"p6RLwJnQTgLU","rKd7q0z5dmNH":"li8FkbYMZ8qO","JB2EUBipxCui":"GJ1Kge2td4Mx","Gm36fNSgaSFn":"53Ht99AouZ4Z","VnfoBXnPQKKc":"WDJF4kcvhyUY","u6478O5Fwuxc":"Chpp7EoGH8gH","rk5SKxgzsUi5":"GNgT0OYN04YE","OVQEsPpplW6g":"6yx4PX1pP76I","lqVRhCkLvEpE":"4JfKYceLuvNJ","gEGTmAHe9w22":"oQrYvxTFQsfW","LjKGCYg36mRm":"itaAPN2u1yM0","Vb7MUsfKhro3":"S3xIjdc8WzJm","4toITw0iGpHm":"JR33TkCXVap5","75eIwtOdrcTC":"q0XcFqfcECkR","VDWahdzyYvrX":"5R7trZMVw4Og","XQdXeNgwcV0I":"KbK4DlBIzgOW","46h3sbU7cxVB":"uZn6T6ifqqk5","L8AR7A6CdMIM":"1XMBEq0ktaU4","7Shlm7ff0s4S":"JZPPho7FdRLt","zsUQvDIzupPx":"VzkkjgxvLBoP","kPW8viyyojE0":"Ph6hjWLg7TQF","yLB0vZSRNlmp":"rwNeXIBOWyMn","LCFv1cgmXD37":"9P9TMrJoKTtE","vFt4gJrDFrbE":"nNzFMr7YJoBo","1iJnOjpK5X3X":"NPIOoUAcoaSp","wZ5gTpOxHSEc":"Dzn4Pev6OhJ5","YcjsSIum8RLn":"sOsyseOVZKvy","vBUaPDyju9RN":"oCEl0fWGcypB","IYn23tKvtPMv":"NMDYfoVb84hN","ofmXdAyplp5t":"DopFc4zWDagP","r2iiQWbq8p1H":"sDQbAm4HItDj","wx1snbxUugAK":"6a6I8QnoSJEj","fp4lGkkDAqcG":"jzhpBjYYt1kf","TcTTJqlLegRl":"tVjEHPOOde7l","7vacw5Uul2Ns":"olIbq68I6hUg","w5qOGroPQODP":"7vBomPAFRADj","s4259Ygm3uSX":"4Q0y8DqiRFys","KbOlC69J3LLV":"PfqwsTKhxSpT","J9UYnLcE9ngP":"QzMNmC7ahcI0","AIuDrJCt1JAH":"X4QpwSUmQs2v","HTP2fRq00oYr":"PjFAJdI4i49M","h9lPqYSbubKv":"35XEUKMxMfkP","nTpqXtkJaBy4":"4x89DNEVqStn","S5zrCkAvOpSf":"Nnj2QcRVZckS","8mg7Cn2W5fEZ":"ZK00FYHCyiWs","wgIlybnF7o9f":"iGIa6sPGSVFU","zVW9tlpArQW0":"viwVRQg7hYC4","QyOLswptiLJG":"PM8aG9pdxIuR","E2CSdO2PVBLn":"OJjH1BA3ORAH","7sN2MpGGXfRi":"wSpL0EqcaPUY","kVCm184funTc":"KX1yyZy9gVVM","EdZTDdZa6119":"LBni2Xj5UIcb","QGDtZE3Q6vWe":"MHWOMuVHzVrT","pmnLONI0Yez0":"Dvv7mTWzHzb7","lOCyEyLmGWcy":"bQd9RslGwSyI","6uxA2o1GV9Yu":"74XlsIDiAIKD","yr7mPGXwLtvg":"EvWu3hR53WtS","RIiFDU9l0mRW":"KHCBLamX1I73","8GNMsspmWMPU":"rgugUf09E12H","EpTJAr9B7Jkz":"qHMu551j7ZbU","dLPCvCzR29eJ":"t50KgTfnYQps","HdCu7vUzhD7B":"oehIc9EqijgF","XDQtrnT5Ub73":"vg5aHabH9IVR","BnWZvNUVkCaW":"3ltrgRmEwQFP","ViLBKEB5yhfV":"bX1DtRbWlNl9","wBBb3vMec261":"ECFWJgbZT9O9","4XnvZFfoxj2K":"iG341TJGNehG","lVXKpnqYemWc":"cgOvGpkyMZYr","zzaSqWbJGvnF":"JMOxCw0oMZMQ","mgQXwbbjvFK8":"wDAzCwiLIJVD","FkGAihMvnqER":"TbVy8mJPQl4s","OeaUzcF1P1Tp":"Aaq3VhV2HxkU","rCAksJ75MIE8":"U32uyEgw2iAA","g68qb2Jcq9IB":"KXFfY63bR8il","JvIiWG8xqDXx":"JkJiE9ybmLxB","MewkdQhj6dvI":"7E2j5h4qG0V9","zWbuvnNehGWQ":"aaXDEj8tT6op","A2yD7JpHWdsX":"3kvAYiz87Nkm","IJXmf6xbDZuO":"SpOUoyk4Mi4F","AOury6mWfF7T":"7DZj9GkHlmxj","ysnxqSp8C5GR":"u6FWhNTVMK8l","zPavverUvEYf":"yZpnzVI0IoMA","vzpxswPKDzTP":"xKVAHJSeL254","oLl2dnNBKaqr":"33Dgy9y5UpIU","khxmQ0z4ULfg":"sp8VuOPoO0QH","ZnSpnf6CmuFX":"iiq34awkWa5F","rxsuDvjYLDSv":"dOqyot0wejAX","rBcwKvggADW7":"kbTCZJnzngTo","XS2P7as8cJ2N":"oWDtLiGmYuBz","23f2KKFHQtZK":"snaBSYdsnye0","WouJPBflgFmg":"gVThByMRdAQu","2wAcr8YSHXDj":"hBYDiQuVURhh","lhnvMaFZZvgh":"tTqLkhcSm1cV","A8y1SuermdP0":"djOpsG6iBzjI","yzNyUC1Q0ODP":"CrDONjMFWrHd","Z7xMMh7KfJHE":"Zw0Dub3aEQch","nzsTxXarvdU4":"PdTIDxAnuGCi","y9IGHUEUFOSp":"wWzWCaDzxFSl","gC1Bsj25EwGL":"qfmWqZrFdFZx","6b5X08rCMSQB":"ZmLyHtlrKGCE","S1ADmj0tukCs":"7V7AnnmZ9kRY","1rNiyxOrTW3F":"32KcIKf0A7pE","bPixURiJn626":"a33d2rbfxPBI","I0sWAaZnomjh":"AZdtHAkLBME4","IAQ86zOcYBul":"Z5tsstHbRUXu","ZK0mLlAC2Hfd":"90Vx73GMUP8E","1sgAtCLsCkjN":"4g23sv6K56Q9","TNB0EjXOLZvD":"KkUeVObN3EiZ","KGVZVsEuyiKc":"y8hiz0j4Jjpo","9HHRY89B0DHA":"cgE0X77V0sIz","uXFkDfVCthLe":"LfIH0DuUsyFc","wJugeVKnJ68x":"f6prhqnNNrrD","XHLsx43HE6OI":"uZNopnR3brHr","Kyu1BUqEUSuS":"dYcrhUrxVavv","H2sKwoB2EeyI":"1TUWTJl5PIo4","okvq2bhLmToh":"VQR2SZMu6IdZ","zFEoGqpfndOm":"yLxZbL4qi2mI","6oX6kcNuqSWY":"CV5OswZT7rlq","RnSl5Xq0oqw5":"UihbOqhCXyeI","b4RKXiwG17ig":"b8cbbLKwi0fY","BAcycMXpT4mV":"ouJ5SoQw9dv0","0M3t2U98fqJh":"N7i8osITqab3","EkFzcqG4xecu":"RT6nEOZh2Xhq","ua997AxJOB0V":"Szw6srICJU8i","GHbRUzaou6Dc":"VQKGOCPe3DRt","UlbsVYmLc0fu":"mKw7x9JRqdL7","0ZEyyZNpZACL":"QnJ19e4QR7RZ","AAAhwUq8sKgW":"i6OjpwRSnYLr","GDiDpS3J2QVN":"dSRy0bQAbQPK","473reOx9awcS":"vxEkdl43D0Hz","dLrZCEa1wLzP":"vxTPrz3utWDH","MpqQGC19vzlx":"UjFRo07eUc1k","kYhNvHMxw2eA":"a9IUKzWMTRqS","1wgo2hao0g9R":"B1HVYoedMkUq","esQ4JU5SSHw0":"T7smc1fAWWjc","cncegOTRo4zJ":"6OwshJosHpiy","HyrlpemdUu7r":"udMWuoNUkGgl","V6YX0RbNso5O":"vfEIb4EmWByd","FAYC5ZOjzpQt":"AqlhFZDIUUVI","a0SOTcsQWhRd":"YRCipLVYBhoF","JSNnM5jLzO1v":"7bJvWk5XA1lq","VTCnDHCRYhPb":"UhLSRXZwAQnI","I8de65IXv3ZU":"Ii47RsvrQiyz","rwheJmb2xpUS":"IqWIQpKGLVJe","zMDMm82pfEDA":"4bTuxu9MOibT","Q0PyZWvrjqGh":"1PuRzJ47gJ0G","hcySkBxqb03g":"nn15vQ7fV6Sl","YcGpwtnMihgU":"mY93ylOatGIc","AimqA67nmDSl":"RdwgyoC7u5tM","eFzIJ5UM1RSW":"48ZqtqFXgNZI","VNZqYkI2dNH1":"mUbgSsrFH9c9","78iHbSRzjjeI":"b02rd0EX53i2","nRMKOutEdEna":"GzBViO2Tz20M","fqO8gjiVxMKO":"Slc9VvbuO6Mn","YFCNOGOc0sZn":"fDSsTnkDqsZc","eRV1nkP8MOV5":"EqHfWCRmCBtu","cKSMYvrajPJ7":"j6lfCV7jICY0","2CTxnbQ8TMM7":"kbb9nR1HcuDc","zgtpxbekrgWn":"fwsAKRHiUe92","HTj4V9blqxP3":"Lr0p6xRyT9SR","5aB8NIc9hpX9":"jhrT0i0XStd1","Ler2cLvxBGgv":"Y1Ix3GtvfvOu","RNjqjCsYBBcn":"8FqK3ogcx6TC","hiW1FzPpB4pk":"2nBFkwbia87K","oqsD3FU404ya":"IZqz8ZpcEWwM","PZazkTnCoDxa":"YN3dLNhkaVLP","YFKjAvd3VeRN":"zFitWFLLF8ug","QEYCNDJTc3BH":"Gt4EY11XhU7P","WdQyv0hCuf0X":"4kTeuSiymmLM","nIW5DFx27iyk":"PGSstNshqzbw","Bs88qnJ03Mo5":"yvG27vija76M","VKcSUDB5fGWX":"YAbXMgumqwAY","uP1OBWKVh8Nv":"zkyotcTk3U7p","UoaZyY7jnUHo":"y7wzsuOQFKB0","HHAAFEWUUw85":"Si5kE7CIcvug","Er0lEzN89rfw":"fO5ZDRs9xx72","43X3W92SanX6":"VPVtgEkd1wqi","PG3tVKsnJGKd":"l2icdihvzqur","M1ruEwpswbfI":"RMkmE7oipVwO","4vQWKJ1YDBaa":"045ewsrBhy3M","cmtJsUD0h1cM":"1soikCUTYV0y","VYOmScni9bqM":"GZuRXRupnyfU","7yHlthFs5k9y":"YCvUAy34EZhG","ceaZGDwRGgYF":"8endqinHPgxz","QMopH6VFN4A8":"bzH6xwxmeN1v","zlrdEkX1lCtu":"G5wQhK0pMpjd","EVgkseUb2mwy":"ldXtbqQyu0zc","LKyDl0pQUZtg":"smznIur69PrX","UZPFhrTvkfro":"wgpgmggKooF2","ofGoxkhnn1Lr":"kJCNJvK0OMOs","oPivIEgZQJVo":"UGpxHv4yihYL","Di5WnDR97tbG":"rP3p9pXwz0H3","6Eiovu6xuSkr":"xtdBRaOptevJ","KudJ4milbsFy":"PPaTgGhl4xR2","P9oujQwiYDzQ":"EdJahC6h5BQj","N7OZnh7pIpri":"nvwMTmtMSsEP","5EhGwtdySznt":"PO6Tg58jmtZZ","arMRMbzyN51V":"oDCSPXVF3Ekx","MHCm4wyghuap":"YbLipqdpU1V7","REK9Pzz6bdWu":"wWkyijj7crUu","NPyNFJIpiLY4":"d354srgMXvlg","LMBqXx2NJV0r":"iKmf5EJL5vox","mn77nY4SZzOV":"oAjxuLQRe2Kb","o7S0V0qim5qN":"XoJNEjOGrDrO","0xIKzN9AxzqG":"cITj4vcmZzt8","s18fN6zQdvV7":"y3Z1oXihYa5o","SpdGTp055g0y":"aqBKmiQkjM9E","9rsj5Uq2j4Rr":"S30uiY2VWJ8L","FaVbPmG04EFX":"RBlBMds4wjBY","ywdUWi6QQO3u":"raWM8McSREFn","HwXTdjLoUN6R":"ocVjGGpHPaHv","eUmNsUmEBW8T":"TQHr0vlYclpH","Dc8IgePB1TbV":"WI2vwnY9SiRo","QaZsAOwECcPj":"GUlIGD5sgQjP","PNpwNuDfjeTA":"3vx9Hr7BNydg","6l9qpjpI698a":"MJIDkNQVrcyp","pzQRXgR8MKX7":"uMmcPlEd7FYP","EE1oblC5G92U":"p007LgQCCsqt","cJL0bmbG621Y":"na8imwhTZ3ao","aiX2laLiUO3F":"qJvbJMKKaN5a","jTc8DWUMfq5s":"1K9SxsVyBuGc","tLikozsb40OZ":"rT5vy77TjHqi","r63AZRR43rrQ":"FgwshjYFRf8S","yYsH3Zsq7bfS":"VTA8uVvdvns6","v9oxuw5XMSlB":"nGrVvtI7r7Xx","EPlwwtEp7UBm":"LviUCVeYK55n","troG4YSfAFal":"5dzZhhoPUY8D","AcUgmQK0kmiE":"mFdvczf7KRR6","bO9K0EYaKc96":"nOIGdrsVYeTr","KawCzp7W6gBU":"Coqh71N8kY7R","QtzMHbjwPcSC":"abubHxafTbDL","zilJ2nabF3tp":"VcSkiLymHOn2","frzkhPpWDhYq":"2eyDWmmQfYYN","BwIVES0GosIu":"mEANixfqnEFq","2wVXyoatrXqv":"SEx27ot7sFc6","KqhaHTUUJG4H":"OYEwKOPiNmIR","yHmNS4Sp2ZyC":"xQ3TvXUlIu46","fedi0O90dlCp":"LF7EO6opTJb4","GMQfTETmtFZN":"6NNrQnOIhSxA","XQKkw7t1UQlU":"WqOMfmLBzJbJ","pamkYjQxagmr":"378g4ik7FNRa","wiQgfCshqoUd":"i7l0sbOIezoM","Da1c1hXSmV9v":"0CR8iya8Ux5N","dBwRDa1jvFyL":"ydP2PvpBpQIM","R2i2rQdA8Tsn":"v79uZXsrQvTm","5gcVXkELdgKI":"nOdvLqYveC3X","GhASCavMQWIY":"cmK1CL2w5lxJ","GDxiOMe4cIg1":"YwuwNh58M8Jl","IS6ImIP6lYX1":"3eEKxq9FmC8q","p9UOC1jqFC7B":"vCr7WYBKYGqO","cCVK7SsJC0MJ":"ZOoFfiXzA5Gw","ZL3nJq2nqweO":"Co9cm3FtSrXy","1g2U8TgqcIo9":"vOtyr3W7O9RS","SKjZZgwwhN1c":"sHaftPxusb4S","BjBJ3FTCu4vd":"jKtxvQ30JHyE","8OJ1mFQXktE4":"RjuHAadEGyJi","aE0R4015ujdT":"OHlq3b8c2Bna","qKFJsmXAi5G9":"oZyag0EsxDax","yzaTXrbPZDyL":"eLMdrqkht8zU","1Ch0H9Wv60I7":"SXm3Si09GtnI","kItaiEZDulSy":"HgElTbiZLEdh","Mdg5uprrlZhU":"teXnyL7D7yrw","1fu70u2v5daJ":"oQPkEvIdRbwZ","IuHxfzFaGGp5":"leRWPVoOrnAi","4bYSFhrw6qww":"2NJvNtDyxRij","m6aeWycnhsL8":"tXI8tiANyiix","utVjvi6WRMtf":"J971Cbx2sqrg","7EEuptjHEyf4":"TxWYxEAp35gt","KUnebPULUZNh":"5mjMXKzUHqW0","ARoNn3YuAfWZ":"4LPkTpR7Hlz9","woToUhoNpnE6":"LzUWwSNeYf8N","4d9tjyJv5kmf":"lYmZYlQQsWQ6","h6dLQNJI3l40":"WHyUL8mLLd7v","LPFYYDZDSxBb":"8So0xKKos8Xc","StvMEvV1aW8V":"8JYCUDq4LX1U","2WwFOyYJahq8":"3hDs7c98ljXY","EJcGSWd66hsT":"GMRpgAVPy9v1","YxrkXPveWs0n":"17kGXNFlDs55","kAkbj785eY86":"U6ERAxquF87m","FFp65N8WgziC":"43NZhVNC8zl9","6krxVNeqXePQ":"jx435hI2Dv2u","o9wEn8XCM3Kx":"kjAoqTrtfxbU","9ZY65P19ujKt":"3scAU4iG9x2S","qqLJCF49bDpu":"fPirBOJU9zp4","2SjOWs8ymwQE":"vYF2dHOTKqAM","SfqmZzU4P7ET":"tOgSaGfMoSC1","BoTp7WGPwthi":"Aijpxv5RzhUA","ALDTT9KuiRfk":"KjuAPsF3tl4d","OIJI8JtgTEUm":"46o8CjSRjbGb","JHwCv8rsjxaq":"sWmWAQh0bDt5","k84TNvYw2FSX":"cv3E1xB9DZ6g","GVwmgx2ovlX1":"cEjMFVtXNXAw","fZgYSnayE8Vq":"DQEUKfo3YELO","o7aCXOfgQMHQ":"Li6XQppUx1bl","56iqe08ju0QE":"8D1kOD6gMklC","WHt8P6ACdU5g":"GmlBYNCNtAxB","W1vdoxHU6DEz":"Jqs67KIBrkWM","OJoCFyZcLBnr":"gmRGZPrmyMVy","xp1afV4lvaq6":"1gi0lSYa6vB0","XZ9o9KbfZO0q":"IvslTKi9e17Q","RWYHOZLqaSek":"9WNQLcu1o5S9","avfvVPaZIwaK":"YIhhLaXoQ0uM","pa8wY4BOkQcy":"M308lWdJCQqw","9tlF8mPwAyvy":"IX0VrmZjCRNi","zl7h7CAtqKxr":"o3ewjjrwp9J0","CZ3XFyD9Ochi":"SH26jbN7lM66","eklG5fHuW44N":"T5du6xWQUXsa","4DudHUeqo0pF":"1pyp5Yb4zpKn","G62XxafzPUuU":"NHmCHcAhUzyk","qEYAI75SzvEQ":"sbprLmdNM2hf","3sh63yziDYX6":"HH9MDqlYAama","nRqcvWNiDizG":"upbzPBFVe3gn","Bp8iKGFvFzj4":"S9sqltWD36YF","XLjpFsgu0oqR":"g0gUuB82XfN3","JyEA6a2JTvWd":"TOXzAaefNcId","iQiC2cFhboym":"ZvuTVAtYiotl","LWl3l3shZVDg":"ysHavTjF370U","GVs5JktEcvfP":"NxhTXOqQ5fIL","0dsb7E2qzEd9":"WxlNEfI3HQWC","bQtTuUm8UX4Z":"FMcoyAI31hWb","GAXYNPHAZhXX":"VVBL5t2sarLm","U2pG2FqprRpu":"OTSLJFxkFzqi","bGDIilosH1V2":"mcOvsS7WQeVI","ge2fkKDfLh4N":"owU9vF4ZHSXh","quqldSUqMcVw":"Yy9iwhVSgkAj","f4BHRL1xsPSj":"Wdggpzu9hK4m","gZGClLv9zZHU":"MY4xSMkhKCCE","5dM9BbNmgyud":"IrtM4BYRVPqb","8xCoVUolGsRj":"enObeOwFZp4W","RQwLvldewQ7R":"NLmES455tnHm","qvHs6WF7F18m":"6xWin2exjWTn","wfWE3L6yNj8t":"h4Ln4ghJjGJf","1RrAddDEtY24":"4RfLmdaHiWGl","MSjjeVlxqNJw":"J4V1DAcOvHfT","bgT4CQ3u8UFj":"NxnqQhIRA3I2","HC4JKs0iTem1":"7ooPBCJLvMRf","vCHFoRtNcqrk":"L5iIeco3Mtuy","YdgjMCTFVjFx":"84YdISVzeJDD","Znn1t6RnjCFP":"asCJ6w1HY7q7","1eXk9S4W3cRz":"spNdXF8iKCoR","AuGjDK0LaSP9":"ynRYT5AFovHj","wBJLhasZ5qwG":"DYwL8BnJhwro","nSRstxFjEReC":"Rbc8CPxCbENb","YQ0cXRlxK9i4":"pZ0cfy9Ufppz","0CDCs9yqTfGM":"qsppoO3XUhnI","plkZr87XtHDU":"KEpFkFHRAkrM","kc2PdZfMPRWP":"8kiRM7Ui6BBV","2BjXRVZjYBk1":"4GhFNjN8ac3Z","owiitPQiWVzu":"0yAlpnlI1RjC","bxdsyXjLKxqN":"g22LgGk5P5mM","iyhvPlSWfuSg":"2eFzUIlLYx0i","krg0CLiBKRmt":"TBCpwhI1910N","kHJQAyc3nHs2":"olSZZ4QDUJZ7","UsbtGiyMPS89":"jEniJ7nutT4a","VJFpMshZWCiC":"laEH3ldeRmSd","zFAEBBcAbbDZ":"86LM0AjkMAhN","fpgWAPHq7beI":"4x1lhgsBIiZE","U9SKqDjbTEyD":"KM2DQOrFQolv","diynKWBEVAsU":"fmFtM6ghO6QU","xPDmEzUWmwy1":"cWOdOAWGSffY","zlP7X2YwwgsB":"GBhChxJbnkud","TaIm6MOvnaak":"mwHn2MmF3BJd","FVZe4s8Mdo8y":"SYd9Ks5fIIlB","8zNR4xTH2jmZ":"YVt3Oe8uGVQP","JFYe1StsSyVM":"7I6z3ASuBeoP","nKZjOmwiYQ4a":"G9kRP60Wouhw","iFXKOhIaAhGR":"0vugrx4G5QdZ","1kkvRhd2WzRK":"0j6Kiw81qWgw","vbjq6LNF2eRT":"q9fjvmlEdv9z","SLiYAbjvmNcf":"LRLQbti4972o","3iJo220bMtdh":"YH6LJnQWnABh","gfkbZEDwH4RD":"BrUVYaEDBuAg","xIpy81EokVBN":"ntsR3czYwzPa","5ciJV5CYQpKt":"MwP5NOdm3A4u","iP6Ea19eahcg":"wzamiyOY0Y68","keuruh1Q65jw":"PmhLDKvsRLzY","G0z0GqkyTukc":"nqTDRZHHxmCe","VDFnBCBfTOvv":"49kXLR9sUDI5","20Xio50QV6F0":"X5lhNkW22MGa","yxNmHh3UH6Ao":"pfmjBQYwBh57","UtxSHzvL4Zpo":"Hd0LL83xssoi","y33sI3FgrQDe":"xBGwQHWresR6","XgqoXSTZ5WNK":"dEoa4r5c6OvB","NnCwTqjVbqqG":"cFjtayC6CS62","HxqQjJwIBEnO":"7vVPJYR0TWQn","VucfVhjqKQXS":"UrD9eFDGrYxm","io7U2iBrUciB":"CANIoHJ3FrYV","6WX4R1QAm01W":"PQRMZR0lwFRQ","QAhKeWZQrdl3":"JZ5kg57hTONH","KB13pTAISEtN":"0ehM9SyWif9j","HzWcPiAUOdbD":"Mw0zh1gj3VCr","7U8X2YNVuedP":"35QMyxUqLSqQ","yitw1PAhnETV":"LjaA4MPJwoRc","eWFcOpPA1U6n":"bOomHD7vjy9I","wZFoh0ohXEzQ":"tEiioWUDFkZP","hmZrL04mJG6Y":"Ac9jeuzWkHfQ","Feqc2t0NkGpV":"JrOrwmKRlvRd","t1G2zz495XmN":"o3iBUgCJ34Np","HyEcAkA2ZPC0":"LLacoZA94G1S","qyeO647eRx3p":"8XvYB8ICJtyS","YSulLhbP0QnD":"mufVkxwBT354","E4ml1ZM1WIbW":"EtddFX3pqBBL","SNEVLxJJ613P":"HZrbveoEE7aU","psLX6t5kqEd2":"Ruz18pphxajO","aqd1Ox4d4U5q":"4Xcxc5gey4xH","pMk4Y3Vpu85G":"wT46wroORjqq","bu52YZf7f9tq":"jrTefvSA8Tu2","NYTkypisziZD":"s2KIr3QNCNBq","brcHU5n6jtmb":"bjs21i7t73uV","jKo1HVkCpuhi":"i6XxyXEOVPMH","UXMntQrRNo2G":"nbQxzHrLbBCD","MyNTx7BWFTDl":"8FIKsLXEpOon","uRBH6oe9RewQ":"M1ViQcOMv3zy","3H33NyS3O7k5":"Ve0cwbPFojRh","XuL2zRIEIMYd":"0aahXbNjAs59","ONf8TfqfJETQ":"t8TifSVXRxEx","GFNzv2yiz0gM":"f4C6bh5LaBQX","lJo7eh0JU4y9":"vk7TPnq57xcT","DT0fy7sxkjsi":"voEwZHXUJMM2","6ycmoipipbDo":"dDOCB0VVtUAJ","vRMO8tvz3OD4":"IeOUPEqHMjlr","pSD0n2fJxxTX":"hIZMBIfsTyyC","YlAxGm1cdubC":"OEHc8NNvSeNY","J8abRadH1F8e":"nuEkBMrDDS0w","iHaaGevzamrK":"QxS8JucApww7","0dbGLCka7CFe":"hDfgVs4MmQPM","5FVXSTl7CDgA":"24kmM7PZ21MR","l9M2hDa7K9sz":"SGQnde4MYYdh","Q5TeBSk9ef8x":"WoEEt2BgM0mz","frgcV0HXgbW6":"xsRcQaD1cS2f","WeiY6d9oxdsw":"fSWEmIbToR31","kB7E60tZ7qAZ":"mPYpNOD1STdV","HsOgK4Zestbp":"vkm0tXiEZArE","FEZkSXH8sd39":"XY2E4Cj51vB7","9BQNiVuLBHGS":"s6pcS6BKc2KD","EH8MkYTJF7H4":"wHj9mVyZLBqc","zIi80uEE7tgX":"KLaO2fks2SGG","VZzVmYIJj5RM":"EJJz0QQI9Hf0","RsyWAINDfGZN":"VtFI07KaMKzD","MZiaVssE0tpT":"zTLFpOmQFMkd","8fmlfILcN10Q":"VGfRnOQP8eL2","QY1iRBhErK6s":"45qiXXgFmpRU","mT5C9qcfSR5X":"074XJeSyvxKy","SYdAkx0u3hys":"xVJc083m6fwE","PCWAlUGHvUVH":"oFIrzOMGqt5H","mIunOhOUwbEk":"YAo1wvxlmzGn","ulrOycTmKgxv":"GnMp3VJr3J5B","jawnH0kbyCNR":"ppJbEJCGocdy","3GQiR5WjixGs":"fY711iruKmkL","hVelIJKfrFPJ":"SmKCF77ksoZq","Fo5TtXbLLnMh":"3pFuYw3YVnPO","Q7p5uikh0pnX":"CPsTaeNK5B79","qBAw7Ml28FTm":"kkQHVSa8o75s","ndIGqIIg3Dj1":"D3qxj2QyhoLO","aeTcRZWQQJMa":"eiNjyU8pXvlL","qoEohV1aWcae":"O2y3wzRcZRDt","KFxzqVJ1JNiC":"vuVzYcKLqBBI","RKGS8hkEJqox":"osxdBqFW1WSZ","QPZ0vrCARIYn":"KsdnTL7kHgtD","0EBmMF6MEjwc":"xSCoqJgHSfrq","cnFQNRmRHuYs":"tfc9xAmGYksC","btbP9dlAs1U6":"QY40kTV52nbc","sU9meidEhV2c":"ZOXm8hjo646u","ePBQUsGjZ5De":"KLUStJdUinD2","LqHj9HTJIwii":"dHbyFaEqVRTv","JYOFDUzvWNrJ":"VNGgDDlGNJsd","1ZUUjQVV6iVN":"TSanweNJq3SZ","FXeJjbkpXCMb":"KOsMSXUlwj25","khhWgUtw8iAZ":"r0TVDVCvF7it","qbWh30fPcz8e":"P9UfBOCVXkFN","PctOnicfYYNi":"BweHAhxicPMp","R6jZGQaMS0So":"NFodNchtvawR","asfFEZQuwjPw":"G1s2Y6gfzRDc","fnQQMEeGz8Wx":"LMgJtqHhdGrh","Dz89wv67yWG1":"wuhzhsQkusuc","8KaHe8neVcwA":"ByU4YYaGGnvD","CvjQD0nV5gNv":"Vf7z9x7yGYsl","95nDO3BQTaZL":"pgnC3FjNGt81","yoZ2jxKqmsnd":"pzs5uOzKvqu3","nH4d2zHpW0xB":"4KnkXQXgoUYJ","O3XU3B4UCT7C":"RXn0LyfqJB23","q40im2EunuG0":"oVORxploTIri","ThDUG3KGaink":"NSZlYCr8rSRa","ivVXhApMaESI":"pqNMlZZm96lL","6YWXWenKlmbi":"3Allr34vJZTp","S5xgPtqSc9oF":"pWgbUInmh0fz","4QjHdWf46Wtl":"8DS6gMOd8wpH","Du4rc1tW7fcM":"bnmuhHajuS3r","vVO7pXNn2t3v":"YHzICFmJaCaK","BNZENM4ncfSq":"3F7DnJUdbYYK","8iMLUmHicmDk":"EpvmHK6HnCI6","SRsOZH7aOmUn":"et6iUiumitFL","lYlRFEIj5PdN":"R5l5N9SQLCAx","oz4xQBEmhOCj":"IeV6cPuNVVqN","fjYN6XKFmClM":"yBYhfWChdXTw","O91HX8Lk9qVB":"NuQjDblJ8484","WuQPIvDKvuYe":"kENp2nlku0gh","ShdjBA4zuJjA":"ZLsKdWTIlGEa","3eQAHiupWyFN":"wGvQVP8rmMxv","gGl7KV2wjt1J":"iNJa9bttrSma","7KxVpNxwr2SG":"5VHR9AieCM84","Oa2od53cXWof":"pYmVGsTjLwOF","Ma2cznfX6lfE":"ieptxbw77Zkh","ydGVH3QX8luT":"DyHzqburysqz","FLzHTrutRUB9":"0Con2Y56UXxe","Ip100v1AUL4n":"YBzkvVDkgSHm","eye1oJSSbVba":"OUThTSW6vrVW","qg41dNeG5iVy":"LMOMFOtMJNVQ","ZsLHcCIpqpxW":"kLyDdvNERxZG","weCtrPbghBXP":"f21ti2kmXHnA","R02SJIAAZg0O":"mMZAHGDVn22b","IxhTcHYjb79i":"axAOFXNw3BK7","CVWpOTGnPC6U":"ziwJtuZ5BudY","Y5358DFaUwTp":"VJfTpgkV84cb","mzEJAYfuVXgP":"q3RTlbx6yyKv","s44UjBteVziK":"fpua2W9ow4A8","C24yZy7szbye":"FD4EBvbWoFl4","hvzzyEx9orC2":"RKMCcna5YTSD","bXxOM24860qG":"of7hmx1ns5fC","7P8ZP2gWfOrP":"cxFNZ3SezRYR","TfnP4MinmfQJ":"TYjHqfmlaUGe","t4DN0w4nzSWF":"cUPwNMwLYAKU","iUonQKQYyuD8":"wqdnRJbZyI0P","Cp72VQ21obQL":"EutA7tCq8mo9","pUyv45ZkVSI1":"CjvBvr44FSbp","lpEWUfwAelEI":"9latZ6M7sTLB","6Ji59qKVcTT2":"RtSjnRFLwUHH","18i0j7M3Tr3d":"RPA423ZDvDhu","CsGm534SEXlT":"ZOL0EvYQaIU7","5T7wnuOF5L3Y":"xyHy7s8Kgg4O","fsLlOdZG9yfx":"4Bg6xeJDC6yo","OjYPoUqb4Qzt":"AHc4UvOEpT87","3z0iuthlST6q":"XUOhxij9J2QU","0ogra54VDywN":"0t5RSFhXuTzj","kH92Lw2Ef3i2":"COIvghF9mJPR","OgJdr5GkHUpx":"6Zm4GLZfW2s9","gpvQhNdFgCZt":"N57CFeG3CSKg","1yJMzzvynOWW":"g25kANpsof5k","jmGQYkQOMc5x":"q5VUNcEfZGNL","s7n99lcqcPjq":"D4ntDn2GIR4u","eTGgQotGaK6i":"sEA0AWo1ZKZv","eLElzoOaL4rq":"q5lPoZKjYLLC","rpV4IerqqeNb":"OrSxapMRSxfu","VzPb6qrkpy64":"16ao25ngH8kL","aZ3YtLgV8WjL":"gtYyTk9DKWMj","0U8xYPhfASLc":"qztE7XXN2cJg","InvWmGC1CrmZ":"Sqe1FzPICjGO","CB8iDyksKh9p":"sO4mb7Pj8nyi","f1gLROUydr9v":"iKFwxUW3zenx","o5A0AQUUupPR":"1PRcJ2zCzkV8","Pb5V2HI6t4mm":"dlJhjNHIfQLF","IpySDjIrmFCT":"mqUmaQ14Rjd2","17PHcOzHEQei":"EUPqZZv7w3uL","WhadVpGnCnQi":"bk3ltmSZvgQY","MzFuXvBBmvRg":"x2JUMgJwwsCf","UIXKdPJUIsoo":"JQGjMVlKScn3","kN0laKfRxffe":"csL59Lt6tU0m","wOh1yAbr3IaQ":"FwCSTQlrTdrr","DQ1BtdxGXszY":"o1VGYxdqA5ky","Kz4zTGYJVlAm":"zXbeKBAsRzb1","Y13O1lgyqr1V":"jAdtuxV15WHn","zzQpVj2wdhhf":"AzSRyR95rZ09","0mCYrFV74luz":"bDtYWM7OEkx9","H2e8aOKWnOlN":"2LABhMcp58Rx","6vCfLSHBpjll":"BAJIzLNwk48N","QFZN5N3ApI7G":"pugsbAZWdXgi","GlkcZ4owC6qc":"wa62nnDTSZCG","986Nch7AedGN":"yFBGcQLUL8cv","GCNdd1ir48NK":"iQwiMUlguk3o","zdTRFNWjs08e":"V7IFsOt0yjp5","GxWr1E1K0o9j":"9xoTMsD56QSa","gLG18RuWFv58":"OVjZDzZRdiwT","zGfJavPhHHDF":"tvJHt1yIyobx","iwoWnoUX6ZRm":"fFyCj7fkWROL","HAkHLslKrm1h":"6s3hdw9NK0cZ","9ICSW2Tk590H":"G5GYRVVXxQzi","E7iYSNj6rsSR":"7p12KZsrKA5w","iZDsKYJRfR9K":"N0H25k8qm6FG","ALlcEPk7G2vt":"xfPh3URY5lxy","qMsayuBhkeq7":"oMVxEDOys1HZ","JKMWMTleCacR":"wrIoBeMDc4hz","iohxN4xZl8Id":"3QZIBDPqaFhT","bfAYPDRIRfAF":"5zGjsT3CAAKW","p6k9f9Olse8E":"Q5ktXKAKNczu","C0LPZHPwKuvd":"Sj0vAt5h014v","zHkbckoQPTL4":"G0d2wszMatLo","CILDedC659eJ":"g5fx6EiHerxF","T3VbYKOI4b4T":"YDmb0myEtLBZ","omAdH3G2v7Ht":"TsQnYKQviBwS","h4W6jn13ZHYG":"QMpNYSWwBmG3","vWFTV8bFIDER":"VRIz8WTsHAdZ","KKwJwMfTFfzT":"nHBTE8w2G9Q0","fBKAqPPBcry4":"h1pgegjmKkoF","VONy2Fn6S9Cc":"u5gR089xQcMB","2g03LX4naaNV":"LuDz7FsIiiXU","xe25673F5WEn":"kwSJf1oj9pBy","9AsO0Bixwcvm":"fJHBEawfrTzJ","LjxCSIg5fz1l":"5E9Y3ytCsV39","G2tnDwv3et1m":"lbCiRKrgWgpr","f54kP6zJuEug":"npHj7Eb2p3z7","BL7KJOsaO7dA":"1XuQAzerKhM9","YUg6sEHctTi7":"pQK7nDh2kD8J","UoozZZtsn7oj":"kkMpZbuivkHN","22T5GLXgyG5q":"LehFxK9K6GtQ","B0aElEeVsNgi":"Lernb2F2Mm6a","wuFhR014vqaz":"QmaI3dT0Y3Qn","7VRAF5BWwP7N":"95vWSmq00b2A","E4MFQG7sd3C9":"XWWlTV02zO1c","n4Ior8IcYPoL":"j2BXqVMRl9Re","B1oeFf6ixJVl":"hvJafNpgHq3q","abhv1cbFJ84r":"3pdoxDAA20Xs","pPHKieUb0psS":"l8htqzGvhKnG","a4GdYgrZKLRi":"N78ZgE9BUfTD","GhduQLYrnkvL":"yJ0Q0aunG21U","qlM6jhDrymxP":"WYVzDDvz1N5j","lgW5ImPykjbR":"pgC7hPLYKV6Q","P2NaCnMqdiQU":"Zoo7sZw3otTm","bNH6TZveuNL1":"70LkzYpqKm6R","DZvMnNCNF2YP":"74jka7bIkmzH","TJtHXnhzcZ1V":"SWtNsyfz83NB","qKs31NnipmMm":"1KYOemPNJWEq","CRDkjWOsDJ3b":"J6yrnJscqluz","UXI0znsfxTTF":"Y1y7mvUZqgWP","IT3y3VL1xjAn":"BehNatfwkXrk","gv6TkGEJwwGK":"BouOny1eMmoj","ECko00WL3ln3":"r8gJ0tvhUq94","Q5XdRnbtSgk1":"dazQpJ7h5JhN","fU0SmGRVaisZ":"5kImremfdIXS","FHpUyUpUiZQJ":"T1R8IJJF8VHu","lwcCBpVbBuf3":"m2s97zafj8GX","dfZfXEHJVlmr":"itUZ14vMChY0","WJb5qdi1sSmR":"BaOnVuNdvTcr","5OCNYeVo7dRb":"8JrBI6AXdvL5","yVQKV702QFzW":"LpjaSVlehipI","zwXEC4GvF2TY":"PeDYq6xkyFpZ","hO8S3qI5h0vz":"7En0dxSqYhHk","tjl6a5dIYIEe":"sI85jKfDpDSl","gSQiKYG5NfvP":"qjTY8CgvUBTP","fJCpojR3GKkl":"gB5vg9vxezd9","YtrfISsqABce":"WDRysZVDqFyz","PZR66iQoV0Ds":"NfO1UL5HMyEV","64yRcakuYNSN":"ICmBYOfA1KXl","PhaQkA2OQ5Z0":"VdMk7tkQgtEB","qgsomeF4z9Bs":"ATYwF65A3KYr","OEzmdo3RFySq":"fd6YLiBIcBHq","PQjlxh4MQO7c":"1Kt1KcRyqw9D","ywgeu6kBjXc4":"tRdWqk3Fq4i1","xDPWy2hZ0WZm":"vlykTjOwr63f","gASaON9TOR4c":"H0pbYtnPe9sf","PCQxl16PFCjq":"XY91nSJXpg5U","CNPq57GunBay":"60K2q15FkhlQ","JOukxawxRBGo":"y8LbWo62HZZP","hi9QrQkRlir1":"yhId8I6qSL7d","SBu5YWM5KPQq":"yOpjPiHmfwlL","DZD1Yzksxjbn":"PNbm6RvmHzZg","jVb5yvluOwk6":"J0eba8sqUtg5","Qeq0y4auzjzP":"uaVvWwToVnow","jwjnvf5VlCtj":"lfk6kmGdE0Up","IbSKZ7Vqr696":"LjDr0dtQCJsv","gv2mg49f2thc":"JSzVCMbaXzlK","PDShsYHVGduc":"sP1gbvTwSdjK","qfDCtMljTzbl":"jC241G7y2pte","APzVjjlv35gJ":"39xMbIx5Mn0z","yKmULvrqnkiG":"AsXUCrgseAW4","gCbb56bJmuJ0":"M6DYnaKERHAK","EL38zniqvdsy":"WseafIjdCO8e","nhLq72E543EQ":"Lc73gMKNrbmM","cJMEkoxM5BHe":"wEPLc4l15yyi","YhCori2QxeLt":"htiHnAqKKDRY","2RYHLA89xmjL":"EEWO0fUTevSv","Sr0oMLMfyNZd":"fGTkwFYEg8Ml","KjBRUFrpyOmp":"kUsQntODxSbA","IgNEgZbu52rV":"bvOyvdYa5GnM","DcFcDfeKH9Bj":"l005lKylKoD8","bPdKzA3onwWy":"7EvXFsvxKphw","Bs8e7DvP8afj":"MsCxmNog4U2x","GtovxhuxcX5s":"FK9Qq5LGYB9E","OhP0izPrGRkx":"VkSmlVSkbBa9","oZLUrYTdI4ul":"YCkUM23pGRVw","TdhIaW7hfErB":"RjrabrCp1Fap","Fx1AIXd7a7sT":"fiUTiYuEj7d8","u5sF6kc0DPIj":"NMTABovp8ZL0","0r8SZC1ORcbu":"KLRqphItxk7V","lCGpFOUoccxf":"qPvzqhByoZVn","gAu5z9gOzlen":"nkurXv6txjM9","aQML7qi6qOLb":"hPqIxyBd11vD","vWfhMq8sn5d6":"yOvpJ4lUsULa","UgNX0hjGBOjX":"vkMJHnvIpPfJ","xd1bEA9kdG0z":"0jHvkzne9zuO","MKITLToLF7vl":"41UEfHPVZQkE","VbPgsf8pZhqp":"WJV3PEsvI8Y1","Beb1XdbErJnB":"XGCBjVIgcEm9","ykVOg5iYAxb8":"vR20rQB6Jeo7","YjF2FQeJ9ypJ":"cqxxJtDaiGx3","C7V5Nq8qibWK":"i37GFQsOA9K7","e5RtFiDWp3AB":"CpOXbUJsSUj5","Jk1ERaUFkmPC":"slKYRf4nve9d","CLdA3yU7BGnS":"QXytSeswzkAK","FHmARyJKdSqK":"Aaw2dyNXmkcJ","1syYqqZncRc8":"EeNPUln9HGWz","UWm01VaPaDNE":"9XK3PE3ICZbb","kWXwvTEIVWhs":"3zJslQY831A4","Gs2xSRD1GuU1":"Wa7sYuN2v6qr","8sL4OjfezRM0":"KY3rbdtMGzxN","ptmzmw4IzbwC":"zJdlbLEHMiq1","MGZCzSiXRdrB":"pZwluGB1OvTt","TquwSkyG8jfF":"8DVLBT6ewvNl","kR0Aek4Ng7fs":"agnLhoGUAv8Y","VTGzVpeKDoDw":"8GaMKKezlqSi","54XVkYAiv8Zt":"XF0x5goxmQvS","5s1L9bNyCMby":"CKJzm2qAwOCz","Smov8aZftF8C":"QqwPycboe7Mv","AXi3xLuDaj3r":"Hn2MQhlyjunm","QDy4CfvCR4nD":"dHHKBkfqjCvc","Zue6cnEwxzdQ":"XUNXjUsEkh7r","33i9kxMMd9la":"WpnOIRXAO7i6","IgyfUg28nWp4":"L0buQ2RzJSUx","UBbivf8SpU3w":"c7GERIK6NaPh","fDdi3QS5Vdyn":"8bzkiqjCkX0y","MW3ZEKiQyu8z":"cCIpLD8bSQ9L","SMkq57FgNAxw":"H3orXYGeE4dF","8dC6lUc9dXWy":"y6VAumyhHvIQ","5VCrmEj1okKn":"1ZrdGiG5asLt","3So9RT3EECLX":"6K8cnszmhhQK","9mr29XiHoc8g":"V88iluo7HOrm","eXStwo3clsZD":"K9Ji1wmi6Yf5","g4OLKiemhgmV":"yLCHjgXZJlrY","tTaSeAMSFoWi":"0VYrDF8u9Mkw","qWcf9BqFMzzG":"6p0pweUgA7Qx","fcs6gzysAPaM":"jxdaFVbcGnPI","Bz1lAt5FEpJo":"rQxjpFZDBG1z","Q5kC9zaCoK78":"ftUo57IO0f2z","XwzUn37ZY3up":"obeWbQRDelSv","lvuEgYXeoUhk":"5eBu0YzKMKJq","WhGsVpe8qlbO":"kJzA3FmqpPQt","agScjukxCQVM":"agvv00YuOrM3","XoBEn9Qibc5r":"YuADNqDQwN9A","BBD25laY2Txq":"SV11jV0V5uBn","zpELuRwPfdkb":"sbJzoT3SRfEE","JARMNHXKentd":"WwnA9PFtgQZJ","6HTdQkUgMVZd":"YI9HvrwddBtw","PLO7YvJFSPiy":"1bqFiMsV49HV","dxGmvte8OsUL":"R6sfo3cvY826","BnJir0Wvt6Cc":"5AsxzmgCpmzA","MXkBJkxqekqE":"gJJrXQswzAbQ","NVjoUq98GtGe":"cFX1tapvCZcI","1gNE33JY1VAZ":"W17j0eSHcZGk","cfKf5JDqJlYd":"AcdFz2ZlfxeY","sqxmH5Zk21im":"3hedDFxe3LN9","bSr3F78FX7Am":"xsBvDPSzLX8S","pT4CWT2S6eGq":"oBBIitPaTQe6","Rpr7DmWl4WhE":"Mqrd8O1scIlF","kHL1kRLnbxuA":"Ecgb8SJfhkZH","m6MwHggrKbT3":"5PgXO31TJyEp","j3m3dqyZ82aF":"ZhQahvMU0FB7","psmlHqRiD8TG":"dg1F1W7VRWbw","yDLDVKlPCAmY":"Dj3Is0SPXonW","vp4VKuX9Yp2H":"Yn7J9JKOXkca","ZdDuZojDdfAU":"EyBmUWrM4X3F","WhSVs5R4Nn2t":"Fbi6cjU7nYhv","RG6bjkAx2uP8":"72idHWy7MIIH","kzwBiUl8LuF4":"tY3r2Ie2V7f6","EbeAhQ5BwYDm":"4v8eWSkRuR1z","m1w9mQIl0hEc":"taCrVlcezqZT","zCpbxR0eGQXH":"6QCQIEJ89kGZ","MzvhmjaMWzao":"dZjqtoA0rsqG","mmCTnl7fd3Aq":"RRbbHwmDMKVZ","XgWq2aSVY7OQ":"F5qk49vJ43gP","YQ9vxL5AiflI":"2sMcTzZJBHFu","U8nlFmSWJhbD":"nfs9OUu1aaGG","tN5NnUXsTCoT":"qtEyZ0nLVxk2","tHCoUlO8zW0E":"9KKBgYXeWcw9","RemJaNJ5YJDl":"8gMvgGcow8Qw","L9PhCb8WXPwF":"e66z9UG4Gswl","HfgtpGHxugEN":"sdQnsZLEL8hn","ITcnVfVOrsOL":"fIg3wnhFyOdD","XxTdHNe74irb":"ginuTqUWIQf1","MOsPLo7z3A31":"UPBgNCbidZB2","gy8XJQStLpGT":"iJIdYND1v5yu","LUQGQuLzUdxc":"BC4faa1C8JT2","o3RTP9J4R6lp":"FHbThnaiN3Eh","5F26YD6pqnLh":"NQUZ5XdTHVH1","JNM6OWhMRqnL":"VLJ1w0yIFcka","1LRJol8vpyuY":"kwTSiVERj5pJ","j4qcBaozB0IQ":"wTMfUJQHVX8N","ku4vq1lzQJZH":"gxUgs50pRr0i","O7AwUbGIm4Is":"qqjnHzzjYWNr","H7MwwBWWMmBK":"FSBISC9ErG5k","2cIc5m2KyMQX":"AqMAdq84wXes","hwvnuz1WPcNe":"Zx0QOuGKqLKu","HjxN5yVZHFd1":"AFxonw0hPebZ","bpmkqnYKurVA":"0QMXrfwxJ8TN","eBktHxrd8xIF":"r1dRI2wE2IVZ","lrWFGuP7G3bJ":"pYTraRUUgli4","4ie6lU5NBHDx":"oHFZKcNY3ilJ","EtWVHCuyTUUJ":"F2UnSk6XMlee","3uDzezYmCnGb":"SP8uHZLoTjzS","DwurDXgRg4Zt":"X92yzMGKzDbe","BN3XobB4vIyD":"tQftsJuEWT4V","fWBXSr3UGT5y":"5obYENfTwGpZ","GVNVmqs9hrvU":"L9v5ZJbqbltG","v8vZ1XPPzjul":"5u0MTrq25ldM","Ckgzkoc23LGp":"t7sKsxa21nZH","JCYFMHvW3bQS":"RjxOQhra2gTw","kQVEvGcYNOSV":"f6wir47GAUks","V6t5Xo8IvgFT":"rt4ntnLFH8ew","9r17cdagW3TU":"jBkZZwXc8F4v","a67F5Hr58u9u":"Y7tgq5PDjXwQ","52x8ZH6WW9R1":"LwVFU6EijalJ","BFCHngCBxB0u":"6Ou9X8zrJJzB","IE9XI0wTVDaU":"GULTAOPoWzTq","WRBCxlmgThFX":"BkgggpBK7urU","Q8XEj3MTFATK":"bgjuU1xHwqOt","7aAtWUNMJqXK":"z3b3dPF9S1Nh","mTYC7AFGUk7b":"fyo8bulWGR4E","3m6l1yihAvzJ":"RqJVJTvIH6tq","QYlemDJCw07D":"DPNC1qIsxzzp","X3pn37J25G8B":"3iJ5ZajqjURk","OfWj1t2Vd2G7":"zWhKZeaCwcmA","8VRU4YHw3iMi":"Hs8kuZ7es55g","rg8AsvBherF3":"BZs5yxJVN6WB","gM8ZFN1xWQ7T":"pIpy21jw4i0v","gDf3kc1akiNM":"Rphgm0qoFJDl","TW7qOLH5vZPJ":"eSClZDDSPvN0","kwcXFuf8aA13":"WBr2ZoP9PvBD","aSVf7ikx34mH":"da1c533RjHqk","EFxWnFPhCWK4":"xIRU11MR1amk","fh05Hd4dkJjb":"wYk5GECmaEsP","ORyM4GqymAdN":"a9dbUWdT4Gxt","0KK4doZm7B64":"mV9DItXgC8i1","VogKfwK2fqQ6":"Rg7EBxE5lv7C","112x9kuCJi4u":"q2LoJ2fJJ8uo","TiYkmm4zSYvT":"LDMeoJBUgfeX","rdOTQjlf6z1W":"4aEBld8FraBx","Cn8WnDJbknbv":"VQ1MX0QTAhSv","Qt4imPJLgC5p":"NBlgkZyY5Car","U1wc97Zcn8XA":"McYNdkLFO7nD","tKnvBmcc2rTN":"k76qBQ1WFKv3","R6lvWGJYc0oF":"DsEuXl1dVOCU","khJOppKYoym9":"vK5ogsVS3zOu","1JjLiwvN90hP":"Ky17OGdVphlt","x7c846RpX1Wp":"50aSchX0Cdav","0dr3ZFpBRJtk":"1AfSwcoFd5Cz","nkGkSugoDecB":"nMZULKQlwq7w","NZ1o1mN8DVsS":"MQakNpSj5OHE","xuyvqKHULL0B":"EwgEUtXupj9F","uCbbClo73hcC":"xr2l8QiditL0","qhLfONOS3HZH":"COQmyGLEUP36","HYinBAWXkR9i":"92y5JGhh9uxV","7SqzVyZtQWrz":"9Y6sHStXNmRg","rTlK3vmplpjk":"L7QBDejUfQEs","eF7y7IJ4txPE":"liwZUu5Loq5x","NVqk2zHZULpv":"hx5WHsyn4Hdt","aC3mIYQx2D5Q":"Rhnin9KT8wvk","IWPh4u4Xoi16":"aSfDj4v73kjK","KQJ2gy49DCG3":"7qIGidgnFJce","94zkYtwaB2O1":"wTBhRIIxbwCx","Cf8aTuokcglN":"egNjZnemQVfd","Kow52Voxc0sy":"CIX0kDB9iEAc","2Obs5zkKaWLx":"u1SPLwg287K0","1eULFqVFA8jm":"VNfi48u32kJu","YtrW74ILGWNG":"kV5aIed07VVl","D1ZOpPfxexik":"7XbeuGJNS32p","oBd683Lw0mMu":"U0tv9TNcFssV","bTbSGj8vJtbE":"WI6Y3wh1iuca","rrQByAITP41u":"i69dpZhPZ4vE","ibQEOJyxOCi2":"1Yan0YFeznmM","ONIGGD5GjM8E":"yJwUpZ2J32dE","J8X9plDuyjef":"wR8CoTYreG4N","w1p28OEyZQ62":"akdpxJ4Sj5LP","MIWyYLduHIqf":"LHcSeMfdVDdC","l49PiCmkLDdT":"ZCFPFzdQt0eO","68jh844k9Jzw":"NMm7fkXVv1dK","KWQjIMPaFwVw":"wJVLiY9uRMA1","cze6lBSDIUzO":"Fq3rSDlBPYDP","UKpDwV9kJtVr":"4w2XvUUXS4JX","XBrns5Vkdx6d":"hyc5v3Ld284J","GnleywZcnf0i":"4Ye3RurbfXsQ","5CGFNr5QrHiR":"nZdDlUjS4U6E","CZk1GxuVlDWz":"gqHg4ChYbLnR","E1XEyHA1hlL3":"eDAJT9UeDJK7","57niC1EhrEvJ":"5DJRYlQZ1XmQ","UpJ2x3aQ2DMX":"CmL5171kspzs","Dvk3z2jC8vCJ":"PDKxBEX2hxVe","hXmdk3Q3Gjk9":"8D5cYzYH6Dul","PQSRLx2Nd8MG":"pAJqoopgJwH0","Oa68Z0tkqRhV":"R8XvZIBX1y67","rfkE0NBL5tJm":"YoaGzYR7MUkz","LGVWtVVv9QvN":"SYO7QofqsxXc","NeECiMnlu13P":"oU8IVzJEz8Pb","EzyCoNtGaRBg":"q9cGfqSaHkfP","dRzhhlBKxusY":"7gzdao7xgzUP","vVBe5eIG20T2":"9VixwoBDqaob","30Jq0As53ol5":"VpBNLIxFPjce","mCMZdSYP3pEN":"cDQ8cnlKzHel","Lj6Ul1z7kWTp":"ov33MsUCjsma","B2fPonQCy1fJ":"y4p1z5RyoKq3","5LDfmARpmTCM":"eeM7dLeVC9Yj","whC4BvrJOVth":"A8b0XSCk5lcL","AGVmrer07SbK":"Tnc3YtloAphB","viwUHCsF8DCk":"RO3aUjVumNBw","S9cy3mHmRdU6":"Cc9LvnqU5Wpc","Wjc0LmXHLvMZ":"WQa94XDphiQT","YbPPysgHrcEv":"y715RhmP5AVm","3b5wTrEUuUc7":"Nqq4TeP0bynt","vabPMhFWOzov":"MOZy7EFKMxUC","Lhtv67IxxCkY":"SVkbsA17Kwcq","aNTB2aCJtgEW":"UFirOGAl3b4n","dVTH2CpHKCvp":"9CUgdTS3kTYL","mM8IPEW5SrPm":"uG1HurTGXImR","ceAMPlsF505A":"I9PrtJDVIbaI","dz4qmBU92cGn":"kRmHnAVHkn4P","hUeNysA6JK5E":"HtZGD4MYFNVI","00hNo9uz0DI1":"r1lGOlQuW2Sl","VtT8BXXsFe84":"0G6F3vDHcNQP","wsawf2zCMIq1":"bTLaWVr4GWON","6wh0rQ0OTcK0":"4R7iokBkihMw","UcBiUSjqj3Is":"IJkKcYYJdrrZ","cf8Dklkzpij5":"Ci0XPu1mQBLM","6YA2pk9t4cKK":"5N77DYLfWtT9","cVT18AXKc19X":"y6ExaUPrkTOV","LviOFRclRs1t":"okx5IzVqaAuZ","aEMCzsXlYOgR":"bfnItWcjpmmb","3VBq0fmvdfFx":"0V3vbqoki0fv","sO1L2RRcvb2n":"eljtDxrRfAMB","8qOzxQ22NDgr":"qr93yRPtVvcL","Bx2FtoflIEzU":"JElRCi90ohs7","zt5xs9v7E0fa":"asvM9SAYYRop","AXuAn81BIYuf":"LibTR3jrq1PN","8Rq2cpV3tqd6":"lr1Eais4J4ER","61dL4XQrM8MD":"XDS8ZQeByBUr","NvUQrKoQBnfM":"fP6OgyOACE7n","c7tKNcdxShZf":"hnF0WiiQvR29","fI7q2MzulMvQ":"GMPF5W4cofUt","t5Ao2i3gOMp3":"jMuVMbSr91MD","fQVDEaNwhIVB":"xiaj37iGxeYI","X5AwDHefIRBG":"L99Oe6HjTnuw","zXmLqgxNwebm":"4LZ5QpWsj2Uu","dX0TK3ixn0t4":"sXMwzvQGoGme","vtrWF3EWee2W":"5wipn0O5NIn5","B63lUHeRg2nD":"cd0MwKQJQuzW","vG8YgGG0pYw9":"XX0l39C6yoij","kBc3zgLq2xFr":"zHbIZtxu9NBd","vf86lcFEj4fH":"PYbAtzVeiBAa","jBXuraWI1pGX":"Q8tGM6r3FFk3","1Nk4drcZ4VPl":"pI7MUnaKsJhM","g6qTAZag5hx2":"sVya0O19EGIS","6hJXvCsmmUSM":"TxJATqfrXsKU","vkDGiEXvalsK":"AWcotTuBNzas","Zkk0HLGQivDZ":"KAzFecrdjdWe","zHlJFFGtox9z":"3N8TkR8QBVth","MH3JSVzhbMZm":"2oNfXe8wM92W","m4KNQFdW3QGf":"hlVniBbL4a42","0nzF4rgIVlv9":"OWAZFVBrb8Ug","NjugXOpmH3z4":"zLM1jIb12tzj","WOtupGjcOJqV":"uZcJAbmtrxd8","qEp97HmwABMg":"CIFzpSnNAuWx","E3pLjFHm6nuv":"AaocMoarvUzn","ao5vF7LKjBRq":"PQD51iNEDkY4","4Izfk9psBvG1":"Z56rZBW6WIqo","Vz447XjqTHNj":"bmIdZZtFtgMP","jM8iLepLQMzC":"nxfpQXpcWPr7","RxnnwS3fYPm0":"SxX27E8LICcC","dwyJ0pRwftX0":"b9krG6J3XY4c","PsEmpc81ESqp":"C3dVFOzaxzWl","X0njvTCdMAXI":"klN9tRdu6QT5","30zGyLcBPkxc":"tWKizK8zCNmH","vmC2t9aPPrky":"mEV3U400hF4C","ixx8NZNBS5HF":"60brninZaz6Q","7Npwt7iOh5tT":"92p6gPjZ2skj","eXLgSTdgbEc4":"Szw2JBY16CCx","QGsOYQD6G96u":"gaJgeylJRXp3","wb4qwQPfJdKf":"ca9N8pI7QMnX","YlhUa7bw2oRH":"m2IdfFNZY0d0","edl6U1bAuf1p":"yM5v8nT9hGP0","bO8ln5IlWpJ7":"O4e08YojkCeR","qZIpeL1oBQTX":"VqHgEnoxrQrX","d8XRrmtoz8C3":"XdW4avfNQsQB","sFv3sONu5X6l":"vX1Tz8bpf0fx","ibLrqJBn1zSh":"9PGYDWX4MD97","XX9CCanze9Zd":"COJ0mshM9GFR","3Kui4IEq5lmo":"MscdOk2sCKol","CBSZdELP4Ose":"qWD75OsRwqb1","bZwq24sUjFMO":"2TinEptRLpSs","z5F42ojEcdVY":"19s9eOhCipM1","NaWZH0iNjf8g":"UU9Ll7zjuwuT","12e585FxO3hY":"59PilGtTXkhG","d4i9F2hpr0p6":"TmXfNKiWg65N","cvnsd4bpddSJ":"OkRzOD5JFhXU","2dylxvaSvess":"R7LS8vTLs4gy","arZXpj3e6xAI":"vXC9Yg8sdq7y","0uIPJGyXHRpB":"cIhZqamS6koH","3yUU2IxzmiGy":"YJVCM84ss8IR","suLZUye0eAAj":"W9MlTIZf2tK6","Zmg60DQM7ZKL":"wpNV6DtmiuyK","YqurW2hsQ6c4":"Yq4INtVL7JxK","7VMwvGgRRCK7":"lg6r4T7wnFoK","8o9YSniMC8Vo":"W75b40rkUMae","g0jEIP9iEQhx":"uyZQWvGTXxjd","FCd9Cj7uS47o":"JXRS3jTSJDzt","QvhsACmarOe9":"ngQ5SzXklW8w","N2dJyRQWUoxP":"UuhCaAG6GoQv","Vqd23kt8lzgX":"flqPXhLMFCoS","HhHCdhho4V2f":"VJNsiTsMfK53","CpLQwCPcO5hT":"YKWFYEs1qcAW","B44mWT44gn52":"UzG0DkRXBOiO","DwRtCosN9rag":"ccjoxz34O4RF","CC0RXPvtoLMa":"ER3CK2LAb3oS","malmWK4ZcmcR":"Xa75oIW14Py5","JTrYQXQ6fy4F":"OTcbgjbuLRG7","2SfBrtpRMjD6":"BSRa21upS28X","SoAzF2WlBDCD":"XoChnhqMmtoR","tlVppxSu6AZ7":"mhdu1K40T4q5","ULNCZ8r8pszJ":"HS8ENyEiiNsJ","ENig2AZg6PIQ":"LhueUwrz1THZ","d5NEXAEliNHf":"gy9JeTSF5P6F","cC4U2v5EJKXV":"SPtFPUTOTxS5","KWUlLRXX61UB":"kbpV3IWfKZoD","XEX1WyjLUgmG":"zGjaVnq2zRzC","rBninjfYVyp0":"ye7T6KRzxG3j","2MIF6vZkdDsL":"K3eHapm2QZ9w","QrnkkJ5jPizO":"7ADMW5UaC03V","bIJk1WLWoYde":"hOfQV7WRGTGf","qrUY2f6seFjV":"DOlh2V7hwM2G","OFXfyEEB50bT":"XsEtnaCkRBvB","mLfbqII1dDDt":"hwceVYxovi1H","IcPJDfrI2A7V":"9YFrUufMQa84","tGqCweyENZfO":"aGz5ixf5jzjC","Vt7UcfB2TmV0":"qS2Fj1Nf6E4m","PgrqkCaCBcl8":"ZuoMMYOfTlpG","eXKFETBdwS3N":"FHgy4lApnllk","IptlvxjORM9N":"gNUSIQJzGgaU","FQHNN4IPOT61":"Iu2m27CnGHVj","OhEK1vpJvllT":"Vyqg7LarEVcM","7GtcoTRw6jQ5":"E8yf9nQYNCqe","xwzgsEMAOOBv":"yUCnqImS2NuQ","o9lm82crIln3":"yqwBad9FrxP4","tMYCcph0NE0Q":"8decaEwJFr4J","TYGf0GZQaocE":"HvWU8mN5NjBn","33AT4dayN3l9":"gckrk6w2oGjc","3Xp08A7n4WQL":"8CM8yLIHbuLj","89RqpfAmyNeL":"VVbPj64mSDtG","fAAUPSJEBlAO":"5NFus8c5yiop","RVuyItrIvAFK":"DL0PfDHemsL6","0Lu7dKrwL1cl":"tOmWkfPrnic0","Ibt5e5uYeiLh":"QN1EXHwYvJXg","j2mfILv9a9Dx":"F8his9Usbx9n","NXCtUSXMhV7v":"kJePuG1jHpzk","xpzqp8Cj4qk8":"SDWjtxNv4uK8","uxM8MsJ2FOxO":"a4GPomblyIAx","6iBdDNZq6iG4":"ZMiJkQXcEUi4","2xTsbygDrQxe":"8m6fn2hoG1wn","D5rQDUfukAgO":"94bMgLyh3ou2","mYQIna5kMExg":"EpSty4QlrO6Y","ZirFa7vUBFZZ":"oqSTTTVvtoeP","C2KpGpTVOVPs":"pIDh6YWvJb4l","HDigJOaUxLy0":"Zc35sOvbOTgE","uZzu6uTY3KTH":"iwYyGzyqIAnx","RaWViFbLSiwk":"9B4Z3ZOCfWVL","DLQD0VGVAPLr":"TNCTkpoJXYFl","YlNi2eckFYTC":"JM2GuUSrEySj","TWxe5jObYuaM":"OOKzECIyVQd1","kGBqu6erRAhS":"2xMv3UrgIcQt","m9OB454F6F88":"1VvO03bYCY1i","3D8QkmDjYuzO":"F1wHVK8ucqAT","tgDwK4bY4r6R":"o9Q5bZTHAiU1","ZOEMhIoUlUMc":"njS0FR8LMZ4l","kneI43EQSE32":"pK6giuEGX4Ap","aA9j8gelGmUn":"QH0ZPnR27LI2","nC862vzSWYBN":"9ABF2esAayM8","cFrIgaLVr2bR":"uQDodGKbYWzo","wj9grRmZqRlu":"ZbGPdyCZJkfi","7vnAnS1SJUlS":"Ku9awMk9aZKg","FHGrLB6pLfnZ":"mynfxZNGIX6H","mLwe4zYXYrV8":"segWP3eZcRlH","4WgeFbyh2I95":"wCDtVEboddkb","pu75qYCXCsrs":"rkDiOkX3l7un","A8i34ZVLhI8k":"Rukn83rfrT62","kQaFiNruZwHM":"MZ4AGpShaT7N","oMpqBjKCiaFC":"HO5fzE3xepDx","UfZDD2eoxZOZ":"f150E53jEVed","U5XdjGMhbIub":"wNLHYnc5PzsS","G64DEtsLGpIf":"6DWY1f5rqtQt","rVXnty4rb7mc":"U1FTAo9zK6Br","get0Hp1I3imM":"wgRN2ifhl5kd","z6TMRiMkKZt6":"s7IB4ZZCwoca","XmTWqnWgAGu2":"ockKC9Hkl38G","HmFljlTdoxiq":"ZLgzaWtoqboU","yrDDy85hwMxs":"l48RGUVH7xn8","OLmF15342gN6":"ACpP9ClfxHn8","i7ThneRtg5x1":"m5J4Ju0fCnfO","BQIw7TZY3orX":"loE65DhfrSxm","9zZoOuScZBch":"cOdO4QZAVIGL","OiHLZ1lEPZiY":"WEwByQSEQzNW","lxZzs56d8XZ6":"7zsKNh7D7Rkz","qYWnNc6A9FlU":"qkop5n4BnwlA","OaarrjGRO9Xr":"r3qUAU6BUU45","twAgzxzFOdL6":"B0gX3LpNd6Zu","RX7pRgwf8WPW":"CbBsO3JCEjJg","JzXXHhkWOj7w":"J209TRBE5TYl","yWkapl7dE4CT":"oBxz7WVkdOnQ","TkcxBYGZJwyt":"UZbxLOni835k","ZPcCDBoRVnnS":"MeJ3ENuZYTfT","Mugw6zF3MC8v":"IvILYbEutpbz","7AO82glK42az":"oRLTX5jqgkrG","p5O6sSCauGHH":"2P7pe0IEU1hs","UGrLUQAuxtLq":"zi9XTK9A8wWz","yTiKTFKQuSea":"3vBoCVhNXade","2YDEfJf4TKnG":"UQ1H7Am1WJr7","VkuaGc3U5OXC":"SdQDmjUxRaMm","IReosJFaisaZ":"Gwjv3PiHD2oJ","AkCerphUD21C":"KLYfO15F141i","zEgaR65GvnWw":"lvJLGOejgrhG","C9VK5JxxfWz5":"ymnsqQXMqh6J","1tr5LmLPMVBf":"nRDTUex02MHY","hfdnYToc0Jqa":"XWSYTSSkCwfK","28eCFXUQ4orN":"kvBW0OixJXfi","6QPkl1oGczLc":"HlSuuxwilo1n","JHFVSPhHwcrm":"VOd4cfHUfAbd","StZRWDu3Ad3N":"RRrCEoeWiDHE","WOeZ20da74Lp":"YBC13jkBF1ix","Wz4Pxx3KH6Wu":"eOauIZrR4omh","EiyGwPyh1pyT":"P3Bb4hhZoGuR","NmbP4hsmdHPX":"PjkAkkdjlUqv","2BZ6cq6DRae1":"dLnbof6Iav6E","ogUIbhYwJl8N":"8jLF1jt9iVNS","om2GCjNUcj5v":"IhtxbTqdXWbp","KrZMtCzaEtbL":"1iNUw0Kri9v1","EgazSZyuAV6U":"pblTjt5Soksq","d0O3mD2IcjKq":"kBPVL8TSyaRT","pQ0neXd8AzFj":"LxaOY79g70Kn","khl9vVvo22QH":"VjCsqt2Srrxb","eixgFRIGavrq":"cTZRZAYrXrIv","HoTHD9QYxrcg":"2jEDjlONw5B8","JobQB2rcdwAC":"MTwCYaWcysSn","Ho6OLurzWcHL":"sCLhfl23sZX0","9li9Rdpa42C8":"FmwFDlX5ZSpC","aXmRGUqq7H6X":"S3oUCsDJ85Df","yp8zs9UxKl9n":"uBCwVvH4CpYN","WlTuLz5w6tcV":"lJR7u8aL0yMO","59YAM4oh4kYb":"wFk0SsfTEriA","amyzlgIcGKrs":"gpAonXXCG4Y0","muPFrK3IGIDT":"DmMEgvtSUYRU","ipcvF1f8PAXE":"XiIppRUkxDMz","yf60CfV4BKHG":"MiQ1DA5KBiNi","TqY11YblJRre":"fDJVXxkvOqAp","hiQCFfCDReUc":"KqPsnINIteXz","vYGd4zIDuVFO":"SuOX5XHnZhrb","Qc10IRt3S3HL":"zGh3qBuHvgJe","BEgrcUsXtZ7x":"DCTQJGZXNoyz","q1Zm2bA4sBdB":"rVpxCVA1FhOT","6BFRM8ba6SS1":"ZCBw5EEfSytA","59isgPY6PGRc":"B7yRWhjJDJ9w","egLCsiife1Wv":"ydikcCV9SRz8","5T4DPWKZwLxr":"kN67aEuC7XGi","XxalBoFGLAOv":"qyqPckCD6aoF","syfJZhLuvt6J":"upxrZ3GKWn48","kkCfG0T1fIIP":"yVOTCv7aDVpQ","x05yuw93m5Pm":"w09dYshtEAc7","hsCABUcofez5":"EM1fX2tkNAtW","vLxOyZQjsgZY":"f69OaQaGPFGz","Kru2acqoiLBl":"Ye3SNFF31ySf","gefA6Q30wNXe":"CTyeSDsfeNsm","SJhGaBQcI49k":"BSzkxB68nvSK","wTbQAXBzHlLG":"1nOj32ByxP9j","GDw0EOixpci8":"rr7JUSPle2fl","uEvzFBlWm7Cw":"AEXX52XkgSbm","9Xb3sYA8tHDv":"pXDZKmKiyujb","G9db7pVEaawM":"BzyK82W19K0W","AeszE0cYlPNw":"10aK9GhyhtIl","GJkdUExtSvrN":"TiD3a7UJOBZM","2e8BaSIEB4Pf":"TKPLLi6JP1RV","EU5PN0oKKmxv":"UAj96j0qVFYL","BakAomUsSRfW":"Id8fK8I4cTKg","U4r3G2BWfb0s":"Y04CQD4SBIDw","M8TL0rHPWszV":"ZxqrH6NLq1NV","S9V7nNoNdR1m":"V9q1W6ON4yo3","7FKDDwij4fHa":"WAN8jWMLG8bT","nv8ayo8XQLPs":"FhsFe6Ug1rmi","91JtQO9Ha0Z6":"lIqhHUxMJGQr","kF2bofigWWzG":"VglUT5bf1E4s","zk3CecY0aEo4":"ywleZl5wbp8U","Bxk47CjlNWwq":"qnRgAUKhCgUN","LdE1zCuQ6YWm":"2VEHKvX4W7A4","RsV8WuoK61C7":"vIt1VERv8oTv","oLNfm6T2LOMo":"qwDAFfy755nR","X0CfKPSseqyD":"0beVvDwd8Mc3","Cb79BlxLcx2t":"enwXODxxXQtT","cANkEPhrEFTs":"tUe5I7IZo7f3","okYlSigIvcml":"P9ks3bDuojEH","lZsSdCYJ6yVc":"BXVTi6MxilQC","m0slSMfBcMUg":"fMoOzzVBra6a","7TjmeYGNZDyo":"pdPSZUnlkvXe","kbh8VZ4pHGXw":"0Glb2dF9xDCn","91oLsiLca6wL":"pKpHmNzTUt0M","ZPjTGhH69DNH":"bLCkH32SDYGm","e6ZnYg5neQG6":"hHLVG4b9bDxr","eOvAhG2vUvq3":"aR0SBSg5b6f9","3ImTQL77KLvW":"ThqpU2cuF0Ov","xRj8ThGw8V8j":"8GsROUbrwln3","jWOz3lsGu5I6":"6kB4RXrjnTVQ","2HpRH2zVN4Qh":"sbE7Gc3jYuvt","RQr6fWXQIaxk":"SOHD4JQM1YnA","0TGuAQLCBxHt":"XSRFgjQp2zmh","KhrHChgxB7j8":"SvIlGBsEU1ws","JP1pUvfJws9d":"SxfkOiRW3oLJ","LBKrN82jFYYv":"SadPTVbX5nXR","gyiFm5oZjEjY":"HwhvxPvBaVZR","FesqrNQl2NNa":"dKwhwMIHG5P0","GG5ZUsUvmHVf":"DfMp1s8O2Y2p","Fb9iD5xWpfN9":"SKRtH9t1wOLV","e6wrkPHqjHlC":"8noum6RsGEEA","CdVn0EHCAVg7":"1TgQ4GXBnHGh","U9zMNx25g8fS":"cp3gFOdqGuyS","9I5dJX6fUCP6":"nhO5XS6TIbiP","1v0u9IUb3B2n":"0MISpcbEEvL8","4vpeb2nLi9Km":"1gWCObsoEm9s","PleyJjOsaFIM":"0fyYFIyZahGl","QJCeFWIZd0ow":"2tCpA5mIPO7c","faqez3agG3SD":"Z4LxKRp1TL7v","F5bGK3qC2ShT":"GuPntETAFPQP","FmGxFUUcoeYs":"HcUuA2Iskbfk","joXCA4EwMseO":"7Rr2ydi74hOv","9YFDMw4JyNgT":"Ai0kJRxwCoZ8","F38T25iCsbKr":"QOVzs3qkaYs6","OWO6nnUDBSwm":"vNJKTXDcV47d","xEFP0F9hnS6c":"TUutkxJomQxU","Y24givSZ1rJU":"3EDycE9SZX2s","hBUzTw2sqXs9":"BemRnX9xEIoo","bjCO4B4U5X4f":"cyqzitFdmcXo","B4aRjMlh2oJr":"MhKunHkNVtWu","XSWD2CB6WG0j":"NOlBOhR8vkVI","o0NtqSiCKl1v":"gmC0yUQTLToo","1302AdbKDwvr":"rZg0SqUdlQ7T","1QaNe1RBUa2n":"6EVwKb7DFe76","C0KWKHDPefzV":"QHSbtrT8xR14","Ea5g0P42NGoH":"sLNbjtzdPay7","9lGjjarDzwUQ":"Qm5iJUsuRqr3","hkt4RxKvoNvQ":"X8GH0MNBVPvX","ZoKiWeyjxNMd":"7z0CFHMBpbI2","nheLjLOIgTaO":"zdxwMeCKFIte","Pm3KvWy8Nv99":"kBULyCcLxQBc","ckCAZYWYp70F":"hTq7voM7g2aa","hSXasMXKXsiH":"brDHnZc8NGPV","BKePcS9iLPet":"jnwllrNLj8qK","Uoy2huXQzwWh":"Ne4kj79lxRmm","W02o0PotzNRK":"UOpv1I1mi8cl","mxFMMlVXjPUU":"ZcclHOw57DW2","nKucrTsOyFc0":"j5kt6BK1BlnO","RNwFReER8xXp":"ySPa8NKTuByP","imgOcWyby0Hx":"vE3d03zH0Pm7","e4JHPjMNV7uU":"yQvl7hk38Rbe","QhdF7eKXfuLh":"pS68JytCONxo","MwIYLdFShApZ":"x325EfG8v0z8","VGSpKsJllq65":"Kp7X2oaXSjt2","e1NLxY7ZdeQ7":"kEgNLLky5f58","7c1hGhIprvhd":"93sc66Wn8H8w","zHNAdRCFCDMm":"ePOLg93T3kHh","XMK5hyz5pRBg":"j8BfRfkTNgko","fBcreNfLV1HW":"RwmNTFXmKnE7","6QHPvrByRC2r":"8lV3MAYqrz6E","srmThtcgUSSt":"6CNdpXVMGyX9","rdKx0j3JaKuh":"jxiOwgcRaqG1","GBWbfQoSa2eG":"BM93Lmcih9CD","GjAxsnOAQoCH":"9jCUBqO5A0Ne","7FjOumKK3Pir":"Rk5nMkTvrpdk","fvSLyeeh5frL":"vEygbr1wS81R","xIVqq77CGdco":"mk2A6ejyrbh9","gs392NFtS8Wr":"AEeHAVxUGnGw","lEVv8gwFQdv9":"UPP6j7NJgTIe","E2uqTJN2bRFA":"K7Q21IsRcwUw","cOVoUEcL4TnC":"PoiujAwp7zLp","NedcwgkppTIs":"yy9mfw8oyDtq","aTAegwlEPvgy":"GaMKrxiGICXz","UBgQCVOeiIqr":"03aUo3f9G7Jd","uYa7ZG8ZBZzZ":"pBgADOiUjbO0","GQe03dUd0JYo":"LcaAsxtCTVBp","nLf2wfEHe8mg":"wazOjFwjyaNH","ljqGBpd7jFOP":"L33RDWWRc65S","SLPAv5iOjJIG":"t7mgFTEJcmgZ","g7jiENyfHmky":"Yrh0z6IUFwMi","BTlORPqeUx4t":"3IUtNtuV9iHX","PheN18hXkTtu":"P3NjE4l3O5yz","Jzu0RwtvyA64":"BbIExcgST45c","NYFBSQjrsvHF":"hPKrwYlmzkJs","gUL2PAmZRdhI":"OnbAjdDaKZve","2J3G8vynZ6MJ":"lgrGNdMvlAzl","YcWQx3JSQchY":"QjYuUKYcG5LP","2DBvZV4S9id7":"b1oCawqht0zc","sZAYbCpjcSKb":"GED4gA4Rxcxo","RxM4QHlPZEYX":"JxsvlLnjmHDK","3X1YnIfdbhja":"6KDRYk0wFkXl","dHm7OMkpbJrz":"dDOKnqdqurJR","XnxoHDEbXuVt":"OYwOSyEPtLD3","XAnmhRin6ley":"VnaxB0WhOwHY","IfBpXPzcpqp4":"fPHQfQTMxrcJ","dH942v6jjOME":"PmwmQME6L86z","CBvfKFWBbmQQ":"SV3FTWf7VjjU","VzTdTF2ypd0j":"QC9pViWIHD6l","SDiTPJcjeIRu":"G0y8e88kIqzp","YiijV4pocI6i":"uQofnsfGoPXn","E8p9kLq18q0g":"WpGkuqDyBAXW","dpgBSecWed30":"cOWKa7bl70XP","yh7GnAYchCd6":"X3H8icQPwixw","F05wfaNppkTr":"TJ0ANWIVDBIT","PZDSaFCxtrOJ":"mtGaChwGwuPt","WLu5rcVXGxY9":"xPv4zzSLs99L","LB4wg2OKqnMu":"WxNydRmqSfWM","Imk4dshMPSLK":"rykVDJQt2v9e","2FDbeM1ua38c":"OixczLTKr2jq","KluvxNgpv3AR":"amdP6UpGBGGp","4TrqQsY4CnTJ":"qO1yyuZZ97mK","xio2LVMPqlg7":"gxZNPmeCgh1M","Tka8USH33kBO":"gMsrw2rGWKK8","Ub3JM7nlnTO7":"gBsZxHktnC3B","yxr9ghYX70CR":"lOZoaTRYFg5U","SyA8NhqEXk6c":"1gAc222qzpts","2T5WU78d6wUb":"GdgViMZpVHs2","EftSlltUY9MA":"4RZKr3gI0pRT","vIrpU9Agc6cb":"5cjaRvhnPWdf","RDMxAz19MEJQ":"LMzndRY5aeRL","aHLOfsrfUBUS":"5F7rHNqfJKIw","bFffQ7Wvlk0f":"l1rjVCFosRxg","VjSLMFHAxOo5":"ZCs73PWL07S7","h4VIhmLWhZ1W":"y6DKQPCWX5Ky","0Oc50JZVUfty":"5lg31EBqJ1SL","yQwGmeKKox7Y":"1N3yiz5k2sDq","Z3BL8SCVsLLQ":"Q9A9H9FE2xbw","uxtI1Qhwp1BC":"xhcNJQmLQKsO","02n4N8aErmd5":"gUnrBiwD56Fy","1MUyQqrS4lbl":"tllF0DyhF6t9","WrGcfRMtFBuJ":"VsGQlILqwlq3","seZjlekGrE6W":"CDoxpm4yEeXX","qDbm9baBw6wq":"N94HmWcDtRrA","2YcAM4MJKIwW":"nVEcHUAcgpnw","RmIUA22mz360":"45f1vGKLVt68","CPc1kZzHr7V3":"kEmFtTiQHkEo","FYbYRnILhJ2H":"SxAyWRNAPd3H","EKnYc1RAGUws":"Z9qBcloIMxnx","S1S1Zs9HAWLH":"1KvgKxpSAILh","22ioeBl1YaxI":"4JrCPEAc1xM6","uJOcgDjbcpbB":"qs8Fo1YvgGbA","yN6DQhceblOJ":"rFDsSGvXk2HT","9FxG0y9o7kKS":"e5ASntcY70v9","FCjQ6n2LHlxT":"iVF9TW3T8eAW","14OSaEXcPK5a":"R2TMN8qKI31h","FILozPGOtQj9":"u9EzuChFdg7v","hq24csy8wvSS":"Xyen0PwJDUsw","d9N4iRquUgVZ":"c4yoTEFS30no","efSYylANT1Pb":"kED2RPJrhb6O","n15QRb9tMOx2":"Lb5Kg5jCJQLT","U95Y08BugkgL":"5nktWpwdIQzH","Ppd8OXiFwxOD":"TsRKooj7OgmJ","eCTMG8PalPKQ":"u0wQjH9aPNTP","Igs7e1X9anTn":"Ge4a2k20K2jO","0M2Lg1bYKOj5":"XTfSyyTtq8Z3","CVz2vTUsbiWS":"ipb3iD5AxjPO","Ds1YaKnuEa8Z":"V7DsqWPUrSrK","CBrTgNRyhY3Y":"GKidC4fTF34m","URhnZ0FD3jgJ":"DR28HpKOKN5z","ww1r2YQfZmBS":"1dz9jd4DKxmo","uB3c51fGqrsH":"GgBlGIW2AyLF","MKsVwnykhJ57":"JGS2XEJ1mfay","fA58aaF57R0f":"SGmE7JBZuAly","bITi6GDYbbwu":"DGjN6iBfLlmQ","kRZTsbg3C0vj":"bfGAZjAxWHKW","Mh0DwR8VdSpb":"Zu6jweMBki4s","rEUXRPepVAvE":"sIFWlcVjhvPf","YyqBRQHBQhuI":"BsvBALQL4oVg","O5MXQFBpAts4":"aAS6H8ywtUAM","vkzDdgN1wpCE":"Pq4VmuuMZGem","PGbdAoTHQTXJ":"wNnCgQxLzpoH","Ocp77SytYBte":"dVBPxYIL4n86","yDKNrAcYzxJF":"MxQvi9o8Awi7","BbrMPPZoPKVm":"BDCePqvIiV8u","PTZByLDQijRH":"4rydAJJhJ27g","2dly2eKqfNgY":"3vZIIxhTPZD1","xXjMN4tZxuYp":"OEOGiBwgKXag","LvoUDz2Iv3v9":"YJdfKHm0NgTs","NF5i6vKAK7TU":"YA3jc4YWmPRb","JyeJ8VntMLqu":"hCetI4meH6vI","OnOjfK7uhyq8":"mpRyN3HO4JUl","1k5arrR57qus":"oz66PmANr7wo","pXkgbBMfsV7b":"5Zo3CfACR0vw","obscWXezxw6Y":"5LkOIworVMtx","1nxgpqfv1Q5M":"eDVYSd5kAWvH","44aOMVK4KjMB":"n031nSD7K1NJ","IVSYoV2k52AA":"4aP5pVknW0P6","VaSM5TFNzzYM":"kZloRtjj7xiO","RTnjq7GCKX73":"ozMbMXc3F7B3","K01NOX5sG50Z":"n4uUcj8mFll3","KZq41RDTUf0L":"3Vm93NkUrnLp","sTrTDpbiVMvb":"O3ESFgtDdW9K","42iwgGsIUnb4":"Yy2XkMQN0amN","LhixmEHLpdPl":"d8K1EacO04RR","zGPQtLGci5wh":"oVVMX2MMynuO","RBVCmqGAwXsJ":"nJZ53RWwX4x8","NZrEto6TI3p9":"zLhICdOZEQ5x","fD7cjNUiBZi2":"AOhWJJjdldAP","AKVOeB9HX5s9":"1jvvPTUiNxLL","lFGK0Y5LfmcQ":"lrl4PJfxbwdl","PkQ1q3t4e3ni":"Gs0Thg5gO2FU","qkygEKjCpCO3":"LbixTZydDIA4","C1R62DJV8oEZ":"aoRUzAJJSsjl","K9caFzaXuPUX":"lDRrIsCe4DmG","kVdc1li7TiIX":"wSswnznYkEMg","3IpiORg2aw0l":"5ebhP7loK1ho","x3ldHpkLrwyI":"NdqiRDbEIa5Z","FTZGDDCCOu3N":"Zwdgh3FToFu5","xwsaXM1dMQxC":"EjoZxRfMbZRR","HLkjYmi1F01V":"VWjM8Axc2FKF","0OwBdBunoS6j":"L4ud4dgiSo0f","ehRsFG0DS2hW":"LuinGyOiYOsP","25zT3vmtPm3n":"vAV8awN882Xc","dI4OTDbjeyYQ":"TCg3023vpSmp","7UAVV0vHBSsF":"mwxZKZPzNnsk","M4tWv8rsWe8m":"l4w3ddABi8G4","na1IpLE1dPDr":"Qr72t0pqWmfN","ObVrsBo7WGbX":"yr2N6hCcoZPf","wvc68C8ItFWz":"1Uae0apbmMwa","cjPdLxRa0wnm":"pbBMzoPrPNSE","TjwvyNBoxKEv":"npGEwNCjEsAk","keq9pUZ9rqaK":"9BozSunC62jH","flOOo58q1rtV":"DN0ouws8FIGN","gBOJcSCn7vBT":"yJPzwOYKzxaK","hvRT9GxZH3l1":"KAH4yMbvi5wc","7g69HLeCIurC":"2QuH9zNWat6j","dOlBRqnadanX":"VU2YxzFzb0jJ","fJkTFaSBXAMh":"LOMtCtHLa38G","RHvAtwLu4AKe":"ZcftcF1kvMCf","XcgmjPa5DHnY":"Kokq8S6VGSHT","r271uO3Z2wS0":"LcExXBb5YdvB","7qUaOJ0wpHhv":"bK0pugdRVDqQ","vtGf8YJa72WC":"wF4LjXwkglzV","JS1Rz4ZnSmqH":"hyDv6FByYuqm","XXCmUoOcJutH":"1rfY2hGcjx0Y","TyOOz1xHafkk":"WiJVKgO0l2Xi","QTWjxNmvCeeP":"s9uFHtEiK8OJ","Tns7wizLPXSx":"I3rlvM3QTw5o","E6DbprZrflt1":"YsG3kex9FcNP","rOW2S6DEYy6H":"55CCR6z2uUMN","y7lNrNpFUe7k":"j9rOHWcSKnF5","AvnomkkQjXVh":"q9RMpSeb2BxX","I5QjIHrhz8xS":"BJB54K4ZC5Y2","Ys0WhTi0ciFd":"NEuKpVePKqux","UpQKZcv0144k":"E55FfG6Vd11z","sDHh1MzQjdIf":"kZtOFPm3VVWu","p0uzbHxP4w9I":"UYvVWXifYcCu","4Cu4bBveGRwu":"DkOTkla9uzzf","gxfRLGJWgNtD":"brrMoBbaJxXE","mitxNBHJWT8s":"cUoDGBWeKlbF","x0oTKRbdl8YI":"fZqzgt9NoNz4","oT6wrHHRBLrA":"A8B4IoSkcfdn","kdhuhIAqEDhL":"3t4hH2wcYWJp","hufuYJXPArtI":"wjW3O8SWT36r","CK5jgbhanK2C":"2MFS9b8d0Lqt","ly1WrEy0TQMZ":"IO5m5i2vgmzu","WbZm1lxnH4aP":"r41LiQV3clx9","I3wtFz7VkTDl":"KxOjG44oPyVq","SX6OGsC7gE3p":"oddDgXt5OCeQ","QUkYM4YSUPLG":"csVG6XVGVl78","rHibcgvJQEG8":"45waHGWa3JcV","vdllX8ELNlGZ":"WpnoG14UGBFF","Th9FDtWidlIK":"e514tTLBtSjU","vRton3csAphL":"jJdf1Bpooln1","xI2nMp5FwkBW":"yzSV7eAaXbbL","yqW0cKJHlUz3":"CbGhcy7H15EE","4fBe6Bi4YM1V":"VsbqR3cHmpAY","L0Gr1mHwtWgg":"RepGVd93Ei2i","meNOTD0LgX3D":"FzVSxrqePdzB","Sox5ZbwG39fU":"au7E5dimqtDm","dUjNC3lsyoWD":"FDm4jNYb5t8u","u9EfxyK6I6nu":"OR2FNRCRKZgV","Gaw7KhPdXiV6":"b6z5T7I7GulL","fKkDDINbcQAA":"NBTzDcV9ULWr","CUubcMFeXA2c":"3JJfhPAaxXt1","I90xA76jUVhn":"zuuHswn7KGoV","UcdvHsnOh7aB":"Bwdb6VBELmKl","uKxTJzVd1ecv":"kiZ4jHDcyVZ0","G284wAKsCIpe":"ZlGC6yiKO8au","qDcCP7nVxnD9":"VHXqCfMfjiMs","dBuBLimvuM4o":"Y67b6jQnLu4a","fEVJc8Xqt1o5":"WSE2VgNWudLC","bU9FtEGbWEvf":"SisGeJRpVNAI","yqnEFZJLa7j8":"DIIoAGj4OD4k","836ha8lLIoQc":"v9yZQOdTDFDe","ZyQUSv3Y0AnH":"zKammdhoaKnS","aizI6RQoEAbO":"uHWMwfpkBv7H","pRddrlSdSYzy":"zyfVOrDKSFhU","4jGCfrAciUyn":"py0HzyuYgKDl","SMAHstL3VBun":"xWYJzY3c4ayf","nGen3LqapKHV":"5s1YgvDW9FFo","yRcPB4GFKFFY":"N23i2CNnJo5Y","AqRzHzzNh79W":"jMa9pEQ0qEqH","hwjxcF8pvh0e":"IO1F6z5WtBBe","DdwAi8rpjvA8":"XMIaaT732Ssn","gJW4xYYZXA0m":"yDdzpfJ64nZB","TP5ZDH0zc4eX":"nrwZXA2oJLJ4","dPIBCyo4MOtA":"A372DUW0wboE","sz7v0nOzFTXZ":"gcHoyc7fyBgK","QgrCj9MnYRgE":"db56eYyjT6G4","hhop0R06VlvT":"llf1mK7j9c83","Gi6f8NA3weFD":"aXhp6y0j08lK","s6XM01FaxIpt":"8TuMSrCzQ9Ns","55O9C4W3i2NU":"WpSLrUpvtZuO","8ZLCFbKgxePy":"GNPqM6WQU8DR","JOwc8ldJu28k":"DNe8khAbmmLv","a3FupGPAxSTx":"NR8RBcxDRSw3","XNKE0U2AmPp7":"j2cVdWsi2KQ5","bl7eApplImZK":"eRl6Y8z67LcV","tFG4cbq347u1":"PYN72ksUMWjb","u1EnxFI3TMGx":"IH2h6hw9XOC4","YANzhnZEG4ov":"yFY8SXH5y65W","XG8oshgWENL2":"i0sI9XMtxuOV","Uk0Xh75nssBc":"cmbvqqgFiWx5","0wX0z88bzpWI":"Her6ObZNJnh5","c0f7lxcV5Oz6":"Dp20Hue1hyTs","xrLHoMEaBo4V":"oHW9Tx2RZkt6","poTyRgernvTE":"PgcEl22J8Byz","jzfPcuNSs1rk":"PZwL1jNpFg4n","1iudLPqswxnA":"Gu92PFgMjh58","5mmA7I7c5Fi4":"J1Ow3cG8DsFo","Xb9h8iLcsEOE":"DYIn3MRzAuFW","ath2sECxLwvj":"IDNHDiOLaD5c","aLjwVQsPhEkc":"wxmOINpt25cP","OWsmj6TxgL85":"vDyZQ9uLxCwD","O7FCS1xeis1u":"mGeQYrbRPOlZ","ZOPfXJqBzwVj":"WfULcOPDcGv2","nJnfXY3nsIH5":"7o66ALyF5XYG","kv3WUkKne2ma":"hZJmfexhiOph","oKB304psmPZP":"ViyyoVcWEXO2","ONOuG01yT0Gl":"wdu13ngnlBgg","G1sqMKFQnNXG":"O796cyvA57U0","CaH9dI8c7b3a":"POWL6Va5njyV","WnkuMyI29UFu":"M7OrFMejDz45","B6BSoLyBYHyq":"8NsQoHf4wyL4","ebQZ17et2Z5f":"qQuiStkrOfdZ","LtCAmTg0giB0":"sNwYeJvQabAN","JEgighnR4GFg":"gmSB9nhxX0nz","KMuhGi2lOP9R":"zaw2a0WA5DBP","qLBRCelBIevM":"SNbWyhdkUkg0","i2n9UCDhHzqA":"iCYBbFlOrJ92","3PhOAv2yxAlv":"yzPY6Au3LxnG","51mgPV3J4qpF":"G3EWO3RKzUmm","r4YVaDI4QqAK":"zSkFpcDDvoYu","wY0OixXPvIZC":"HcfhOKzuphr3","uSNUu2TLMZxC":"P3viwhUleYfr","vSYbN7nGfJbr":"Sh5kAvlyyVt9","e5gfBvO3WiQm":"5bghjd9hANtT","Ik51Naaa7T37":"1UomL87nvmTn","Mq45Aq8DFuXu":"jPFhVds904oF","RN6DZHwWuMKP":"3wosnfvS6Pwb","WSYYAa8I64Sj":"FrSw8yQzLjyv","jRWzp4s3Ha3K":"B2SjjqjxkuD0","TLD5QuBeMz5B":"AcaXRguDBiua","wzzzVeKaqDz1":"ui65p2QXADQY","pHMeaIBuoKJT":"KVTPHAyPg7ED","6yLJu1ZIwWI1":"eoonHlq9qNu4","CRyAZgchUuPl":"XhrdEjYvzudj","xGZG7bLzlXGg":"WpOA9W29BYlG","ZomsRlSOLVix":"u9VHbADzigPn","hL8kWZJ90YsA":"0rzwYAIy2Thx","wLYZWbC9vIoq":"SGSc7I1dqaAn","ryz3TfuDSZop":"VZCJU3ggl9oe","rH4q5Wf64h8K":"Lo8e8qmUFmF0","1MqBcmKidwH3":"1NW9wQximxLx","gymDEQKvm1JE":"WhL590YQS6uA","a509fpyb8vIl":"gjFVO13OUk5f","ZB8FzHMAhiI5":"QsfCftAGcNTR","CCEbFfEcNe8s":"iaYJVDpKwxsa","KF19C0qZJTLq":"HtHa7G0mL0K4","2f60Kjy5JQp2":"WF3GiO4TBGK8","LtABoGMdIetj":"s04hMo0Q5wSg","fbw2XN1R84ql":"CF2ry2QZiBQR","nRxJYY3bW3LH":"dfKwWRxzCy4Z","kV3sfSC5vrlq":"7FAv3gy2UT53","Mov1q0GJoWlR":"AQVN1EWTcZCT","To0w32IF5vjv":"9nNYRCZxtaTx","dYVJEvs1iW94":"EQyTfx7tWl1X","kwgoPiJtEN1Q":"mcw4NzUrvbYi","R32LpqBdfSqi":"e4BpD7v5i1uc","ETTSkwFXsJIa":"1LF96OyzPLTO","Wc8SVWnHncnf":"xTkRIVlSyv8s","i3N5J7z2VIRN":"epN6Xx1AJOqX","XgWgTnT2lhde":"A9QEifOS9yHs","ugNmVEwhyj9s":"1aD9Qlo0vadF","GGgdKOT2Z9Nu":"XiiZ8QeTTRfc","esVIsdtJKJ5P":"1juuMziDPipS","K6UDYrAHGhK3":"grdvK1n9TU7M","D42gSK1lrlKq":"RSKGMfModK2t","Mr7wu1ttLZSd":"cRE8tb40IdvO","i3W94GHWWSfN":"3WYy7OrIwuQw","ztYx8NYuoR6J":"g5d2YpyUr6R3","xgBDuslr0NPE":"2sTQv5YQboF2","qnM4PzZ2xzNH":"VimBmdfsubhv","cgC0Nwsm0dbw":"ymcCQY8h69Q5","csuGfrfanIRt":"nxHSsmNenjBE","1FqZI0Wy4r5d":"epgFYrOQxvrh","Ghy0Oc52VLVN":"x9w2BLo7fRrv","Z1wNA83gXRwA":"oL4wop5N0p5W","T4zmn3olMYjY":"hcGIxRuwApAQ","niL75tMlmKEq":"JtZEnavDUqup","7hgTe5B0CWBb":"McdIhxpoyX8f","JyM89mH3nAIw":"J8sPgRQwy88v","gjUyl2c0ZnAt":"21xmMLMwAsfF","KCn0DOtx51KC":"HyfOCCkhynwG","t6XnCm6dHBAT":"iQIC356qUOfD","2H0wiwvvcyCF":"rogjAmYw40iU","3zEfuTXHIkrf":"36ZGOhRQnbTl","BpgnPlfEW7Vo":"FWiwfZSAlrut","u47qTVPm59eZ":"jrfYNDQOzkHD","yUNATpU7LteV":"8COfnlxQDwrT","0nMM3OyUbQ0L":"kEmcmgkyIxtU","b1CoxmimbXd2":"idG5tsxhorGD","muXa3jwvwNWf":"8GLzAXThUo3h","rC5F1E12gFPr":"z8KhMeq7JJEY","uzMPpOdxbqhy":"rfcByQ1oxONt","XzC7Fgvneqml":"w9tFbZcSFXZ2","3jWrOmwyFdFO":"CCQ9ANtQvJ1m","X8h8IgmuomsX":"KrCyc7kJ0S35","m63jnCy4kvEY":"lMzh2FEu5zbk","TNR7hPEjMeHZ":"M5nRih7KetSc","oYBA8qikOyLE":"2EFe6uloN0ts","3bNTwTKDtfH9":"HnWku0HrnnSq","hgAKJc0ykzw7":"u0ekXyFBSHJr","BbslcAY4w9CM":"bjo96y8pgGMJ","pcpShHqGWayV":"0jDVBVezLeQc","rLFxdYt9SpHf":"IAA5fTOrWX3w","0xn0BU5ptvBA":"C4pWbuCOh4fu","BDDurOyb60Dh":"aWLziq7G26Ai","6LRxDNarzEtB":"IXpV8PVsxrVD","Z0AahyhVniso":"m1WrXJ8BRKkq","tWFsQWP4OI7m":"c3Hk6smfP3sD","GXILpQswIiVP":"Pzhl524J8LmS","cDiQZ12Tucf1":"YJvJjw61BAhG","kYiED43Fw33p":"DYwMzGJugeUA","oJeIDWqoTkjt":"RzPInVnd7Ehe","WfWNEHFuZQ2V":"HSOjNpV2td6t","dB24jf6EwAR5":"LviwsebLBNFd","ZiWCTjbWDL7u":"tiMYlA57YUaK","X4Yyh9SAHPPq":"ow7UEYCBTJw9","uR2Jfv9icvZ6":"prjdr4rvDAo0","yu67PnUCmxjv":"XtUGrQkYWsar","Qf3eLfL6i94f":"msqvnLvKqobn","dvCFZyKlSKwT":"oGyBYjpDl4UY","xrvX2YqlY619":"TRF9eJQ8p7qi","MgHkNni0aclJ":"gakmZBd46m5P","ETHBNI0vjOAp":"j8qwaWcjNp02","wQGxHj2nvjKs":"XKGaLfzgaJFv","seG9ogt2L0VP":"qYczVGqDSwzM","7jKGXSRwSYNN":"nBW6gv1uZ0vE","qOGTgzzwyA1H":"ZOVzOFseLZg2","e2Afcgv1wmCz":"mRYvMk7A8vRX","JSDL3UBQl7LC":"mN6SLYpNYeD0","nifkIwb16YfD":"4Sk8ToWuoVOd","6Daq4LzrdlPp":"wKzIT7F6YYYW","RGxHMaOsQbPp":"KoqNTypyvwuR","Pvt0YBuBlnkm":"eM8xAzH4UeC9","u7RCCyeVAybi":"eEH0Xsii5e3m","jiinqNtilX9p":"aUuk2CQRe06W","haGpPXA8GIYz":"kfFqdf0NRhqt","4JmsH5y8F24W":"U3liGZZnknkf","ziUTPKSbRJn8":"m5ybg4oFVhtE","OVhLIflUJlSJ":"DyPDcn1phoLb","tQkJ37d4JKnv":"rhhHoSnuYl8J","vxoJPCohjhuS":"rP0hs97xPCPl","ZgXmEh1vF2eb":"DZsEJEIcqKO3","XXII8UioXSs0":"5Y9Ktye3AVmk","DGHdKXOVy3Uu":"YNo17oS9S5QD","WZTc5LE5GYg7":"xzuBslNDiBkL","i45JEC7zKXea":"qWStMpwGjt1J","rJpwBeqkkwd9":"pO4b9Bv8lIAO","lNv9JfZoYkSU":"mYkGwW1fZ5tx","HtKLlHk31qgK":"WhmFBGDiEUEi","i2LMPvr2jHTb":"hLOMlL6WTIOT","mrE3YQDNptBM":"oLYO9vqPqQtz","BTMzCGLUTlLP":"l31M4rqeLWEV","amz8dlyVITE8":"haMXsdpTBRAW","bVWsjV6oiIJx":"XRGJMMMlhNJ5","sdmDSHNEVcZL":"lAhir55NjecM","Dhwquetkg3cZ":"iFYLnlTGfjpy","9mXKdONUjSMy":"QytajeNFrCJa","YnFDOqaHUGGX":"6KeROTSu0MIq","lJ2ig9PTUGyy":"AUm51NND3QAl","MSVDgOlLbyGa":"ZFbo6Q4J3A2t","zZYRkH38THov":"unhv2Jq1dnun","YUL0eQZUCYVd":"TSgG0uxuqtAT","MaSG91Lkc9w0":"uhUuUZqifWok","3a42ojB1Y6ZE":"VEBy5pGIUf4V","c2xFZdFc4L8a":"BYlrxMV5Dmyx","0vD2FE2VAQDV":"CRSz9YhGepL6","OhHklm0Gdap7":"xEFVijhoRTgN","xBIchTSnXCpX":"Qj2u5zUj0A5F","ToZtlRWDhhjh":"QCMLtJhFUiq5","gtMP4AaR46GB":"oSqf074vQ9IV","GeGKOH0zxNBy":"toHcjP5kPsPf","IAQdlKqQa1jG":"47c2ZdLj0Uka","dExHXJdeP6lC":"DrMlq7N1z13j","dl4DscMmfThq":"Xyu61T4AKD3h","FWf3LR7gzKCZ":"pRfJm6Di3WGK","ylRVBm6Cavu2":"D7gZMdhWL0jt","ZQ3wB1ofHjtL":"PQkO7JCMLn4Z","3K4pCMKIttdP":"aq21yrIvsSmf","i7wOiIRydytO":"Jg85X1dlE9ev","rdf4VZjzaSJO":"MPQ3VCV98uRu","28mk44FhmiIG":"od6RV6cURAxw","WN3BpmoCk4Br":"syW58i5myTca","gtzFBCkJYlZ6":"yRnt12chAGjc","8Z41TadEaOVd":"2OokXH1jplUY","eoMMs6TzfIat":"1vEw9VHtY9DF","o7oCcLrhMbbS":"nTJfd1PA8yU2","tydCbLB8POZd":"JSsryi8YuBXk","AqCaYwnG6fPw":"m8l1iJAQOUkD","Sud71jfuWAzN":"TLEhunufaYg4","ZBo1vfO7gQ5U":"mx1pVs0YGM2f","OtD6RhQM9nUS":"XEKj2ah3NauB","DL15hPTZtzb1":"ZAnEhqwRwGsx","1wktQd4rQuIJ":"W2cIx7560IF6","1kxVrSJ7OyQA":"lMx308VK9xb3","51mLDoFMJcQJ":"RIpRRUyVGhr9","4odrNwjxKApg":"2osgjNgSebxM","5KfF1HPn5Hub":"mODz726FvAny","njyDnfYpCs7s":"sJxo1SPn1wYO","zBHbutDK9KlC":"3nKx350NUGGf","dYaRGQcNfVA4":"RanFwyU7ND4z","k2OdUaea9Ldq":"C2xdOCZZdmmv","QjN3fURai1gB":"OWLcSQqqYWg8","bnlhCMN8pS8p":"e0oslZ3bLYnD","l2lqWJyz5VMn":"4ogPLH2w6H4M","yeURpLfyyDcE":"25D9ra2TRvnM","Rl1uGlD3nm6a":"BHaGaJ7KmdfI","vMHvu4KH4mL2":"nkAucf2zcmMV","235MCmVFidln":"GIvAt5h6CBMp","JvFW61AB3N9Q":"5cX4KCI2rV13","Deie6NzlWgiB":"fI5Aany5LDtH","2PevQOo2YGAv":"mh2CVvRX2qHc","ELRpKKAOfCx4":"l0mMhjsRfTVK","UNsjUp2KKz0E":"Kcpdia9zlMCa","qB8Mu1hGBQn3":"MGBntcPN4R2V","oCbkZbNJadGz":"a5BSlCNVtqPG","FTwbRPi4dHoJ":"AmtX612tzE1h","9P8TQe4NXmFd":"xhnmHOKcSpwT","u3Fr9AqFKpbG":"f4JVCAMSwEa3","p335wVthtlvs":"JC5HCBxeqVUG","CfcMepi8t25q":"GvMp8kaoFH5A","aaaxrbqECpq0":"EVNFfE1maTev","TAflDN8KlXhE":"HQucMT40ek1F","ldrC10PUg1RK":"m7pBXasItDI6","NsN4E5sc7btc":"FBNvRYpP7iiK","p1SfaGK0TxTc":"jcJ4owhgBdOM","iCYOYDEEH6r4":"e6OrqPfShHV4","dCLn6FuevJ4H":"JnUA3W9rUdPv","AuUq30hYIoO3":"iG8VzidVl5Z5","k07wQ5V22N07":"yiZ2mbUY6Ewx","zAqvyoWCf3uI":"fwI6j32hAmcN","AwRJtmF5OLUP":"KHf9bU1csIkF","jxCfgp9LQx8G":"LFHyFEsBwdtc","qdoafvqsTX05":"iifAQtnPJCth","XtyQ7OvbXLfA":"Kio34QVUXID2","gJvCiHqO1pBX":"4DXTOqBkyCN2","yS7gs0FYhz86":"toNGuFV2f8Hv","msMe8UtVyqfM":"lBoTFziCXrr7","b0h7DVD2dbnk":"093YRzFOmddg","q6mKu7l3Ui8k":"06awa8He2DA0","GGHT7QgtdMXm":"cT4SN446LD07","yHPfkLFaJIdy":"UzgtliOY3USN","Ta788jsqwJRj":"4nYyVQWR7FwD","eYhh2fZ0AZjl":"xfbpQ2oEuBqe","FKDKrN0FF20y":"RsmIIxErQKlp","LJV2CfE7BODN":"0009FAQVf960","Zk21keiMyCG6":"cCrilKbXbaaG","WMm9ajih2Nq9":"6CeP02zAtHO9","qahh6kR6YWVO":"ZbfzzAv50n3b","Un6yU2OvHM6h":"rdn3csviVAlR","gHKPWToUVE2r":"8fAqko4bsihE","OQyP19I1UdIA":"E52okI6pO9SQ","ln92g8rll31p":"VEWOIVrbKn6P","8tidq2Uhs54Y":"tVckI2DWDxco","jVbEOj1r7EO0":"rwja3CU5tAJI","dCPTcJSUqml3":"8LKMQTxoMJ5v","MHnNT6HJimfS":"7wQWyxXoHuWK","Jlgea4mv3AvB":"m7Dcii6jm4LO","ffE70BvDcnuN":"Ro8Vc4AITAFs","BCy5DkNebGmJ":"PlQgtcg7XbwI","OiyOromRhBUg":"V6dA8xTGbBSy","fKtUPDl2KCLT":"n3pPdhR8feML","lSyxyv9IhByC":"td5j6fceJgAN","3T7tq09lcxuF":"ZXiZMy6B57SA","e0hOkd4ZZ7B0":"yVS5xgANG8IQ","l5QW0O7Djmat":"a50JcdhOg8Zq","TRKAs7boj2fh":"ICqj38GCGzTi","RY7CK0qTaQfr":"SbcgLcZYkIJz","aejWWeDQVjaL":"LbriDSGUxfdu","nf4QIP8Jczi3":"UmYtyhB0TwQg","t9Vl6M7RveXi":"NUGx1hmYEdGx","Jl6cdlKO9cHL":"GGcESvAvUDz2","LYWKgtEFAzkg":"HYxwHSIF0RXv","G6INRCAIFW0m":"42pQhkxII6gL","3De2C6h1l7Hw":"rWRufOlEz2w6","u1gtw5TMbkEI":"I4xlEOIFS9QL","zuwg8JAwUNx9":"v4oQxM7v5OcV","EPIZopJqXSaQ":"Zls2FVtQzUA9","tvguHtgthyZK":"3fcktUfPUeIF","Ef32qDRa2Y2r":"ehxFTyZhliGp","AwRaxFy7mmYV":"WcUMc0Lzg4l1","4fY3TptGABqk":"pWpUZm34NOBa","RvmB77KjPpmp":"olXdV93s0g8G","jHGtE274vSyt":"lX6MLIodOPYg","1n7DiSWhADIS":"NG2fidZBgsq4","KQ6mwGwkSAk9":"QLhxznVWvZco","HX6lk3QRToJ1":"etkXzmarkN5L","H8P9VHC5nJ9B":"vIIzRGJX91VX","CezA3Y6XyeyM":"nFufLXu7WcBv","tZvuuItHpx3M":"tFyJ652qnEtR","PREwFO8HfXsF":"ep16VVFTSpgl","WuYt3VK1howM":"l6GFMjP6Mmxe","vDXrsQ2umdzX":"uuVJFjBSdZVL","czcnt3R5WbYh":"uIm9NNwrxrak","EJNqlT9gOuXb":"K4weS1N1rp7I","zdgbQV3qepdF":"GVeFLoNwQsTd","CjoiRPTJc0uS":"VObs4O5IMkZo","dzoVSVF3j7Bc":"YYVHAFyxYqWx","fo1hZN6ZtSc4":"9YhWqRZlM3XC","3Fy9KSLyuNju":"rDIad4jArMUr","PKYWjkyrgKVY":"7PSj1WokwVH6","08RP892rSGyK":"P8jJ9WZ6u9iV","T3STJsD9rj6K":"NYZ2JfJ4Brrs","ad7DgDcqu2OW":"3aU4ldBs1VYR","35hMF22G3mDc":"xYyPznXXM7IQ","knVNHzR0Ecm4":"kMuIaCQJq2r4","O8kViYKBXizd":"Er5gl7n2O1Sz","MQN9PZBNr8Ak":"OsKa6wRIq83q","6lYCf8RH6WKP":"w4cDofBODTQ0","FWdOVLbeYRYa":"kXGvHB2DJci9","pV3pvG2mOkCA":"EngOlRGys4lD","4hGux0e6qypM":"PaRg7CcnT3Rv","vq8iprNoIZvs":"3uWAIjfBCQJ6","kujTuqV3xIDH":"kRcWXLtnC0jj","2y6u27zFxI9Y":"rxCcdBRGI2Bg","B51oMzaOXVKy":"XaZefv8P8LMu","jzKe9neyChkV":"C8xD6rg8aNa8","FS6FEjn0PKbv":"g5PJb43sCxjI","hcpJBPMp1gBa":"QrPfZXC8yTzH","KRHo6xwjOyzl":"nTTIE5YduF4i","BVMeG0KcU9Xr":"cRZkUDXvlGeE","Bu1JrMxBKiFl":"mUkjLN9Ve4vJ","QbahdS8JSO2g":"kuQzWHqMGnC2","2KtDjrhqajOh":"TAQWcQ0jFgUE","3hHdkPo2ibbc":"CjDM0FQb1Wpe","JbpJfrR97cbl":"hMKcBeMhDTzI","YgdtgokWwU8U":"4qjGi7Qsnwa7","za4w9EDJMRsH":"Cs2xgfFV8XfP","BELFl5ROxLNt":"TyyqzCLB2oen","Y4UjLIjYArkf":"Bm0ikfyKI2wv","y2Yw861H08qG":"LPNvHyTcS021","LgOh1k8L1f8D":"HuSw13Lq3shS","5a88XsXrtHIi":"0mHcxIvEwWq4","H8KTkoOyls5Q":"h9QCp0ROfVAw","5jhKXPKfJopK":"ZJjwGRihblU5","nFLaqQL6xR1G":"Pfe1hHsEU6i3","tm61GvnqAqIP":"vrxstDJpDyLA","mg2WWQf4R4hs":"bA2BsMqM4Zrc","ueIE0IW6HlYX":"k9nVeP5wzgjZ","hv3Nwxf3AqaR":"uGFpMgUlsDMl","uI5IBMpy60f0":"26G5qClxpTGs","BIR4oaRCvW3M":"U65UzLZCAxOX","g5skYhfAVl1T":"S9LBb6ehpWGN","A9RhdAxrjWBF":"kLMoYzRLNSjr","dZoKOlZEdowH":"9Gzkhltc6Tzl","rQDzdJfhKC21":"9CPqttpwgJlT","5saJ5mCN5jXf":"zjKbikCsWCSy","ToXflGlXYZeZ":"D2qM88QmDgMr","VyfQVk3axUjD":"V3CWEogB36Gx","qnmcVWijaru8":"RqPPPNzZvovo","20m1kXSXdsJG":"t1ZIb853Jk42","LB7i8X3VmOAK":"6InD2GYFnkvk","sJJTpUOwu0rg":"4avBN2ClYPqm","VKaNrkGUKDMg":"LuExXTHL1jJc","ulzykBBOkAjk":"dbtIowJDwchn","OlL2cgNS8l4K":"EnTG3d8WzmlS","baggUfbIo8pd":"uFRcidece9NX","k8UQBqKBOFEM":"kuUnla8wBuhD","G3EhM8AKvalG":"ru7XY6ON1UB3","vpnZcO02tUf6":"3PJv3GQytDxq","ubvkjo2wAEbr":"Y9hk5DL8tpCo","SVgzcJjYB5NR":"fO3rotHSMtT8","DfS3KbUcRn9z":"kr4TFh0b7LJt","i98bKlCeyA71":"eBHz2zVLcDJ8","79ctNbXmlxcq":"D85awklKLK9r","q8nsPUOplFX8":"Xn9Y97rDZLrh","Iuwme9AJL7gx":"ccplQ2jhG36r","8L86NZWWAnNP":"TlX9oqDlPnIT","eMKIRCw97h1U":"SMrP3hrpdg9Z","9CW4zfq9C4Wc":"OLLdktb18VMK","sZ3qq4N0YGTw":"UW8W2vmp3LWR","M37se1IJkuqG":"UUK597mUZx71","qHkxWvBjHn6Z":"i0B3uNRcEsDD","PMFyo8Yb9b6J":"iymhem4i8I0Z","bs0JYx5Q4NGd":"kStX7xdPmFph","g4OsOEQELWkr":"444ZHl6fljbI","KyQs52mCvThg":"NnejdcoOrw1R","QFjJN2RTmyar":"MVS9bbaXUjaE","9JqqQDL18DhZ":"mw6s0Zdp9IbM","sEaTrYFNGvhD":"rQEtHOFcGUQg","QJvsAF8a8R4n":"EOUJB5LkXlCX","EeveWpZXXQ7i":"4CTtttpEF2e6","LunCUTMuWbd5":"n1CoX54PJ1iK","5DCw4YCB7TcR":"KxWa76IDbiIw","o61qU96i9sll":"KlasXDD16VyW","iWmIbTisdyaC":"jP9tMtbA2ytX","A0DbmQPaxyDg":"VuCc3a3FLsb1","zLygCiVVQNQv":"fdLyQlw5rsYC","EiTkyN78hhgN":"BebKsVQY8OcK","uDjQsVrbZLsG":"dUgNq5N9U5Vi","2JQ0sL5GUs3o":"23SNO3KF04hl","YjlgmLMcoeIA":"EdwGFCQHEnOR","iQjkKkBwlQi5":"i151S3mvBV39","AthsBDTb28j0":"aRIIS0CsAznO","dhgfRUQsWBe8":"vO8PRedLah1O","Gw77vJjY7dVC":"nb370GiYU56W","biCaEaFs9S6c":"hEqUGS9gwx4x","QANdELhdwelf":"JvJrKvQxbsV7","21OlOhUBLTnS":"kSsSaeG0vcBy","Nh6R6aomRuYP":"QQ1A8pTIkdR5","GCgYuP1XXp6r":"Pb02drejGkp0","hXxL7QkJAAnH":"PZELflng7RZw","74HIk8J4LJ4a":"cbChHE5XBt9R","BUwsIzRaoxHG":"yPGnysawfG5A","9qQO60hwmANG":"IJhATChXLvby","0wavNOBhtboI":"2Qk4R7PIrflG","VIlJ8apLvYzC":"E2kmKnydrFLW","oLrdU2aTvFds":"T0hOPpnVmBdg","jyrBbMN7ciUp":"pr6Z4HoO8Bem","VdWFRlgO5uhc":"kinAe6omrAn7","hINTXY7ZZzh8":"Kp1whJbNj0Gg","p1IOaFYtup2P":"72TrLWNPyHFv","4GtEWPekAi09":"TSTvtrgAaP1H","OgFfH2sy6gqg":"83XK3xxwvEQn","KIZRsHUGvJkY":"Pb1WRPLBYBoy","Tx5eH6PmBpJa":"mmO5dEbIxecT","JJWxRcEYxz2n":"grxpGODlufnJ","7GLsYEwnPdD2":"Wkdz2R1LWkru","8Xhfdg4spwql":"0tHcS3fMPSYg","uVa2k3XUp1K9":"vwIQMg5rgIT4","nl3honh8PLZo":"5pL97LFvRtKC","IYQO0UDFJ62H":"CB7gJot5DlOA","Lh5aULYt5PTj":"I0gyXbUyoXwD","eISWpgTRtRZY":"1n2qsrCIgKP9","LMpNR4BHA48y":"bUkAaoSSp6zW","v7LcWFZmjOSA":"R3bpVryYmntp","6BAcWvq3oqso":"ZfSWJjxiISQv","m5KqGV11aZSH":"pfgOIWgCwOaf","vQM5nLTo5h0Q":"GKUfQtmDfYuz","fH1NVffnZaVf":"KD3ehmWKy3tn","XuYq25a9gamB":"aLYLnDM9nTiR","7r5nkzTdL9v8":"OgEcT3ty68no","uvJz2LVZBQSz":"I5LueJD1CFNP","j1K9T6ZY4Jgz":"T8lKPnCyBKk3","RXMvoahoIbxn":"d48CtKE9qYtE","RE37H9oKms6r":"h55iO22zDDKg","c19YSY0WLxph":"Km63oF2SIDI3","samWSX3wR5eU":"Q1bn7CaLFV4y","Tn5J40RSmVk3":"kRIreX8kPMFA","FSkYfTyPdEEl":"In7MwtHI9Vv2","kDG8LHoiycCR":"fsy7w2LHbF4X","EXRjcxUE7mNn":"ZEWl3uGQYuui","08wQya9voCKz":"ajxkakcwdC52","3kvplMFeioVs":"CM0R08PHg7JU","yhUYDJPFBBXQ":"U9lcsuKzs7NB","RoLmyx7acFCw":"ToCT2RmqjQ0g","OLwxzBTaQvBv":"KPosHOEyhcoy","FHvayJ0u9XX4":"PMNU0vO3TXjA","cjQvilEVFr3u":"HmyiozjU0u1x","meOGdtFHRiIu":"2q2RgszPhTE1","M0tMJd40EWAY":"f5XyxiRJcs75","Wd1PYiQwROeS":"EHGNi3jd0RbF","OiJWdTDRRusg":"VhbLOzkSIU1p","h9aX8bxDTSA9":"iekrrpocZrSV","R9Wr9Kttx15p":"i6qKgyvuxNbD","OwV4cNnRPUUK":"rewIIwN1hUrI","pp6xJ1jtPP0t":"bsyC1RsLP5uG","yN0mE5Fn0ZvG":"XGeenDBQNz3r","zfw3Y1uiHVgq":"N8o9CeRleMu4","6NZkBvmajhBS":"y9SVxQ0njJnW","TFe74miKrQ6p":"YT3xufbgx9L9","DG1NLJWnZXHG":"AybLtp5Rszts","7Lzh8bEI3P1Y":"BeAISi3wxZD8","4PxVflEuT1FQ":"EJIfpQIKaVaU","CLL1bfaGhv7z":"zaFlHX61WDPW","pXvHHUyhZNBw":"lnvBD58VtpXE","PJSeSRU7lld2":"TS5I6n2AR7LS","ezRLBeTYDzmt":"bueY5i5oiiZs","JU4InCX8fMbQ":"bOWqgEQyL08Y","gH54AuDn1Zri":"NkbT6gPUqkft","WUEH58cynzeg":"M24NErEhSa6L","ShYmJUN5H2oT":"ffFyLxEF2ZaE","KR3OF5vsoDjx":"LUrplkjTQrO5","WuHF9OGcmr72":"lYiYIS8qVgz3","hC1r1ys4cCT1":"AxXVrejHtXju","te2E6WovG2F9":"yMnwP4ev45rV","yffQ8xq6qViR":"o3zMNFvvoJGA","4EkbvABanSpo":"erMzV316yWJ0","9Vxkg47BXjaz":"gdVvI2y2pj7B","b2n8i2uHPSNY":"A1lal4qUDQxb","xQsaBwi2sfeC":"eWelMeBWV7a8","PLouWKLqNh1y":"8mtf6SQ4QyT7","tQzzR1MdeFHp":"80eUQxUb324p","SKMlcMqOdgRG":"x4sX7IhVTswv","ri9GArxgsqIt":"XnTEqWLNfE9v","XzT7FByUmRLb":"7wwze5tP6Gxn","XoTKAueQRrda":"EUX2COqOsSmd","xvX4ApMgNsHe":"jwJvSMIOcUWa","bVupfPQZx1cM":"0ntJ8FDQWDP1","EMFG5pgXmZ1D":"DpWehkbqXwih","ByyzKVuttGb5":"SyyWwxvWCn2X","R4vtAnBrG6HY":"w0n0dK9fYQCT","FdBiUPDg4GOi":"W2jROylhzARi","9iNuK5bL4gDZ":"rFlGX8PRP4dO","O2dKRIdb06fv":"wqeScyRMCP6T","S9kCn5OBsaHN":"tWI9Cu2aG8vS","Uc8gJOMmHmKT":"P9DPr7WORu11","D5zaLiQH1rhe":"rX5jG8wyXI29","D6bv3EYXuliG":"w2GVudpImi5o","csZEdIpvAbFg":"PxBopE1zScxp","N8AUQ9bF9ZXW":"UTqFyzLmXjtL","y5BhGlAwpT3Y":"rh0rRvMGnsIE","2mXNDuzYlMzw":"AO3CTSX5JiRe","9rx8dMILBMun":"7F45SoVrXG7u","Ak78s7LAWjWd":"xrH89BC9609r","EkcUfKGRMDfR":"9E5A0SQGIkqv","pybt2OG3Fhw6":"rkz7fydz5Vvf","8OxhXdGiRzU8":"6pV3nan0S6Ru","XUNIvCpg8zzv":"tqd3hPqr83MO","h7bAomOoQctd":"9HkLVlivX9qs","ii4Olsib8kEx":"Iy26yIiiFz42","oLTPEssFw5yG":"h2kSR4LdTtQY","23N9e7ZVAMtt":"rj80WmFV6dEZ","2gV92KIUroZK":"F2aDo4RlhtzQ","URoWEh3wtCi7":"iRYsBbn9rUNO","AsMTrMJu2aUB":"ryfkZPU5BnXX","cQmMP7NB4Ocd":"GcBzKLFlTDy6","ekgiVtt1iKbq":"mO4FGJlkjhRu","9i04n318FZfD":"XwU6NHffoAVM","mOmGP2KvsHPd":"AH5u8Zs8ARIV","Rq8xuv5Y92rI":"sOxoUqThBhRn","ZyknDRDB4mYa":"X7FspHIAhzKx","hb8xt1LbDaYB":"AGOMESayu4cr","2Ls0W5ac7Q13":"eONxBrrLoz0E","bgbaQKn1GVUb":"uLICL5nYdXSN","l9pcixgeHBFf":"ft348L7NlerB","rLH0DU16YpFC":"XkZRJLiLR63T","ZZEu32CbDgCk":"YJ6nFh4bPzJ0","rjQ0FRAVrVIJ":"x3HbjBmtzZaC","icvBI0NZavaf":"9NzWBaZJHttM","MiHh4XX4LtCk":"J9uBtvzaY6VQ","4PCoKZrSOwgw":"5MPlJPIfmKpV","tBgPd6I5mU70":"Xj44GTpOrYx8","06eTczwroHNF":"a195Fm7FGSR5","v3OznbcXyKT0":"5ymIcigbKwxz","xxZNHI6ml1RR":"TonIlikI5bXn","rvsXZhnQMQEZ":"yNgjw6KXa7sm","uNSyneMdRedi":"F97NKuCK7Ob5","HEIEzzAD7So8":"cWB8NuyK1FAu","nMtI0Wuzs7An":"WHDFI5c7mYYh","cUGnvEn8AdRa":"yQjjXtksFcQX","OlvOCZk3yHP8":"3BoEbf9E7Qid","HB7uuDiz8jRy":"pCaQ3k7eqhGE","Wj7RcCFQQPOx":"YMW3RuT7M713","7j8paXnl3ODR":"vZTM5A8ppGiN","kic6KF63PYn5":"Yr7McMgn2yhV","96y1CwROKMw9":"IUQ85zvymaLT","dbYfXrf6S05f":"gxVjgPaRZeM4","noH7ipgFVvAm":"B1d3eTac3qfS","jma4NU0OntgF":"KLOEWDflNT9u","Kbi8F7cUhDTs":"JRZUFlWaH1OU","BHV9FMdSJcEi":"emZnlF2L5IhA","L4Rls360ApH9":"rqJcnjf0glG7","G7jOqrVOri4B":"DxJfCb1IfOgQ","lrzgP6p00q0b":"s3US8w7pW2yb","pdtcchmtogaE":"sIvgXNxForZg","2Mumab9MzV6M":"AWXLZ0J09tW0","4mpmbTmL5K2H":"FnDSLPd8VR1q","VrByqSLVKtuc":"j9NyCpj55BBJ","bnDZgW6a8T3G":"susncfnxMbOR","n9AL1EU8RGP7":"js9Hu0pi8kr2","KMO21xbRt907":"VUmjHF9maZuM","r2RZgBpQd2fY":"aYKNmxS6IzEN","l93iSqkNCkpL":"70er3OwKyMZd","Ey9z60T1fPoi":"K90rMn7qN1sC","mrbhCgyfbedJ":"0Y9kHMPSHH1A","k9iphno9i1SW":"0DiiZryJoPcI","mfBEYDdBPEZK":"xapyEaxTmoFF","uoails6ju83h":"vE8nXPLSPUBb","SqJ5TWbOafEc":"2WfAteSM7E2q","6qc8SI0cWP7h":"iMGdbAfL8Qcr","Xx8g8ZYmKt7i":"acDR7cXaD8X7","SD4nb3ZIBM0j":"A2XghQ7cswxv","sY56do5YOjEP":"9DxhlSjC3w8Y","Abz9bMJ89Nuz":"vLjuuS3YCDyk","76oU77ITJDdK":"dui7q7milCif","czdwz4sMcpp6":"g8vaD7E21thC","TNHZLHCbVorH":"CuaJRbyhv1C9","oYmPZGCd4ZIz":"WIAChm18oJbS","UrAkUgJ4Ydfp":"u0syHDuaPUkd","64vXkTCfL5kh":"sv6NeTI74a1b","ssA77TSBA4kM":"QvlGQ9KTvBZL","3XmYdTpa4z5c":"BVqPILmuV3dx","sxUGVjy7yAYJ":"wcuqOvc0RmvK","caSmIlRCuSlm":"9Qgy86MQUzdq","G2XyU68yuE1I":"pV0qqd1Rqa6o","5yDD3XdjOh0V":"m9G1SFJ69aCo","qg8wcKJxQM5K":"Sk6wh7rAa8eA","RbspRGxjr1q9":"zLnx4x0wsl7V","kYmpEDbt2f94":"XqeEW0HEuB1d","RCg5pw9aunRu":"tX0InDq7vQZr","RXD4SGfVWUsu":"Viun7qDh9UNt","LQMN0RtSgJ4t":"xFIB3JVWfj0u","LBGuOPLKEOex":"t82EzmV62Dxk","ffMs9yjbfpBm":"hELoCM3VOakO","DPYQ8aUoIxKw":"uIGrm8LsKUaq","wIwbNo9fSTwt":"pTioBQ2wI4qC","jYPCtuSk2cnV":"ww6eyGN5qR7c","NnYeBVVqo26p":"pisefus4Iuu5","verpFOFoMTGt":"8zsBhzxYAGyL","gEI39lIL9dOT":"tgl1DdTSz2qR","choitjYL85qq":"q90azJEUgDZf","raoABlF8kDhn":"S0nXBKEVfouB","fdEcgYZBqhxU":"jN7CBkEZwAI4","Tz4loFvFmZxr":"u0w9o7mIs766","skHmgngh2FCU":"bAvEgUawe64y","ItzBOM91A1ij":"LywNhAupfWjy","6ub5ZeRYX8yR":"BhL40zJvG5OZ","lDeD5m3JKdBE":"M0FzXO5ifzU0","tlZvNX8OCZSg":"3KCCkOGsnHUY","CKpaJv2ecl4Q":"vHI16KEUWYdL","PEkrJwwD0az2":"WLsrcZ4PPfI4","OtFkQW2P1kCl":"399UieDDVT9M","1SNtRXXlS9yU":"sgRZHQgUJIPG","IZjw09103aX8":"aH2Qqq1IVUrK","g2Dzes0YKYFJ":"rR0QxX9k7btJ","L04GbuNmBFIS":"tNScIERuCWZu","vvU6bKUZsTGt":"4xfWqdgnFYZl","yezNGXaYBB70":"vfLCF9inAHXo","UiPFMqVbFSzz":"UqNWHjtlGYbU","CA47jUxlLX74":"HDigEtxy12j8","rpGMpAi99UMY":"8ggpiaBj8jrr","HuwBn2y0fHUv":"fwJSS28nM3uU","bRJdJe7ANkQA":"GbuEsEdbbtIP","xTrd6TGssfBy":"g54p8dxUGNpn","rlHVBrtMk2PX":"mNWbtJY3ko9l","vUBH4RpLlPZk":"mUiRl8krIWYr","7BFwycxBUD5f":"8FgBm0OSyPyp","AY6uQGSMaKLo":"m4ZCN0qF4caR","GOckZHf0SUfP":"7ZRfLOhcx8mU","Me5glzI8EX2R":"hwY4iDDxRqDH","rxyo6eMT0srw":"0JnBBVawdcam","6LMttFzBjRyU":"0xv0uB2MAdlt","2Js5W2epmDfb":"6VW0Eqy911Xn","HBk8Mr3H0D9i":"Vxl8s0sYBWJL","vPxIyg6iWQQV":"9O1BNjSXK2z4","ccEP5qgjDsFK":"vUsQmEzgxcjE","5RpkLd2RYb9z":"PrnteHmMOFzz","bfIt6kNHFwmj":"2hN78FiVFisk","XnW070jHyhFV":"5HDRt3AaBErj","b0mkJLwarwsH":"GwnlbN1RPs2F","qrvmJv9LIrQK":"yEwXcpScPhP7","N4TO4n29FIL6":"1PCqr9lTZpo5","IRWt8WORB9Gh":"PFm8FV9xEJGM","1DOLMYZkoB69":"FacWm3PjlFs8","zHtmtkzpE6vV":"OJXRpnGR3TDg","thqQeiova0GJ":"aJI510iI3Egv","IPCDiXb9sBRl":"dvP8QhATncgl","R0TRxT99Qknv":"b2kT5dWIqkkY","MkpCcakH8olA":"QVHYPZrdiHBR","AmjHJYNdgxLB":"gUSPY48FLlzn","6Mo8z2kYxTXG":"81K3RyqQnkzN","7xLiYPUwFRvx":"DLXzHgN77Pyt","WCvuS7Y7fchu":"SDrmQROKjnLu","lEy6n9wGvTQg":"nFz5xNryHa15","8K7UZGdqELFk":"EEJimvFLam6v","rKr2lf2BuOIU":"asbZ7AP5h8TQ","GehyIWvkg5Eo":"2bk03PL7puqf","byzMp1z0aEiW":"hUVrTZ0wphBr","J7lrTO28oeli":"mooDy9iFJwti","sNlEDBprlrdI":"fVS71akij7xK","N86aP6d5ziBf":"9OjS90jRMpfD","Jiz0oi2UMd9D":"3N1SALXAhsWP","lAmPKuR5xHjP":"yimtWxj1okKZ","duz1pXx4NrAg":"kW9tz3u89DDo","h5AW8IIXp1cE":"JJo2QOWdOfJz","umSi9qAQPbnr":"zLftIbGf35Ki","9IkRmY3fw18h":"AqEyF5wNfXF4","FHceXb79jvzC":"PNVfGswdi9Hj","2uXfGO5xiMNh":"v2qS4daLNC8e","mhbnvGMqt1qc":"5HVLCKubcaFi","EOnZEnJiIuKX":"EsXZ4WDLA6ZE","ih2ajlXmYCwD":"Le0lrcw2sqgx","WSQuIeDXJoKb":"VO5SSXVFltYt","HMVAtiT2Jwl4":"DU9LnrFXrQ7v","CVYw4KTOlOta":"nINwqOL3tjln","p3eFFAVOdIRb":"JqnU4jExXtPA","kLZz9CGIzMcA":"e187PYFA4CGu","9xpyuqrXsCEP":"VAL4uBRmvhde","7wniFSPs3PWT":"cBFCIetMGevX","2T78tMyvesxT":"GK0AMkkbsRlI","DTuIYukJEBpL":"6IzJJCRoQoSk","hsWoUODcMyTQ":"adWUjHWxM2vt","kyL4YZrZfRzs":"KAkDYctmrZXY","sNqRgALiG9v2":"bb7zRev3KxJP","lkrssYBBxZ6i":"rhQWja2vZsW8","pKiZfGuQilo1":"zEzJEH9iK6Jk","tATOTTBuR6PA":"enmmqzYHBhFU","R56yrMTmWE41":"I94VzuOv98QC","hIt2JFeBYniu":"qinYb40ZVquV","UbXXOsKyK11o":"SU5P4AypXeZA","HiCumco89nkc":"wFNJlJNHGUcz","3gKV3G43C4Mg":"muXtTIQB7FMb","BCaaVFNMlcTg":"5WVg1La1cBNU","D2wFlhae6tuE":"GTWOY7REVGrm","nhYuEEl7DP1Z":"vk7eUvxbBSnh","M8gmLAgtbbGc":"fI28Hdms2XaG","wSqddPoTC7lo":"VXoYzyyFndKm","ZEnzXZrkuGww":"t9ZNOWcaTzRf","oeZjwWS4zfnN":"U8INIUWBx8Dy","TjgGIES4KdKf":"oPMZIihtM10y","V3Swz56pQDNP":"VuDcLz2xC6vX","GbLGOsaXGbty":"jKOw8juOPq3C","XqKl9Z4nLF4P":"IP2XXxqTbmcO","IxYLaqmpbVOJ":"xVCAtTcZyM3J","eBYer7gK4QG6":"3td9PaTxJtdh","qExQh9anCUDA":"kc7WiJcUWOxj","ElmjAmvRVga0":"pX2rc4lt8KU1","7g9kidyJy7Ri":"ERjtK2UGHkRD","mHthBxG4nBkt":"C6QJ14IxN3BV","6wiL4LIPPYOp":"d7NAqYuAikuy","C95E7nEGlpHB":"5HWyn53JOYLn","EuCiqxUWOZv5":"7AneDmjrtfyG","itufpbarKSb5":"leHbbF3ahPAK","PDDRZSfRqy1U":"y1ii2X1AxDrg","xARnkpdhcT63":"Hn98nBjr21Dh","Cnjs2Wq7Tsq4":"zlSHalR9MZkD","iRDjqkCoJ4oB":"M9nsAqPjkGSN","v5B2t9smTCcN":"C5LvJLLojjVA","dWny4467OcOl":"wUH39mBUO2qm","pmnlAbrKiI0i":"SugFKThhgry0","HqBeKL6DOFva":"tPDWAEOG1zA7","X6G35nIuzEc5":"dnaPtgZySJfS","NtAVZ7X8sFWF":"sWpRFJAyQHuT","pkrv5by9l0D6":"9E9uQ0LcU6OI","Ebeyi4IGwbMw":"YFdAGguoatOK","hdqVAI9mqNz0":"qCQQMQL163IC","TIj2cmCeYDmB":"Vwvk2V15EMod","1wMwxvfY5vOo":"zNWgrJOUFfP9","jIXR0rJaU83n":"52du1jbFhJg4","9EhCdE1ZotuZ":"eCyszQuIKFl2","mh0vQER7nOoY":"2hiNonPAQBvC","R4s3KZAqVn0E":"3HUjnCPQXvLG","DjVUYCaUhvR8":"mofHB8eJO0uC","S6XJq9wiG7Ie":"XWkVK1pZXOr8","MdqrB0NDs0Is":"OewVkosbIlJk","ODDxUPO0SD89":"fNp0bsFhAx07","2G5EeysNpf1l":"4fjwhQ9BUb68","Rqc50nrDJTso":"7oJxeSM8lES4","HPlf3g7t5jNn":"t8vDVBU4lgPV","HkrJup3cBcB9":"RHcB9iG2797E","CwSXfCp2WW8M":"r5hmEoNdowpu","72bsleRcx5WO":"BrZUciwXUlTh","CCiiUvhmDVXx":"VY1uJz5sK9nY","FXJxhnbVg0nV":"8oaoywvya2QK","UReUbKiUYAcQ":"VY1Q8vmysyg9","7xtbIBLVyPW3":"Tt4mgdBIYrze","IPd3iEDc8bpW":"IN1fwaDZobB3","IcirKa5cxKrq":"p7ev7MpVusMU","Gj6vbtTkeokn":"C43b1qRG73Qo","ea2VWOdsfP4q":"JmjHfldCmItp","l89NOJXisVcm":"Qzdxvm9eAkC6","7aTxPyqV61yH":"37ld8ocRPE30","APKNaIZdn54K":"pHHXN7wMeCQ9","kDA7lIh4buDG":"fcZtzZ10aQG8","Ubn0yOOMVH1y":"S0U1VXLZ8RjF","HeDYc02KpTfU":"EHP2rSpGcVOB","mwgY1twjXgSl":"OQJq5veJl1WM","AH65Ezl4Jp0a":"hIoz72z9JKDb","FvyxdIpbovVB":"bNylEzBpceGF","p1yRDasVAOCC":"dODawT3ZCA2l","YgaE4npRCWpr":"3wSgt4e3NhiS","BGueSwANKvsr":"UsCvwpYkvuML","YwYpaYZRtqHP":"q2QyaNEedUOu","jSCwlySpewOU":"rWOzvuHp0diU","TX5vWZcxFNKp":"J06zS0jdFHFx","vFwQYrRqhfjj":"WJMu9bQzD8Dr","QjbcVRl7Ly92":"iWVczGyOPrhw","7aQXMHZIWbTI":"JBgRLik5KV2w","G2fizRWTzK6Q":"FEO3nXWI2f39","NZZMYLLBez4E":"i5T3CLsocHbA","S5aBvF7HAgM6":"wQr3Ib7RUz7n","PVeyb6Yo52ai":"UCP9JzUvfEQk","HcCEL7SAGibP":"5BavhOGmRa02","Pi8530xiB14X":"YopVwbJRtqgF","lh9rS8lmed9n":"xrQMmAj2yTuX","jWtYo77U9UtN":"0DqutkjipXRH","CqWKSAZGCBJ9":"nC1IkKo9mZXg","EKnMIly3Yons":"F50CIDVVGO2O","fNpM4bLvfFXL":"W0Tp62B4zUCA","fU0oOWxH7NJs":"TeK0iNcL5PTU","AXobsuPYqDjo":"y1S1HnFndkCX","sHkq5Fz40L7c":"IrL78bO7Jzgs","1wOPVjSdjl06":"WTpxJbJa6H7R","HpultlnyexUR":"L8GbcgqYIXrJ","3wwHgyeZOzbR":"3Kbx1k2nSUfU","lommq4ihjBLz":"3AVOjieYiP28","qtjkwSiknJd3":"eH5bxAFTbYdl","9ZG09NF3YNVm":"qApjqq7grCem","vYL6n8rLU1Ic":"6sGPs0rHzKF3","bp0wFxGXtzQF":"smdycuXoKn2O","uqArKggTVpqE":"pbg35Yi8ePKZ","ff48ZTGBnu0G":"50yCWmLIGLiY","tZ11jtYWKE6q":"VIC3iw4hvM9y","MigbONGOleZj":"8rcbJ6YJuiHO","eOUl9eTqiUFZ":"sdMIICCeXGzl","iwYaOfaYF9SM":"wnBO4FQ46XEA","ee1Sd97FNkJY":"v7zsDVqc1bTU","TfZe8FSDwjgY":"2kyE0n45JNNi","b7rgEf2N5brb":"jh17Ltb3W1hE","ou5NbjZojYJ2":"yoR27o507MDU","wfJsn041WVZX":"QGU770s8IvTf","yFdXV8WabQRR":"597QeMZY6278","15EEwM339esb":"YvPUK8nZOpaq","fsJlmndelZ8B":"DiCP5pMnZYYs","x6tFDiOZYuRn":"BGSBX8CAILXv","PlRrFROvjr3x":"PhCTW3aaWTS5","u72jAbkSFelF":"5ojzkYiMOlcw","iQsRN9tnruNH":"ue3u3bE5YzIv","fZKkZxOXASNh":"pmIAHiy7NFag","2xy256w1eOy2":"7QnseENahUWL","gohO8jQU9nIF":"kbJ7Tnpp6uic","sJxfVHKkqZvA":"huAKwY4fgKHu","RdGDyfakmg3A":"Vj3DQxeA0cgf","KvXCuztNIyjc":"IdbIdh43xDir","WvQ5tF4U6pPc":"DOfjNHnEgVSw","rtImi0FUEELC":"aDQHyCV4QxkH","96hDlOeUuRmG":"p1ysLGRbfVCg","M3TdYxMZU7Og":"Yo7saxwU5YIO","8HPfNjzg2tT4":"hXDsn8jx5WcD","phhoeIuXivGH":"a8hhUh1h7kgR","dzJj7VeoPp35":"vxCAZGu6fK1d","C7FMHcRoOLRf":"YmnFSxQ0nJmK","MEF2pwCXfZeM":"OLoYR10zhawB","h8LbKtFrPt9G":"6cNgBhqS1kZb","yCGhJSrCZR4Y":"iJ9JEoUEi12I","rmeUsl31YQcX":"r6ahoKlfnt7S","yniWRz3bYQzT":"LIfuRGw2g5Bx","VA6NS9WcK9hj":"jSCKcrS4Fjo5","IZYYh6OSV0ZC":"tD9V1s3lVQum","GMuCjHSkrNSo":"4HYvoT7n1EUc","oIGLn4CbhMKY":"39UVijuUcexJ","lNigppwvIL6q":"ffNy5bnHuFv5","3MbYWFXDAcO1":"hrCJGua7eqZQ","oZXSzBimx2qT":"7NzUNyad77vV","bduHczJJUslL":"fLlUaPzpLJD6","BSw2Sq6ip99p":"40YX4Ia1NRX4","5SJMBFRynlDL":"GV8FQ1zTYgoX","vVPGUsEAMQPF":"PWHsv2hPcKwh","gyxRnkK2NmMc":"zY7lslZkvJX2","Adbyx9iKv0lw":"LAouQqHK1Au6","E30oGqMFULF8":"amkwAPY8JkT7","6SkEe4sEr4up":"cADIt83U8ERz","6xRPoDjjvkrR":"DrfAcQx2snme","6MeMvsC5aiIt":"YNm5zeTQMB6p","axzv0kUsnfOj":"8oa0jPDvjxti","mzflfpaywuk0":"uXcWzZiBfPIO","ZQfbRV24Ixfg":"GoNvmPjJRBzh","wqH53Szin1Nf":"zFfb5zT8e1Fw","iAKkDrn5KDrD":"s9ozUjdvm0RH","466ZALmHeldv":"Jl1STDPd3BTX","bCmKXE2BLKtG":"Hg4pBegft6xW","UjMlniw8JwXZ":"WWvnryasP1HD","lggysodmm8EJ":"i1jbRCvoL16J","un65QfxS4gg9":"T5mc6RfHnRMj","SKUCkqDQz0FI":"Y5Hin61u9czS","CcQYV06wtFDU":"wWxFFc35doTv","pBbSOg88uJHn":"yPZYdQbmPcS1","9nrFdAaWAogr":"bPi6pmtPUM5h","nMTfApoF5kKZ":"meVo4i4THcND","n98uvYgexNEz":"hDZBLGcdrqec","P1CGx5odd076":"LXBFkVwzIvhc","2cOYb6OQr9b6":"9bWdaPs0DMNq","LloPlsLBYXI2":"0l0HdT0T855a","TqZa2qOzSova":"282orc8t9d39","9HWEKAHMJxOW":"trUIJNTCtJpB","dt4kps38hnrw":"qwvF5FxQA5WE","Y1bjAL6foCkm":"KmgZu83QX1PI","J2vI3qn4kuNs":"BvBHyNXQQV38","q9rruBXEb3m5":"cLQqbkbvB2cX","kPGCZm5Gccn1":"8HAVjKXAdVoM","B4BdxNN5N25j":"NeqZbNjPGRu8","0qTn1FYlKw5Z":"IFSXBskpiQlb","D8j3MYg37fRd":"h6BJkoZwcBtV","y5roPefMzXq8":"ISKc8I82LDWr","iKRkdQw6IemI":"jrUusE8eMzoX","VIrVxvuboT6V":"E6GOJcWcuUk7","elaftZHIUavZ":"4kF8nvLBWOjs","mJInC6OhyyVk":"PEH0x5sZLbPZ","bAN2SZ7CaZOT":"KPXqqFq1oYAR","llknHPUh9zgj":"znbV0JwfQuHc","KEq16ccAz0uM":"ysMW3jWV46Iv","bgWqCL3BzHvp":"5P5vdQmspH4a","teQVvyWt05A6":"vyiIIKbcvf31","UJJ9UcHXKupR":"pL8328FhvUKz","8gShZjV5qPJu":"JHNn1z71kvwr","hOOlQDE7quEP":"QkMwl5MuJsXu","1T8aMTnHHnTF":"Gsf9i6A8vR8f","3mDcfL3G4UPx":"BhBHtLGsGcUj","nRG7aXfTjon2":"fuxTjyb11Hl0","0pey5anrgfOf":"bkJ36E2tz1m3","sKBc5bc32i1K":"M6w18gEI8iyq","TJfNf27xmnRO":"ENAVnea6TjXJ","AaKQDKUrzeur":"ZACGgb8dtMM7","A0DK5W9S2asi":"YjTNiRv89uAl","6koiEfBUdJu6":"M5f4Pr4I6QSj","OSmYJSAlWoGC":"2vgajWCTN20S","eq8lRfaKUtIq":"ksQb9ycarcRu","vsleQuGojqxL":"mfieEfp5ObDA","JGmv88diFBTO":"bHKmWHFaUdC9","mb4DrsuzvjlA":"vpj61D8QWhzp","8PtT3rA5zqn0":"oBRTUtBDh3YK","cBpDG82jq0x2":"aouSnCLoyI2p","GgayPpMOhBb8":"wc7oOZWGCzHa","PF93k0tZrvuL":"plNACLChNL0Y","pWTy3bN6plSR":"8jP5UFxg96jV","pkiU6PMk0ZBS":"KgkuCaXz4nSI","9n1tkNe94ReK":"mlkdF1v8JrSn","LRBxbURW3SJ8":"PNNfliSBFEVw","X3TZJuCgDbkh":"4SSu74ZNn8ng","KStkkOgMQtX5":"ZTmt5xXAj0zI","68m6npBkAiTw":"822HoJ8Injwr","rjOoVzq9VZxN":"hg5J32f7eNmo","OZJQAhl9P7GS":"n90tNspoZ5Cj","5WzqD7VUJen1":"3qvVl0WMsPM0","gxn8XmGmARYL":"BhtaDRfCwTck","a1mXUmT5FWw2":"Jvtu4K7yXCMm","ro7tIj2ECNvX":"t02SQNWgF5cs","QU3Ha2QiaF2G":"EESWhbkafn4q","yJXjiQpQUpWq":"WB6TQLEFEcOH","UfmFlr3o4MbA":"5QnRVOHFTIn9","RVPFwoWR4FoA":"QgVv03p4quSb","fPXkEaI321pa":"ykXf9Qvutgaz","7LQUNYQgzwiZ":"whG5UFFI9oqJ","iScG7249sKAD":"ER93seGpiisC","Z9CBO021Ntf9":"0BX7O9n9k6vC","shFnCmrkWIVr":"H3RcPjn5aNQB","HYwdEPqlFWG9":"jI05PWserXya","CR516QgvK9j4":"8AWXHcts2o4B","tdeiA2iLSjip":"rDuMfELqi6RP","ia0IBKwGynSU":"cfpqCxSpToD3","Mq4jLja2URYR":"SlSnbARxL2Ih","tANphM2GaLUn":"NTYNvSTKszC1","JHYPPQiPfZUl":"FLskesIlSYeB","Onb28l3FtNr1":"RePQITzhYaU0","6KEP3lOA1IGC":"kPDrW7mEyKer","895URhHsCze9":"litskmCrmc89","dNqToKuTFVKk":"Db5CdUwGvfgd","dYGcFT1CLcbi":"8pBEVBlC96Jg","DyGtL4Dp2ejM":"j9OM5xm3jFE8","7ttPNHhV3wqS":"2DKBnlyNfEvJ","GIBISgiM0Q5I":"u1nkEm66iJVR","bSzZrCXWLNVg":"XeuseZUdhmsf","j6vEFSOH85qp":"sDPGGs0iAxcB","Y5JMcLYsPmLb":"wRAOWuihybxQ","NB34MuzFQ4VA":"rrdtOH8CZV7c","SsVwyE3q6eIU":"l1tZWpMIQWdh","JnKofonxi5p0":"sHtbWIAUrzdh","flKM6c3J7uc5":"Hj1Yh87OYgd1","4uTuacIkRISb":"cQhaUXR4yc8Q","gO0FTYIKRKGf":"p6dIADfzUJBe","RPOEgNkOjGm9":"kTdTMR6VlQGC","pGkqsWagnvBO":"DPqv7teoGlWI","HhZKuL4kCmw7":"y0gLoC3BUsSK","ttL2hXKj3jW2":"T9l0mcy6QlIy","bw7MLLKCtt7m":"dmxaed26E2ko","3l2zOawF83Ah":"hlCo9VlaHta2","DuIN1xyB0vjK":"A4x4E9oCKfbR","wIbWw77n2Nly":"XcAUir4jBLbR","MTAGPuUsE8DO":"B3HUvJCLY1PJ","luKr9QNtNWGY":"mouwALtJiwk9","iN7gvVoUH8F2":"ds9rwYs9xw1x","BzdOAfUq58Vi":"1L1ZjVFJZFxI","o2D6rlkwlyn5":"CS29Em6xlIxi","4cvIsRjWzFSP":"sbJny339NzYJ","03De2SXO8Jch":"95lGX02Nxau1","VdRwp40uJdUi":"YqjNIirOYDnj","VAhyXk9uKF1e":"qqkGZhDDu3Jw","ZgpDey5FYLMo":"sQhdK0DkI3KK","ZoIKq4njR2ef":"ibkvEZQTAHhD","MVsnvNzwilOW":"j6SIfxfJA1pG","IZv9iEqVIKqx":"bjhhpxukR4lM","o4DKIYXmnLCm":"JZOKcUEz4SYz","2gIvNszEvHbw":"Jl6tskwNGKY8","REwDGpHajHwq":"0KGS0S4yYVpj","L8ha0kHE8xl8":"P0lDZy4mAgC9","CvkdTpTAf27T":"TlRsUVPYTTxf","IV6LVjE29t3N":"kOwlkeY0bHxB","2xwMztpIxb8w":"n9jHZ1ZVWZ2s","srM6kgr8gv2H":"tMhUsGjtDE8S","boOFxc2WiuOm":"KZcIiMC9MDkb","B7CqqqfCJhFI":"L3Ly3ArYQb7k","R60ljjaQzP1J":"xBLXUfxkayS9","fPzNfrPIdLmJ":"Y6UTWVRclTkZ","tAZAlsDVEobH":"p9hbIvKyGziK","uSzDYOdubhc2":"RJ3dN3GlPr0K","oX3ll1DtOxdD":"8c6sIWiSTIsI","7YT8C75dKlGJ":"boLynt9xyBeL","QCAYbBNRDLGH":"UORoBlxqOSzm","dwGJpZCDNB8T":"3xICYTosgG5F","64RCVk3uTxLo":"K96HwiWcfsQG","Dys19sDW3smW":"0bGKHETnKOes","NarhdtpNDEuj":"amT7jrsVl49O","lLL1OtybhX64":"FqP9N1Ft8bud","utbgHF8CCQYX":"aGBxu14VzD7V","1Ubt6BF2Y92o":"mlEuMbB0vZrg","2Kwqt2hgHLIX":"qBpqYq9YGVRI","GXjKkdMHKws3":"SqOJwDxlzgjH","3e7eDl8Kp5nq":"c0lyEIqW5WBg","y7J0wkFCObx8":"99mb740gubta","vYsf8cOnvMzo":"FbmD010CGaGA","yTFRL67DTW6y":"uGswludXJSBI","3A5eGEbyui9Q":"rmITmDcLdShA","SULEUy3sL6XP":"pRkSCVplJYgp","EWfQRDJdetcX":"V3xMqPqAS3eP","IssjqU6giVhb":"wUMBTIn3T9CN","BCuVX7XzmkdQ":"nrv4j7449f2h","so0KUaBZOxED":"nNWoYM9EVAwz","y17LyLR0p3CY":"fzwSbmHlcnY7","6NhgYdLl4R6G":"2v53jTSBpMJw","wVJy5Kwt7Uz8":"KaCGZY49giOu","igYewuIfjSFT":"B97lHjNZI1Cq","2H6lKOT2ahTj":"7F2UKgYAdv8w","H622CVlMyJ6h":"7FAnkuwY7ugL","bj9DMtEoJZeO":"TO4RvsSsQczY","SCpbqQxcDaPZ":"exBdObQ83xCB","KHiAet9qkXuU":"N7sMIJ5JVybH","ffkigZPLs42Z":"DOKtLi42lNOy","Ih4UewStXxwU":"cGkPQJpyWWpY","10ochKOvIvpF":"bvUSA5eGFzm3","CXf9pm1e1t54":"BtJfGyXpeSEc","XbN3YRsz71iv":"iIT4IVdMIOOC","BrdJDjaOn90s":"MDbV2oV2NaPM","k6Qjbtb22yCz":"dkIdkAoVPC6e","1DHUPgw3un4Q":"fKHiZq9I9WJt","CHAy4B4iNJuF":"nXzNIxhUi7f8","iCIsNDZwteei":"YqRA87f50xoP","DHlD3TjYcO3g":"6M20QlS3uBLs","THaGotuskHFD":"4LcPwiKbIStR","kcR8qoV0Mckm":"qa87qzRQYw0Q","W2pXiCVYnVxf":"7WZhcugNThOM","H7MIekQ5JTTA":"vBaaD7MSJTaB","6CXgsUv90KZP":"9d6XOUL6hKA2","I627rwLmCVdw":"TptPITCnsQyK","Urn27NsIvCr7":"uNM56fTf4P3B","0WcrDg6fejkb":"Mt5S7c2LP70V","N0jAhUO8fuUi":"d4eM6aETAdR4","WJxU2k0rlKvv":"PB6ursZi6AfC","PwQa8qCYicRx":"0u7Gbz2avXIM","bYCCUZJG9Jpi":"FTJvzWHrcsHJ","ciLhEaR3G7TF":"BpMap0aSknKY","Nit5BI0TnchV":"8bmUgK7dTm5d","79i4ytFhyLJu":"gr5gNEycxIQR","cgpGPys1L5Vc":"HfgHaFb7cnjp","17ODiwj1feNI":"fmgrylV1QUFu","0kxCI2tHnuwc":"zxwOiioCfCPK","Fa5r9goLV9FX":"8mNUgIKgeqLh","oda75lZ6MvTG":"RzgFExFsf6k6","Pr2m89yTIQgi":"RtXH5axJQRSW","esCtDx5pcJ6i":"KekNCZIiKZ4l","DARTphWafoFE":"qxXwavx1JPe4","gYA0R8oes4f2":"AcsAxUL3pMDg","Qk2J9dJ732Rq":"pOWRQ9sVcESN","nosms3FGbfv6":"tUX1cduy0kra","pMqMNZnixNT9":"NrXbbGTv4PVH","2mjZBJ0a31YA":"B2LnwVFKgyQv","D7LQXlHvG3C7":"dHSJ28zX49Kx","qSgA3HcAPgNa":"gkZ2FvM2cJwM","gsIpoAPZ3VxP":"hIruLOdQn3TM","wCSNjOATQvPq":"RIBhDRCaq99o","asgCapO8NwAM":"IxLFVvhNJIzX","YfanR9GSzZYx":"Iws5Xz84Jcws","eLuat7sV3R35":"1rmQF58qtzpO","DjNCXRb6luZt":"7feImjZFa4YH","zEoAj3JifFPD":"JZvrXJsHpQsB","Vc5X1sBTfl5S":"B7Gp6FZycDgC","djR5H5j8WhpI":"8jtMn1pbUQPy","BxvhBCEMd6Jf":"Hj6XpEsWjKtT","PSTIzxxPF3Uv":"cPODfj35Y2qL","JjhdazmEzHet":"Xgu9N6AaSHUP","cQ83irqww6To":"drqOSZEZHQqt","wI5Xyoleo1A7":"G1ODULsHyQIu","6CRce44CncHp":"YUc6Qu92Y3pG","BufWMUDaQt6x":"Lb6GQsTrl8py","9q0Avi0YmQe5":"8k0AF2iyeZaN","oKs4HBUum2KQ":"kd3zI8FvWESm","8SJy5HzFkPEP":"uNKGMiqaDlv4","ZdqCoBng9J8j":"9TgOo24eAATr","O9UP2MckepzZ":"fmLxGqChaHcl","ErQ9wtTHVaG9":"sBUjQN03ExCF","QwQoRlDs7RWp":"Vi9Mhuasr6ir","O2pxuLlcqJBn":"iJmPkPMvK7vf","TPeTHHy3cxfE":"SbFfZDgTyNvl","Ys4Y2mZFFdO7":"EuAd1i8ijhGk","h1cpZWlY3vXf":"0GfWYlzQnaEh","xtSmnxwJZyt8":"saQuX7qIayw3","gODYJwC8NRY2":"oSXiCWjMTW60","cz3YhduZ6Q3E":"ZfuUDur5kAoC","qZ5smZe5JDIx":"lJtA5xE9PZvm","1FvNFvppF7hb":"9IvsgSruzQ95","VyKkxpyUoT62":"U25BjVdrHJYl","QyldYA8nSzCu":"ICbSfSuMUkMA","lKWXsWUhWaWl":"AgVjsyYFYl2S","GHlxeRcnS1hc":"J1lP0giJx8Om","RYZrTw2yAUPo":"AQlViudmfWRv","7lZCRuiTXHLS":"R3ivLnGwTJmu","DfUA5EFt7IGL":"nsXv4ge56Jnb","V73QMxFwHjkk":"Dd5KpmYa68Se","dVUfjH3LYeTX":"HsgGZaQfF84B","KobC6LxNjQ4F":"VPSHq2N9LGOP","f7eZV1dkFWAN":"d9W5kh0IzhMp","iYoUx4Ibe6gs":"bXfWrb9mMgd0","yetWni98wlJS":"h5s6bXiLjnwj","6V8S9OgI9jc7":"x9UKWaeFOiT3","H9fLoi3Ygwn8":"0kGu4UamL1ol","xS7egcPpV0sr":"MBYMufslg3po","HgctUnphPaal":"2cae0Af5w7o9","Ok8dvvzSBIqs":"lVKp3e9TBuC7","PWY6Q9oWfnTi":"Lc3Eaf0prxFZ","u6I1fEDl2EC5":"Umgb4z1p6olH","9bV0JdZQhpdE":"KPBy1tjQBJvK","VNiGjJLuiI7v":"h246bVl26EN8","FBTsRgdsQAVS":"vbSSGHo9hmHd","BT4dS4X9QsCa":"MzCqabFQVGvA","TsBLyt8eG6nD":"yCSjDCQwwssK","Orypa4PiTdee":"ikdrfNPSFV7n","eisJZ3xT0EHu":"MSV7dubp6kjl","BDRwLxioVzTa":"7oDeiRgrJTxI","OfoWqIjxxOBF":"yKxl74ADwlII","5HEZ6OkoMHAZ":"HFMr6eRQGnrd","VgEZ6mbIljQm":"jZQs6TLgzYk7","XDsn3PC1ru9Q":"nw8lEHfei4j3","wv35F3NYJADt":"D1dFtQFegdpr","OKhwJ24satmJ":"9iF8WsBIDBrw","bHNmPstGsHDF":"733FNT01ZVm4","9GksAmHM0hIP":"1hsWQEy2xrLM","t9hpS4NVm3lo":"MZcBNQ0Yd7lM","Q0ckcmrQrwEP":"JZttGYjVGnv8","RSChp2AcmH2T":"4u55UYlxdBQz","jEsHR5ul1vHS":"frjwK1jiQOM8","nTuKVqJTirqH":"Wj5F16aONwnR","sVNdo9xfSvnK":"YJnlURsrWKf1","vvvVEYSV4qmJ":"N35rcpuO9VdQ","0H5TZqjncqEt":"EWjSDWqw5XRX","YI0le19uMIzE":"2e9emYRjXP8l","VlW2tQUUmXkG":"dotAbcJLqU4g","EjdYRGCpKBSG":"PJ5zGMGdgp1o","EAcSWnYRgRn0":"CqZPrHG7NAwR","4kBdSyFNIsdi":"IKoNuoFaxeLM","2TG0h2o96OJp":"c2cL6zZjOtMa","Kby5HS3Yxxn9":"1NSwhTJQxP9o","ESKnTJxKT2o5":"yxlcnmQbvcWF","9PqS5awkJsOi":"dGWhbBOTzVRL","c7NtSmXyHqVx":"kqvtb609y25h","yZ00GbRbXp6X":"lYMN4AhWQWRP","xdOeWBPgcmE1":"cjbscyGUgaTG","YgJK6MT3aOtc":"RBOYOTqUcxff","5Fd9vSK81Hi7":"CF1dZgcWSDvO","oc71joGrF0A5":"ggbrX9pqDGL3","R9OHpIDe3bt4":"FzEyCul6Zq5h","JUCKRIP2f2cg":"jP6dMSZgkkP6","duyvHiDoVRSQ":"ui4NluXNDzzJ","j8mj9zJcgW8o":"KWfS7SX2FU93","Y8GEBt4bznuG":"CCzPr8NuqwRa","gP3lPXA3yJh7":"T2C1q0T8hYAU","VsPeho0DUUuo":"sVfr9MT6wHUa","AaZNqUVuw3xK":"DbRTXikAMuh1","XUVpQKOiRwpq":"VZEHo8PKOqLA","xGFCtZ4HxihR":"WYcq2W2lze58","0kfe32GFNgnR":"mXQttDMFJDEK","GkfV0Kfy3wtR":"ZuwZlmFfP7N6","0KP88kvkH6O9":"yw56CMVILiCv","Faacv5iCzH3N":"mq96tBDgxXxD","pmxePKxBDoPM":"yBZXdUqyCo0U","hanusAqKOn2c":"hnB2V1C5idUN","MzA6FUIRZXcP":"sglTRskV0RzD","lAq8aapT3ov1":"xxNAnpdj0tcz","ffbIKoIioPJs":"ZCbDAwqONqE9","bdDigYDf2zBg":"Fp3C0ekpRlEj","HEx6UOjdmPnn":"huP9UscVunpH","tFkszxYTMruS":"QQHRCexQdRLY","rExOjI1HOskO":"LZv3Rd5HnSQO","1qEusIzsoYMg":"6XYOOUf8tUGB","Lawzdpd3G5ge":"7rGXGkc7psBH","ugcvJ0ndvpF1":"Y6OdAarnyypZ","dCvpaZApNRwY":"kT5KVZIyswmm","E6s7SOe9tv5d":"ujtooxa1CUpe","e9u50R0SqwAs":"MbpofCj2BRhh","wrI4SQ9hkqyK":"VKe9fMQK6BK5","NO7wF9GaRvD9":"GFQTawtHmwKQ","LjS3FrOdmgDm":"nZcaGHrnU6Sk","e0ipHPGhYS8O":"WxsFElRz9R3F","rCWGIKIIRSKN":"iW3dnkEu0OON","i8OWcPmTNbiY":"81w5vpWTQnNm","DXY7LVnPpMCj":"uPQp7C9FjfrK","wVG8XwVchKBx":"c8cME3RoQqVO","ezCOZSHYZ4sr":"zUf9ZkpKjNDd","oE2waJ3LUY70":"czmkParHdaDW","Z297kuGSqV7j":"iPs6mHkG0OUE","LP7UjAUknq60":"7U8IZ7iPKmwu","l6zkZmrg4ILH":"vPLzQH4UACBS","l2ECmZjwE3xn":"OOc4Gk4kZP96","9d5yJGLWNMzR":"f0D2sX5XWmYj","NAabSQBZ9OrN":"GbgMt09bjRQt","V92eTXbkhNme":"H82HWShZ8lAf","gaGfXnJDkUbK":"6v0go7iut2rZ","wgaXRkWNAFzY":"GMRTuEmhbcIc","poV2RGhdwMRX":"v6rG0dilSppL","orOAycseiftJ":"Asdul637FBCQ","87HBipuSPcZp":"5oQbVKaAybxs","IMil8R06aMhR":"t7eCzHVenRd8","5x1mVLtNecC8":"Phv7vOeDicQ1","Tk2MjD2At50e":"Ynzax2UzegKn","g6U49jVG5XcE":"QPFI5fJg7aLY","fTI6aDAJ51Ct":"AcfkGqQ0H1ij","qzn3mVteOvRg":"dt784MtTGTNn","3hxCZ4cXzG2z":"MmVA7QG4SEH7","t5jJ55msQbvF":"mycUNVafXyKH","jkz3ckHmIM3P":"WzV5Sct26BQs","DGUIaKiANBkI":"XgzCLsWgqK9Z","7DXl35VrrrU6":"ll1Vvt7AGR3M","hBKPsHq6vJs2":"M213OKPNKNRT","3rTqu16TRoib":"cq0Na67TQ7ml","xG3oN9PpfFIF":"WH1Rh0AuoGkI","So705gP737fA":"XSZ7rjDt37An","60cLyD7qqHnU":"csgeLhAZQeaS","GxxSMpqcKNWP":"ZjofUcM0NaYa","9GA2JOjrAUBQ":"DoZfCmd27SXr","ecSJ0EvVSIhO":"UNAGcPnomd9A","wrwi0FvGsuWe":"j6AaETOPx2Db","UnOv3sSvFWkw":"QaEHCEWfkdAG","llWG1d1CiIXz":"mmZQtq9j9DMl","lBv7QlOJ606j":"bwkjg3NeZ0VK","Cgy1B8LIvdTk":"4hiuQSs6E4pY","6rz8K4FImtBW":"dGxEfP33ASyz","gC3h6pFgPEak":"y8ConVniuAU7","sMNCGjIZsiaR":"DA1332nSeAyX","NzCGVbSd6dsD":"gVL4PIBar8mW","F7n05A2el6dl":"pFvuSdYxPWrB","BS7OOTgUG0oP":"edU9Ubz45UtS","2Uc2r7ujnBqh":"bUMqTZisiSGf","NeuosPltg7JC":"u0YnI5U29pZi","NRQI3fpAafVP":"yoot0BZ3tK9m","ct0XbSZyqNIi":"PvhwJyNeuUy5","meAc8fhLgdiu":"m8THdhPAkQyW","CukNXluI4Hlz":"0kncbdqxIldH","yAweT7wluxWB":"jAAj1zOCRFRt","XENzQ73h1mJA":"ZoT91pNmfQNt","EEE6k4o5cGZ8":"E1wpaMLQKEhu","CaCchmDhbtSQ":"x8TZHBtikLjQ","WoTO39ACGBMV":"9Eb9TllrmuF6","QrEP20qY0iG5":"LBdr9CZVsjWt","DIAdpOXNhBW4":"6gbjUBOF6pBm","6fdSNeremLyE":"LgudStSBznGn","7zppRdvIxydQ":"t1QAMju5Xt01","PtJtwDiVQVzh":"qN0ygbwnJqRh","MFH3QeLd7cFB":"0Qlrv6T4Ukkf","FT9RUBlt3ClB":"OErHOhlhRboU","5er0svKmw50T":"ty3T2tKBCKl6","j4a3SeQFPEya":"uUPvzyAo9Hii","rGlW7MOnhjOY":"MhgRw8TAZn74","4vb5vR59TdTI":"tSqwKmuAkZ0n","zBL6wkHa46NS":"osdXuhK3rQRT","SnknzOicc6V7":"TZaTbSDjyq1l","CqiGNXqyJXMN":"fa4JZMaHA5Lh","PET7g7mljDWN":"klVvfgGcmrbO","aDm8Wi4VMSNZ":"Ry1D3ImXomv7","4KQPyZ4nza4E":"sTjKvyj4Uoax","L5uqcXr7TZ4R":"UI2vI6mn0ru6","WU9BMzmbCxX5":"t75M3WbxdJpl","zx0TgA8ksh1j":"tgVu3TiP8buI","Br5P1RiSkI3I":"XpvIaYiNlr2d","2isU8PRRqtWN":"F4qFdE0Iz9WP","EjcuXR4baJHi":"yv5xYJK86KFm","kGjY0q78prF2":"V3m9wCsQLqQN","EqrgjT8j720R":"AA7Ow6nRCTUp","AibT3zLOIjGB":"82xDV9e38pCM","CTBbKVtGzx3k":"j983UCfbfSua","aXYASw0rIkqL":"70zD3hZU9vU0","YCUUuNqBZjEo":"RHzhSzfUE6cS","4WbJeN7Nfqto":"QxSIHc0mSkLh","2vHHBXp6FSf6":"fPtlCAv7vnJL","CrXtgyNd1n0E":"NfdqJDU5cPN3","I8yhUQEbWXL8":"jLwKTIrXiEry","w0HVqIdZDV7w":"gjnbf3d3BhR6","bzXFL1tk3Rtn":"zrqe2yVDrZzS","S047wYsCdIOZ":"XP25QuStDrob","ntDRzM8CnOQM":"Fnp9Lv2Gn2bE","WkILjByrTxJn":"MTkg8zGeab50","rlpeCmv0idIr":"xIo9mlR6RFEb","zcenyJYTKMvu":"osT2sIEZW0V5","WqsCnmBBo8Hu":"6kqfWaOhJafe","YaQVb54Lg29h":"ZV4ptj6RU2hf","TnOjKvLedUkY":"84lNf2s1lp8N","THvGTHeziANQ":"ZPHsMdkN6yr6","ue81SokwNIk0":"QPA2wFctbr4B","TR8D8NPOlSGJ":"wOyRUKEXatZb","8N5GEmo8Wn8D":"fKI0rmgtKfmk","py6QCHcAPMAC":"j0Paf2r3NbxQ","wqnYmGatO6F3":"zqDYFH4Xn9dC","uRklfz1iIiGn":"RvjVqT5qvSKh","WLAMGEfGVrXs":"6dGlHuEEN277","vSj3moUEpNDm":"QCJqeljl0OwZ","fQcyVkFjkmkN":"QuUYv9VfK2cK","K4RfkIgIz2MM":"6hOLjZJUIOg2","hGjF54PbwOPQ":"sZAX9hki2QoS","jcEARe52QNfp":"aQ0EzsrvYweP","dXW2gXttpNw8":"73TRKWgbjy1n","683htIVwL3nU":"40jnjt8i4fSJ","MwvgZ8Pos5RL":"2OVyQOzhRD9Q","QeieVuwrqo6l":"BB47MeQFCSBz","zK4ytBQ3Abmc":"UM99YDU9vmMX","07jn973oLyB6":"1mmTKQ0vaJqC","Nv531tr9LZzZ":"pq8WfalHEvqI","49gfhVsFyeSj":"gAGHMn6m1Fj7","DzNIb5KYrEUJ":"ipkDoBnpZAzZ","NCpq7gitxyZL":"37brVMO37YtS","JMwl7wqTcaCX":"EpBaMuzgpfwV","xRSPLTPXdUdM":"8onxAMzk18Td","tHNSuqOnurYQ":"u5EiinNEREZk","OGdCYJlXugVg":"3nStRmeJZSHi","4ABAubZ3IWdl":"e5DKnIrBytOE","ddfQF5hopVly":"DmiB7s9msoNj","SNYzCwOkx6kO":"oDZpDCOCan9Z","z2phXMT2IMEy":"dnWeTTtPKlcY","TQ8rinGtP74K":"i9LkHfmghe1s","MUkLPFVcHqAl":"bs0VWtBhPSuB","esuxDVSqUQd6":"GGpoWaWAAeBR","kVDDhVIYbxFf":"ratXZz4601FU","8vFfQeX13mJa":"PapXW4wcTrJc","7lgDVOFuMfcP":"rAgVLpeMGKoG","NyXFdsdLadvd":"nfM7ywRZqQZh","VmK0ysp1e3YW":"BY0YGBEqRqsW","7kp0WSQxT940":"0zATU30msDYg","lcmngSnfR5Ad":"tc0BwCc6TnJ4","cbS8sIPlTorF":"8osZu4N7G4pE","GArwYGx1rqKb":"MKI1uonP5NWK","JWouWoOhUSEI":"1JHLdclLXnF4","LZ9KYG3EezUL":"RA84niNZt8US","7h89O53u9HEA":"ju96n5U5tQ4Z","lpsUAYcozUM8":"KMEgYX8bXRjM","0JkfQck6TlTB":"ZYC9qwl02XmZ","AS210YRdxIY7":"1v8AYXHEpqsz","jv7AElkontC5":"ElUTg0bHg2DZ","HAcjBfXZeNI5":"ERWozMphtEwl","VvCbWjMuen9p":"N5JbSmyNXQLr","SLekFFhTWIZt":"JDuf6uM57ccx","bAFYIKRfSRgS":"TyCAKgNMbC1I","e5xdIdHyE9Is":"3nZZpq8RF7xz","BauSd0HtIh1g":"QIhNGh3fYVfN","bAPouXYpN160":"RWJLtFxHaR5P","zlnOzkX8jB1I":"ev2vUe4mrT7n","9hZaN2elyWZh":"KPvuPGkxBYwa","n8ofZ5cyaJ2N":"6beX5gLlGhXl","hjuFy3Qy01sC":"sD2nTiVsU7oi","qf7OueMpNfGy":"ogA6X0u1YAe4","z86EQrynvrtm":"8mZB9JYBhieh","krxqEvQxn7Tu":"fWCLQoSRQiPR","RpyE0xRcPjh3":"8E9RMYbHWMeJ","MqLsIZlZ3wDa":"AM5HF46w0T6x","AjWMfavKDGGf":"5Gl01rbYYiku","DfMAc3EoXMUA":"VE4ANFGY2mJN","l51t9rmN2ykP":"313R2Y6pJeXW","8Cy7JjoWVlc5":"7x2iowSpd8dM","dBWQCnyzx57C":"KMnZnpG2bLfY","wpUxahXraTGB":"ZlN825RH7lxK","6OkLAry0dB0W":"mzWIgxjklaYe","DJXrUSJYrIHu":"j6DPdcvIOYKF","b6h6H1mLWiuY":"vNkE0ZzPiEDF","25vU7vAMXjXy":"vD5KXNnbBks9","c1GfUZHmBb9U":"01ZhouBLGLQG","H2Oqsc3lZqEr":"jFFesPA5FfVH","G3frmSgoxAzy":"1qFcrRKRkaBq","itRAcFfdFE3J":"hi9EWz8W2sDr","2vHd3AWnZ3JT":"aZ33dUET8qGb","Vrebfx8ZOnfY":"yJPc5a8lOd72","XNGVfGGu0Tb7":"noPgVsMdaMSb","UzO6Nmc53zCq":"6NyXYNQwTWiH","UUy45smPBjsw":"Xsfr50hk6Apc","OYQqvKVyf35p":"scjuGlVSZ3tx","xAiM6niJEonw":"g8lxRFH3V99x","rc0x6DijiPel":"NiDt3uFygxXV","KWJ6LkofN9TI":"0cX48e30v72W","wQO6ek2cMrrQ":"OQQeCxFhztIu","KwLLN5XWrc4x":"mDXhhBqWXRNv","KTC6KE6Nmsz6":"AzdSclJhK7BO","RSwSVtaI2hWm":"tVGzb0gjYEci","1Kh65ACq2hIu":"NaoQJfWgh2wN","w1zBzChSB9VE":"xz2eBzj6K0Nf","TnWDQPtGanKx":"php30mgjOgnd","5vPYkI2ctXmU":"irWSU2MqTJlR","A6pdUOt30guA":"LPt7XziTNDCc","fWerjqOEzmGJ":"SE76IuJEJwa9","XnTYHrZLnhLj":"VjlxGTv59U73","j3j4dmgMvkIx":"q7vHDj0oTxvp","40UavFPRs5q8":"FmHFhCEosI00","czMz42z39whD":"JEvjz5LIlnym","qPY8EpX0JyA0":"oAARlSov8IRv","RBD7iHQMQYX5":"9doW7hmg74Xl","Iz1qixuZGMBx":"aJ2o8RA9OSjX","YIFvP9Mxc5Lm":"RpvLz43z51Is","QovF3aInxV33":"MtZEdymFsRQ2","mrPMIOTYWxhr":"AEBRDOYKvYXV","5ldN9S9nuRiA":"avFlJRhgiNH0","gU1mQtqt7aKB":"qYb3mr3BPia9","GBAXNqvVVs2e":"VqMiG0F6jt43","NFaVIgJduecX":"xZTgrecaRgJo","rb1Vw0PwRURP":"jnHrvlTj30dg","H06oOpmia6IF":"1LRt1MuKjdn9","qoje8NOci1mM":"QnI2FKyv13ey","tH3b6mvQypND":"6Vovsjd030Hh","YYPiDkJTh4K7":"9jmyEzqXJQ7B","ojmpNnZLirTe":"Qeof4ZpMl0nE","k5qx1TXRXzyE":"pWui43qvCOVs","6Zp1Kfaf5rSZ":"wDxSDtyP7Ocw","sP6t2S9qjsC8":"tgZMdL4KRb6x","T9TInJLYqrFk":"cbYpNwVrmvmP","YiirfwbVJ0eJ":"P9IBn7DqVcKT","wXdFGS3qU82b":"byQDMmF1VByG","CD06hzavmcrd":"rmgbmmCpVgwQ","nzkxljhf5c7V":"Koxvx0fgNeAu","0dASmtAKVan1":"1mu0i3gLwMIb","DYCqZ90EuY1R":"WL3PyqgjokNV","CVjlDAyQLMqN":"42qObysBepSG","FSxif2MXpInL":"nqSDRuhzY7os","zPEdVMcDkyKV":"LfbD34wn2vFm","nrh4w5vOPTtG":"ATOd4dRUU6Xg","lPlIF3JddKrl":"sPp7gwSLoiPw","xRzq59RBIAQD":"YUb8e9USm3a0","ZTkJoJGMahof":"KQoYo2LnuM3e","5Iy8WCt6wFId":"d5aGrqbpGba3","zfnOLyJ0g5s6":"xsAFgiwZPc5G","WIVKaLSLCIoq":"jtzVAVFxwioC","CE9KAWKyeyVF":"8UbMZRz2P5vy","H0lfUhJZsEsn":"Nwk8qGoXoIy9","veLaQ9md8Nan":"VGYDhVzLtkPB","4Qxli0ZTyOwB":"is1LjnBtgVV7","dwPqlieR9MaN":"unkoI8uT9J7b","gocXvtXCXlIx":"IObjxCGvnFVA","TrohIwtKEbA4":"denr4XfLG91H","uy1YK1xtynDH":"xWflMD9kHw37","g9WZ4abnh8qM":"SggR8QjtogJJ","rUjLWnlPdOn8":"Z4V0L7AoRnjJ","NTn3zGMwUNcZ":"XGF3wsDBxBvc","rvplT3VRSw7S":"3Gq0AIb7oLPL","cAIXoXDIP8o7":"E5NgH6IxCcdD","OJiHODBdNprS":"cL9H9QRWxzZb","r38R7W0xxgp2":"3OQyflOsCupk","1cz7aT150ODu":"ShCIZ6MsWWuz","kbMQ4GekcnVO":"ugDaBwcj874b","8InjefD1a98Q":"N6YaF9RlWWkZ","T0h5Nc3z4Vcl":"8sKgE4jrjqwt","iPXuwMhi9UZv":"GjNw2HJpDkyH","LCQaYY4Ccz5x":"uPijHS8c9S6p","YXzd3Q4duMwf":"XTpCmIyLE1hK","4SPl1CUfjSkr":"hj7bIo0CGCr6","HlfX0f77OTHe":"0X0b5OWZiWfc","m6Zpc1KkfhlN":"OXQgAjJF9985","W6cmrgy1pmIy":"i4xMKTnQIC06","2LLzLWsM82TK":"b9kMnjKox7sz","kNz26M1IaOie":"rbolZGlKQ8Qg","E41pVGUt2clh":"WqpZOfqYmbXD","37EwqtEH63vQ":"Ljs0wfDwTJqt","6w1w9LBi2GfT":"WXp23McuVsZI","ka4P6wITqfO7":"1w1ztA7Ps78u","Ky2oN26e7Jg3":"YZtPD7UKDpam","qWvfydXdyvVw":"3bTrTD4vNHFU","NHniLC7b7o5S":"nLV7EKsKAbvr","FBc7sKnRqibl":"vCCElKrPBiqs","iXLCQMkBvAtV":"xfw2a3XOYqSj","d6mA9Ox220oq":"gXjSV4Ytpi9R","JiM2q3piN8G2":"CLHHqUJ8sfqD","54h1fDbQcFLH":"IPYIDAt7vvHB","fdLX5doYHOxe":"AcTQxSkpfKft","N0tvZxKDfeyR":"Yc3klWEuuiIT","IlagjQ6LTiKh":"ZdUk9ClbAbmr","Erk8y6b8WuxX":"ocPBeRbgaiQF","9klLva0ryarW":"97o3VXF6uT8e","zkiuknjE6NTM":"rWA9MuWVM8fn","3nyH4cA1L5Z3":"Id5oYW5ZHrKM","ADSXuATAW49P":"wW3qMUbeN46n","P4C91ppoELFl":"biwYFATj0Km8","GyqRLTnwyPZq":"xfst8HnPeJWx","CRVbagskqHpE":"dUb33rJ3SxoY","knwpO5tMQ4XM":"UPrij7Qp4AnN","AZCxzXFSmLO5":"zk0SZfgkK4j9","3PNMuVlYfSNe":"zUAqpTWCRG85","SRkpQtMTpdAv":"QOhvGsAcHHYT","ren4FAfQ9K3h":"joetIE9LQQr3","rWGHnSMYLDZz":"pcchqZ8TPzaY","kQT72wtA1bhs":"LS46sUzzYShw","jhN6luGiOofv":"RoLMVfPZRIEE","a4QuCJenRooR":"nHaVyItItsdN","60K4bGgb5tg5":"iE8wdqbfMYPW","qbSpxNoXBbYu":"DxrRseakn3zY","Rx9VPmYPw7mC":"6HAzEDz8aPBB","DeQXWhMH0Wa9":"JBpMwbzVTFaR","7bSDSPrGeZVt":"L9W58JulqvrQ","ETgojrOUoBei":"MHjRAtWtTCvh","BBg9DJObVqqO":"wJJcGkmSvQKW","E4LCrULzonOD":"qyFNUF9lAwwC","vjXPdmBH2B31":"QQ5JYL8HJN23","FjbsGOnQU1j2":"KzLmPS2p3k88","YfQ3CmvlbWRV":"hA87vf5oAKBC","iYHWWlFhsStd":"JVdv1SQIySsP","rAdaSDTZUlcn":"etXmE7PbTo2u","MCwh6qbfG4yN":"6IDO7drR9TrB","1qwNzoecTJrU":"Iy2yBvq0eZUf","6f2C3lg0BMhd":"Q4qyiaDbjawU","D2aQilz1JmSI":"OcuV1JroXWPR","xol6ZlawBmCF":"IivPFdXYiA78","vA2g6ENsFfKR":"et9qFBxc2f3v","NbQzNKibjqmE":"qjuyhEBHqBgn","zTlUzBSsW2sJ":"suyYJH64iQ18","86Zo7f8lWkHS":"DuWcPvKQfvDX","8M2IBkiJfPoZ":"f4v3lMhjXxPl","zo068WV4hC7t":"QVsp0MkA5FfQ","s2IVKe7wT0FN":"wH6fcqqWr3Gr","NOiPjUIgbVmZ":"2mitvpYADr8F","0kJX26qtjM93":"VFEyCCmDjrUf","OKQYRX0lOOM0":"bxabx9P3XfvG","3TMfWgzY6CVM":"HikZrh0JCwwi","BHfj1joLtiZy":"uJ6Eb2LbjTgR","ZRKFUfSVovBS":"GMwkT4JGTtzM","rHf6GOIkIdLv":"wUaLS2keeLE9","jMsN1DnZG8jy":"Lg9E4pSdNG0O","j0tVBlG8UVi7":"T29Tkmb6QblI","5IFNWlwzfLje":"cFu9P38XcDRt","s5rfhW1tqlw0":"YpzPlqaumxIM","oOo4TCn7I0Wn":"LC5QxPA0TLV2","TPW9xXpRu8WW":"8nkdrVDawQ1m","GlHoxUgjJOd8":"cP8JaoAKSm7O","PKnAExzxj3lL":"cJP7Od9OiEjC","1sF6UJjUiXPO":"gXrGZZ5OTmSJ","vlRoUJatGfxt":"2H5yBtm5QVxy","uYe5wJaQyb4C":"hg07LoSWwZtR","m4CbEKp54fn3":"nDRLuYSewhdN","DIPerQc00k4l":"vLh5G7uhYoFw","Z4QA7S69H04W":"3i7fnmfqBz4n","QeS75hASCbh3":"taVdiZTv18rt","zw6syC2GECZJ":"xInjd4tY2Kgd","sgu3Gxmrjmrk":"Tb12ECR2ECBK","VLtsYmVle2rm":"EFm3rT5DMXfa","YF0WyEgHeKlO":"ctsLomrmh0Vq","I0TdVzrkfSoO":"WokVi35g2XrO","a6KEwATI6mjc":"qoAWJkGc15IK","ym4md3rDaume":"yABg9keSnfcS","u1W37EBog5iy":"VIVOOtGvtJfD","dorFaIIFZQoQ":"1guAbhfYeIZD","A49abJr3gojp":"qC285vIigwBu","H98qTmsuD34x":"y4yIv2HBSJEJ","7YPurob3eDtB":"0jYv36xgRJNg","8PExapcgSFPF":"nY7t3jTVljjT","WSQ1pF1RnPAN":"ZPXi5HG8OC6k","F8ouJkgYfp57":"qvlZxvwnkjIw","xRvZnCgYyWI3":"7pyH7SbpZpW8","GUemI7wRHgFV":"DF3DO57ZuKHp","Nky6MnCgkH4A":"HRZNttkVCkuT","I0LU07LBQBfv":"kHpy6iT6qG51","fNJ5ZwlNho5m":"Vom4JCohReD1","Jna1fIRKqxWT":"LwhdttNitius","04JtA6unHtwL":"O71QTPmvRVMZ","PnIzWGPtiAbc":"TRMOqRGuafDk","SJ7MpN0c4voy":"aGO4TZN7yoEW","QP441PWvHwMr":"jGLtAGuu5OfU","evrX4LP4GhJD":"5zLsZsYATTbz","5dYI24DM148d":"HxnQFLZEMmAm","DxFBLI5mJjQW":"nJzrhUvdNbh7","TH2KmgPLqqFj":"b9YgIROsjEF0","sN9YyeQfDnpJ":"VL5dithrKo60","5JoR1GSirzun":"dh52tLMIQyMP","plU8hdqS6EFZ":"g2nZlOgAU6AP","ssIuRBf8yw6N":"0nl8yaXVtJPs","MVQfUuywd76M":"eZTuNmOPJCKT","qwoAYue1Gh7y":"ueqnmtScvJS3","qv4YIrKk3a81":"e7qBBUOLd9Wl","LFR12PcXrboh":"DRu1LevhUoPW","UhF7p7LhYIvf":"f932Cx71VGxw","4iq0O292axdQ":"iPhKOhuAN0Le","72cgYCpWKrz0":"WYXvCQ7eVYxi","NMwfLbMxUA4T":"GcZtoc1oS9SO","ROEuiP0DCxy0":"2W69Hs3oWlRn","Y9OQoas6dkTT":"QbJ3E0rnykam","QQVczjrxJQn3":"3iC1luBfORYE","xR8yFx6ZGzVp":"frnktGj6qWbk","hsVjXwIS8d7l":"aocwUzQPUgdQ","FqZZOc5Y2qYu":"SpmZebo2yVuj","KgsEL2sB2ABt":"cy6efS8glx6W","KKVpj8zopMu3":"mcHZ8eUaWyYz","5WAUSlE1FJSF":"Q8NsWBnseg0a","qIlsA5N6RDlC":"6dbRiKZz85q0","1yT2Pfm7RTJF":"Lz37n11dTTSZ","UTFSfmLC0ZFF":"RV9fyNkBX6tc","2XA4QwgYBZBR":"GMvFexKn7N09","RnwC1WFaJhMd":"sdKt4hqVW5sB","umyCnuYl6paC":"feHwjP3xk4Ee","FThjPIuExMDi":"lQWABs2utAfQ","KEe6tyeHUNul":"lpyPz8xkUeBx","Bauane2IFnkB":"jpZ98yAB5XJV","PsnlkQ8gaIsd":"wM0fBN3NxrtH","Zwl1qJtmJZe7":"rgPcaaIbxmVP","ABKjPA8uJqtq":"7z94aoONEumt","9zlJtpwFLarQ":"jF1ORBl7vX9B","9oxwUL2jrl25":"S5O4AqlBmULI","ilxd5CJsoJGL":"xQI7ZynIoIFk","nltY1z0Z9h8L":"IlKXEQCAJIZi","14m1Pgtxc90W":"IaOzhsBKE9Pb","G92guciWzwPy":"cSW03td3G3Yx","XP9m3306VdoD":"btIrekl0WpQ8","yP1iAqWFTqof":"S64qgdpPVmH3","eRjW8DmheKJG":"V9N9Z6NoS2rl","7KB7EOZB6f4H":"itCDZVTXhnVX","RYvJsLVPYZkW":"jTSA0qiTrvat","oERWftpfbYo4":"suEHiIEv1axP","d2mZpjZyPbU0":"QRGf17oUjIXR","F3riNMqV9wNd":"SXbsPNIq3AW6","uWJxmIurkRCf":"fLN9E0zJinEZ","MOHgiDoT8Elq":"CzJTLIArHuLP","SDW8KcODtIQb":"GhFa74u9Mlj6","MlxoxvEleDgQ":"BYYukgWrvlM6","NlZaJvMKaOa0":"VshAqoGAjDa8","4pCPz3fuT0OF":"N0Ozt4VVND3M","bs7H8a4BiXtN":"3JIntOKqRVOt","jtdbuzI4GtRX":"zu4C4GNoiW9h","bQY7MXiFKFh6":"2p1HJN3T2C9V","OKQE4hQ1Xknr":"Infwh8Ct3yFd","D1cQ5nTEkzsk":"UYd6Z1AJOCIj","glXynFsCinaO":"TTVRaIEUsqEc","nJkmWxsouoky":"I51PGFgMtjrs","aBrTe9o53TmG":"lcs0pWTtOKKh","ZwdlTi3k1nxr":"OkXILz2NPI3N","WR6P68Ts1ziD":"5NIzlsiPea89","wddlKJ4c4C92":"16XdgRh6y4aV","jVYYYpSiXKWk":"3qVtH44UbKOv","FOHfRSvoDXJl":"bOmNd6QrhWA0","X2Kb2UDg6luR":"mDRGvcdRfnIr","g7cSW6ITpXvC":"e7wAZHxCfnMh","rbGoxgzA2sQd":"VT2RbfZx1YVs","wChvdAvOQe54":"iHkXyLDu8ce2","83CBPD94jvRH":"ucX0PxtQ4kex","EVohFPnoIldx":"rEEDr8AE9D8B","qrsCZztuDoGM":"p7zmB8StWGGj","yT1oxlUwQOMq":"lSRsl45Mfjig","BaWJyQfS1nzu":"hcybbf0qf9CM","OFAIQ3m3FUiB":"vEKfXM3J1war","5pJsV7bNSh6U":"GSFxFB1Tms6W","VilO9TPQD2V0":"V9MxKIUxmISy","ovmHj6qztUsA":"QSoif2MtnoU5","vGi7PiOPGWgx":"v9170SK3S7j4","k5SKfod08EhH":"mzWqY5EezSqt","nd0Tq5RkafZm":"apaDx4sCW1lP","7uw5gc5j4SSh":"xyOh5QMehOKg","7RFCgh6NMmaA":"dgmGCFkSWzH9","0kdDy9R8eQnx":"P1HmzY42KwV5","VyOEnqcvEQeD":"gzeFo6mR7PzF","eJ8uqYWcsmaF":"pJOKNJ9yDgDm","TMiBu1avix9r":"oWAfUHO3Kbqv","FugwXJLWaUQm":"cxf3p9MX4rjM","8b1kfZiLd8Ux":"PDuSZWoOmV9Z","xsXubNKocLas":"OtOn7YGy31hD","wGKlxoUI5vCn":"1AJCD9l1ysDF","fmHi89UYqJxu":"ihl7jgMmSave","PhOKFWkgFxLi":"8bJ1BeSpLUoH","UvvzdQvXDFPW":"9IaFwxwWmJhl","aZgG9KO36ZFo":"w9rYnExAdv50","yP0SD1pNr2MV":"3L3XjxNWFRLa","Dzbukim3Qad3":"zhyM8zFxQATi","MkEf5ykX3Hpo":"JUBw1vdmJPG4","IZ14lNcum345":"0OBhIdtlSPG9","k35EzA6A13fA":"S5cKbZEo1HRk","WghvPtBb8CI4":"7FRTCNHcnrm0","oEsBDOHuHlTw":"BtiiPRIb8Ts4","q0CREHxMri6w":"KSTvmmyrxdVf","iRVHwohbYC51":"o01DwtUTFscd","amy1FElbhUNe":"zRwESBBYR7g0","uLBHduOsvZJQ":"s8bDjYjwPr49","eJn0Lr05YBCk":"eUGreIzRDM8s","7GxiJR8aEAzb":"CdQwbaI7GcPh","ocSwiAI4YDXD":"JImeoqIonKZk","UoGJz8GMcf3w":"lYx7KvTW0hbc","gPrLaYz1Z2ir":"ULXg1oBnVJac","7confpDHXmw7":"LPuFQA2A2kaO","LcXkGO5Q5LUw":"6NKqEuQcY1Fb","hXdtzOeHIsBM":"397KeabbzZDn","wC2RGtCSEbU4":"CXjPymK9aPyl","gbwxSy5muREF":"OznTRq0Jn6Jp","7X34h7RqqUwh":"aR0Xqa6TDFTl","ZVpat7xERziJ":"WBPVIjWgAMHV","zCujm2fZtAVK":"9KghSBeIsXHM","xgmUnqMsUD4t":"x4EXl5nIStiA","VzGvFXquDtqm":"jtct3Ac4iXBw","8tzD8hRvsNAK":"wVadXergTnuv","bAlQauzwXO0o":"9FY4YgagLBFi","7txhbkpsSCPF":"sAZwgjjIq4lt","OHdifqsdsE4Z":"RF54XT5b5Yco","mtoy7Uo6QmdN":"XF3jvShOHSAt","0g1eMVAzozQ2":"OOlwqN35MshB","1At2y1ZAiMfO":"TDIbYEA8cTYp","7swXOYvcQdGQ":"X0elYtACmXqT","HYvrsGFB9CNo":"oAeIpU2fBdxR","ozxRh9Bl0Av4":"B7Ju3hNE0o54","fSE0MVEL0zBa":"fcah2rm4HAaN","ifo8VzdjfWSy":"D9a143vpmC02","S40Bj7Ori9qy":"HbGhPnmdlkzs","edm0o4QBylXI":"O8rh30RhAIxU","yedAYfoXxSIw":"yrDvZuOBerYS","a6Fk7VrGxxgW":"1PgyPx1IbAEa","v6KVAw4QrJc3":"6wUUF8qESuJJ","znrUv20uNwlA":"XFmNigAeGTMw","7jcLJn9LRuQj":"qrUOR8ZcBkjj","bNBCefk4KRkp":"QHUgHr9vM3sI","o3kziNIRWTZP":"TVeNO5V2YPfu","0S1lb1POIRSF":"el6CuP3rWBWR","eNqPMiMVHX9i":"L1XW2hOnX0B9","3YVJRYtOIcjf":"f4j4eZiIcrsH","A6ZySmwzX4j0":"eTjZd5V3qUoF","I9flEP3NitZD":"gjWJDbP37Jck","4PKjIft9EiRw":"ij66QyYzZeEZ","D1KmEmx3NGXC":"i0NIZJzY9hVJ","RiL4qBcp9uR9":"RSQHZpMvxT1w","TiqIvZRL2kLT":"miS2dIp0F6l6","hPfgqOVrh9Z4":"OODPzv0ZPcX2","FrGRGLSG0N7f":"8f3am9l0iQAy","DhpcwVWhhOVz":"NbzQxXxFgM96","Q4aZfXfbpdK7":"xO4hh10cSKnE","xM6qMjEvuopS":"nTc0fgDovGi6","8OsFQDGQTHML":"bmm1MfBZRpAc","ChLRsdICDfiV":"0vn0zk2AENA3","VcYWSfyKdEHB":"Fi0XyxV9cZJf","W7xpvxCkWSxK":"EHyeUDPcFhhK","amefJNtK9oam":"5WdTLrkCJhZm","zSBgNgcL1X6P":"SEhN1EdPnuua","4CrVAzzUw0pj":"BYiN62fgw2SA","p0y4Tq0rISr2":"x5eoOGvztO5c","Oa7TtSRyY3vw":"kEV5hUouT4pp","usrZDwqPa25J":"Y4Koq4emDABb","NEedASn6U0la":"IJdUn4OlGAQ5","jqpUIglB3OET":"e6sJeW0QgEEw","olTO7A2rOFlz":"YCBElQ2rQqbu","YidrFY9ZYxRz":"C0a03fDzzG9F","q6M5EHcuXzZB":"EnZrBT1QLbLs","A3Umoh4lWHAM":"J6q1AMGlRj58","EdEXVJ9HRO2s":"f7i036CaFkRO","FZGVnT5ypZ5S":"0WFb2tV6jhpx","jclTwnoiFP8u":"hXdCJvAz57kW","gWXGKhpZ5qBR":"wAt91Iet7gtz","HnsMxWugAmfO":"pWCvferLQApb","GDKLb2HaBNnd":"PDm7R8ma7l4b","00rP9fBxEjPh":"hDo0nOnHwzMH","6K0cdth0CWZB":"rgZCXAZkECFu","5j1qUD0dXZjH":"NeBa6DMa75s1","tOo9DzyS6Xfd":"CnmbyKmkMv0h","CERUqiB7FqB2":"VYHNQpGbrulq","55Pz6AsTKebz":"orc9IvL3TxJE","9GYzeDZhr7n2":"QiRmiETJ8Ywh","9LGYxJyowFEC":"cgk1QZSx2tNN","TcDmHaxwJDwk":"DfRs4QZLXUYI","dvimTJrtPpQu":"ygWvzILdA2yC","qveAwIpGcOSd":"ga1o0IcErLAb","UVknM2LVQ7ul":"oJpHEV8YUXGt","GMVnPaj00Auh":"YSPIQ0zqVDDX","H4yQoi7uGO1x":"6MneQHtERNCE","OTgIz15aunb0":"BY1CEXcddLGf","W0nSoXMTUCbx":"gs1N87kGxPPh","777hdU5sNdrS":"cWRpG0W8y05r","AVzAYdRQbYNH":"NgrimoJ74mvY","zeTLvXXZJ4ja":"Agv1HhhwFVmw","WIkZYDIw5P8F":"8F2jsKRsEiLQ","aXobIoIaN36r":"hYahheBWvosn","Akov0K1ea9Ot":"ZdTrrq9LnmCR","lNG2uIo5u6i7":"zDDVOj0a1LLw","rBmV9ibGJsh2":"3om1bgH2q8be","O7A382IV7lhM":"zG2WErtN5PnO","iJzPvhDWLzc8":"fp39MUg1ipvQ","tMMgAOsamnWV":"soGZ3F6XoOST","3i3wy44tSUKS":"JtY8aM1zsY8Z","AZEq0LpoJOr3":"sImvzKYn0Agj","XTZpXyNkSC5p":"6Xd6sqGmwMsN","RwDTwJxkFI5E":"zf3LSr2ZF2cn","pFR0pCPRQoRg":"Z9PMN41J5qky","sPFbO8iJaOnl":"o8nAhH6zK0nA","jKtywJA19ni1":"P7X2BbOciKjI","cx0xrM20vLV3":"O2KndkC845mQ","gxg0iGw3xPTh":"NnNB52MfosJ7","pmuP5zipRKPK":"i4CQbaGeQ5in","QKuF3vVZH9MH":"2WJtsggB2PMd","4vVw6xgWw85W":"crFvSXCG9qkF","0jlZkNCfk2Kg":"LEC8913F2f00","67O4sl83fiar":"0ysLUdZ4lNxn","hJ6muYarKdOD":"mJQmdpzcBvKR","Gz7bUoiN7PIu":"H7j22CVjXVRB","mXAcjnQj37Q6":"ARxiJNzhSO4Q","357oaTkIuuqJ":"CkQHToV6ckL7","4rluSXYAQkA8":"PsHSq8r3mksI","KXeT345eFQXI":"qAaercWc9IPK","L6KBdnpCMtlk":"W9sarQyiyhpx","yAw1VrSUtCj5":"YHLESWmjY9el","B7RfuuNysYqf":"wqOjJ9FRb90j","rSENkBdB7eux":"Vp2Imk5lRjvs","8IiGKtted55y":"YHNYseqt1G64","QKYcQ7WwIUFd":"Z8O4FtoX5JkH","T19M6Ko2Jgbh":"C9RyiReMBSS5","iC1Eekv3EzdX":"RxKigkNMIcOF","U9ujOd6suYwu":"ickzLbhxifPx","Uv3DPBYbHCmF":"OmNcbMMLD974","WnsdIHjvgeQl":"gF3wkF3d4UBo","KEGJIWKEINQe":"XY0WqgY9FH5E","wZ6rVlUmYeW3":"SBUC2uVCrZdG","dGvTOSt8iMu0":"R9xeU7vSRYSb","fQCsSgZg1cSJ":"xvhokAYt9onQ","tW9MFTrF17sS":"Q08qXCmSuvHn","qpKR48TrtS5s":"MxOr5snX4Gng","1iWx47c9ivl0":"MXTfIXItQqRR","6Eg6aol1KeWz":"W1ocg43eAv2o","8FO82Cmh7vyC":"BD8lWq32gciN","BEu5qs0CJu0p":"NYX22ojPdInp","c2uy4SO5upk8":"QswbA66ttw0M","lpqUb3GMT7Ey":"1YZXETz1m9p7","jMIXgNwDELWL":"lUNfXvsQ00Vt","lmMTAcJOQU5L":"nnFdBdNVre78","tj7korXg0AkY":"rnFW2vHGYJni","FSZQg5aS6u1Z":"WTWFs1wqL4fc","VODg0vaUvtcS":"KH0fze3jAm0y","0Lhv99utC4p9":"Pvn6bMBOLlzc","vB2Cg43lscba":"vbjKcHPpxp17","I6D7uOZT9tdj":"ytGajanznJhi","xhEIiZH1VuB0":"gLtKNsJVB4u6","xJ4rR21q748w":"DAMwDY7nokc9","QHC3CJp7W1Jq":"zKIiropzEYin","69AVoVkZRm4D":"ya5YMlQHljpk","RdBP44Nu6tnc":"VFmbRCHbZiHC","khA7Oke5uaVI":"UO4rkHRGSMm2","p41uMkTA1pQX":"CCAv4zQCekjK","1oTMfODG1DdQ":"j9wgf34M678Q","xXXHkraERy1G":"s1pO8dM2BAVE","CKA3AlK808gu":"amW1cg4y5sXG","xjeVaYBLLkCw":"1jjFkzigSyGm","zdUPYHWPKIKP":"tGCBCVR9vJyp","76DA93HAsIYD":"BhBFlQZSLHxI","m9QTA7JkoY29":"KHR2xdqQreNm","MHxlkUo62yZN":"7lhBjuPdH3q3","4Hq4YyXyJYtw":"EX8Exo4Twtde","2zIKfpn46XKc":"tEBvTTuZDHsF","wzQKFhZO8ydx":"7o4Kez0c3hVv","FoE35zmA8g20":"st5DhmMC1fxW","de6RXOo5Y7qR":"1wPQqdYj0Xzv","ABUqmoj2F8lb":"bXFwOO6ZOblf","HN7Pkn0HN2R4":"UqgqJcgnP57j","QPP9Y7Ow3TWP":"cuzWdYnq1voo","L2TQoqFakpdD":"dY76uTtdlvIc","8e1oJ1fhEPj6":"breFSCDVHiOR","5H4xAkOt9Bjh":"HEJ13MGQvdA0","7DVu50O8g6k4":"UMBkqvf44uDI","jb5tOMG0Aso1":"G8wH48ByfJGV","xT2tRB7Cbs49":"scpdEswgg9hc","iG79bwFZ0F8F":"42ELtZWcW9yY","gnDuaJeqdItm":"aRWidptJsRKq","L99IuImVvU2d":"wEFOtgmOnX0e","m6B3ocLDobKa":"jCQkMAeAN3Xv","imRRM1IABpiq":"mwZCdUJdWSpK","AlV0NUUSjMoP":"xLfK6FNR6lVP","953Ib92VJFSF":"dMZTwlaASYXh","jyeNNIlG1tyH":"GqKB0pEvwZ9I","V767G3NbjJmL":"TONtDyormpWp","1jMjZZZMwEde":"GKBuyb9PiNTx","LtQK3dYXVCV8":"gyB2evJ9QlgK","A1o49UvEzDgI":"7oNPWawc5B4m","3VgFOx4MkZZN":"iU8hgfMoWqx2","TI9RNutOxi6R":"9k4FCzuPFjw5","1fczShDGX6QW":"8r1QCjjqXG1w","NO5pG36NzHQL":"xaovz98vCGBM","kgSxyAdcsyJO":"YL58m1rfEbBp","4PBNZHXVUApQ":"UqLAeAql3cWn","atA7cJMLPHay":"WkR4IxDgJv1H","wi4G0CT5rNrI":"6WKRBx5Q28Vk","YWjAa8kRH8X9":"fiyuzKStgAZh","3Z87IFqRp37H":"9QEcGgxJMcFa","A2ybAvUXkfxr":"udX7cx1OKtdi","dN51f09SnBnl":"z3QemDyIqjk8","YGz2uINs8X5Q":"THQY4S0xpHjy","pyqxEEtH35Ur":"nMmAf1Nw1ChR","fVhhUJwaL3mI":"A1kHgaAjzAoy","ZuhvPgBmLrBd":"8KLoJDXRB4cA","JyWZDuHuNkmY":"JyTMSbsBr9gq","dyhNpHEf6iDt":"NWa2mNqAooMY","87EpqzTRu7mK":"WQneaafKp7K0","AUGriGKUc2f3":"4igzUxjXRghX","o2IRVfjshH9p":"7udPcjXBd8i0","dphFDh1UfdlO":"fI42LN5CPiqQ","2IqYYIMDoWtr":"VTUg2xGS9OOW","6h5XcMhuAgK7":"i5eb7sKq0u9V","SNl8Y7yXAfO8":"IEkwGGDegeEy","yhg11Lvc5Xfb":"V5PXUVHJ4yot","zFL2EMwUxojg":"779sEcArmBsq","9xi8uJTbp5Zc":"uCKy6zUGkY0h","mw9y53KD9hJE":"WtEo4xWVunVN","X3M9JF7ClJQr":"nvpcEavOvSfI","56oT8ImG6exa":"uy4Gr86B2xJx","qlwCGsem58OJ":"JTHX3tQmwZwI","BQRjYh9CTvis":"7MOQsrEJpjON","wIXgXKtv7yjt":"euMQ4YGOoWTt","lBaYThQ5mED2":"xVTd8HEuwIni","7xVxFdXVsuuC":"wmR2xxeWOKpN","9ACEAgInxl8q":"ivUOlzr3xwUf","Da6P43YyKyDV":"BiF9GwrAVeK5","XWFcr1vsg0KD":"kZ0G9X6LCOHX","WA2OD7c6keUq":"57VWWJjCrjk7","Xux84IedL1ru":"tOiAQ5C4DL5p","F9rSmgYBMhL1":"7D4UuSnyAD4D","j5bsjChjdluq":"EBvBVyu952sB","FTC8p2KTHmgi":"WTdUU1AJHumb","yBd9VzR8GJD3":"R4lWEnpiDITf","8UUylQH1FE2i":"YXfkOkmWh5EP","YU0X4iLCGNMC":"M0S0eoRVt45h","XNK1kGJiDhk8":"GGjoeQTlreN8","nwrY2wIFqDVN":"6hcrrvEEdJMG","yAcw0TBjjSuz":"kShMcrcOijXp","uP0ibQ78i0Es":"z5mRziGxGsWa","wh4Ao1ZIvrZz":"aET3dRiaS7MY","b684ONysShSy":"4zPbX7OYaRuk","tRvBLR9JRM12":"MSnGBJrcZRTA","BDi2lPuE77Za":"yEPniqj0F4vh","zY9AHnSw0Toi":"vyBrln6oItJl","qOZonVDOBZ0A":"w7kvmm58Q6rh","DIQD98G9SXxu":"n01JfJ4bsKAw","4cuNAGfhHKB6":"n7cdMBAmloO6","8Q5GWNUOCm2n":"5qAfQwmAUZ1J","AgU3b8ptIGRu":"smAVzKj4GDxf","uXMzi3gCbPqu":"nCeAzPYAo5Fr","C08W5xRM4BRf":"g6xJ5bko2agj","VDEcqJyVt31P":"U3Y9OI2gJt4E","LAxW9SN1KBwq":"jpAFq4rPJIRQ","ze6ZWrkIlpTZ":"BELhxfcMxtyG","ejcCY2Y8pTS6":"SEdvG7E75lLx","e3YIxkVIlvCG":"8JWqMavTUha2","0l9nzBYmNNYI":"u6NInU3GR1Jf","my7V8yblmAig":"aq0hpdFbRaQS","tzz1L390BEYw":"pDvGfujaWXea","dZrG8b7NMew8":"QFIN6fcfYI1E","ptwKuVcnuN55":"agKKeHYcP2Ed","h1UEpSM2jOju":"x3BMMx2OPtvj","0ywZ4WwqOosJ":"9JyX8phTdxlJ","eKBvRvLgg4pV":"bvlUtiNXsqHM","mAaCAzwFMX8U":"7cpGLqpIlqJN","AJAGaDJky2pV":"aS764yj9ZRnn","001sV1HasY63":"Fi5mE3yUUQGF","R7TCyw8clD79":"HdaYoo7eoPjw","EgJ7KivceHi1":"yTf9u5ODzaAz","HaU2a2FWdoF5":"UnhPjNLGq3Kz","S1y2UekqFyIp":"xxDyd4TI7Xra","i7jDt3rzbbu2":"4qUVAXvMTbbT","xLwoCEl9wZMb":"dNx9Chs3WHHR","GezMclERz6K6":"efqDkjYoKyvd","qOiz6Je4Qmjx":"ul1ihldQNANj","Msl4eszQkg3f":"XPljkuYqCEFL","Z1xzCt1dD8tL":"suIGZozpQEfU","0E0Vh1aPMkJd":"KY5PGh3r9mcV","UrEX1LsG6JaR":"0iXqIFILob9P","py5Z2wGfTpxu":"tRF5S4j9cxWv","OE1mYz2AAz50":"AziRBlgWfITq","vYLMzoY0IHjC":"t1LRCJoLApCF","DWdkJRZAMKtK":"jHfYa53zT6YD","6kUqrb4JgZLN":"ZetOnK9ClHqs","DqJuQyRiiNU0":"KpANa0JqXH3Q","xiqOtCUMpTtW":"ZTKVQXHk7cWn","LavmT9gqLSHF":"fVyuiyiRwkEH","eln4a211ulol":"9PsyVkeorgu5","5pxfH5gRe55U":"7Pwhjfp5US0g","u3d3m4viuDN2":"vTK9S6WIm6cS","D9c5JZrjgKHw":"lFFoTMDtSujb","NEu1B8WBL8lx":"Q6zPNGJm2SQt","ZtOkckpVNzKe":"JKWY2z5zbuIK","R1O1xYvYXuJG":"GUIirfoR3sYn","kxGur4Shm3dT":"HuOsIHu8MU3D","RSL2eO294F6n":"J9OMxyE4fLl2","8EhibBtYheNV":"S1coYy2pPBlI","x8suNYSEw2Ad":"Dls4EiOFkmGE","ZneLCrLALLDf":"pt2AdtZm9Rqk","44m7GUmVOzE3":"kFOtxHBUxG7S","VIiFhzafla32":"kCoEhBKcKKeH","ycYIN6Faf2AY":"BFlBdXd5lQpy","KIe891riq0lI":"fZhvOBXuJ2WN","G3gSNm0FRwC9":"XF5qwqGdEwQz","hbBuL84O7mjA":"TX0dllcYTTqS","iloFK56Ovm9j":"7ySVehAIh3Nq","grzwsJgfoDqF":"Wr6EEBLDODR4","QzyVjyPfq0Nq":"zUB66fZRwgOw","4FstiEaBuVFr":"igD56mb6vAEI","A6gtRM1jQiKl":"YnyKKpNxSXiT","FA1dLF206Hll":"LEko2I6IZSrU","P7vFVHO4B7Ap":"4i84YnvEMNW8","1fB1dwEvAYIf":"YgJonm4qSE1b","oLf9Uby5TuVR":"ufDmfeTyz9ie","gLNbGK1JkM0U":"QY4ruZ4WAFxy","VpAvpTZmJoC1":"PlBhfOZ6Y1GR","qjfBBDgmtIfU":"JZidX07xdegM","PzzPEHpJfO33":"egNPoMHc17Ej","qJDsCtOVIhWf":"2mewWHuHGzJM","8d5j5J9qaRAd":"iz3dF3o7MsTy","Vs70kw7lN6sC":"MhI3j6g2ZNUx","e9R17Wq7mp2G":"IFRIZysFCqI7","jYMAexkBrnfO":"gfN5X3Bm5yOi","Z6Ggrfgyl0RS":"GMDpnK1uY0kk","loOrrQzddCxo":"9rpm3UbPjY4J","kl1cBPAldkOw":"ugqkdNc4Y6rT","EG2kRn7dnCmC":"nszY7heR7OY1","vDTw5muhdl4P":"B97fMKjlDzXC","DVebVYbOqyIB":"urz10OM2qavS","MwmVKxZr8Ie8":"MHjhEwMnArXS","N1zesmkr3ZRR":"1Yy0M02uOV3Z","03KGcA8sQUt5":"SUtYNsVf9heW","ZK9u5oiCLoFI":"52VbAR6nASN1","wRKlT0rLs0jr":"Y1aPvHFuuGlW","3MPT3jkcUPZB":"RomatGJ95C6l","szHOobAOUXXI":"iLfYq6G7nDJh","IokAUhXS84xC":"WM8Q70uqYXd0","21UvJ718x36d":"BbFwrC3Xx6Yu","7TS2CYfevem3":"x8PFPxTYfQd0","Ms7duq4hUhEl":"dIDD3QPPzCiL","w5oVXEk9ePzf":"AdFvLNez4ioo","4DugHqQSiZsK":"P29kVWmHAN77","ZRn2aQh90gQW":"Z4IqRooOwG1J","2upY48XNIN5r":"R9loF3Jd9gHh","KfCVznR5JThT":"twpiXJLnoRSL","c3ApTRNOukeH":"EXcQAVo3FCh4","9DuGdnlxMQ4g":"WBH44kMlOqHf","8Eui2IbHCOf6":"3YQ6egMnkVuX","5Z7lKfMIwIig":"RnwmpwuGX7ZG","7lu3X5haqhMa":"VUMLtEnEeIJd","Jn2eAKCDkKRi":"IVIHHs48fhux","x17HDMvR01ov":"j5yIHaaLn92d","urRhpCdoUm5j":"tnrga8ljRDtK","fMIJP1VBTYTH":"biwRYBGFYrfT","NUlGtejUP2zo":"rDXhPP7q9bcc","PMVo9K6OLXUH":"pqBzK5oqHimy","BIm9ubegQyFs":"ccOePbabPylc","t6kLwipDci1A":"h1u8D8XVkOrp","b4ZGICkrORaC":"BA58lE1rxXbG","0lVvPGfIOzaS":"fxeckE4bqDXa","XIBN4WrIEl8l":"l1u1rsq85ofl","ORB0ddTfSfPK":"zPT9nkKLdDys","mHjQhmybZX5F":"d9HLdVZKiGeo","5xKRZNXj7Hn5":"B4nSaSihAE1X","dnI2sY7ZlPCe":"QccrEIKcL8Hy","7oCnf9mpE3ma":"xId238TCqm7x","aCKXr1FAfALk":"wqbxcW5LAgVk","n0SIv1b2om9a":"1D5JDkXO5MZ3","AI7aUpylSbjW":"EXCNEJkubi3Z","5gelxM9UX9Xb":"d4cz9P5ArnuZ","R5mepNhBMY39":"S7kmsL6OMyRa","l1YpF5DgJSuO":"vxCGwdOuYFRp","9TR7eFfS4LsR":"si7XiYERuhZu","RJXYAtNWuh4m":"yBbFUH8cC92e","erf0gZF6JEll":"43c3jk6LQMKF","FwuWcZ0k9Ifv":"v8TeYtl43Qnc","2sOjgFixPMqn":"eFiVkev3CGxc","My0WLwZLPUlh":"9OWnfO1bP89o","sW5KJnVG9RIT":"Lw1AL8nlfFUA","rN9P14Chl7u4":"Wp56CN6xsO6Y","azRsQZKsgXBf":"EKhXedWqNbld","1QozP9CeO6SN":"J7mYSy7KCNUf","5XFqAkHFxoQ9":"gcfxusUnsSFh","GahBy7Rs5Lm4":"VIMY7u0LpFXz","LG4woVKArzAa":"CFuq2TTRz8nT","3HUnGzpwF3TM":"2fCJpv2zaAIL","DThyQDjgNVAP":"gJ9fB3DkanW2","6wI5HV7iRqCt":"kvr5H41FXHZ3","B95uDi74chPl":"Z7oaG1LetW9K","AQXdCg9BCPdW":"hFOVngEUaChN","RAQByjOfp8wh":"FKaLL56ag4vV","f1ml64migYOQ":"sciEjlN4yHiy","UEiBB9tL9wvl":"Zkgrd47frEP2","mbyZzHxLDHFB":"kFvrQgbQXbDs","jjsJaZAWrYUr":"G09S8AP3djjR","8092U9e6h23m":"HsZEznBKgop5","hINgWo7EgRoa":"KB4oYXwrcALy","JzNWwt2WdN53":"6M3kgDKqNefy","rLNPIGrBkiZi":"M9KvzKr9bSYT","3qorpntwTWYC":"XLiQDFGWaRXT","QHJqUCKbbOOd":"RR4PSN9zcHv9","WLS8iaBp34pg":"txFl1acaWDrh","IZeUjLv1V3qK":"x6G9yStJklqZ","VgIDu26hqx9v":"aptR5QTbYwFn","6syQ4ixA1Ag7":"wvaUmP5lAorw","RA10qp1MBBbl":"ghPSPliu5kEp","iVieBusMpyYy":"It6MxUEZhG2H","H1CPhazjUx4G":"W3gcvOtNgTuU","OoYz5USF7auj":"7RHpYrl1854g","KeFNrEWtyM0p":"z1w3nW0eCK1a","pWbfmtW1T6oK":"PW30mYsPLHxw","eVYuiFRNtPnD":"emvGXxiFmoHy","oIAb68PbPgOF":"oYQz1CRlHTK5","eSa4egZ5FFQs":"MuXvGHSk8TDX","uHne3FKiS38A":"ifgGZjzV8EkT","U2UNj3JwZB5r":"6Gx46UPaZZFO","NmfGYJOpLSkK":"skzIaxrN4kE7","JSwOOAVwQhca":"RgU0mpwxy9Tq","bmNCjry0XhTs":"HsRP1BCEdbQx","fsTt9l87jKRL":"xqUnOp7ss23O","11kyOhJCgjSR":"E12wo0xXKh6R","jtphFWXtnKAf":"rJyjyIzWMb0o","tyfrOGHjVnL7":"0aQAB7lDKuT4","b3s0OINMBUP3":"6bJ1NOoZoNVO","ToFrSVlVrhPQ":"gNou1vs6aEmF","Q69LOcMJlr8E":"lCpNC3H77r99","Fi1tBy0CGHcQ":"uphbLh8dzTfy","LxmeIhFRBnmB":"axEdRD8C6WT9","H2mbfudx5Tbq":"IlklEuQPuNcX","drfr2qKzJHkr":"mnUDM00SyFUT","8BIIlaZvTLig":"gj44SqnYaDkc","HEvZ2t8KFHxY":"M6eozjnBf6vN","vGVbcFkLth97":"rhyOENjDvnGL","apZMPMiUdPDE":"e1LMxn8NrK4O","8LMV3lpJJXpD":"bpO18R4DOqBO","Ppb2QYEhPITP":"MhwKIDnrGdcx","2nG0UwSgWMVa":"lIP2voJWaEw0","ZqI7qu6rOWhB":"luYY1Vsh8u8f","edMde7aWSEn3":"nCvqIijFHJOL","RLFLCvJ1TLz0":"o8j2JiIhpKvE","r454GH9uBlml":"5S5ra0ZPkDl3","UrsvTe8raKOR":"1txwSk76TIFr","ovdfT0MCoJiP":"fW2laxIOdGJV","LzoHhzV6oPRc":"bWoWL15hTteS","s2SYrOnGLGqM":"Zamwnu7erHmO","4zslG7RzsGVN":"JgLJvpQIp8xH","H14y4onV8wMM":"aZ5nkqAwD3fD","0utyP4nerTXx":"8cNcjgLCOdjm","qKjY8oAqUuMn":"rSFlNoI12ywp","S1pPyyoMI5JT":"TYwLzXCQlZY6","xQDrm5LpbQDT":"KsL0KrXsNFdD","8v66RwD04hpF":"CdDxB6HyxkM8","4fAO4a5zRA85":"OjfN9hhtxJFB","oq7kdbAlCkQl":"NsLeHn4RRh97","qVVvshvWkcMd":"FeZhTgU7NLO1","2MrruXZPaldw":"VBDBoS3L50gp","UZL5CYSEOnZq":"PMRW4LGqjJKN","aDNTfBwZINxU":"TnnR9XgHGBhD","TQrTh367daHJ":"ZC1N9Cnbs8Cm","8WU4s9OqU0mP":"fPvlJA5wHUEV","ZKuVDOsm93wO":"0gx4NVCa0yV9","oj0r829sAnv3":"7UVEe2R9A3Rc","ZRRvX4btN637":"MqB4gN5pClf9","92pCLrNxSfUC":"wigyzIjBs6Jd","CU4EyQFjc7ff":"QvBbz3IHxacC","3fZoubpEQXFn":"CLrH2UpeOChu","75QhHBGqMKOM":"jQ064va0k4n7","2NDu1i7z0VLv":"9XgDou3RvocG","uyedc9819y8I":"4Z9PJaNsn6r5","GAQhEJVDdE6Q":"5NaQoKUGWuGy","nXR96sOzh6Rc":"dlaTalrezcJs","MTEtCF97yPKA":"fPs6of0tHZls","EoeRuc0izQDy":"NWg2Jx6RcmTO","StKqmqQYlE6Z":"mvhXKWZjvFEp","SewUXFGADqr0":"96K6UPqbBwz6","JZ9YERyuJTW1":"CchDkkabmgmg","zaiORMwoMPKI":"wmvVZxnVfKy1","NNT8rxCNfjhi":"c17D9yp9qelC","xxUp8qDMW9R9":"TmoeIOjKGut2","nAivsuH2lSD5":"ZVUEX7MNssI5","0UhSRDAHWNKd":"h1IWRikpJ1WC","do13TFOSMk5p":"CPDb20yHaev3","FbmKYM9HOnR7":"U8e1g0JA8bN8","Npjr80jwgyRY":"f8cQjR2haaug","gFbj68OBfKvY":"tQLnZayhNZ0k","C1uqyY4y1SO9":"7A6Y61VACqmi","AX6OWYs3COTR":"c4axr8xMeKFx","QsetxFQopUZc":"gb9bLzfPKhyP","XfyNIo7cvJtz":"dgXXdaOGTsK9","ZL61j31o0tkx":"avuV7XXjDYqH","gX4NSZ7p2hx6":"cwhWUacliUVI","UEROamu0POdI":"yU6yrMJXS1KU","ZOOee4Iopcpb":"iWpi1EZwDvGL","BXntMO27J8Ph":"wVjPrCkVOTY9","moWOqXP03WSI":"ZbHNBk0YlkNr","kI1yNZFzeJUH":"abzbfKOvU7ou","nHq4W8dWco42":"B2ja1IMFRQCs","ewPAKgiWvvy3":"7vXahLssJgd0","J6EdcNMblchq":"1pdMBB02wt90","11ieRTppQgdm":"iOOV7Dl2agAi","G7rGQ0RBs9NR":"NTzRsY7O7LNa","vbfxSLRFlpeU":"ni5A12rDY6GY","d1MYu4RH4UG7":"ZwLiiLzuAik1","zshwdR5zynOJ":"aQe2i1ezHSRS","9bYsWdw3lm19":"J1hy8Qc7mfKG","jqBArCSrmdw0":"dBminfKwUZq7","OaRWmfwkf7cD":"2KLYc80qQ220","xrAHbIuPhyi5":"axOk8k9wQ3LW","rtkOKe2ggp0p":"Hw3y1rpa70qr","w0gW5FEoTEWC":"v1JSjrJFuDMY","G3ukgY6O6XER":"DNkLvH9m0dfD","CDe44Q1o2ici":"Kh5T9nFa06u9","UjIOMJksXw8r":"Nt9j6j4NJLk0","9cwBIwwDPyy2":"LNOAz5RihEhX","AFI7eJMMfp7F":"ykI6bBUyH9SJ","UntBZQj32Q0T":"Z8QoBiONMTjH","mAMXwkeSLmal":"kNf6jjwfcFM0","XYsyoVhinazM":"i766NKS3FLYU","o736xflWj3Rs":"3hwo9gYiCcSl","8vMg47NMP98i":"SxMZkByV4CsA","2DnHaFUpSjzl":"aeRtMuCSXsHz","AkWKfpNh2htH":"JuqCMf4MCMPy","4pahyyArvTRK":"4KU1l6qJAmKM","ICoCv4kqBRM1":"UBoeNsxI7TdS","IEQah83fx4kN":"3mxp65Cwsejl","4Pi4ygpSsqiS":"xJhX7OIA8wD6","rVyeNglRrwrQ":"UB3ZkFQInfcK","AYNj3ECYPkPQ":"2N4dfjw8GW6n","3cg3nCkGRF9N":"pSGtBNfbOogE","heSuBiW0PksB":"5fxBqxzH6hIm","IsZCt7F4cqMi":"UCRbgW2bGYwX","Sz6CUksVUVfO":"PmGJ3XDuWKW1","COHk3KVAAvca":"yFWIZEqZG8YD","ZCNX9Tpnsz7j":"JtHzobk5RdId","uJCZjbq73Sxj":"NuvY6AIdh3Fr","Osb15CNISQ2e":"m3uQbbheHZ3r","8o01JqqgHrjk":"DysADn2nmuUM","plW1wvVK5pR4":"IyOCIMOJEHO6","RF2f9JNVe7Mg":"BxhSWVkUFLdl","f1XSuGiF5a77":"zDoTZ0fLsCD8","Qr7WdLs9iTgu":"9R8kcl7uK2x3","W3m9kx5aNAgv":"o3bXJRPG6KjQ","IkBoPtG6FuZt":"xjwhBNcqn8aT","YZQFKhj4HqNj":"Glveg6zyyKBW","j6x66eA1xQxP":"2ZrVY60GEPGw","htdpdiUYTS8i":"wgYZJ0sn3EN5","F6vqbzFLYpBN":"H869J7EBIPxX","fWpUR4lsCVwl":"yIkjHiUzOCvz","jJ7Snqgk4Mel":"NxLBhny29la0","X2QUXJzV9w9P":"LRHGAwcCkbFt","sZjixnI8VFpg":"WbMOitnbg9gv","Fbh6JGtKXLbb":"4tiQJvj48Hr1","oxJBxpPmLFOg":"dabC8SFE7UQk","BWx28xqNwrEt":"PDYkkXAuLXMK","eLriWCrKB0tP":"fpuJfr81mmkP","lMh23pLpPaf4":"vW9bx26rt10L","dvY6KdWBs0Jr":"MczO7WVTQn9x","Bejij2qBEhEr":"8lwj76fwJ4pY","hlWGuwdy5wnh":"2dZLVpKj7qeK","yrE3uA5gxr9x":"x3XcPCnPBCav","m1bcQMkBjHMs":"rsNh1qDwjWJq","zd0CkD8i7w27":"RdhoBEfE21Dp","tRphx4ETuHec":"K0wB3kkZpoAl","W8kiSPaDeSnc":"7FAVD7VOaBKT","nsAfFCZsb4fC":"IiTnaMypTIKW","E6IqlCNo0IDQ":"BqsqyHttJ6YB","8Owa3Tp9yEj1":"1Onmm74LJD6j","e3jr4NKAMkOr":"thFgxDPSY8aU","PaX8zDs0WYqU":"8GQRLXKYr4N5","vFvEhzM9Z9NR":"xBZJkLxkSEes","6hMWExoB6Rke":"Zn3C5ivKGgdP","IA91KOK2byJK":"MhkiDVwW1eA8","A57ZEdNXRoRs":"jX4Grx5a7Zj7","dakoYcWWq9eI":"7wS0IBTY1EO8","omhen6cGb4Ai":"wKfClImFMOc1","2o1kufbmD2V2":"NRH6Gg8oWweZ","G1j5nyTx4MSa":"GySBC3rZeSx7","10EGBGehsH7b":"lGbcTtYsSSmy","zAsnd1hbF1K2":"ATljI2ouXjNs","0Dn7Q54hvNP7":"9iEjiPrMEdu3","MguxOZjVR18P":"i6rPZPK2CNZ5","K9MTxv0RIItc":"zR3j53fvOXI1","8W5xEJUdLKiI":"mHn3SXNXl7Ja","5VJLSdUEXm3b":"TrC5fgPTuN6g","o0BTcle3Tqpw":"stnvlku9pYzH","4MEzpr0JHNKh":"NCFVx50dpTzB","98wlp2EuLk9G":"Q5lP69DPNzYS","IXNv77DqLiGY":"BpJYM6oF4ZsN","YRnV7sByEkCG":"PNKtzFZHVKyU","AAt4UZVl8a3T":"8bsbRiLg2fr3","1Q2Pl5KPQw39":"cZLNvtPbegTe","hxVBFgRJYunK":"Ay4r1PJcfgDz","QkOisbiRmcOT":"vVgP0EcauPPd","3dmdiw6xe1n4":"fGONGYlFPqjm","x1Xe4wj834Uu":"S4CZcpkX3ZbY","Ep7AEaWi2NVv":"QwWoOuH6N41n","1M9HU9cZzdnv":"d97WLhP8l6Tm","D66UPcfCgceP":"dCWKgQQYqEq1","Ev3FtvulQ4JP":"JoubOHrNGCNC","T7A87ylgWJbN":"IOQCKk9XcPJD","kguSobFTKQQC":"BrCkRrGnZ2Ne","ZOAs60Rh01Q3":"0wZ1zkyjIbPE","0Uba7C6PkQ5N":"wXWtxKKqb9ot","wdN1Yfiroz1H":"HUScbze1yGPW","gQnzecE3nC9h":"HofAlIriHigB","48H9oLt6WE7o":"sAArIVMrkzEC","clN48jfHnrQI":"I7faihCZGBOR","cVwszevRdbmB":"VgCRcPI5mAzF","WHmTBhy8EAo2":"C6OutYKMLZuO","PQIk3CjQIdhC":"drLoJw2gRxuS","lOBtXtdUJ0sn":"E4OKHUWL5rnW","i14t6PZseTqg":"OiDmDzUNPQEW","jzP2YBkQRl88":"JF1ccwLGgjxW","r8NGQcZBjFJl":"sYwXgFhmr492","5OR3kFEgw1UO":"kRZSqknrUSM4","RpNXxYBfmnC1":"LLtx9MUmJQIH","Y3BcPmVdOvt1":"alAJWNMPgiev","43mDHRjjUIoo":"GxTKu92zNgvm","fVykqZSABHCl":"YSdR36lZSfpi","kFHlC6Mtem0K":"Az0sNKL17NPW","cQfw7gGpmQQk":"9Z89Kex7CmKC","CxTgocyJWaiQ":"3WZbbqyg9RRC","hBjQ2ILzPUig":"FfOC2zG9acMJ","ezQcTbs8eWnq":"q2k27Y58WGoS","Y0UxWN1RWoQz":"TPMDTd3xZb5u","ZJ4uHhVjkXk6":"y0uacTu2SkBh","bi3wXtzfj2rZ":"IBh3Gg5TICNR","RL6vv2JaJcFy":"gIGQy0PwxsLt","ey7xRRy7CaT2":"CZrfOTY2McrY","qEMqAO95G9BK":"QFiDftbFyvMo","yiaaSyd3O2s5":"Tt4dtcmfunzZ","annqudy624Nj":"qUFTNSQ6uQI8","vmBpX3s41cQU":"L4BIvLLqmXAe","Z9QMyci4H5Uq":"QAMmg3t1HI5Y","VZGGv4TiROZa":"OZ3JG4FPDTLQ","a8KEO8xBl1ic":"bXuIahl25v1G","mKFp8q8Ezovw":"ztabcXsH0s66","jNlmRhbW3lkR":"wVoDHl4sYUiO","zce6NHPCsdGo":"0Px2qaoqE9HK","EsvOqcomFG3s":"tAikQAX7Flow","dfagfoxWtowU":"fsDNMTRRs1Gy","f0ZHAzZahLw3":"PWNPIyrMy0Eb","cWzsui2IkVWL":"SzRWSSbYjxzo","jr5QGKOSLdkK":"RbSj1bLh8mFT","MGA6P59AdRWh":"leeRqvzmvgtg","HRBs7hntmPuK":"Nl1JGmKwtPuU","czuujygZDSGJ":"KQsVkc1MvVmm","3wWz8gbJtKfW":"Q7bATegKKiQo","woHg7sYm6bbf":"2z3X1u2avlL8","QdWQXaXV866Z":"qZtC4upoyWkw","mj1fRYmSot4I":"RnofGuZ5jtD2","bKF5MvagqDgh":"TRyf8d1UUsK6","1ethYdsuDsdC":"utSukVKVvlZh","JfQCyqlKGeqw":"J7sLNgmrWnKJ","ej93UByEnPYx":"dU43CsfDltmE","xlZc2OL8Shyz":"5aGXfbsyevFk","qJANLqMv5O56":"uGtnLslpSttn","wKnruvzDChlH":"HNX6XqfvQLu4","hFwc6eSPn8Zq":"UPggPJNukSTt","HyR8PAC75O11":"Y6nS1Bn5l291","ZEiFILZeIZdz":"DjCi1y5SEg2B","UrAljLmAPKIk":"ScVHK9UsEFas","xnG9TqDFnW9u":"2ggI7KKwBbmK","AviP96U89s3a":"SJQqLLb2X0jS","HizyRgQsjH8H":"0kSaHn0RoVgq","5oNWu0SINd0B":"6z01pXj0W9aV","UihZNvCWsYEP":"ryp7unnOh2sx","o9oiOC2TthOE":"i0FlvuMVLRBL","YpBC7LziB3Tc":"Z2XUXdnfpPyH","T1w9t8NCgkds":"uq7zKfeycHAB","OirFC308t0hs":"fauZURxBbzV0","eURRyDgxG9sx":"1HsAr7jwyHlo","D77fK7MuD9Zc":"86UQdsjVII5r","1qn5UedthOz2":"lWGmiGkPJCOK","m0wv6MiLxQUE":"PLEXHYz7L9Kc","SptEu47AOqm9":"MTsjO2XEggNd","t1ccB6X4Wq1t":"jrV0yiDwO2aq","uaLIxA0phqn4":"wETf3FOdEnyD","zG5hsgf6oC7J":"LQAclSiwG0GX","VYyMpa9LKbua":"0cXaCFslF71l","gmCrJof2y3X9":"ux2jTrLxsYOr","V5beHxBaYXZs":"d9kSjlIXCVjw","5zoT3SFLfpjD":"OxPmlaISREPP","8basnnJlVZaQ":"6HVpwtJUJhnK","WS3HfXqlGGaV":"XjXzPhRKLU5Z","oXdQ6tQaXe1W":"1244UG4huQYN","n1Az0to0XDMr":"rHESshboENSA","81RsEt7avoL7":"bDv1h6uBt4OI","j4bv4RLzcCg0":"PauuP3H5BGY6","pEwSoQYZVcXw":"pvc7nRawFnjQ","ONM45fDuAwxL":"0TE9R3ikaeLt","pShHSkhf73ui":"3D2xkmsGzIfD","Z8cW7EISvEu2":"IwbzZzfLHtya","FkRAWKoN87pd":"J6p6qH88kqxg","jJGJbDa1eBPv":"xqGFJIjO01tr","RvbiIBl7as8P":"X2JV1MybFCla","ozTetoO2LQkk":"DrblPoylWF3c","Wka7hxP5cQlV":"zkCDWOcsBSW4","ZSgZLyp7n6WS":"5ZiZ9KL5pB7E","1yjpR5875rMZ":"EgvvHMefnmUx","n52GWPw70OPC":"Y6Vcmf8R0S0s","dh5OoCFMG34u":"GWBaJ3jcC0vc","JwGkYmpcOKWV":"crgH2NZDtgyP","xtJ90GiPboxF":"REOnpXCtHA8a","X0Suv1dSBFlv":"glQRIOMjOAm8","wecuhSjQBuZC":"wF5ZRCT0r4uo","HSWJRRIEdCtj":"R2oKZJOdQohQ","FufXnJqbYDWu":"P4nu7hUDMEKT","oQK6wWMPbPgy":"kVA0EOJA2Xoe","uaWFxSX79cnG":"JIlNd5MpfSqB","jRhhVxERbAM3":"oLgAeKNRDjXG","VdKyRxjdg7Tg":"NdWHYGJMjNto","BLnnTpnCGvkS":"yDVRHq8PLZ3D","WY79Ws91cT3e":"Em0UISsOtFiI","quodetNGQliz":"RYKqvFb6iwHn","gpOhvMuJamBT":"XMiYguaSS389","ikGLZzezNSXf":"vo89J4Adfn0l","FTpNzjbwGO6v":"Hwq9upXsBZ00","OhAa2jHCOzd3":"SPVIUYZv2KqQ","kBw0jMLXbGxd":"aQQ6SvGAOmQD","dlfbg6UOwpJl":"oOIQOU0FL97V","m72l3d7eOnxt":"BVE2vwUW4kmC","fAT3VsMMoSs9":"e26TUaK4ypQj","T6rkXcziVsv5":"Ud6Y71dlEv9Z","y0c5Pn7Y7Gz4":"1UB28pazRHfP","LduKk8IKd9Kw":"ZG2Nc7JFl9GH","vS0HY06PqVCZ":"xJ70rTakj1Dt","RpaGtGjVEEWD":"Kgu0CnbRFyoc","6dXUeuB5vMZl":"FPBheClsP01X","IDEFDWJB8251":"1f3fICXdlxhg","bnpZ0VAZmaHY":"x7CO1F1MpwEE","yOvmFmtN6aRg":"4CQ27VNandL1","8vUD6DR4kFNs":"fPwZ7VP5oGnN","zAGYqgUSK16B":"sbePtxMWkdzB","2ckFLYIedRbD":"TS4uiuZH1WBT","OYuKc6Kawnbx":"tRZei3cJTcU1","QYFRDvlKR7W7":"7vORxmbAattS","w4IECNg6vjou":"ZNRgqBVp6PN7","N9ClLw5wu6ti":"7DTMQbPuNNnz","0SpDqLV5R7SJ":"XSip51NHk1WR","2LsJy69vuXGc":"1oEvXezSM0F1","dD2yZkN3p5TW":"ZlPobf9fxo8w","FWNOTGReJXaA":"WKV83WwDFdE1","Ba3NZlTEQ9cX":"U0H53VzSQ0iA","8CCb6GOIdJ5C":"aUpqbJw7FZDI","2w4r9SfTUhvd":"xMTlUErK3Efx","3fQMXJLYlDLq":"e37KTlHKUhE8","g63FtvwWg7dR":"huNKN6cZFazL","c72BoWzSbgPQ":"q4R5HE2RwWXm","y076I2IfpdxR":"s9YvUN9VTRo3","emNArzIEYn6P":"G7GWLSg7uAcM","0tUvLxIWoFbR":"T2c1swixAJCa","11vXnOsNw2Jy":"Mw2rWc7wvjAw","Rp1iTXNcAYLy":"YcqlD8rU7Rec","wHrz3Dz9MEyc":"KjA9Q4D4JHwj","WQ8Olum9BgLA":"XnqUhc4oBdqh","ZVlbHPyqW32o":"rnaZ4kLXZxh7","i6PqkYMNI8Cb":"d7MVLF7s25cH","f36CCgpD2kJM":"dPpTa9oKHSjQ","3SABF59n2Ivb":"uJqNM8Ze2mnS","lHBllEQEU2Nx":"lteKAUhY3ERk","Msv1lAM8RAQE":"RvIwn0zzxlU7","8X0fJLv7vwAp":"ICaeP8ujH1ZY","bqwCGnkMrpOq":"YfabkFDDk2pV","Oj6Hrc1U7Hhx":"2PLHmNcDKKjK","zHgdSnmmHGZ1":"E8h1SBqHjIsn","YhvplOInjWcS":"itNzaPWo50VP","SH8crsdhXpFC":"ekXxIITO9ikv","Wdxi0Lh8otQ2":"v8NEdhay4wHy","VjSSKh7CVIdv":"N87dsxsCyGQf","FV0iTvGIqNpm":"Cl6foj5L1t2N","2QVGkfQnUSBG":"hbe6f3srMIzS","bpSeC70L5F9K":"SgTo067RXzKg","cBbbbvdcYBQc":"d4MlYhEoLSfO","4alHaEQPRm4N":"CK0l1RTZPrhg","OHf4rAmjsmyS":"PBcooY89yBgT","A2ZCrvs0i2Vy":"fN8icSpTBjmf","lytDHuATIwV7":"ZRH9l63v9skP","Ms145fZVVo9j":"ES82E7uNTRvS","r2ddy4HCbvJU":"KV88T1xXXan0","RGQNSYU2AKnQ":"fPIiknuxQYLV","vM8abgcsq0gy":"VgZ3DD0Puabv","r1xPq2rXJlTb":"leR1XcRsNAMk","KU6itpHgbiU3":"VY6aWt4htmJ9","C7WdsTh43WWx":"VSkrvYh9cvuJ","ewxdhVvTK2dS":"XxOJIyh1GCRm","Lqs0Ryr6V4lz":"SMrD24Cqz4CR","BFe8EshJzwrU":"31EbTJIEtGIh","GqdY05mbuTVM":"6oLRIDFaYsBY","qO1pxEkKYHcH":"haR67sU0mqbA","hWm6dGaWMbp4":"sS5a4JzUfA7s","DZc0pAhOTx1z":"BwjfNnugObdW","ysNzckreP7Rb":"EYqwG6AkqqLI","mMyCfamHnATG":"3fWdW5UnVDQg","wpRjuLNK05zP":"7dgwLYIjmun2","EVUjDe9fOoY4":"TU0Z2jG3AYBU","rKVjlsP6RF1L":"pgJ6NcZkuFUe","I6wkUsvhtslA":"V1yjBDY8e0ra","tkkZtFcTqnul":"dUKrBqvFJ4HT","xAXukRoH6g8j":"RRbX8BdZjtxU","xwADgWm98vSH":"5Hzo7spoQcti","v6AcnzHNTqFJ":"u5RT9EtcD8Bs","TWt5VNL3GWLc":"1Ygz1dnkNpe8","0tdQAH2TxsIV":"wPSpP82JwrfA","dwyFAxqDd9Iv":"oDwCsepyhWEg","9Cx4k0HrJkSY":"UJNGOLnhW1V1","mZoBbSuJjFAW":"9eyxFJHiRv1p","VwzlbcINNtVy":"zBzWgPpnyl9i","RLFBev3tOkpl":"2T5d5APbfiYy","ckQrbrophYRg":"AJVAchyCsfge","EDUQDPWLiBGv":"zeRjbWtfY0BJ","dYF1ra2FxxlQ":"vUSd7bwKD1eo","eULSmb2ZVKWD":"iLVAcuGmYOgx","yrVSejjegcgG":"FT661jrPPkSm","fpGU41nGWGPk":"UpHwFLuwsUPY","utKiv4gKBZml":"5t2StVuWncmi","XNLTwQh4mjCv":"tTjh2kTeT43K","RfVa8EBO9DVI":"hwCOeFiaM73g","JbdpUOoSdAmK":"80hNLHlMpwtr","MaHrOoDVG4W4":"yTB1Y6Y8HA0S","YnTx5AqNvnnb":"8ZhIjDU4l9Sr","RxNfxnHJW64S":"l6GIkwZorI1K","jXDy8K1Wg1DY":"GHunnlPjLL0b","VXvYP581Mnmw":"5tsVLhxmOakB","tUeXgWqjyalE":"lc75mEPSLgIo","MQf0KjaZKtim":"4oURNwPgidGa","grhW0muMwjbf":"EfrV4ACG7gjr","MNbxUNJXLxXZ":"vWnVG0I723O3","97Rn5QXaffgu":"PdXujqyRnfWF","ZV5kCQqBaBpM":"b3iSWnNWqkpS","ndMXI3cDAsfG":"vwOQIONpEXkN","4VnvXqwxeLt1":"t54ALyZn3byf","9Yo0DVPSPUFN":"hc2dMRDV0OkR","z9EPXLl7epCM":"gKI1xyb960rR","jJwZTphHgzqW":"mykgK7l5o3Tz","qFeTcxZcLEIG":"7PgX6ubXTfQz","kP2ecbHkaqE6":"Cpr45gN2tiOd","ugBj2s5hvstL":"EJh9qpLXQnjR","HQAMQkyABirc":"x6DAwzwH9BG0","Q2BqyMAudvaw":"h7gQRLMYqucR","jp5GxjmKMnXP":"jc6oMnhXE3NB","XDdtPF4AY2e3":"NPsdCvx506BB","N0HZ1uZhOfV1":"yC4rAqXgLbTx","t5eL5a9iBIzX":"OqmXPXuKq0DK","8QfRC3J13K7m":"8bx77Cnsi4U3","4yPjMEJaVm4e":"4xInJKKI0EBH","w5gLvMH4anAj":"Zy9Um7uqtDbG","HNkvMhMRPbz5":"16TAJ0WsIt6x","IuXJVyrKeoF7":"OdCXaalyknk0","pirYwBR3ClnB":"LmsLM2UqWq03","f7NUcr4QFTtA":"5WWroYOT2cwZ","bP7wMUI7POsr":"KDAlW1gRHDNq","BtzfOHfy5dv9":"rL2VYepIV5ri","bDGYz6I2q2kq":"IOgJmdLWJhKz","6J54Rcl9mt0i":"6D56Qd8jxXN7","63RlI6lO0Be7":"eijH2AUdGmHk","TP0PIeifcIn1":"PMQsSahERSpM","uI1XdfJTTggl":"NJ5VUIdyRTb2","0CfOsvQc8bC6":"ON7YvgVz7oC5","nui2WJQT5spa":"ogj9S8Chdsq2","MfyYG9pbUAjG":"VvCEs2iqnKsL","xDUbsmJ5u9B1":"trKDjv4uRYWE","R8itQ7KIuD7z":"4Msx1kTwv0Og","46kCW24FeOyI":"CcDJU6fotyPN","8Bp1HYhsGUa0":"QD7pB1NSS7CK","0QagcHmPykNG":"z6mbq9Gs8ViJ","7DHb9NrtzXan":"hOCLnAcFioSt","RIZ92M5UmLhR":"TKQzJYey0pQb","zpyUcHKQLtny":"TBvA0jnRMIRk","522taH6H6eoV":"3ZSSHAWL43Zj","tIEzmOp7dL0U":"nCXJfVS8HSaI","dvDcikHN8HtA":"6OTdznHzr2bt","7sAAOwbtbQRr":"4HNwvXZE4FnX","zIirL20JcZBC":"NLGS1eg7y7m8","X8DPSxf2lj8Y":"ArkJiBwCn51o","ybCCuSgd40oB":"SHPBsD9DkUoc","JMQJUROtlCuz":"D4HxlQY3dOsM","tcAGG9Hh3jnN":"pe7mano0dWV4","2mp8qVxGo5MD":"hTuInDg9mvPI","rpdxstnxGG3C":"ylwZyb9FMe0B","GUiqWrqZLPAj":"xc8l31F6S4h3","rqCK09hC912u":"EdLS6o0A4QpK","xmBTjv0RcJLA":"EPdritj75gFu","QKwAZJMZYo7J":"z3DXjAXMfh70","urYdnx3220Kp":"mpHCR63gj74r","FYTTnTw0ba4H":"xiKzxoIQlqp9","Lk3bGs2iNZTr":"aK6QPiAaMCMx","WNT0BgzkDVdG":"XVl4npk3hmp9","5EbAu8i1MgdR":"IKnOkLXQyEDJ","FR0SebQYY45d":"91F5WJ6EpZEz","7LsafK5vgnT9":"ShLiZ4QnhCBk","Teh4SPwfE2e6":"uNBAbCu6IAJm","23tVlxg77R0z":"cAN6JajkM2g1","83EOP0pFDILB":"fQuC8gPHqkWD","ML58vmv7lNLO":"yBYc8IDjxcLJ","eUf9Nvfn11qU":"bsM1WKbYSQ75","KC7R8q8F5oed":"8XeyU3mpg0By","5crOvwrxQBfm":"HR2qc0DQpnHb","324TSApUp0mV":"nGXunzBciw86","yGDbVySq6hU7":"MO5gWvwh68Rz","z6OtgiTkH56x":"K3IdXUHADAa0","MxE37mma9qP7":"SH8Ucjctf0pN","koOEcqFjRzv7":"GZEeXnfc8G18","yeId92ujfFTP":"mXfRDqfvPBH9","qDD0ZmSLP6SF":"TkM10ZCqR9wu","NvcPyeR3npg1":"U8kWLUMITYR7","QLznMyddYqBz":"JNa1ql4SCTvU","dnPVIKSj6nPh":"6zs2DR1R48ef","hRB20zuYLVol":"Ol7GImYCambj","phqo0OhuMhEH":"dyOUHJsaT4w9","8Ls9vR4zf63u":"xdx3ksSHIQ7b","pdqLwbQlSCyf":"n8D43miFlz5T","1QR6pMc7A2c3":"rRE4Qrr7MUEA","XQBeOYj6y2N4":"bzyQw5ZLzisJ","FpCV5EM8svHc":"L3qVJ4XdR3Vy","EQQhpIIQp37Z":"uk8iGJjzZT1Z","sbFBUtrJ0wv0":"8klNUyAiEu51","CaZoWa6JRieg":"u8QccMBEtR02","SDGhRASqJejV":"LeFFl4AAMQKB","4iEocTlEHpsc":"kWXKphJowIkL","iDYqLWSz07ES":"6LlWhBkqaiwA","O0ury7FFooy5":"1LBKMhJlFMnR","mg7xYB1cMBv2":"WNwvoXe00HwC","bGEReKG2kreN":"9StTremqn8VD","CkaI1ojgfXHy":"qcXGdrLCrA8D","Cf1MnGR2osU7":"Tb5dEY0wEG9s","2T8rxo3TDZWm":"3grDSegj57Mf","X6Zt0YPodXKN":"z16DpI4RhsWN","znzdAnyrQhrl":"O2qAuxi09yl8","VrmIOi9sGidY":"VictCOFGCsSa","dKMQG0IHCkvh":"6F8GPfDzU8oW","K5wu6fO4fQe9":"aTmrZ1yORPRD","86iEo4iljNBH":"GTH0y2xvVW6c","snJZC7fvG2cT":"KyffprjFoX6r","GBcbUjntccZU":"vVil22ViECYw","v7VCDcZLT0ze":"TLJ0TGZ4D2DL","IHasfS8NIIE7":"ZV9sDKWBtKFs","AP2JW0SOFLQp":"QHaewAI8W1oF","IJ4o9X0hltnr":"hCXp9ddyJj8z","jfAopG2LP4XC":"Ozb7l6SP2KGR","bIeDvM3d4Ayv":"6uhBiuJF2HAH","9MJh5WD5PL7F":"06EMiRCOAoKp","6RGPpRtcbNBI":"AMoX4ESN5Jci","JcMSeE34jfvf":"UYMamv0u0Iy8","mTRwIYjuSJZj":"GXTCUXABSeNW","VWKhBbQL729Y":"7pnf5on8ifGP","8ldMILYmlb20":"9m2AxzZzQhsM","yCLkxWymnFnc":"KDBLI1fpm0eb","dKyBMp5ftBXO":"CCggDB3RSC66","hRK9CV05YjiF":"j14UQoyje9NU","gpQV1dZ9qc6a":"1DVjGzOvULpL","yMPfpSeWNCvX":"jptTMd3N7Ykg","6X9rbtLhV8nl":"aDCAlDC3fPkH","QDGc3AWYlfzO":"34CzOUgE8SU4","lbuR54eEgDkv":"fdUZLzB7C8f3","DUTecRloME2G":"Sszhz4yyvfqD","pc60v3kZXoES":"n9BCPIRaSaJO","45e2Ud0S8NgI":"eTP4GyrwexrM","M2ucfbl1RjHN":"5cxdZ0RvfMVt","Odcc8ea6FYgB":"vNzn484DcT9p","GjZVGAaLmrb3":"A3tgT2NArzTp","STvz4shLNrvu":"iIJG1gmu58l9","HQyZcQPi0PwK":"WajS3ylBRtL8","EM7WYSXKeX5E":"JX0IPH6aVSYH","GaratEk8IZOV":"E56cy5I9VomN","gk26MiCKuWnG":"CC5vTEGjH2rG","3dCILnkEikjk":"mfScblf3jGUu","zAxacEvrHH6c":"YeMFi41aBscd","5kPqKS0ZJEN5":"my1D1I7dSah3","S4Xfoots8irc":"vtPrKHYKddMt","LDUiD430Szov":"g7KEQb8vZEEa","w5dMmNrMLaW5":"DKhAQOmnniof","1RbOgASw2Sp9":"WI8zKFfxHQNX","7p8yax5PhsIF":"Khw5l9ytFc7l","oCVXDhulX6gv":"VBA4FXtmaVtu","jygyys8e30Gf":"tm11Ul9HpVkM","4mhWcIygkCsB":"zfDMMmiVERCa","AGfRsDUQ7EQ2":"EY43LQVqD61J","N8z7qfTTo3RI":"EK38VHMdhIbJ","dUFvn96bsbXS":"ehoC4ywLqsvy","zkottgueF1BN":"LYCGOhDhWpES","nJisRmJhYYPg":"8gYK5KYZTR3z","QEBtWy001jK7":"iRXYs8EjUw5i","KWeIhztMySWa":"mWtG8ELpQmVB","6WbGIpdvAEsd":"LTZr0tutxRkg","MQcsYUEXjuYI":"OadSZMxxBVnh","5Itc0ENNv4Ea":"paakptfAMNI2","MXXAlfR0Yp74":"ULfPQay5qyIk","aWqQ70KXCjix":"k7Ko5Je7NZn4","SNT05mjU6f1G":"lw7G63ytPe3P","u7HCvSPUUkDE":"R0fa9MIwB2ZP","vi82pnFRW9V9":"fwXVnRUgK1H2","RlFdPqPsf9js":"wWx7KpcYuktC","V1OkkhOHMt5B":"Vasr6bbwzwUX","C7W6hOiDgQPF":"KRraTUkXYIwH","v1UPsniVruLR":"iEusiZZfk4KG","nGpd1KLNzg3R":"nR6IbtMs4WTg","zAJ9rBydCKff":"l2WmdZkeVoUQ","1mAaJ43eQJRP":"HXbvoiYVxGus","3uazBlokLgdI":"6WfjyiuqzEEZ","kSJSDoXhWlfS":"huaM14YVuvbF","jOHlfXNE4Vpx":"Ep0NCxsSdb90","b7qADzi97HQw":"vvjyQfTkcmvB","L28JmtWDQXhJ":"WoMxliWl5gWq","ZQV0QCU4gVg5":"Z07pSsP2LUEs","aIY7Zm40gGC2":"RrXTbGUJGkrK","SClTDgFgm2DK":"RTBZrM9NJ1AO","HkL7CwicM4w4":"AQKALpfJdSjX","oC4n5xDTgMW9":"5K8f5o9mSmjM","LC1gLggk9Yel":"rRstbJ2JPZlb","v1e2aynMe2bZ":"wU2T7NSKjEps","rbAbxXqIgN3V":"iOdXKebUtbQX","eDeCqHPRU1wA":"ObaPuLpqvcv4","50MOvsNd7WLh":"nDc6FgPzCyYG","2V3V5M7GxQC9":"yqUwBXFbw2xf","qMQQJ6AJL9wV":"I3e9pJJaGqWX","70PgEyyOBEMG":"NtBmmBTFJtSZ","dHfQxDpj20ta":"2UA9fkPXKjPr","lN6wDh3KPy5p":"1u5dMgQFAxbC","tSE5h01vxgjw":"2R4Y3b8lC1VV","B7oB4FDMMWmh":"vreBANRRgkUB","zN4PLBtgwVtg":"JObGrlWi2rdn","ACsMHCgy0kkn":"S92nCpiwgeAP","9MbrHv9TSgn6":"UAaKJdIRmQBQ","xkgpK6PdNhDq":"exJnUolUbS6t","7yUYmFpYia1j":"5vrkSTI4cP5n","AomS5mT3lros":"PLHYVLfDsxXw","iW69q5nCGRQZ":"iPNt4YI2TkyK","Yskub66aZH9n":"tL9RcgwqyrFH","njTLjDzFSWW8":"FiAMXQaTf04X","QE2BKFlTSN2Y":"4YJSPCQvYgow","gbdUbXH0I5Pd":"OB0giR32pgf7","YMcit98N7kuZ":"Q2nnhDHD3oug","QHwc8ny8e8rm":"WKDK7JJ4YeKr","NpFDNLCSd6uQ":"HVTUwHC9isRF","7l21TsbPdzMZ":"6j0azSOs76gT","kyYP8xuYnu54":"u0Na6hklZyPa","LTwCv4n9tiAS":"e2uolbgEv3i6","nnCRcx5B3Yxc":"NkdP6tWFaKL2","8sJyIiWvoUTc":"yMgwvCf22sz5","6crtHODOoCGK":"dBnsfd3qJUkA","khi9dKtM9D1g":"PWFkyXFCeeN9","LR1iTKXV0zoV":"ynHM7bGjVwIs","ywYQfdpwkd4y":"mNhBrwVDUa7n","adpWz0ZQzCiw":"fiTrOmLfZwCz","J3nTkGI4NvFr":"VtPD628UYUvV","mwu4149ixeCf":"9LUtp2y7Jas4","yqTfmbtf0orL":"WjZHrOJgjon9","OPimPSYWwMGo":"SulwZ20fSYNj","4W7FQkxdOWvj":"xPXWczYIdrbY","xJijd8xNtb8T":"4Hb4Djd3KvfN","gk6f3LR9L9n1":"hRUitf0q4cYQ","5UvSxRHcp8rn":"jHpvJaz6kcbZ","4JoN0oYtFMuq":"OaL7bDyloH2n","ywRTdZ2e0du6":"B1SlNKUxoNt4","V9CpIeJnh5KH":"8U1f40Wmk5Rg","E4lEs6zUgKYr":"cEEqzp33FpHB","2K2nYxl4rosZ":"wkzVzEohrgrT","fVA4qbhXfc9C":"p2g0RKmAOL2L","1exyyBH9k6P0":"7HvaiYEVIve6","7aVeUdk9qozD":"H2aiAUufeLXB","7Ls6PHIbnaCv":"4BfYVqkpnEus","CFSDQlqa8BH9":"lO1WMBy86YAK","zE05BioeT5G8":"hkBgcDy4WJsv","48zPrQRVFTKx":"AON3un6jXOK0","IJqH8jkhE1ln":"WRk5E27Hgfyp","jaSNu6uGt1Wr":"CqPgtjFYdlyx","XsaGruO2HmA8":"obehrffXGXFm","fxhunkKb7ohw":"BLD8TzrFuAaj","A4f45pyDNUaa":"4kyrfe1sGRx9","N81bk8aG3DiO":"5M05HNi5rnKJ","kz3EQ6EQQoY8":"YWzRSf2qrbjM","71YPm9JHi81T":"Gz6d3k9pDZEi","JHZJg43vlnHa":"bP4ulEWnRdtR","Jso9gs5bt9Sr":"4hM2KeGoSmSB","cRlYNV69fNnJ":"8cMfmR4oEZaY","7QitzYhQYW1f":"gGi3YCiCCvOP","aiTWcSyzI6Bi":"qknHsp4uZGK6","QzR3jNxKbijW":"H60AfTJfYNvK","KgchsoB45psI":"u7Xe7iohVxUZ","rfledCNGhFUi":"EuV4NbfGFsc6","m331oqgrzWPj":"4MNn1ZACqxVj","zDipP1xVlGfk":"bKX2yVwLlZZS","XKKFKkGlyOZk":"d9g57jAPa3tU","dIqC3eZ042v0":"MnojoEdLfE4Y","ClXUUKfh2GWQ":"n7oVG6DuFuz4","FrumpmpKmoHE":"ki2JN29Kokwt","L4wzhPcdsiut":"Usx9ey9dsUei","we7XK4qh2B6x":"0IhK6aAmsPeM","2ZVD3XuPq0f9":"MZwNq3skkO42","C4BZQN2MvKBk":"51UNJpKWj578","2NsvPwdIhBWD":"z3IrPgrx99UQ","YuVl7emQUPZ6":"eXS1S295vuvj","6MAmaNuSjFqK":"e6ac2tAQTtbL","84Livs4in3jT":"YKexkcr6y0hn","IRHmCgT5rkXq":"sRwDMMI8lF57","kui2wwnryrUP":"rKJrO7IumRhh","KZxJtHGclvmJ":"iKFgrCh9QCoa","njEbw1JoH1nJ":"1jIu07ejLJr4","SvNXFe2BXtG6":"KJCBzzYVVOKS","CKrVt9RNami5":"2yDmDln2Z6Af","7yyZ2lnGKoZg":"8Y8M4vl8wPlQ","AfPegek8ogNo":"DvWrtPD9y4iJ","OC16CUOWFFGj":"qGR0cQ3m9HXL","Tx8VCAFddbN3":"ifGI9mQAQo8q","h6MMz8HzzaNv":"3S138AuLVv55","V6scBi5vKSX1":"O398pdBqpRJF","1WwNelBbqIOU":"sGvmS61dYfiX","fTtcixBjM88y":"sjXBrTi6t3jM","0BixQJy02nwq":"vL053qeDTDla","fn1Q3SwMamPM":"24GWmMJdKCJN","KB8D2r4eW0wS":"RESKWsYELRI8","rzHXbvN2MaSc":"4ckGdnPp0gy9","5whPZRkekNoU":"YtWljglkxL1Z","CNfMX1Ae8wWz":"yvfORyoMdeJV","7na1YMxNp2Xn":"FKgVUf6k8Zft","thAVm80b5g3Z":"ZjB35RV0gmjz","618J312noVe6":"wksTWX9XYI2E","S3kpKhZkJJBX":"zqXsSKNdfZNI","TfGQVpXLm6gV":"rtAJDYDn4evk","cljAr9GnkzFU":"zV7BY0TQ3xHI","Ge8f2iTEg3yq":"aCZzPd9BvJDq","VQX5Vd6LrMHO":"T4ANly291UOn","2gwKiSi21chl":"XvVZWGiuGGZm","kPNPBaYdZyiM":"iYH4dB4ZJ5nF","kZFdM6PPQFxR":"kTWdMeXqOsoj","e9k1Vw6rgmL4":"dVm0bCrKfxSH","7kmlztgcfpmb":"xWlITswZpTMo","eyiyNa2NwEko":"F13wcSoS0R6d","JQds7VLEBzFK":"xmB4DqkT0wVo","Nk7jl0SFD83T":"yHBmqJW6GbyK","YgzgGMIgpD2E":"RxWw9umGNbA8","uRRbIf5rqLuo":"BzBwiFQDtM0Q","CusGwL5WPTis":"B3yff37fb1b7","ilGjDoPAgsSk":"9nK4HH8A8A3h","77ZwC4ITcNxR":"gDCsfU79mEed","8SgM7JrQVw8A":"Sr1K50tBY986","WMEwDyd8YkXQ":"LMaRPNhg8It8","AWbZWRLO74fQ":"CvynsklHJLf0","4G0M6KmtZYC1":"6ivFiBv3Oety","gcvkMhG3xcyO":"ysbsbxGvPcwj","68mTAuDJJ6NH":"3ygDlyZf0VKt","kgkuoYlaVHbU":"tFhk6pZ6ymdY","TEDww3erDyqN":"sQYKspfl58Q6","aZLHAyiyGs7a":"8Co6hoToG9AS","W9RRko2Zzagh":"DzY7bM40CcBx","sz8JSIDsNQZc":"yOxqimEDDN4K","oObsXsAySAzV":"NJ5P6uDukt8V","HR1A0OEGpE4a":"843ZoS7xLQUo","TkMC0UM3fOV3":"xUuXvC2LuyIK","qgqI2q3RCu8M":"u7bYlVr9xqwX","yVQEgGvNg4qz":"8HCl8j0E0gB2","mmKmqPMImUEG":"Q0kTX0oOTTUO","SYaKUtAzw2xV":"ZNVZveRCEhL8","XkBwMxDbrTnO":"n8itU3CVBDl9","phUrlHPYf1cL":"lf8rt6kHz8PE","7fZvlF32Upne":"HWBGlXKQDv0n","7bkygzzzk3gc":"KbAN5fMq27hS","lAidacTmuVQl":"e4FS3z7SdxVL","Wqig8vP8VIIU":"0zmVZCmaL3jB","Qbhs0HuBUC0u":"IqyWYBg0msdu","QmyO18L4guEJ":"qtms5Tj5ffpC","G6e9BwAvS9VV":"7cU6Djyinxwk","MKEYWhWBv8yZ":"RSO5GJdB2RK9","EEjYRHjHlgFV":"WaR7AvP4k3Ih","Ig3gOGlf6Qo7":"u4NcScDXpVZ7","Qi7WwUXiDdjT":"GFyOedWoDU80","d8wzqr3G7SgP":"lc6q79xDs7FR","ukj0qKPO4Vbv":"yA8ed8zQuMO0","cHEQg2afBgL4":"RfSsGEYxkQBE","psm15cnKGsJg":"bT2GDV52URMv","jCTdD8N38iKU":"OVRKQZv6ffHa","AsipfDVqsT86":"tGnOtBhfAWsl","NYW2N4QNI6OP":"MFq5gibghQsT","gv2GSIr9lM5Z":"HAQnZbvizChC","wX84CtNwXuRN":"OouGIwOO1LMu","wFNZUsreiuLm":"8Y2j2zaImNFP","qGjzIeqLIIAM":"I9ZlnRPWAXOC","QGTvZpj9H3tn":"CqYNGAWqqEzn","p9OcuzdT1Gqy":"Q5qYLPIwMaaY","HHM5IALtHZbl":"81hSAIvUpSVi","DbGiuWdy0VPx":"P96XDIVN2p76","LL8B06TCy2cJ":"hcPESbLrbzk6","0EZHzKPwZpCK":"d7R86wraYhkE","slwBGrm1Sp0l":"TCP9KMpPjsdb","uQMY9PqFDcdI":"cdlbEhX6mJyQ","fzXOAfUmrxZi":"gfnj9TMXBrbG","5UUjKT3GLFiL":"lNZ7GJlNfDbN","WRAVDtQyVNQK":"uS5ikDluWIPB","geGk3e8OrEo6":"9vorprzbtKS3","dNFv6yXX0NA7":"8WChh425fYjR","GmE8lY3DWyVf":"t0jZWXc63Pgs","8FHnBpcAKzIS":"aFNJIwPjlZsu","0Zrz5SPzODYq":"cN1qMl2QdCfu","IytDT0Jfd56l":"zISkVpkcdzTX","9t2QzDmp4Llw":"xt1Gs94161Iy","j3wRYUUGmWJc":"SmT0KI9z7KBX","9tRycov2rBQj":"iq7jq2OLGCsp","I3M4sEySBU28":"E8IC8IdT8vLX","Cb1j02poGVLK":"r4qYJol2NfOD","7NUkFiuEQcMc":"1PHTa4UOQjKm","BPzFxyLi3Xjx":"5MMLXzq2pEx2","m7bm9JFD2Q67":"QlD7ghtzKTM7","JatMwO7O9sGe":"ojFTN3TC0Fnp","O8EbnQhnFprB":"b15vOCdBbAUJ","1fYcKISOoFXb":"Q6YnFcpTS9QL","RSF3Bl5WQc8c":"tC0qhhFr3xAA","hp2hYS8ejTv3":"W9FXgpvce8gE","zjlcYiQC0jSo":"oVL0ZgCC0O2k","NF3Ucwa3kQxk":"rP2sb0BA1Iok","WgRtP0IlVKoD":"ZtrTV6Y9rDAS","IXNtXn5P60sL":"hLqOA6brQ9Oj","SPE9qKHE12s2":"Kh6HnAz7rdsr","empckoPfskRs":"N9FmxHn7DCWF","95iAagMeC5vG":"MRRmzQWOJUBD","5lRHrhZa5FN5":"UWOA7wlB2IdX","9NYSnOjRF6Cz":"7Le8ZyplEzfI","NU8XJbgx3qu3":"5LA3Us5gXesc","Ca3zt25qlgAj":"2p87AyaOj6p4","mSCKozJYbEHF":"zYeTwvd38SsT","colohOyp7SMA":"GfbELC4cOQVY","3UKtR9XzVrsX":"u98RaXVw8J5K","4CKVf7qvSDKo":"Ik4ZrcnT4pGx","YT112nDzvHmu":"QMAV9z2G3UKB","yfp92a8fGZRP":"vvxZlsP9Y0O7","rfGl8AndL0is":"IX1XW5FFnEjp","sY0pjiA7BIPt":"IRQMFZpfp0ph","WjTFJoq0P50z":"CqPnyda6l7RU","6FRNsjNCmF1e":"WiXrNbiN8zJm","0m5jKAfuvhF0":"XlVx8zDw2B83","vqRcPqQA2muH":"13lHUaCQ8MrZ","40lh4RFQiNty":"TroSjM2IzcUi","3fHa6qg8P6zJ":"1F6CUUZYBKPw","hm5ZLB8p7Iyg":"BsPlibpo5Bt4","FFuf5aUwaCHL":"adpHcwgFUIoo","ZBz7waySsHrJ":"c8FgRU0gjOCP","Dbmxrm5FAUyb":"xLj2m5u4BKga","z3kWdsxBPB4r":"swjXZUHPzmBR","NmWpm9poiqg7":"4TLiLNhIiFNC","mDDNzwAkfQmQ":"5jgD9pCC21hl","1SE9LOX2js94":"pFkffNPfc108","6p1RyKA3RemJ":"HC6wWXuSZ7P4","tmoOT5PNnF8V":"qXpJbY5pG9iZ","RjAX3psmjYR3":"dok880ycbzgH","jvADrFf97nTr":"juANGZwS6Jab","VKxz930kMf1L":"o2FAarOY0phx","vZcZ1amHMSV3":"paxZBXjgWCbF","2FPtwLYogLdY":"e4R8LCUfUhHP","hmIFHXBc9u51":"qCDkn5Li1eGD","oi4lx6VNWo2E":"XLSA7X3smvgS","0EBQwbXoZfyi":"yf9AkYDJTulj","cdcWnMJfeLId":"ImABRbghCWt6","hqma6OyifaCj":"tIavnWOF2NQN","eSyOQ1HjF5Gh":"iWFhi3uUjHt9","aIILIYDr1pnb":"EtqPcgXFsade","wcugaRpoaZnq":"xIbslRrRfEqV","bAPBxL6lWTj9":"lij04a6L0I1j","jjildwUMRCTx":"RocOp7sKsZk6","KxcYPPWkkx6k":"a5V7asVO1bRm","gzufy2T34Bee":"4nyTTEYRdGNh","IyFBh1HeeCVW":"hu8jDVnSOyhW","cMP6eHVCnS2Y":"OaKvmjJZQDEk","9cPGQo79Kb0K":"5lib7Y4djtLQ","D2zeeuXnMSAd":"E9owscl9yDxj","TlRDhI5vENpC":"6l7oioSQS2am","ZRpgzCuB1N2W":"r9CPEOC6WB11","hCqVwOyIAZuH":"Iy4MFEgcnZm8","aqPQfjx7M0i6":"OJK78ZNYibKI","ta611uTNkMS6":"hcaVtCq6Gqec","GTRsF9NKgA0H":"YixyIUA8gkLD","t4h4ecCDILRJ":"YUKM2PJqPFtf","HjBvaognNL7a":"W8FO79SF0coW","BP9tVFcXzWBu":"pDYWVVYHci8j","XuzZNxprM4Oy":"NOsEGokPqiI0","QdEDg0jDa7T3":"OQh5fWGX2G0J","OFdBZBlFQAVi":"GTJg2sv9XPoZ","mSNJxzD7HXVy":"JWO4cEKQTHpY","WDruKG1L488Q":"1HQ3gAb4QJam","CYCon4LwgFOM":"B4g8tm0QGwSB","JIK0uCxuGwPk":"TPG2eZaGez1j","SiAQwOLD9fhz":"QuHrw531jlqT","fLgICa6x1gfd":"KYN7aH2tRsXn","LohGKvToRZYK":"P70LBcjA7y9L","XKVzAbeeqkUS":"u4cbe0s0vevs","B3JpVrGqmW5d":"RhyVvvsQNp4k","0DJSH40flKAV":"h1Z365aN0sr8","DRFIILs1RUZz":"YR8QK0gNspoD","OVaMKjVnweiQ":"iDaB7fKd90zd","fql3lvHNEu6z":"lNrC6wNWN9ie","XCX8WvcZ9FLH":"Irwt8ab95ul1","VhLRJwVHHZeK":"Dx9BXworbRiK","ErpTlCbfolsf":"NASKKu5jKGWf","3bsR89h9lMH8":"wEUwXC5EUOtp","54ewU4IKAdYl":"rAXDHzzNbgQu","7acHXU3bAgLc":"IPWFqFEUwiOH","hcoRyjTP7w29":"aeYFPu9Y1ZvQ","kAOrCyjTG9Hv":"uR0jXyCLypcK","sUceiXhts7Ai":"f3VFYYp9Bxx7","vSxvkFmA94sS":"E8qvP7L2TXhf","WY350JWAE2nz":"9YbYlQJZMoLB","Yl0cOyJVRqUy":"akdekgSkwTkj","PJjpgqykjyS8":"G873iBYKEDzR","pV2Bqjb1MrDj":"ZsPrTgG4kJdE","S3PRlwSbux7s":"GCKQfHjz4iAt","KC7m1hCma3vM":"3b2oXLG7jnF9","p38gyULJ4JoG":"OyzwVWHAbk8L","FdvBEEpQbuMG":"JnZ8Jn8YurvN","jLxr2VNSUkAl":"7NZ1vgkGICpw","eBEvjj7ecFmO":"UEHKb5WuvBDK","vTutsVd50FjZ":"uA8FSidomHx3","jwiAzs7EoCC3":"aCs1abXggH0z","X89PD66x4jBZ":"e0EiSHkp4uPB","FDiUt7L53HSU":"zYdgrwAsmNZR","md9xjKwPyg40":"gTHgNnuKqCCb","99SfxZ2a05Sh":"GfjUTiySPJuP","EvRm604weIBD":"Iw9Q3mjsoHNL","mcoiIijP7iNy":"hbjdHGGMzfzV","fkAbrT9BcZQX":"isdfkkpy5Y59","KCB88ymzT0Nj":"saiQghWMsl8s","WucZ25BdL1rg":"2fMa3t2mJXUC","7TfshmjLwHVn":"qpSDY8GqJF4x","PCgIEbFRomQL":"GVQ9dlv1uFxc","rrYwKAE7KRE8":"KpVuixaI6an4","Cq2OVLVIZBra":"cRQubqjM7nsX","rZ2aWj4ZWNMl":"yswVfRLDaFa3","BsErasmP75TJ":"tYBw9l99iZUj","RMJfoh7piEAF":"irpjxcfGaFMc","zCdepi1aSSv6":"65Id5eoBHDv9","MqX1VfOwkKgm":"eLqmg4m9n8MH","Osp0riqrdJ1L":"qoaiJZN2LUd4","zQIGMzy5lM5t":"hSQKsocIEyZG","CQK7tWY2VJKO":"Eh11tQLm3775","IEsS7SNDXHbr":"5nCcFxjgueLK","cajxMehMxZEN":"ikLxxrAs4JLz","I5l9rIwNMGQm":"OZdUjGFeAbtg","5ocPSQ4TBSPJ":"RFK4NACyYlyn","H6PCtAUBSukJ":"NzmgXB8gitSU","NCoNnHdagcIe":"3hVXYLNz19vw","VC45PBROMLAW":"4g1w0DKdfb57","oBozj8UOhX9n":"i6rh6QQ2W7el","jcra9ZtAvuOi":"00sXTLsGolCg","QfDHHnOYtuqn":"1vh7XJSBg4I9","SJY7tU1XMgaV":"Mtp3f0vPJh5G","mAs8JmhgBzOG":"FGC4LcI4aK37","jGE51M8d8xAa":"ZWhE3oxeaCs5","nok4IorgbJ9d":"uknwhz93LZUB","N6gc2xNMqClM":"INjvs1Dzw3b8","9JMLgIL53L5s":"0rw9fuzc2LOC","0zuukikJkVFA":"NO7Daw9B3ZZO","S9x3WgedfgOL":"2AiiyHI7T3Q8","2CaPa9twHN6q":"wHUEae6SHour","wm2f2BwjNn7a":"3dUnyFNDLdbY","NTQ3FIEPBAS9":"DA3LhhNMVEJa","P9yC1m1uYl5c":"82KIUYjEqt6v","J3GiEbBZxAIq":"3CGPBtg8Wckn","B5tXQlYgVCTW":"w2g2uc80ehRT","jcQMxDxTYjjT":"LKPrZMGupnRH","9ns9ep09bI9J":"eNiq7CsIJCzJ","RWQZTjoTGG5v":"hQXexALi7tEf","8KnkP5J6sH9L":"xalTMKuhe91B","CyYvFmIGzECZ":"QURjiWkpzCNa","dmLZpXj63BQ1":"XXJyH9zdrCA4","kr6LW1LPkvr2":"K1uUA5bysklH","ACKr3U5v2nPq":"owjeKLfwJRq1","D78SkYXubdpU":"hFrde2RWoeOV","dNP4bU2YZyGv":"R3j0QzVxv5Mh","c2Siwx7eZlCJ":"lMCdl1y3FVFG","3vYyuC9OKfBv":"8xyajoU2BGaY","tFvQg1uD6Lri":"FNrnFv0NNhpE","QAdLUeQz0VoW":"KHsXFafweiyY","wDopTEG7J0nM":"xWeLRgIzpxWA","ogMaNgwfHPXe":"xGNsSbSAv6TS","t9zHZXz9hwLo":"zzo408hMX9et","9ChcUUibj496":"XA3q1fXu5N7S","GCrGI2bG9MZW":"LHAo0v7G1CEF","EGXOHPAVuWut":"SeFYHwuZRJBj","sNs2aMEVpTVL":"El733qHknHd6","pTPYGq82Gjzo":"3hZEkeVgdemk","juFH3qLVB3HE":"eKxUGTq8nSae","g8xlH5C1Ayut":"4ISmXJCvVj4I","3vXENx0Jo2au":"m9gvDZezSaGB","6sW6l2CjG956":"hgYYwbs4cWHN","OZD1rkHK7pKz":"Lj0yHd7f9bpE","Xm7PdaJVEMK0":"Il8PDtsvv4cu","EIPGV1mS5D4L":"JewTfmRc5kDt","UsqGlI3Uwps8":"JsCR0Uaub0C9","gaRAzD9apRDc":"xSr2iv3r9yRZ","KJYsS4RZDHN5":"aNLZihjMzJtr","DOTFyqvnRrMG":"bfundcHEnnDi","9XX4WEzOHGpm":"lrJeraeVE9gB","1EfVbsxgSbpA":"EKxo3tEtX2Sv","tWUFNCrqREh1":"ose7zzBhyaAu","KA0nGZJyn2bs":"1n9WECFglQ3x","beQ4Attx6DJI":"aAzRb8lx4OiE","5ZFiNsVhCwI4":"Uj4ssFOusca3","lMzY1sLhrUaV":"TS8kOS7Sa1TY","zhqJj9cPBNgl":"tf2TsGqiOIZN","lPAz3sET2oJv":"LJ4oPwZ16Lm8","c2YRWtNNxtzq":"DMzjeEIPGcZb","laGmamKKI1qY":"CpcjXLeFsJox","9fPojOieZzcq":"ohIH4L84h3eo","aRZGd82RYBIX":"X9ZP4h6FJSRk","VjOvV5qLqp8O":"hvcJlA3bJdgT","mnPtVauh8QHA":"SCo7S7J5rKbd","VTpzzDeUo2EO":"6QHUjE8txPFl","DMFEVQyCDa09":"YqOJAz2S1lJV","fi1ewSdKr1rf":"9PTHYauhWWyO","b6qVQRpSNNHI":"NpxhQyx9WUdJ","pi1SaIzKsr7F":"eJRaZmHFOMM0","eJYjSY6jZCc9":"xBwjOVt2XkuZ","s1Z4wt7mg73t":"hzX6G9Fhz0iV","up2J6EtKMk2T":"udv3u1A1US3I","FWHiHjY12ikN":"KqO4ZRIZg2Mb","7FMhKn3Sqcm9":"dGNrbrQhm2Va","Sss2HmkvVJ0Y":"vCJXaZhUYv6G","FeocOxPLTRij":"83VorNvsxS2d","9LGIwOo0x4bg":"vk1lM49Nk5XX","V9tSt4Ud45V0":"fs68D8cmdWGN","zqFHC7ARDN0x":"jUI4Ih5VI9ou","ArAX6POxAkGd":"5Zz5SdJJoBMk","ZekhsUJzYQyx":"Er0mjvE4d6zb","rTj0XEZkskwe":"8nt4ZpeAgTRt","Yr01YYHM3EGS":"OiR2U6KCF8VD","IT1uW3nmYAeF":"6tFpmLbXJDNc","oQgOaN7eMd5W":"0hKYQ0wim1xR","SOjmxQc6jKze":"k6yd8INnev5o","boc03WaHjH9Q":"YTLRyglkHVfY","HSh6JijIRBUE":"1FENIk6dT9BB","1G3glyPEOlhM":"5rSn8qqbvSgh","0pifb8K2SS54":"4hVXR4A1LLgU","gWAwuoGePRri":"Gh7Y5QcjftdU","nNUqLRIKBXD4":"1wwXBxCD9tNg","St0S9wBVx0cQ":"DZmcvMLpoSvt","MWiSZjmUuygX":"bYMuzda86IwW","bEjljX3rY9kM":"0Ba98VfxWrQt","8utzG8nKmxmz":"LfbpMhdYTpuy","Aae746ioHbTB":"eCkVzi196pjT","yfQjziktbxoe":"AcFb3ADzJUdf","JkxaX3Y21JNc":"hlDwwPBjjjlc","UfQEDbI42Faq":"nZwWh76IxnSD","QOlOp29eG3q9":"Nxf9EsNIGiJL","vYVP23793R4d":"BJzjtY4gYgMC","xGg6EtvmiaCe":"YgCzBG01Hy04","dsfOrBMxXFjB":"DkZtx7hzihmd","ivp1hObZ3RXY":"zUl2IMK4dAP4","WBTefbEOynzy":"VBWzYarvAKQc","9QXevBwHLa1t":"bs3cLUOsHsw7","d8bdLw7d9VSN":"5HHC6N8orfN0","X71NtRidgLAc":"eKUsRNZOoc4r","wxP22xRoEpnP":"Qy03b2qDmujm","s8EQ1aSChLX4":"4vShtDjhQ4Y0","hZWfvD089aJX":"xHvLu9pHcDu6","LEhbc4E1QIxi":"chmjwtsDAFzA","7PkPQpq5M5MN":"NdiR5Zis2LUr","sjrfq0nY9fbU":"V1WpmLqQsWdK","qp30eHE8ep67":"D5QV16myW21q","JrNudnq9APih":"RBV6IIaAJuGT","Nz8SnbJPx0mx":"deuXhcH68wBP","qyHcaHYonm0J":"YmqjyVyolNkd","bQ1jgMayZ1bU":"7lotuMEoCdhO","BpnpvVsn1N9w":"l1j0hRaoWyhC","E4Mn20ZZzLqF":"e43W9zr2koV7","nQwASsq61lgK":"lScRxIDgP79I","aJn3fJTpyLzh":"AVjfRZM8Crbd","mS4SQEFiv8fF":"9VkVlg1t5hBq","etMlGOiDJ5Xm":"awAkYzM8a8eI","xGEhNEZY1LBs":"VyxIZlRKWBOb","e1GbxdUhDfpV":"euAmt4m9fsXp","zEUVjXKMDYp9":"lJBKNAZpTSql","owy6tF2f4eqc":"XhXwFY5NLqTC","chCDbYNjwdxR":"F6vEWghTpcx4","CBuIrJKlXvSa":"oixfdc0maf2Y","kbrvaRTfNorC":"IxLC8fDoWTKD","X2y7FwXpSGdW":"YMblEA3mtwCp","dzBdibSGBUQ8":"QwsXdm7DaWyb","U4S4ETAgXbmN":"egV0gvHsFJcy","3dTZSYWrYXvS":"dcJi1eWXwR1D","YjPm2iti2Eg3":"prUJB8NQnPxO","8cqujnxKOsob":"5LGaukBQmkHX","xiLdFwFW50aA":"EtDvicb8Ytd4","0Rf3rPOVoTqA":"h8wUjn3xBL2B","XNQ3wmU8dpBL":"CHhq089uFle3","I5To8DbEjOi3":"bPAKbytZpL12","SlTcDABa00m3":"LpaCrYaFJhV1","ik22sQb1f6Y8":"zOF5jOqGkgEb","QKG7FPvn7dxW":"alB2gd5fZvvr","a4ZfTZO63hH9":"GucpUya5YpQY","aw8EAmBTdK1z":"XX5Q3W9uJi8E","w6ftlgQNS2S8":"cJ8m1AKLeAXi","gxRqEOGH5TYF":"ZItncp8nHK3F","p14z1Am3Tby2":"bhssZ5v3GBgw","29FrjUkG9OPt":"Vv1BjBPM9q3o","46hNoSRYrNQf":"ioOoZl9TGatA","u1cUtOzbrgeS":"LK3nDjrTntwx","2xIBrjk6qzxH":"Gof6VvvR7U3p","yQ3keyOpIkE4":"XA18knXuzjRW","qHL8fZcw5gnW":"5CfPMcuDOsil","kINpL1fokVPm":"Ir5oXKuwNFXk","jyz6Y35b9bTs":"zmgYcsa2hdlT","CvxBE6u8htks":"pRl29KzJrqp8","e1rTOk6Kw4H1":"6X0CJDIabE0H","jSPxwpvalfXG":"uHyIzsnk45vs","yDmw1QQc8qS7":"HkrcX8UtMsOg","Nt1a5wgdIt3s":"Ph47pF6ia5Fu","GP8rMVtQMEBF":"lfCBfU827zG4","p4lEQSYSMK0c":"I2Ms47z4Bj3V","i1V8XxsbpME4":"ZfAUhxNglc6C","pTwQz8yh5cHh":"sAOWgeXW4PQn","rjwrwxQNTY3T":"h54Z4dgN9ziX","v2hc6dmrKvCB":"15SCjF41dwQk","b083fHsaSkh1":"XyFEyfLJX9Pm","WDPirgiL93Je":"cgKmnqDPfiz9","QJs3ppFSAYy6":"i2N5Sj6ysYpL","hSjFOsH36xmi":"W6fcdOBMk0eO","KEVh1o5k9I3p":"SnwKMzDt3W9m","qESgUIhRPjVI":"lJJX9iCFGAqm","h5k7aOsRfWKG":"L3LvTu7rDIrc","4THXDlDlniZU":"7zNa4eWhr111","Z7VLH4SSKuFV":"qfjphrAVJLnx","gtTcl1JiN9jC":"cr8poTUuDkSQ","vWy0WmuL3q0a":"jEgrOj8HHSQr","joxsCmMsdKoe":"NCEOHMmbxwBu","8E2DuqnTEFgT":"c3xz0TuLBHbd","tD1k13hEUQHe":"tSRK4AZAlvJI","GKovXmSBPdMm":"b8ACGjiWgQmC","1GBjuRK2uwft":"jlG4hLSQzlZr","wPF68optRqbj":"hSId9w6A18ua","xf15zvZxlNNB":"6GbveronyIrC","IPQs4b36gfFq":"HsX9NhJlMmNa","y8CQc9TkF15Z":"Lg8X7iR1EBqT","70E3UJkSKbZ8":"a6L2h5WqpLZs","1DsgIxFxxHE2":"GMRl79QoPT4n","RyZwBAF50xeo":"mqL0S8rnSUfP","GhWJDUVVMB2x":"XJsrCSW13pyG","1XsJZg7jSixQ":"8GCkWos7FtUY","2mb4MnbLO2rm":"78f7o3q3Rvfe","hxbDvPXRjS7M":"qFvqJuEWKCq0","GPxCPo898ULH":"6DxhPwD1aAr7","LwYZx102Yuwa":"NRE39xQMzFWE","l0lefUEk9Yz5":"USYm6TBFuJF9","qE2kiSp9tAzz":"3erewBIXGZSX","c1y77fZ4yWsf":"C9TdNRX6ysN8","WOx1Eb0HcDLi":"bYeCR4BoqdoQ","6HBsyiQlmpRV":"WB7xA3lCVnIi","H5ExZYMOeiNn":"6pKqsZ4VjFhm","Y6B8LOAnPF3Q":"3EBqw5kLz4eM","sC7QrSWDWr4M":"D5gIKe6iIP7O","GiYpigt6XmzJ":"FXnN1zcYHgMD","usuO4ArltdB5":"8pwODe06UGdt","5SfnFwNHGvMZ":"X8ACzge3G7y1","tBT6RjJAOpiy":"YIJUeAD7kXGt","ryohfl6wXLJt":"CdAF83enw5li","aoFZbYEyNlx3":"zAuzqKsZGkLA","D27UnsRLIlXm":"TjjLtxpoYqET","GJ3BGGdYUNyG":"ia7EiJapaInl","BO0pD14Efo5W":"d9NLAM5yfzYk","7Ql8Z9QGEsWr":"pMgS4HZm4aVi","YlLowp36ppEk":"PJhEemqnS4T9","7zSRyF6ZJbHi":"9IyJmEWaJ71v","erhZxeHvMVtQ":"z8uEFTvVptQj","N1YBDJNbnvFH":"vedlrj0stdrU","lvijh10OGj9f":"ZUsrmba6eZV8","5XpqqDsotq0c":"F73A3oDzty4r","XXQDZMtiPOcP":"w60BaQWjlTXh","u2djQJNNj8KP":"DWmyYjLcN3zx","9FwdfICfbbDX":"Vf8mlmtqU1H9","1utjRLbO24ig":"mrgZDoWpZ7w8","OE4VItHFRG3X":"nEqdOWi7vWIb","Z21dtZO2Go6U":"Aljc3JqinjFN","RxstUX45rWe6":"9AK54EtVyLwb","7U4mwsDFvyxf":"iuf2XM73vPly","EM92ZFqtE37w":"usDY4uOqYWWN","9mukpMkeR9vx":"VJRi8zRyN03i","bG5mm6XG4rnu":"etaGGxH62Tgs","53l94DcYmeOi":"oBJ2mX6hkLjk","QXqv5oZNs8si":"XjTZk09y5sKY","FXL8xXSmMB48":"uUQfglqzojCs","A1trx9PUOuNm":"V0FLuPn2Q4v0","3MBstgzDes0c":"fXpAUA3sXsJL","SrOnuIzQh6Dq":"V0fn1NSmD6IZ","6iBOmoQdxele":"QWVZOtcFe77j","p8gJUkb4vD6x":"aIuZxXKInkrq","OZmFQzoMZVgy":"Q7S680UhcEoP","v9lPWlI9GJ2G":"cLuHemD8tBFw","HfQFXAxfvNwK":"iqhNJ2845bMV","8obnv8Sysmru":"m65SJaGjszLF","lcVaOkeL4Cvk":"4QqJU5fNLBiP","QcpjDzLQylBw":"MivfhzUCUXDO","QeBIm1m0HsKQ":"PMYHXqpDS97C","nvLdlRwFhpGW":"IPUnLHlyF2tR","QhUwZQt7ndQ4":"SFvo7EUciG0w","JkJK2RIr6Wk5":"FMb4Tw4UFgyM","KSdTH3iWIUHi":"3j2bElOyDzUH","gzVPThJC4oDg":"E4AwGPAL8IOA","Lg9DVV6RqY9M":"9NGT1lyWy0FT","YwhTZBUQzdk6":"igWY96jJNzVi","D1cNlAonCdu4":"ibzTbnJZZ8sI","rj3UbL9TImy1":"pIYsspIMzAtf","fOVTRpyBIZZL":"XTN3f100uR64","uAl97P48zg21":"EHPgOI6p5IJ6","6o63j2IwX7tQ":"Hqcsm7umRc1K","Ui0mTE9pxsVc":"YPN9Xru05hyH","sJJ8Q2zf8eyZ":"jtuaaQxZGOug","2BOmdL8izrr1":"OsIV6AiIEEnk","QtmPcmmgArIG":"D8UXMdIw7h1P","Gu47U2JEpyor":"h8xvWIHydwSk","QsRf7pDKvKA6":"8kfStjvUr7Mi","JXHCGQJ2eFNd":"XyqjWkeg75wP","x8HGwN0uTmbF":"ryh0I3AIIaEV","6AGITc485Y4l":"LI7iXlUkOG2f","z0NDjbBkLj1B":"vzG3hb8iniL7","D7Y65x8FoikU":"DmJpOuwKA4il","Mz6erU0tVg0G":"wDcHxn8AKL7V","xYSj1ujxm7fD":"T329IkZIHmZy","jBr65UC1wqPv":"1VU2r9YNATd1","bPIHirgxTTGR":"MRDlbjxWPQCp","QfhqZXnIjJzs":"K4sFTIWW8d0b","YllJIBbfHqvG":"awSBSqqbSg1R","vuGp2EDImefC":"obS7LjdRe1ZL","rYbTOeNDiiuI":"bKDRyE4NyoIB","SKA2H0m83MSQ":"s4t2qRAItUM3","1R65y9jqhkKn":"TokvVbhwrjlI","iM0XCqeo8zLR":"6XwYF1BxQrYV","yRl4ztljiwkw":"EdG9HfvmdsXW","sEoqYZrth6JD":"S0HyHb516Fym","E8Pj4egUa9Nu":"KhgVKMeY4W8U","mYl6Z8aVF6m5":"t76bHA6hL8pJ","R06qPGn4Fu5z":"poqlBbS9FCFi","JBatfk9qBTeV":"Hf61CH2Op05c","DeeuS4jLzkIT":"B9k2BtG1Ocnl","pn73UD2FBmt8":"18mxCRU5dBIW","iEkv7NXJ3BWo":"fzNL4zPNQj0b","VeSFIxeDDjPH":"tGohxFjtk5FB","npVHWvjckHoT":"r5CMlO6KUMTY","0PGHxvUe8l9W":"ZVW6oaNNnvHg","Uk5cgzO4ovbK":"hrgllTD5TMxs","OlaiDfl90Zmt":"huQwIy7tzULw","dwEfDrbdEM4z":"bQuMs7IW9cxd","P2sXSZZqCSPE":"aEpd5kdvyxe6","DOF2Pnibf42L":"x2LJXBOgBy3r","5bgQV9RQniLr":"feWq31ksJiLd","EqHAX8pX7ewr":"WXOjTrnlJohq","c2vfJcNWfIbE":"38ir1loqSoxt","jywM4UljhDWK":"qfatJmacm34X","cmmWLIThqbHg":"fuWjvRPAxIA0","kXllJkHsMSOa":"hARCEnb1nExO","33z7Hn4IJvEX":"ggp7vyClKqkB","lkP9EsZcIaNz":"JOlZYWmVu3SB","41SqkS3vuQkw":"w2DgO1SOO7iQ","0C2uLFByTOJP":"3VqVXMKvjYBf","K3zUdP0NlVxI":"c6RZ5BLXdQoj","zKLkwR98TNAr":"MIVXophWTFEF","chWyUV4dd7wN":"7ZiuatCnoAwd","h5xPrLbnw3Kk":"FR9v1OPZIKy1","qTXG5y1dvdlv":"HtWJi3GDz2Or","t0PiOqeRMx5t":"NbWNGLYG6Ouc","9HUeLYGeL7Oc":"uuBrFI5IT1mN","PuU61rKYSibZ":"2UvPl5YW3NlV","Gwvgl01nhiTi":"YWp2UhlHT1Md","7tRUGKtOXxPg":"boflweNp447k","xD1IckRdc1vV":"hhDty37LOITv","tfkKJtQsMSa9":"tXOWo3N0Uttl","tknUEOQmQaWI":"H4vbOKu2A5fR","uupwRoqehB7D":"NdM1Lf75yZw6","bfDN4fFadPFY":"p0UwZstvrmRD","P6yoxGZSQCpQ":"16nKzKQwCUud","j3SxQLH2MtEN":"goNxS6IUIjfg","LDNixuAAAaBj":"QvrTsyDBJpnu","wjg3lOe26Ey8":"kbFfovBegYgd","utFpX607bOUT":"5CVoCKTQQzbq","G7P8LjJ7b8RA":"lSwNRgfPtkQt","nSly29i3V7mD":"7k6heiNBkucJ","VuOrj5cqCZgl":"1QkNvNeudPuj","FxYOKzQvXhry":"eDoGB6NnyXSX","OUlkPeLGcRjj":"NuYqvJJbXT5I","4WhFk7WsefGr":"nbcvrFcbIDb8","N47NPY9uoPCr":"OfdYYxBHxI4O","dBCCxfwOOfqZ":"IGss1xW8qmlt","vbERJScNNk5N":"9aM8nnwTHUmq","HQ3lfPD32GQp":"JuvEjNTl1yBw","12chEHHyboXa":"wKfE1tOiecxC","yu82MDS2GLEx":"FAZBxdB0Emo9","i7f8nsqrN3AY":"wbu6tGpYmqTd","JabwqUhrSs98":"DP50RhJRvTHp","xOtNFqCKju6w":"zgJ3hwzG17Bw","RUKeW16KDYSb":"W72sXOGGQbqr","IhWP2Vn8DVQ1":"kVNfr5C2EEZ4","J32cVoNIkw0L":"7WtKqEORiT9I","u8c7iunkgn4B":"8PTtBXbfJH5R","gQQreoQ9gq2G":"aooWh5bjO5IO","fNWnktHcZFWB":"yahjHtfvmSqI","vnlNSHjxeHaZ":"Agg9Lwvfwm9B","AtmL0gHB81Yj":"Ml7YmJH7uABd","xihnGKfMGrN2":"dOV1lTgkC35t","j9Ew8wYRRpRR":"KY3i5AdYBJcJ","DVb75CsmRbQ4":"t9vYIc2ME8aY","NV8ossdli4n0":"z1UM0ulIaPD2","XTFesfEm0X8p":"xA20j44zxRj2","OrREje0OHk6E":"wh72Erp2oPtI","SoyCp82biKZY":"tGdcVdABEYgw","OaWanPP5fMkK":"GSWwhG0FMXVh","p5H73NenYBT6":"B6lZLinYL6iQ","0uTcQJZAZDbV":"4V1u8WBrd0tc","4fnXFiOKdBml":"YQEfgNY5T4R0","yYKJpSPPLXeO":"Ug3ik9KlnRv3","08sABvMgCI8v":"10nVuu6bsNNt","nqHeslFcjJ2J":"CAHhYj78yhs0","qKlKagEtlfpT":"lXRidc8C8w5J","JR4EnIib106d":"H3Sv9x3f7Let","3VXwRiAwukdN":"232CsKFgqWff","GPrdBQ73S5q1":"cflruwNRCKG9","Bn7To1iC15sX":"647n0t4gMmYg","0c31hycwfKjm":"F9yfQIDxF2LK","1Dyr3OURj4OR":"KgruB2g2OwEZ","CS6JUd7wUFdn":"bQPoPeJKyMFu","mPOFxK2gzveq":"TfB93KeMHIUc","6bdSZhbTyuCo":"izvcBFdGZc3r","bmbRqjlIxKYg":"E5YdgwRT4ZOv","Yd5Z16Fa8Z2u":"NOnzSaxkm07t","7e3XmW7ZM9kX":"9NYrCuD2kXcS","FDGcqfR8P5P1":"ApYFHCZ2zWtr","ZNy5pxwl1lD9":"lw2qPnP9W5sv","lWfQxXuVMpAt":"YDgtiMhDlIBD","nhqrKL2Ku2kr":"qVJEu7sX7YHs","hMDvMQiaZZKc":"XPsiVnN86u8m","p3qD3fXdqDhI":"lJaTdO0iQECx","3JEhzI5aORxb":"8zhGdcNObIms","Sv9kD1iZmvzo":"q8qzd3J7PQ80","nGwub3zRyS3a":"IsB0eTdqbtgQ","xRIcqCsywwTD":"vkc3FfwmfvaS","cAytJM6ldMgL":"gkDjxgmhFrNe","J2kgleWtTQr7":"IdzV371CWcco","flFwj8VpOSRz":"I4q5XlQaoFCH","xOT0JDrENosR":"jTXUtrOPVo5D","9qZVRksmcbSJ":"Pqy15cTacMhi","1Tnb2M8e2B1C":"KBnef0qmWj00","O5EaCnRVrmrR":"jHGJVTlnb04j","LWmJ3fqzpNQd":"YBkVGoIbTDCi","Cwfshwlr35Xk":"rHrbWXd1fQw6","pUhsr1YcV5s2":"Nizu7l2syFey","7n8r8y0bUJGp":"UZtWFGNmrZWT","gGWjfFVYTRwW":"9seTTEowkuAQ","Jlw6TC1EcceZ":"mSetkgK3egJU","iTHlDK2YpMFn":"FLbRxSMfomqp","jwn0tTKtuyH1":"AdcSUlbp8LAf","FQibdBTcqpgX":"e8l8iNlnYRfq","c7MMVBvS8MUR":"OtlEk9JIrzOo","31xmEechfzE3":"C4PjvnpmngzS","v0vy7TMK9tB9":"QBgLUfHphg4h","fTe37PzQFo0d":"pZV3SAGyqusw","pSf24RUJQS7o":"IAGXzaLGqEcQ","SMN8mVt5izIk":"QF23u6pAnGg7","1OIkfaaCYghJ":"MWDtqDdCYGtz","Njo2dk3hYgxu":"SyQz9ljgE9BJ","lsjPT6mUR0E0":"7uxvX1MULB32","aJaR3Sty7tnG":"anotI3dbl8Qr","jNmrvYVSSZc3":"VfoyncEw3qGa","wdUOrEwxDPbB":"5iH4VhMZn545","yfZKMGYOQ4eC":"fSUQwxYjNUUO","lgqO1NdMDcLs":"ujEw7pZfDgGp","Huunt7SqLiYb":"9QfDX96CC7l3","ZRM46qBlvUoa":"gQbxtnbGCDcP","r7rrWPDuodaY":"AFhjHLsu2zwR","0Js1uUn4bUNp":"f7BstScnvJgz","h1NuniUjLEVa":"W2tEYaGHjujE","mFU0647Ku3CS":"h7Wn1k69KqRn","yemDEBF6TbhY":"Xdl1LHNChj9p","Ye0Vj3aTRTEZ":"GKg1dppS9nik","Af8UZBzxiZwL":"bSWClrL3PRKY","hj5r2PwSkw9m":"JuJr6WjwEQuh","GuXNJLUftNeM":"Nt6aoVDVYurX","3stzkak38Xj9":"4P9EUVK5eo8b","xVceTcTW4A7w":"ba1WPt3nMpWC","Qjfgpr62zJ4h":"qzN1AOMV6N4P","z8uCfaj3yfqm":"tovKVs1U7nrJ","qOhFuoW66eWY":"zMfNgdONIDMy","xB7YA3QnngPc":"Cc7yt0oLA1a8","g9noreOvEfrA":"ttJWqg09T7ko","6Yx3ndCrHKcL":"Icpa7ALahCqB","sJ1wzCYfTRtg":"nI2QIlrtNNOu","6ApymWAQTyAJ":"3bVjf7m1zBuD","xzRvB1K2KF48":"qJdczyosYP6Z","WaZ5ulC1PzWT":"LLylmQcQrqEQ","ASphIuQLwMBD":"xfmsJ7A1kHLf","PzX53DtYKF2d":"4HUd1Ze0mSwn","LSrOJsdGSLGU":"CmDaDiXy7E7P","e04Bhjv4VK21":"l5K8iklOSdJ9","dUHWEY5ZLS8g":"t11N6z9YiaUc","PwpOVfybjYd9":"6s3NvS1OEgi7","X8eIYsLXFutS":"bqfh6vKAxzgi","PfpsrxxGXETz":"Vb3j61RHXzGl","hdxFu6dhJwYC":"d7qIcOg5nvAl","ZZvwpiwEOD8J":"ZpEKhS6WAqGz","oSzPrGmP7uHb":"rTkhJ2PGhyeg","rJyJlQ89RbZ9":"sWEgEj3aoNS8","7AbedqGQ1lZg":"4RaRtesTSzky","i2gGUFh3oKg7":"kzru6cZwrPrk","Gk3IAMEl9pWy":"at12sCRaGbgt","46j1SBmHDW8u":"e5kelv4YPhtJ","AF88TxJt8XCL":"69P7X9tyVKfW","VWBi8mj8IZqN":"J8IzquLm7kv2","qsV4IuQ5Yzos":"coAosna44FAy","HBn3a47IdOwo":"4VwadmrrG6XC","9CywxqM3yff5":"RbKrFhW0H5sK","nsM3Q8UnTMHj":"yKB7lYMXcV9C","aqNdGsRaFCIZ":"Sth9lXlZfiHU","X8CaSbJCeD4n":"XAZ5fjucoFxT","TopGvacf289I":"Jtd73AD4SiOI","WM7Z7TDctCYr":"yPiaPIbgwOqr","bNVU2GcRtCuj":"57uOrEt2wwvg","mMTWZCrfBU28":"wDWKdQLXartM","Z8553PxGBAca":"LezJeyv2bYKz","3gDoUOxiq7r0":"QsxatdaNO5hu","PJpCZfXqtuFI":"9fZJ9eLplKHZ","RFiDmBta7GY0":"d4n8VU9EDOqA","a46Ie3FnZLMy":"IfhliZUm6Qgr","dIY6XDxJuk95":"R4ACbNdSdANl","HEsDIdzfaZUQ":"mktNLNxFzPoV","OkTTbfOaERLV":"vaQgYyjBDLNZ","4V4nZ98XWkw3":"vtNMDXcHc4YR","8sAMsSgRbuCf":"8e3SrD5ays3X","iqb3nUXJAWkM":"e46uy493s3Ke","L4odAVd488eT":"vGXF5ykw9NOq","efEaqd26ReFf":"dsEHSQ68MNla","gEweG32INV5y":"jj1IbmMId94o","X78N7JQDo9G6":"G7P7DxuTOIdm","c60a4q3ZACoJ":"oTz6T9fYE2fP","JFNBZIq0FgA4":"qn5raHW8dNHA","CLf6KcDfsfNm":"q7ppNpN5Zq9z","xtggofflH7Z0":"Yg0SEcMf8N0B","YepB3AIoiU35":"k6IvzP3PqqGb","dMrLoc4M37rf":"Zi47Im15OquY","ZN4fWHRENTnY":"Flcm2LUZiR0I","ZoZX9yW8ZKJM":"xLYIPbyCDX1Y","mdRmy1rflXjY":"lL0kHLPE4rlt","iq61qAoSWmpN":"1Ag8OlleWMaY","FqOSRdzDghON":"erMaLJdQWeQ4","LeoDWldV650y":"PdMXLiR5sZ4f","bLc5VVm4R9Lm":"w9x8fq6oafpG","Y9aWjLX9aU1a":"CrAVWeoEeFmy","FV2tjeIqjsYv":"JkjJGmm0e60c","7Ti094IuYPjx":"FAMUc5odf1hy","7U9KlplA4Kjm":"ZCgpSqgW25rk","EVnwXpBz20O8":"dCtUjxjGG7QX","V6lr62lbP5Ov":"ZCbtycjRLNeF","bh9lCv0iBvO9":"QnA5Qww6FL1t","i8YDfAmGwIzr":"W4TyDOQHYk10","wkI0I5LV9F7k":"tdnAnL5fJckM","33rWSKUM6kPg":"DVhFsL4xvXrH","TuNnEXLRQD13":"3IQZy3vUp9UV","Cy0YYTSZingu":"Hk2LBeesrjlF","oQ8SYUFpFrH6":"BEPSM6KBVN5I","QJdMAyPJevX4":"rSvJbATOxr6h","8ouwpGLH5AKM":"72VAqAbVE3zE","UeA8Q3wRpBlU":"PKouqy7bdl85","7AflimiJ1pIu":"DubYLCkOngDl","4Va0q2ofOlba":"saiYOcqwL6Ma","yyLydGdeidi0":"ZGCbIR5jxXk9","0dTXwBMnFuCf":"OutOHrwBoqFK","GhwOGgaxlGaT":"W4FIiM8fQDfW","NnD1nbE9FCVB":"id6LdBqRd9Vh","F8TkySpKwwy1":"Tngke7zQZcVa","clIEcWSOL1ln":"HOY42jxN9nx3","Vw5IzCfD7Blk":"ILrry8GqsMO4","vgo7RMlKzFz3":"eogSAj2xl58N","DcnpDLgpuqzX":"MbSVdiptNOLV","Bw6fr8hb1EY2":"UeIXqZYEBebl","gXAgKwBZCg2m":"KXlzpzbVPX8J","Whc9Or7WbVSb":"lp4stiLzm4eH","XdzdY4i7mf7h":"Lj4fDkI4gXcx","DdO76j8m35Hr":"cDdpD8hCxIdP","h9IGLdKCYtQK":"Um3Nala86mZk","lq55QrHikNM8":"pp8Z5iouRc7S","PGHrFdNzUon9":"FNjs0gL7mYL0","oDXr0cM7C8Pj":"5oH5J3pAP5bc","qgVwjztSI1ow":"KrlqbuQaU2cy","afLue3nd8HI0":"JesmbqvTmKYP","C0xdz6UDWRYt":"PRx2P0O5g3zk","aytUaTCtnWuG":"E7pfotxYAD1h","eb0D3MUYkYUN":"ursM4sujqEq8","W1T5ooP0qLtX":"gnEqG30CyklW","smj7CH6AN0J1":"G236lhmqmwGG","VfuOgky91gbw":"xOYyFVxrMpxM","HPSnOf8zWj2g":"5aFAQ1cPKxqv","10x5to6VK1yf":"weI2ExukXap7","V2BBWLYO6EKz":"TxrzLw1A6E9D","KMu7YEm34Ydg":"jYHRgpeU3O15","kExSo8cCRJrD":"DwGRN2wTjNzA","9fd0De1D9pko":"R6wilQyNsDki","WvPHO1aJDfM4":"RdwnDhA4G6ZN","lZskTrFo0xlC":"DtWEqtHIrhjX","ENkRDuYfeACI":"gydA24OyfOEh","MqTZ2L3NMt4X":"kSdvKZ7ffjR8","aqTtenFKqRNU":"xijfAr44c5BC","6KREkSzoJ61y":"mU5rIw0ezhBl","wyeYXGwsxK0W":"C4W9WoC8z08o","9J3Mb7NJ8Evy":"MLCg86kapATJ","p3xrw6T9mQp1":"QIHs06ZOLSGy","KXo1hs1FrKft":"hpUc1YfqhEOJ","xd7avBmMsoJK":"tntmqbhW9fhi","51l2nfZr5Q25":"7i2n4AFLgorS","7MRhmpYzzdZK":"R26fbe7so3RN","l1RuypntT3Zq":"UPdtDLksECyI","pHSr2xCvgBg9":"RkS6CMSzQ7IU","uoi3eQw2RyQt":"mf1JBibtm35v","p9zgQ1SEN25Q":"pv17oHFD7xp4","cRM0jnCECdfX":"Dg7uHTQQlEaQ","9t38Ek7AgVsM":"so5VxGNWSe0h","grfcHvGfhRn0":"MY9L89hHN4G0","VdbwkUGw1RwP":"FF80byRAus4G","suMR7PWEqcn3":"61brMDfoPLlc","WQj0fux6zEiK":"dcUS2l75xyxY","IoIfBQEeRhJD":"zgOnL3Wxl1KA","RIYn9eUSOaHj":"dtJfYvCpWRJ3","GHEzs9CrQTYu":"ipSR4bj5LzKg","xeMBVMrA41iz":"si8o0euGLzPg","ArFxIla8KfbM":"p3mihNfL4YZZ","wYBTIxYo0ljw":"CL8gy0DCQI5C","VHVeG3jmLGeg":"7e5DKuE7kz4A","IU05XpB4kciS":"UbF29oKNdrmF","F8KJShwZjqFb":"uq0cXwxxiVi8","8DFXzOuhruN5":"v9bCKVr55vv1","i4kSKf5J4aVP":"2yyUw2PgwkHU","VP0Ibh8O3czq":"dpNzwbo4LB4q","lR0dhREKQcgV":"vVEVKt4TEl1n","DDSoGb5Pkdfm":"4NiSGv4qh2jn","WH8UJgbLXC3N":"p5IgXpT9q0f4","seo40SCV7waq":"BZozQBrbwnQj","b4i8i50B8nah":"j2DQh0MujI5w","wH3XBohwLzHy":"rdvxEdNrJMXM","Pucqce8mZXVS":"D48io4Nh3Uxz","fBHINAQNZYIq":"idRirXJhPz35","gDmUAaZEel8P":"tiaUQ2wBxAen","6dukzwfEztax":"Pl04IokReGQH","kMBklZDg67Ms":"Y3icgQqfVTBE","thbKLae2LYGb":"iuQShaoembIA","YJb5tIP5jigk":"k5Q6v1QortiD","5ktU3Ffihdq3":"50RR0nD1Y4DW","z84ZyijKUVj2":"TBIsIwTeNGEo","0W2UceYKnE0a":"MuQYfLezV0Ee","UNXhWGjKhedW":"bB0236hcnWql","9wabbNoE2UjM":"MjMlFB6GOD9l","MYeZNql51Fi4":"uFSMADL4DUYM","6nM9QClwRUu9":"4wZFBpbaVow6","U0dGr4jnu7no":"8K2fLCceg6VX","bDp0TmgqiTfT":"nbc5HzoX6tyV","nNMdHnqyxpsQ":"KQxi34YDfNBr","MSH1s88rywYx":"ezahDzAkwu3f","n4Razseaqvp5":"m2RyCb5SJl0g","NNYvgYS3fXxT":"s4qmpLtvbQMV","7aQchI5iDypc":"1KD9wzscTxDP","UDz6g2htfqxi":"3hRpH2RxLgAZ","bHfqV4tjuG9g":"vxjyu3HQqNfZ","Xn6ptTsqQ9nQ":"RePPPhWwKB7U","NDxQc0Ia9pRP":"pktxAuZoqsCz","q0hgLlZyc1cg":"1DblED7fbmyP","5YaJPTVy2n9P":"x0I4zdeCWfrV","DCiJHpjHcZkR":"yho3YxXJGmHr","I6sejy7U1B2u":"LluM1PxFR9Bp","smGKVgZTLpMb":"LaaeMWJtYuTP","DSaSMxjom44S":"bsuIBgz54azL","Drh2A7vgyiKM":"Dl352XVsLlOJ","dgg3527zAm9s":"HlK1eaQZmtXi","f7qPvhWkTgqH":"4TRtnimtM926","zAUKF700g10I":"40hGVI9WIi7B","vGgXWI3l08PB":"JB4JFihVGgHk","sBWOLvwP0GmR":"6nNIpes8IXs8","aWEdjLlfYJyq":"tKn9fnX5Vd9V","hwXJVJ6u7FUl":"S4TvIjfhHzKT","32KaCg4LzVuC":"9oxDeSiMv9af","eN8IyaF8DGIN":"3N5I5Pee9OKv","JyiKCuKqRPzV":"B4kWcFKUYM1T","O1vVH3eA1aL6":"SvfxYd5fzqZJ","qsVqKfyj4nm9":"EabwovwMdNZR","eeAgtuST1qEY":"qulAR5V9zxOg","5l9XIgXGksz6":"t6dLRUFl5pPs","h5bYoYUsPnsX":"182zCZvf9hjd","C3Ryb7LBvTj0":"SkrLYupYcakC","1slc5zJFV3pr":"332wejyz8gqS","pKhw46sbAdCa":"SnVeGoSGdirq","JHKj9eLAZlZr":"dELlG4f4viBd","8h1p2hgROzNX":"EsQJJgcoCD1O","5FQ04OyH7VPr":"h5sMQ58bDAsB","IBVcCnoGb9yX":"OCs81xJ3weQ5","BrAge5pndYdw":"XfdElfwQXRpy","yAr78YlDqOBg":"lb2tpEDGeJij","cmSYmEt6WBu5":"mB7jol5bo45Y","rczkhISTHfZM":"5wKCUjwZOlnX","V5i59lrkbb9W":"n1Y8D0DO7grJ","6aXrYBxAWPo2":"eWbuh6qhzNGL","Bf8caaMjmDfe":"NYqJBp5wgUkz","EFP2yAFCFH9S":"FhLMc437RNOB","o7pCfYhJvFf0":"V8SPL9TG4az2","GQNyhHXRHX5u":"bphIl7uIrE2S","Kp3gOMHyHw7m":"3wCs8ruOIFwJ","ZoHlLA8PqvUc":"xUTbw9HMQgLV","tGltRa479u47":"YwXEQPm6Eng5","SOQmbtWVMwPG":"kZWvmMbvlUwC","wYOVvZAIw1sa":"RjiplqyxwBU1","XNI98NjNPiQS":"LbKuX9mjt7JM","N0NxZYI2D3wg":"TQtn5kLjVeVv","Rm34C5Fm2VwD":"fflCvmtD1rXS","LA37uZ4JwCDu":"vgZczziE0TDG","LmHA1618p3aM":"zwIkgjYElz4T","EjaF3EzteNzK":"4BLdiTH4gcmq","YfCC8jUMkMTB":"omVG2n7XXehx","eBgBS8KxvrMa":"uTCUXxBoQOw1","6jDlQTQ2hifG":"74wrh72DfXKo","QNw8CkuVvaGA":"7cdze48bFU02","PT4KE1gOQUSP":"UYrQ9OWzAWN9","yAp2lc7kGHi2":"BxN2oyCWp05T","0Jwf3OK0BDhL":"PxSRe38Fy2Wy","Ng1xGodYbU1t":"mUGRBPK0ZTyk","zdxCwcVGhqXi":"KzbLcmmt5BOO","Nku89RNeGXGY":"zi0w7SZctv67","fLfqr6bSVTh6":"idIo9mMXc4Kz","kBeczK8MUNWp":"JOCrapd9VrtH","Bx7elJs1sNMm":"IrDwCls21I96","v31w80BGYcUQ":"BHkvVL0EjBj7","LyLCWfsTOgkg":"0lDfnWYfksWw","1qLclOgKyvTY":"VkhJrQx280O8","pWrmDXaXI1Jo":"6qRsI9IxnufB","0jPWbORkYm2h":"AOGZA5tH1sVH","LZSuQOMFYgvq":"n3n45bQckjk4","Lc8mrh6KyQNj":"HFZwj4nQGMz0","7b5CBDOAeU82":"D0bvnKAfvly5","aJ6YCsOI0Oi8":"d8dTQVLaKr2P","JIlZrP8tbCRJ":"lSTQD16Pa1Gc","9SkI1G0Hbb0x":"x5qcfr4jcO4c","0DXdMFNDea9B":"S96CuKaFYyvH","f1GKtdMwt58I":"tlTcvh5RloRE","vSsewPtQfObi":"NoH3dzIdXnOF","x9U96jI5iHRH":"66p82ewJPplK","GpHgt0qokTwY":"hjAHX2qO2KUq","y5yf4mkskcEJ":"TB8rdhFzhJAZ","1HGmUpNeJul0":"3UR1z9S4ReE0","Yo70XZ3kt25e":"Nd6EFifKE3Ad","UjSSFw2sTDIT":"ZpGGr68W8F8L","0iXzxf68t7Ly":"HFtAgFbsTzss","KcdfJfMgbHHW":"VyioQFPjyi9a","1qj3Ky9uBtW2":"tf5UQOIIQGZV","nRyRHi2Lnp9G":"PAc7QIEVjc5R","UEdvHxU09j8C":"VKAHLYOM7Kgn","8BiFbnXNfvz2":"kTEFSGVRPGp3","JRU3fmOxRrgs":"1rjbQ1WysJ4J","JYGU8qmpCjjt":"xiBosgoyuPvf","oTWZCEIBwfNa":"rtvG7J5c7vR1","RMBUruteuG7G":"PokYbXjxlIWk","vCmlTB09w1DK":"rFMhVFiczAQq","tkFXO1moQPbj":"tkYSfvQ4bHm1","ZJIxlbbUgnKx":"ry8Wa1R4ZCb1","3sN9L2bdORlm":"PjJueowElmlU","DN2qz15Im66A":"M0LTaXkabfz7","c216kJdM452W":"ZZfTAzrKKcjR","Azu8SdK4HRZt":"XUy3M7ItD3NL","0OmTaURhGZvg":"NgAwBkyfu4l0","NjKUwe0MeSx3":"Vl5m3wfqb2ZH","9Ak51peJrUz0":"xE3NarnTGUR7","BZX0IkByp1o0":"3rISLPjeOWmq","ZLUrQhCwRHId":"mdPD2dRg7sgd","RdQozmWBYxyo":"u2qQnGXmLh3s","q4KvtFrWmbLU":"Qg7j6J69J1WU","Fucqzs531lXo":"2tiI2BF7zup3","oIyaFUTlMNH0":"gJVxUWpTVvBT","XgK1GFPZqO0A":"GNvHrONe8O5x","BLEcVA4q0z7t":"at9IIZkYiOEX","AmvGSAR7Piwr":"htURF5iiX4Ji","fVK3wNHP7lLm":"Ox9JCnvGKRTB","Ooj7EsVBRlnP":"mxgojpeIeJns","pYWJBSJdjpYT":"iOHRQL1hTl7e","O3TjrmoblYPv":"IfauBG35Sf3E","Qn0laPDdpg90":"F9RtdAuVMjY0","y6mSMpVjXqSA":"R01zq9PIPjPP","tGZOFlQ2Hjp0":"V3u76UZ4fTNI","Uhn6npc1tEnk":"hKQJHcoWFtdu","FT1Yf4CEJ0af":"ap6mX2fFpNb1","fXa5P8VzyvHN":"HXjhdSSKSRIT","SaHpEN4okKdN":"7ZmCXoGJDBIf","4dP6xde7gQDF":"Awrb1W0fyuM1","C2AqpQXK8GWi":"oWGnK7whiMnC","a6h1cRdLAT13":"qCmnv8PS3uCx","Svqr0mVrQUgL":"ZGTGFXfN4rR4","FlTXlb1oqTDJ":"T8CVeV0bdXzU","M9e39wvjE6lW":"fmCFkKtSOP76","o1logYoX159H":"DDgSOskQV2co","OA2TvA3cbY40":"ZUDo7460EEFv","AcssnZ138hRR":"PB1sD0VAuas9","RzhiKeG0eVpo":"0iNCNJcyQgFl","o4s4oWzZ4QAO":"63jqlxPkJ7iq","3MqVNEbBh24x":"gtKwNsia7ied","oxZRf2kqCf8R":"ZoOm3tJJH8m1","q3dVVX7xyILE":"8UIkp8ksHCaW","BTMk2QcEtBvv":"PUddTUBNVukB","d6J96Ly2fIkF":"sfAxx1TBISow","PyT2Ilm2pHV0":"OsAEIfaK2VU1","QFbQLLXsx2nY":"BZHhGad4tGzm","y93InepCN9z4":"vA5lnknx0vwX","BXYcB6JK8AcH":"5n5OBMTHnEDq","YBVoOvzuadQe":"bjZ7PCIaUW66","gdrFljrCO65Q":"aiRKj4zhlS4B","WNXEQb1nSohJ":"HjyDBv5JW4o1","B2minCqRry4U":"C9blUiOzjdSS","slB60lNCNrfW":"DaI8XH0faFeJ","KtcIYqFzi99N":"xcyXuiu1q9VU","9dDQhKHtvpqB":"EeG7BNFsWY7H","0d5VTcJC8toA":"EeE9gpkgU4aL","oULeepXHj748":"GfSKbJoSfu1M","Jic2WeLXtePP":"E7u5AEuLK5V0","rP07rIFom92w":"OeTuQh1lnMm2","hrkZnHuWpqTw":"UIZDEeHAufgV","qHnp0rKwcE6n":"2bw9QTVVg1t7","jUj4xQ1UvdVZ":"xia5hChgV3yy","httvgLWKvFih":"SvczgjZyhOir","lhqCIyIG9yFH":"Yx6gb8svVDHm","UbhADVFDXlHs":"JHKXeRrZ8JLw","CdeyIpQsIX8W":"oklqQA44G122","77x0n0GAOVKm":"FRljDB0DA2kY","bnvQXvUjr0mR":"fRImHvCUudl1","6LoJ3LldYbdB":"jgbf68wskykD","Cok38VLfkNdm":"9tNX5bVJcqXz","PAHit4N3s2p3":"IUs5wktEMWyZ","YZSHiDXnA0xZ":"psvYTqUq67Aa","iQDiYTnvphUc":"0kIkuierq6HC","GlskzcEBjdZw":"biA3ZlFAa5Yg","n8coSnV19NI1":"RebHaUgwNTsz","MRTN7kg2y427":"nJyiFxPRWjj2","Wuybh0ZpWn73":"YNiwL5zywARl","s3mPnJFRcWML":"yOfeMCe1KPdv","tIH9k7uJqIOv":"Md7wMF8aSuuI","XdctFgE4Ycwn":"WvzW9JqLcMj1","fPyrcp2sxmdV":"WpY6kIrEUjl8","MAb51PIalsri":"mMjWSb7dqkw6","yfIJVaOhZAx0":"KuAgT6oECPz7","oRqNwvD1FIIx":"aMLpPNScV9dc","Gt5ifeBeUoKo":"XtdsXeKL8xHH","SMj3ZZF59U89":"IQlshrc8TCHT","xrtvtQSc0Oxs":"7BkpB5VTL0tq","UnSLEqt9OO1x":"InFIxNWIZ7Q9","SFDZZ4pifO5J":"xLOCpJ1k24rK","Ne2Cjkb0uPcI":"OcHtS4me5oLJ","KMRfl4mGqIuz":"lc9jbAf3EvTQ","ZbYq8gq4nZEf":"oRJK5eoTAzJS","HQqxRrNzcjWQ":"CT5g2CXwzInM","nPq4vEP1yIXS":"MLN9vxLKb0gc","tUmzJOZrvpcf":"0Ezlz7UCncgn","H19ij6sCM0eS":"s0J0cSaGP1e2","oSIRuGv8kAwh":"a3WIQVltNFKc","39ixrpnvNEs3":"qn9OmUt1FWNG","3Owk7xl5Nge0":"SGdM7NEuIm9I","Kmmlw2fm1wax":"9KZpZ0lyI5Az","aIataq6Ai8cf":"SkdEozLOROYJ","SzLjjf9Dall7":"cbvtAE97nUpj","lsoGj2ndGOq8":"pbpZrAxeY3qV","W6EgnEreRlPF":"A9GG9pr5kobp","BWeYX3LmHZjb":"vubVtahOhdwp","10MuBe5TObeK":"yysVoVV6Hs74","FaDKVES7FwHT":"KQ4L0dJV5Jnc","K7zSCLAwfgnl":"NozR7eWLDtgp","o89a14NvRdQQ":"9PQ3CMAPI77q","r1j2PngbbraL":"VhfrocHstRPd","AzTTSq1MzCPT":"BY5kMoLsHjBJ","kATKnVZYIq2J":"tjHZJP2MRdFm","YVfUdvhJadZf":"Fl6eA0gVKGnm","QjQAGM0BLPBu":"AthwGuVuAr1V","kXJlEeFWBAB2":"kVSUmsOAlaCM","1kOnfK1HJJtp":"hzifVPkTyaUc","pYKxT2LXfIkI":"kcuZVBoJAZE4","8c0sR0GW7ssY":"Xe7JFgvLKB46","RAI9lqNkHCXY":"E6r6w4bKDAX5","lhiXQlcNw92C":"7YqZwtsbV1LB","30Uc0ip5sO2c":"N8HYabTOvh3v","yQDnoZ1OQLji":"cnInswXaW9Kl","d90OaoMsd5Tr":"z2cwcvkaFGCD","qZv9eyzGoSeA":"U2mSUIzcAXpb","LGcwNd45OXTr":"jqijgb2ceOVH","IISUhNOJ9t4q":"ra0mUPhsvF8O","9Ev84QEIAsM1":"111UOPAXAWQA","y9yLLmPflOyn":"2BjIKuW0eI5e","Abl7td3wzmP0":"N8OQKsEVMYby","xvucjilRKtJQ":"IWfmOdWXujkn","9tryhwh6HYGR":"Fa49h6c3U2sO","D3TBC56owiZu":"1rENDFraji6h","fWTV9YrVU0vp":"ANkr0lUJFv3j","VNLHew7a5LW2":"joqcd4AZyxyB","BPY9cAbBOAPH":"CmxlZRcyK5Ek","FAOWDUg32vlK":"wYx8WMEjtW0e","V5RQ2u9kgNLt":"PIsfOWxKObmo","LywrlkmVuhbC":"ysBByLsXee7T","hRgSO1TBi94X":"HeCag9IMlHKR","HKetEIkVwbfr":"lJ1dlBZnC2Kr","4t9OrdrbIAtt":"csA8I1Yv4en4","yHgsEfpl2Qmg":"D4qwsj8rz3nT","iT7oDBtLxl5r":"4Cny7HqkbrZY","dTUjrSn1UX0P":"hghoY5bh4y76","oilitycO8jNW":"P4Kpn0i5fvef","DkzVshEH2LAf":"12dVrbgqJSLI","UR3xTcvFUnPn":"nqPBXH3uhZdS","bEJvhx1Lkjnc":"sul6fP7ldgY6","nW6OUc9Z6inq":"Q4zU0ivSWFWa","XSwSkImxPgyN":"YWpfDdiOIiog","YxN3MR1NL9xj":"o7LtMY9Aenq9","rCaqyf43cGge":"eDLabvDbSPWS","GlOCEtwC1ZqM":"G2cNwfq4AFd6","aZaNz2gV7sy9":"jDOK086cYgj3","AJNpRcLBLxUu":"2NTrRYB7VxGJ","8JWsRsy1OyMt":"8mSikzgCnRly","JjMmmdfk9vfE":"43irYWTGE0nt","IaubnLTww0oi":"jGIoPhsB8l1V","VN08GRN1dkLT":"JxJoL49RgvOY","IVByG8Sriz3C":"4ATJLQA8BRr2","yOqpeLb04oPJ":"3csmiDsL0c8y","xZAF2sKWBzVN":"X7s5u37ehSQP","pMpMIRPs41sQ":"FkvUAU0SsEpe","sC3SQWCpcjsk":"c2O94mX6NxyG","xIDhKku3tsdu":"vfnCZgkE6h5L","DMRyQ4Cv6mBR":"b4LgUuZh0XAt","UbEatN8XKf4J":"6FOQT0QmBQt9","lYztSGx7k3Rm":"zIunt8I8BOKQ","dE5UrCmmPHhh":"tzIP0GhhXfqI","7SdcR0liDQaL":"Zu606ihopJZd","6WhBWwVyTUYJ":"dFJ3Oo2JJzqN","DCMeUPUZc2hB":"62kOPvpvYM4X","Dsj3S1lRY22W":"CrKoyQ1i0mhA","ZkY34T3rwhnN":"FC4LUcHh7yYY","DRxGtizan4jY":"eLMJztXPIII3","Zo3xLoI5jgfb":"airrb5YRNwio","FmArIp4msdMy":"18fLeTGDo8s8","HzqSgzzAFl0D":"9LGQ0GbabLW4","dMmlwwIBUUMu":"9jtLrvnb7w9G","eNkFrgzyLbgz":"M3WSsMAjYm8z","vD1rRRJpYWar":"j9HopztNJs2V","OI5CTu3ikHnO":"63cE12mhoNKL","qn8no3SvhxER":"VBqenrM5LxaB","lMGoxls3dMHV":"gV6fefpmwiOH","Zv6ZyGQyn0oV":"wagXhm2q4YwT","CdlMrNPCkidp":"hKhgg8fdqNg0","84uIIX79yuyT":"w62hZFKfWuux","VoHSLkJqMRoU":"zIIwyZR07oGa","4Tm0LJo0sEhY":"t7WLePU8GnaS","FE6Y0s5pQ7mO":"boXmOu4YYDdj","cNjjfXWbS6K5":"MxQwoIJ8vd6x","RG2HCBOu94N6":"PSN7gxDKk2qE","cDnoE3SJn3Po":"1Y9oqdU6d90n","AcStojlgdvxN":"WbqrxIEsgsfu","QPVhMIi0Z4v7":"itaN1slO9sk6","TvhHwQvxm6RQ":"h6n368fD6pMy","QNP3WpVMgkXM":"C0swExf0vyug","VkziucMYpJeM":"pzuY0T7znKs7","UgE5VN6hlrr2":"r5GFo0LPARjm","2x4SyUwTlOKj":"SnU2bfa5J1Dq","FQdB0op5ZAUG":"XcUbkcD6Qyhw","3CMib2gT5fUG":"YdL2nOykwU5I","U3qnVW7QPbtb":"EtrEIGettadI","PeNztgg9A5TF":"jzwI4Jcusb7h","Lk3ZokkNLICv":"PyiddAwUuICZ","Al7BLtq5RdOJ":"Zve70llQNY3j","57pRAl5jua0i":"MSvQGZuaQL05","AE9QlDLjbgud":"KhLmmbAWqvI9","w0FAhelMmwsL":"PCLj7e4xPgvE","roVrLt73El6s":"Lue4LVRrJpA5","UJMoPlVorx00":"woAhj8qLgYF8","05FQQcEql9P2":"t26hnbky1YgJ","1VXgfsv6cOFg":"7qrJdmqBbbE0","O0nTRlkWEX2V":"wjHfkehzm2Sv","c8IVKc38nDqE":"0bru6o46wMFP","xa5l55QhWtns":"rH0Okto1LG1J","lY9O9btCvjUN":"dpsXVVS0pezo","R5HDw8nzzMIF":"JEq1OoAJdqv7","HTo8vxK9EsYk":"EhKOpx8pauMK","UC3lmjlrAPFA":"ntDChrY0e23I","spGqeCZYoYhm":"wX4uhSIqu5gG","vxvf850tvtmK":"7agYhxHBssnz","cHmJtfez5e8J":"iTYKQSzEmHfR","L5ACgLsS1mRz":"JSSR4VURBh7g","RKg4UQ01QnxM":"8rz67Tqxlasl","LCdbnLtWxlLW":"jvK5IoVL5dEZ","5yuHiAmUN9eQ":"pOEBLoxSTJVt","xr97WgBiqIYJ":"waCiqFmXBRyK","JsConxkvLpS9":"EJvT1HQxmNc7","x4rvVcXr2f0I":"D8ksWExaVIBD","u7N6LbBuad27":"OmA9C2AV3rM5","qE9TLPxUPL70":"9OBzrvcnjoVx","od9zfCmuG7pt":"Mld9nVhho0Vv","AR3Ejjni1vUH":"5RAm3FlwO9Vj","tbw2kU9Dhi1v":"26zn64uYBeZ2","6a7boK1TwcOr":"XqD8UmJgCh3m","xt1xY3WVACfm":"t7Vr3IAbYcj9","0gxs5hLLApZ5":"IiGyvGezXzYV","7bEIH6VSrvfO":"rhVFs0afhnl7","5I4nbB6uGF66":"kA8hE3JLdyIl","2mh7TuFlswM5":"qaBD8Vg0IHw2","5K3pUB25ULIY":"8g7TuGBgnv27","DhoFLQjUfHrT":"gFUEWkwrHmRt","nnDC2hrMWkyt":"NtNQMUwlS2BR","4P8ZamLL8mtO":"k09ONW0svmC7","A9SG5VjoUpCA":"w0D4EtUdsVNW","T0VGO3kIdrlk":"nDobl4UQL0BR","TZE0wzQ8meBc":"RK57OfnJfKrr","hOkfDrd4piit":"WbFYlrZIWWmX","cr6RvITUQV8k":"4ftzltlfxMjB","299UFMWqJuOV":"DSPA8yp1x21R","EvbLGuZoXJo0":"tGRhKCZ2L4OZ","bsEkKhXDgzse":"FZYjbSaVg2S6","H09jrdGXYf2A":"yE87lHHQrHGf","pzYyffYkRAoZ":"UZ878pxkmlrB","28GsuvPRUz0q":"aRZXHrOplTaU","nQ0Apl73NTqr":"WKCjUWVSGLnY","aN5miQcCyFYu":"TqMmbgwwp3RD","bUkHTC5K3SMo":"UXrzB80SyZKf","QcZguADY8Nl2":"zwVFLE6sXofm","wzlINkpOYeDC":"sJrsTLCbfEud","q6AftzESjHks":"xz9REgro6Xez","GreHxVPUIVxE":"RwSBOxe4IV7H","j54yV8QA9Yct":"WjQCdPiJLijs","calnovlQn3Yo":"CG8L3VY885Xe","ssaZvXz1cqTe":"6GW7frc34nIn","9Io4XA1bvU9f":"DKe0Zr9RCyvR","d8Le5bHrF0SM":"gtowUlw1GVpu","toLaXiBxrbQl":"Alj42rbueTpY","JJeyOH5lRwbN":"jOyahIzBb8kP","dpHXf2HLXOIL":"D8a9GkKk6itn","9ViHuwdA1yC5":"2RNqqR7zLSbr","hHdHoeebsqiw":"kwpsp8J9ArXt","rOJpdqpTVU1x":"KCOFP9N56xUK","rtwcuQlysGG4":"qPXHmcn34uWw","aLZrV9pLqSK4":"DzralFOgcD4j","wa8SY41GO14E":"gsGJE0Mex7cp","mx7AeWp7RXtW":"ERfMR2XTvwDi","aM2bM009jETY":"SBp0SvSdfG2U","7LpdOaDZe5xP":"JqAc2gjAov0a","AzX6wJLRXkLY":"WgcyssQA34y7","9RRRR8Ap56fu":"ID7ai8deXq3L","YbvanwXtVUC4":"G9TlJ0RHYWDF","CAb20SIACsLr":"gtEcYMwhhc7P","s0mn9yJeGpyd":"THGn78gJN4cO","sJbb04yadHcY":"8pwkvgs64dzU","d7hXs1tSS5Ib":"dnY3teRkHf7P","EZwN9Z68UoOF":"vpf6t3fuYZVM","PsbTLNRejqBb":"QmpUSWtxiK8S","UnhFEqYEWCjH":"2c1lLIaFG3C8","uf0xJ5MErrPQ":"3mjUgTPSc1uS","64NTyx2BIJl5":"HhfflhbI5OS0","2QC39uKuEd5x":"XfHMa2s9TtFJ","qyKjJ8z2DeUL":"mXDPThlI2K78","H7z3HZBLSIEk":"cDpdkuNUo81h","tG4nJB1Q0p1D":"WRHjuTN4KoIY","LgIe87VIHIEv":"UK8F2NB3AnLa","UCYzcqgSCFhR":"iJ4OQ9ftgr8X","sU0mgqR8d8PN":"uwFhZuqysMxi","zpUGccgDjxJO":"fyteWVk5hcrC","h7KUlVdNugi2":"SV0uQdnq9YtZ","PHTOujoV9Esn":"3QcYfgZbYyez","Qdf0KbVbHftR":"wn4OcbATbrGY","TX9FunwrY5Ml":"EXNcpzX980Bt","0HmtW2nEQsya":"TFuwzH9DTV8T","qYqfHVbfLXXp":"GmYAuqy7BpC8","dspgqRNuuipU":"CnDX08se6hrp","dLdOKaKhiRHJ":"OZvFa2K8PTDT","ZPhpQJZAdge2":"1C6LQnzRsSmg","xSFprwaYX2lK":"CwneswiNZYxt","YHyz0G8HmwBu":"Qxja8rxjfRmY","59geUxKDqP12":"bj9fDPQREkKs","9SfgYIqXDmZa":"fhOTYoYNjxbB","dunMtHb1Z4Wk":"RlbCEePRxElH","dQ7hj77QdVxw":"v3JTmMrp0H2S","cVe3x55NpiDR":"GhrXPRODKjGV","BwOgysfr9fu5":"xVRL4t9ypHHs","6o1SQscnnGZ4":"LIOwqdz7NycV","hLTlklTRdO0g":"eMbp8OTm5nq8","PNcnFkpM7P6O":"rKUXFRA1BYBY","0fiQtVlzRg7q":"VuvkiS4ZNpAc","80RJBWZtTvtQ":"oXFednn3Dn8J","YM3NQW1FzU7B":"T7PHb627CGcH","8xWuxI1fo6V3":"qVwempxT1rhv","DU9K4W4fsULp":"6vsXV2gkaqOX","R48tr4HiMZmc":"BC3vIYwfAwvX","UKPUwCbxdFO2":"fVOp57dbRtLB","AvoMwmjHgOMs":"INOV5CowCkxk","XoNtnL9PmDKU":"TkFZq717gV6h","W1af8K3U9ZFl":"RzsxZqweo9JM","7X765mL3ttsL":"Dke9xRAMh75B","U4A6ddfuHoCA":"7d5Z77YZ5b60","tqdmC8jwe6V9":"S3umlnyVEDll","E6ZOyA3KXVQa":"0TfhWlhZcbkM","XL6GHhyuR175":"jBUgYGG5I3xC","0RxYi46gzxgT":"aiyU0OOWqkVk","EHvRvRzJKPGA":"cElru4uX67SA","cPdLIzlGQA8T":"jbSjkxXLnqrL","2BtUHBodhlGH":"TzHe3fWzEeUR","Ju5W945Z9bM7":"BVc0yOua0SvB","9LSVEMjIrH1B":"kWjFHlJOgew6","9yA7wnV4Zvlo":"Gfur5swMn0ks","wbrLMG9sCB4h":"7CcJksGViI1Q","zvLgNGwo4NYL":"XIHUs1OLQtSI","a1PC0QpH3ubW":"CpSoCTNjRjXl","CkcRNCnJbv4r":"a2YpSrN9g5NP","gK737QIUMPhe":"H2eJapQMz9of","F9qnsfV2YwQE":"S7MI97yLqXnx","vnkqNR7pd2oB":"ArY2h2KMckWz","1lqPnIpFthtK":"mJSzuII2eziH","ecSQZslbAwZq":"dQAmRz9EYFcs","yftM8Qx5sEin":"CPOdrfOD73fV","kswed4PSMhYm":"eKceoFML4Yva","SjXRgX3sCG5G":"q36PFB3hOQCK","k5wW9TtoqTAU":"egTr5UFFpfg6","DweOe940bVhf":"V4rgGZxiW62D","jiOUrsj2OHSr":"do3a68AybfJz","5kriRtxTQxIQ":"H3gy5bnkqkdo","Yivfhd8Xaxz5":"daMCEwofkSLw","aFwmBxgqiCul":"iyeaIKm0ScGZ","V82js9j6ib8P":"3vKlSwBmTPoE","k4TJGllVpLgn":"OTqXrEPh5LtR","UVTAEfHYhLkV":"iMqkTECpKwMd","Pf6oqBDlJbPA":"JdLKaUXHqVFk","jlTOn1dg98pl":"u6QNQA6mtTVw","Lhcg2jmw0lAs":"6VevoHeg7UQw","M6qrtVeLidVt":"V2YNb9lNFZUv","b5S9Q93PjelY":"fzXf7DxodaPz","twzStwIhpxmD":"CPUtZLlepZOv","YSxlSTB1PEuj":"xjHXCCJKkRQZ","cACJpVTPxuvO":"wZNvSnKHsW7a","dJXZHxoYA2aB":"XSLcD8vsnItd","CNDfMPAu14HX":"hEWw3qcmZP7R","QzddtQILcgrE":"yd7GnLN9aCEk","05JSargs05t6":"9cbtmQ0ntIhn","Mi9609LrEaK2":"OO9Aa7uwzvdx","sOCUWMXLc5DK":"xltxyTCGCnWX","tzaoMBy0CdwR":"Hr96NKKwfpt4","tujPtsGqM4jI":"dddGZujfZF0a","tMwNmdYnVgAk":"ks252yxfnHRa","gIK444aftB6K":"cg9XCXaaw9zd","sYEMDOTYL19v":"byJiQ5oCKplb","cINM0sgOus8g":"G9dwUz7tT4EV","IoFqYWZ6FCNP":"OnZDgOhMAqDo","JbWjqAgR480M":"kF77EK66lzf4","sqBNwAm4vqI3":"tdh1rGfO22Ty","YOwa4WVkgfxY":"2bDlvghakJAC","CBqTjuni3RBn":"1ks38aeE6c5Q","YRHLQWaD5k8Y":"HOLNtTpDJSis","otyrULlu3xWQ":"4IDOvuIaAkzM","RJIPI1PyLsrW":"6czcuf9Yj6y8","1LEOpCAFupRb":"ZqHmdltyD3eA","V5cV0PjahPLW":"twQsU0gHVL3n","XyvHIqLHfe7m":"scGp6KBxkdih","YiEmwaNbsdbl":"cQayD5RhqlNr","J1XixU43qtUx":"CWPhge5qNsao","sa9Uc7bQF580":"GuRxL5QqFYuk","mLHkXkE2HUFk":"JblWi4dfPTDn","vgZndgLJK882":"uT0iNapI7WrT","iLYgSnW3lTq3":"KrwnkyQyA9AU","Qtg4fjYr1taC":"OspWh8VEGf3B","6bYfgujLR4up":"kGGTDl7uxelh","qhvZgnQxug38":"PFdP76kLG5Nz","hZkkf0qbla8P":"0XZ3l5K2i0Lc","SxObYCWV7XMG":"wRQALqHorpMb","lWl9qdnbc7Eq":"K9hDJkPI60Ia","xBZcwKofD1kg":"O59bunpzuP4I","5G7fTN3kQQbD":"fevFRRrW5ys7","cQJCOMXhuI2Z":"Rw8hnOzikv4G","uth1MhFXu6cv":"LTLTm9DNc8gb","wfZGycLpfDqg":"p2yH1ZAycl42","B2t2G6iSCX9w":"rbDNic9rJrYW","x1aRVBbtG9VZ":"6Jbb8t5D3kH9","ZjYPL7Qo1tWf":"las3KcKK6u6x","RK3EMGkBnznL":"EY1LikSpnEvi","8hAlyFEV8Mpe":"h7kBj8puLgSn","ggxPXEGKO6zH":"T6TMshIo2FmS","VByLcKbk2zWj":"gtB0MOlW5k1H","SODAr1rnnVpL":"FVoM6PyUjlHv","2NYqvmAp757X":"UO6wAiQjuIvl","oPDO0aixkwrF":"9ZcXLQ18nv3Z","2TWs92vI5ikb":"MukTKf2MM8nT","MCsBKSKeFvrM":"x6qd1S970dia","bXK7qPLSdMqi":"mImh7PwiZxcK","q7V5pEL8DKH0":"ZxxiMF41bR3y","R2Xn1q3FkgmN":"0V7c5tS184On","E7dNQNV6v0TI":"XJ0HGlXZ04Mg","g1dxFIlMVQno":"RlemXmKQ2Cq2","mkuUwIpsnGG2":"WcyoBGrZqS0Z","NAkpIhvF6pog":"xgNeCNGnUAjl","FFT4Q7oJkR5s":"QMH7MCMhpqhl","Ou3afl21cAub":"rCMEy9dwMZQ1","awNZ6FWvOSRF":"JY2KhkxcZf2K","sk0mv9iJDfd6":"CuKVDTH6eDd1","KKEeiYrK7TgX":"An5HGPzswVpn","V4IWwM83aRbh":"XCi94glnWoOa","MTg9xldTKwpZ":"oCkkrxzo8MB6","s4x35UMM8GoH":"OBwX8znVKV37","FMKluHkJ648X":"cIyLSJ9OBcgo","ZHgzkrF8MR6e":"8gLRKo9LD5nN","nvRZHx0twcjS":"WWmgT21Jgo7Q","sXCwiRc0jiQG":"dRuqMPs7YIwe","M8GkLKafZ3zZ":"xb6gGhbtJ9Sw","AGJagDi5pXuq":"Mon8qMqq4AkA","KU5Q9DUZZDfy":"S2OHqo87Kwix","jYg0FdT4D4nO":"Xa0nnepWeCcR","ElcRLqfAyjh4":"8MBYUEse6Ov0","JreDgBXNXqwV":"BCcq9BptuYfc","LoU0940t51vp":"LvsYr7b6pAiq","A2B7sF68iXVU":"I3PViOFR5yQJ","9EwiauBDFDuz":"YT57PspTjqC9","N9Mk4Ei03yiF":"uRz27UJrfC5H","1dH5bCXpfmhg":"HGB0jGsC0YJw","tA0fFuoIHHpu":"bRBblSD63rIb","MQvUaSyLxJh2":"Zvzyaq6OXRJy","Oa9WoDqVc7av":"xzmtHwVmV0Qw","k3AjUbKu3zJt":"XquwyPBTIzGx","06u1ucAHEO8M":"OgJImnQmuJF3","r6Z1cUnuStMY":"XuDGQZEwGPr5","RMpxcFffBiLp":"mQK2qcQu4GxI","bblfjidqFGVF":"NjaeZtAOI3Zg","EPpinHNdDqf5":"3ZM73HM6LKmJ","6PYa1OlPQmdQ":"GLHNGMO2V1qG","now4YJ3TPd6J":"1E6kILQddrBx","Q8XbbMIHKx94":"FH4G9cb0f6Ki","ci4FBEm74uJS":"MKykil7IDIG3","TTBUzQMyTYJu":"1BCNQqgwfzed","DGCkxLKbDrmf":"WH1oyyd8RlMv","pk6GSTynePHe":"AhAoWIXIpb8F","qTPm1wonOykP":"ADVjC3Cja9jH","0z5HY4hroPrf":"9CblYP6wjTU9","WvchT8pPtVTr":"x60oef1M7HFF","PhJXaOqb3jtI":"5T2Keh0rY0UM","grmI53WzAtOk":"lRESB997o02G","xLZFlcYrd7LL":"PBrYKGeLLifC","PR27iqfc7dmg":"bBu94hNBwGKb","I0DTyuWqCbn2":"MJnDyjFNVFr4","xZOn1pqipoa6":"BOcWxYF4kJHT","HIuCteJIxngl":"HeJXKeroqJG4","c2PJuHQYSoEV":"JDOVZO2gg5bc","xUUmg5bUjtKs":"MWujdU9kwyrs","qmlKh4uAirqW":"NU5CoPGYDx7Z","57ty2IM6G7BH":"l7b5aMV1NFFC","JYfJw6WtY4mv":"NQRk4C8kQVmo","WXpQ8tKPOkhi":"RdI5HP2yGLy0","vACg4ccEoC0b":"3KJFx9niH0Xj","iQLKrKj9t1JF":"Q2JiiEhHaEVy","e9Y1AH9iS8EO":"CD4XN3HzGiJt","kFTBZo5sLjvO":"ZJCvNcCMm5Qc","Ti9vwF2kxjJ8":"QAN473gcRkUt","dkE68fJUT24t":"yqvAnZR2wvR2","HQ5bHSVflDU1":"qgzc5omTJLdU","pVBcc20lRccN":"Uyu39yWKgJf9","RtdkeVYziNaS":"sgHo0VPZyzLw","BQuqgzlv4lTp":"cUnXkygVdE2S","qQauqpYsHfA0":"sqtatofQKr3l","LElnvEPHAiTq":"IVOvOgfly7Eg","Wu3VdybxF4wb":"vaCdJlXMk3ky","F3yU0nS4Fsg0":"eRL0CkSNfss0","mcJvfW7pWEKI":"IEEtwrbqigjN","27F437frmawn":"xwBpMvXdVffy","RIWAdW6h34Aa":"rIhDmbfsHmfN","FDQ4vlEMIkvl":"D9jiSX2OTEM3","aUAN6FQyaqsy":"DRhsfR4xryHW","I5h8CBhsis4y":"sHKqcZKN61Uv","6L8ik32izfYz":"wOOSDYoIAO6P","B0uYqmg4sTUV":"LCbGFGOaGsdU","NwLSTi6tZu9b":"5skt1IIxHx98","zGYJsI9jmY6j":"AUAZaAqzRFT6","WQN4WmGMD5zC":"ciEBCwTXekvA","mhZKsXrsZtsN":"qmrlsNupaLdA","7pPx5Vq0OsIv":"JNqbBmAsbQmw","SKzGmB7kTy4B":"Kxjcsh2vzfEz","PcT5Z6BLpkk1":"4YK2IMTFcObp","19av5iIMDk2Q":"reWSoZO61Sic","E97XBSXwKStf":"TsVcRHM7Ygd4","X2laLXVhDyPu":"H0lSTnupnwRG","IEjfNlv8ilth":"dsZa0IdgAvHa","DYbmxt4cVKcG":"deZTHALFCjjI","JnT1n8cJbusj":"azeLtjhX6cOK","mYK34naQ0thb":"E254zWMDk64H","V4I5CWak3b95":"V8Qw6KkNDUlc","IDSUqR5AIyso":"rmjhnsFooq4e","j4iQv1U2T04o":"vTL3fm5LIxmW","AigldH1kUdcL":"M39gijJtJc5C","ElF5Yp2SFM0C":"B6jS0EnGKrMH","yZBvKKbq3ZMK":"bF9gIQLXPh5S","DzL2weytjLlV":"jkVN1pbxA0gi","HjlDExKHcfvm":"eGxE5U96aqVV","6oFYtMiLPc8D":"F0xoEEA3B9He","3iCu7Hnu8RS8":"Tjyh0adgpH21","9imJ9N82k59F":"vfxm19ZY6s78","4Mr459xrk08M":"j5aPwbqddAUy","WDYCUlCHBQOb":"TLNTu8lrHUM0","zmH3lQ6HQehP":"QUGvRPSQTJ8o","P7HPczMh3MOv":"2KEVvzsiZ6FT","rPD14HgIEDFL":"dp7PcpDIYkV7","E13QvPs93BNw":"4lGMwwvuYY0R","XLrlXE0OH6eZ":"stLXRuziZa2H","A9FK5gatgwJB":"qV0KzZqtiFyC","8nsPA6K41fpg":"fv1YwQeBGClr","yrzoztLj9FaS":"9bCP0um2D770","KuK5AL3HNaiz":"ftGGvDt1SXGB","TmRGDQfS8MtD":"MoNqkCGGEzcB","S0wLkXJuFaNo":"VB6IOIAEM9YT","ECBvJmRnAhUZ":"i8Ib2TPfeJD6","ocs6QNWo0zZm":"5gXNhzxYKifC","7m4TmanWRfau":"kjjI77LIBGxV","PlU82Skko5JZ":"hA8wJIyvcCvQ","pRiAoacav0Ly":"l3FZj6TtmQ0T","F9EmoUeZS5vA":"pDFbhDCcnaiT","RqC8rtXfdaf1":"XukyZ9sErVLX","TMDC2jHyCXDN":"a6Oy3peQAM35","HcLZ02vYyOwe":"YrSi5WmqOi8E","AuVWM4Kbf5NL":"bolCVkWGd8v9","B1424mCQTJeP":"ZMtg0puhIffI","P7bAhsGzlXfv":"0Yv52HOIGh7o","3ReHEumij6QJ":"WC0o9QiNoIaJ","qXaazv5EMvs0":"yJhoJ6jBHQmf","z14z3Kgmsrhf":"JAkmkJLZjpdD","owXLrWHPN5yE":"34FyCZBNZO1S","8WT3vGDqQSjR":"ChJGqEqJcCwx","5dZccL6l5UbH":"zbWEF4Q3U9aj","DangfSBDIQ9S":"Zn5M04C39E0K","XoESMQd6Sx0A":"SMvHSQTpCX5U","IDW2wNm7jrzC":"mz3GDGBsx75h","HhjSpezMlltD":"ePKQvZq0Iozd","IRp0tQoXbUay":"SJWuWFJeluSt","vuiiTiNSrOEX":"jnQnlA7eVDDW","5xnJj8iK5fY4":"MTUGrsTJFd1h","R5DIuFDPXPQx":"4mtdID9vMv7I","eL7GZkHfj71V":"kgni0TUEUlM0","e9FIk0f3SEJi":"P2WsA5kaAhJD","wLenF3NHyJo5":"4T2blDL3FFkk","iAEngozMxX24":"9TkAeh5GnBsH","GEqvY9KvBsq2":"aJkKpOTGQcLR","ZmiixtItWKvc":"qOmIc1YKCFEF","62NN7TcGrDJI":"75GIPiW7QKfd","yAY8k31nHxKz":"qTLyoXCgpZU8","WcyjvDjiyvRB":"qaoNTpmB9du6","BCCFFHMRtU7n":"sPgnKrunWQo0","cjhJ7FoXjssg":"GdQy40WJ4vw4","XyALxZuKkQts":"7NMuDkY4qEpK","PKP6l2kvFZFd":"n2GIGerSgOhf","j88nPI4ZO6dI":"UGFdseiFvzh4","LZUKFEq0EoG0":"TFpoLN12EMH4","fOiTmtkFHtxw":"98AumqPGF4zG","YxmmQLkS1A0k":"AgCsof6iNfO1","Cgl3XfHOgUGc":"ZYyJFEltq92e","LqZL0YY9TO4C":"ZJtn0Rpy3J7e","9klS99MUB6Lp":"VvlvSEs3uCqP","H2CzWJDYGFJX":"PvBWLuhMN3EI","UVwDYPvyaHwi":"38IWJgxXRl5I","gp8Krz4YN5j4":"Z0S5Z0brejTB","xbsYGmdamHOK":"pYiPgk5Asvjg","ywXiXxhpibcF":"8XTDW1QP2ZbD","w2xNQo7zaqBB":"LZBwnXfR2tVB","plzfcQwne1ha":"dAJzLgVlZf79","C2ZTl5hmZ63O":"VnUQ75nt7cJH","1Y56rfd4ua1w":"j9g3HouMyPbZ","ckbttQcbR8Wt":"NcrwhcYCFf6d","tAiDo3QptdBx":"omegiWaFx8Au","epgINFpzN6BQ":"3FpuCyXNxsom","lNG7LbyY2UgT":"s9TiZV6FKsoz","pCivXANbnmFG":"guR7mElVXV9j","XCjJEceCypCj":"frVqh4y6Eouo","XLceE94A991H":"QYE0o5egMS0z","jkiSSW6bJqpV":"1P4Gyug2SqaG","6VteSu8aFF4t":"51lNRgLJDVfq","6xKghl3bff5D":"3MXtpHXuo6c2","r4lTj1GZRK93":"6lOhIQycmJbr","qciJcWhi6d6H":"sPvZVHiDU18a","OV99XuMg5Fmn":"2pkYTtgmlmLd","H4IXuG08ai3Z":"ElayXWFYGYoR","Wiv6W9yY1cAd":"rgswC7EpT8cU","F4LkxJsObLoJ":"E3SxLlU9YIQF","m6H1xakJw9ho":"W8vFrfO0XhoV","Sfa3LTf7qiNq":"32dXK20GbDqu","fG3cbKMVyYbq":"2GaezThofaYJ","HzX1En8TSo6G":"hZak58xLxOyz","QprCZo8mBX0x":"il8yhXH43Udf","5309COgbBa9K":"iZjPTk013dtn","7oYmcRiRCRPN":"RiM9bBWjcDY4","VWoDLngWUyMy":"GkWppiFhiisS","WiCMElT4a95W":"PheIrLyJ0RWj","kUZxiHHf0SAf":"2K0Y6EXQFEzY","MWFUa4BJCWfC":"VQ90tKyMhuXj","PaIYlO8zz0Ax":"4cc1CTjiY66d","Cj0yItEUhRwi":"vNtKqL6nSbpz","guZiPAwTRHBo":"82V3Ro0EX7eM","V7K9xJmZx9Zs":"9WuFUQaHohLy","uYmgdaBOWLzF":"4SIG1Zcnmrw8","JCj5OrFN9IiE":"DQDLsnZqZSDu","ZKNceA7GrslE":"5dBE8t0j69Zd","PrV3Key7sNTq":"Cp7eROWyHeu5","6bDMNlMkhshB":"m0V8P4o0BVAe","HYVi3fZBLmMG":"FMQ4rxOW2P1l","fETftOM9GcCp":"QZaLfjx8iOXt","UaIQt1EXNtww":"ZCidpWdhdWsC","hC34SPQlXrtt":"MZsEGWHe8wY6","Ho1wulMknSX9":"hliaMRgVV3V5","cbea2ACyMGSv":"K1PKudRwhcJQ","HznVd2RPm7lB":"sqNdxXcX2lmN","v0jtYI4D1tsE":"nhTEPmcr08nT","DiQwy4xHcwpa":"CSSm2pxSNZQf","w5g3X7ZDbvqY":"uprb5NW4WOJv","YvVuEggBLKco":"SHt4ss2ZSIDh","uinBcP7jbBW4":"sap27vMTuN7w","fwiBtrYvs4n7":"qJwc2akpfVdA","g2LND5Igpaih":"lu2cQK00hZKa","ocB2aIOB1gee":"JVbThm2GydNP","DRQ2tRg2qEN8":"pPeOGisNVg5G","2Jhr5ujArwk3":"ZYRBsTdZx1sB","eXfaTVrLe5MF":"sjJylvdz6g3Y","USmHJLxIrxyB":"0hgfkuk5XLM9","QrdckA3Er1Z9":"K7GBjg3iJBfE","tHHSKKjmJkXD":"yNg3tHMXxxbv","XCatnNK36kCE":"Dz37jHsRoChk","4x12iXpOHvPn":"lQiVuwHYuHUe","oQfu2ipYE8YC":"KPImacsbJN1g","HlrH4HgGOIJj":"t0PuNs0OcsKs","MtRrIvd5KtPT":"0sGKITIdsNUa","UpAAphPmodi7":"Ec5fw9r9eTSS","S5SlsB3C8QIc":"nofnq2BVI3fZ","1YvR8MewhA0B":"50Tq1TU553oy","dbkoCWhO5GZG":"hW9VZd7GseZ2","rqQWnUoDq7PX":"ufggjBSnEBCP","imOsjcbSRYGu":"BIjbriwKXoav","HivfyiECpPPp":"qfi95vlMws8m","XmBV4cAqkvuj":"PtApNzLwuq6h","EkgsylixuBRR":"ptznZZKkD92P","rU7lFxpgmuup":"UX2RrbRSLd6L","WdWEkfjgzWiF":"rgY4ClrcYi1m","sXjv2OgTp7XR":"z7ySMcGzpL3o","NjnohM1DWleq":"RV5mdHoT6V04","WFRkRquvSauP":"9KsJBuwQ5Gh9","p7jpRzGw7X4j":"FAs4BHSbblz4","nmVGnl7M4OI4":"027o753AfTQ7","KR1YojLa8am0":"IAjGi1uJ4p43","T4jtrmWasvp0":"CuCAUmOSd9fQ","1BHlQuwMtbMY":"mjJePVgJnqhB","hX2sv1aZGVz4":"GuDf4M3ITu0C","zNo4RxMUh6tD":"8ykAl1Y1qiVM","1EteKvknzmDf":"vLfekrT38oDV","cw1BAyTQZvxB":"14oUwsx5sirq","5PBjucaKTFCD":"H412Lzrk0fY1","NqDNnxjnneI3":"DcB7UktyM9lf","B3IsDndMRn86":"5xQtF7a05lyP","SUOL1EVBDElJ":"KEH7tCNo6HXN","yEZ4JHxa7FNk":"SKMO6vL2dvzJ","tkWtWH9UzeCX":"lBmg2J4ZppV8","FYUf8Yzn7Ki8":"O7TB7jY0HVue","ySvf008dTzpF":"jP3cWIqiFnM7","2OZEN6hH9GzR":"eige5GvXzqRx","kl5odcsrLAHx":"yiM6dROWQY5k","n8jvwro7xE2Z":"ll1Rb3HCgOSo","9w7ibESjfOYP":"qtQMtcziHRmX","Ns3Gu1uU59wv":"KYOKuOzjMksw","WX4yLyTd7Nr7":"u6ZHlcDGj7pe","OcOtFCurvDPv":"LwuIjj7l1EaW","yuqxCKlC5w9W":"60tjZ8QtfduD","sEmczfekHBHD":"CgKuc3BCKWJS","PBCqp6TBHzVf":"NuxIX6nnc47T","OIZD5tPhUyJm":"en5QwWtve5zJ","detOpyF1KtE2":"YPFP1uX5y64U","l1GKJirZZS44":"ZIGOhK08qHPD","OJIKPLLBzgJk":"ULMiLMaclnbd","55ubCkQzcyfj":"5oHbCEHG5xk7","ge4R0biXRMIE":"SqcMVBlhZBFr","U8y59CKsUFDt":"9gDisC8oqYZ2","xLsLdpEMJW8T":"kA0MICFr9Oyd","ogommaEoFGk7":"YD3a1J1rw5yR","QgVj7nkqHGji":"c8gY9KiSE7HH","YV7j0DYoQpQ4":"258qTYR6bXds","KOW95AUG3C3y":"ltiKhdArmsQR","1fSgqPhZKKmW":"qQkpyOJdgpQP","TC2q80fLwzgc":"Q55Zz2HB1zCk","OjVFEWcK4cEv":"Xh1zkdaexcrg","HDVsZ4GSd3GX":"p3RrBMEJHwv3","8EiBcZfYxzTo":"HYHQ3Q17rNSO","f41TcakulZxk":"7WmCYKuN98W2","mxoGPnDNNdBs":"uwsVHxeHsTNy","NGk6ruo9zIHM":"Obsa26jqlSR7","9MPPHbwzHidD":"TyOxpfn3yzaR","Xy9JdzE32WEm":"Tro5xkkIxObj","K3CcdH3ZoiBw":"GAe2HNZ9leH4","mYJmUu1C6Gs9":"HkhNElBN4bNl","mGaffnmCWwFe":"hgEmxT7mUyqw","o1BVG4huMXQw":"isrXh5pFqjY3","CPA6SPYuGCK2":"goDDUMPA98sn","hKHOVfCF2WVr":"GdwC7ta5Unxz","NDI6BwtdAWmx":"J9U79WvaSyNH","wKYx6w809u6I":"DRfnNsTkZqdk","JTUM9pATxhuc":"EQVjGan34rBF","myvHnL0WO5vy":"ssQrfDiVefYc","vV5296PMvEQk":"ktMjrEhKXr5u","VN1JwBQnw4ec":"Ks50U8kBSlTd","rCio55z3fc2O":"ztnlmXCUXaQe","dHUniUzfOZwd":"7xSlacvesBOb","6aEXbZjwqjEx":"TARnKtfSXb9V","T5INZ13BubWR":"kJnQo4q9djHk","fcfV1cmhSJyb":"zSa7gQdr9vDw","u809p0NQlwsT":"HlUojj83G9HY","Gb9IEc8gOtpM":"pauXbits68fA","78YOtaQOAeHG":"aMal5fw6Zq4o","unCKZnmhOWBc":"TYQjg1cg3mRz","WtYuLLTqIgXn":"3unFmx3zBPya","R4Qc6iUOLbiO":"Hr8gsZvsF4VC","9XwVguyPHJ9i":"8sNQ3x8oWDA6","JfOmnLyF08Yd":"HeeehKIC5BPJ","DdEuno4BYFbC":"qjhFmNKEMdeF","8Z7wyUKIdkj8":"5WXGkVzU9GzP","5uisR0wocOuf":"mlBq4Ry9FFx4","jTyCJCNfOmAW":"opmCGbbccvPT","peFV14OpSzQO":"CTTaY0fMF5D9","CrHEMdNQernx":"GdnBEtcYiEhv","fJDpxEwG004p":"m4aUyI6BDdK7","dlEgvo4VRm1T":"YsXZs3egcrft","LLIkSIwf4HA3":"hyuuO50K5YuS","T4A2YzjiYThR":"iljdZ3X3dzzG","Cg3oVYqaaS3D":"XvDDlvKD7bFa","LKupfI9bBo6e":"eeuuR5HPK9Zt","g4hZBLIzHdMm":"cfeyiU3gqRb3","Ha4lAyVCgQhX":"tRto3IdC55SQ","jgBZbrA66Mrb":"u8nuXWcZJ7Iw","wkj7cSBbN61T":"o4fxaaaikwCC","pu4kneTPXFIn":"YZmGU5FupmyC","kIpPlF65LwIi":"yEu78nal7kWM","HcOMQqpUZM70":"IfG63yBCEMEL","CkkzFYNDDTlC":"uXhMTjvZ98xi","VQOWpYpyPWNJ":"RIvv6Wh6NJLw","mUupfBjJCjpM":"T0oEMMmdWNGW","IkXgmQXVhNtc":"ODmUFj0lzq4w","k2grkaErQpqT":"fiCIRy5TMYMa","30uRWVREYjcA":"2n8YbpvPMBGH","G9STRZ1GqoUL":"QPHJaWP1lBx9","5HhGFeX7RavB":"FX9j0lcONUYH","PFDdC4F2ZWVD":"0UshsGNUgFv4","bW3HaV6yNO2K":"4TfJhcpKFloC","90CTgZdbxgE7":"gRMZ26DhNVl7","eBcXs5yc9zXj":"r92IvM2DGu4w","RQpA3ybcKNHc":"CPDq9MHMhK9S","bSbh7R6Ik7RF":"r7ZlMJFaXZF5","iWXwMNBWiBDL":"AUzIDurJaceV","5bHl8OXvyWdv":"PJhwDfvk9pQy","i40aV7jm0l6f":"frxEyrthn6qS","u8AM7jLMFmKk":"ggplI2hLOYcz","KUTrKxLi4RmL":"xXPKWITCI3XY","quvJPtww76es":"JSAlS4Pg4lSI","q6uhQX8wr49L":"vMF3T8Rxer1U","hYGbNf3jd5C0":"8ZQBa79ybtw4","imJLe2TSiHlZ":"fYgfDuGwsq1q","YBqjgQIQIIx3":"XjL7Qpo7Ei39","lTNGH5EdgxUB":"CbkQfZ0Sp8RP","9oaooDrvyoqg":"noO8qtBvSBXk","PloWqzWwV9A2":"dJC60dr6Bwz0","6goCAcozpNkY":"IRuV6ls0O7sD","xy0orX9jjX5j":"fsu6rd9SfDsd","Bnq0gBBFSLRP":"CqcLq0V7VxOl","d2od1L1hx2I0":"E5Z25WToBQfg","LWCF4JPFS1x1":"b1IUuNhpOnDw","rgfPtv0uIesu":"Y0zoF4DhWlMk","AYOIbnW0zom2":"E3iJrE2JtFCL","45Xb6NPCOpSF":"28OP8AiFiT4v","j4yAmQz7ho9u":"PBnSASfd2ChH","276azWMNCj6z":"aTWwV90UwBsT","QYft1xlcFLHz":"U7E6fHqYBZ9c","xz06lenM8BBI":"tRxr5IENqjHf","ZrGctnAmJDzg":"gRwNsXbg5FoB","dInuSlLxRyfa":"l2tesmcWGWWy","mZVnP1OIsSoP":"Klmny4qr0cKi","B2AEEIkjy7TY":"LpnRqJDjsI5b","ICKa7VlClLNL":"4mV1QdiRwCFE","ywoXBD4KZWMH":"h10L43fd5xwu","nBeb6gjLeYGQ":"Le0YWJioDJ3n","KEzH4HVRv713":"fgDjMWjQMqiL","PdsuEePU4vc7":"HSXLYQiQwnJo","eYPcksApvS3s":"eNstsZFxHQd6","WXWcpw9lN0Dy":"7DTW79XXOQa2","8Mm0OkW3nsGw":"3IMmVVXl74rf","dexne8srj16Z":"fO5cdTVs6ID5","Te54TXcYxoij":"Twzi1nC7JBug","ekIIOFHMBDc4":"HMzxsRkB85fi","PUzoqmQRCyQm":"etLlixaeruQM","wOFfG0KkCSGJ":"rwDmRZogZB8W","Ovu0AsI4eGxm":"E4acJzXkmekt","XEV99wPUSavP":"2dRjF3eYdDLB","9h7DRvp4Y0Gz":"7kblfca2id5t","C8SR7qOdgbzS":"EysxQBKLYaMq","8yJA2dhNSKz3":"bzUNREVRsPOe","PrM0wuLbenKw":"3ZmHiB8d4Vna","Y77OoSrbfYxA":"1ntRDQ9xE4MO","8oCVZGdFD4G6":"M5YFsxHkOpXO","AibqDcdmYpJZ":"RpMp0IuMJDW9","BR7HUdW0OvMz":"Gxmg8aTZ95Zn","yWmrzKcaBjlq":"FT79uk80CmWx","9Rjb11tNJ2kv":"EhzNeGldWwLN","5kBN42w641Sk":"gxcvGJZrZEoZ","pT6QoNSAWWMU":"btkezly1tutQ","6mmSFQCFms0Z":"C2mF5mAYGudV","GLwTVLEe1f0O":"ZdG5AwPjRePF","oJzGQTvtc6Ic":"d5FhaEFk3O18","We15SPcJvQi8":"LxYZhNoGBbkS","xZPGvijxTbaM":"8WkCPvuIgogC","C8lcy3Vy6J5g":"onKnC5SulGhS","yfrJMm6HNbcO":"AY5WCuJKllPS","sTH0mXjLKBml":"J4PYqkBjwNxI","lvF9P7qNIvir":"Nnfa4Qe1GwXd","g1j16bM286hH":"a6jYmVc0V8Pp","FfIw38tjBEIj":"NFfPbKsjibJt","ZLsXhkHaN7TY":"PmXK6JYADd74","Z0DuEf1Qugvy":"HzmPVp9BTnpG","Z6rx1MlXCzqO":"6i7Fj4gjk9Ws","FES7HT6B2OjY":"rwqi7k7oWImA","1LaOzpbwVV95":"Bhua4ghiaA5Y","ZrMWlyu4mFkB":"jyurKIZcPvwX","xL9l0ckl2Kj4":"V3B7oDBRwb8x","Wlz63TvxKp7x":"udTeiFyA6Wlp","SRKvHC605J5Y":"qhfI6UU5pB2F","Uv0U3aWQzQdW":"gFtTvVPZ1FMy","9FiLX5vbg22l":"vjjc2qzFKaNi","hIFAu9zsPL8G":"XHp2yoYbbIi3","gFgkjABMB34g":"wAniZVFAtYyF","TW8MoBW3nns0":"fW1k1SjQtVx3","7Ukksxk8ZkPq":"6xOSSbZZzT8p","pJoKJh51w3eU":"fXy7u7BGQL5T","wWlUrFedhhWT":"SImQA6Vjp3Jk","grautfNyHovY":"v6HKulU5vzS9","SLY4xEG01xSs":"8EqXxpGahmvt","ADhNcJpEABwe":"Up1e7bGBUxCA","QnITv3ADnzST":"594fsNo8eG72","Zafo8EBB0GOX":"ntfKxufCOQGU","GbPJvuinqWtY":"WK65ahOWjqJW","f2SDNbgiENmK":"xp0sI5kmzQtw","DbUfIvwFZVXg":"IqO2Cg2iIyMF","PKbev3AQEyuK":"LfPamDjaepiI","jAouMfkF2gHz":"9m2PYjrUCoRx","c4Zf3AzEWSHz":"CUdEJyIUzUW2","43dep43PFdrU":"4zGzN4Kb6dym","7IYnElmQrxxm":"VNiUjLaHqE33","FwtAUgvACvgM":"pDkOEVpWgJti","zeaiwLpVbPdH":"3EuINYohbTJr","sYLqScIPXcsj":"qHwP8rozGNCJ","4KPt9czfzQGH":"CixcpD0n058k","vArWf8bfgumL":"K4yStVxqzkAx","jCeupH8G27t1":"zLun4E2KgUz4","Mg8uQ7PuXnZB":"e4g4gg9XICpm","HTAHlgJTbvIo":"cGL67eefpleg","gwLrDHIZYSb8":"ajdhuqvWVDRh","jcWBIL28BsAk":"o3p8gCrsOuCV","T4YsRiQGHc8p":"ptO2NOOLJmU2","mr5fnyu3TE9i":"hIYradiWK8IL","tJXPhEp5vUX7":"5iVieJKYOvUS","xbpBRdz7gM8R":"mvSUFLxxqfcv","IfaVOqGOTC0g":"Axb6q6RUmz6u","ZWRz84pitfR1":"PfxL4SC6mUHd","ZCc6SoVvWUGf":"nmjOz0anDmHI","pfOMmh1p6JOh":"Ew1ionpxJtoe","dZUcVfdSFPEc":"1wo2ASY9tBVb","GutK0mjKTdC3":"1C0UJLCg3CRz","WGVnd83u9cAi":"n0pGwqE8gSaR","v57fb3G8uize":"ZPtCBeEox556","wMii7HgdN1lm":"rDznXnzmIs4e","wXuWAFotoXW4":"V8ioKUPusKT9","vysWhXSI6OCD":"GzzsaBf1rGgb","2E0SQQv0QgyD":"uMiGAxxTMHb9","5HvDfptIq8ae":"rGEx8tqSbAYX","2VRJckdooEc4":"7oBmIuslKXeK","hHlidniItLxZ":"9a1EjE6XsOay","W18wMoG54PDG":"ogSybIbmTQhk","hLNOqwvG52lY":"fPvRTaGoPfvy","oVUIL7pDLucw":"iBFIqvnYCM73","gdA8HGHlrsM5":"EkBIHUrUmQ88","N35XQ4bzL6Gw":"Nw6EmWdfDUGj","K2Tw8TGcUWl9":"1m7b9EG59aHN","En7jwHKdi160":"S59YM1BHz2lb","tjRmc4w2kWqK":"xUI62IDe4bXb","QWSqNwZ5eCWr":"T8rOYKh9jqU5","PM9rEyMAC8V3":"SqnXz0KEbNim","Exv7tO2mLSnB":"A30rfMh2nu9z","UjEQ17QuvAYe":"RLAFrou2CJTO","c7uFyAYpIRJ6":"pWegolmuTito","lNGXSEwckYr2":"h6lBlxuhZ2If","QUZum9B4Kmcx":"rXyWZCms4WkS","p6GWlsEjJ4xc":"8pkShFWKomBA","SaxhqyQ1XWuq":"yMOrtQSsgbOO","5kIJnimM2Coq":"S3zuIV8A39ce","dGDhpIJ7GgbP":"XwyKXj1jbJu9","YJQ4xi1ZAsrz":"L1FK88TA218f","iY6g3boEfPqe":"6TK5u344jAic","sgieTTtbQhgI":"Ptcyffu3oB0C","s0xqMBksJgiM":"gitdagqHmMEB","BSUNhu9n7M4Z":"EWevGAAGKbXG","EOTkUYZpzK2e":"tc2Ww3OQwHtD","DJyRAuN124xZ":"4ag283PXPmhF","KEhlZIbTmxUo":"tDoGHEIQTvA0","axqBPIGYtHVd":"Qbz3xUNgN1gB","etMRNfEGiQFA":"1GVKHQPEK6eA","uOSuL03SQvEa":"FXrc9KhXwpkx","nIdTATDI8c3o":"RYWji5HmCPPS","iGr5HpyuRQCT":"9rFpYNMHaAPV","qM47y78KzjTC":"QQDQGK9bYSTI","kd6OcLrS8wgM":"r1Tw8rGwsLSO","uECDBo5dQqM8":"ABPoWVtI2F35","RcFApxtNu6KV":"Zy58sg6MPp6H","DZv4oSPR13TQ":"7BOUoizRtecO","Wy9dTOa2mkDk":"TxX357Wo28v9","Wi0ixz5LQLQe":"fOgm7dpLpWCX","DhLoenjNohwY":"gRQZmvNuFWPu","Vy5EUgZvK6QB":"N6A04YBZmtv2","he6Mb1vIxtPW":"epJlepWlOqUY","ycPBX9RuLJZ9":"cdqCLiaMURDm","YJfrt2ZYcFlV":"ldh4AYiwgT95","CbzmkUmn4ajE":"bPyz7r9vBcVY","ao88szikAohu":"WZllpCJB0Ot7","sHXTHOthLmXG":"8EqhU2Hi38Nc","G5sMXNSdKOSs":"6qYPQ8QCsPzr","yv5E2rVWcGeH":"V5EKO3u74f3J","6ZswCLXlixsq":"mx05XGL4Zazs","LVfikqNpc5nD":"bwzmJHBZZ9CM","9oMIEO2e7hiB":"22MMyd1CrJdz","NIgYywSWXwsI":"jWolhmIfPpMh","JZXM4Qs3XHMp":"RRhma17GPOZG","RFjZ4DYs8VrP":"ugLq8BMLUUSl","4CNGlrxoHn4v":"j7jNo9Bu0mQr","qdCBoJ5R0uKv":"cbFEHHXTzNp9","2MPPpkpBBfeh":"OwjkfgqnYH8U","SAGptEsQnUYo":"U7uBfq7Dh9nz","FjtkhmFc2jgp":"MtzoKiAc3UFr","EWwM7cVqFClH":"6gqQP5i0d4yC","fQquKDpxvk1y":"nDRrRDXCIH1V","8i4sOXQPv1ku":"AeCKIPske48e","5WJfMAQqohTH":"gBXSl2r8Coxl","vBYIxlnleSnm":"LyIXPoKeq5DK","nppaNpwcvuWU":"U6tfFAiNBixE","Q7marB6l5eOT":"XQORxhB2ZLEy","MKz8gpYCX9Ot":"Zy4o5RvyOOoK","4Q4v92JAXHro":"QnxigfPHlL5k","4xDi63pC1ftH":"dZ3ARV2rT590","FIQOid7nezXx":"02htEaHTPxNF","mcPpwjRilXz9":"0MA1OLzELpRk","nYmh6lSGnXiq":"o0z75fQ0clgl","HzUnjaPctBJn":"lbQ39nZmZ4Dl","2yOi3VAM9R1i":"VlldqeLtoviQ","aYiRYCmriqEa":"I6nTigZiond3","4g13w0MMslft":"GRWyJTTUp1YZ","8mNXugew2yEl":"lzGgZ7ixiMLl","SSZY9n8nILht":"L9RSuWqB4KHk","CoW6LZ1ARPoL":"nb5rTCh5A3FG","zKrAWquqbeFG":"JH5H5IggRv5e","EORkyGhqFZDY":"dJpyMHYozWzF","J24x8K8ZQXs6":"14OghRX2KdEV","gkvatrQWV50r":"hvmbPtaxc5CV","v6bjFTD6u3cV":"jJkJrMLfw18b","2Fgd0nLJBVWD":"b9LI23EbZpP8","TKKxqFCjcI7z":"bUeI4SGNIcOv","JruwMoFHTKwM":"QltMcurWfupY","jfRlqzSGvxId":"7Lp0fAGF5tzW","pBZhQTJw1bR8":"PO1QYIsuzobV","39Xtd2PGLAwk":"OoqgNjKDRTnB","x5bE0RjE9Xtn":"1oVMzXLnNxd2","19PdJTTCgE7t":"GFzvmwkpumQ8","bO0QtXpFzjEF":"75Ad2P3IkZG5","wXcwYRHkp9rZ":"P7rs2SIv3Z4S","l704ITnu3kgH":"AGR0YxhEWdKv","2MSGX9CinFre":"W7NnhzNoAJhi","XhbI5xwJbLG1":"OSgvl5fWvngw","xWxJKsLc3hcr":"5Bh6OnL72IfF","6TX4b12D6qgt":"mFAVCdF3F1vj","M9ZiycAUvEe1":"55I5UCQm1rum","nDv1Ec1P8ZaB":"OClkhdveh47N","J3DtF2U3ZEKf":"GFRxhtzu36T6","PCAHARHNvXZh":"9RYwBdrFnlkN","W1vmWCjpK48z":"csru3ZqvtdbO","zvIaVasgeyne":"y6pMEO1oS27l","IeINxXFQrqvo":"QRrggbiY5dIG","1I9QO9w07FoZ":"CXEvSmWKSC1e","lKyY05Za2zbx":"Z3CdzAzvGMmc","MWmnJpNhoDUm":"kG5QOaTXwwdJ","cDW38wR0HzX9":"0oo4S7qLGpi1","LaTeNF51M1YU":"JdrFQWbduMUn","Atk9zhPGhCsM":"sUrx6J4euQqy","qMredKY2FUir":"bM88hE8Rxd3E","irzF3Zeck4HU":"uPP3bl98EbgF","Br8t2y6Ew8J9":"1UnkQyVL5SGj","BJkU0fk1H6q6":"jyB6D5wX2jlq","krLZRLvkA6LD":"gpoyUROc26M0","cr6wmEzSPYCk":"58QM3M2AGuup","yBKIMkXiUp2C":"7aOjheXfmo1k","8LhvL6JcYGA3":"OZhqAQhEOocS","zd9yDgHCW9Ud":"lMNrVoElTnMr","tgGZONerY2y9":"bx4VABVfFFBX","iybCYCOx2ogP":"ewCjAVKqaCUc","aJ5pg2ZPRrey":"zCBe3PNlQnBd","0E61hs3KmYFZ":"XKRDgo4Zcpur","l9wglPMoncGB":"CO8VcXh68PDL","QdhdL4Pvwau4":"eQSIKPuqC9hs","Q5ordVrWx5y3":"VrWFEZqIkuWq","dKWgS3luIqnn":"dzNYdFXdq1FO","lbmd1KORqjte":"eucxlx6ZDYYW","Y3j8HB1KTwfz":"Xz1zXEbYYIkO","nsAEdAAPBtEw":"HTMtCuDXNNxo","SKFDsVyo2gQQ":"U8f3oF6NnFgs","0DV5hoBoImTE":"NDYqcoZYXqht","mNpVoSXDQAHS":"6n1ActopDeOH","yZ44P0NtvkEw":"PNGtXRuHaXrK","7paDyiVSU4Re":"K895lKHCK01K","XcGv77IxKvhi":"9MNg87G2pP9u","YjJu7vD0TbxJ":"LdWZaHFzvPQ6","XctNHPmlm7EX":"nCne81rGaOVV","kY2QhPBRxVQc":"dvduXtbeUTJq","BQ5AKCOHm1Cb":"E5FbMU1DXQEc","vKXq99JMJWfT":"Z1vdX0uuiVkp","5xS3IU1E7pSd":"BCKLsWN5yqTE","ISXk6a3vaGBX":"q7pWEQeonjbT","3pQlJFEstofn":"qQhmYUbvlUj1","DbOQuB2Ovoyx":"YyBTYre3QB7U","19YDEsaeMQD3":"PQ58CN0sk77C","meFjJNFuHx6j":"tkn0ygCKZ92j","w1bhIR9i66At":"lRlMMqSw3zc3","ECYPQ2ZUETzP":"q8MSZfvvqXjF","bh2ERy9Pqwtg":"FDfILCJSwth1","7KplzBLScLD6":"6gDmOFCBllTw","ZX04v2zs5FBl":"0z7WXEQAp8iM","bjhuJJyV9ISL":"9ajnWDoegjs6","DQqLtGXUmEEC":"dxEl0ul1pS2e","kwsWcV2rsFSc":"kQ9Lvj4d97Gr","8KE1KrRwYigm":"BABmKsTEghv2","LQqi0H80h6sp":"2Tg3aX9Eyp89","LMHWNm0rRHjl":"uDhPzWhag4iK","sWFiMxcwtm6K":"JutGGhhGqxJZ","T9e9YvgI3P8T":"72yZl5evGlwI","pqvoippC8ZnR":"bnDxb66jfgzX","wgHDNkk4BOQJ":"bIRC4HNcg0T3","9anL3JNa6gzJ":"I3AMBiWwxGQT","W2KLq9Ki3X0G":"Jm0mFvqYsVSe","mUKtWb4p5tvv":"0EA0bDxEhVES","2e3YzZasGFUV":"IajDHwKDXCb6","GaHw72WPuCPR":"j1GQKC66Esbl","QdvGirAIoIZG":"1ck9YyKD1nE9","M7W6lYpS0Owi":"6PfEksuqTuG0","LamVF0Kzelft":"bRzUDRGzpgMr","TXkxz8HARTVY":"lSfPBp4Eoa2c","x7MnXZGh8njb":"f65c5TDsLvKK","YnCBIxw310ze":"yAhmDBI35bZW","0cS1Ki0rLE3E":"x03KnP5JYtlG","iQRPHD0onmS5":"wvXXboKYYTYB","MowXOPxuF2u1":"t8P3u8TiteO7","rNFeQ8IlwWAX":"I2oEOP6K9SbH","sZzvagHxL4q2":"uwmSsgBj9kjz","yXZPLS8ccTJd":"z82V5atquGxN","rB0RA74eBaHR":"sMl6eB6kLZG4","zDOIps7Mv4JL":"RLBQMiRlhHs8","9sjafIr6a010":"eXEfinHd26GW","UOHgbsn964le":"7195bdMIJqvQ","Y56s5Utd2JLZ":"deAeCHUBE06Q","jXROClW93Cc1":"Fme0zjNh1t60","T44GFePUpA7l":"rTkfLqYpBGY8","JTOtFthseG3P":"WbjQMCVEB5a8","apf5Tgj733Cx":"wJ6CGU5FZcP3","NOry5UKltmhc":"hZIZSQgR14D5","o26kzbiYqljr":"gbeL3AWESxtN","uefLhMrRnrrs":"9Zvlq6lYdqgC","k4GEabf7PZyu":"upfOCs8gxfko","gqixumodFa8U":"mYcnDr6KHFio","XxDRZE5bJdOb":"e4aV39j9HDjt","zMpPP9njEAby":"NlD9FAJlTGm0","WNWP3Ths4A4X":"f5otaTejQtsf","b4P9e6lJzci3":"jXyouflPPyQc","Kgi0v5DLJVTy":"l3l2xgFLwBvF","mD6rqN6sZDki":"K0WAnqnFBG0z","vGQ8QW69TdUl":"9F9AkyCXCNhZ","yAnQQjsxZ3NQ":"NLFHmaFWveEB","RjtznRwnT1ZC":"M4yHCuCHlEvv","iMmn8weWU9fL":"XUrn0wyBxQIi","bcU5Of9a9lqx":"WwKww3xD4NYJ","Wt782hyDtTik":"lGAgXPNSsQBk","EKTLOWd8qp38":"6l01xHD9nlFC","GBIiuJlCyrtP":"nAhhJqxTCYYu","NJmVvMEPq1um":"CFFyD5VNtecf","sSQR07O9AWWn":"eVoqPwLrufWw","esJ6uuG5Y0lc":"pOICMv9np1xC","wQJOgOYZEacM":"LtcxXG1LbMCu","NRmAi8y4AlGv":"h7tNJMabDsVh","MrWb4W0KsSXA":"MshOVOb19kSL","mJqch9fT7SD1":"EqbtfwbvoZKa","ddAaPAitbIoE":"XumMsr0TSGYD","xoZwiQK3wuPF":"hQWD3Vg7Gvlx","rH9qAu3G6IOq":"rdWAvxUZVfz3","DsACGaOSjFZs":"CqDD0P69Wplo","dn6Kxt69GbN0":"EzuMULJFgzsy","7KQ15JmjpXSU":"m5QJdpeQXFkd","6rng29mEwiyW":"clvEF5fwxyad","pBVdFPVwz6EB":"u6yG6KeZBsMe","SVDII77L9VHV":"VrQ7IpKzUMlT","yzmU2qMf1r8w":"HFb60GFcGlXy","5nQaU2Zxx3xn":"Yz0c8ITWEIQ5","7naDTEhYMPyh":"jNJL5pR0U8PI","qJcmJUOaYwJj":"cWoKN3et2ul6","rZvtY0L9L1ep":"Oqy1dHLoESFY","bu8EKr5AJVu7":"4fpadZWSfo07","UoAh7bMKq4xk":"0q4pSZgBxnh8","qFATghZGlNhL":"HrG1wnNcYNjH","Hhvk2ZUTZPhq":"G2BHVxNluJkH","V6ZJt1ZO9qNA":"oO8oJZnjNzG2","kVQBwqr3u326":"HMhgJwNrgdwg","rOxtH22CDVE8":"c1TZJRGEWNj3","zrpTu0DZ7IPO":"7OlcZyXYvcaI","uYAj63Wa3U6k":"mDffzh8fi0Go","90fzxTxLGya0":"B5r7k1bb3ALV","KAS0HZNiN2T5":"W6Cg2kiekION","RKjiLETieqyT":"ipYoV3AdCJTv","iBj5YlKsHI0Q":"0x17QI79z3IA","R7NBUBrdThAS":"BTTHASVN9Mte","bjVYPESbGpAV":"K2kpq8oIHdSZ","nI9f693Yb6ZB":"hgN1oWAusjvD","FotwTbZfOZmR":"3wdMKPdFWf7Y","UBDIr9Aah6iP":"HTqXM6dlYlG3","hemiDjGGWhSL":"zm2U2GNHJtmJ","tsRnS6ktId0K":"VjVe9vXgMR2N","ZveIZOeGCvbF":"rlnBkmKryyvS","iqXLSSkSAk8B":"0XEFLoBqxJSF","FbCjOSZ0qHiV":"VdmQv9SsEPGu","0umCr8ETdtGW":"tQAZ7vjeaCgV","F9APVRATIRma":"KaCKkOPY3Eek","93e4TDp9DqAB":"eAaPWsKFRcVD","U4tl7Ah6WkwZ":"0OgPsQTiDtpJ","lrFQObYEZlXZ":"8TH3AMC1nTVR","pu7vLrzEanrB":"EUs7aQYwoTXJ","a5R8WKL8rgY0":"3hXPpk9Pv7oD","JvpmvfLEWtZ0":"KtuPi5zCnZOy","k6yqVktNSPwb":"SkAEHQQ6GiCi","aBSphn7hp6NH":"9aMtX1M62V6t","tdkKc7Wz9Cxv":"xnSFq6yz5j4n","IHKytTwMBU98":"tTBhGKx5kWZP","U067sQZGJidU":"kaSqmJHMLok6","5yH4wVmUryPd":"77QfUmrNoKdg","RGFdNyhMMyZt":"dR4F2teOx8uJ","zpa4BJw66UX6":"yfjfHYDENNJQ","ilArbi4RMweP":"N7njJYQC0U32","9UuT9m87u8xc":"9NpbJB1kBjc4","jcTHeVs6nORO":"2HIec0amrCOk","PutGCK13DzNL":"GHGPBpJMGoX1","JIUHCyxPOK64":"bD0pAIGWrhTp","KevP7YV7lFza":"ofIitNuiJU0q","mHcoFEOv0HoR":"IMjzwHbVtf7N","cptXqObr5O5J":"8uKGDGXMj29Z","s82sDyDjYe4k":"qYiQzx5FZ3A4","UMEcDvVgqJrF":"v9wUFKXqHUP7","sBhf3UeQBsJ7":"Pu9A8sraEuos","fuYrdXg9WdhJ":"mKkDJuN6pxHr","BpS2keuH6QL9":"TEN3eOC6rjqO","qXiQkdKG6lto":"PPfbr8cC8r49","GFpFybUTEEBx":"GSMJh8qi9rhe","PbxLmtvHED98":"CrxO69JNiSjV","pEdvr9gN4N61":"h9kjY9R2Q51r","kE0QgESOVYcC":"29dBsFFWkCoB","0dPqTTsj101v":"uTqWH4HIYO78","wrGjKtYtjw51":"IJhj21r8suam","mhCc6KkD8Oiv":"G6IhsUvvtj4B","JZTKNtwlnJEz":"6P2gB4ExfBMn","Q9doCEpaqCRh":"hupXr5DbusOx","c7Ls6SK3ly0R":"bVUz06u7HmhY","uLD8RvykwMIs":"Kg9ksP6B60EY","jwnf7B9KQhhy":"nvUg92mXHDPq","2mmJlTSkjWiY":"Utcxzcuh3R5u","02H1mmkZvoXk":"A2hbgDzywI8X","9NiJDirlP8DU":"xykiK2Wkakqv","sdRPHK1GAdOd":"lGzEm55ngg3f","kh7jWoweg9My":"HBh0MMqe6RD2","QkRU5ZKmuyZZ":"C9TRcUUMBmXG","2sORVgOH7PSC":"BtnapcCtBr3x","DSgUk5efXTfx":"1Yf3gLjbx0o1","rsBCBkfr40w7":"3VnXzlGRV9MX","FjAValThETPR":"JrBG3Ni0m1fs","ki9HXvwHBhZk":"VMrm9d0Whz2U","t5ah3A0jAzi5":"i56jxEzwNoi0","R9dAN55qp9dZ":"bE08J2BILfVu","1lKv7ow80zJH":"WTDM2SimCO9T","ewxwL7jGT7I3":"zqONsoqieFHK","aGoOR9QpEgDv":"3x3ugnQRAHGo","LicVESqmWvuv":"nOBvGGtfpDM0","7auxMbWcCVOd":"ejtw6iVspDwt","IbT2RthWO7QE":"lBWCc7lV4Ldj","gohp7Q49JW0S":"t4qCdILyXmYT","rkZZzWt5WyXO":"5oW2nvg0msOE","mC3Jg8crmmgv":"l0IK35LudgQ9","u6J4ajrcxmtG":"5VED6spxrdmZ","DssGC7zYxcMn":"v6WQQKAQw0TB","YRYYeC02bM2K":"SAz2bNML7BKk","ac3Icm3gyLmg":"sGIlJbI7jcNp","9I3vXn4VFRlW":"aYbG2eSrY7FV","gYSHz0tfSk2s":"RLHOAsFptYr8","9fK5CbNOuOje":"k7gF58Hw9bvP","35VhY7HRmZIM":"FvWMzOR0wcEY","bXdia0xlU9xW":"2HyPgYsI3AzT","3TOKICVSCZlJ":"u3SPaxD5ZMqA","h7EgA7PBn2fe":"tfGDw2Gj5OYO","TKsTaGZi6P1y":"NYT6Hyi9Tncv","eRWP86WzRyUu":"BXIXEnep4Wnx","hQjtVfvjTlkp":"edNFedO7lmDh","N2GzP3fGusAr":"fiTv0bC0G1ep","acbSsauuAKZc":"Km5sDBGFvGfN","MaRhwZsiL7I4":"eYwx83ALS1U6","41KMtlZ6h2NX":"FZQRMuN2NJVF","FGfBTGOyQZDx":"0XwoThf8nxTx","s46PBc9FXVBB":"4cBZfcxh4oe8","Z0S4sTenkjvQ":"07ZRNjpH1mFw","yysXT6Ard5Tm":"U2l0IThBY2Lq","w6ad9IxvrRjQ":"qrn9leCIm2GV","tjrxJuDZSf5s":"w6OdX5YOq3M9","HEDsgqWqn9e8":"dTfzMjMyGM7B","lNmQNfGfIE93":"7SqNessDHQhO","r5OwTpXfS3DL":"FZARMlquLXl9","h1bYaQcrIK8P":"s1hKI7eWrfvT","PLIykTbo3Zb9":"oUEq4qykLvcQ","k0kdUTI2DqHy":"EIhofHtDCsZc","GriSGJNjxiqY":"9EnZYh2DVq4r","dMcZxCOtNFEl":"5WBOwbaaxbUA","HGc0iYrjnsL7":"Jt0ySUi2rYo9","su2xEp8xWRPU":"zMaZtBSXC7qs","bu8ppGHehBCw":"0iwJwNFghICT","WkhZBIyszOZ4":"FB2JAqtg1r9A","pWx1pH6eGEtU":"TzVqZ2S27k4J","enZzHFN279d7":"6fMFAXihOz18","8FxW0XnBNiKT":"PMzmLa6hrZTE","kZY7KDQ82SpF":"jqqITCClxnby","PPcKE4tEgqZk":"iAzL3h1PdPFg","v3UZg5teWNLR":"f6tmDPTqZ5RL","ij6JMrrGw2Oj":"e0z86eywaLsW","h8OVkEb8c37n":"BXxN2Fw075cy","pUm9iox8Eskm":"ooCMIhj6LKed","9UNQk7g7AJZd":"UdLbmHKCkH1F","OdbOeFB3FBEh":"RPDMOJcHhoMk","7qq0fbQ26ceR":"xMLlU2Ec1rJP","GAirUkLZJ8cg":"UefLY6qC2V1G","UehuWlTDQv66":"p3ZYRCRq29Rx","fu6wEYp0ZkKN":"ZOL3VZ2cg9iI","jq6KN62H2iKM":"vtBMbCNnCGxL","hIbLGWRLxQsu":"Unx6s1or32bq","JWQOvasc4b6t":"JUw0vakRjPe2","1DZQiOaMtLSB":"Bmnz6dnISAql","M8nZ5FertMR0":"Y5zvO49H2QOw","9RJl1mGJayPx":"OIK5Q1R6c89I","QSOUsaOnFauU":"HcYaUCz4ncgK","4EdHaV9wLViE":"5xweVuKa9Kj6","wXmrRH4VLQMl":"MPHOekQklDyc","wH4nqZXYjsTW":"eqG82g8mDI13","H15l6fOQbbOf":"T0ScVhoTYKTZ","DoHylzgz2wgG":"LnfTjdNEevqP","4ull1eu2mZDh":"h2FHvSkJhwn3","pQbvciuq6YXh":"aCRLGBmM5yB1","v18u024QdhKC":"nwDgdAOUPt0l","vzzMK5OxO97A":"DnVl0tyM1usK","7tfB9zZRymtU":"SZiDZuOGg2Wk","eNNqbe1ha4zg":"5DA9PPcz67gb","yRmI0eU26jzR":"IiFnDAXpxrpo","jkovarWq1uhr":"TLiIrqVSBt65","0CYSbpTJ8mOH":"jEjzUazqLCaH","QvhDyUpbbvLf":"zMysYI1tu4zL","bJBHXiT38zXd":"BT3V9pFAWcxn","ZQmP5k8zWe4L":"wem0MriY1TEp","adzK9ZUrkrnv":"WcYB7l154eHL","aVCCMTOVbBZE":"GXUJl16WwXFI","CLoGeV87hgsx":"0dpJhkStnvAQ","gGZpz0pvOpog":"cKDf13KlUv82","CgEE1nYL2ujc":"Xq0AjljVlC3N","c6pzvIGJ6ZD7":"5TnJgQhqml8V","TMvU3Ayp2KVg":"QJdsTDmOPJk6","iNn3rguC5NKI":"BhRO2tjGXZYx","8DzRNVkuzjSR":"jcHpqKsjxvjq","0UknarfBl8Ie":"z8waRN9QdwoH","VnuoAh67LH0g":"7qQfGrY85Ayc","S9uF20nz08wQ":"H2A0eso375cR","9a9Gy1p8glf4":"xwW3aE9W5Qjr","rxKzZ1RlGvFI":"CmYI5XJIU3Yp","9ZOJeNtIaImk":"sbidhxYPcbZH","IRU9lvJrJf03":"kD4PmRDmdFtc","7OrmOx5WHdIX":"W5Dl0ICMyvRb","qsDXausys7jW":"KV16B7vcMFFB","iAkEZAxWYNiB":"IYnE0W9tuvGx","77p0rtWBETQB":"PkxAkmaShL3J","hcDH4ozl1cYK":"AQ1nLze9x8Kk","1La0nVUaoqGq":"kWMGXJMPas1L","2XNtcxWMd55f":"1Gfahp6VDuLf","SryBWHQGggkX":"zAJHJsU6scXK","nmbT7aoQ5n2K":"BUJLxlUyLNbm","Rhf3Qi3EOi9l":"82BfGaEXZYvY","oUvC6niekdFY":"FEJVpgg8Dcmk","f20n7CmpU4Fp":"p58ZVZrnzERr","r3MOtHtr1kKv":"AJYt7SXmW16Z","EbEsBBJTmqu8":"7YbUfDfQNW3i","SiGbeEXtkbes":"5jELvoJFstW8","kb7zFBTHRNGW":"0M66lbNuAmvF","zP5Tmptsykly":"hHcplhNiLZks","dkBy8442CdHC":"ddI50EetEZtZ","y8WAjpXy1nfk":"6hIWAIVPKT38","U6lCBYk0mxNq":"0Rp96DBtkgSK","U6dLDEWOwfFX":"1sb1xCyyLQQn","lgd60Z9yZGpA":"E17pzHkdgreJ","9sQ2SJ3bdCPH":"qL4K4jaykEVl","5ic1WDKteFHs":"pXqV5iLOymAV","gZBjlt9O2kQl":"SX3cQMRaFN8j","KLVnXKOgFiDI":"j3448YAAM10Q","4xgmQPAcpnD3":"N1KanObu0UKz","cmMgxCB028l9":"ueYmq2Shp8J5","BerAr2FWA26O":"CjZUY1iEFsRB","NG9NsfOMmUJ8":"7nMmHgTY67Vb","nnv12mOfzA9m":"sAWrN4CxTpkW","9rwI5uqcVFKJ":"cN6igCowoB9e","a3ezM9emMAbr":"hdys981lS7Cd","c20JLnJpkqGn":"Sdd8sjWcMaee","zGjF1EO2sdbQ":"0FLBu1fsZ1ao","pncU6IBqljsH":"BHpBy4SMBrw4","Mi7GdvEdZ4Cl":"OBy3txP3FISY","aCACSlpFz9oq":"9SN2aOB5AuL1","BL8djsa3L6YQ":"hoJ9ahZKECpo","Cl6LnN5NFM1Q":"RmnifaBjklwM","IGOwHpF0S8SW":"9NQvTrB6pdkD","2xY4O9tW1ZED":"gdVqdqhdgVuf","mvS378i5vGxp":"Xcn2nIcomR5w","t7H2e1cIQ6zU":"mcDCZg39sWl9","bM6aZWxu5sx7":"POLfSzSG0hzG","NDdkUVRZCLQt":"S6zihWHJIfRy","yXA49YMehwNq":"VTn2Fi9AlVC2","mRRmZgysEK0p":"NCiTOAdjCjJf","4qr1r2yApJ9g":"n1N2jQINm6PX","tOgwFLLFwTc3":"3YLiuj9zxYeM","31i7tLw3a99t":"kJ6Mn5LRzGYA","OgvvBWKCjd8T":"iaFQk8nmCDUy","9vm4KfhpwGPM":"xgY1n1kh80Wj","fuYL3NZ7IJgM":"adN9v4zT5plq","Mo3Exqrh7eCY":"fWhuZ66cfooU","5eOtWcfppNHd":"uowIRKYDoN8X","l4B8G5gUMe9H":"HHj5eJmzvr5M","w951UXweYogU":"itACidDILQQa","HTgMN5ReIbuL":"FwqhGcwkGOry","0C93KEIEw8yR":"14KvDf1M8E3H","LVBsWuNTw5vA":"cpJG2w7lATx2","fzlbtW3HSZ0V":"euqJ8lohoET7","G0C3tlADjnLo":"5Rkfe38qViJK","WgVCaflE3eMS":"qBs5wlJVQ6Ph","BbAVT574bEgY":"s99jFrboqfX9","PWvfRiDtUpS8":"8qN7qVBE9xpH","WDVfk13ACv0u":"9BgVdDZGuret","LGBMBo0ng5QV":"dfY9ZaI7tV6f","U7Z4hB8StM25":"y7rMo0KJxPqR","afDo57juKvC5":"ms84V0qkzOxv","hQKdpBRF5q9e":"pFK3Urw7LvSN","hn7B2fgiNoTx":"FtZeec9OcB9Q","wo0wE7XZg00b":"ConNi2RJhcYm","A2iXEES46c5X":"zgxZm7gE05Ag","IIDPGENsA68x":"gydQCCOZm3MF","tFqSfAmSA6gc":"sJwf8urfHmMd","SSMFVlOVIngg":"oBxWEPFnA2uf","TqMwFygXGUNd":"mBcVYako2xzf","NrwOEaJa0lNS":"ahvEmS39RHsJ","ZMEbvgkLurk5":"srboKSJLOR7D","SiWjbEDuAozd":"eZk7Iy16sFW0","SwevFtqLshtC":"Ub8K01gcQTAB","G6kPXCmi1GtN":"RISo2kiaKXf8","rXP7VCn1PYvE":"nSKEAb8SGg5e","ukhvb6TQXOL0":"g6SkWsapahct","r2YyWwepuTi3":"YTRWGHiXPGhz","IzlWJl2NWYTg":"oIPzVCrdXWQY","3G7lJmnAzjOh":"D8Z7QZI5auPV","wDUcYizI7Ygp":"IR2p7OQ1h3L9","Dcw5H4UQhFFU":"efX2fWYkVZ2u","IFIUiiGeLcPW":"KtaqhHInmrNp","PBXEXyKbBuT5":"6aKsedGayzII","09mnxuY5wTBm":"P9Klzzb6YR9t","xMxDv24qCndr":"CzptFiI4XROV","x1wTLiJx7Nak":"g4tktfisw8id","Jjtn5oPCKbge":"8LV0HlRln7ui","98FxgfPxH0c2":"jgY93NAdAyDj","LFuuW73XCYE4":"Wg2OTIlxEQDu","rXJWXEgv913Y":"YfLOkvj82MMK","sPcvZThUyzoa":"AG7zm31wEvlL","rOVIZEINAzLF":"qaKtPrYnf8z3","Wm4MXYNKa8td":"IRPR3kyxzWd9","PnSxUzPJ7kMq":"y2Yr4cMqPwcZ","XKWBd1s654Is":"RCFbuPQHAbdB","5HksmSUkUs7a":"PUHoit200xbG","8VdZWsd47FCW":"64iy7EYnmYY0","sTb4c41Z0op5":"p8Fw0Jw25Ed5","URz2EJaPAmHd":"5SOHNK96onCq","aAy7cOoTyOkv":"EsKZwfy3955H","XLazC9hc6gwf":"6gme2H236GVH","uJaDf7AlpYM5":"71mJdsV3yx8s","T29KBXQ0rZjL":"tlMvUvyOj0hf","pfNUai5Qh3wP":"XBUSxWdCTK6r","3wK84fOxaqYo":"PpY2EcfecARE","xsEjJQ49KDHT":"GN5ZVGp1Ub0t","jiESx1Iglm7n":"Ca9aPNdCkCZm","Z8cp13VFYz3b":"18OHZLO6PlG7","3nHa3qYqMMY0":"RftCPBndfnaN","rHVSsNX9TpG7":"dW71Qbtic3a8","j6Ww46XEdB1j":"OvvDDYmSGkkr","wuQvwXBcerFy":"FVT5mmtPKoPL","iglHqL9nyYD7":"luyiYZOaUz2q","047q1sVeiNWb":"YWqNx1n30Pig","VZ8F0BjEVr9c":"D77LQevqbkam","HoUpsJ3eSOBS":"Ur1BfvtI3u7D","iqhwDAjgKOxj":"hKAr5SGnsgvQ","8kN4EJxeXSPl":"oeT4odUY3aje","v0CdBKfaZur8":"lHYKyl04jej2","8HoQOYiUDi2q":"OQqjkladr0Kn","jWghpMQAFAYt":"cTF3sbxOEze3","Dq5YnfOJnrHR":"y1BqpNqkP0pX","3ZtzySveI9iE":"aMWor1jNKtqN","eeBOZ6Vzf3ot":"veHkR8YK0L6L","0VZogVSTPdTC":"cuzNU2wPP9XN","fy6nKvUQEdzB":"W2d4d6EPu7Vk","eCKp9Q8QZQUJ":"1CDwwSzh3htc","2wN6ymzhOFM4":"zyoXJD5aWNxS","WkboP7zMMi3H":"OODmmhN6hOih","pR837q3wI9Ho":"SaiDrgNyLuPf","PPZhwOty4sbU":"KXM480zqqtf7","9OhbagkWM6fQ":"Fg02GsHJ1LkA","fcBWdx0VR2Uz":"TlJNmEKvx406","ra7XdgSPpT2b":"AJRPwaxLJBS9","Cqvw1oeo8yBF":"YJlCEtvCsNaz","yMUtsBOE0yB2":"3kxms1xf97cF","hPVpsMZxy7sy":"7oR1xm0fbjWt","A5OvP3fBylsv":"zHo90cOj8r5Y","zQqKevlPsDcH":"cDTcE3zXBqIg","YV9lqEeUlLgq":"ZNnm70JNPphh","PVDXCxblCoXZ":"Fk0KByPtUbto","9l2MjoSmBUyt":"wO6foyzkP4Qv","JWHAeOPGCmzD":"HESYjD72Tx7o","iDABp5p82C8Y":"7abF7TeFqWMs","7UNb83AuYstA":"yZh6Accn3KCC","aXjip2JU4fS2":"rbOKDkuHFpCv","ClXqdIGr1Gjb":"Wq6joqPmXYE3","aBeYdoLUhpvx":"eL8R5AIyIwF4","LenrOExqggQA":"7kSO68Jb0RZk","wxP61M3CPUUM":"vf8xOpfub12u","eaBTmJozmMrC":"hJDIwnUiOanJ","ZN4Pjvktsc4A":"Un2ysswtUzNo","ZsEaUGmG5zq6":"5iYK0eiERUhR","1MNoj8c59ALi":"UX6oeoAtcXMl","uYis4mGaHPdY":"RRoYjkXH9stv","h8pQeuMlpsly":"rz30Xl7bEPSJ","gV2Tg7NkbCJA":"GH37leVH7GSU","kH5KUogCOPPh":"QhVRC1nI5h2G","BDtOtAPn82tQ":"S3w8lpIBkODk","FCA6yoTeSd5d":"eI6L8OG0aLY3","HElMIDlvAe4o":"7HqIaCJmZ8ey","9qTI7d2ZgA9y":"a4eaduc0S68a","rX12okrGJlOC":"SsMPAQIN8D16","DtbNkdoiESyI":"GVJArckd4xX4","30PdJ1F6MIPK":"NfR7VmqpmpuM","erdLaCEh2r6u":"f7uJWMlHzXfm","imsDkNa3Yyf3":"jSGNqrPuvbNd","dzpxjk4ZgT3O":"dgj6t8d1ScNH","uehuoq5yMdRQ":"1oJ6dkSVwANj","CvHgGx4Tqr5p":"6fPXGDXUrXe6","LxnaMUyFIptL":"I9yGLiQIRNZD","IHyb1OKBN9D4":"gEUUu6yRhMHH","yW2irMMdNi9G":"UYTwAdBzMwcl","pc8V0VauAlm7":"Mz0jMyYJIi9L","HR1FdW15DVoN":"6GCW2thi81PR","StAXuWTeRX7k":"8vlhZvGny2W2","vDtB0K3rvIWj":"vmkET11QfkkK","pCKKBn54AG3v":"7SVgDOfODamS","mMBwGdxNTKQ0":"chwf8VTVlVO6","uStb8Jp9lUev":"YKJzbvX3Ewou","VoLcWaGBTwEm":"HS5psIN4s0wj","3BYtBB1XS5sG":"i3kVhSO1xgHW","Q5amfxC07wWG":"jCIfdveRMmMx","12kEHX6P49iL":"YTJqtOnRgeJB","ubcnYrZtdrYa":"lSPpFP5wv60w","rtvnIbgdjNVg":"Ao0k8oXtx2Kc","dRFmRT3ueNe6":"IuWqeeFCYV3Q","5MajUGzJ57sM":"K2My7kDHjkqG","TPy1YOro3e7w":"Qi69wQ5ckfnj","JpFnoJzHKJ4O":"OWlwndIHpgO9","N3sVHInd8sJN":"uFzDZYzcPa4X","ctLwd7JiUrau":"SV7yIgiCBaSr","OQeX2zBUospc":"rUzOKFTytqoX","jPWo9hdJ5U19":"Ej3ieXQXw2FT","6S81tDIWisWg":"hE1kWBDKbRVZ","Swsam5fY560k":"eMWJCvWVX0iS","4pwAAIFWUTcz":"q78xlfFqkxFO","RM3M90Z3PAQs":"X0PffL5ScSh4","vmiLkIIJllum":"QENyPY9cAUrI","Fr3rk1O9FkxL":"mzkiCJbGeMQ8","F7nZYMpspVVd":"61z7QkrryDVk","YNwvHpCHgZGU":"kua9liGmrUbn","WVMlcQKEmp1C":"HgJqHf7FdVA7","kh6SzmRVapQh":"djyYXXJMQNSR","lF2EIRydrfjj":"vK0LYEydjj45","qll1cunTEIpA":"EGIJCiwlUCeb","CJvl9OPivDX3":"Eb4cNdBZYmCB","aiwiRqDsGXcn":"NT2aUdOW26Ou","ZoU7LkGW7sAD":"RFvbmydkajoe","zbZQwBn7WxGD":"4xj7rzYNy5Ey","ARX0jZyyCV5C":"n5zqhr3nFtHB","MZLXRXE1GYm7":"VGiZmZKt5rTU","aHZ7jDOADokf":"NiYMioU5pAEc","BuJ7kw6fA00c":"IXlpFehpdkPs","0ERcgDcUYzvf":"KWKAmtYhoLK2","scdM7PHdtdlc":"2LIVWYnf1DSZ","cIzZlRUMfjcr":"M33FznzHx2o7","6xJZWdfsuomD":"eNSjUZeMab04","F3sjwfPqs9KC":"zdLnPhVNjp5U","Im5tKFTB4Vam":"1aJF3iygtjR3","5Fsi6Kd2bnjq":"uUtvjE1XaQsQ","tSap0OASUcBd":"ZrRUYiWLMV41","g90Xx3qOtb0X":"v9DoA6fJaC8a","kDavn9yY0YzN":"NAuhWctcDbO9","qnjt9G09wT2l":"1x6reAMG8SDa","dmYOFA9YxNhU":"LRaRJhXEAya1","g0UC0Bvk0ziS":"zjr5owIKCxIU","kDdtZmrknIhW":"HCVRxGTN5tXN","A8NsPxRAr0DP":"PtzQo6eQEEgP","CAWniUCwqnMZ":"WxdfHyIJCwHN","ZifoFt4blQXM":"cXFFdlec2oe3","Zo5XixOOGnC0":"d3zPqPmP1B8L","aZcq1KEkHfhx":"zjsoqbCGyfPt","kcmx1zQDrg8c":"5Ib5vetPELjQ","L4bsJxoNvplD":"fGPQ0irhmFv8","F5bfkgJEttla":"XHq1s0XlqRdQ","eigLCOYhHM1x":"kTs2B37NQARU","na8gtktwMNMu":"ntPZ7rapN1RG","fQ6TbwYvIYXM":"uwfsHC3jWPSx","9XkVIZwXxJyP":"60M2ly3gOftA","vAJiPhpDLNwz":"LwnrvyPoS2C4","QCDx3qjABFAi":"b5SrqI4cNhQl","4QUuIIqFEAAE":"LaLpeflEDpOb","kpKW8Kx9uElN":"Q8epi6W4kGYP","lb8itRKbfsHG":"pwK4eFhzwbBY","HTRyuFkgfP0o":"CYJpYMNDLihL","DhirXf9yghUH":"iheNxWen2Ygw","9ukdKY5JxwRh":"NQuZiPUDqZE3","twkjFoujv1zK":"sNI519KQbKqX","3tLh2rOefyY8":"qePn9Rek7u0b","P1XChGbHqCg1":"UzPlzjapXKXf","I6bixYuo52bK":"ncWSibDJTHsK","vi5r3gQbVoNi":"yVyaQ6GVaEqN","XaL9neUnPIxV":"ED5ZqZdwWy76","M2ayBpnNbEcW":"dVEzC3DfN0Bz","DunCUa3bXWzX":"IXO6MzmVIpHt","F5G4v5ILH3L7":"5sSpSy37STg7","l3HnfcegFugt":"WtqZrlSaaZPR","tuuPk07m8igV":"ke3ZkqAmjC3K","RrJVQDL8gmDW":"apoolbVIxhBv","5hZ6NDYLcHqM":"h0vFnDsoEut4","14a2t0FxETLm":"C8urJJWoonAY","jIVi97shlo1T":"5drI0GvDPPoc","xut5DEirqFy2":"OdnMKkkaMOhD","uJPBH5DyGcRL":"SKTJv48RbCUV","1742uJIWmuhH":"QB8E8IgLZNYY","VFbcwmH4FhSX":"pHGilop8PeG4","1zCFk3SQuL5y":"geijAk1hIiOo","oS1j0VHVEeKS":"qBTA3E9zFzzj","y8Nu6oNUm393":"6nhDYMtmGWmU","mSBTr0pTSS0e":"U1l6bvzXGtsI","AZQ7iGV6RFZz":"1dUSIOF4bUrE","VFk1QQYOJW0p":"L2rb0GmkN3tj","8isgDSt7pWgP":"DPjdNOIYoTNP","01luoQs21MNC":"l8QrVVrbazjZ","Ujfah6r3XmQ2":"F4hhaoDoWWis","mXePTxUOkW6h":"bZrx37UKvWMl","uszDAsZ9DTq2":"QAmeq4CrM7S8","Nav4dHWOG2kW":"lU7xXG5xlPck","dSFCVhEhU8c6":"JJBIX4FldpzT","T8xBich16jnh":"vxquhn1HAla8","hS7Ials0Ayaw":"8JE3zOHYjUlI","GSH0qySiNCTs":"g9PGSZP6hO6g","TLaioipjpPmN":"QBXpy0wCDzyC","jPT5x5NQVCjE":"wlzKizKNvgrM","rAGda40UzA1b":"MkuzlS0n3pgg","yvZDlAzY30Zy":"SMcQ4xn6EUcB","qk19y9Oj8J5U":"Jop9pyOaWO7C","9NjRKcF2Cgq6":"yTWgqLaTT0Em","58SuHA70aIBq":"KdGFoYGOY2bX","K0Ehh0vfNeEd":"NDt0wDR6up4x","BpTJVz1CRIH2":"yZcnj0F1Xlka","NDQ1opz1AwRx":"XpZoRkHHpZOQ","EScZNkMBOwmO":"T9v3I1zlrcu8","LE4KnLXXQS7x":"etIctrksHklr","tkA3fNIcdSPG":"uSxIFUr3LqWN","0t9V6Ibb61vi":"Y5KuGXwwakih","ZwIqTSUp7jUe":"onhk1V8pjmdA","oiVKhDq89C9X":"n7SD5bcBesX3","ljoNMAWn5rNO":"mg03yv5q2tgp","nu5amJstR06M":"0HHPvbjTp09y","SivEtqCvwcQs":"8KdeW4Yr7OH0","zK7MPT5IwkJO":"RduJuep9FFbO","xI2HFU6N0bul":"Bvd0w8ygZRTg","PMPuzRoKZxOZ":"avbYK2Pf4NuV","2ujXV7wBwfn0":"kKq2l8E6mA0D","qiSQdKF7kzfw":"6BEpQ7mloiLA","GRtYY24fJj4y":"nDDU1F5rPg1B","DzN7xloRwxeu":"27WfJHsIFhGO","wPnmKkhEdaQu":"oztJJ8z8vf6D","qHhepM290sWk":"MJtcVFWDbAwY","wc3Ekq02lKnw":"ouJiNNKOQmJa","Oi0di9wwKN9L":"chMFMtCOuhzG","Kiuqd5925SGQ":"gfM0m41PwANb","gTni8Yekyawb":"cAIKlGfwC9mK","ixtkhcODBeiM":"VtZDkGHQJEn5","w6MOgDJCIhqb":"qRUXmUhqK1u4","ovZnkZiERkrt":"JlmaknGYeaLw","7O73fNVT54Cy":"Cfas9DomGCG0","fX2Yo99XZkaW":"4DnVayFMhPNh","ftO1LZ5GptSQ":"Lx15IfRDvyjr","qPqgB5JCkpON":"xjRIHqlC9LAE","OhlGPCHfRLtu":"mJgZf1xnoOPe","fxw1k5uPAKvj":"avxGijtMbJjj","KevZYWPmAUQz":"A77PVg0FbykG","PuOCJCo2ulMC":"P5iWoo5keYU0","eas0qFUKz9NO":"yKbHnvJJg3Dc","iiyLlSTCC2ss":"RMUzj2H5gxXu","8w0sWKoVOs9w":"5NeXlR9HrXGP","87yEcXt6btwY":"v0SsDQbc4r4j","DG7kEtNm0T44":"3LobDW4rNoTU","VpLvRritw4UH":"V1KjIR16sDub","k1BzJ5YvDKQU":"Yd730qQVsTBQ","pFi4WPUK0f5Y":"Cnoe1hiumi0m","Mqk7asA0vxOx":"51wPTfIBgRG8","CGrJWgUOqpVD":"vcvoYOriT3Vk","wwgSC9nYhnrg":"OYedSXdpQGEt","Og9zqtAmlpvF":"ZEoW2yl5rZGn","rtrflbqJh3ej":"z3ywYp39koEb","DqGr3ZdKwRye":"tj4M5JfmMZUh","EpQxh65EMBBJ":"umDSTFvqhxr0","CRphqLvkxWPh":"ybe63bW7qLWh","WzyH7Fx34WjW":"6IL4oR4wABW3","O4yx5v8dEZ3d":"J5Y1tngHZ1do","bvzL3VgtZi08":"nu0bSdeqBYPF","VunOq9OUc00y":"BjvXWaYxTGKN","hp0RlQkrcvjK":"NcepjvdURE3e","mxiRCgElupoy":"xhFFjsJe3psU","Wh2Dy2ZWga50":"LgkVoBjFPDzy","gl4ywIZktVYw":"ifgOODxZK6x9","E4voi7QCmJMS":"uPGQqInkGZap","iJWfCGEpWww4":"9XDU6GZ8QOin","EUzl84GH9ixv":"HCLGNYdu8fbl","pYpiXxwGIUZ2":"l4vYVNX4KNCk","PtA9QJQaSKRw":"NEF4B77jmkKT","KISMi4c7qSHH":"JZMWZHYUdc3d","SaDOo0T1PILC":"U9vUKhotuaEI","avNMr4pcvcdp":"99mGtRtDy4A3","sVhy9whBwaWL":"Qmn5eijAte89","KsHBY8geGIbH":"bOZHE4GIirYw","BxMVuFtCNkfD":"6S9zGnsG3N77","uuwwX93cIyvU":"bKKWbPLk0ES7","99VvjTFrBkVT":"iKCo5LxEvOBM","2v3psfQqPt58":"GsAWEg2tVi2G","3lK25u7M14Hi":"C2LB88SeVg6w","gPsEtYJlp4wm":"6nhkKID9O6RV","Uv04Jy2D7q9j":"vDhereJDrijw","ktaJkaPTfWBi":"WvzotYONuZ32","3zJI54zhjSjx":"E1ogzXAdixBA","afYe4eXHBrvn":"KhTTaVw5pyTK","uVIs5KgVgAt9":"pTcIgxdOwdh1","p9cHUTWG11fS":"xnFPY67xjUmv","hVmDkaC8F2HU":"8ywuOvAGtliI","ffLTfCx92NpF":"aBKE9R0KxsH7","p7LyuSx68nU7":"UNKpJhCyB5Si","zeidczd4YyVn":"8006NhcZz4YI","kRBuIXwbOz0p":"qAkRAo00nwk9","iUZwDR0isyn8":"5rWhxewr0Gl2","lO6gPMgBEUR8":"e8LxZ1Z2psdZ","epr9gOKaIepf":"BujqQo0FQwN6","y1bEuMHktkZL":"Ef99QaZyM7oc","PtpfCIkr9G9r":"ILhiSZ9izKLc","82QZB0dcpYhO":"3NsjS682wdJb","RrMzANrCGO3m":"nOlKXJPqSIjb","mqBW18PITt9K":"FVjDdS1o9Qwd","IgnEfw4S7cUi":"qkdkAQdZ7Oc2","eXoPA1t3i7vQ":"Ts0qDNbtrzVP","Am3LIqvSYTLf":"1PdsylniGxLD","yV27c66uSWdr":"1Mr6ySQyoZ0A","vGYzI1aDzzEA":"qmZK7BNdXG7d","U7caOBBe5EmJ":"yHavMFy6R9DJ","qKibkAkExxoh":"kDZFQ12FiqeJ","5dm9fHSsKhxG":"IiNloiG1Jeyv","HI4opzYa5FOh":"b6hC6CWzMkLH","YENoBwT4meYt":"x4G16IKxJZHy","RZrCdlkxmoGp":"FYaBs0z00lOc","JUuWYzH4lxF5":"9Ufotw3AoscO","359fFjkTEahs":"LIBANnVfIrdM","vuXMAVUb3DZX":"e272tQ1m9p9W","9efn2x8tyzEL":"RabLIwFLnDax","dQ66ZUyYtaZQ":"sUAVZdUN3Dg3","Yl2cB52d2GS3":"NvixlG4mdvte","jjuGl3pnhrbW":"KqTgiJxNMLb2","RUsrweLASK9Y":"RIFjE5iVsKsh","q5SemuuhQB6t":"4IdL8OsJSvSv","3XWnxsc7Ellv":"vbITSFqK86bW","hUyS9NfqbjJC":"L4SxlnTiOgo1","6Dw5f5RNwga0":"UN50tw0m7bWg","3tznvs66xlUh":"vBgdPLwK4miN","rkuMqMnuW0gW":"k84nq7WgnE5i","8AAC09x2isrT":"HkBIIif1EGbn","ust313mMjzep":"FQRo9hR7gIPd","63IUYDoTNvh9":"AUFd5fUmHED0","eTsS8Xl6aWq2":"kulbaat4IaGQ","8yzeooMLe9Td":"JXCoOvLAMHoy","QZps2A4ynpiq":"XwS7CW6bZwbj","lAJtuIz2NZ8b":"1BD359VAAobr","XU0KY1pjl0MP":"JGFCAzg4jPOw","mnMO7XZc24xS":"Bs4JLmxUKvma","2wyus9pt455U":"4uL5ifwYAL7T","UkPSIcc60aSn":"cndeKEhJ6DEI","SrNfKRCWcOXq":"LP2ZldJzXZP2","llHd9QD07p4k":"8V23anRUslWR","EfA2tfmV1yuj":"JYcNqV1xUrgP","WrKVXFq6rbET":"4vUePKZyoeNi","8WUEa8gHJhCT":"vOgS4lCvhjp8","kqQBG5PdxvlI":"gT6f3Nt1MWzG","hpK8omR9PYZg":"r1pGAtdb57Hw","7Jtdwx1Ja5CY":"0LpcMGa1wSZ5","OHdEOOvAKwrP":"L6Fht4t3yHhr","KR5sWIy4CYAN":"zDwXyVcStLSp","RzHawoohMxCO":"fQu1MKNyxChN","SZvk4ltZfNYc":"96MrFOOLT11G","Ub8s4qxeSCev":"Q1XWFkJebAUE","tJ7rtjOZdHQC":"8m8U6TveqOi3","U861bjewrHbF":"H9R26QmMiXwD","SKQdeNqFI42u":"t26mZKZCsD8E","jHnbp8FiFNhI":"AIaIs9zdFgtF","ehyht7zOGV3b":"uxI96PuHjMGX","FIcWqbuUfj25":"H8onQ3lurGzZ","V97bXSAyeufO":"ACgEAxIjTnZe","V4zeHZWKAvPS":"wdNQKyFhQnoU","KoOclva4fcu5":"WhFeTXCxRuHD","DfKB0SxznHCS":"QFDhcTZeGRC5","HGJeXsVyxPer":"Wkpy4xr0ciKb","K6ZpCJGId172":"xO6yixT96zXf","QahAX8ObNISS":"l9FXNITRqQHm","5GfaERp2KLld":"2qGoi0gY5on2","CPljXCdHDIG2":"FAOoaOLS559b","8lPlw5Lc7rjm":"YFpUt8WjzXPg","Ui2X8ygwdjZS":"9tMVipxmZVpJ","EkH5oGa4KFOu":"rUSqIk55eawY","Zt2F91gQzKZ0":"Djuc0dmCFq8w","EagaqhTLzyfU":"ftOV6yuP6bTV","hJkA5AQoqbM0":"gRJCQqzWuW0S","nibxtY7PQDte":"kJQEE0vjVakr","QPjjajwhgyrD":"w3kib0qKGkgq","Z0UqzRaGka1S":"L3kjjC11KCFE","P4WYFSio3rWT":"kK71PLuQkwZs","qc3rCOdWOOwg":"sLaFBzbeIxMz","rpoBEM2Nzn8z":"CmHytXqt00hE","xcxzl6y7hfre":"4VGqt3Zo2S6Q","P5oek3aDkMfT":"6RbVf1QrShtO","hTCFbU9BKAGA":"0hn6yQ4OQ6gk","TJaLQNbyH7fv":"w2OwrSUjisDN","QcEnu41hS6pl":"ZFcbTKHXp0mO","qBQTJlQ0GPTX":"6xVzl5sKy8VF","pgQp0NjTLyCU":"BMKvLFRWi8kP","EqAReLuSbbR7":"vsmlmEHCVBMg","4LvkKwTcPj8k":"Av7pI3boIq6A","IdspBeJgtZfE":"1LEts1h830gz","FR9XQHPKptVf":"iTaHO9dxZKew","QEQZtjs9dbUH":"rxt3b3o9JzZQ","k2diwggbykkL":"OwcMdpLJ9QPv","d3sQST2Zv8we":"b3438vL3kfvA","vtcfYceVW1oo":"SARxRVF9Chi7","zegwkjFUnx8l":"JsTM90wkJSLZ","FQak6q23tB4G":"9uZ3egJNflah","BfbvzZ68MHQ4":"LcsRF0AlW49y","Yv5B2LH8lRAm":"qpHhoy6Zd7gn","uaMXuK6N0hdx":"2itRS1RXiPY5","SzIcu14SxUDe":"5qt1yYU8V7Ls","WZytwZ3o3g80":"hEjMRYUenj1D","lI1omTWtlluu":"lXQfP00MzCSd","2Obmg1ckPbBl":"G2z6aymyvvOD","7SCSBy1jJxO5":"pr2xdOsyPc2m","CWlW6CKQ3F8L":"u5DFbhXW4DOK","jg7Fm3VcjcPE":"jynmr7a73na7","0mS51BZ7d7z9":"EDkk8N9AqR0S","TKSMJMeSVoHQ":"h8UjpCbdIRgy","iQfzUYPbjeKg":"TiSh7ILf99dr","jsTlf97Rsrvq":"xpUaAo8wewT7","Bg28VujvENgx":"bVx6LQGTtAFh","aQn0cKfdAnFj":"8x9zg1MCpY83","RUYkMhruJ5aH":"J7MJ20HIXb0n","1jh4TksCyBtW":"BStQzeLRRMhk","ep1mZPTsimYb":"eoqmMTvUPenO","8MQ0pdUfpae6":"h2jxRwuCb2Yd","YsD3oD5f14fK":"EMAmC495kEN9","LqWBYF9LShAk":"VFGveeu02QqG","TuOmO1jlEfi9":"dYn3BtsiDD8u","tBmqYDlwv8ct":"AYbI4pWHmLnf","cSsRoGtlZCGe":"PTYin0PI1C3H","vIiJTr2El4a4":"0Z4joyiIY19k","VoPPHyyKqglH":"pjt0thlOnZUS","UfW1k2fBtcCO":"3gODA6F9cNwY","bCzCtqudBxUy":"vfK3oC91GWAE","USOBbGShGLcL":"BsftYO20oSYV","7HB8xbfJj9w0":"N0UvXZtjXHY1","CuV2gU0li8zj":"K1VlNjwOTSBO","7iGybGEBcO8C":"MWYDndaSw8xY","JGos9xVMC0aw":"Gy6S3uGZQBmE","tXAoeedy6LNu":"hAQMYVH8YrsA","HQx3IIHMyC0Q":"mr7me4FFxHjV","2V0MlStAsz7F":"RRPthx6zP5IG","R2Gri8SRrDPp":"laxOXUqdwbWW","yGvNxIYPiell":"2XWoqKNtgpzH","nPMTthEUcWAA":"o2bwhgOFUJxe","icuX0pyf2yeb":"JnAYansVVbXq","QDwfS7Y2WjP3":"f7nb5w6UymMk","EaTrRgN6HuLM":"s3aTRFWQsJ8K","BQnZf4OFjtKE":"ZLEnAr3v9Q0Q","u1kSohe0Y0iw":"Z1BN0MJ9MjOJ","ajaHA0rtrQsz":"KgPiYEe85Fp0","PCXJn67xTqmF":"yxlKJDWED9Vz","KSYNZYKklNfr":"4ojmER4R4WXy","e5Iw0m9OP6h7":"M3P4IAM7bN1P","1YWzx1RzXLjA":"RnhazPc3Txka","k45RQ9ADqA9f":"ezGslECa9JZl","PeFwiGRZzgv1":"0fXOvs8VyCg1","OcdKKEGD7oba":"Rpk6kVcElpT0","c05TaD8eD3cH":"EJw9mQmbuScO","dGEIcxi78MdY":"I7niaEakXORH","fGPCj3z6jzAB":"euAvfCK4VlKh","REPFUcW1VpHr":"7JA80xqtWjQ6","GuR9BCpxAw3c":"WKvahXycMvgG","aPRQn5QevKUs":"uuvQDSsFTFDT","qNdU9fe4Sikd":"kJY0DXsyyjsA","gzmINjNniyVK":"JGlz2CruYgrL","oFwl1Dy2mPnT":"Rn0GXtVXdvrE","InBx0I2hCGwX":"7DSSEtE59SYe","ti5JhIr4wvu8":"o4XDNvzS3v5t","1hWX2QyYRaZC":"XBiwMuVXWKTV","FGh7gWktvzPF":"YJiOomJqQmXr","Rvh3221qYvxk":"HVmcEXosbfFb","QaeXirQl2bBU":"XZJf0ChepP68","rWoymyfnKBJ6":"dLme4opiGVAr","GcIrdcoVmH0y":"Z6tPeacOassb","zY5HaD3CMpFR":"orcepHMfIa0Y","Duim5QunSXtB":"kSgFr1PvoBHv","1v6luvklRS8t":"vc46SXxHzKfF","bYFkoBSafuw7":"TZkzXMThSXXO","p20E2l33zxV0":"fVNKgbp6GEqE","0Z5B8fi1gD3F":"RIKWL1wSX30h","qSHo4ZZZtYSY":"mnuaodOMCzOu","Jzo7QlCZ1dby":"nVGOlE31I2Tx","7eTPH2ypxcSu":"iC6x3gXVfaLA","2J3NK6JHAeai":"obChIONnvVyh","Q97xkjCirHPy":"iu7B9HvPWLLw","rPG8vs4zJX3g":"8TP66KUMqxLt","JdKoI7C9lSA7":"IiOkWQPmAlvX","GlJRDMBDsCFs":"COGFITwNm4Jn","jeXzdfiFdN8V":"ruod88k8F2eZ","k6knZ78X3ihQ":"7IzynnRKL18k","nFOthcJebvZQ":"vtKpmMY2H4rI","0JXBtguOAn33":"oo66FXMrrJHz","nu3zxgQ0FkJB":"3KfSiRbwsjc7","dug0XJsJ2MMq":"2F3Wt3S3u46P","ba0js9ZYlyMQ":"iPjcU7hoTr3J","Y6fTnMqZfhtG":"eCBINIzTBIzv","nW80ftq6cdC3":"gbESOIQSuaDK","48Yato6MUXJO":"JJSQXgKc4QAf","XgirnHot2t7f":"phnEvRmx7YPJ","Tf4dKYaPyBAr":"MTXeDuB8dOg6","sirHMT4d98m1":"faQEzigViO4n","FzFoErvHjkAx":"S234csDR9uyI","Y3JqJxDtghy4":"2n8dqUBbhwgX","YjPyZPARITBg":"XiNDstKjOcuP","ENQ5D5n9lAJA":"7vPQf02Z9SJS","poKHLQFXTRGr":"CsxpfcA6Sk4k","sb8LLtOwa9Z0":"nIrza8KV1BYg","k9qSRVQcrtQQ":"WbFJGo3Z9Alq","83TTYoJJX740":"CLaQlX839oZU","k8s8930A2ZoH":"hUJnQBIYc8Km","bFsrLPaeeYK4":"jk6f0QkEmmI6","Y5dYEjXA9vy9":"zZxQxkT2TXGQ","rE7GXmAYfRyp":"RZk6JeMlCyXu","LTXB7qErbqCs":"cJjCgLuf6JT9","LCe6X1odBXBg":"91cUQ0vwmM7l","Cr9BUd5YWQai":"y9u0FKHaw6gv","Cg37dst7IDPR":"ja2DUkcGPgqe","IyDd3jryEo05":"fAePZEDSkcTT","kd9vgfWuxOqH":"zihbo1H6ypqz","jdzGwVWm1gdv":"9FrgbX7LShDz","lpKCW9OBI6ZN":"Ofp0DKt6jjLA","aQhng95TOBqD":"iqEnXusum2Hi","qBVBc9gmeGcf":"9Najkogh7a42","HsldibctaVTL":"VXfxc7QWl1z0","8tyTazIXJRXK":"x9CaEuTsjq2d","bgw2IbRSrLwE":"FUAO8QbWyp3r","QWCgQcoVzjp9":"Ki0jizx5hkKn","cDLfiT6dZrNJ":"pdhYA63z9imz","YAS0UqvyYWe8":"saT1E5QX1WQ8","Hne0iPZfENeG":"Nr1RV1UsUV8h","xABXm9WxGuDu":"2b9z9UbU5pfN","nKaoCW6XwwGR":"NFntSQLRlisr","9Nm4Wmbk47Y2":"uUsiZqHSirPT","vrLqdz0kLGAQ":"9VHgc2YMvBim","EPqmQLWGxHix":"ZfaVxKWuxd4s","gpMPfNQUUGzy":"c1jSl4hO15bN","0Rt5vW7BzhnP":"CzoMUfn4PaNd","NTmCBjRfSCxw":"frMXAZBNBIHY","oPicUuHfE1p6":"NUzTJAawnGkI","B6hZBTOSk4iF":"ok8o1PhnvCcB","YbXpzrY7ix0k":"7Rnv3L7xQvcG","JmqC7Xo3FRXU":"MRCvX4JXlVaS","467vF6GKpnXk":"tTqlcvnpM6kv","e9cRfQHhtuha":"f6jkH4kktnPZ","9w0EhbeqUTq1":"YUvmM7yofGPj","QNHbtbTHaAN4":"y7yqa4ztYCY0","G6eRfyIxFLi8":"bnjSKnqr8kmc","ZBNpC39YgXHK":"IR8CyqEEZWTh","kKQItDibhsw4":"4r2y8lR5kePe","hEGl7dR79wTJ":"xQd4jMvK6M7r","PaMjZVMVU7b3":"dlicaTIj2tws","jlL2svdzDAVq":"ZvjWcXMTVGcX","6DKmbfkEyVgI":"EYUCJqnl55xK","ZMx4hDkitE7B":"Zd3hjCjvNxOm","VyplvjpCHilf":"yd0WaMNQaFq8","jyjlFLGJy8cG":"fy2K8DxmTop6","IPc1Ypzcy4tK":"rPiH5n2gJukd","RK6dplAXiHW9":"b9JlhPUajdZQ","2jOuuMvgskMV":"jx7mwyguuJEd","03demXUgWNDd":"fwJPbQ4cGw9D","QFWY4fgcZJ8g":"DtHS6ALy6GxT","732h7SYE5SX6":"ixdTJer8yeYP","D57eKeD6r0Zo":"H02qRK0hazCN","gBkJboKVJQEs":"bGybNrFouiY8","z3JwRMp3dzRL":"l0mcZMoLqybt","fh4r4YzFQnRw":"whZ8icC0I3XI","W9qwZFyvtYtJ":"IERq0ZT87ykr","iygFtRKpApup":"mLMlzprKbaaH","fd3fv8bTiOh0":"4tFpm4gdP9Kn","htOQqBo7R1zI":"FZbqcEFBWMp2","5R3VhwtAtG6n":"Y0CA6V8R8NO6","veSlnJFFMal3":"meQ5qlzuF2lI","9JdytuRowe3s":"OhmH4Td8bR36","o0hDt9VWREOK":"MkC8NWu6NHni","nld8cHU1CLKb":"nHB5GqgdmAme","ynXfstnM5F7u":"igtSPW0wyZLw","Jmr9dfnXoHz6":"79fBJ8aVkFCl","LSRz5NJSBe5G":"8L5MDlwhdJOc","9Cy23FOPwQN4":"aFWWVycQWDDd","pRZhInJV0SG2":"uopqnzi3LxKa","LFjbhgm78kn8":"L5tpvT5u8LI3","alBa0oxBss4P":"s6JXVfl8eZmX","Xh4VwDrGOmno":"43YQwv6vulx0","bIneT1gHKasN":"QOowFetploQ3","kSBjcgsVloto":"RUqpuZXXvcWl","8mdeUeJnjUQX":"p08KN8OTNqjO","1kOHHb2rjwvh":"kU7BBkxXk2yQ","zZDAppAFUfDb":"7pr4QjyVSx8H","E1DBnmNbd0hh":"67eYFE6NiL4T","9RSD914gSrXg":"RMefieDKpO1q","ZUPy9Su0mIaU":"04MLRrxGbpqM","DiV8nfGNQlbY":"yZsyYTlq9JTZ","PPC7xidHQNhv":"ggKDfTEuCWd6","GN79zBYjOwqM":"4EL2Ynbr0LDC","FENEfT8pv27g":"tvwIN1HL2zid","wlTUdy9e86Ff":"UEtVVZnzISZx","pPIeZIQDMEzV":"6TvSibU6XtKJ","bXhXm9z6jMaL":"Cbrrb7qnBnpX","j60g1KbhQ7SU":"kj6ag5JVE3BB","NVsVXFRV9Bqj":"3MIQFrszIb0c","61AdwzPz6X9l":"sjFLuNM0hFne","9hvJZA7lHfxy":"EDrVwIRp3I9d","E8YwQkmAu092":"9IvHyGMLj5fk","nyxn8NQUOxt1":"fXWrvyKTcyPT","h4zyF2DPCz0O":"FVhpMaThrdGR","qJXrO3Y9TEVv":"g4wYZlnO3LM0","TTHaP5CJ1hJj":"Pw47fFTm5lw7","hNDooLCZdrT7":"1kgfeWQBxua6","3lkjvAfDehNj":"PYA4EDk7pSmE","QgRkjwLs44wa":"IedOsxgkiZeu","i151sxrGTe8J":"SQRaNIy6WxvW","Sil5efQVD0PY":"C06Upyyg0rh2","vzqruP3NgjOp":"w2NIkO1yElhE","eHPw8krqYV7q":"VNMKzyl4rv28","0hn1O43Tf6pR":"vtjfy8KTi906","Diihn1PNV2HA":"fFXw7ETDBens","scg9jIorq4mb":"qcszhLIL9NiG","zo506magj3Tq":"TspjOd3Ayk7B","rQ2toNbGARdE":"RMZMoiptNiZa","aSXosC86MiJp":"FKDhUyqMWBzI","blnXp0Hgt2y6":"761hMkYqCpW6","b6cblNSGTRlh":"Ve3qqHFxtnmC","1ahCxdoU279W":"VjV4HNJRB6Zj","yYX9NwGdkTeq":"lXSHqUNSj0iG","Th8bDkXaL4dw":"N4NBk2zA9Yvb","4Hx3RR7X0g3N":"268tZQrHxArG","lhWwOH2SoPDW":"TR3wD1GDQk5I","YdpPcPk9QSop":"0zPTxiqrEoYL","0TIjuINRmwFf":"Q9IkZWiF46I8","cvyiBdeF0Pat":"gIEwnU0PJ9zC","oPc4iet1c5Wb":"ivBgsJbeIF8U","ygFAUtEH82Tb":"Ka3sbZyyq4Xt","BZbvCNTcssHm":"b2TnzUDZEaDK","911ASXdE7ZEy":"9ovDKWr48Fj6","G0ZpIVvhtJvR":"SfEN8Hh7iWSF","UaCAIOuPz6vq":"tUcn7cOQIUWU","Wl7By2gB0Gyk":"Ey1EnGsCBPGX","l61FvbleDXkb":"3ZaHICAJ1oq4","yDyfe79Em4cY":"34npnivS77Od","w1bfVgzRgzOg":"rPIkmdQRUjek","6ybq6nhiWE4i":"D0d6ZmiSqggs","kbmDCWHra7GS":"5RDrq8970bkb","vv9nyglBPLs0":"ZApkDU3JMn1c","ktqDaFlOKYOW":"jIFnBdAkwQqT","wGLaCoEiStKV":"cEh1wlvQTBL8","uB6eodL59t8y":"dE8Ucbva2Siq","GmM46MOEd39f":"LHpqCx73yY0L","VmL5XMZ2pe71":"H6KdpmcPeRIc","5DVvHptYlpfh":"afSWVIo3cXgC","RsFXFS4mxBlR":"pfCr8tVy4y6h","c6SFk2NGtSWM":"hUYfPNAesQF0","f7keYfvxyf0P":"0WkRAWYYWErL","TQAC75Y5jHZU":"wA3AutDfIwrK","NQhOSXhu6IoF":"o7t9jLV6gUqa","rcerHGYs8kg6":"WAvlYJt53Id8","XXk0kQbE26dB":"C7gzlpyRj9k6","828pPKPmy0wt":"AnoIx5ZPb6jq","wVB4244dqOaB":"mGr9wlTMP8sZ","qnWBfIpbWScQ":"gHAaIAPI4hHg","rz2OZdqFzWR7":"gCPSMswTsTS5","NoTbQct28JL6":"YNwm70IFFdDv","eLzOcpt0fAif":"WhelLbK4ornJ","FPFnrmMhWi7q":"JNQNgrbCmnR7","WpxEeBT3mSRK":"coXb71HyTkeS","lD86AZdknKej":"aK7lnMGUI2bR","Agio2xsGjYuG":"DczGnTDG5tsh","TqAL9Ww6B587":"8MaMHre4kFEN","jIew4UA5g1jP":"Ho3Z0tSXbczg","IKi4TBY2aHkl":"bGg6iGfsJZif","lTIDdI5ITdhs":"b8Hj1GKEBGJ9","loHNeUKnhliS":"2nxhAlR4DUKm","OP8lKe16ROuT":"CXy7NhPSfCOM","vdFKuPybux6I":"GNpWgYfeZl1L","J4bDCWJstpKC":"b0X0amYCpUj9","bqnNNwDdsiFW":"rNf0MtyKqn53","qCPoBUwyQi4V":"mRKObee8QKIZ","MjWjyRymyo4h":"1cgFIndGfPva","ratUi1xlNhTo":"W6LEy0D6MAcf","J4Svx3AH5VLc":"vJpQiGUeV6OM","4xk7Ou2mgFoU":"2c8wSoVu9A9d","Wm11wI20RuPI":"pItb0OiKOFu6","bKU5K1Fe5Gq2":"hpKh6T7VfjlK","2TZ31GQoo4HK":"FZYD7PEPYPGB","K5p3R2mFMSpj":"Y1m3cmHdLCnV","LmA0O9awantl":"EFBec3pOTN8x","dJ4Fa9dRf7PK":"aUwDBKQ50R3K","iBJoKMaFp4b1":"YOGc0WgGecfi","JT2j73JPimO4":"Oc2hg3VNFzCS","GKVrTV1H1Ds5":"MS4Qf7rj3A5d","xN24TuAKikap":"WiQn9Ou8SYk6","M4tsaFDWWj04":"QqniOWfbNLqa","XTVHiF6CGzQa":"GYTN2nDYfur2","F1Cj0GSNOeF6":"tD56C4HJ68Rb","OufcQMhNK98a":"XuoIMzE8D4dI","NXKgpKhNXIN1":"UH6pBSa8bKkn","NhYEyxpLLNhh":"ThgmVlJmCxL9","OeLBLzVRVrgv":"dQfU15HAXYMt","VchoIABSIoiD":"hFLRkTpbeDfS","qjBZMmB5KvPj":"E6hPyDRwVPQ5","HtCNBcJkdVgB":"IixPkrAquSAq","1cPypBbhlkvA":"WYW3SHQBpMLN","ojMKVTHA2vlV":"EaXXdCAQnqZI","epUAtwLAWF7a":"dZnSwQUfUbC4","CrsYNff1cmkC":"pVshWpIDOoiX","JH3f1gwfQiFu":"pPxum5j4ngPi","BcSMgnDU1Q88":"Xm16DQ9MUCRD","P9YWOtHHusGd":"SOAYmvygyhtI","S8u0RZxIJg6n":"J3jRRtdqsUvQ","rV6TSpTFCWD0":"wdtwddIhyMVH","oWrbsdUQhuAV":"9BMkdBwUgyT5","fTI6umBLs3H6":"8G6o0OVPNvm3","v4NvnmioHpgC":"FD3ZPAiddY0z","zVcpHRTiO7dP":"Iif1wLOi1SQK","lxMDaUq1T1cy":"1J23246e0STQ","VnIBANBg1C3l":"zyqVqYRj0C8R","gPomiNQsky8g":"886sYVPwWr5g","UyATvto1B6kI":"apYAnGk0tQgp","O4ZwB9MwRIIT":"mNEpBc53FIIa","aCtf4ZeLzwnI":"dGujn4OCxhyv","wiiijHgkHVEl":"GPbrgWfJcxrU","BFyU481CQTVt":"BLj3lzGuChV7","Id3AmUER8kT2":"ZI4IZxuACWez","omgwZ144yTtO":"NZ5o8ZSwPzTm","Pcf5D5P8R3SG":"2FiEzSW1UHxu","aqHJIJr93o4V":"X2UOLSNzDBKh","NFK9k0zkRrjs":"wzQPGJ1ikuPD","bmsIKNfuiM6W":"xpUOoJq0zcWd","1jUfcNAUVumE":"MVUK69cRMkOj","NkwSq65KnCsQ":"oRkHFq2P14uP","UAhscdNZS3AN":"kHtvy4x9P2yl","VVAGX1oZCmJV":"s16tI3WmAqKO","yBHp03ZJFtPF":"7L4FZa6HS7ju","12TCom9kZ8Fd":"wTWy428fX3T3","KjD906HAKIWK":"A8YUCsyjLd9o","kichWvjSWY42":"e2QK5RkwmiJM","W60faisSblHa":"x99wkJn1Yjc6","meOiWsgRzBtA":"ZcsWcPdpSmOy","IHNNuon1sdY9":"JXcH7djQ0a1O","msDIUI6lEOYX":"ep9nvbQlatQD","GbkxVpUaMq7V":"uD3tcMIVWewq","KP86oDqRFqUg":"g5sSarALRknB","yIrdaP680iU7":"qZQVbIcuiM0C","FRnkqgzqOQAL":"ZycI9PIsLoko","CJTzGM62WbYP":"sE3xHrgrdjgz","5UwvuNj933rF":"7Nz6eTuTarL7","HZNsk39r9Trz":"WX0A3HnaYfgO","V181ZQNwaQS9":"ft0dxJw6bnbS","0gUbS23UmXbG":"M3sLQRjzm6g7","59EmK4V3dAWW":"ulkUhoOcQP2F","pSGW5u9OJ4iG":"j8VTtkWRNCD3","EC4Bli2OswDw":"hHG5KS9Bety1","QiokHBrqCDvE":"r7AYSP7nyTkt","BCbCUNkjPQRR":"WP1Lfz3NDknQ","O6kFyPzI3fRA":"os39cLlMHBEf","Lbg2jnBEwhOE":"S5LV069w5iK8","ykUaWCD6wEc5":"w8Cv9yyf7RWq","xzS5QWmVJHW2":"ukKKuqQpLTNJ","qy0UYUzuEAnn":"4WU0R12aweLc","vwWoc4uFGy3W":"SO6kswxRHAam","NWU0yOkoOvCF":"Sq1mvuWBNc2L","VHCcRQ5tL5os":"FcdT3zou86pN","sjYnuy9gnxBl":"Bz4LravxplyX","BWocNP6t4xeS":"X3XvGcAG2c2I","D1dRh07d9vfm":"OvG13xwIiB1N","QJjzh2zjHxEg":"zW1HdohTrS0p","GoeIu5JjZNGt":"iRPGyk7ric1A","fQZD49uWO6Gk":"MtTEHra7zLz2","bCmCUlWYMfy5":"alCtr54A8jk8","KkSMpQD5xPHF":"EpYXwNRiOzNQ","STXRRSHgPOKW":"rzI7nMnH0CkC","NZoL9l8Fagaf":"sp2jGhLLTSxA","LdZMHDqCcfOK":"SaqcC1DFq6Yo","xPNVfq4o8jGx":"ZjHYsXV4W90s","RsOsEDNR1Nf3":"HeErRXA9erGU","5U9y2EjH3d7S":"fGhhxrRzHFht","Aj3ND90Euxbm":"TAzJWXAVL2Kz","P2V5GUOXqB7r":"oDMznloQCcsf","wNhvEAjPQmz3":"OOf8XsnQDfpA","hQgVEYlYODxt":"6xJhxzzwwwhs","CyCPMPh9gskx":"5Li6xzjIQqCU","ueLcCYjh437B":"qSZHJ7zpbLxW","J6T4tyC90Gi1":"PyQ8nbh5Nfhe","C8vPwFc8dkmU":"9PBOApEMZQYn","LqTqe4EqM56Q":"FU1xkSTmishK","TD1X9twYKF5B":"xhxwzANXXEpL","PzN7cG5zo5ML":"zLXPSzVc8f23","qx2Eb4z1LJj3":"ZM8wJvbaZXST","FP8GrNSKexhN":"giGMp4m4GphG","zVCWvdqwaCRR":"3ebwmZgxDJIT","KMUBzgUgn1d6":"Hu3s9vGGsvgZ","FvYe1xfwzOC2":"eEX6JqlTDL8M","zd6flfOf4box":"rF839TBxBDdE","BMqvrPIDDJyF":"x8RLf2UDEvW7","7Axzj2li7SZ3":"Wf9jzOFp9tpZ","P8EQ3uFL1Us9":"OoXnNakpYpCM","gyd3m3AOnFKH":"PEOMCSCOe5LB","9oPzF0bFt59K":"wUETiyMSWxH8","pvrOTehsImTa":"oGfaDiH6xVxD","PTZ40OjMlc8p":"hL59ss5OGkqu","QAC4FmIu57tI":"W9ojAYrzIWRN","NA8grbWknBhR":"PQKLAkJuU8Sx","U5qqVqAYbHP5":"q1gvsRKIlh8I","NPZ2wxqDRJt5":"NTtg9lySrwl7","E8WVBF38C8En":"IzxxQUDZ4OA4","ZxbmbEt7msND":"D7G0wSXdGXSv","vzQ3mnAQzrKB":"oV690WIkicQi","CZhNW0rv2tpQ":"1oS07rkxOLPZ","3ZVBidYCJt83":"pPHkZ32nkfIz","y8B7pkk245FX":"4Ni4sw0aEYTo","QxXkxpM63I7N":"JluW5GEnb8F4","NA4nXTYQjNtr":"Z43aIp4Th4M6","M2wMiC4YP4H0":"8m8AczyAiRAf","TLZtm7DhaWLa":"s0WU5JnRojQh","6z8ewRKgNCQV":"cRCSeDfIm6va","Agk3nRU4Z5I9":"CltpJhpKJzs2","e5GrPF4XxouC":"WMluhBosPOjW","hPKkGnETKDNn":"bHz6TPzsdcW7","xMMwYmQ4HxUB":"5c2bAvlCShL0","7fVpuPVdAI4K":"hALDRr6B1Qi9","Wssi4wlUSjCF":"U4eXoEgAyN8Z","eLn7k0AeDYRu":"72d6s1qotnG9","dlmHJg9L6Vlp":"ue4xdt4Ncbih","uXnvixnN6NL8":"OlNCA05vIFwL","fzMGLQoefv1k":"pdy2O5oOwc7g","5VExWn9yYDZN":"Mb89X02lfxMC","8tN1d7qqIxf1":"2id4m3cWCHrc","Lgs71MmEwdgI":"7eNMyd1HgF8s","pV1eXeO5naFf":"i7sBzOrRg30v","iIME29yHtCHG":"RhOwNd6ABcKk","8367T96ShmpA":"vxS4pl6BuBNu","aStOUTBWlmz9":"cv6qQ9uRQ9yK","eItWXKTNO7lG":"1tBkoQvL1T70","dmJrkOMdNgIb":"eyDAQgyHThnt","7YQUU7fdMIJx":"UjdZnszA86Y3","sro7RGhB48cM":"8Lxdik7nZYPA","f2UOhnEsaRVn":"TKl8GWK3MKNW","gCXJ9C1wGwnV":"nyts9oFqrpxt","CkiiZN5ryXnz":"psaDjLmQBPyY","fHl4C1u9GMU3":"bFtKUVmy0w0C","1qX3SqQ95d7J":"gM27jHccoNO4","fzgmigL2j6qi":"c3N7CvG2EhI5","ESMyawHzimsY":"PdLjvQ5125m3","BrtpvY6bsSuZ":"eSZx4CDoOKrW","OHDhJuHDbNir":"RU4zWgitOZWi","Xb0apEY3ySye":"Q16A2FauFC5U","gmyOpjqpTgWK":"BvJnQN0Tklao","k3zNuSOs6G5t":"WsLNSef6pcCS","K5GQ8XJLOVJC":"tVYG0LaS0M6r","gHa2ZSzZfdMy":"0Nt8jrZSTnM6","S1cNBxfwTXi3":"iDteUPi8fXFz","1jeJBeJnyOUe":"MGY5SPpKLijs","29gRG9PyidBA":"FWpsXvG9VbMj","MjVm7rRhjgup":"nRtfbxKbQe5C","K5w8pf02vXc6":"SLt7JlOlAkAP","1YuHgv5PF3n0":"bR8HOsyFSxs3","4aWm3yOm4ZmW":"56sm4ea3DCCs","66SNClVkDPPy":"0EMxiQxdO9Kv","hecdVXKZpmOU":"4TMg3c4baFis","8q8klmwZ2YIL":"welxntSC5uNQ","XsefY8l6GATp":"v61uRbUWCmcn","GPrIHkHjj6CQ":"zL4SnfQMuUqx","oUfwK07BADUT":"A7KqF3Zuc0iy","8PMU4YcK7POV":"fcDzeUy4P7PT","wM3owkpk5ruL":"YICKzM6cVnRU","TibVOsnjvyN5":"UFzuAz3cCyeq","80Pf9SR2LjV8":"Y7b94ALr3RMQ","TqL9oUDGas7K":"xR9ETNrRhE6V","vm7uqNU7FgtC":"rVnED6pPkLP2","4kZlz2YzP2rq":"LHNnKE2P5rwx","Fu6otGALDvxv":"UFMezvG3bBOF","oWLzfZx0HJJO":"zzhNfFvGiAWf","SvKvRe5uI4yf":"qPOs8h87h1Li","YdUGLv6qMthM":"CHYDq9kfN9UP","ztB0SZPAHToj":"R9Pezi7Zo0em","sM6Re7o2i1rS":"wsMiqvfQm0Xe","AKB5uNLb8c1X":"phMTMSJwzW2Q","9RAFNM2eGf1z":"KVnjdE6Ny8FE","zyQdhZPRijz6":"otWJY7xRsPQs","K6sR5vn4wBv6":"9LH2NsGrX3dh","gaXEFQ6fvNwK":"srdR1OqOGHht","Ll9h0hSUczFT":"UYUD8hC2luLi","1rOvm8MnWUC8":"SBI2veUxUfrQ","HqgkPN67Urcg":"hUvU7GF06lkk","u6IP2pjvd90M":"pQglV8AMQ2ev","tjtmABYul8Jc":"11NrTNxY6aPm","GyqtG2c1HHND":"KX3GhxmPmMQC","eMQguckCOVhe":"K3je0XUn5FTF","7nCSznbmbb0B":"M05vs1PaGYwL","BTq6NUn2TT1r":"E4zQVXFypjyy","BDAwDKQwY3d2":"lYVGSztt7g7I","EXcFK4KlpumK":"1yHAWTHfKLPZ","vUQEFt21mSXi":"PwDQaPZf7CFU","rEiphUGRYq87":"9k4ZhmaZfXf4","B5zhbhbLXU17":"4F8FUsAGKFvD","wJUGwdhBH7iF":"i1WXJ3XIljEa","BXunRVZEpYel":"KrmwZYWYUVXf","S36j6z1tQivo":"VELjOdfUtXTv","Dw4NNHIzQffq":"HlTDJVG6pCxx","hHKgedBqc9oh":"zVWYj82Rpgad","WamMIefk3wDu":"HiOt4WITrgoJ","Ru4CEtGY4o5v":"OHCDBS7ZGqAQ","3FBntZw6zXls":"TH5UqWcMx24X","jVift0rlS3DN":"OWhZYV4VToUs","jm4iyvrqsQXz":"LIdxjJpqcaPD","v64GIZHTCHR7":"pvjq8ObRGPg1","xSKGr9rQWY5S":"bcVrkpvOIElL","MTLPCdslDH5g":"aB8MXxZlqecp","ea8K8IrGxS2x":"riq6viRqDKmv","o0v48ef4kvzh":"tQC0lpAG2DAi","4OAR36VXwNVU":"om6l35GKs75e","7MAZwk9ykx2U":"xiTqptFYosVV","sEjiABkPB3w3":"kfIMg9Vlro2E","fYrJ1OGipEJZ":"Ch1ZyBsZZ6LK","7TnAOzHJQhDy":"4ljaxtTaQJ6X","en7bFTfak5Mj":"u7o4hbHNWuvC","G4L7Zwz6zKAY":"RQU4FR5jnE2b","mY2yGyLUlEDA":"45csLbyrUJ2v","JYiuTGPX6oQv":"HXIxGR46RO4b","Uwm4HqhHJzWQ":"zHQXIyfzOrGy","y4V7I9XhcNo1":"6AE6KzhSp9DU","1VqRZJfcQHN7":"7UUsLlRotw8a","na4L92veCqXp":"I6isBvEjCKOc","3wgOpFHUVUoZ":"EiCnyV2uABKv","heJ41OVc4IaZ":"c9Qrsx9z27m5","BWDp9sl6ulWZ":"6ppbii7nPiNK","3KEJP7Ub3pMs":"0fpJ6PQKTm6S","XVU6qJ7dcKh9":"LDwvbjLc0EWq","5Dpwtwx7u4iM":"mZT18xnAvvH2","AWrvptGgj0Ln":"yb74osbt0uER","EGsd2iHjE7hl":"Y4Eal2oTMDaQ","zamfM9qTn8LJ":"P0DhC8V4uym0","T9EgglpC6QQG":"YDJtzB40cjoj","i74kaPTd98HY":"Z2Z4WvuNrqZN","x2k5qQXwhsNM":"XZbMczDNWPhV","1U2sl69bw5yg":"KuwNX1LvUxla","TcwBk6EpxpI6":"fmZYUpdkHwGW","JqIBVLOrwWty":"rNpF7EI0JA8K","suV6wvL27He9":"vxtlmYv7j3lu","SVhxvbJULJCw":"5yd32KmiGDGK","x7vhh5Ug6ddo":"KIOMbiczcCBa","oB5mhwuY77hN":"zV9clhz8nu5z","H4XSXfpSDAK7":"YUw1fczeGYQb","hW6IZEghK8rb":"LCAnKFvNoERm","oPcWt77Umide":"t8ELxcBwNZ6n","YFbcKEWsEv7e":"0CSS9SktSGXO","HnyGnUEnBIUR":"cVKYXlCzCoYc","ZvsIKqzaeKDj":"ZITyPQfzfYFE","wxZ4SwPcBsMr":"tPjf14knObVu","cdB2aW0ulfEb":"A0rI8NH9b46K","lkgMt0cuLvCp":"nt9UwJukarqx","uUvy5B3SaAto":"oYzoVFPNR4ZE","aOjiVHvu10hN":"a1GFcTTPyzGm","499vZ5ay22E2":"ZKaVh0YUbZ85","qFahoDzhaNup":"NdN0Cv5651tC","FyvFZDUvCVOw":"bEih7gWIWe1n","dH09JK82vX33":"LJxVaHPAlGDR","FYrt7P213Oc4":"htSfJ4e3WIHn","SYfmcd63XGul":"9llnbwcmOd1r","zypcwtY2gdcN":"jJE0EXSiQj8D","hOskM520weQB":"kPFJ0RQmwfbo","TKfL1RIhtAVL":"kESq8vIExJMq","CJkPm1gKyIlq":"xKGMlAXZKCUw","EqiSTdeUAxtI":"1QoVmsrTrLbL","YKqg0z9yASLY":"EtzhypZTVLEb","xaols3OcevCd":"OiAQIsF9KSAx","xME1QfIkgBo1":"bcAqfDccc8wg","q4rlrA7QvI5J":"XARTO2Zvd5GK","Z1QIo2dOaMFl":"8sJt8ce9qwMo","5Hzl36mCkKci":"mcnJwRbSinCf","RFdpw3yZhAgL":"1vupvJiK6EYx","rWbZWHzGh5DN":"lfJcdXCXHZ5m","J7KlxXigWSOz":"4dsI9mGxpQXo","GEwEVNMuT7KZ":"ogcrj5xkr7B4","D5GZfsEHkgmz":"fCmdC2hwM1Yv","UMKPc6zF2RCG":"tUv2cCi46A9q","wYo7h1N8AXP6":"YQL8MfFpc9dV","YCWpFjWVxL28":"VwzPPsocDUhF","4Javvd2jcEm1":"tZB4zg97nsnQ","y9O1EzUCy2NG":"yRQ5FesTh4Z6","TmXcR4qtC8O9":"PGgOx0JBau6p","HdKlEQ5Rbt6i":"EEF9F41teNrj","gxckren2r8UY":"cojwXVQ6oc5f","CZ5apCiHEpCP":"luQB2BBTN5Pv","TXPRlzqwnFWN":"HlqC2s5CMHOU","mdivJ3cniKvx":"eVAMiq02Lwus","pY9lSwrqMUUS":"gSBqbShoJdso","C7wHpWgDHDqj":"nqvB0QhgshYU","UIp5XPYjbwyp":"ZYIf19bKz0Sf","VxUzLzHgFrRT":"ztNVYY5fegc5","xNNzsLQLYZfj":"xXqManHYEAFB","GhgxnBNQEz0T":"ZxRjkBgBNbQO","CzfWsn5BcKB6":"j09fBBtIymOX","oBPuXVA7W29L":"NB6VsvI8LHSq","KdL3r4IUZoaI":"NWjaIple8Vsy","FPOkDOGKUxzs":"TiPvmuRAP2am","1qk6XyAMIs1M":"2eqNsHN2xJFO","fIrFC9MlIthK":"eykK2fIodHdp","oVIbXWJFD0wt":"EmXGFe5rslUn","Nd3S5Jku8ys3":"9pTjfC231ENB","VfcNVk63YUOU":"HHNCgyEAnE4U","isI8lLfqRbQO":"FsvHSgvELuJH","x48U9pM1w8FZ":"ZypvPbiGhXzj","fa6ttxeZmb35":"5uW0M0kuv1Sq","8YxzVvK9Fo0b":"IeR032iLfv5G","bHSQaSNTwT9i":"hrLPwnAHTyPN","O0VfpPR37rvz":"5gSffaEjUzhE","47vFAJnLG8aM":"DrxwmtsRLf7W","yJ8bin4uZ9qM":"XvJasncIMp6E","qQdKQ10JTnVM":"Pht5IqNt6xEa","UUGacVsoUntQ":"9g8IWVAbAE9N","f8yqu9Y1b5fO":"EEyHOqJRJO41","cZ75qUYHbVdK":"oTaCnGRZKAta","93d4FhAFA7Ha":"s1pKYAMGTin4","F2yrf4ossFYz":"ggYSsqcOVonk","ViwiFxCdX2i7":"MRGPN7ucsZat","s3mbw8V37YXK":"LGKa9SiaLjSJ","MRSKSPzp7nzT":"bYC9jgnG44Ud","KeuINnOkq1rc":"Lh4CxK4yYWCv","hPmXF5A2JguG":"Z0cTbcw0cTPA","HN5Zptb8mTxO":"sIsllm6FYXJd","39Hr7KnGW9QC":"w0big9PXZb5F","nSapzGPZFhon":"Mg4rnw5PG2Yb","kTSxfEXhUZGG":"JDJwrZxHR9Ti","1JD7K8LNDGzy":"gSOGWPYvzOeN","UZLRIbOnHrlE":"ASb8cFF6ZUt1","KF8sE8IGmguW":"pkGUJzhrOiFk","LCR5WN7TXTJQ":"ScJRszjCLT9S","6yqjsl9uUXQb":"uYULvHI9B19M","LWfOLepZYOFt":"enAfQlGxqeIa","ej8rnUYfiEAE":"Lz7T2s5qpFkH","rGOx6E53pYkR":"Ybxim2we1hAw","Lu64i0Jc05QW":"mFOh9vwh7mjC","fnqR82cbD0a9":"RxF7SANTl1iL","JmIwYHLRoPBb":"QvCwADNaKckh","22zNpMmctUXr":"aaWPiPs08vnO","EkhUTqPWWPTW":"qYMsE4RV3G2W","gZ0ZiHns1XpN":"HIJS932CI0kh","dCvwMCusGQL7":"TFE9era21ru1","w1H3N4Ps52bs":"XZdYgXyhZciA","lZqzcs7M1rVX":"1lCPtzxEBAIc","5FIVZMlVh3jE":"WMF39iT6nlkS","Whv8KFaMnF4Q":"SL2H9iSw1SOC","Q7RfiY29DJvR":"eh7bVnLkqpct","ntZoK4mX8IZL":"1cRLpIFekjrM","Gah4nzDTdi5d":"WTjC4PJq61U0","owQNGR7bLVHy":"uxmlGI4ifcMp","pzVk2E1q0Uk5":"chjzCfpe8W7z","H6z3o03tol6i":"GUjl7PoZkUqK","PR1NSEcT1vIb":"hn0B7jpgShjS","PgNK6T4B5r4Y":"8F9v4Ytxnjyg","Jw3Nw7Q5lqoB":"oiXpx5dHpMIl","L6hvhcNy1UUs":"z4hyJjKcYMKi","k83lCYdH4VyJ":"gmtEyQuKdOHZ","awPz5mUapwEg":"obAZa1Ziwjb5","pC3wdzJfMEt2":"aXqq43J8QllJ","mamiSQ77CioW":"FC1MGDuxeX9C","G7muIwxAwqgg":"9PxEeZpHW0T2","uYeXtzvVb17f":"c97U5Cv6iDhm","RlEIeSMwpi04":"h2NwJs9wAMiR","uNS2j7fTwTOl":"OOSJDx2kAGKM","p9ucGLWu3jH7":"Ev9H6cb842z2","fQKfT2now0rY":"ZfQrV4cs9UXK","nZmHZs9vZtdw":"cKOp4S7xOEyJ","4CMpoABa3vzV":"UO9uycRe0Mph","t55i4LVr6axm":"d9br1yANujW7","iGvi0h0PMpYr":"579g9aWOiDnH","6DXpMp9zpD5O":"EBxToLKEYNUR","uZcMs2g8EeFb":"QfA5xtcmxWlJ","uoKqZrbeCyBR":"C8JMW3VFl1xl","pNXbnNjh74Gn":"k5IH4QEMLj3n","aqgnQSGWjd72":"0fZRNaOWSITa","p0UFrkeLRalk":"Py5vXG0cTVuB","kgR6caL6Lr3V":"2reX1UtitD0m","hdfowZrquWEZ":"apXq7xQ5bJaj","0QBPm5ShDsvU":"fzbv0MYLggXH","GqkSHK0aQdjk":"3BxUOY8p1ZTB","bGAAi30TuU4e":"jWK6e054eO0x","lHK5h0kXgP4J":"iXh69gh6wFYU","x8d6aEbZQKim":"CcPjIuJzAe0v","sd1kk73vGNW7":"9tSXAnwYuKw0","D1n1rvkCoR2o":"zW23MOkqUoU5","GXz0BgwzkuCR":"CNkFPkCVe6Iv","WJE8GbTNQ5Hj":"W8J1B6wIJ7bi","VUpLldGpzgTs":"j4IUVvDy4VZg","jAPww18cjeTB":"fl7sN2UgCgkY","XQD8980UbJmQ":"4O8tyalger9i","t3FX0gH3yksq":"nkat55qdADh3","LiOPbcmxu27L":"YDpHl8he1KRy","OKltjCSkLRpA":"STYVw8Gr4MGO","finF7zBwbClv":"JjJ1kfnx7jU7","njGj5vgKTSs7":"FFKcXUQWo8is","Sc0zoAiTH40J":"pf9aIAe8amY6","LOktiYsuGUnY":"F4lSwcjtA25M","0iKnbvJlNSzX":"hBNdt7yixBVR","q1T5JxWiYqmh":"oh0eTCOJEwGS","4RyR22zlcIRt":"cHKhGufRGPM3","5KROfUsNd1BK":"jRf8NzuNakmJ","sSlnMLdG4qFH":"T6J40hbHtU34","4jQRlPG1Qws0":"nxGnf7sJZarg","dfyTFqpaoqX0":"6YExT5oH05b5","hzC80FWZK7z1":"kylcK35t5mm9","FmTcTtLTI0zQ":"q3Kqp9Nh88ad","JKjcVv9KmZGZ":"rPQFT2w1HrHr","ooomfAR85FSX":"MZ0DoP9WBYsO","rIIINRtywRp6":"vvRSCt81YXvn","fEjK5NhRlaQd":"J1g7jOS6LRmk","wzMXktm4af13":"zOdMTGp2elnZ","q5uRBagDCtMe":"HlJt52kmic03","b4yBWoQBFSh1":"abX6SZ9KN1Rp","R8b2kIBfbq7J":"EcJE6S7V0s88","AYEL7gVcgJHN":"Bf6idCaFzted","lnRkTwWMusbe":"BPHftrM9gIac","VTKeELdpBT8N":"cFmLiIGxYCEf","M1MHuxnOFsYh":"xnrEwyQPRN2C","RqAXIEWUtj6u":"4NCBV8nkM5Do","UenvbtULqtoj":"GQbbhtjJnIYW","D6pTNvhJ4s6C":"jao3uSLlSqoY","NHTUduKzqIbv":"8vX3UShaAdjs","z46wT9oivhip":"0y1ETzcRfmoI","AFRqIYhDCWko":"qKPkhUyqq76n","z1cJzZyGbzcw":"KXb82EgnBDj2","NhvLSzyMmphA":"uPYCdXphWnQ1","ZZh4kSHUvjWu":"bdH67Awskqbg","ABk9awcCI250":"2VBBVvUylTFR","0kHYSzai7GQv":"VqQ3wB361aFN","VMfFXYyRiPHv":"3scunHvhEcZY","bdbfSVqUkELB":"fumsZzZhVZZg","AFIooME335NM":"zxgeE3Mm0hpD","F1SuNAVmZPPU":"5nzIXuh064cz","3sgvgGgbtWry":"uqRHrD9LaXcM","eJFB3PPUbzUu":"O5B3XOioWluP","u5L5Hu1RSBF7":"jESjpkSE1EtR","Y8VMqZ3E6aTW":"iGHTxOxmHy3L","w9zwaShMRBXu":"GKx4WSaZreLj","v6Psfy1dQQpM":"QIavcG241hFL","T7FKUbEnlHzq":"XTHgH1s3Mpq1","SwIQrF0iQHqQ":"qyKBUQDh1w0L","vlOmVVjyQPN4":"GJOi9xgLo8eW","aDUonDvbs5V5":"kXVMtHgHDzYx","OAugDnVPgAE3":"CMpX1OQcfUxx","jSFJ2swZB4cl":"SZSNVQTf3DcR","1CdoELohEn38":"OIbMjLQ5e1sm","pq6IZ1PonFz7":"Qo6lPDXXRpop","uVyFAeosxGDV":"TyjfRnF19it1","WAf8EOL6mupQ":"jU6CYkrKFBRE","BBeTCUOaG8A8":"AL4Msfs6jmzh","90UnGmfB47jy":"rao1uDpqdZFm","q9PnSgRv6kHT":"JhPPlzW9lfLi","KZCWC1IrKwal":"XBIIif3mN3sI","dnwe8UYaWvvj":"M1uvFb4aLdwj","b89WPym2gkkU":"BTzKmaVkWLTU","lZlpUV0agZfP":"CnW8wtZHPVwD","cUCgZIdowfLw":"s2FUghI3cV4z","aXU7i8LnDrQI":"LzE7DgDGqstw","fo1XpnY4Ff8v":"z8KYvpb6rRW1","GDFIC9FcYIaH":"hQzhaTK5qQg5","YV4SJg2hakUY":"3km6fn2vnX7F","RqYwRMIgxof3":"uElHf9YUkdwb","fb1vjr6shWav":"dwSUAfEQSlzF","zArWphG6GVKI":"l8E1xAd1wiNG","5oTIhfsdUypM":"xbQsGQ2GgmZ9","W7f0HfhfprG3":"l9T5Yrg76qZX","BoMbWTxII0Vz":"mXPwkxcD9t9M","IwrGonLDRu0g":"sqrTehqSUgGs","MJSznrUN7wK5":"fvM84Ny9T0kv","l4ftmAFhhagX":"9iXUepXTrPoa","2ppEiVEqoQqH":"Z7dLNaIXt2ni","VijmdtLdt7YM":"KvFCZIKifrBr","nXni5fQ3v6UV":"t72jeL8tkhtG","0ODfBBfluYJf":"FSd7PFSkAlQJ","Nps9spC0A8qE":"DhXhUbyvpRU9","LWccjHVumdbL":"hz53kL6UDiC8","UfWUqRWPe2ix":"M2fXyRHAZmfa","DMd96MTTpELE":"VV9gCTq31M5p","TBR3FH79wkhL":"r9fEDI0g9KXn","90AsBwVn9cUd":"DZOdhxjRieWe","S0vG0oVx8w3E":"oKRzFbly4fYg","1JymRSTE4wOW":"HAfQhJIts3j8","Ima5ZKSvFNLg":"D9sEfnEGxXK0","mqSFCjyU2Wnp":"DJMf8tnW05Vq","L8CUf3t6pK7B":"7yr46OPW0X9E","aVMqGWZx06JP":"b5JweTGyt634","w9oOQSWXPFEK":"LXUX82A87CQD","mooZbWWvj164":"17ga6evJg76a","hCdmdJCm25u3":"EcvHxDYGFg6H","n2eUlURVTz0Z":"DvGriViaDLoK","2WirFOuUvsTb":"LW6b6H1WxTWh","ZM0kJoXOaeMp":"gAbafpAqBKlB","Ou9DHin1xpba":"dBUXDU1tWAkH","Y0kyj53qm0ts":"spPHDfNG2386","Z1RR0dNWNV2D":"zlsSfK4Lmg1S","yVaXDHv1idnV":"oux0hRqsLcBJ","9KvzHizJd97R":"p0MtkL5lXyu2","75L8SeLEQLiU":"RhnJRubwQ9UH","CbrErevynCmi":"j7vLuU9Jfq3V","vIEpg2ZYa0Sq":"U0aLmnheD2UJ","HNR0vO2X2mCQ":"AtDfdj5H00Sj","WAN5fHx3v68e":"Kq0eCFgpfJLR","EX0AEBB1yTbm":"DnbYu88LMW9e","DrXkifPhPY6N":"vM12rKWUac4D","Vdaxc6OlrWFb":"sqaXtzsnsBG2","azpAS5XZX5by":"YKLj5skJ6lIK","oHXe6CbklfOQ":"PG7jkxLhoKGj","6weQnBk6TA10":"tPr4OTANoelA","H7553bVKFJgG":"9Oymbw4ixJts","E6T6mr10NTD2":"Kam2OyfbRGlR","aS3l1VrmM7wV":"WwAAk9pOd6w2","V6GxAFqka2GU":"auoRMIM8fhQD","5vaiLPgKeABz":"6eIQ8vxDaI94","8QyyEBqKAMbC":"M4wMduL2nWET","zP7HgjE1cXr1":"8bXKMGehNfJy","DmpKQ5TNsPur":"FdZm08IXnpU1","WdALe8o3o4CJ":"jctdKwgpCKEj","VcMh8l1Qu9m6":"OQwZ8ks5kMmw","xiKhhGqoaoV4":"tvpt1aCnSGE2","5ONoLsDlmxAM":"PTPg1yzgC8Xc","sEVFyJmptlWC":"FBhUv28ZsS1g","EX5J534IUif7":"1urGb5LC2qbb","02DyZUz9Pf7C":"vGeauyQVb6oS","FhpBxF9w2kzw":"hhKW5bcM2CeA","tdKOLrqM3fgV":"EQ4HJOwz8rmA","sT2sasrwHLU2":"a2swSPgsvWpp","KUjnnd9y9mQT":"mz7c1DUpoOeh","8B8nPtGfQ9Ub":"8k55rwwB0QDr","asSrbsHZ5WS5":"qQPEye8UmtxK","zxR5BNurUkg1":"fgS3hCWgycLO","Dk3u8PPAd71G":"W9OgUlrlHDKU","P0EmdwnxOrjm":"9TXZkFiI0KTu","ArlXrBZfUxUP":"tmvs6oSKXDlm","5i3Owp8YRodD":"LbCcU0gPfafi","5oKKDOiVma6j":"43K46G0W7BcI","lQwZdTusnL7r":"FpIoq4RRExnz","a9GuSyabvp41":"uNPBmbOFLW28","MM6c754C3nAa":"ewizTRYcpNLt","EP0p9Bgbzq9T":"XBAbC1UlJNM6","SpCUXtHu8XpD":"S5NpHnSObO9K","AkpBGKloIv3Z":"e1WgbsdseAsL","kVDYLaDq5eaY":"1p0t4dSLQCem","kSWfv0s0fCxq":"wPdENZCf02rp","Y1qxsFoyNxc6":"DMWSxMg3KtQ4","ySWUjW3gf7Ni":"MKjSacJTkmv7","OtvkZ9ll4CgK":"LDq1GMDZ1Eve","odc2qHPmUdAH":"GUk62TpAHgtw","cnUAiWkEPMV6":"ibuYmee6DNZo","e6ajSR0YmY8n":"ba5JjHZF44qh","b8kDQM6M1g9g":"qy256zb8MAOe","nrajR2tWJUdE":"f4TbFFKV39KB","bgPszfUVxZUb":"VljGFDuEsIl8","Ya9nURDGU8Ij":"KelN5XtbpW1x","DhXoKij0vA2c":"8yCRq3U3AGXU","cxw552XpUjoq":"nzZMBVpG7WwH","GacWkUr27q3D":"eot7kebZyLUX","MPvHNLgfu8kB":"yebmSYhBTSDW","fsx1kxY2fwRs":"bV3nn3oe7RaW","NnPS9QS7VMl3":"YND0ByEN3WUL","GGrcdayNYLKC":"4ZIbHsLP6U1M","aA8QWmLifSr6":"MQUp7r6p0nQZ","cQmYDDPzSzwu":"O1ErYusY1hsK","JmXdVa7wT4wf":"gVZW0ZJsbWAJ","IoydsSPI9HCR":"QScsGTtLvHrz","n0QoGHA2kFuB":"KWpp92M0SuPR","AHEXe85twE0l":"eN4373bKvvDp","F6TY51L5ROMn":"o1DdTZ3GpMh7","VQBRYhJZPJKn":"hi0CMvSCksTc","8igKJcfzhRfV":"yS2WhJQFTouN","29RIl9gVEztp":"na3FRrCK2V40","wc4X6Z09JFet":"g6hpCWDmdLg4","yCo80zkKaPKr":"xCHu0qROApVa","SOW4vsVCtB4i":"p8yrhph0ZFwY","G2fwmRPuRsAo":"PRXw6NwrOmtU","z3NUtuui74xV":"sZZA7JAV6Vs1","AwNrB8vLEwCm":"5lTrtK2Zlqd7","0IpRovKt0yDW":"wtlu62OT5cW8","3yAwRadOS0jX":"YE9XsroYDcoC","BblsUTFqlEgY":"oYiu1pV0jzaZ","Pqqv1Xk5B2xd":"ZPmoEsMlBUED","YaJ138hSaB2s":"b16AiB80ytUH","E4oxvgwzDfSl":"MawQ7AHgEOs0","yjw31NP5ZkNC":"3CvEpZ43XHxY","FRT87WykJfi1":"NeEBiaDIJdpK","novY5saXoTb2":"sxcf0dZMglVa","C5VVVm2Wlwmm":"wH1y72a9foWL","CgTEFts55oAQ":"UusWEIKLMXdO","pBlsdhFKw33w":"bHio6vuM0H6h","vk7iPBalQPkf":"zdm6xZFU6H7O","uVcjjkYAVv4q":"2Bg48iquiDy3","fe3L6m8apyza":"vau1LdNLrWAe","AUAag6T54ORW":"Gp53BcxyppNC","hHc2KQWfG4FA":"vMbgxzZ7M7Jh","aTba8O44HDfN":"zMrIgCMgYne8","vM8LaqFjAVky":"y54z8lNT3NLM","3oLxTFYh88VD":"RXmKwGqFXZ35","wK6gpxcH5piJ":"Gfmf6J13UE1L","o2iVaWCTxga6":"QUeGpSKSk00I","qQlpFbzZxEYq":"2fsJ3miqRd8T","r5J3IuRkg3tl":"MMJVIusg7Tp3","HD6cI7D9DcU5":"pgPSg0h6ORfq","xgrV05UhwlIV":"df1DKwI6eF20","hQikvLGhhELS":"KcbR9feNETaM","GL7vGa5qjS8z":"NSOANEFAVQRS","TYCyk9p7OwPg":"xXaAdpWvf6dL","uTOnGraJHZs8":"cCijHhjBhBOd","YFUkG8x55Ueh":"DxuXgHZxKzz5","TY5BkJLuGW8G":"agRVdYCr1lMt","r25KhQpPyyuR":"nRz1rxvrHG7k","wpWUYVZiFMpq":"3mFgN9ety0Th","paVMLUxVIzQc":"CYIiKtVm5vdL","MaigqDpO5ggt":"VAckE3XVgYMv","Ri1wH23vldBN":"qO5ld5aziJNl","7cW9RFVW0ZmT":"2xfvrCAZPbmK","Dil02slxkYT3":"eqGkH1RrXz0A","95mlhUhtMBR1":"jDvB6nuQ2Oe2","LJMKVz6UhsLQ":"c3AUBEdmeKg0","oQN6oHBqd69L":"X0h80OR4EX3w","X2EB9nSWiwUJ":"FkrULFn3zPHI","jdBs8hfy5cdQ":"b3SbRGqtKO1c","DKPvfuX717RS":"CaJYOJ91RNko","5lt4NQbv164R":"ggYLEHKf9C7s","wNmMDEb2CtPY":"o4vMHaqY1vaW","RHOmx8UBB578":"mC7ISIuvy4KN","cH3b33XUxzyj":"brvrIEGbXmOr","xveWHblXwtG2":806336178774,"XxGtNljZJ1TG":277923370003,"BPbWGRmKyg5Q":536499246625,"yBtd17uyIaF3":152584511757,"9EWZusY70UpY":703561489107,"CQyUmkSY0xM7":522410866625,"7RpGxjO8GF3e":459006600381,"vGROEu4c1AMI":944184830049,"VK1lt2U1Av3N":362369598694,"KlMWd42PJS4X":130127984401,"oyn8tmu3LtGY":538495095434,"ehGGlzCpXYWw":715406455057,"LivewBIwUSWs":928085914844,"cXFCZLV0d09U":233498800314,"6sjL3uhH0cXG":3455585829,"o8jC5AHqWIgi":551892766538,"ZvQuRpCUet7h":84810280784,"dhyEKjbFfCJR":758694400414,"TKT084hr232M":978999040029,"NLO5XvbIqecO":706130095327,"UaQUMjlVyMWy":839800544559,"nffQfXW5sX3t":483818583119,"g10Wl2twLWc0":929259649755,"hY0O0mnh0ViE":222966881430,"LdjTiWffNwH0":498516588920,"VfVFKfn984QB":112065237895,"Y1T8EKQbjIsI":877940195728,"4VWHpJCi4KLv":88276890184,"TqshwOMoMG4v":195743986253,"jkqKPnc5CR0P":826674256886,"GkEhuCtJYRjI":972045444553,"vZBAgGxqpMRw":260516964687,"LV0VEiDm5Bgh":943328612174,"05WMh4hvl2u0":614880133789,"nCtr88WQNJXk":605629184519,"bXuLbvGjtzjS":92365992369,"ng9X5S1FlTY3":697153201993,"uEnG058lpsp6":351131949016,"mYnV6Qd5Dggw":463597429443,"tESvkFxNhCgH":909480517813,"XjBWuK6OLSYa":504633606935,"Jh3K3uUiN3He":311482028109,"eZOBnmjMWKQ6":362646586757,"Jun2VZLYDR2g":349819170156,"ined6yX5C4mn":589376516195,"zOqJGgxOSvKJ":95215597897,"n2FcppEhaNn4":396047582833,"BK73bicR2Xct":992572756790,"pX1bGsmvbH2v":972654733274,"gk3BlQUMcioP":347039921310,"AmXQJnszB13T":475762652764,"qbRyWyRn2Gd1":370037860154,"STmbyNQRbn7W":360696536544,"79vh2Dgraeze":797690006203,"Ocr7Ai4b2Ozl":870811854696,"eoQR3HqFND9g":18423396532,"4tkZGKOcS7OV":877250580856,"9TkJCCMkOsHm":373802839462,"Jtcsaq5AKFLb":981364138623,"YQiTfUr9Lod6":186929847514,"ECXHcQS9mx4k":865115258217,"pCybKY16Uccw":491999363618,"onVk4L3QQxCy":211937964208,"4Mvt03bXDHzE":439157410862,"8INbdw8PYfO7":769206269035,"TyQxHgb2mYvp":964534434974,"qknuVJWvFeb9":8276628450,"My22wna2G3vd":703950001951,"D1humUGuVHWp":96039496453,"HbtN1NhNwG5e":431456130752,"MBOiogwxA6Lq":135502796324,"qU2hqvv8NANG":918708106750,"lmzuQTQf4sTf":988419363288,"PusqpjUCXCaO":546342113168,"zsJ0p2f4kGHV":468560245204,"346d0grELhul":699433347419,"vXXrDtEO0qoN":660059633170,"y4KGfwqi02YU":602232510225,"uUkd3wsvdi2P":42230449660,"N8cSZKevgsWB":461188575212,"zU7vxb4yMRnw":308646686697,"wASuXigspAsa":211024317259,"LbHaI0H0od0p":342448845010,"kRLkUoPyQMUk":872482839604,"XeZ0xGdjwx5Z":563023980962,"4pOyRzQ2Wpry":683784459163,"7zNwg2T6nmIm":129728451485,"IFSjT9MCzl32":400269867034,"GnQ3Pl3ZJoyX":177413399177,"xDrAGdq2knZj":622576813335,"ucGPfdAn4181":98607298935,"VsL7HifrGET7":321527110589,"hUbFh8ZaLuXW":414235019123,"0YwrM0Z6NHyE":967183810241,"EhLT3g95JJLo":998928814619,"QQeDZLDbnvsU":799746325683,"Oqs6yB1Vljje":491455693504,"rLOg5WVJbUvB":413142320285,"j8s3YOvuzSp9":82380693790,"xXChDuuUpHHs":143874867176,"MlZUrDX2nxsI":99936139642,"ly0F95hGTYqg":541366901009,"KyBMHAJG8Bic":237121704322,"w8bAhiSOs1XP":364104674597,"y1048XeTzH4M":43415975265,"3v4IBPBzri6i":939586265295,"eN1IBnKHLIbz":991322430183,"bj3Illw4N1Af":789408005953,"JkgNCypoQNBQ":875120530137,"IfVT1o2YILyh":930039674737,"shHLyg7USKrv":221988433426,"1vl3O8A63yB6":452978780108,"YoVmk1UH5Xny":504175662467,"s0Ch0NM28hwf":698150141570,"sU5LLiv3jZJW":194911541998,"8fipQpxebHWQ":521256176628,"sChy4z1UgyQ5":587138340465,"gwRTF2mmhbc5":545886003151,"UhJlY0EtcFL9":884448562153,"EUMP1yiry08J":138136948214,"EopXIR8P0sHQ":515656676256,"gtKsTmsA5A1I":819040004957,"uAYV1Fuj4umq":331321633959,"CtsIwloTtdBe":723583945057,"A3a9ecvQIAqp":903654624193,"AnZdrF5WMao6":180108724301,"kPawG1vqbl7O":969922361425,"NcTVSQyTAr6e":104084559069,"ygf4A9gyODR0":227476220784,"XxXNeSJ7dpwY":51840798133,"KJVgsyj98t75":533796298368,"cE81wqkXDh4c":848257275933,"mRZMWq1DF17q":905468765739,"e5IR2l683x0v":91282573761,"jf7UoT633yI7":552211069960,"3yqEs0rggIoL":14183615435,"vfYyw5SCOmbi":551978830976,"97DcBKSnPlq9":15538182083,"DiHy5cCL6fYj":909381378886,"Z19PAqLxwdWX":500366216111,"YXPBTYPFf6Pa":541015244008,"yOmG36FvS6ba":537358725932,"6xRHg6SrAtLn":754795522430,"SbzGyjeIP6jT":880801221141,"mGzVChwrKD2I":749758517656,"r2Kvvhvxv92d":524936908754,"tGJTltONdH6V":191330789049,"Gd4Bq0iPDP2u":248201285687,"6rtjI8KDDyBA":990449165223,"1j6Vm0mcmYTz":294801209098,"RWulMGcQhYoi":880104741615,"J3fFnxsrZwmv":604005913279,"Bq3TD4OGUK63":835784827386,"1BVFaFR1GjkU":88236663741,"7nz2uQBqP4zm":606197872015,"9GG9BkMA7hBF":830323808994,"U9pRHUTujzCY":24737077594,"YczEkY0lF1lM":269632379245,"shNIJHoF3dqO":583558245027,"rE1g9Kjhpaw4":490568632468,"vMCRpM4xhfu5":416550902028,"d3rV8L8HcyvC":12915042388,"004QJLrPgG6F":539831884570,"8D9Y4uoCrzMx":975151666740,"EjY7nUpv8qie":143816407452,"301QJU7YkSnS":651708707406,"Pk1CqvOwT4K6":978216641631,"240N5AHKjkOt":193504346109,"YmLCs1uc8t9z":855647152772,"LEdUrWS6tr2E":606097445036,"DxoNUGn6YFkA":882357914699,"whjkIuPTgF6z":251482577618,"wy8E8bcMiVbk":724631620405,"WeutCLHBFym5":133243594661,"v9Gms2hRK4fV":715974893513,"h0vPXhxzl609":199101027177,"UshPwyFkE7dE":461655812227,"upWMIh1DrmCz":824345717453,"fIucnVJaxx5y":839937939959,"mwpuSqeLvC3T":306654438003,"jXOZMQTnFTpJ":468095322695,"MRWmHeLqyNiN":215459828163,"NeNYGI64wzxK":617720867907,"GT1WT5jNIHYi":183498121895,"6XPWqyUA3nkV":667995378996,"UWNOeJ7P9uU5":547576505710,"5abE2KbpUwO7":658376542613,"bNLIy4bvkSWR":73160556136,"mQXlqxuZniHP":601573836035,"gweCNNTDnwG6":691560758927,"ttU5rquSZEpQ":549599522902,"cCEzwcsXzaqg":669320330163,"OitjurNZEWw8":968958640670,"KIE1KpCz1LKh":487427756624,"Uw8dJwPRk3TM":449185035850,"vXhmjMIr5QYq":606050311,"wsqQV9T9kTuL":75314392723,"VzR0JJUeIBaw":90959595771,"PSnlFOhlXHa2":700975327979,"RDDPVFyRSQfw":149876542598,"XExpjFd3XFp5":814770336220,"m6CPsLIeDUba":585589129366,"VLOTOJRy7nni":413513113759,"p2f5Xar1GmtZ":867126879790,"bBAoYf5cXMFP":699355676932,"cnzttdXqoqjY":580777614073,"dEGnAqDTJsjL":620537058828,"AgsxUJw7uYGx":303915856482,"AbL71psJ404k":699582775258,"igreraceN0K7":21680880329,"ZKOxyGmLPknN":856373026631,"NMiyvTqandtb":945685176623,"BRHjNhgY8YOe":503768382408,"gKfcJGohvbS1":375002293542,"zm5a2myR33Aj":486343186066,"ihuBom3r7hJh":190430297031,"3a5wq9z1loWW":656798146546,"78ngaNSAGhLS":364929288711,"qbPQy8XKjSsS":847254899780,"PQGZvrRv6vqU":551517383647,"LrBRE42jkVau":592495834919,"6ZViafxkdLnB":231277773116,"ERwKrmRMNpEo":106529684815,"F6V1cj5foH9G":194584938450,"hpIxlljLi7Nu":923324743154,"96feP9ZSmtU1":361209857004,"5NDHMVrve5fo":913882991145,"5xVBIxTm1LQH":178329362474,"woy3IC761UZy":999893225684,"epd1j5EwFziD":661593709524,"YtR54ZsJLzoO":251167459663,"jLoDD3V4rK4O":588267445185,"6k9NeYowB64m":364827310445,"KibLFMs50Xy7":664469885096,"whetF004On2h":757494721882,"LAbhhN0UNv2i":128815716953,"v6SPguDcgZKB":361436533505,"EqcArTwMa5me":533810113915,"QlJMVb83MYUJ":729417915005,"xBAh7YA5BTF7":993465458650,"OYl7189lcz0C":692083751403,"oWPgEHSx0xHx":500291549290,"RI1DxhtEbgdT":28615235015,"At3wv7UGqHRd":809456345189,"ioOuqYD7xyZo":681806963595,"ytklDzNqIpmI":168821666875,"3nJEYwHFBRa0":616572968863,"SB5xfT9UhApg":536586534717,"od4Yw0yhwN3R":199623616269,"I5sQnET3FSqz":581886406550,"N0ggS1R5VOtd":705457672671,"wcI1z5l0czLV":613124251171,"8ZlGutRiLPcP":938992344523,"2irhkCbKawPn":32704612976,"IGYnY3qkUkot":207107539951,"ffqEafxxOASt":608003629113,"wnUYRKZyopDe":26395218903,"RghVHRKPqJNA":692258514795,"1jL84KixXHBm":748412494083,"WYqFCvFRV5ls":538654801207,"o7bTCGlIf2uy":918106699507,"zN6KPc9nyVZ7":899666101160,"zAbdeWvwLQN7":819598401070,"1oSLK7m9bBPF":162050581728,"oQQahm87HIKS":48430851989,"lJaj5AMNyeYn":675315686681,"kQvIlQLN3KHa":707531669807,"yRuFyLlLIpfV":850556080747,"SRCdkL58V903":407510396422,"6zTJA8uj2OHR":517977094052,"dQ8WRDbPhPJP":21533659450,"huwQYkHssjAP":158403549293,"4yujqsmCvtub":290362432690,"ncpKCjF7X9KV":840621093482,"nO8ua8zLshO1":984130293991,"IJr4rcn6I21a":150681190453,"ArB4CLXPfYEw":240272307644,"weUwjAuUNyys":845769153820,"S7sWhezJbFjo":880064309160,"IhfHCOrpeMv0":551337966996,"mA2QCKdBFPbd":956435965006,"qjFUBUJyxD9a":380909681378,"x7bEFcQrEg3a":47820863305,"JYC3vqMMyD7v":921768244843,"GLb8QhYl747P":153698817128,"7neTuXILTlR4":893428547061,"gJb5sgRI5U0S":873551179641,"bPsZYZv5Bi5h":870374115818,"cpKdAzZJOo0M":325510897652,"jDHVWhuuV0Bj":262048714263,"zx0f0O2JNptw":958757540478,"nIJ6c85clzqR":755963528908,"9wv71uWP0zhj":319151953886,"bae8B5AOZIfv":668699797350,"aZqLdyTFbzsf":164969425568,"aHFCtRCgvB0A":237018583417,"CjKDCw1emQNU":710133494423,"8jMyhDlxsp6u":200289933752,"QKaMZVu2cAOa":31070548139,"pfGuywa9KEx8":70125891441,"bmOIO6nc5Nfg":304447963141,"RnRpVXwyrTm7":338303709672,"ykKopTbEQIri":580884535624,"nzmcF5EagVxk":301137124330,"GkK3FQl8e7PE":841828711776,"pbCtW6jKQdMF":496169224716,"oegSXjLncfsP":511636941584,"dWKQTJNHH1SH":188293614183,"BINKYNe45NTE":239458863353,"Vgn1oOaQxaIp":628032769982,"gPD0PVhqEkuE":87942877381,"KrIwbPMSpxah":298775825225,"2QmJS7hyi1OK":144151959935,"rz3e22d3yeHd":368576501377,"V8cyQnVsVJVL":931538474504,"h97gdSqM6ql7":236097941211,"5M70vlt2juA9":623237550434,"auNh3KsHywJr":126120104896,"bYWSe6zItlcm":447462913949,"JJTdNmuLzSQv":54788925621,"fstJ1s77lstg":274424444372,"vzHI1NlmJXCA":694749559949,"oc97qiacMVeC":736807921289,"poBmln8idBMZ":299646980682,"TMxkJEwYkw1Z":415534454013,"eyCy52Ck7FM3":353890421327,"nDLFCrvdyoAz":487938033697,"HXfJlYC3fbXB":961223057844,"7lLFmkXd1nXc":760455968304,"tt1pUNfmVDPn":707457879750,"bqp4ItmzzADg":988197295955,"zpLHUpCJRIyE":810600244603,"JJTNyIjQNJnZ":297588725601,"yCFJV2RikIlE":849612354274,"8JXzvtPbWAV6":563010687163,"bcdCxFeDr3cC":301339119546,"3rrIHxuYgOri":473556180784,"3Jt7EzMffArF":703889018950,"Mg0thrIDbyop":784173476508,"h0HX5Fj4a8g0":772897118329,"zNnhPaOzLTU2":765586640994,"Dxw1biCnVJ1B":438965777758,"LXGGEcCR0e1L":465459942655,"UePlARUpAPhA":547364642800,"KdZ8tM2HSxk3":970538304530,"fce6Hdztw8Q6":360307045306,"SHpjqYSVwu9j":980222198330,"ZH3TGW1kCIJT":474063994320,"d64DfZgO9o7v":963901064024,"QtMgqEsC45ed":220197651023,"8lXooJG36fJM":73559128179,"ILZuxD3YWYSx":660779853023,"rD1BTL3678Gl":728649231486,"kLiydD5MZRTW":329931518665,"DXaaZVY1LYPK":987103982349,"hOYhClzcOLZb":65380571928,"8QgWuCnEyzo2":246495990864,"Y3oC2ZWxVFhP":577333694850,"jTulf29O7LDb":517129168064,"b2d4uGl8tEbe":704219731027,"xJFMnOOASiRx":363715334893,"bzVaZXRBVlzY":980719102340,"j7f3SyzXowXV":879057771591,"XPrxWgSaWNFt":542220183869,"Cqw1MnJxV0bt":790233054928,"lGBMd7UQfRJb":923522217014,"F2DlK1KSROnx":478446672952,"peabr0US1c9d":169212611933,"NDF9ZQaWswM3":903449187145,"HSiqJnTCLJGi":177650025865,"gkUwkyWasITe":47456877207,"Um0qcDbfddoA":663005612005,"JZWkAOlO6UbB":940061333083,"01J0zw4VDVNr":500494312973,"IXNpSaUnv8bM":435024418081,"rmNjjVrWYfb0":731004051963,"kGj7GQJVNX60":781922115364,"Erx7by8oVFTh":540216484877,"MVkyH0zvh6z0":373583096513,"zlnQe3OBeur0":460617066381,"zV6p5ehlwlem":612797589788,"r6c9HduVIDJo":110182022198,"pbffyOAHUJxi":453858857439,"7Nv3uqjnzIcb":670747414575,"JLqzA6QD9azZ":157847869595,"CZu5PGxvo4aF":199967691836,"MOIbySdKq7CN":19989623829,"gbK4H6mFYUe0":846084224786,"iMIHNPjeQmwh":304816148581,"3Zzj0xI2dzU4":215359474308,"1ugAl6d2zme6":596056160391,"tRAT9TeSDSZC":869357348324,"m8LAu577kpAe":38708685331,"8MbpRkH3R0V9":337055241077,"EvEaVvGjYreb":843533742167,"Exf0M8IB4xFl":361789652426,"kts66g4ejTgi":10956574456,"dMqnBiYSo661":465804041094,"Gs8fqjy3ofks":136327424120,"iKpNUwvwgfgH":429693928286,"Qi6eQMburD6T":42318830077,"tx20c7gCuM94":74146495712,"F71XXLexkHDj":921675519897,"v5faw0tWhVpv":214797860513,"OSY7rs0fD2np":881450085018,"CKCsn7z6IBe2":552692207534,"ODZSHSTuMgpQ":797865785253,"CiVJ4jEdUaCk":777757251491,"DTIoqxEV4Xwh":779995171481,"sxHujCeMx701":875013214575,"MX9Xauq45b0j":147375141106,"wvcX2kWrbhWY":84664815941,"wCnp39Ymbmzo":172862080351,"kBo6NnB1fsq8":157368422318,"lVtDThnkhNCq":109429803529,"KZhAJp76H9pm":51910749517,"O5Q902kEQycj":329014622345,"X3Pui2BFITcr":555862017553,"57qUngAytmmn":539120915154,"9CJlTx7eeMrl":605769395130,"4yEpyIi5cbyY":873372097477,"dXUYtwTcWjyU":187787059240,"CvVBFmAGtTr8":503383553022,"IKjpMDZKdexS":950892742959,"WrCuMIdsRQO5":560562575341,"3Dpq8kjNq9qB":664128472700,"b0O8vOTMOvpG":97364275652,"wXKhIekjIZyl":210963700497,"D7M1VhLJlQSm":76012878797,"36D1G7gPv4q6":922317411911,"XozarL4pWG7C":534450233463,"2FY0AtDYHKVI":399391614489,"401F1ylwRJk2":162139789308,"MGtqtb3lLFrF":663936111827,"IpgpurQHi32C":125812462225,"mm2VqWp5JgLd":485785278664,"izbq6EQVWi0f":702203660229,"ZJit7T34UmJx":944839224838,"tfbp5HYQ8G9f":998998231918,"UKLx80aEe1Lf":21648020773,"1oqoDNfedjZa":17139648486,"oLbPYsJXXgEy":538529999328,"30UHq0eUWgYb":931451171943,"ehACoCiCW7nI":639491306195,"rcx675sefKAq":646731401841,"o1HDmGUoTECI":818587002369,"8nlAZ0pCUn6v":374863302881,"iPBiNrGzEaPT":68810937373,"KWpRfEkZUYL4":275635244316,"eWrfd3M8avVw":290260347043,"SDUr3ufGAbDJ":938956162915,"KIDap6xd7ETP":48210327847,"fqGc921oeLCS":349767834032,"iy4jAnD43qzi":830199083119,"sl1D8IIy2013":831582222494,"zVYhfG6OoBW4":789201296833,"AINrDsxqe50L":846138426887,"SOtGVlzvjKqy":214254831692,"at2Ul6Qb8vEc":267728078229,"jw2cNXbfvnvY":754302308873,"iuu83ziz9roD":173615898755,"Ccbky8UShmpT":804596188045,"dJONRHIspSQn":957547821218,"Y732Cp5Z2yOt":679637124185,"IoIpgEjedIOa":368972836198,"GglO5Tnws2v3":159311784801,"itq7WGGtnMfy":278193730645,"F5dwsf63isdL":552498315264,"La2eLq3dDA5o":206532189372,"S272B43gRE31":142790410679,"nPrHVySX15wL":502772011453,"nO61y0RuabRQ":60250464660,"GvN8vBYdzX5j":905669119202,"sSP2ZCrYM8Wb":544441281888,"eV9Kp4VOnfeg":823232925363,"TxujB0nd7zon":409566453048,"WvGMITrue3vO":174136102934,"8m6avsjRxa0z":89144076843,"e1i8lay4A2td":490264116246,"mmX8i0rIIVUb":324596402419,"GC0o3aTwTwxg":255892489758,"lVLaF9rd2pnu":508506398384,"DskTPjaMcw24":453291980105,"KKEBsbYjmlb4":238129574304,"Oqrt1OzvUSq4":452604312735,"K8sxqqIf5XK4":264646729608,"JoWD2V6ah5eH":511350016046,"V5tWsPtiviGw":476466729201,"3bJrWaBzesgH":40477006993,"ltxR2WDIgTOb":728740585314,"agjKR48s8VVX":986603788094,"NARWZYPYLqjo":832213502889,"hJ45GyhoCTZq":178029756099,"Z7KwkI1KOw6p":445003303900,"76EkuYBc7AdV":69752373349,"kiPawHP8SsC7":983226425511,"Fz3eSzETsxun":979111933719,"BucI9EWp4m7R":418393328460,"qs2bsoH8B7vZ":648955680039,"cFdH8LCbr4Wf":636928752754,"BFPQXTpZR7Dx":202173316970,"cDVZHwP2NNIL":794867057013,"30IVTsC4I6gw":452494585835,"Uf4c9xAyVDx8":553640894675,"cv2On1tT6mVf":171078284836,"UMI3nm0fD9tw":257270922233,"lJECQimF2sCx":442221612182,"iQbhzb3UYGFr":765267489887,"uSr7BuMZBIcD":188126584418,"TOGjyQweHOUh":552503718738,"kTp8dKm8ZP5u":809892031029,"90d6vwkb44mp":309122994003,"BGVDWeQ36qH3":438878056789,"XD0rrAvxZJUs":613961973362,"bA4DWJ5qcTyW":638645720096,"IuHIaGoDEV4D":341561009978,"8FtwHBdurlok":330767169360,"CqN8HgbJJuWv":561051080009,"GPtdiNhkU2if":392844062568,"GGO7tcJc79ev":36515402306,"LFw9mn1OXH5f":368638907275,"Mp4x0TMPar6q":633347854785,"9zLepXZY3QIS":394610440074,"tCypAhTQMuD4":558899837311,"TlDQ3ygGWFWC":90289249819,"FPnVOB3DISyF":208660584616,"k3d4CP3MyPuO":650039934256,"VHivN90i78eJ":961887230031,"vqTwXsUeWYAL":975370400861,"1nCJv2ApOpZf":452329549008,"4sg5bFv7swaI":13624212028,"LhQtPiomi4nT":402392695404,"Lqfi4tdDcaJ2":165284646023,"GDmpUyx9cgtl":841710396906,"nrI49fR7SklQ":129657292633,"T11ttXgQ6xwQ":399421232862,"PTTEkAxyjuib":815618631357,"iar6701TtEfG":635198797157,"yziATLDyky0v":798759154887,"zaHrZHr2Fiyn":617422992621,"5MSb4mjfE6h5":305737093110,"S5ZhejMAi5cJ":883088886010,"ApHfNPXr64j4":595771841045,"SsEAcJRrPUid":556516936042,"L62PCwaMpCMc":229849071473,"AJRJTolLTCxT":958017952378,"Sh5a8M8CUReS":303097473956,"2VzoO75oEwRm":325427426896,"dK6ljXgCaBJ4":770608625693,"Wehgh0nSqhIT":494955965528,"bwzjZb7ScmRE":316578750966,"rnT4qMhqJ1yb":607882062932,"xoWSkEO1pDHa":336500127680,"JnhqPCmbQ7H2":869001025352,"6xI4FnA37LpN":201455889449,"VDpP86YcSlWo":391052312517,"NP9nHHDcOKjb":395328003197,"lB1gGQpvOLg5":38813383112,"2MYdVRzYOMmn":393631629242,"UauczeotAL7o":421098966229,"AKeRcwOCC6Nt":894010044795,"c2DGOqb78iJe":872128007364,"hgBLeyE4Rnx3":725235702187,"nRKCME4ROa3Q":659375252091,"A2IH8w1h2Ypr":497097815789,"mpT4jqcBtRbC":641127429972,"JyRzS1rRLZRQ":792664402441,"dRRsf60uPb5Z":770191869746,"ML0nhIJjslWu":356467048648,"QhYEvbK4EYrv":983152993091,"kmStySY6v8Q7":266949121001,"goxMntf6e2Ap":998183751039,"xMjWk15GbbGo":924884561911,"s25O3zZGCJ5B":733684444736,"8HeQe0bDn8e5":695879302068,"Q6KtQd7PmMYg":395961706655,"imd0HfRBwqJN":440799273821,"CTrKsY17T947":858815745472,"nGer9pO8uOyA":524195300916,"jgK3oCyimpdO":697002742586,"YUEIkeJvNWx3":958190544820,"0UYGeOGzJrEP":460473711114,"A3lK296Rxjrc":534692592416,"LkwXrDFuSbvO":102814746168,"l3nFIuc08l0c":332993717986,"x2a7wTm1i0BD":431236289137,"RBaz7Nvn8eNR":570134635308,"CaIOzEM1BQpe":98978206546,"0SLg83Zfn8bx":438694445113,"6DOJwKHpPfKs":995078417741,"aAvraBnlFO6Z":43409009777,"YNitrbpBFYdZ":561995357458,"4NgdHW5AMgek":298795291218,"ayKDTEOCVYz4":694631287375,"GL6Ay6ZtSMXI":850260242569,"7pVuBOrpDcFc":786355833465,"4zV3ZrcRiXeO":200345371467,"xnQpfCti22Ze":864599976996,"pop4JWX9gJEP":297150030877,"t4MjUNqqQAzm":498223918161,"bEPGPlxj1Gji":387000695941,"hRYJf164UPTI":486435651461,"kgEfbrdHfaVo":897569175535,"SCgOaZp4WbR3":609623022761,"BFmAXQPNtnr8":333649911341,"KAnYS0TFIJnO":452057252720,"753AYvQ1kZk7":581889051240,"2uDKq6VVJpoJ":299902281822,"DBX5ugnOmbYr":51731321605,"4yHseRCZzsyJ":165741001142,"qzDfNXpC9Y4Y":753214261819,"ynZ7RbY37vVF":566954509799,"thpv4QbWQKcO":435159688363,"oTrMQuyJzi0G":895941882693,"mAmnu14XiQCP":403728320840,"Kuwvq7bZrenk":464594729138,"k6P6CTkvIIQb":506815321504,"fxT1Z3b3lrl4":878989290572,"tYuWqwyY2C1h":798525275712,"5vttt1Q1e9W3":402096388990,"S27DXRofIyzM":450058112146,"UIgEi7ql0egt":658501139931,"WoMH5wxHkKax":460106763205,"LcfMIDKLCJMD":745046627033,"V2AX7pbpwp9B":925697518609,"Fdo4WakB0UOW":16523071052,"C8B3x3EUZS7E":139675815212,"t0QYIoRjPk4x":352432718167,"MDiGGDjg0Cwl":931197047654,"KoYi0LkhC0qS":134888214423,"sjeGmVBNE7ER":592705715938,"XCEHWK1HKLpu":559011041739,"4PfmTNkKjjRy":205829375381,"0Zf2cw46gPOV":786311811230,"zRHSYZkFvJ6w":731349101458,"6CsZvpnD5HQU":246265761788,"I0rURELdA7Sy":851821379549,"m4xD8PxGs2jC":364005856857,"CaP1VccwQQpA":771288541282,"9ueVmFyv48W7":623802114449,"ettvN0lCT1YB":947181035227,"MZckIUQcBQ5c":250105429200,"yBXGx0sntrKr":869280465628,"PCO3qftJZFgt":404208513970,"Pj33FXCfSvhc":696169719036,"xRToLvQz1eeq":657791849401,"zcHq68H1qC9F":453841644517,"tvKUHIji5yX3":217986707175,"1XpOPNvJLYCc":488373565470,"gQOSPwZRm6lc":33812761745,"3v8XrWRcF6Aj":394616066484,"O0gpttzjaopt":552567542129,"8BNX0NsX5Eaf":825071702491,"zpem0K89hagM":383696603134,"jxKn58cdVKUp":59177742058,"LGBOV98v7roJ":719750689749,"8f5SN6Ejzg1L":137833430619,"tFseEYECRnfy":53748911699,"hRFp54awGH41":855877301405,"EQkYyvJ6u5dT":579740374582,"XtZM1oaKwz17":127671074788,"CZVKytlItB97":635255201424,"3FIDHWFtTI8v":292056553243,"xAF6LGORHDJu":995399137778,"4uHvJVzIQiUn":599487059452,"KeOiExZxM0dv":168707767838,"OD3mbc57VNHw":495575131990,"8dBSVHwwQsDx":97753102099,"sXEl2ix8KXzi":979215573256,"tYDxD9rZNBjX":371126758729,"YZ89KCstu720":383079156044,"LsbyV3y1onJX":632532849666,"voHuH6eM337t":873560819455,"7FiUknKYQejh":968894786938,"XIXSNtKFkSQt":213726469165,"MndALHJnJafY":855911006028,"iMSrAX9u73aw":335663006716,"7a4PuaIXfmMa":706285356598,"5QI8XQhXOnCm":535980688505,"w5zCc5VEPFzH":505386242746,"VFZABHovwOWu":629566995491,"ilWOFr1gEebt":112143037401,"P2bb9Zt2NJEM":440633572716,"9HM1Ayleub91":459111356755,"hYqRXzK4H4VW":74113654446,"77WOBGW3eIvf":995655845098,"p83W3AIZWT4k":116904096585,"kyBKegQ7mbr8":729253406831,"lHOLXIQAh9xg":111946813658,"S82RB9WPCHfY":861292509507,"cLE367xJgR2L":746196401182,"MpfbPC3Ir4XC":276727471921,"lA3XdOBmWIjZ":265929857899,"JkPZK1U9s7YR":320664005143,"iudazThIxDFc":68686635042,"wSFeqi3VONNb":115586743653,"82jBvuOwKcat":917638609884,"KAf6NKpo8bpm":282437337971,"RuOFW8Td3ywi":492731088166,"C39urtnG98IK":71531221145,"V0RksQqtt1xP":730794437199,"RmFAWn4YkBOU":490637865151,"94Pn2Vgp6gex":816550527955,"QpceWD34PdKd":250700860225,"6Qs49Ck35T4q":1742075240,"92May8mXU0ve":30313034975,"Q6EjPFzUenpY":446260342214,"IJCq61zFkHN9":455446114046,"2VqxGrKanij1":691573023331,"NwZMYoKVgjC2":110939705316,"UzXUZkUpaws5":733364559504,"25oxR8uV23pS":53366039167,"dmb4MPkdAfCY":453641762943,"GhrfYuAZVgOf":168259543835,"bWTzKzhDEsK1":866210944551,"L2AvCJnsgViW":334852860391,"DSpEKG58eRDH":645827402679,"2WQsjoRt9VmA":149370930996,"aJb5PfkJZXsA":270746607610,"L62fBZDd80mP":507504752639,"pVbALTFJfcGn":828474870657,"fyDlAcHpmlWZ":396319378068,"F4i8inRF83cm":291640744067,"bpZkmwQXxPRI":803350726062,"CaNuwSZcRycf":391093517840,"4TWFK37wNYD2":723834625821,"Pw72bEkxTTYR":556970015056,"OFQdvJdwLd6U":467068912703,"5NCv9iG6KmeC":656782694938,"dDcTrDLkfki7":699689179336,"bCnSrP1NJIFW":708970079572,"yZ3EOy1sWJrZ":836321760362,"YOARADtU5zoV":236106143074,"iSZlQCqrlSVJ":218141310682,"Y3Z3Qrdqhx08":120119787024,"DBgRdYfFQ5Ak":804957163111,"sKbau05FvkTw":543720469060,"laiGRBpkKE0j":468210836943,"sFlvgAmKnBMk":896417614209,"duxvQ1zi22pp":401386413007,"Mf8ll19UTSbf":928025567199,"GmxsE8TgkN31":764452629084,"X83dv9iZZdah":663275307697,"Dybx9Xx5AbDT":509087768273,"yFmddDwxtZfj":865010696996,"xy4sLOARPLfo":592006044092,"6f52sm3Wu4iS":809467671809,"ac1AQXMxUc0H":465934634780,"bdglREvvjFSB":195170643346,"A74HghGpXUQ5":270776928259,"1gas9Cud0mmu":77309685592,"zP2mnYtTNj8l":411224554505,"cAiEdFE7XTsk":92337788457,"GfvXcCq3Yl7s":156631150065,"UCTZp07cy15e":678879612477,"a6Iw4GFn95rv":859951289863,"XyBVVERr5bpV":713686333480,"KLxSy6Ws3jOB":759874313451,"oap21ChaJTJN":900459199389,"VlTrQSaB9wjA":548209494448,"VhMyhu19iSU4":953831951845,"Yf4LOzalXotV":536867715083,"9a3haQ8Yk5ac":20806984039,"paPLypFcgfWl":81847473118,"7jCmx46oknnS":849125523483,"IwndPijnOlmO":556136197606,"9BQ7LpbD8Pyg":108700424634,"Ye8rlNtRhBhH":185840209816,"UJiCnYb9OFwE":753375028946,"HpQfXR4GKKpu":578815347692,"dzyfZzHrB6wl":228585454982,"yiW0MyyQbU4N":971756541522,"CALyeJqnRBml":173264586648,"m8iJNlK3d9aa":799329436514,"reFvABxRAmTh":998151941318,"iSjEFgEbifI4":309266972580,"jwLC5Klst95a":440613909545,"or3GyioEbSZM":658612984701,"ADUDBbnAbKox":666777898417,"BOhb8ROrIVb4":903735200950,"E8qMHIbpyis0":321958115214,"JzxKR3SiAPbq":625534251438,"VB5f3ezgIq1g":673143840830,"7X6q7MDNlxGO":751894728235,"YzDAuUaP2pEd":21806029456,"bk2xSMQWB2ZI":511718191317,"MyqqmL2jjlLU":554891988809,"hpEKwaspXib7":524570347770,"ynVOGFQWcSEE":156094970449,"mkn2vHaNl8sM":558266919006,"9oxAlbv2bMJC":502747442341,"Ptk3617X77QP":242340766742,"kw0ihhmnBbuk":551338551368,"dYMVHunZCYQp":608790818085,"bvhmkYtEsSVV":358524885771,"meA1WlNfxb2F":772013584788,"0qvirEp02moC":197557829371,"oIXReB4ETHfd":109222970626,"5bFRNce7ip4i":200987824899,"wCaJ411fyC1D":216521970606,"AbFiEG6wc7hj":993663045063,"A5nY1Q9PfgU7":718294182306,"59Z8jI2mB1ld":65199059649,"URs1IymjqZFf":461599340679,"5ZYRmq8nCKdR":454263917003,"RO9SMj6CWJWY":414644428909,"clVEft1MOVUx":396432036344,"d0kw86Bqsa6t":514677779421,"kz4HOE31ceza":485944961861,"5URwZYGSRuM2":225321571548,"0Tw79J9zHsGG":403560329184,"gVTHTCV5dmxR":794644777332,"CrYNzT5hhUd7":668369226008,"AjhTf2r1ELQE":433441694484,"iOx7kuCJTzqu":656990576617,"D5nICfwiRUgW":583564833445,"Y8fKwp2FpWjl":747422822692,"nL2AleIoH3DZ":964987659158,"EO9iVYttmezE":464642052350,"ObRgRxAttKVC":467053470661,"DeJuqVp7sfu9":635732108197,"lx7OgNjRBEpE":587018613678,"WU0q34rud7Cy":296528593670,"F1S9SNMDVRLG":501495362852,"sTqb4CAketB2":321556756803,"CUIXhNSgGfo7":189978337428,"DzgSmUTXYoUT":729974210698,"Top839jUJnKT":311631314917,"dod6GCxqXmL9":356185956555,"8p0faQUcoKxC":270220575812,"OxwOim9FLusK":911086211570,"bzB34eNzU4nZ":464979983829,"S3g769UvBu5L":549544554564,"Ec74Mt6tIX6f":122918216808,"ypK3pp0aH8iV":633553222188,"NfIQPYURa0gX":948541729093,"Y2MpnhUqbG6s":381479276131,"swK7LiTMXm8l":55525319290,"DfKSasjUYz3R":662272432410,"ujxvue7FuH0w":897607457405,"aXGO50xmYSgk":172357380787,"JOGxjwrrVgTN":699204798064,"anrijw6wK56J":352023053674,"1vOvMVdJP2ck":267500355559,"ZDf3Z81UxH4d":862938021120,"zoGrfPViIvYI":76115434774,"OtPEz0WBnCo0":247942979335,"7I1Whp0LsVir":783880070726,"f1S3Yrm5lNKS":808091439286,"q7Uy0d1jHk8e":822435959851,"RSb1QJyK456m":772874307974,"HxYpM7Yx0oqW":315738854480,"TcejkYwyDwuK":671641128557,"XJWoOthljtwy":478898961843,"urWp7zItMJFj":987128612620,"JcgpxF4yHMkz":630103944425,"p2yMnfDjNktW":794558970353,"jirrF6VIzPxY":656861944002,"pjknCtSowFkx":871409683114,"z9iii8trjn0P":1899071004,"E4EBOVFasbY5":647084619860,"GaW8WZJVf3Q9":508253508249,"ZqZx7vTglCbg":959679270405,"KhsBoz0IJbUL":578323158400,"aJ8duYJKi0y9":514095123966,"cmMVEBknX1yb":683078433613,"i7tprEgRLkWb":876795295684,"OOt2sDOU1YNB":916091355808,"dKE8BF1FzfPo":305015003610,"AfNkJtUvnOcr":673221625682,"wHA3bfdDXMJr":761603526144,"MD3Et77r5uzS":759063015783,"kEgZyCUiD8vb":691263276808,"guDTZ5fZd95Q":805000174286,"MG9Q13Epopr6":223099347326,"faRnTKwAJDrc":786577665338,"C7wFgBg6mdym":836067260308,"XLCoKyr03xLW":884868647344,"NcvktZiDIQ3z":701223719855,"ELGCqBDCOKjh":118031935328,"ybnkNLE7sJLw":667341013899,"KZsW9CJep3tw":529383695744,"nzFE2pZ4PcVT":441210870164,"aYOHGIP2OOak":411082649840,"uVmyZO6AxhG9":852538860710,"4RQhbplGHbX0":486920847010,"c0t3E5eGI2x5":65089953208,"3mppK0XsRkUr":743146382984,"fepbeBUTF49e":820045708187,"Nok37LdVkVO4":859347958400,"lcB6jgJO2LOl":346285707376,"gkiVEgQ1qd26":477500678474,"p90rzWx7EPM6":846326614031,"v51ZOJ8QhIuT":103003143215,"iPb5YwmT2V5f":631628614326,"T4cw75PaMpF4":328895009873,"evkOQdhHIaK6":863777534919,"R7k0z0orEx9J":599301214638,"vV6PK18vIOzj":4315347991,"JhNiIgTwIPRu":541203104631,"IwOKkA9on9xq":596296765960,"eqPyWfKyLWTs":69723416909,"VMoCVJFCKyIK":225101444886,"KJ4FAlzyLlq0":474008590387,"xXLkYYHnwn7w":123844233534,"ZO7SlaQyVsjo":152275135100,"QyhE81odqQHX":632657459326,"aWvd9yWKmS85":790377888739,"iXXHPBZ2BD4f":565404484038,"PfHeidPARFip":588658860286,"e5kpJ2JleQDi":331015888089,"wBZEkFBl40hE":481704199949,"ZF3NiIWTy80y":93364063521,"NzSWMpKqWsOH":535807881354,"ceYxDyESAZ3z":199265336470,"pkQzuwtK5Lhq":429559549619,"RMzySa56djpv":489554266912,"PzuUUGXOhueo":244198267225,"GKDswkNK6gOd":340905209435,"3vpXsbymElEO":9737503832,"Y62NfNmmGJ9x":394495237893,"Fjhebk8dWPae":676149320505,"w3Q5aSnfa0u7":128671211928,"solvgDBARO55":149842221336,"rufJIccUqORY":340750727583,"cvCfaWPyGgib":984023146631,"txKfatCKTKMw":956924422719,"2gExOL7dMagQ":997793960637,"JxMfF15qrxxt":792805845949,"CoAY0necfDT3":198401320660,"rKzRCb67OvBQ":956654005196,"MDKfRmGQEEr8":457317814760,"1mpZOkxoFZSM":163070105033,"vTruEmkuIXEY":71950333976,"FrRf1meBHsNB":976527134198,"wjyxrpwRaDH3":150718200664,"TVrONKoMW29U":6588560238,"UezlLyO67iBQ":809620970968,"t9gU3wNkpCQ2":837356197278,"HvC6V9U7ycdY":628636831391,"nJrWhEIg2AEW":988130788927,"QmFlVDnRpAp0":672288470803,"o1JhxoIDadxs":551651094633,"dHJLn6p5W25o":883985230393,"2UBIqygc0W5u":738560046814,"0vviBwxPNp5U":164597185038,"lkhipFLdFPb1":106087999984,"zgMzTxzp5iMv":192804034909,"EEoG5uEkrgXh":887778784235,"Bq17ezimXjcD":861828014733,"XC3AsTFKBIE4":854338568534,"xctWfLAk95Oo":791753318628,"wmbgjdFUKZUy":858112997117,"CsZmtcjFHWSy":660404636979,"VoP3TDpG8rc0":230498333628,"ZLtw9cb6tFgi":680838523424,"9Q0IrG5PaiqW":801252297180,"k7JPED3eEm0t":133910093370,"BfaihCzPScF5":457042115389,"Auns2NP5cJbR":296097704198,"xcyridEcBkOz":228839929744,"Px56hD0GkBmt":155312160420,"IREbSwcJmT3y":699344870852,"YVci0hQAftTz":238556338787,"mEIObOUdrtXh":455758149836,"AUJESSAycUoB":576982524579,"mUb8exYocZ6E":724198043041,"NbydJmsXi0tk":741007083732,"yIMqd4wWdwzj":886230445278,"lDMJ3zGlxwzj":453956473724,"KaZ2vnBnJbdb":22684014829,"cSUOcjXTp5rP":958347028635,"oEZrt7VZKMMW":621683663323,"dcHnKylrQet0":744373876595,"YAlkDU211qvO":912246263663,"dDNV8wJojhUi":706383252263,"u8qlzeF1jpkZ":450925771449,"KgmtM8e3viVG":826838877464,"SasjhNwfIsbG":208610620093,"CqZZDvq7EOjA":691832050329,"pQHss9mvcKHN":885509907031,"st24lleVbyVl":484813804282,"ZjyN9LoL8ea4":855275535941,"aoAgOYfQbtLg":367419405913,"LcLUwsaIB6gH":190205526483,"qTBDlyVD3QNa":256539129393,"ZIWRPPEV8yWD":955263680910,"8OhU8MN5ibHF":300940889636,"xUjLUPo7zRSN":14026530566,"1mkckERhbWsT":301959540604,"90SlsMeXXVKH":616423203629,"OHIx5LCIVjbf":307522589658,"9EqbysK6BvRe":81698374506,"GzlczTmhh8d0":195387002954,"bszw2INXk8g4":922456996261,"poOFev5H2q2P":558875436240,"mw5Wc0GyQcUj":894698018581,"rjFE51A36BLK":279002953484,"Av3GiCYOSZ73":132017671495,"YGViWQPwxKLt":185584728688,"EGV5LA2yo3G2":942300995724,"4c2ezjRev4Jz":550017132992,"74kTtOVSTK4Y":143128024860,"NTHFlkw1XB3b":181841485340,"nTzjVb3rhPdi":98707035946,"ZeCr0Twxm8Pp":619410755078,"zlQsRlUlRSPo":725643443840,"1oovKextx5PH":258133220766,"qcF7Esz0MoZe":642532259115,"ju07mxzfhXnL":36617946291,"qVnDMzHqOytz":350156756575,"OdMSwIY6cNAg":594553629901,"yy9vr6C0NPjo":367316226224,"tlyQrIR0gngd":16106183676,"pgBiLDOKzcdU":62598980898,"q6qyK63ZapPz":288352516931,"YG6T8M97A7PA":269174647869,"bkQqiI8jElIX":205942358986,"CpnP2CcQWotq":867008181690,"BnQiOeHYZCOK":707425575500,"iexp4iiapvLa":906640063401,"5AnQtFvTtp7I":485051907260,"IhK8TlrApwlY":388024706235,"pyyZ5XYpyJW0":651459804899,"bwWR9tc6Ael3":851088511233,"evolll61zN85":339670456529,"qG1x8ST1BVn3":984547884435,"LIESOB8obe09":451830492046,"7zKxaUO0VMtF":534913175509,"fBhIndgwdmi1":661979212038,"0rCX9146GOid":928259456925,"6hKXjQXKD6r8":611811747228,"YQXNmX0O4Em2":56719942821,"84dDhi7np6NT":329531172576,"543Ka6XtcKsk":374962934212,"Bryrbqx26anu":616226189829,"2AYgnUrUIyH3":770841987585,"l50BAxjWoots":450750694691,"zmkLdL8p8SgR":63242076626,"vmhtfzf8TL9w":99541102231,"xKUmBI54r1S5":376621890907,"eDs2X5JW2ws8":371488269358,"Eye3JdsagVXU":412533231323,"0WjjiGIrWuXt":612384521724,"EBECIcdV8IVM":133724684571,"GCHFANE5wJ2p":123344132301,"LpQegprbgco8":981632474478,"QHIiZFiWPzyh":986028457461,"ZKkLVQrkZx6m":517088237863,"tgVrgL2ewKxk":212617729430,"HhwxUvftKTe6":561407750696,"GyecpHt9UsDU":740818960790,"Zrex6k55wNJr":550301647661,"5nTQoLHzFaui":833637466682,"MIXBQFiVF5x1":316308841074,"sH8BOtU3HmR4":591605971582,"EN0VDRV7JIPu":6133014839,"vNBcumO1rtRW":928701195414,"5I26eQAB3ry5":57376761682,"rPwdduARfkxv":339570276895,"BYs0kyf9D7hB":305884062505,"YNDlcTg9xikk":913601184660,"v97ZhNCvGGHQ":272553534778,"UmSHX6Pipg11":935184467019,"fMPxMaG9Tz07":660909964978,"M9MVVD46Fu09":801502130423,"JXaWkjaQAHzv":123619849109,"ATi1GKt6C7jn":506185985047,"tTBs54g96hp8":649277998577,"QtFZWVrdIZFn":241570301528,"CNi2CN7NDUH9":422806163370,"MeFXTw3fvviq":177797976494,"z6OU6oELpnLn":662818318935,"wZtihV218yzK":538117858101,"7EGfeKeyeSqH":201037318887,"ernhLAKJHkcN":152516056037,"5LeizeQUQup6":762070224851,"lKeeox6h60Qc":514281072483,"K4T0uQb2Aqhq":270787214180,"rHoiA4DvvFPb":132919021189,"MnqFh0wzQRlh":141204028475,"cZwg7gOTzSAt":694158314296,"yxpQJtAcLxEO":528161738145,"p8U7Mt7oDcQM":466342613726,"Tt8QU3up0yxn":352300215193,"wQT2DBHEYV7J":62629984946,"j8l8IIVcrtBo":351739879503,"nv7s030ox63c":701781551582,"2s5eCzor6ca4":955647523058,"uBUMscjfMxvK":233139828066,"roSys9vfp2fQ":922173072489,"MVJv65ZsCmvr":805240356239,"cSJtkyRsZBwb":151123514829,"pJZigcqIYce0":756363900949,"MXXjlz9bv9OW":297047194671,"8KNPJ8t3HJPk":520277056414,"KUNPBrzR5Ct4":441900027885,"xzeOryAX7qpO":991820835716,"zIOZ7EabUTM0":631700735862,"u269cGfQyvm4":719614192565,"HoGMgwazAaew":845673459891,"TomHEFB5uw5e":216763594690,"QdrPnJSsjSXC":219321980815,"V1G7GAvWS3pX":248485734143,"8wqrKeLxEwjh":694883317159,"XCgZTohDaglT":355767911433,"BPnDb0SSUHeD":36825389476,"MhE1cdO9LFOV":994933616778,"KzbVkFprIOOy":956664406075,"nke6iVtGhROz":721516336955,"VjvksS3LFtdz":201174054447,"hrOmhANtqwjs":833580435596,"DxkGQWtUSL4X":637241560278,"MGE6Fd4CUVPh":625630395303,"cw14IYJwNBrS":108913613466,"TlKd01tKzP6p":162352199669,"sWEl1ly82h13":535870882693,"tML7eTAmIS7V":61220011116,"l6ZJLqhtBTIW":155985996428,"unWiUcmElTdP":151068858495,"KJOWIjFqSPgn":8131999912,"UE3asQgdXGvT":448079966167,"NdLGPSZ8vQjd":976946184043,"G3YSdxnoDS9p":609615270995,"1gWP1Up3ENhK":247787459679,"ni9km9EoEMRP":135630799042,"1QFry7ChPFif":375870736933,"HLG2kRoWLJNB":558369875936,"ljrLuCjR68hx":553595976333,"MLGb7jqCrOOD":893775598291,"SdTXCd5CX2V6":494942106787,"K1KrI8mdbYF7":105522182683,"lvNo5GWeprYj":433375728118,"4Kgb2O7gwvn7":294586544179,"PNY0f4957vBs":857791892502,"KnkIatzhsig7":400600867415,"UYnq3LXCmIPZ":13013667277,"b8EhyJ9llA7N":334613725539,"PfvW9tf0JcRP":996499866303,"Xcl09VMvPSGR":91262780713,"gBvbiIylV3MM":711597641968,"9NG2eb2L2dFu":716218208762,"4AHA1jA0JjC5":882820398228,"SJJNe2tzntz4":576860841927,"PzINHVBdOH5v":83366777273,"jN9f9osjjAWk":136121120296,"FAdo46PEh7xf":865330609726,"jglvaMwAgc4R":720369447027,"AnznSS0udDZc":665509788432,"sTEYS1ZHulfv":614674174986,"RDmWE1kNGSxJ":80618855906,"xMGKzFHVNOYK":911387011269,"GPJ0u1IYzCWW":131819160099,"9CIxAiM7od7o":836962681919,"7gleVWhbu51i":671728423896,"GwOf7mvwrwMw":283713249586,"wepLqQtqgnYC":160464374724,"dqak63ueHlBA":32656606484,"cxX5X0oOmseI":514261366439,"BEDeRErQFrfC":733135111199,"rLKJhS4VWvSW":827386329658,"alCGY8H8CuiO":309859418925,"W3fjcWTJkR4D":772428402175,"gejtFnsqPkM5":684269407475,"zM1gVcw0TsqN":111183581503,"WTo2vP1Yhbr4":26759181672,"UwLTRy3tbqja":887253378126,"ulxdzbzSBUZD":867895166379,"fa8tI36QAd1w":960677959310,"xn15kTEK32uQ":90480033518,"X0xTvre5HWKj":194591870232,"NCGGx671OkXj":388642721968,"x2A5zAHfFdN3":432109165135,"PwkN46uRm9m1":547228486235,"20vEx9RYKXOD":785972673699,"BGFdrwhlARAc":925137294562,"PFsSKySs8Qjd":104615917243,"QA3IYKnCrCBX":472090247612,"NRDlxXLxxQt5":788919694428,"OkPa3Byu6mGM":9238439232,"R6xXi4CKBb5u":121391871398,"gbfiNLvMiFCB":339889118194,"Gdad0r4XJE6H":898962699240,"omqLR3JubhNv":159844695925,"0TumLks249rz":452204952858,"opxJt02zI4pa":243080267959,"fCnNGVTH6iUj":77232739783,"2rgeUmxlv6MH":772579646194,"fB4dv2dc8QbD":818842443236,"EKSs2J89KWGo":546434074623,"NiPPzjwucJuo":9013366193,"QfMZQzWVLahj":768782030187,"nJuDvUJSdQWn":645698156686,"5LHKdNWGGvcO":341240359421,"gTwpcNqab76a":309434360182,"ppXInIotiaao":528724530269,"c9q3tC4arDWG":366318080383,"CNQNRTXBW7i1":120124066934,"CuTvBGTtp82u":959979037169,"Pgu8Kt5MmXJG":384089304383,"VU3TtuAobkzJ":280910275136,"6fRJoqv7GE7C":531078640359,"Anl3lRYg3ESy":642487898991,"vDrJLcmVqJFg":817796255932,"5hmIIaaJMPlw":251505666284,"6Tw4MvdFGOi0":867786785375,"LEAISuvMljbd":161973553681,"Uww3aCg6uPI6":178796681937,"54ihDhC0ZGv5":671945120388,"cgz5wxeJDhkP":709491892149,"Q4Wxio2MhoET":287769186749,"5yWi5W1vE9E1":978613038846,"wfPYZBhMxNIs":819598482215,"yKQotI6YHlYh":785664659826,"ikdg7jiEFyQL":440660912135,"Gv66NYf1Oa1m":669672144592,"Ohg2AeH5dJ6M":591903941001,"OppYypJ7KcZR":357518107053,"b1reB85VKwJG":717680841916,"dOSbyOocl5Cu":884477768891,"6pFT1rZuTVPq":133999208043,"dMMotNTU5CTm":13451288330,"2uKW4A5eOgjr":536257442286,"7orbyIu0GYZW":938146921981,"rLdndmNMB0KQ":309270642327,"B8JPCkQIJ4rg":13656986965,"RydSH3rKhsFY":42601614861,"isWbNxnSEmoO":284184164517,"jwnZXFTNzh0f":180024836150,"MYDO6itU8LW9":104350386255,"mYTWclMhfHFF":124201010714,"VpTForkDRjIx":628692113543,"sz76jkwqiSnY":498819709418,"8j7pJQdYZCPX":902355342948,"HYhBNvobj3LL":365783130554,"4X7Dswx1bieo":726214500895,"WF3vdYcafQYU":78390942834,"omsc5aIpjRJA":632767953764,"NzPxq2tiI5Oo":411899851237,"nM1DjuA4siQw":70505733290,"rQC1Hl65nCfz":111937868969,"1bulHSD2Ltm0":272740542524,"U6GHu7DGPJ03":48886816272,"JEt9o2Tf1opb":115367379723,"FGUuDxs9gAgh":725662462166,"0tF6P5FstBYN":793285011935,"WB2tWlCc4dTU":637341460565,"iDr3UoVfBiiX":785115003120,"iYt3aZeXMIIK":241877974947,"LGTsD2nwZi7t":917742334754,"iWX0yW0arnZR":986278656386,"QkKVYzB4YfnP":636438336376,"tbz3o2fi7jnD":367796070871,"TMkUdxpsrO18":806666041126,"Ndi3KRRhsWJt":949888175812,"I83UAp4qsUyU":126181171716,"Ta09m1iA2P2x":302045838714,"dYlckE9HDv4l":16485241075,"bepZkzJPFMhT":761229896637,"PaN64b1XC8Op":556673997343,"8OHRnvEqs6ip":271129492542,"wCi9iQ6A7W7W":260844327632,"01eoh0dWG0u9":127527431483,"UT5pjJw05OoV":728530945173,"7JIjX4Y2yKt1":872905453389,"jNV6C9HQVhEK":228999736601,"L09hQvImpb1Q":78306562853,"ATeH46kP6vUR":576771715996,"hjiKYLOzAK6q":195970052938,"WCAZBw6tV6cc":641262380347,"udEPOz2qIyLz":407624420435,"TYBSIfJn6E9t":674748179133,"dzBLWSdFkO5L":25034444960,"bjp8T6iio0ZK":795596365081,"V3r5J4dI5LWA":994245262312,"b4MBHvoRgLrG":13432636255,"mxbj878VmGN8":151356533082,"Qg4Kc77VH0bx":373698649942,"Fhppz9147pRr":450988310831,"CegFb0mokism":201153910173,"N9q0vul1GA1Q":287970818761,"D4rd0q6uJytu":301619968044,"e7tR3v6JPuL9":157636948573,"24c3gLwUxjXI":390864701950,"GKeupwRSdmlC":651135405593,"APdWw5DIPdcN":912331381209,"DWOFkxGdIzQK":390393421807,"RLnvLfxK1ai8":704057521997,"WWDThounFfG3":244230490220,"Pu5BSgSi2JCo":970183701112,"LEQrSmOqLavP":655742622981,"wLNLqs55lxta":29911002125,"SWOoXr0eZxJd":536581832041,"DcLToRKc13CU":470658888642,"saruC3qN6qBG":158611196982,"s26FJ7gMvwBB":677616861349,"pKpSzQkdp9M1":99669365834,"78uxErMJgdeh":836786676816,"NpKoAg4JKnYJ":685744895292,"Bsr6pqQmZZM2":331388251933,"rVvb8ltEpz1w":336669433703,"cKBmMt0F0BjC":848033821570,"32zHCHiQfVE5":354818458001,"nv22TvqWRRrY":922168219800,"TTfATMjyMKJq":744096700314,"0TNETNKEQxNe":57737923931,"T6nKQI4JfHle":132372812427,"ijoBYQCrhMok":355572357108,"GlfM4c5LRY3t":285042669464,"2zhs9GDEXMbp":555004704770,"mfKbhO87Lm6E":209111324234,"0WV2nQAi4dr3":519687807986,"UVu78fHkg4gl":87637612334,"DwhKSyaD0f55":801970663849,"fkHdbIilvySe":207590843862,"AshXLVn6CGxD":786657658663,"nlugB0PiPe1Z":286562526784,"LZcF1GixBrFg":643944133887,"ChICGwTWMnvK":824489562458,"K8KnAcE5ILfG":917750550784,"2LAST3j6IY9O":266674747022,"9pHMo7bOKX1H":414846992835,"T2b0CdWi7pgF":782101411588,"CiQ0AmviqB4d":867365453342,"gxm3vw5sm3mi":973472825062,"P3D5JxzXu92X":772593580509,"eb6OocJ9MN3N":959623152638,"CoulRIIPucUX":519459329358,"sivPXOuKpJ1p":851002714710,"38ebyTwAB5BE":223600791928,"zAvPNsGPJZlg":16639282000,"qpmzLA7txltG":798566490706,"7A7KJeZRqNDn":894049491058,"Pc0vlJEfDj7N":403324378940,"mFMRzfHI4TUv":190493684912,"f1z65DRRyi0T":193742801654,"fMyQADzSzOZN":754671102563,"ztgru0u4gtrN":128457141328,"kHc8aZMJG2pL":437704170552,"ah0hONEXFSTn":384816341947,"GyCu6HhHPs5I":653635542142,"SoA56bhEXnM7":542619650002,"xycWmZShqNUG":599129624125,"GxsHUJCbSn5M":616153215599,"rc9v0ZhGvG8c":217860645456,"0iP3xQlN5NJm":733450859032,"UZ7nOjNnMSqB":535371928753,"tJwCHPKT2FPw":650283547764,"6mDdJiWjZytn":426562868275,"RnZfDiTwxtxo":952287315343,"KnMwxJm29jpA":325711085643,"Ri34t88UOC92":761058255768,"laRMrztgsGIz":908804412871,"Xv4oFBXD4s2t":549771289672,"KGlNz66YqhEM":742134980915,"kEYnDKVVcJk6":156710866629,"B5s0KFEzbc3X":607315576278,"HaYZ0yBD5v5y":344397479596,"MRSfS9D5xvsl":137729437218,"bdanqb27R8Yi":972300823148,"G3Kdaj8bkrDk":894316631953,"HwODdE4zU8ZJ":386053988236,"MgN2HXW1SOcw":69841058703,"tNKtrVzF44yQ":14522187856,"DM974mVKWvvH":620849633637,"q8KDfGVXayRt":114025419820,"rXIjBsPIQEi1":477211477612,"1JZU5TKn4qrc":459534246747,"U9CRwhXiBjfy":591586190406,"wJIv70KFkUVc":363299245825,"icicLxIiV47o":248698107913,"k2ADAyvh8cp4":815879573440,"9hL6AoVN48Et":187486248586,"KsJzv515ZjBk":724636359414,"RyN8CaBiWqP7":193474392350,"GsM5drqvZ8fh":548013241928,"p1fDnX8Cuv6V":765653020495,"bdviUVw2KGkR":294262484001,"oTQaNQNCDfxm":213884987578,"CKLXpvvQp0IA":670164301610,"bOD8hXGl3o5j":401848850617,"9o7Ca5UhbSMB":311400694775,"vfUn5S2l76VH":322064010377,"nkpLuJoRiwwd":769283934581,"gojLWap50SdL":275119717448,"ehNRid7OViSX":600050362811,"BjQBWMx7jtEV":636639808403,"xDjazDqss0ZR":365153131760,"SbCPJ9GK4oXP":789981104317,"28Jwqake1WAS":619492222463,"UaEMJjXJb952":945545425145,"MN1SxpAf6KXt":514797292233,"ILN0FBWYX9Wu":855733348144,"ExlnpteCVR4Y":763226954769,"rejLGV8SGTAR":574792696847,"V7XdAmBfTXoI":134698407011,"NO5D0POqd1uM":727356172665,"ymkHpGY6UHkD":698161454495,"7Pb4c1rHJXU0":861345847094,"I1rjRw1ig211":279516031016,"57gEJoTE4fOp":741108373442,"rBU6TliXNmia":204987349210,"PSIZRuX4YFHn":778837799743,"YrAOU8w270Tq":766348480568,"NRJkwstajLTn":430577642059,"IzwTMVglM3jT":489574835191,"HQj0ID0gagqt":993169095122,"kDI86svmIy6D":366222147750,"aeg5t7AArWaA":762073654418,"VmEiErLnSOCt":251146878420,"DC8kYn2ZjVMY":547623300158,"8qZzavaJmpl1":166065353345,"oH84C14193pQ":124908155595,"9qZDLQcelXc4":188675658902,"dDbllhi2O0yI":783264517872,"YJffi1iAdaXk":906522154717,"Z3neJX7VrsCP":618750729075,"61l3I3hyC7k6":765171389573,"hZmJV4kQaAPr":539000371946,"WBplpRasLIR2":649962746232,"1N6Pkvm46yBA":705984835152,"2gRfcy6SyVRN":670188641244,"AeFqZsYsZNkB":784749383940,"kpDjxlq6h078":629086321744,"4bL4dkWSB5uZ":397120305240,"OZZHEnyiGdAB":972517128850,"ZLcS4EU8dgFX":127995847718,"8ifRKQhdfem3":367522106495,"vb64rPGNH4fa":830459483417,"BAWYwR5bQ75T":124542477376,"13VBh0cZvbT2":909850172825,"tMYJa9sFSWiK":517475829116,"M5ebgUm5kOOY":746124479084,"kyGaCXEoEATa":599411890497,"W6MhR79Nb0pE":320784586816,"dAaug4eal4vq":714233095693,"ZpZSJPTw4NiM":135512306118,"R0SM1uQxofOg":445283949768,"QfFwYoaBq7fJ":188928580155,"LdpH1CTo7jbm":852539057240,"63iCNXInRzZL":726993706920,"NJmUljfNNP6N":584075290787,"ovum1pxT0s4P":626961292679,"NCshen6pYQQa":662079670102,"txrya0tYfNLg":423741113602,"iZMhuu3tBgx5":789320170535,"UyOInGPkv6Py":68743351810,"hzecEnQzomw5":23327857382,"45wMoWuECmwF":683866849844,"0NlE7HlQwMlQ":862685330078,"2rRR8Om7FLJt":841759335974,"Kve2STeasmfw":9691529288,"ZpYdsskLiUkG":577526425606,"2y6cKUrFfepz":289704942304,"EOAI8GiBKqNf":753905869781,"Ag499eOSmn0e":324937529823,"vMajsCutefze":896508236634,"dz5Jc9j41rnR":282223030767,"Ay6rkxn3ers9":204854631838,"6A0nfoJSbPgE":162852837611,"Z0FPtHJmNwAz":538944981333,"04XdeE3ZZBsH":424322485498,"TFeZD2wRrDHI":573564468643,"oRyqZbnakVeY":724480698450,"4m5M7FxQG6pF":388998689497,"3lPGL4dEuAcx":999290350302,"eeWqRdN7jUJH":59418639021,"7NA9Tg2LsRI2":40555817604,"UQi5KjKuHwOw":365199732319,"YV98jBBmYh8W":612754526640,"v7SBddBxCo5p":304939699425,"1JoSG1NzMI7z":76341534902,"FTFkQKiUVHzQ":705789347325,"deKn0Ao3M84V":47814682305,"4Q9kWdrPxvep":978429990293,"ct2rx1aAARVw":324213382571,"nZBH68KYcl2w":418690301515,"I5EmewFXOank":352010878958,"qeNvHqJOK8rP":643442641526,"ufcKG2K7dJLH":527143135121,"MpSOdYvq67dN":88296804573,"tKVJJ97F56sR":617644075644,"VfYdxVbt9l39":795596231425,"8DLzsV4C0eGo":441071196744,"cKo6IZrGZBvh":208654702864,"McZ1pSAzzNsr":211603613963,"R5OY0bIApkLn":827761578177,"Fw6PC6IhbDgW":363320158404,"pjv0j8DpHOEb":648513886849,"cTBJqqQbhRLF":611936546152,"XuqV6HQPD1VB":94305910860,"en1hq2LmOVqX":829008504272,"9U6DeA7dI8RY":932909581425,"jg7SNpBaE4Jc":949962890349,"QfbtH7uyr4FO":534308645844,"JQ36QzRkZQ5v":647249450956,"K21K1DIvYc6u":749736929806,"YMR0Vzin7BKX":725787323573,"kQZzRPdmJtAZ":7041194956,"nGR2P27htZtt":935947225156,"O94YB3D8W8ku":586652152353,"Mdmm7PGCHQhA":231525277717,"SPPUiZooi0cW":801328641134,"UX3KMgymlYsT":39996645883,"IQxAUddoBRWi":684793700561,"0zXNCpF5fgIu":94921238475,"sajftUWTr9dv":830072632840,"npETtwd9YqTM":204015430924,"x4PqtAo6cNBB":388941805378,"Q5G7ZBtQP2Bj":107168434477,"OcgBzsIP3Byb":608550440267,"sHByy1FHHrP8":110520671880,"NqkaQsygSvkp":983330660087,"bX3dnU3DwmsO":511736622918,"vBFyNVAgZv3q":476907822994,"GK3eJIFB2gor":831992810907,"P9XZuTmRrGby":299553513335,"Ld119obtW9na":268903740197,"MY1lt2YMBPVB":192968729862,"R5G23PQKi9yn":310718818030,"9okrF4GVuy8M":311172382639,"aql5vM3v2P89":966665223861,"2zKt2wQeL4wn":475512829854,"uokJZtHBr10x":819511836564,"fsGwBuZr9Evk":881331995027,"JUEFytO5zQDn":148856975155,"BgaDx2evEc7X":444168910603,"V2vzhsullnyg":712209481095,"OhImWGDtRt25":101856115329,"GvaCtZqCYKVp":865286997267,"oR46WuCTbpZR":62559971924,"Ea1wiA1RGeo8":659952922810,"c5XSrcXeQ6oL":939743472570,"RHAe9MWRrchY":853197388024,"sHFBtW6BatAt":678996148029,"lx9jZo6Q7y8Q":248342789297,"cUlusb1g1B5w":671327526711,"jZfalItn7EH6":458859234357,"GmhJOWwcEZAo":673083308478,"jeVyQu9TUkOx":751181882643,"caXgEgwDYydd":934153868144,"pQe97gpsL5zy":983411489388,"pqL9DQz6QCJn":224126873416,"CzZJq8kh5zH6":435643147998,"2heEEJyro7OG":341531588283,"nlmmLFKdI2cy":792557232180,"vGW5yPePopSI":512067681824,"WX829rQeqll4":278156551854,"qg8G8CZD0NHi":532216916631,"2KbhFDwYIFpe":755568816968,"rKIUX7E2Q1CF":749297700346,"jQUMKXjHTmCV":457435876845,"07Q4bdadbmBN":879958077017,"76r3K9YM8PdJ":116085495649,"K0cVEylLd2Dp":61636303327,"V2OAgLyWnx0E":869345816824,"vRFeRUBruw9A":601955974962,"SVNtNylzXJd0":478209725408,"N0q2HIFn8HdS":375101283887,"AL0JCGT9Tkta":466540162165,"ZTuu0RzQlPQY":329436418833,"E4XClc850mpw":86783415735,"MdffmEfNE1PW":404311122418,"NYlWT705N4qY":798668733194,"gOKLFT4WK9nd":47742195642,"aRXOSJi0A9Fa":132726475104,"hdSi5NlKRqbk":455244642546,"reQddmCn2KrW":28690617705,"jqB0xvv8ttmG":467906454938,"fuWJ6WFAQmDI":656823775760,"8uxnPxiQZuRk":737543475676,"MAkcqzvEW13v":428583528063,"uaj4cVnPzPcI":817316592303,"d60O91Gc7Jrv":919606008578,"REZfiJIvjtuE":807286572156,"8Y2EywQfS6Qu":292200212023,"gvxHXk7xoLx9":938306444434,"TLoKLrErxfgY":875960094999,"US8irSGQ8U73":515010761164,"wRiIJMG2y6wf":231131282624,"lsS3ZnhFxpfI":712043787174,"NdDK9JUnSeIr":271123309264,"mwG2f8LDhXYO":24266763660,"wflSdr6wuLRF":784760563238,"lB2U2RlG8ZWL":681704447227,"HXMm3eVyELJT":43201964831,"SpTeMiS7WZLw":499013515020,"QzbgiNlBENEa":911068579309,"0SHWjJmANDXY":171786774879,"AAwsirl2p5lh":431644767938,"mAWu2VX42YKV":11041560671,"e3jpNwaXMY2t":3452265669,"EVINTMjCDtsK":850903422204,"wtZwwueQzIq8":693641389605,"sbRdvLOUbChc":281784971778,"DJGDRiZPZpcq":403785966384,"00JsepLbsGyK":202680760212,"bzxj8kzRuVgP":741920068630,"CCzBlZi4t165":923839703018,"T2o8wGtF9TF1":626637819135,"NBu9XgO6oBsr":304788580427,"kAsTHMP2tbUF":782473405510,"BCLcR5Ue8hqn":816553654277,"5XDvoA4SUCVA":665928837080,"RzftMp7PJUyT":522431766258,"ZTjsFvysZFWS":163269462022,"gNeN0NVPqAF1":354476085452,"JFuhEOr5ZJob":589249866800,"lnVTqYFzqIof":806174663032,"DdpOefEUOTQb":92941484791,"3lGSVCYfdkWI":55730812700,"3hbBSenva0Np":401399135874,"qo6MGPCT6hmv":773990047882,"O3XFc9OlAAEl":699740932402,"D0385DuieSoJ":24104769669,"fWg2F8tL9Nn3":303347082910,"TGzUqUybHY9F":499057414348,"0hgPF4eWr6NS":536445854550,"JMUjy23A4bb3":458143940611,"nM2KjiUUOP73":984201911642,"RWZmznHyqPym":6035143623,"XvtSnqQHV00C":242828779368,"e0EOhwzdv8zo":271197085843,"8JKaGbxjKRY4":36885690330,"KRkTIczYqgoo":423621674156,"OsHdkg8fmFhz":960557423139,"gb3T319LxbRy":529914272440,"tf7gCQElX54r":629335962203,"bS6lFOj8LAkK":362415612173,"ENRQU3Mn9a82":837096349004,"Xga3q8640K0x":182543568504,"OJBiyQmM5Lm5":602007190123,"ZUUCUxgngH8k":37421387792,"hLPVPefPMgek":737968779888,"pd0Az6bm60mZ":585489408979,"GbxtedUtgQ0t":552316635330,"cHs2xoSU7DkQ":372654514027,"5oBf89teb77U":13397270612,"4KRMflqaJgGF":697306204586,"x9BkDDCx1PeI":784089162021,"QIuhcepwyJrN":332992845380,"maDCriXjioNw":181695493343,"OLrk7lf4WGUV":160646465095,"Yz26mr2GNQbk":764820218020,"sMIY0oUqbsdW":960448216513,"tIDVArxf0qeh":58770272395,"9QQuHaD66eHH":618449725075,"BDW9q39p9PMN":638772471266,"KF3WLskvLwmq":943247677811,"PMta2EHxXWhu":858466147672,"GXmxm6PSXHmt":906590453473,"aenAIh41KPb0":66301021935,"VeD8JT8JQQHf":398143876024,"0Vhbp4cUXNWc":644436376941,"hAIrvA24zfIh":706490065811,"gHThNDMfPQ81":653870614932,"mlslVCSdJkGW":739200963079,"nvc5bKVVRL9y":619170873563,"sY3Hews7riJK":990856872095,"MKQ6DHhcJ0Zd":160750768370,"5HtoRGsOQPJl":653499399010,"ZQ7HnBzGlENp":318067245928,"f4w1E317gilL":825988309234,"TZISYxIjEhO2":651276037993,"qTbioocneHtw":858035871534,"Is6Uwlel1Abx":667310635033,"PZwcyFGwSbQb":911780579915,"wtYi31IJe6tm":953132365431,"xjbNqxwI24sC":326703410917,"d7sEBAuDbLiM":930807784182,"PMr4DLEW7fet":925348644561,"aEPbkdpikBAs":690232997433,"0ZTobbuAoivU":820930319173,"eGoqcLM3PyOO":478710724700,"vG5Rz7apjbZp":593496722325,"dDWKOCTfgKEb":421948555875,"AFOexq1UFkDG":186882700522,"F3XRjH1Eyvgg":223258570154,"jZNij5SENXvX":57357917447,"TylWgi8qYcjy":189245268705,"Zu2Br0cLi74g":306890658797,"VaYDcnfD6xr5":898467707350,"Wmd7Xhlt605p":729086085282,"ckjulbOIt0L1":231236354315,"tG9Mrl6U6mPj":899704912782,"nf5xuyoBnWMn":73992734624,"yCXz9jQVDgUX":835313545510,"mF812CsbI2Ip":727833127322,"dkEbkLsQspqR":796141122428,"xQlVrJGejzD1":765707955026,"BYVFpok90ue2":837866605682,"32Ur8rnMJowv":75776873205,"gLZRBCl6GVZE":446078224855,"518h3HMvhtdY":384102523491,"VSs8cKMbE4n3":309666952236,"8r7EXFsD1qxp":447993558772,"Pg1x8KJrANy4":872045582437,"1tYFXneuai4N":991671807488,"vhzifGEHtzTn":901587443303,"4HGLSo3oU5gh":656085119975,"OplZEqx4iXwo":57546655698,"sTIcQELkHfuf":291116827399,"DwLdgwv2amer":817460653422,"MyHvlAzTK2Wc":354085820449,"YvvvGyHqtmP4":790542841121,"0jghZH7vlXrJ":67106003086,"VW0v3IRcNOdH":221082446174,"RdnAny7UimOG":406746163699,"I43b4BNxjYgY":416753215357,"uKj7WPQYDirh":804457050118,"DECwbxovVi23":933316115899,"Gj9GbgUTcrou":265666197248,"rzWFDXc5qi3R":605561790478,"Ox2yD5LhCRJs":729403614212,"onQZ1gqOvzi2":816860950920,"LPKdOaxZWiKj":124030737521,"PAmjLX7pzMWF":383332510940,"O1FZnxwh5Q6C":335696570869,"qr5XhzUdwwvq":228382973236,"BJM0OxceTLUY":965417939702,"Vzbpql3xoXC8":948347330344,"thShFokfRbXo":688262130896,"vRCTBTu1w5M4":162401968786,"4RC46m1TYih1":144246139461,"bFrlr0buqLFl":460062408705,"aPl1ZlwC6h1d":96480615348,"z6TqHiT6VWIb":853590009121,"HtSv7Zm1Hmyw":106332541001,"Ct5OnQxbCbfI":788182111251,"mFl1ZzBiMzLQ":976425139640,"XQUZr6N01upn":365862804570,"Nu0ccVtcq6qD":17128365215,"m7qoWuR3BAyH":403326921614,"FCWoUSopec9w":736442078069,"d0Lf9q6AOxMM":294456267833,"rww7BZ0MwmDk":654532527246,"aqqb0WExXRls":89124736307,"ERRO4qABmqkD":300252045387,"Ks357ekSjpmP":899222545059,"RmQCS3fV5ZrJ":905991493725,"bmGtGiYNAozH":743607264836,"FJi6m3yVojYE":916714804531,"pLiZk73FL7ua":332077615642,"i7Z5CVjtGUFE":349812409592,"A7s8OBxkVRWg":511551861687,"kaMR7A0HqgOF":489656501507,"Akq4zI8GQp7o":57785912776,"R3RAP78x2Tgq":885060022998,"rgXmbFHWJNTC":743712728777,"uEyWsZTQVQIC":851267414759,"GR8pOHKetlMl":642644338988,"MId6MzPROm8S":500169981836,"k5eekMpsWy6l":931046910060,"rWVR7EzMKRXg":575905701280,"9pnX3eqJjm6G":288342960522,"EInHxJkmvjLd":487732205921,"L5cMp0PrwVv9":48029367703,"ohWkrJo98kd6":630092749375,"78fMDlajZ2rC":499373646751,"YTUxlK8rpEUu":263213571921,"NDZVBwdOcH8A":852860589462,"jrY8p5fddUpr":557122117855,"ADmNYfvrBKnh":923623746940,"gg7NuakKTuNL":633907342142,"BgQJjZF0r548":447971585289,"gumeMVg0jDrm":336224719000,"rGOBgKq73tGu":485580190463,"FOVJAIsDOjfl":899744539554,"bHFK2KrG7DCD":733046527004,"Lt9RHFN63tsl":451781933626,"D3V2fWcoY2zl":153503704162,"66ed9XUONSJb":164297212395,"KHNjVU2FpoJp":788879886376,"ngDQs44J9WIX":899812888610,"sHJGK3UCaVRU":41292650515,"P43lW25VQA0j":294411000691,"Iax89EKzmDo3":685726672773,"tKr5YedoaaIo":71798806980,"AlUOe76KRTij":502987133099,"lDlrQYKTj9L3":267814522686,"ga8Su8V0dcH5":169053108035,"AA45w1SrgcaH":353776390951,"ftU3aJHzkwnt":39420252630,"7D4JsAaeumFN":565276062121,"IxdlWaD4LjiN":361389109714,"E5vCOJv0Xkng":383139611505,"mfRBGosFMgSn":888672392264,"cMlEXndQnYtv":398981050371,"MayAeKaH2Zh1":953973071477,"AQEKomhMq08o":627483928734,"dAftLbVV4KCe":818422343818,"nN8e3S95q8EM":889645748354,"mQztCS2TxBW2":197209722914,"vgBZkd7CzS4b":731796512030,"fhrvK0N28yxB":725951728820,"1WQcMhUupskM":447514568055,"GvjpEUcQYcZf":289077784636,"YeHImq97CebI":428637591881,"l7bNL5UX4lJd":920541737154,"6UwsWJZ9zxGr":728102011791,"q24zT9euPJBs":197733401326,"F7mo244y3b7T":669596123489,"4mHSqtCOAEXi":984194925414,"TGWhUKTsMlet":877784451768,"MdhZadMYeME5":313390201737,"UrkAelvSA9DX":942107054020,"HCNoJkXuA1De":510050322759,"vUwzsTKOMUUT":396358014561,"oHy1WOG4hxvF":913207415188,"Ognn0yKiQ5J4":693961863812,"lhK2Q8jr5gRW":463480209503,"56Ci6rlHdfRK":669702430634,"uqIwuhQxe0JA":50513494978,"nyuF5tvZAwog":338798730941,"80KcBAO5oTSx":708439459364,"uJUyhjv31Jmq":996609547096,"fJM52bK8OGMu":775420009129,"DXsVUwhIRlzZ":343210372373,"JYEQxEytd8nM":195008963992,"0VApkbKxpZiP":517174951147,"95P23WAIBpwA":916674699218,"KO6xNy28oRcB":305037957304,"N4Kf5qbOxBbk":53661815650,"rt2EbfsvlQ75":116592818477,"YCAKJDbwRGB2":390064023369,"fzrAVLRW3D3K":188697416244,"2BagW72q6HDx":400013471332,"9T0tmgMQDVgR":481818359922,"wVMwbJSUOUP8":388773012560,"usZFXGG4nmzk":718694481545,"J0XrIM27pT3l":637523045474,"MRCgGbxGpTQL":389954962874,"qwv4xwKtDn78":217748059980,"CV53UanZGz8F":309111062722,"AWGe7z2iyiDj":947517685960,"4mnRSEkWxFC6":311509315751,"0M0pmw9jZrJ3":421939729803,"hD9pNEU8PL3P":505279529046,"V3Q7Qj6CuCUj":976245747294,"FWQD7toSoLGe":199285490746,"hPuRLC7Fz7J3":92035248982,"1QkhjLil3QIt":985545456472,"zSgJZulgp7wo":586160659145,"zvWjUHTpsJ7B":756735003371,"03DEQIydXIPi":731222528899,"6wCaYt2ieX3p":564124084874,"c8zfwkOBHaKf":491870095020,"sxNuiRfcSth6":358817779987,"pm42aMSU9eCL":274355295307,"Ecb5kjF30Kcp":523385482761,"qzxIo9P1THrt":37798079361,"eQ8Wuto9siHw":149133155677,"WLIeQ0yNvKwi":925450166087,"1MMwqPqVwRvd":48510359160,"x0Y5yCdyTGhv":973551440449,"qtsCnd3Qo2Eq":133120153873,"vU9fi6TcQKdz":512905948549,"tQtLSme8V4xF":866330405223,"5wTixYyXEwo2":892284929861,"dbTsz2GxC8Zd":249323505779,"VplxrWPw0AD6":246891467692,"QEDJq6cVuKYF":937247093402,"maqZhKKVp4pw":397764233849,"csqOP2sB7xs3":82668023902,"RsljrTuIFaGJ":608034597685,"GKJ69bjo5ECG":736420127463,"lZw1kjKEMhK9":61100905672,"HNhQtXGKzusB":960308601519,"YYYmVYIliaIL":431705774021,"SH5fnqRpeMNW":543732868866,"hWEhyePCs06h":780323086741,"Ze1xQkZS7B2k":979372578557,"UtLdhkNLsyrw":258636813453,"Q8IQvquY5Dpf":712692390648,"mYJfuW7rVDOk":819112757458,"Z3qfhJF6KQJO":235959818535,"fP6Lk9PPteXx":604564125348,"yWI6wg8ENpGH":958281126167,"X7uj3QbcqtBG":902960624,"ilxpOZERdcO0":307254514395,"ho1QO6HKIzzx":115276118648,"OFA9TTeLu2HG":993933465811,"OOEr0F8iCSXd":756246789852,"BwYCgCsWEks1":908642580602,"FisWUEvzdden":639726225587,"ejzNyrV9rhpL":82330775450,"T1mU1H3dr0xp":473623893945,"2LhLWe8QEsJe":230607952548,"Lg56B8kmuZjE":254100869477,"5IHfxHasBzah":138582847383,"2sKs4q0GIZ90":780782373481,"DgCNLJaPQfSY":363693776580,"JgXCtVQzki2B":593463675732,"fB373GuS3dzf":868991767529,"L9pxfaDINJxW":32697070717,"tKtp8G4uduwY":587135001442,"RFJBxi3hIWIW":342140392560,"5tKcAn3wcq2q":119456490610,"Ei50ljBK4POp":345205214260,"grGGKI2DiRWn":879656706780,"giew2grexrNF":951914953008,"AjBEUB2ydVJU":19878571399,"Yg5JNhYZtQ3i":733796788040,"DUoq3AZ72HqJ":182399809061,"QeP4QKiiKEqw":557329355459,"qBnLXNI9PmPC":206049449760,"j7MS4HCZK0Yv":225738654615,"vL0kINFjZsyZ":672635373756,"DEoS3jW6u6W4":36252551219,"1bZPIXA7jNz8":307092881530,"5nM7lcM30aIh":12050928980,"zNEMA6ETdVxN":140344009906,"GJnkCgxorVVN":135246488158,"HIWIQb9vuVCn":367856456494,"qHeySF2UeNk8":474003481233,"BroqtwW9Kx7j":164115296690,"lbFhxPzz6gpW":767555175370,"HWGIpiJaFYCK":966093752346,"wqZ1NGRBtL99":197415735789,"WzjaE2VjLrqM":185132943268,"OnLrhy32t00i":830844793399,"rMra8ZPbD9Rp":569458555075,"qCYhnLHD8rMF":281850745563,"484OwfRohvxF":488174164574,"eHWdUKbPkVEP":455234666027,"9gy2yWLkUdm3":818007060176,"yvNJdzzCPkYn":288022821790,"2AVscsoCjGyJ":635669097857,"3iYkfs1NeU5t":973323898357,"9m0m5aXSbJkx":987083343561,"WzJc27ZDPKXY":494342047289,"wtvIcn9MPuXZ":651411308975,"lJLSBloM9dMV":880196798945,"xqhWfqaSIVgT":276433368466,"4KAKeWNitOp0":663153329555,"qov1FfO2X4Co":731988430049,"vyy3nYU2HoaW":773169007176,"HXaXWGk4lKeY":645066737947,"scziuUCZYMJw":540652198959,"wXQErqOzJWxh":83432488799,"jSARITV5pnlL":752296323750,"wRjVKcRwtt4G":515014537470,"OBPLLfuf058K":528381403614,"a6PsfyxUi4mq":411765747178,"oXK85htjREeh":385753139454,"pM8iHLdfzQsE":850672234362,"vAdaYDhKMnZ6":426042412985,"D7O5qjfrGSNw":681267859285,"I1ou8PMcT4aF":155861748718,"5Y7xXwiEDNjh":571211793738,"fSoIe3BFH9X6":799550663240,"WnpZ1HsSWRlw":372615381813,"v76sxXGhTHvB":782182116286,"GlnZfHzeLULH":502909195912,"6OAC0xmhZNKB":306939259915,"qQAE4keSwsNy":863569391123,"CRZJWd7YazZi":398213278787,"XYMhwphlidNo":550053398996,"PizzvbXKwuH2":964651747869,"vAbucA2HdZXV":600912322506,"kJrdHNPpJfCO":585555955678,"tapB20wJxsMp":444157186717,"4Q6yk3ZO9hph":428488734529,"c0Gs72uNgVLq":645474765764,"o8iPZ1vIfBwq":632313058953,"JzyiAZ9SqMf8":908697449399,"hsjLw0iKl41R":645438939304,"oYtWPETKtxZA":119998436175,"lPjGeMCGhaZK":5779600363,"3P2kV0hHkhAg":17329442863,"fRNXSr7se0Om":770743498892,"PrEmyCaaj65X":960636123875,"YJAMNlxYN8KU":114670655101,"L9jvjVYLsj93":858448687621,"m8WeW20hVTDk":519696632990,"qhOy59zs6KPE":97008859173,"3KHxE9kBFnx7":528672639983,"Iest0ZuRgmrr":304295376056,"HzIBu6xXC5wa":221010524620,"TprzYfq9KyKu":764400282694,"FWbYNS2Fecvs":560583269344,"s4sEuEzyHUjM":610282078538,"D0Dcf5Dwj83P":39805192159,"yXR4AP5DOHlC":778650503953,"1UmkrahDVLzu":603079923190,"cbqvoU0SgJAW":952998534392,"aLyBlDWdtQtr":996150581473,"rlYjt3elxYMk":489300827470,"wbYAqH9zcRZs":114749732042,"OSQdq0ZYKkht":463523212974,"rSbJruTgInCt":609196227518,"1ytKKw9nxBEC":385283699470,"9YcMbfxElRlD":345870099277,"r3FreB2YhBj9":379180563257,"6Ud4c1b0f5Wl":258537362086,"FCe1Qm3UP4WR":274488188625,"ZzXBinydAPPV":251340144328,"fW0P4rn7fUZ0":791399201831,"NzE0QkXEEZvx":544540586999,"kWi462EYMRqf":708184921321,"MOI7GTdwoTym":625555957700,"BiO82uVwrjor":148316753533,"9UX6wCXyYHvL":630246221935,"JPxr3xwU924C":478613973471,"WeyPPNR32HES":553294779994,"rZKm3BJPXkPZ":191911677959,"xYfccG03v2XD":308155149208,"5ijmWzA5bAGf":965290998607,"h9bOh9XXMiej":849283759579,"IJalX9qtdpK7":415218892933,"P3SNVqFE4NxZ":741344594446,"brRTnwxW4j47":981893167975,"90k3IwCCfCVA":768329669653,"OhGnPVLmVG3k":455487705204,"glnfsvMVgbcC":768151674333,"bQfFm5Ya6xYG":647865930563,"cH6l7spj9kCb":763994716085,"TlSx3KjaEn5Q":717558044199,"PJpzTeRV7zHl":195066269901,"c7AIiOjMGPXC":34766301417,"e5DJMz7VRnPx":833302324658,"6LUi7fn47tPT":964288261137,"fAu0V5eulxBb":811576628514,"yqwy6LHpq7Kw":381027622444,"XgGhoxwYsg1i":286035448861,"xxk2lSGfA4Dd":168988200252,"GQsD0zHCjyIz":1802635263,"8LnOvlAxp9tc":264982421793,"1Y7YDZonN23a":647204848100,"dRFH2GUnebqF":840093525446,"48sJuWaBtcNQ":435995785074,"Cr6HmtitpS1d":900565280219,"bvAokkAeud7r":363493306504,"j28eWOT4jBhu":420769313060,"xCEQTDtukxSQ":399988492407,"8vJhNOElZc4U":479140677855,"nwHAxSxZPj8n":372389736691,"rMWiyZ2Eeqil":783548861312,"IJITPY7mcsBx":347400483927,"RBmcGcMHPnzW":903183249112,"EXF7IM02rDVp":179171714420,"NUVWiQ62SbHS":387491207110,"pFco9uuIJAv9":910133272248,"bMPq0l7PavIZ":783931935677,"dHPE2RUlpmDf":70069895833,"miR6gMuX8SKl":669983506282,"6oUfCkmCv0U4":697747219258,"gylmfr3s8hRl":20103408273,"WkcWXzcy9IuM":787053927780,"kBmKjHs1xMgy":936714144594,"u0HsXjK4j5Wh":529928114230,"U0QD2wWgPc7C":159453503463,"LiMrPTnWeuG7":997116377616,"RsofNWMfhZzK":861562344417,"5fbFstb59T1T":741438349112,"4HFMi3UCkknR":621471689944,"Mv7ZifQJDmDl":515544229396,"jB214iNHI3TL":130701748944,"UEppTekdGNxC":468367121902,"SD0zMdDtHj9S":735697095525,"MpvFAYfWtPCD":609960203508,"e7wQ0tQUHqqz":524279584121,"iweRzl9MvGfq":118579813646,"CfdvmAD86ZrI":211391960011,"GKEOWR6m3E0G":525537714238,"nSZbMgv5qT5B":275372733735,"I7k1uifEWcdR":588107015371,"KgREm4096Mlh":386997839064,"K7IrQZkHa5VX":491728489945,"Hb7j9K8oCYEz":490086399948,"DXNRCypCGmo4":263124457966,"eqQYLc19rGUU":751758953999,"yNIh1Uzo4XsI":95879765867,"44uqV0uUxD27":700186443210,"Os7R6ekGQcvb":396941755507,"uxW6Eje2nSit":247969962730,"owZN1nYUgb98":162690411334,"1J64rWS2jNl6":976842782813,"RzYdZZmPGGcy":655425935169,"zpFQTjyTRBiZ":35900789728,"dbLiCVwBoZNx":767293761604,"UrCWjq055dAj":295296924256,"nsS3DYhcaeUY":390154817784,"WvyOQHXBSvFs":968638029467,"8F8SaBf8teuD":435603383248,"l26MjyiTUgON":162233624470,"vRuJG32QKmTU":822341896698,"uNHlOz6G7WzP":639609843512,"oMuuXRgoOAqP":314201993681,"4yKE1i41tdYU":786742874750,"HDozBhzuluas":640673814630,"BVgLry04lawp":947330695140,"fD09TmokvRnq":144869385530,"IqAefII3t2OX":993400181953,"LfD5VWLvseey":793640809367,"Zc4tiFsi9kZt":648576842114,"mjfOkuFwOO5a":580516588260,"cGzhRNrp7quv":644795299321,"fIWptB2yI738":343753760618,"AunBVqQ7S69F":331578368115,"fW0twGX8OtrT":458001383635,"n5bWhPnpPNXq":206662235687,"1ly86syKf2od":530346769633,"sV4nBKM1roM4":488695070432,"zFB15zxvLUOn":544905882528,"dT2erzwF0kxR":882050099558,"jU96QY8xQaB9":748176964305,"DmKPFFUA4p3Q":134343284637,"llDR4DUANQ8g":256798452358,"T4wD7INvvXtW":448561557932,"Y7gBtDYXPT7u":41029014109,"B2hvD31WTjCU":851643334252,"qMIyq3wPHuED":226943718737,"S1ExZzLfJ0Mq":851720624325,"WMGVUl7nu2NB":863108000142,"jmYqorW32fh6":8602447864,"ek6jbEZXq93b":194545265468,"3hVlEMJ2Stie":154051501654,"Hv3RFWykwrUG":92211128709,"BWDrI19w1iRr":317896207934,"st9dHXmCXXvt":406163219072,"RnKdJ0jDnBQG":592659081999,"bSQqihBpO6QV":644052812232,"YLwzNq4LRHab":359200317505,"2CU0YRq75kc3":860028552011,"H01Q9zEABIor":972118464654,"ogXHw4f7hq1R":390635126890,"WizhEQeKp2f5":167815197380,"0Mrk4Q4Jl6dR":248519080552,"KVJpLtvrXFzq":954991296476,"cY6G6IS0ru1o":188237907716,"2Q8eu7rGZ7uZ":685604032474,"c8QXG8yqF5Lf":437047652052,"targu4rgD9Ku":972370985526,"SW9nJ6adGY4Z":633026454276,"yKedhCobkhXP":894336630876,"vedgprS6fIR5":828419216796,"GBmPeUqgIcNO":181115638440,"APxLPsj3GELv":214962657357,"lJd95LqHziio":132606382013,"CfLkJlvhWfvY":909514689768,"vguLJat4sAqv":655827048817,"151iQmW5by84":291421283586,"nzATnHWTumQV":883576020924,"r73M9BV8WSHo":369406817421,"OiTSKG0NKySl":302487775233,"3aw0VofrPEpO":869591001352,"hLHsybimkDKi":385242013296,"Jc86H8W1d1BX":584104276490,"hLMP2MTRhJsy":781566912637,"kXAqbrwzoOpX":547245912178,"84zk5ZzZEtkE":914604872338,"HxVaAB4eErbh":541634351164,"Nu9hiM9OSBMD":336902535967,"ZvnZpjhuUfd2":361654328708,"1nd4LFhyBzRE":676978286908,"CxKmpEDqsIz1":9624032123,"nd30H4vwewkC":400459026532,"jyA1KtdXxtCC":616229085191,"kgianMdDk4ua":195522558745,"UAZapWMPecyv":569255728291,"2C63LSz3MhBY":375362652910,"Ti1siyDxFihT":318104484969,"XCV0SXmuu50j":197643395432,"uQh5taRsYNI5":331222806387,"b1I5B1m1ykFj":520557487569,"2eMkXTIworP4":325749601796,"ZnSLDr96shmA":108399941221,"xYP9o99iX6BL":618737548893,"h9Qg9QzEFjsE":169847493099,"ntHq8pyCh9SB":300300914180,"s3Jt6GNalFYo":622224033977,"bmjblvyJcH8c":565174245790,"rABUf3znuEVd":361870225098,"BeNFBRhGJyUY":945685956523,"PwGPvHMtmdvU":719849095904,"1lmAbvfTDuSJ":93354647536,"8vQjA7Jut37o":393920842649,"tstG3SwV7Ier":456844901579,"wYN164uUxdaG":3553927984,"0r28yC5sMX2r":975626895979,"Y619F6rKxej5":568702458651,"xWwYwl2yQiW0":939202285937,"7rEH2UiiCQK1":176169033796,"SISoYve4PDDk":625516401157,"gcRgNviQwOvz":167262293975,"oFXmv8BJ2HPC":916149825864,"E6r5VJbl2IiF":407620950943,"3xLksLcYwGHT":25875671809,"OpgrAgpfGkIm":981928953475,"LpyoM7j6MUM7":888755242402,"m0x21nmbgaLp":359100193676,"srhCsU6TLUqh":485162695615,"udDpvtOupD72":864988202966,"OU8NVKXjcG4x":393028813890,"bUfJki0kpq0g":181074937789,"KmC3Mv03UGmI":750270932249,"2Aae4QrygYEe":116356952134,"HfOnjVoLrWIg":445213483840,"mvdGkWLBPEeK":116539212733,"njirOcUAYjf0":423370944772,"PaXFdYF0Bk7Y":429038936840,"9a1FYFF9Z1Dm":892586998765,"5iyaSSIVlP6G":238283448552,"pIpnx5i6rkuv":83489392956,"W6kWNAsExMOn":363162063375,"TczCWxem4Kee":26631604189,"RIG8ZJpcPmx7":82417367895,"LSwu77RWG6et":365611706496,"X6m9v7iC4vCb":318660053487,"lAU05t516Cv4":834495006804,"0f9RXB9FGEqF":154963278953,"IL2QDOTjshcA":611566291967,"p1g0tVES8Jeu":350297168330,"7djcHsLxAuy0":620366027686,"DeWhHF7Xaomt":807330906216,"9RkfJkwbuMIj":477199656745,"kzDVZ4a3s0rE":211447499251,"kiJlSzSeOJ7B":879842794760,"jmTlQHFWTMMI":952949764037,"eHYkCuyXMaJ6":446630910838,"FyknZIb3ArDT":144917189192,"v8hjj7TjMgLf":840559904298,"ljZKtCd6jB8o":553609076906,"sAHhhnI2Nin5":102163003489,"KYpnRBY8F5Nl":46425902486,"S6juWGLVmBQK":171425770781,"Y2VPQnbLGxYY":290512156455,"20ejCz7lGdHm":456110497634,"XGV6sN6oOLGQ":598029461618,"yez60j1FmOLd":663322571672,"PueZYJjpIeoW":515919561411,"hD5MG7oVophe":434253407837,"vg1n0a1sCSVV":162989827216,"Cca17pQ6mFQg":823244045950,"KxomKo1WUp1d":888462366052,"WevjP0JDqqzo":475926978961,"3cNnrm7EWYMZ":542518253338,"xMB5mhmrj96S":304819761226,"gbkrUYyEoe9h":969763005749,"EDmEuR7vnonG":867832396658,"0UBk6Lpwh8LA":477753503902,"szcvgJsFysvq":194932139772,"HfAOR3sC3tT1":280174728199,"m2QDtVZkHtqs":612656823204,"gILG4b7k4H9Q":262833406950,"vdIOl5QabV4d":814669140192,"XdeTkmu6pb44":61191973742,"VN5oo2RMLB4N":517078527461,"iUR0IuloWjm9":783512917563,"XsBc9lCKIGqp":664598535915,"mFVtgrBk0LKV":946253448433,"n1JyqnekWyGR":474204859732,"OMunYg07XELq":105833229424,"Ed3JwSvVHo4g":910991552951,"7rBdgOJ9wSxF":833430031075,"OymCUbvo23Li":399574233139,"ikhVfMimsg5V":979250576596,"DXj443HTpM3t":165443440741,"KevkXinRdJDn":646192254247,"z54erVe6gNjo":872315780225,"sqyFaekQXWn8":238440790528,"IM9EdHAcrLI1":905974394526,"HcYVFcxbCYOC":393556403106,"5yF57GF1irmP":451614448730,"D9RYx6AcbdcA":158038382020,"4nuND2J4QxWV":826834710171,"CUPuxjZxlVFB":170690708501,"LVeJZzoPYNcn":886527013647,"TYSe0aEucuXq":718581526547,"O6ab1xy5KxZM":42676351887,"ra4UPiZGx6l9":435407549095,"Xp1j1c8DtCPZ":935654534233,"dOdF6SeN7nPU":304487073891,"ZyTrWo6wiEEM":962529835891,"I3ZrYvN8H51r":825645394083,"5HH8hMpFOAgy":521622903060,"mktlUZYUh8mZ":618076559630,"ZCDgHyy8dOqf":701200320410,"vCeVwgKChNEc":63165231082,"iIbjnW5917Fj":238905477130,"6ILtnkSEpvu1":820357320426,"MGUx4IiJqMBe":304682478548,"HfOQPtLddQaw":632863653192,"hJUQbQTP7qxr":78422266206,"GompGbF6vn1P":987707094953,"Qr8PcPvQWe23":850435629934,"7L6j6ayiXmVA":742407608078,"Ae6FxrC0OJiu":849527435193,"HqmEss0vLjjI":180127458307,"bXa1VmxFctQe":27987953178,"zIB9HgFiJp1p":176288995243,"4LUaPlmUDiEA":707938854085,"49SQ5ZrsF4sa":90748024844,"NKefp29Y2clx":185584880236,"qksBmdSZlLZf":581903266966,"b7kwONm9hooa":844884339179,"9hHg0VCAPChS":516200478854,"B581um42ecy0":31374369806,"ono2cxDMvrtC":134490060976,"QbNXqpU0YZ7i":714691345791,"qf0YB20BUvrd":995373510306,"0bFTbpAOFiWu":642234677152,"R489Go67jbzG":200167116195,"8TPAvzW7I5ve":796260832357,"gbd0WwHyUDRI":890607710959,"KvUlqgtEgnjm":911548259495,"7AcqGGhZbN8G":817691066384,"PAxFiBLbhFuf":276824704030,"PXiPZoK7vR9k":527085430981,"o34Aub9n5S5x":430069501012,"8JNzaOFu12u4":260519867062,"vcfUM8j0QK1q":976360847633,"cdO5oAvMnFAb":747350796229,"3Q20VD0En5KS":143946060245,"43neWzk6EMed":784634526336,"HIGoZjg7m63W":579241494347,"cAHYHprdyEYn":57454902903,"Vmmvqmp5RyD0":847789564763,"2LW5PtdgPJGc":667869782138,"r0xdCWFOr7Xs":711823241036,"MiHe5PJQWR7M":593833820930,"ytf2BcqGAFcS":409849487398,"XvAHwAfp9wcH":579699252076,"H2QBjc6Vy7Kr":996797457733,"8zltLUPQp7WA":228269252831,"mNoDpf3LEDWS":27189578677,"aRkN6UwldW4a":43027862036,"EBejkCobpC1G":942851666890,"pDRg5gb7wVyK":151588547472,"FH57E68Hj4Qs":37411955552,"9PQSE59L6cpN":180544601370,"hpCxsXg8rXKp":907978394970,"jsmb5JKAFhIZ":962356363275,"WzS76AB5XWFx":783020706304,"62ircaE4hRHN":919300564876,"tPj53wcIp15O":341895919438,"ZmD2ieKUnX0V":462429835266,"bPMah8TJmuz5":567644652874,"c6QlWfJSHj6a":520731822600,"UJdnzVCbsIEs":481604998679,"loLLlQFAL4XJ":304370134786,"TfODPCdk0YWY":393404662022,"Pocekq5Vwm1I":668699039127,"L1MxVUIqglqC":924217868810,"4ZJMdXe1W7xj":744388723718,"Cj3uyhOspWdg":339175054294,"RSFXqMy6XGVd":148544631191,"k9qIMpSutpBn":808512422116,"dQJtUKk4SIGL":327143086896,"P3FcyMfJBLTA":862196383941,"wwIrBIX0vtRU":604408517929,"g46wtTeI1O1H":483976326460,"Yg1SnMvPPO79":77599042348,"dnQwTu4olpqJ":626134375385,"p7WVAdzrhBcm":991189716820,"gaEO6NSn5GEY":855929354471,"XbD61gSGU9mN":369714986367,"lAcNTaMqxAIz":718178468606,"LJFyL8h651kM":877580174865,"ea25wg0SfB7C":892508983589,"ujwEGk62idi2":759201775032,"hA5xbjVWrBcJ":336467603089,"LgNDB0f1w2Gy":891852210539,"DUtV38PUCqrn":748193962319,"uqL8chpGaNfI":776683932958,"90GoXjj99jgn":176469281100,"VnwgFLXTy8it":733006995410,"7u6NQT3BSKrt":582437385225,"fiQoWezd7yHq":566737034844,"wcHYjA54bmqH":251689636959,"i5301QqeBzCU":511465173848,"wtn4KXQ5SQ83":276275377429,"b176yw6hmwqB":600852012306,"83j1K52NXCsh":440652536722,"nxJ2qQVeD3Ul":692072592581,"BwE7c7TbJLHx":200402026395,"zajDdUmUDHIs":93112733221,"XwEPytji8DZR":955690153569,"xD8CccgxQkox":621798150971,"LfBZLrgWbpPT":143286230104,"3QviIEZDsh5j":522340591479,"3qLQXlePZvmX":501822205882,"py63hqdhRs5t":566800819471,"5VujqaWrNhm1":801905176440,"05SR3fIj1cF8":626967685992,"Qgthi1mdkvDl":924353145014,"ifXyqM0k6zi0":488694519567,"HCW8Vn0U4bNf":702819427153,"IaGJ5KGnueI4":739021136188,"oZMiDaIx9oP1":919550107438,"xxroyrNcIpSI":138738170200,"vGIrV61soICq":185329291935,"4v4d6A1DJC88":519094105002,"QsNTOj2nSYep":411302970453,"6CuzMyKFhXPC":796502553600,"Cat2X29WAs2t":88298735821,"idgeefyOx4cY":143273912464,"nZsxPQYlzLQM":130730949667,"gaYOyNtbu0N1":16103010389,"6rhVUt7EA0EL":1417912511,"4ds6sboFW4r8":476337196412,"PwsUEIreICti":523074039904,"54na0RFCIh7g":126904682931,"Aw9GL4fbwpnk":53419497729,"kwGCckzFclEk":720484945985,"8UTAoFTaAZk5":360626552651,"oASfsVTwcJL8":535201087165,"wxy4uB3AUgF2":767135969703,"ruUGVtjJc3DA":198895002735,"DgWhPKzt6aWt":566022838648,"stwTAmF2LojP":353887159947,"NU07easx3M6c":688280914298,"ZlSQ5MBovTqz":492018234107,"DftOFtSgeF5t":624576953296,"HnoqiW9n1J9q":159131567324,"esDfWu5iC7Re":744613932546,"jGcL9agBSALP":542902523451,"phQBHNTA9JB2":151701686371,"o6qOzxwrupYl":99306035475,"pHdfW78HUyZ2":78838629753,"kPb9fSLXPxN9":449996750613,"e0KbyJ7xJIst":424143936005,"xwOfWdDZWtWA":807880865884,"tjZ3HGMnZ6SL":417088989401,"Qf4n0BqFNuvt":862834546276,"oIed2WjNLBAx":32942720054,"qS9HpiI6Wnrm":289053407757,"8AX8LxYxxUJa":927280579791,"HYGdJbEL0L81":967949754868,"cDh1XylpExZ0":929698306909,"b26qtPEJDnsn":984555394431,"zpkNgtZkQw6W":380838668598,"m4PVBpwJAjcE":281441616320,"ZK9JzPVNjOxs":622908391543,"Z5xY93OKaQFM":171534765587,"a0PG5EKdzoKC":941350644729,"XIATha2bxDV0":964000408233,"Xx1kZtLg769v":268356262626,"u4iAy9nI1WxD":886722433575,"Y9q10vXmdP6x":372209776471,"SP4rz323Tcfe":830002606267,"EBHdFmGsrhc8":575547449801,"WlDrCEnXaDsj":731387026259,"L8b7mFeSXHXy":939849347796,"xc47TapCloeG":460581365953,"X5cJ7pt8Up3t":353032365085,"YOnJyvxYRxuY":851128980570,"pw0NXg4CdFn7":328021748584,"tA3hiadZpAJx":457200786226,"uOWUB6nToEgr":67337742363,"j66uiqLowibn":273557278506,"NMfQUd9WYtKs":462725886341,"Pv5VDDqiEXsV":226261597359,"yM77ASF97yzs":24542944474,"HC5fh05z5fTG":973343406348,"RR8o9NdHNwvK":340574381830,"WTaeJishTR2m":91012986707,"gkeJloZ1VhIU":224703031350,"xMQ8x8iJ1BeH":623648756563,"A6qI8BCH7kGn":709904112262,"m9K7610o4X5H":750579607534,"S3RWAXL1a8TK":347126646757,"A8bPNdJUxfI0":92775636468,"bsQT17vcWuaN":113759893400,"RHu3QFKaWeHe":599606487745,"fAkfW6RKGOqb":361388691378,"gP2lS9p5vFcB":251679371786,"LtR5SZQPyjNQ":769086089965,"7ynLVz2sey2P":904164177145,"1m7On36FjHtT":152670856515,"AhYVVHO7Wzd8":126583348414,"u1QaPFme3YgU":505561304800,"Ay17mfzDZtzN":308575673992,"T8Bq1fL88zHF":930549968618,"Z9V7TBWeVnRU":378403290344,"cOyhUYmj0T5o":695861045901,"PiGOtBXfqE1g":797182993981,"e4NkpVOYWDxf":853716027610,"PbdOeAEmGIi0":154942241085,"LvhqIJe5VzP7":633916656761,"vefnFzDkZBLu":745247006722,"GKoRLYuNXCHk":883577289000,"YHZmQ8odHjyj":289461189891,"TdxjdZREO9Jl":680526804181,"2j6bu7NhULIs":837751684109,"DMxkZCm9WXgv":31679867786,"LGjKWvZesmt9":315607133191,"n8RwM3sqaXqQ":991725089771,"Ib5kxdidF7sd":392637250274,"KJv91LG526Rj":536151117952,"pIqJCegflMqe":171400362858,"3ZBE3G4TEYJD":315866209484,"FJymQSgIfgNz":10459677715,"r5GJanV3pq6g":551441486281,"CafFuuQZ6yPc":652931609598,"I7n5zKekEbOn":619740363013,"B7h8i7dY4NWM":310893918131,"Bbsb3kSjDmHE":894278038286,"B09JrIa0M5ij":476451550048,"UWftr6slGXj6":790964092514,"tAdYeSjp7sT3":375464929335,"XphjtFNpJc5N":90527638378,"ZXsaEisFBLCB":87813089749,"pLbjSKxEO1NZ":169604833402,"2FR93GsHBMAs":489288147645,"0uXmFfZO2Bjc":118445780289,"BezkBQJwXqkn":690444015433,"G5eTBQEqvh50":78052727427,"NbNSfEOJWtHS":757370457668,"XH1yzS6MpzlG":721036789730,"4FqJ2uvihZaa":244102231158,"vw686vBRc84K":343377814783,"LolXKeVMUPkJ":375172437304,"uK8kJkFmYLMM":116890746001,"ctcmvwy1k0Z6":768200619609,"1hF2cWXsGc7r":368192253150,"7DiY1o6FOXcF":116431740847,"iV1YbNXfeUaN":729177172990,"3Ho7sboNZcQw":497708809311,"eOAMop2eyoTd":381597259903,"0uBYnx8jqA27":183606715340,"aLztmZ7zMj5Z":302338440930,"qAPUR9ia4PhI":600082434608,"u0s8HZ7Kgakh":547703144392,"MwlG73LwxZLl":373567843083,"nNIKlRTjryGi":580029365304,"Dbmo6fJvoFXU":399865542557,"gQL7xiii4MnC":349469033500,"gFkFtvKvlTbW":370724691086,"6f2Hr4BLcElj":536993435715,"NNQtH4oJ94ok":745641199851,"6fQN6lMIdnXR":439617602779,"bh1LO3WaTHn0":838717763518,"jonIoP7Z6moz":192476945284,"ThuOpdcF1DeV":96433410625,"Q5kCAU1SaauX":218717741126,"TXZ9E7NkCJWe":303784808172,"Gya496ZGE1sA":800754182914,"HyOntqOjA91j":175297484586,"r393hrQfsRNS":29301065376,"UHX386Gi86ry":809362278797,"mXPbHrzPnXuR":751035790267,"YpFkpKLUGZ8a":837791997392,"8pT6xOnimhX7":57330921276,"Kgzlgp9YkhDM":780480947248,"w2ZqigGMGWJs":907362081692,"LLHx6LtcJE24":852752290553,"vo3Vxe6cePOf":679358971506,"huWMHjnfFmcO":800263885387,"mgj6zWZHH1LR":202424359498,"ywXVlDTlK0g5":835049798843,"VBZ121yzZc1D":90602974255,"EPu8Dfg3EDdm":608444793934,"Ydj2bOYTizf0":277511303449,"5YzRmQMznfS7":332454735699,"H4mZmzEcWCr4":284597627919,"JqoloB5GIQ6a":603907855984,"h8VIc4oFYLvy":843325310219,"AmG4Zu5181eU":682834571119,"0q24DvZsKmyI":29091512638,"b5n2OLYnJkYT":973992398657,"0VNSlBXNVjA7":661451223114,"EWqGgJjOUrKm":531000614546,"BMudiUPVtgFm":127926618593,"Wb8egEJqLbJM":833404023330,"AovKW6LEIcN0":720906257308,"BDcxx5OmuD3x":732194467299,"IaDJcjAUAX0o":52667025727,"B1b5BOODeMjg":241741387643,"EBxgs6w1tKvJ":408758945730,"WnnNES62Zx4U":248053480038,"kJGKNFCiLj2W":171367029352,"IaoywZqsE8vL":76663484457,"Ua9GVXI17dNk":300332169423,"IHhu0u20bTY3":707278477373,"oQfrO6dbHdlI":953490350785,"bh2bUbaOkFaI":943126065396,"CHR4OXMlKh7B":562519341468,"eG8TKQEt5xLa":463342834148,"20jIxpQVDu09":258532660687,"M5p0UsezJ65H":695531289278,"K3crVJGRwGu9":835864401379,"zH5F4asenong":979913250822,"snPS86e4vXGu":908700572923,"IjO4YSVXGKbL":994151547056,"vxp2aiuPjwIN":3828366023,"XZQFqMLDh5wv":251835868501,"IhIrXbELnx9t":607573300396,"jBNzjYYFYyAj":549241810464,"BtquaTED3Usp":952356853242,"XfppIsprsqWv":287641880158,"cYsCx0ozW9S3":455763682152,"XFbrBHgGWFEj":659444889359,"7uA2djOaSfME":907387224279,"Cig7pujntv5g":977247026468,"eB44ylhxDBYP":865967973717,"T7t0h0fnIZPc":876056460421,"hkie0AUqPKy1":534968030779,"fhyQugyjGCUA":50431564684,"8JXaBGFL4zy7":230090577696,"6abeo4xTE39D":99884031553,"qg1vlXVqDIdu":358669909713,"hNzQKjkrQUcz":666005677170,"TqRl9PYMH0CR":240469389898,"s9vtmT5tHO7z":29029100777,"itSgMejCDCAL":650040435911,"pyq7l4xE57iZ":545353975731,"DMYLdN1FldXY":242468603481,"utgXHd00cE5Q":507014340483,"B24WQT0JtGwU":656295926855,"4vdgtX13edh2":292045499995,"F9WehCZkVNVB":324612144858,"ieYJZy4Tc8j3":654783189680,"vgBCow6Bi0p2":274348934274,"0wBFhBq1Se45":940779926915,"IZKYXrEk8Ego":848590504382,"A4Gwr53jpgnH":487351449202,"kbsrCRERQayR":497525747863,"UclcRUvCTi02":431855103009,"d46DGms1rHF2":193182174156,"vZSEu8DVEDAc":337398549780,"j33n9s6SvYpd":682813315986,"w482aYrHsVlt":453335705543,"FIB0DavZufVT":404390414587,"mrc8RCliNu0a":130242846758,"GQVCKsO7Zy88":328037825070,"XV4Cak3y2lsl":59123541457,"DjYeGX2F7eqh":370418681587,"Iz5SOhejJ28g":727296180459,"CUwFbAP9NRwf":398888810715,"UrngPbH696V8":752719310418,"xnsp7GtS6p4F":536077035817,"zB0ceDptaROb":558272345927,"maja3VPlA1Pa":818320988165,"uivQVY058JMl":350328439237,"WHR0RqOJd36m":831816675033,"53SMRd7T3xDw":416527660473,"PZtBDmdxzR5n":621211729761,"ynFcRhQzUqEj":686813021519,"sAMP41sNRMNQ":382842482785,"UroqG2l6pvot":173590681642,"yf8hMKwWMp2q":586975885733,"lfrYPB95ie9E":680890482880,"6WfyhpyWjMpi":368604929104,"AZ1zJnpFJYHO":913291767674,"TrfJBl9y6Ujx":276331381026,"HIJd4cA2R97c":678961908601,"SRPZyYqsezeu":820616672685,"0ftP2P0NUs38":396772788447,"UPtJN3Qfit9V":859034820508,"AHKgNo9NzQHE":407387779369,"PvlAIqDxPevT":32342966233,"jgAkJRWpsnq2":328435037282,"9ICUCCXwvxUa":913925174281,"EkeiXID3pMrl":130663993362,"B44g31eK4Ksy":88113674997,"RFg9vmeB7kGz":509683699760,"FP7Idn2ThUHC":200626296589,"YyM2wbLi3Z4A":383733666888,"7ZKQmD7zjTID":837599330123,"7EG3ZzgpZPb3":774406611952,"znkQZkoEPVKl":213081097806,"OtnoPBSt27HT":915715952573,"Rb71Vp88Dh7E":674511427021,"k1Leu7dnLXZC":691357935324,"vfv5U8ebdh52":303673911845,"6RmzwDc3dJAh":156744116889,"PxJIVJlcCUcj":247030788527,"mBbSk9s03lOr":826118810649,"JpwhpJityUPI":16152536860,"Wcm60zPJ5Ufm":416381309654,"ryjYkd2ASLqn":98132624560,"6mq0p2QYg9uf":39840865743,"CKjogTjS73rE":434981409028,"8ChUWrMfvwq1":322015845614,"6akbIvJYN5TE":755795100443,"tQbNAjfNODS8":825014782373,"5jinaRP7t0dj":360984130724,"jd7g61HT7oof":896769138855,"jXTnXYNL1rr2":867820273202,"7mnQwzOvodX6":524177853043,"mwoIWA94RQVt":238451091566,"A2Dfd5Xo6Log":710215011984,"pSzIzXh42F3n":585374214639,"HG8psAJfcBZ9":186875654806,"NPZDU7nVWThM":182407375914,"pitlsp6T6vr6":263773023303,"ATrQx3i4PPq2":695837861771,"Fcop52YKP95K":708430758600,"bLijs87BTO4p":857765189141,"9s9miHtf8xJi":365617653818,"qfSRsGKynuFM":609456886518,"CxRAGOCLs7HF":974691657464,"bKKYdMq7zFyd":453023469411,"Au3YsAxzObz3":271900624360,"Ygttt9niVNA8":646349994594,"rhz1rDIUERQC":326714529231,"W6yOWZ6yIa14":961698085446,"K33IuFqYFxo8":757923086415,"YkLHY2HAuiNS":189492878464,"eiImSSZopjQd":248787618017,"ZnEmaHBWHwPt":625205258860,"1swgvDKGi8fz":350304374423,"MQFWVxLnNJ1P":988645924981,"kSCPHzOGxg9z":581323215044,"6YDzEJz6pkSP":941452192566,"za9PruX4VLM1":695098945239,"eCNBzCLFQ7Xq":832158573688,"obQPCsvLiaT0":141392229457,"fgCCnHRQRPwb":939649930352,"RrwVoCL03QUU":256521536431,"oymVoeuTbUGn":569694327403,"cldNQCCfpDwj":580858857302,"LWzs5rVHViV5":205429648757,"dNAJ6iXb5inx":782658154805,"nzIZTxmymr9D":192865103105,"Q2poGRjiQ6b2":840552363972,"9F1M2gaeVQf0":625098566786,"Uewu85d7u9js":167896393995,"e3xopAgpxrAM":704385810048,"AYyDbdAsAd9u":784982672883,"8m59BEyrprND":3368884882,"veDL2B7Hn6pJ":777488302156,"2Z3swXIcBG0C":175416318449,"KarwrL6vJ6A1":520000541292,"uDwQaDUgRxJt":721278017897,"Z6Z1JvYjHH53":378808247771,"u0MqXb76g8bu":527989431053,"cZoACsK8ted5":779500413156,"QmR8LhLxVg38":712298825802,"ClQ89feh5jeL":406995977201,"6ZDvMnRKbOKA":989012148814,"gZ88jb03Flb3":395249286333,"VsZSASnmTEXf":252610796906,"p4bsCSh8s6KN":500582306572,"epZfFsgNIw8q":119124462493,"y8DYTqGI9q3e":469259110418,"8rmB722EapZE":658949838228,"vKL3lNSEaBPo":783707458788,"kddDBdSOOBQ8":328097995398,"yPVuPP0pkH4r":125879355179,"pInQPxWxuPJW":361060420323,"h0G1k9YidBRk":798198554349,"nwYAlNtJ5GmI":530769394051,"4G5NXzv89W5v":795658726698,"DFPvyMzPvb65":332310708512,"4M4goUVt2AbC":573097904215,"wo8KnbHYu2A0":803792367423,"q5tWegqKWn6O":112411290451,"EWTGKu1zutfq":507618803651,"I5FaDB7eiqNm":216262597744,"FFKp0Gu7H2Zw":109487089538,"g7wts0JJuIXJ":149204958742,"2oTSwqMFRFXF":419695959283,"y3DjYL6pyAEn":941678363888,"8fDHM0BWdR5Z":109173690411,"ltB16N5lUWfz":791412584825,"NFHH40dhowSq":97253054605,"mMz54Wnl7qGK":362297594065,"klzA1uYHC7V0":144193144267,"ujrlfYlaILUh":416018460654,"9uIghegttMed":446191577646,"DBQATq2EmhSm":612461676190,"HdufjYz02EzP":730334586888,"MmPUZ2nN0pPs":109283103147,"4lXWgqZOsXe4":48295055383,"yYPMUZI6f8rv":470481986251,"xEEOtyjqNfBW":798468331803,"cX4hlKGyxVNF":2371031488,"qy6sH0IFdGUq":309634601454,"4UzsWi17Jd0T":723129512011,"axDignfSXJKp":860371348832,"53UfjDKydagU":80998693600,"DS2CDCQgl5At":974392121579,"p7G1AhWuWroJ":921072788889,"r8SCTqM4m5z5":63828038723,"3XLo9khtRQsL":726799067910,"hod9m36OBNu4":754043500242,"t2Irlgx3LAMr":25190020110,"u1bydcdWObnm":323257548061,"vRMJHgXJ7nzU":384536344844,"P1PvI24kZ8aq":710920224774,"JMeeEjnDJhXP":814388955206,"TVN0bK064sgB":759768777072,"5ot4l1uYvuuK":19504041328,"Ey68GCVBvw9P":556343514437,"5f5fbyJzRPky":21916759552,"u6NoBEJI51Bv":773167372889,"cbWfiPNexMcy":438894818239,"SiHl3XWFp4tw":356443032014,"nNsghi3h5Coq":340556103562,"KWuyBYhjE7jO":825116749731,"bkbEVA3xaTcD":271009395276,"RcFzEc1AxLx4":373350484262,"rHsU0gFNM77z":334928544336,"HfKljlfRvRHo":854357482876,"sGN25kmO0Mp7":970098776444,"gYmPDHXaTgZ6":231911825059,"gP6DrAOycDSq":203797855724,"08YITNxvrpmL":196391327610,"VnOPz5WUviJa":642008424577,"jvNqrQkZoHGD":899623727264,"P5Bb6hOWQQ0w":543007699368,"pgzwP2Enn67r":665226405393,"xEq0hsfTRrvc":164261858443,"Vf4VmkNzjFXB":367639527949,"ARGymMJIwuZo":625112328819,"3gmsKXF5VrjZ":429909349582,"AHkHw6Zkg9bY":948792138310,"5slaSiJmKtTL":636045776745,"sHry5WvEjRUb":891964480038,"RgWYnUdBH6uU":676795548940,"1eku3dVC3Iy9":119383537297,"Z3BF7CKh4KcU":206074688076,"PPGVe2pe0HPW":355004241372,"8KijayLO61js":763557096009,"I1P3v22hBGf4":66050659980,"1qB5cmjuH8Ic":201127025635,"JccqbFgfn6xs":633534237212,"IHiuzVZ0mPNU":942132373402,"GDvVXs3gNo2S":338318391540,"XFSWJhw9JuF8":858649250543,"CP4dsjsNqSJe":723246092087,"c7lumDl2QWAX":478953721234,"006itrIlYdjN":23587759626,"ORmwl6ZUTEZd":660828163405,"vwCYjadqf97o":140182882330,"jjYianf85FwG":255991510646,"T7gs7i99x5j0":384630070999,"xLYEL5VMd5II":767551256537,"yXLckbmXMpE6":299899204409,"5wRzeG1czHsK":15672280903,"kUU1May7nAuP":390614491245,"3TF7HTVE5iCL":186028388581,"THs4gkGooNLt":953531166648,"DGINC2lcp9MS":824138549475,"yRnoFppQXA6i":25279516784,"xvCQM71aKurz":451730535178,"8YpKR8MbgShD":125777860979,"vMsqCi5WXDwz":860693514561,"WG5OsBvOhsld":214061259020,"LHxamvZIlsGR":974994256096,"giFOH2CbQIOm":382349406822,"6qw3wP2aL1EU":675155997578,"fXhvSkWsZPKI":437139862502,"voiYwBIEudJ8":870568957743,"UK7WWdVUPeHF":547958447936,"5yQwmqxHYVqj":829473555134,"46KKcI8hStCU":254279684567,"OKulk1m5RDse":273664881557,"lAN9ZegER958":442422447112,"JmgI0fQJtiqK":131978023905,"Qh7IKCNrXdxq":712572771659,"beOZmDAPjTfs":625712083773,"uzHhApcBjKu0":427395641001,"h6PSkFTdXk8D":453720137706,"ovMndak1EdTX":893174204116,"EEOu83bYF6EC":637640065374,"ANOht7gTKopy":996416135089,"54CoARkxrkCI":205068963267,"HXYTKaUpFVzJ":669296316935,"3Irb1l2O8SRJ":802270077451,"TLcSFnx1vmv4":268478698180,"KuyucGp34jr5":109262021494,"VqVC8Pu8X0UF":615136277377,"l3DuhYMLWwsS":351880383173,"H6l6VLOWbSwT":190369384565,"I8HYATa9Kx7H":741270750021,"5j0uQPDDF5D8":951011430511,"fFFk2hMS3lnH":324279671008,"uBEdnulUwiMs":928844391407,"aBmK9Ta17lCu":400652411661,"elpjL0vP6B0a":962136381128,"SwRUhiqSFMT4":496546448131,"qzWJAWwkCQnQ":438141938747,"sVu8YGyBF6b0":677055050861,"WJaa04GgMVar":612829450833,"Up7ayzwRlP1B":784888503814,"rRlxUXlJFPTq":395534235348,"KzhKVBnPVgqs":687496266738,"SMpMrYz1TYsp":209784223930,"QzgH9tZT4JXp":470554413393,"nArROzfcVW8W":273392961278,"k09ZjKSSAgJB":418177637840,"1En5rOAAPebE":891521622650,"dbFeKRiO24Eq":295717641176,"NOmKyWUp9Jcg":338276846600,"HNqFjr6Q2QZr":782162092552,"OuD9tJ3J2ME3":837147877856,"JIs8rwjVY5rU":341093091480,"NJiM0ovybD2s":189422087399,"9cJ2Ra5sz0Kc":336251630040,"lARvTsRZ6yFY":718951633068,"I6DOUyPWhWS7":37119488730,"adVbiE8NV8Q5":686030400224,"ZD2pX3YswGwM":710862560137,"BY2PissE5vNZ":953743603648,"KDXdWVWRKNMT":509255000372,"BBV5cF7dzOMZ":853846504501,"kFLQ3bWOOKCN":501799159855,"wsZVBUFzCiRj":151169538420,"1GgoFwX7mNq5":348956537422,"Agjvg3Qw8Q3C":964147774861,"Il4uER2eZgki":271083523615,"K9kpSSiaOEqm":522599755642,"h7Hcfw6qdnzL":945061452183,"uRTtN8XCZDjN":473198835229,"kFw5azGXM0RR":285799977818,"kx5Nj3tU69iE":286321521735,"pz3S9VRADu0k":716500823951,"ifwsjp0qU6oY":972334952050,"rL8xjSGg9x7t":777141830245,"D3pWfBrlLzkc":154121611991,"lE8hkHX990m4":748135557005,"5DIgjRSCT7sQ":922560691262,"ClzCQWdsTmRT":203487572697,"chzQoZ1pDeVa":965009593799,"uSn9ABBjEVH2":514990448537,"cufSOzMXPMko":653227473660,"Low0tcCoVbgn":971958887645,"TXQajLUmUCgD":409292367487,"J8cjoOHYzjFI":841779915258,"wEWivq2uWtdf":939010925526,"Soy2uyWr05XL":466740051853,"7mjuKmjp57lH":152160958991,"3ENWaj5gEx3h":174392172339,"NPZuaiQwd77M":591441435910,"o9Um4yNWAqMa":44140658740,"2JQYFNgwH2TN":118179387091,"ViISX4hAz6Ut":655829303706,"idvyAtaOrBSo":293243448266,"v96x33PVehHW":213946520499,"rDCP2yTPy7RI":533196512553,"UZXRtS0HHAyw":809901649511,"fL0RCSsUfwXq":429038617850,"pe9hxbDFgRkc":253070928212,"LvoTapSWRB7a":752661063931,"UohBeEgMp65D":943512734840,"MK7vXfGLf4HQ":583040571900,"5OHqHdXgOXJv":85390152976,"2iAREhcRjoMx":408555167785,"Om8rN9XUckd8":753113143318,"C0d48ieaAPzK":65073694645,"lASSLycv2a9L":856434258640,"NH2kI0pCQznT":41235766620,"uorANO3UKKLm":850466825648,"VTcXO9qc6Yrj":897545047469,"hLLUNTnmSIzu":299199938538,"p47AVqYN3BFf":113100886940,"KKqIwiIqASt1":314569397052,"0yFWjpoTsBl6":219584699663,"DLbCfjbj0dyL":401632992200,"pYYACvSsqAuV":516884899714,"1Iq11KY4asDk":513722734547,"EiLuJcBnhakX":884470757410,"4fp1tM46Ue3T":578779990878,"dCfRARVMKQTy":962315920394,"qI5Tjf4UPRx7":890256227330,"LtF9bCzhL9Nf":616108070118,"9qiLqtxn99wU":888302456493,"aHvsfM3qp22y":991660998713,"aQOafVH05lgT":374847618947,"MsvqCTqSHshv":713291142882,"CGfB5yDXxVsr":24798383486,"o8j8lMDTfzq0":543836259368,"4OYRF6XziwaI":446372681574,"21AsEFpG6zvs":88470691026,"vi4t41MbnoE2":851409537680,"q6UZ5MYl8Fdk":796908222868,"QaNVpb3lG7t9":119814067713,"RkkGxPlTQGz6":746951327482,"q18yBrs3omCJ":600738028704,"Q5dL166zbXu9":151465182650,"g9qHDzFAjrUT":655312956241,"awHD1eO5Qd1D":619011295617,"HjoTyetj7cru":699219603537,"P2CLp4TY4ywG":255007170467,"x6cQ7qyTQStE":124474450459,"uCgziEHWe9hA":481416552206,"HjxC3IrF4LRu":10329983238,"pDBbgEF2Q60v":419400277966,"mXA70QlaRIy8":515450393179,"Amb6mTU2reTj":379535035971,"tgt7Dj4IXPtn":821613806608,"3UYL7wlfY9GR":19219430983,"KAmi7JJBevLO":144753886219,"noVKzjhLwNyu":875157377536,"AQ3QYsTCIWmA":105328356304,"GP8gyJHSnGbX":207073111765,"R8q0Z35yBiK4":565149830176,"awi3SGQyd1X3":601159568645,"BCf1siuElLAk":907692870770,"4IW8AWyluuNd":27493306713,"QOM2xZc8NaEl":958512343964,"H8Tt2WKPSLrV":474516951123,"fpw5WmSoYwXU":554092671836,"BAQZ875iTzhF":923190136689,"nDhWrQ3BEj5o":93405608706,"jSkbnQePpZZT":434562276077,"R3iqmFfpbLJc":160041818361,"DOxOmZICG3mv":564555871403,"lPkVHVjysSn4":874558220773,"yBxYx81lUp5Z":463368752152,"nI1vkyPphHTg":327324763010,"a4Z4L6iI3JQO":809606491452,"PSoL6UOQETE7":925130262775,"6FLcjy9I71yf":928555466045,"tQxKhu0laLeK":917750651814,"VuSjKPjyNUAY":193120162581,"H2zkfnrZhCUl":386240684742,"Y4yu8wlzY3Nm":260982954645,"YrgH8xWAh3Tk":940061976621,"jc4tLg0bWu6d":473690489672,"vGGbINv5FtKY":82912865774,"Jl40LJRrQWKJ":207621369877,"zgQgdRQ2kcah":361685432287,"TXH5QXjE0zmQ":375545585899,"4s48WAcF2F4j":325600115701,"CxA2aw6qhLrH":59099504533,"4cbfSmUVNBGD":254585545063,"Uli1KSxgKKc6":432092171923,"fXJJsqYv6XUo":171478450756,"wytbwTUhERZm":267097731931,"dCrsBagHtbvu":531091738268,"xel2a6rbvYns":152192183583,"YtcdhTBbgSjG":436620476550,"fFbCrbxhzvqx":279797244321,"Db9C9vthdZRy":903938408199,"tk5AcfxIrqPv":295145558969,"vfxqt50DgLIs":63467198277,"qd4aDEvNhxJa":950033556640,"EE6hoDaeWiJ3":584446486730,"AiZtx8PsrrQJ":748434759409,"vUC7FP2hyDS0":17152566346,"6mlpU9g4KcPJ":344589489730,"Oe697n2P2ZEX":288132897056,"8U49KDGvwgF1":536428221807,"3TbTwSEGgDwo":29032676250,"UFDpilXLcjjT":139761979300,"hX1EsgCFvUqc":461111281427,"E6PpeMShJTIS":489654190438,"owDJWoP6NtW5":105908948313,"EWrYDJI0TV5G":64700476923,"zInZicl92kCc":222179062351,"WavMOwVFkaMx":512904255248,"q9O0n64BKwjQ":607506588337,"6H0VuYXpzaZ3":503129605270,"8JT6tC4TbaJa":175502752657,"HXbvrYSKLcGZ":240417359737,"D1m8WOSgHRgs":625352274552,"SFX8eHBlkKgU":86850045932,"Xa6ekZn0G21u":982741837602,"upBX5NS54vPX":198021133526,"dm2Dsu4NNqx3":708479514886,"P7HqMwPQrFlm":278240502040,"RSwG0kjibmhp":647830529926,"GkkHJ2bCcZPT":350761520093,"GP6C8G1AznED":595178998406,"2AIqzRMKLkQp":891159559831,"1yfrI11rwXGR":49374969661,"goFwDdurEqiY":264725048793,"nj8vw8KZTKOj":778890837983,"tAARbEetcgGs":369075251419,"IbuZHEwKXbgl":655174191240,"TdQSK9G12nHG":611359241498,"T4zv9kZqMuC5":806181579251,"yMjIglvvKGMt":878659904004,"0f6BLucPwieA":899920442225,"aERxjdSrRfnM":533460832726,"0pbs41xx1R6F":350101489979,"LratKCJug2rn":94883133118,"buUgLM0et5hO":689370651261,"PqhNV3pCJDFJ":393382412534,"IRw5HaxB484p":618843460340,"ySOxlKQfGXtW":277456160675,"tyXIX6ZORjTW":544738611652,"c2zrgSLOOJXd":116975887582,"Tjk0UIq4BzJW":144685182342,"g5zmb3S18VOx":366904209006,"pDjJstrcUBRz":583319972799,"9EUthHodGXc2":700604213179,"YiQ1jPR2R0ae":351900307853,"V9CpwNMrf8B8":27728141063,"kKgWIpMzVXoA":278937151850,"QSkROtqEJs2s":597879911649,"Q2Ysz6Z4o49r":625586790287,"U0qCwwkqoUJZ":929468670224,"qL3PFX3YD1kH":107622499552,"xSWWmTCORxvk":696026580481,"BnzUNxb9bJmg":26324999924,"GGyEQdsZow8f":6402279087,"MyXnseGYTVWA":199620257049,"2N6wMmQeclWO":553223997548,"KbkIniBkB63f":724932657786,"IVQbtAmsl4CE":610100365960,"jFYYJqz01otg":762661771422,"8cDFfoi4KHhN":994266528700,"3XIYsVBN8tNx":929950249507,"6XAz1wFc0rpj":106472477914,"g6cVx0xJFsrg":267457628408,"r1Kst8MWnU0f":510506232043,"sEvHFgFB8vkZ":427636738765,"pxTogm2SYUl7":224341977393,"QP4Dxb3q6sHJ":664195809737,"DVy1MFYY1r1e":921356595675,"vckpRmRwlr2G":9111992353,"HTjFRK6XLeAH":855345361226,"qOoWbu2v0amm":678239507016,"SNcBk3LkQigz":229030004763,"zsEjJJXF7q5f":647490392281,"8t5LG35OHyEu":160009461718,"ZHPMTiuPN1hP":908981732826,"WodUc2z9TDwA":874473343266,"GDQFxJ20Owpl":503895514289,"gstaBc1hU5Cq":40701384551,"ndqE2yS3CwJC":526789975809,"mx77fbExGs6V":523718728236,"PkXrm4Y69Zbe":997242861548,"oS5ereNXBk0z":572040425993,"FSHVV4bsJxqS":998128328658,"E0FtzphLDy1Q":256861689204,"gC010nnjpJLl":884949802544,"aanJuPnIHC90":89789754253,"6mKu2qQ8OVtU":209781982195,"zgcNh0BfKGqr":895564542234,"QP0Tkyjdw4eB":250579091229,"vCau7ubu0WQz":669966754109,"7X0cfgTYxcNl":851956842689,"FmXgP9R8y4lc":476781018214,"XyZ7zNzIyi83":687254061802,"sX43yAgwLycN":988799191516,"FXswJiKBfCKg":321679362308,"OiynrBSfGtQp":530961409823,"g4Lvo3o8VWVw":528153852557,"s6yfFfRtqLIm":478637568799,"spdP4m7kAVZB":849396770252,"rgioKqSFg5qa":570145354390,"h6bluTDIWzWM":493025627315,"KLUjQasoAK2y":9730097978,"LETzz0IsqqDO":219045200529,"WtF9NkQVNFq1":152970339970,"tZMZmzMuW2lv":499323120647,"zivNnMxMCi21":539710127933,"kV9GqE7ov1R0":695046478496,"w6r7EQ6o58nr":176063754628,"4tfvzcDQ5BMj":726610576124,"iMSsyFe01Ciu":140271348896,"MERfxCOWSBhq":916010974954,"QCyqj7BTNWIy":343798818931,"oCmIdFkpKloM":686313099189,"h3VF4B3PFPJo":384834765500,"sakYV4sTE74v":291990184863,"d6LPE1tBAZTb":932307581249,"quMWF4FAympm":390238420628,"HtRIlwi03EEf":138497710259,"hRZT3vX7h8BR":829567087042,"JBr7NBunRRx5":81498551980,"e4srUCxZrmo9":882218602731,"tTekzWlcVP2n":805532447533,"gzQo8SL39AJz":96503292068,"SHgRuAqbxT1i":770926231827,"qEPR4jH9blgn":663186347597,"46X1kkvNmtnC":286769035567,"Y1omebNgjXDV":829635672963,"tAVJTviuUFpb":266639886128,"YJ3UxMrJ5lEn":287418135485,"MPc7lANTSvbc":740999669095,"ylVJesCHtYJp":127985759709,"lQzX4JlO6kEH":725509661692,"tgBjdAfj2VF8":225051264346,"TYzS9Bq1Puh7":916682325167,"eBKAsh3R4qB6":764702172036,"S8ZrR8SGIoGy":722389275114,"HE2BSRtfT0X2":771428841932,"wZgx9HJ4atyx":195998750833,"4ni9ZhIVRmUJ":523684829526,"lJUoWQUeOxPN":359893352002,"JpwEVxQXdLe0":455838280144,"8jkhzKYCOEsO":252924318443,"T1WdKaQnKvcA":939212397764,"PUgCrIiggMG6":715983082945,"00rA6COL5TFz":683338907810,"rOl1f7zJWFWq":539257237132,"8UnyLtTP3Kke":773170122486,"TpKoxcbxnuJr":347967401131,"6VDiT9jbgT5M":253431329249,"itOVXJKfRx3o":681772096008,"UupRXxBREFm6":173577530266,"POpWBtLm9dRs":657765803131,"78o9bAgYNtR1":940312740181,"mgP8FkWjbhye":880136483986,"8sPhA2Blpw9X":302638655942,"IS9oE4PZUGbq":249760691203,"0SpllOkXvZcK":110548816961,"LBq7JHVturVj":281384465334,"J0PbJ9sUeAaa":253575280449,"IeWCgpR8i7XO":707271661969,"OyUkfqRNQUMv":150459913956,"lCNtSV4uQ7kM":266466688717,"gmT7zd820Qn9":343903958795,"xCnmn6rNy71d":756865872624,"HnYiYdtBfevg":360321093848,"wSMWBDkl2Lkp":848431637016,"AZ7HLwUsa0V2":140471029518,"YayPM84waEwe":97333180938,"8fMtvBTHjwxi":76976533580,"ddqvCsInAQE0":376160728279,"ooUGWrh9Rhf5":343300571733,"8MdB0epJutd7":123087071442,"bbJMpE5lxTiP":463190775146,"hok3qEVDQUs6":456323575668,"8JXMrnCtSJNW":407530083311,"ulPgHx3Eollb":320157978286,"6AFX2S0hb3kM":801541262477,"LcbeQNXkl9bu":85507776177,"nUY6vOjRh2tk":634245205505,"IfhtadMXEllK":314627046490,"thJTbqLYj8kD":554553720448,"AZEFlM76jy51":407077754154,"tMuavSpJereY":747194459994,"nCPFlljXtmbF":455695396645,"Wy1aFUAQmfLC":745417919933,"KFtBoRovtYY1":566308157043,"FLVrkiCG2U21":798020724608,"mgJId6jix9Ur":100003906538,"XmOKS1YLUVF3":736097080144,"sxlNbcCefPq7":715929998599,"ACG8xt5USDer":109168975290,"PzR8JAzueq6A":75578729963,"nlJjF0LaTwSG":316967344821,"CmkGMdiBiguF":193559737592,"tZSNb58gDUq2":955101941747,"apOu6tCfNGA7":466543189524,"PBGf2kCU6ma6":541139218398,"xoaihwRNdVlB":91113367135,"M7GC6wne72BJ":21675364410,"wy5V5FvOqfqK":945458939260,"0aqanzdCkAC0":768430700113,"MhRyUT47LNov":607379271823,"hM4Z0LCScPx0":864021610221,"HwYcboy4J6ti":841550420293,"Z9a1FSDwxD0I":414904570264,"MenYQFWpMylu":111861733473,"EUnIGP4Sx7NM":456255424946,"2IM2oNVApHWS":869210423767,"0uJG2pWT9wmi":140869882900,"LuERug3ib1Rx":161346494873,"d4HjvKJiYH4s":861657496188,"MtKdOvXA8f3O":570457901284,"PqJwza2WQ0FZ":130157273221,"ifU1EJmmGPkx":508810357083,"qZ4FbRgbbN8d":925990442412,"geyLG04js3PH":784530817519,"HyjTxmunl0bU":612483467410,"AHKFWGgSKSlL":759593735694,"npShWevjtf0u":274004587071,"ocD0mSGBNVhc":80443315227,"zpHrBZvhFU3w":256510913496,"Sxv2nZWxwSXF":65171828072,"VPbWnOBnXhCP":805214816532,"2GFvql7z4IEx":713764711088,"tJ0Hr5EKUUAs":646280598828,"dnCT69xzpFUI":270280031900,"3BECLVFmPG3W":730365054625,"fvpHokhkpGos":777448255296,"bcHO3F8aNr0m":275513298363,"GmWYgzlp7DKP":775554701865,"zWx2E4Q7DbVP":149484457560,"Q8QPcyTsGTiv":19936750142,"63tTLjb39hlQ":710303092851,"PhDEyH4srt9S":20252227904,"s2n1b4dHXYXt":49147041864,"ttpNgIFVlmEz":839580328932,"t3I4xxhMCsm4":174215243168,"Hy5Z0H8MKN5P":338610552752,"iRjkGREO9j42":715263850020,"yILb9tWWI6v8":255788703253,"VJ95QT6q5zJO":848839462064,"Sibw1ps4hVlO":745272471001,"CJkrttmJfJTY":656570714666,"LBuuUSQfJn3X":648300505515,"xSkrkUxkWqVn":382495150883,"GbCQfuRFb0q6":510345523368,"OdURLffuh7cf":557105320517,"NbTETFQWkrc1":454158435171,"HHNfsgMwxXUJ":97544702335,"Vc8TftPs2iF3":378838170558,"CmFTtlw5ZwBl":81269422739,"oUZKBcFuAQOK":260265871429,"eY6Asx1rTCOU":44128434250,"yWixptQgvCA6":134127390755,"hjveCcDSWfuj":739358660372,"wHhPaC1b69HX":900894071463,"togC44pb6ME0":502790047236,"qf910I3cxQ8P":200851834241,"ifxVF9ptA3wq":857416591157,"YDpLMTmxg0d4":237956830913,"27FJm4d8xf8a":915641008684,"dMWc4bspFijm":192429779394,"IYvyiQqgZhk8":78326448306,"zkrJCIKwnNVD":163054545467,"pvHukSw2PZfx":739232054096,"jL2MwIO31r0G":771830546184,"DqWscDrVDCHR":179584868946,"gJhGui8aV8xE":484809526035,"YI40RN2KVz5j":411594086772,"X1VRNUqFJYbH":64224005889,"Qz32hx4H8CQN":768052970053,"CL7KSItJLhRj":684015363216,"xfpToQkqZ8U3":641660425198,"GhEyNmCZhL4X":544778018843,"8RSeCRAfUSCr":769165163582,"rof7WDHR2PwG":303425906492,"DWYcAZwkQMfU":69697536903,"rNUJmcHjUdMR":354134645789,"0w39i8aZ2zWw":845816551778,"tdnCPSIAr2iW":876057310544,"47J3W63TKvrM":299386851211,"Obn4PCXiLrrt":967996806558,"QxyDJ7o6PaCJ":265262910527,"4fKksmQT3KNR":11114349079,"7ZXptj3mZshm":243219091449,"YbvgMPhsfu4W":840496708030,"WHkz2MX2VbQL":861642325864,"CFS9xMn774vp":474960891979,"oaMwN7uSZjjY":949594939895,"LNQGMRg7oP80":751175866337,"1HxDYz4XpqFQ":480336999315,"H3fbHPxqWvnG":680253818536,"3cvZF0jlQHIg":601305778539,"hEWBBYd5SBaq":891856468943,"kmssdCxYh2i9":943227299076,"MwgmnerfVHP2":873193348180,"KMFhD8UFFWLb":323861058998,"3gEuC0W1Y5ja":937159176241,"bCDtx3KYhwRR":460491888300,"UwvJRCV0wrly":218707914709,"WFJ9FSRH1jnd":114614923115,"0sa6PT9szn82":207484477274,"gEVuSp4pXeXg":109079784193,"d05CGR4f6XmQ":814888819501,"EKe1ZEMfW3AY":229283910710,"12B5WymNeWWJ":528837892216,"x2MIHH6ww5uZ":418578405850,"iYr9RdMrbUwG":770779320732,"IYs5EGSm0Vv3":307863081209,"ezhqVKs0M9vG":297250664357,"69YDACAofQvY":793609884074,"zFMdON2zlrlB":587134757709,"QTbx7ZcmdDU4":209153877959,"lIp3n3CY6a49":973363465713,"bHoffu51e3Ms":901349369630,"9bHO34LI4S3f":409418267851,"w9XgS8yqCv2h":199982343203,"7GZCvGeGMyKb":309060991198,"M6wSyn6L6008":808997025456,"n2c3R3s6JsJH":362379263339,"KKmbBa5Btpu0":885748362819,"AFFI0Vi9uAdi":429676456358,"t65fuK9oMLzd":72201252445,"m3nuADtINbIO":918443523859,"ujZVr9k31ICC":591422676033,"lnv7YU4iyEK8":773664082691,"1OzpW1OXaqVj":53197191447,"arzFoVdsCDPV":508678076309,"6eOqjdJJzop6":905515648679,"ggVqcFIzsCof":509616006435,"pUSmDiKQidnN":793384740738,"Yi3nAs9BidbO":749143431677,"dtT4qYhn6FcZ":843414600482,"BwYv7wSwav7R":164759186417,"FTTqdH6gLngv":312461078917,"Xq8PO7ccHSaN":630170575167,"psiboPdT6LCj":72177500282,"2VgzBO5irxdx":302802340471,"Zdh5TK8crJ0E":156011285982,"7G347HjbBj87":604725579197,"NyYKRoa6R55p":20116367772,"6KWOrxRdXdAE":345183981709,"Wd3ko0sWwI89":569300918294,"7oELwWzbAHXK":217646470777,"PcN3Nvsuwgr8":330568289039,"J49s78tUjD7z":903975421286,"NOQWNzDHoVWt":183245613584,"LnsVP5dlm42h":824320992460,"W2GYLIeN0B49":74624004840,"RJCMpsK64cxV":497073246589,"i1NI5VMb4l4Y":537272565987,"hQaSHt7c2XOF":480662192263,"ZvSlr6WBjsAG":124774325272,"6vCmIPvd4VWE":361994865311,"W42PUn4fsXg5":534650094962,"AU7hdqekMmea":852579614020,"4KcmHru2cC2w":105536263258,"wzSgz1JnfeG1":945439380496,"CI6wloXKB8Su":786630371401,"B3RPDU6vt3if":432357526052,"VFWAKpl0DRax":860768924968,"UKA2rLiaCTRF":602138231959,"7gJRMqbsFPYq":248375209042,"S2lQeZniQ13C":390369455686,"vBvGtGzv2gTG":100617808098,"5OMJ5gCVLfAn":99623492451,"w4rQHV73cE4A":348335419898,"gXLbNFTRGl5m":183897964809,"PoEmye2e44Ap":113641313188,"JuyO26AgH7u1":413584179815,"DaJ1oluuEfMH":900058723161,"GfzlypnDlWc9":626878793157,"BbG3IpFihdZO":930385010275,"b5Djmwa252tF":593079655601,"zcrnrJbptY5X":715213380541,"hmM5sJvzEJyA":255638037738,"mBg9sih36yj4":280749414004,"KRNGAty01bGD":805662677044,"P16jo1gBEaj5":555311823960,"7OzbeYgIiuKE":27659622199,"KHyYUEnQoMN5":742093204900,"9VQJqOCYxCVw":596194566063,"0HVt7pdm7Ec0":468424498727,"lq11zOhXyd04":810182887529,"S3P6smcw0PZJ":506467579802,"IGvmpKoNYldN":884156885096,"ODkBsz9BXfaA":383838964374,"uma1cGzEc8Bu":388559124539,"Yosun3mcnbNO":116520467760,"hQhwncIaJ9at":239783466081,"emmT8FYI6D6X":648820975015,"oqV3hniIMgKJ":305501743876,"fAX6g2fJuDEC":313971998594,"YY6J0zzvlv4M":586431452171,"ntNMJ7LAQxSG":271862874103,"Oj9rJQDr3ajg":247604800979,"iNXsioZWRJEV":791799699086,"S2RZ4Ot2eogG":800363557345,"FxULPTiIcD9r":519170370417,"40pX51fTYPF7":717456995738,"Jzl8cyq1WWmC":400822453108,"uwKm6s6i8J5R":856371511575,"NbTU8wzNiRE4":627865877280,"gdAfvawiPM9g":986533355931,"qqqlESO92BPy":391062143659,"l1FTdrXfrMax":234576537810,"Hu97UbWxh4ov":676139403088,"8Z8owaKjRqPD":135035355838,"AbUBH9mtSicQ":263555744446,"6xvRtM5aRI2k":483044704527,"NxSCbio0Dihs":955448014268,"wv2VtvHrPoup":950573443819,"qCy0re1T0QeO":308218712952,"J6ZJFZkGT3gy":102824580442,"eBhl5dvHs9lx":53793328789,"11z842XPnXT6":122436111963,"oZGly97tWJNJ":252094755585,"yUQZY5TL7thf":666396143553,"nyu4uuTbn09z":130197399554,"l6VannJcRUX0":298763674374,"FPGgGC3s1yKg":727488476024,"FBDmDkT9ykj2":699031720420,"io3p6aHozOKb":8080053783,"TZbbT3aOpFxy":209149152926,"iD7Xn2pqy01b":281518627481,"dSnEDDGzXrAu":40448601340,"xWkH2g14apft":578009055685,"fB5UfMMua5RM":634086380365,"mQ7OhQ3H9Men":516076333116,"1dMvBFblwxmv":328096276694,"n73ZjebzP5dG":245835740624,"JmWEHwUY0ela":753106618162,"jwoZ5qmPda05":652655677812,"Wo6V3TQr1UH7":174156406281,"jniRCah1Qmvm":606150935542,"sqm0mSmKuxUH":370475314161,"kcuzwjncdq1j":951824972962,"89zvU2YSdiZW":433931170655,"9VVwUWRhWzFN":442408414144,"lScAVobdnFVT":643684475816,"wGGIosrZvR3P":553041532873,"izI5CTNRIo2c":483669185774,"ikLQ9e4TNVJU":148703149887,"hc6es04yHfIh":784208232981,"y6DB4JIiRv48":888901360953,"E0D03tE0kQnQ":741074476718,"NxYUJNtljfZ1":755431376504,"yLvZ298mbJgs":448592735710,"dkceslIRED4M":133654485852,"RzsB35VXT8rQ":198432649141,"3Fdb3adh1vVH":648924363039,"IOT9XADiGcqa":651428449850,"fwuK0O9Otzf6":312712059517,"l831zvNuMAA0":602782750029,"QdwbklsNIpxz":520759871841,"vADIfjFEQXoB":38304478396,"1VLDrnVDbSK3":865275188953,"Nf0tBMyGiL1r":189885651966,"sMAItBAFB8jC":343707977637,"Ia3xfYx4flGA":618586366230,"FYVJCMzQE6ne":763656817306,"15kCaZw9TgQr":575230962549,"P7vqW2jSbNBT":288680711147,"P5YKo8rmqZoE":100745076371,"z0fa3JxwcQDl":994965576253,"D5UtwXwBOjgC":327438409519,"cG4ZNxYNgGSc":86559202348,"vcqiiehF8JFJ":76389276688,"EGLMecYekpOj":263836960366,"r5kC7I6t4zYA":224520511269,"0GKRs5jbPg69":156973909290,"o42VnediR9mY":526438518463,"dgtoySvY8cgc":273709136658,"7ZZPicdQ4Epn":974020577694,"n7Edtp2m6hHS":293834246907,"0qKMwoAoymAQ":530912979283,"0GezwIPKk279":433153799857,"OE9kQgMJZNt5":732885131628,"tvOx7ma8cwkC":961687976104,"7JbiNCaeQOYs":417776970749,"efHzp7Vsundp":644527734345,"JWtzqIYveHMy":963129595679,"XlameSWzXKZF":219601181634,"uiMPpSeC5JTR":258980051264,"scCo6V8FtelL":619641365055,"Ah4pJAYzJuz0":77792503731,"gnLWkdles6IS":283551336025,"oxnt1cHAXb5M":963815031598,"TSUhYEgDm3XL":748713211362,"DK7RqJcgNzzP":534436760576,"MSqEb0fkp6bo":771572028610,"z1lvqnRaAZu7":964408364354,"ziLojmKQ80jm":989996331368,"lownZy12Kvmz":492390472872,"G6IMw6O58tjp":498389176070,"POf1RQWU1Bom":947211678966,"kBOUxxy1Adrk":996360322528,"2rXHWEOxGnUL":742474382792,"8yRhQRK92NSq":385919293847,"QGmjNTKkkQNs":566277740361,"xFhHXdLkLU3A":749989531110,"kzMubowBAseu":397930191054,"ftNUAkZ88WLc":337079038080,"IIDk1CoqxSg2":711739289002,"yDkgDqcRRRZ3":59944165074,"BCi2YZAEQJyi":420757110111,"Oyy7SUV6jHD1":173426010447,"PyNYi96u7sRT":947141050800,"WybAYA420PLj":4609314035,"inhCAQioLEbw":832896306989,"eYV7DIxfitzA":782452189448,"q9zqoNgl31bU":683648967076,"NC7kfnHXGuoz":71945980941,"OdAR20bSnqVo":202477617734,"uo1DgkC9oUEy":205875589306,"8wtW0DayjDdB":994076434384,"wILJc7u0VVEV":180169888505,"ckYdrniyGrO8":549768907959,"3tZotjphlnlc":531082596735,"ldUTfLehbbLk":709653995718,"7gD5yFQGLW19":740001509444,"QTwnOWV6PBff":179861100226,"P73TTbFV60cl":237680566628,"XQFX6wvsGtcM":653527007890,"b0zZ7qhxpkpU":855832013667,"XJlCOG7nIMRX":909514165971,"45aUX6vFgBNg":176031932065,"QyRk9aAUkL9w":134735571155,"VXMzmlanvdew":638725243250,"F0NBDG0yM2bk":523642277253,"O2quxbx633kZ":617920918161,"18Qaz5MrLTXu":741207902302,"wAtBPMtZr3HK":317359188766,"7QLTgKTiMYrY":307465270484,"RF1JgKoTHAgt":117698177532,"Ya89shfDS0f8":840604115723,"CD3UxnDqWIsJ":696509214226,"cBZhO02vowoa":446443991501,"MWhTJ5bgocNg":684636413730,"BfgE4atWTy8k":522858898553,"hdr2aJxubGFh":224385393087,"RACkhqZiq1QE":900956613613,"9oBEefjro9VQ":311316411433,"PST13QopRpwZ":127324487474,"LcxyQKyS5GB6":842841085874,"u3byzPjHfbF8":170343274451,"4wqP1SgAWjWZ":272664492182,"WE29rWmZWFl0":531568215159,"emLprkilKH3A":45095962437,"JcAYmMtvglMa":636502757982,"6G8FTtPaixvo":74284097766,"GGVY3MC92z8a":429430523782,"6PTuWfPlTRSV":802801167238,"wFZLkhCPsQVm":738185766869,"JRHrXnLc7PXC":835841134926,"oRbauaGCFjj7":929193749889,"esR69Vv2E2zr":972131230176,"BAAsSqHpvFzY":902218435509,"wkTTggxY3OQd":416271587208,"DhnWKnPy181m":509569512621,"nkKnppSrYyZZ":608823303406,"jQBxFCcFH4uM":353180435242,"VImNef0Fgxun":742411575015,"4a9EdvrZMpAi":209328110913,"NJONkvoh47RJ":97608292176,"6pVG8kyswqmH":691401910796,"eRz4srlGNMhI":315915530978,"5Fnk3QB5IiEr":76765294710,"skI167EfuWh0":692416357548,"7oEbIB9z9K4r":567689368700,"WyxIUFqvnfsC":183014823999,"P6lEzKbvZqRD":656393705935,"iXUKOzVvkqao":141444429721,"iA9SChm5tLPv":489482732117,"W6vjNNJFXjl8":411468787142,"GgY7jUFara8c":881819356394,"WK79W4F6BUOM":997263745584,"O6YvEZeawXBw":615706061776,"65dcLgpdsrjr":586138418238,"Z9lv3WapS3qv":491327067675,"h2O6DgWdcxf0":991212121209,"QBGDlVDk38TO":898831634790,"ybko71ESJbu3":289132324165,"dPPtaBh1Aqeu":719097334766,"HBzmToogZXzR":885383377731,"t20NkyZWjFai":16520533607,"Njuiw7vpboKm":60312709731,"wKEpdAHHC68q":680911274934,"YJpsFxjgxWYd":97396712364,"nTeMN2Evaar0":682736839391,"EWtheXqAVXwR":255374204502,"bvGslWxkGkr0":949639454030,"bF8UFTnzXBhf":321441885845,"Wt9jxWO5t88y":662286885534,"cmlPcIB1GZNp":568445491077,"0XsxiASOizrl":523944910454,"3gCcQfYQesDW":636705023434,"r0wK6Cnm3Pd5":292973668780,"l2J3MnsrdfyG":951974686739,"tue9ZxDDu4Ev":56554646865,"DHfcsYn7lNxj":998981127765,"mwCKRa7smrbK":893217490085,"lFVVtECAIHLx":943910932024,"KY0KPfAlYp0m":213749178762,"v2fKftDbEpTa":370618219630,"idyVofQyeqTo":980197809976,"SSBnB9M0Y6JL":115182000898,"lxw3WEEMDOl2":525120322210,"zjZxj7CgZVvV":786169954081,"TWH9d9LouJNz":167434888509,"HypFUQy3vKwk":567254448056,"hP4pcZVE3ra6":54359889933,"R40DG6rjElsJ":595106962178,"t1SketNLROYt":788918937588,"Qy5L5BMIyPhb":527061264905,"ozxtGJPTP6no":352947682850,"lPmQueILzqMt":220180778169,"Q0avbd1X2tUK":147989664680,"yGvv8gkpo1M7":375987712312,"TsiMgxfDYesH":635602148020,"WUrMb1kFplIR":977018105986,"I2tbPvqns55Y":379139985236,"0IlzAOvZf6mP":297737985060,"fQOIbsQC994w":236918042632,"cD8zvodB3WHn":703046286034,"TX4PmdUQxSMP":106475292347,"VrF0AzttArL1":93886693335,"yMsSj1mB2dtv":77270731841,"POHjiMAmOZ4s":604984442995,"gFPzOK5XBINU":408728523717,"iq2igusJsROX":464246605734,"N7kRuLcePyfN":22109699852,"kSPKNfrktB7w":114819196352,"hJ9DOm116VEm":883956680077,"qkpB5Wq780E4":465639828379,"ZKpgF3r3W3A9":176695880355,"QsC6KRI0qV5F":45576645460,"zI0xmtGS4gao":475171936189,"xZ77CRG8QdTI":835196415311,"K6CPT0RA3tlV":809388266326,"NTo4eU0WqiOq":629857771870,"Ud8r6i9o5y4s":141281781118,"2Yhklbmlfa0K":296655766120,"gNEEJhQm36Ek":717243832895,"V1LxkGiAwp4b":397160021022,"emSnWxtHKw0t":847756008636,"b7zmI0b0zh1L":165929063584,"pum7KNng31KK":89276416674,"NKwwZ7niyQzt":793239573329,"eZmVimPT574y":81169745141,"BgzPFninItoc":415510709705,"b6uvinZZHQXC":329512660837,"Nj06x0HgkjJH":408547746425,"qcbEFQ0eclMN":807321376110,"Ppb8PaRYAwVK":275673340005,"uUMw4IldJppj":901531935020,"sJnScYgLOxFy":405404841314,"S0etMFFvmDmR":541704337085,"rpKhJAibseT8":463790738332,"eqSU5tK1pCUr":385006553568,"yP5JrumJPf2r":313636862826,"ROkU5a2tvGnZ":244707257123,"RQBLS2QnHTNd":456656997529,"uAeOQ3hFWVV9":892977551788,"wEjUHL7xR9wU":54524185471,"4BFODugKXUCG":111284541086,"nIIBctRta2M4":809035558230,"bHrZC6vg3EUK":175540641819,"zyOLSw2S396C":213357518412,"kTBsBlr0NtEH":580935458358,"he5bavzrncTc":935823697859,"akuT93FfCWVo":560558817956,"Vp2u5JVy2a2o":945347810507,"THudTJrhTrrs":155368194898,"VGj8G9QCvbrO":990746582259,"gV8VL3dCgg7C":794758085429,"3PGwCIAb3ZYL":662444710886,"er75XfXxe5Mi":717416003556,"oYmCrD7fBpY8":70227227103,"QQWpECnlKT8e":439600065764,"p6Yt2BHYjdfe":409886385041,"6pmbcGc5Hiww":369472539381,"7ja1ndAWDb5n":337162201215,"TDnBKNl1BChd":319753138253,"UJsFVxnpApb1":217580379463,"wL9rssx5g8cB":892452502432,"bDI02IQuMcgv":298077768813,"ePoRLc2M8fq7":118412028358,"jDLNRNxYhmzb":930665972332,"Oei8cilfB9FT":820755106210,"7JGTXGu8ZFJ1":601988000660,"K5XKfpnf3HhH":662208138331,"BlZeVoZPkZZi":850788660320,"SlL7I7my6Uo0":490537838214,"oYuOOd6OJRwd":443080775461,"dKv7yxtvtfk3":320066043908,"7P4Dp63IBZpq":592695765113,"vY8PYM7q3Ecj":864548059806,"nDr6HHRvPuvx":737330103393,"t5BcUO1ruq82":294288901506,"xlmCf0TwoXri":565545943693,"o3YKTDb4LPWv":979866936696,"tt2J6EiVgq3e":38733630809,"MyldliDUZuIp":658695162383,"DrzdJeXpsPRa":223969465649,"CiH8KZocyNDm":97507923315,"QoeYT2JYHfRu":419660215448,"yod6lrANVXRK":347097164782,"Qk4nHPwQRQSe":140738592258,"aThVomd0thNf":806448808809,"pINoPCFalXPY":162179571979,"7Y48yy6QQO62":85499440031,"nZDDW9KylQTv":203131280067,"t7T8lfbNkngH":874821129109,"CJWgFBh1z983":392138346094,"XspD7tKmYDxr":680368033242,"Un5NKnl6tkZN":435456633355,"YFlbmDQ0QMFp":456172457570,"QbvwTTTA0vNE":974093919059,"0Mm8MXUJbBCU":653086413046,"Ki78AwqU7JCU":299651606190,"qZ6XoFykL6lR":202911694752,"fdMT5Yh1mTe7":922951016090,"dT6r8TxoZpnT":651069993693,"zKzcI6GagBeF":795130061837,"r0uz6ydMeo7j":821455485471,"F54H2B7AS2qz":246330114421,"31uyBY33iZCm":370951183365,"6p52dXkhfwxw":784042449079,"CfAtWUnncaAN":694821019094,"zy4x8y0A0xwR":798370184724,"NU0H3ZeRpguo":573016069932,"IpzZGpePb5m4":380817011107,"KVLR6gEB90Zx":593738300258,"KHxUQrQNSUoJ":228486499141,"IKxOfQhxVZi6":917002364363,"pXdzqjDrhkRI":130744449916,"7zhI2AM9rWp4":368333518402,"hyPeGQa0Inz3":453998518434,"38UQf5RKsAYh":102473683339,"Xdqv9ZZa63PO":237525871975,"cBBHwTADucr3":844107455887,"HAVgR9i1vtpN":160048704407,"wPXsA7CFVzXA":893865387635,"GPQmQzVAbtdl":689390994989,"G8RbnWdPnuZl":375735352993,"KMXpQkSn9q26":745052281782,"CYZTmE5s53n6":214475497036,"NemXeGXGYJ9D":838866203258,"6czgzKZd0Zgv":612303029486,"HZeEt6vMtn8m":360825197889,"Apq9oqPUwdUy":643740618505,"tAy7UX1Yh9Qg":485356097067,"MdtZZs6eODH9":930607908366,"UgfNgB5TTTaQ":821023585339,"DI9bCbwtra34":54311632381,"fW1L9BXBdmVr":745678095238,"aVo7LnVRaABj":124080390261,"8D2apkoXwb9S":768886865397,"hAqcDWv6sPJD":832559333856,"ImTcKm3OSBAx":597923518624,"ubmm5Hg93NXz":21082380200,"sn7x9MXAC5T3":459977463970,"PMTZmnz7Kiro":273041797332,"x9FC9Ew7qFkR":649514193236,"L2YUiw766W6q":961483779060,"gROUvYLrnAja":927684519171,"2OZg3QJWcaRI":111508920118,"iwmw1ueQJvx9":50498275334,"3J8Scjj9OAC5":862293191357,"ERrNHHRW3qo6":584404413586,"1MZfUG7J2W5V":222116854476,"Ij2FiJzMLqeJ":355204968312,"mw25nYEVhOlQ":133949874721,"igshJKi90vi2":807877498447,"aFR7DnemXL9i":848881938022,"zzrviunjZ18x":827465158480,"lUQ9djHmFHOP":690929285681,"iFbziNohXkz5":20861898978,"GMNwkjEse7yO":862841184176,"RBZfoUcra4Sj":751310284990,"UZxwjHzrbE0r":316453280286,"JGwfzcGdiDdC":878070329669,"xuzgTdGo22Tt":785243784197,"1Pj8Vi7fyQPz":856009490085,"kteqh9I9mLe7":7218099856,"OqYHgGGLvN8j":668806411043,"bhIsxQDkNR5J":6426290754,"FDTPwkNlJ6Ye":456014323603,"PdSvK9S0DylS":49630007918,"N86UzNjdjvdZ":807922436278,"yycEYmgecNtX":874573480151,"ZRqR0nATeAUt":103522733425,"qXfQrINAssjp":164400969158,"V9YrxqsSjghQ":129312433990,"Z1mfpxT60r71":563697013154,"JF5J2zh5HV2t":484466239511,"TGbp4gt9F0QR":19608409401,"hCyOEfm8ruwg":82420102210,"sj2IO1f13UOP":505136357216,"r66YQjhxHorj":408279494052,"4I2eIQdvqAjD":620025321251,"09BGuF9FjdIQ":945577829312,"gQGXxlxKQ9NA":708670387293,"lEhEC6b1mRTl":974986141450,"UXsEm6msweca":182926143237,"NzrsgpGhqq2L":524783702721,"7UTBTRq9ksmP":914692419388,"4wjsUADKOBSg":451678042972,"SRIjex6kaa2U":707369819684,"vC0OvKTnkZrL":866826847225,"iJINdO6E9V5r":642036467153,"aOGAOQnnGOUO":584491913920,"2T5uYEINELsO":555690226956,"iupAiTn1e919":333079848316,"5tnDplJORmrC":580853206587,"IedkiU5i8fmR":128229219744,"XdkHDjLWNBMi":931506710614,"ufnLtefSRsFI":246824517297,"DeWqo7y40QkK":587374389734,"xkwn0KhDwaVB":165787590941,"2WcSynavr36r":790032004698,"KwIcbsXLkzao":461605747966,"TSIVMKzkvym5":546020680645,"6l94bwqIJaDq":183596081067,"X2NlmiqfxBzw":93665748523,"ClnvhWN6gXvA":831770897307,"dmtndxZuDsOJ":458914247493,"ZQCkhGgednua":592895695392,"HUWvGXFHo7jt":538805038648,"0XvzCUNaeJf0":576168744367,"ZDprVV5C2Sy7":914873569771,"CGwqetCj6M1k":114756751732,"UZBCNTu23n9T":768911177272,"gF9FqZFkhntB":449101679337,"Jar2A31peqUS":483216724651,"hpsturPmOXgr":771272201015,"lM409KUypbor":984095009950,"lbiPRfk7BGS4":951593808854,"0Ts7hiXuEphQ":364548316752,"ISuxIjzakUXF":758419043731,"NglEzgxNeVBb":208428597786,"svSfmavdSdha":311242059565,"eJ3wAQAUgwIB":604004085887,"wnV74p9ixVSm":95382859705,"KM3qc64FZAwH":93355029841,"wwvDWmJEtKw1":47396725768,"jr2wlpI9IZ82":825843132771,"3KvSQkr3FLtY":715886530915,"eQJVpdSANiUC":899195039155,"xdMJUxioHDKw":618363086772,"DIDiEBpsdaIg":669032396153,"trKuJkUzsfk7":916863422903,"0bqR3oQdfC8o":682061937138,"MXVsLTWaUXtV":239696412610,"EMCEPfQfAITi":82204602455,"HYnsIPAndZn6":579385886304,"2NqJOxTQxjt3":407645065189,"qA8tcUOIET12":559759258931,"XyBri1qzrFUO":74552503413,"zvaDuRTgkVgs":370491587614,"RICsXi1KzhZQ":319072161682,"414fT9U39NKe":950138570196,"5ajoKUk6PqWx":294257093456,"ElpTni8OQLfM":614441472876,"G0ibcdDmf7TP":475202779459,"HMBZqNKihMMj":424597968180,"RnP6JXQMA0mP":593096612252,"jmHlXGf2ZpoJ":957297709855,"Dxr5vqEfjDRI":475275509864,"R1fV75C3Gl7A":494543873550,"bLXrKPHcTBSt":856144335341,"kqVHjtVpL2Fr":95437398066,"iw8XD5kY37LW":265743675004,"sKndsHOFYibY":938583566803,"aRZUut7pyHm4":814313881201,"BnIKC0jD3xr8":956188825126,"M3M0JP7TxQ3s":524493007941,"otkC2NzPaDMi":698593362065,"ok2Tc41rPH9m":720935285971,"LNkh9VwtiinD":399954528185,"TUsR2GXC6kuo":740488779274,"txthdwfsExN0":976249209155,"d2sd6ewEiODj":86254244440,"vPCkKDXU1DKW":12280372549,"Ctu64YWlUlaO":224121280930,"R0CroJMYL8pe":402737708311,"qXZXlAL0Siv6":483291067087,"pJM127dJKDJO":348385264834,"PwKxeTwz95Vr":99611951110,"hmDAi8LnCmYY":744427828651,"cReIz00U4TIj":238852599639,"KTfjlhYSdbgR":698169857380,"x24fI6XSwfbI":650607032579,"c9lNRNpRLX5p":493803732754,"bBG7QPYUTVaH":623042795038,"Ijo36jGOXwWi":374035127630,"ViIhCXw6zTJD":970817438275,"aMsAZKCjSdSK":858194893214,"chCt2FqZB55h":466506804457,"tVdBOTER9UPo":275620514086,"yFG5JKoFlvEg":254166509736,"KtIvBflvinyt":683777673007,"h12a42KPWwbe":842585732001,"yrx2Z7mhY4H2":785403136930,"ySzzoQYSKF4x":651091052654,"hMvBTvqZFAGX":654899036225,"1U5CY38OxNKL":138343092394,"lEyIYcU9zfzr":165498025979,"vnmmXpSRABK8":645752743831,"z41zBBYhJTk9":187121334707,"s0AK9OiGg8PV":111771707749,"o7bOgwSnTrWW":965431582054,"rDEOZ08DwDQj":497930942630,"ZUej7wIf9rJf":488896951900,"WJ1Q40ZDhJFW":652500283341,"JG75587djTZx":840263372778,"F5uWVsr8Y5mp":874691992061,"fUpwuvcoQo0f":232376555482,"wG7eOmw76Bmv":583176898005,"XFztethjcQrh":229626090590,"l2TjfFcCxMO2":105192659019,"FQxP21bUblGd":137074088837,"1U6mwAyTKfuH":243224451375,"cLDB1OA2bHv1":218441059806,"gAXrG6Ts4DIl":951661946538,"SugsOajLSviu":31409747483,"mbtgtASoRg7h":768896102362,"iCkpwfxowPfX":371228805621,"yM3h2cXk20hV":320464999905,"0HNYi2uHrt20":399722172631,"JBvThwA3TuRt":88506077228,"8MPgH50hGzvN":920966985982,"vSbqsQPqKkah":645504337530,"syPCNjRGwXmg":708157376869,"nuHsdner9Jxz":341402715562,"6yldUS1reGte":422522343743,"qcmNcttQ9X99":974587466866,"viPxGcBoIlNm":480183241972,"3mz3p9aMzMMv":788764254701,"ARw6TIOcyClO":928477836585,"vTjg5gmD7xuQ":701679331199,"nLyyTLtM7unN":20219265703,"zEfpLeNt2WgL":529511317222,"f8G3kQ55nWIE":769471482987,"al0F7nbrWLd1":714315769099,"ysTj0qo9s3nR":191914778316,"yOq5QGETVm9c":718304757953,"uPb1xwd2Ie2o":137262882928,"cX0nEMVLSrkU":925256980172,"ao4FwB860bTm":240456545969,"vsoXIuEQ0Hbi":49189574321,"5YW0lvfZtqce":689180586492,"Elz1riwAOns7":432129247623,"0OyjobbPqsgA":314121377061,"cwOqXnbMkQpD":429807647970,"XY8NAD4pczTG":837406495813,"2gAMNxxN3Cmi":792801635936,"OPwkpFT76d3y":754059348888,"ZbW23OpPn1Do":892012869141,"pUY7TwRigF0y":815821715343,"JaNK7mYrCNXx":124795863734,"5HQwy0Zs3i4k":655108198404,"ntCNpNM9gvwp":36359829395,"EUEwtdZoOE5s":334230418922,"bjMFQolM79o4":963724504812,"dikrBCxOeEIu":287297843062,"e9jtGB0xqgZI":326660097867,"URUKdwxIZGwm":965087116208,"RwnDwsB1oIHk":552400535302,"XvMBphIsM6q5":49667251404,"lhqCON7k2lBk":188265968076,"HnJIi0buyEPe":493699884407,"S42nQpbpDhZA":353480462845,"cCZq11JfwSZy":130143777070,"ohne3zIpQCC9":824413331027,"y8jJDcHxEYYs":23403394333,"ezKpEVIQxQEW":166412921315,"vpfEG45OLCR8":573274231277,"orJgewtwekxh":5071019176,"jUu6B9xgLMRR":178847815263,"pQrBUSl96xlP":274868486856,"8HIPFgqfjIFG":681668576083,"eHgZp0XTASC3":355550154709,"4eZccbdtnnUr":176168401619,"b3JsvB6ktEfr":723141484404,"2WWpHyeqqX1A":201507954051,"CeRL73NMXrMT":155898647852,"v9nEyBQqm9kG":644977059038,"wRmBgVuJdDb8":924960232468,"V9waBqMtkzCW":905928776057,"amX41dlRxCXi":463521979984,"srZ0m6W4Zrd1":450165412047,"pxmzqqdbHhLa":403632640379,"Au8L7RYFkiRx":526518357815,"QDHl3z9F0DNb":125589901769,"csvKnepwPyBB":299174077869,"JNqfYmdC6H4A":654896089824,"8rkY5kRtF9wv":774413876736,"jGejMCNJ0wHu":46870040674,"1IJQuK3ljba5":593785189714,"H1t5dLA65LT4":804385918538,"DVlKF70doCEg":925658455827,"gIZSftapiVJ9":139214472829,"DdFNdTkvHUfB":353098517744,"43ApkOjLVA6q":149442124505,"xKLXdHBJJHxY":370569117410,"hiGyym0DEOia":373450062366,"7Ostx1lDvEzU":524306664286,"B3bqERR8mhKn":371851062120,"gzEatzfyulsg":259675719497,"jZjeD7EYWJwc":36798756564,"njShlnnlKCJf":709342303525,"m1NuZVhh0gvx":646911574,"tqATT4FuKsAZ":20507094368,"Ve98y1xSRKmq":124739180116,"jnsaCqHsWeuC":723404233879,"uyRt5gVRQGTJ":588994635739,"Yhzg2vrUhrwI":65131349673,"eaqvJKbSBW9i":583760542486,"r8HoGmQbVdTB":605008260953,"T62scxVV7KFr":799776631916,"aNZJ8bslHfRW":540697165318,"kN3z3wbRhdFF":33796729861,"W3ZajQb7fvdb":36776845980,"kv3HEjSBPnkn":785449666084,"PnMfujTFRJVk":936684851492,"wUExnxeD0kaP":295490170369,"3k61iqwxkiVL":739195959891,"bnTnq3A14xQ7":42798641463,"tRzuOwRuMDif":125024916648,"1QlVSJTEAWAO":181909964998,"ioDPlMntRmGL":701815073792,"UOjKBHT8xWIQ":720137083642,"SRIaHGcIgLrh":426878052939,"ZA3Yhf2OZkEP":968509510452,"OZyJJMEhaXqS":148033778238,"BClqS5m4agGM":547246841528,"Y1rSRC8Cqfnc":196057997085,"M7cOuZ6xe5cl":863205405343,"tvD4f8pPb65D":169181560522,"2T6tNSCrQ2A9":974880286407,"qh85XAkPoMjT":429890831871,"fIKo6jwOE7pZ":95390903736,"mdJEDdtSxzkd":810429428234,"3G1WKHjySCLL":460576220437,"BDdgNzfS9DCa":957316403053,"wL1BHGpzuVfu":698641733804,"gUa9hkQfFG1E":676354308696,"YI3UA9DQZLEG":963943258039,"9B9sZ4syeCRN":144284758765,"XYzn2A5wdBUf":835779374338,"10TP65mPUXvu":380913842522,"ejcTdeeRBp2Z":817611088424,"khzYJ4p7MRi3":956834994949,"Vkfz2aXL6HrU":879595603837,"blPjm88TbWiM":961243866354,"sXA6ZNpcTZbi":118041081440,"9L5Jz2P7MXLO":401799764900,"mEcLL1u2CSmG":211276242186,"AM2DYLwcVq1L":868356095715,"bbLEzpVyTHIH":56419700650,"wpY24DnZHXbn":730651550933,"KOwa7amw9tNf":284242930145,"ryvIiv8oQp8R":615003774777,"cTxdAyTtaNI6":463310597915,"ZVvJRAinsS3O":222619027878,"CCNzBxJvB7ns":540849141187,"0P1msgmKxiEG":255903220515,"zNhDsYzbiJsv":308805981890,"Tlv7uXl7iJcp":128684252157,"tBKMBvfodYOz":123543022932,"SkA4rVw32ItH":173084565799,"hhpQ8ytc8BFc":928104477679,"br17OehgH6mX":282107578753,"iiIhNFxJWRfQ":248382736414,"3Iacgoz1DbHi":663170905120,"OD6xSnCsNBqd":923590714992,"bvlAIlhCqblY":883619850096,"4dO3iQb916N0":764551561568,"DX0a32J2l6SB":72745062537,"0IayXIPN6TmY":640000790606,"KK89YrI4IXe7":887724499621,"3o9PrVl8LZFY":725139039470,"R8V6VvOWaUDU":975246664892,"KAaqtEZ8YvwS":312328893961,"GBu9WgpCzwsy":216702354813,"nvFUAmEftOK0":212175368427,"LHaGMlDerO6f":707178347633,"WpmFVq1YMT6B":947016024053,"32tjp2NdkMkN":354990062680,"QxOmhHmTOeVB":393733218295,"ovgr7ixGDOz8":701062109187,"7OcogtsBaWCW":237967983355,"eJuDxRF9MztA":338602471068,"wlnpNsy4l3op":654055980813,"LDxQL89RMvyd":470167261159,"KgPmgoqNXXSI":285638264725,"lgzPyKvoKxlK":392513557510,"Jt3UJ868qbin":719702306034,"RRsqYnRWuBMI":243487998825,"QsWD5JmBg8AV":559800289991,"kIQr2oQ5C6SP":68915060069,"pWZtBF8TDCrE":353182045681,"Sl1kv79Re8pK":962452251607,"JNyXo63G3DiD":685963656558,"ch7Si2fHbNdj":882219790387,"VBcWa8OWxhhC":681034879182,"kWNgnUZ5YSnw":343238276665,"tByvj7UGejZ6":638350492153,"U2oi0bmYf5mr":568477640719,"wi33C9hHC8bj":358759531261,"cSONTlIjhvF4":129610883634,"XzlgnuyKZn4k":859972900101,"bpDwn1cQZUEq":43090860505,"Q0ifvIRJ3mvh":539270692135,"RAREyuAYTB0F":352523727679,"XhqAvEVqiGkV":573674437956,"59zPXl8GFarY":482154439834,"kJLrIQS0ARI8":447275924934,"vfKALl50Jr0A":749268765881,"mDoW6NxJIBn5":936448522462,"GcNuiod1syJg":823211173348,"aNBWELiFVLef":752938568076,"hdRjGnZtCtRC":662586248832,"LZ1ihdoDh2hb":385312767469,"DBoAH18tmwmt":60142863005,"gflvlb4cFg4w":939480754766,"a1Wy8HPehr1t":292851085383,"kfAMWBb9LqBu":969295431408,"LdwL4BZpr5Ns":505385923510,"EMhsFG7mu2ZP":982156589098,"BQbgkJnCzZy1":44939025012,"SSG1kgDvFM2o":30686905530,"jNeeb6lGYGOg":445229321722,"8r4xdr73cOn1":123582941237,"uHyXOY7PTTt3":418043329515,"Ok6mLNTd037f":557185865652,"lWqbbJ8A4vzY":894075887654,"ebIMbYT1u4dE":134818605196,"IJGnv47UZGR8":727317612110,"ThO70eRgTfpT":641793891100,"UYF58I8xSE7j":299333708987,"fjOVNwm4yZp5":141350511556,"zacah0cItoWp":848004805169,"THncSssSw31O":225222961561,"YPAvKHkKSChe":652740919271,"gbUST12eSqbL":569108365949,"Ohjl0reJhZ5X":56255586592,"lmAzpujPQYSl":173307564037,"XHDt8el6C4pf":376774519195,"K4QDsUzzXg2T":326064710779,"xm1RqRu2XiwD":887147056525,"2Bjp0CTX6HxM":26373532416,"5Z5hytcbQv1n":36002673830,"sht5txE6wGlp":822497890948,"INRGXYd1GIjE":347728104847,"TrMnCnnuEIZ3":402223673917,"Jl4XgfR2Qugx":734994765222,"Vp9KD9rPRQLx":152121384019,"OddkgONW0fVi":4807229427,"69U7N4Rdslvj":745205649354,"tjFoJzdFKLKV":766511155061,"wTKtJ9JXUlby":88489497732,"nRgbY2KSGw2O":25769661173,"aICGHhWYr9cn":549567355058,"VdxosuzMJ2iM":526991019319,"AxOj6FBIEhdP":56208269746,"zoGUCxuxHkB0":489148130654,"yFMvhxvNrzhw":405036485995,"cHU3VUu0XoMC":143770214395,"7JDK3yhz2kVo":70089952924,"ceuUMOQsIIUQ":884774694260,"KVUJrnXoZX6N":78974132838,"KrPNh0NObGUF":849556046783,"jZGEkeoUexJG":741112607793,"WW1XvE41XFwj":527436378499,"4aJEhK1nwc5w":437129101120,"pZatw6TOHsEM":586079355117,"g1KVzRwbvIxA":343996605508,"2iyCxDJgsUX0":730573191043,"ELW4t96TTpOl":101409354082,"ltV6ZaIbBxOR":674800886360,"5qexm1ykX07J":234950978547,"HiUtkKRsqfPI":309144190680,"DDlnxX95bwOW":105743890923,"P3FJFzcQr4A8":804639786655,"7VPJbfirUGX5":754658576502,"XwR82AHHKt9Z":915039075754,"k6IRntZqsECz":260084390378,"upYnHcVdKeuW":48198735836,"hsqvv6A1S9Wn":672381354035,"LYuD4SC0Ce1Z":657659179156,"Qohr6imFABCG":352575025339,"6tV6UWSUGmr4":497618698027,"gcQ94KQwyM16":462382526245,"qXiCND3QWMEe":448998577658,"HlhhohlzSLww":538841940996,"HmOpj7wehUGk":38724272332,"ZxG3dBuvVvXm":944414675619,"2BDP22zpJ1Sm":20971564806,"l4woLwLyYfFU":150813972318,"YPOWRoLwkFj3":940767978848,"61BOOtAk7uZR":43451871531,"KxHX3LFPQ90g":610368431706,"OVdRPKHUOvqY":631889262284,"gfd0OYBnYNBC":584777191014,"qHgN8V5v32gY":640727929609,"I1m9WCc9RRPy":610672073163,"u4APOZv3ZaxC":250900137092,"uKRXM3w9B8VT":247274075658,"1xyDSmkbmhMx":866460510296,"ZJB4Wlq1svdF":704374855769,"RO84To5CZRLS":107394583881,"JNXbBSPCnJs3":180949852165,"Oh14vVDszk9m":180452948069,"BVYwcDgyaipy":261497644125,"m3aerwMCTmc4":226732090054,"x9Vt6eSzR6RC":582918661918,"P42jTqeIu93i":764758861276,"tJWaROqaDwDi":496299005726,"P0h4MrUZsKrQ":865301841516,"COzAUkg0T4qU":294032513100,"mRUo7IumD3M6":900847517588,"xYfWOLZ0nczL":383987635299,"nQxcoca2XFs3":950935888610,"TZK3jpQkl5us":25702542275,"anG8vhe4GtQo":966626690320,"buC5QFbF2i8f":372037728508,"dQwXPwt5I80W":475501475021,"vznG9IQ273ki":981451264828,"jtpahg6QUFAp":364892225377,"GeSK5DjOyglE":545559667223,"wtHpfj6xEV67":561180227025,"1xiK3vPLaKK1":962605529515,"oSsj8SciNvsF":566544734418,"BK4EdEpcZYba":244889280458,"fxQa1lxtG3SS":278200048114,"PECLikNUUKzG":513264360497,"omoIr0F8sgeO":666706840129,"IfJmaUAfqBnq":919872063710,"CcRGpJA981ZR":618069298182,"IeHR59XByy3x":316645841236,"3JVwPW5V6zQP":754468478697,"MkJmq2KAhLku":278530820887,"hAZxCyal4A2z":370251040951,"vxCtjnpw45cr":97238575904,"hFQYHrtbPs5N":223551660925,"mpZCYxneNNL6":582772955351,"lqB5XSKAbPYD":706622637163,"Spgsdsve7dAh":6027282289,"NkIe9xySLsxa":592864986140,"vsglM6eBIBcQ":623659703991,"051l7ZdEOmDj":663566309048,"d2OttopO1j5t":745883374554,"1lNhhbpxxniY":487771629749,"f1DzWbic6U7X":436412630127,"EAeeLIA1VRmr":6526586235,"sJix51FtVaUQ":910754859764,"DYJScdDpKLBf":724124180654,"xu3uLmXvgvEr":193577798689,"l2ikmqhvK3o0":696070155497,"zhAhnb7WnOHQ":185760577092,"OTTbLfPSZhgc":520067232390,"WWmj7lAM90Wv":478545675888,"VZn8sERUcu57":811635686641,"NifRHKZLCRB4":356963020861,"2NvptTrTshIo":292696526879,"0HzZt4wjTAYS":132759692087,"Zo7h0ekwJ5cb":90329636148,"kAHRnqTixUjx":382466792896,"FHWh5GWBuSJD":979972510043,"Nmyo9yog6Rqf":512625241759,"yrU7CZIS28WM":982276315286,"LCYSHwZGtbEY":618790048011,"AgUe8xUSpoDj":543931651126,"ZRh9iHgP8i2J":432725673837,"NiS1STydinqv":382020145911,"DnVbqRpamUW5":789474486279,"zg5TDwveJLia":343828047617,"Sn12UIlYhgGO":847401255401,"YLwCMKPiyFgx":145561546757,"CRul5WFNOlQd":624774811619,"7SUlaNHrrPA6":929402270233,"9ZDylDKFH11L":29901422487,"f13WyUaRTyY0":900680671452,"UeC1iy5ITpxK":675598882891,"hvzarnBxmowe":853287487141,"tgnzldvh5m0x":316082786247,"kvKbRn5LH1SJ":234881486547,"DyqD4CMhFpng":259620839675,"uJsGLEhyzcT8":714707948446,"H2PoVL4h3MCV":355813786260,"MM3zXvXhK1Z1":826178567702,"rvdwgCHjJ9hC":908589622268,"hqC6OpoiwxIJ":494747776994,"muhRD35Gg6VJ":590211880977,"0W2rt3jorDbY":554541474559,"00TBBrK3GYOS":479175240591,"prv2x98slVs6":696441575729,"JDr6I6CG3zNl":691061567057,"lnRH8EVVzm0p":404971472416,"qn8h98WeJK3O":899160125053,"DjyOrgL38NxZ":242539633934,"vNpIpXqvi4fL":177033838149,"ouhjaMSHCyQY":452333368008,"29cL42z7bk3F":141125264134,"UOTCRME0katB":195545746583,"buG1vtgxQmcV":405762777732,"s1tnzERRiJ8O":972749217487,"qp1ybVtR4AL9":626567503443,"tq8I5jn50NTE":453394471778,"8uZUlRCxcgbK":847807275113,"08I5NftbtlfM":939145851173,"W5Ol68eIrQdV":429663883126,"urA26SnXbKcd":277183958153,"LxwJ89jZHkwZ":312720165471,"f8RzIJIW7b6Z":275284174555,"vsBtXibu98L7":573899901409,"w17eSnoVfz3c":340143334814,"QEJmdGbtE5Pd":682093383652,"4OIrcdnkUom2":622080394988,"C0tnOZaO53uY":916573167635,"8bFCM3Pqa95x":375532872131,"3kHobdLw77Yv":656612310101,"o13Qc9U5Qz7V":240935331317,"S9jjEjP902rO":44187199892,"nwVCuqbxXzps":721031120703,"Y02otqtD2NcR":194911702767,"SHt3YXpTvSZy":396635839643,"xoRzC784eXyL":24236340432,"Os2ZhJtFQNPY":170198136536,"RZr3aj1y8g77":108378799110,"7vfKmrtZmhCF":511249736654,"mp0hprSGfsjz":326593008417,"oqRHelnBZmY2":192668912676,"FDgRINLb5dA9":427085906939,"aUEJOfOcXrus":840572712961,"0fZgtIJZyUMs":977283275995,"ukkAWwUMOGkt":210470635754,"d2VQl0npXQJS":188851981545,"DxdxiHW8snx1":540084788209,"Q9CKKnjTUqJl":963430240286,"vRsYOI8vmOJ1":450657299095,"ZP63UIoyNLsP":802456787552,"WxgSXs5PdaiK":626529947466,"Qrp3rCR7F9wr":127100632691,"tijW5C5sxGkn":663065591584,"2miL5J2p1pT7":317790482366,"tKqMI2lzvw04":270981242584,"yc2q1irHXvWa":743175234636,"ruAJA8tK0GoG":367231858650,"U4BO657DG0WK":789994148952,"V4nofatGWpi7":844873224369,"5bQ8zspZWKJS":666444352012,"2xLpLlrNerqt":471323660226,"QjUn9aSva55R":389050731878,"ORmoLrmLR9YX":848538776294,"EyLHrd38L1pa":500190415777,"w228VnQH68ST":587821775989,"xzPTTLOIQfrp":450645518724,"YJoz1wAkrChK":519346213635,"oibl2HneLGg1":415747748293,"WgndsWo3LvIC":606454063855,"1O2DfQmfsCO3":131383848397,"toAfQtaBBerr":920179936912,"WlAfN3hW84w7":793641793808,"B4qjwK6X4MJY":110459751841,"UpbmrShmoIaC":641662979702,"NNJmHPFGIZwK":961149703413,"bu8z6XkPOTYa":232948242756,"wxDs2epNOvXs":544991030510,"ZT1dc1w2vs9I":270318186561,"sf9sVwh9sGwu":121278343584,"iF4H3GC3gjKk":635119727780,"Chb5FOoM5EW2":980638854067,"CNuM7La5hA7U":315361731112,"TKJ8K3RrEzJp":221668898861,"uhuP1Jlt38T9":827850353672,"i0rlngPYtZbM":201628590366,"M7qma7LtmR4G":601828649091,"aYBr9OwnXhyN":261745335496,"Fi9BBCaSnXVY":143760167828,"2Dw33jKTkYV3":576278028326,"fuNZktmRPKvG":339752947968,"NLwsVFDARAHW":805680141548,"d5BwTnw2qjzm":829135161011,"Yy1rJqiO6PVL":410076698543,"49okQ41RllI7":195122370650,"SY6krOMXtRYD":435678919301,"T66ya8duxQwZ":407779131008,"kjzSHoFl5qPB":593627413773,"MXrS0v93AkM8":384909808353,"QNzxVgy9ReLA":675720532238,"q59yddDnI6rS":170125129111,"lUabCqefV24j":393546300254,"7kxX9doQb526":881304176144,"X0yaz96DLBwU":726277658704,"hQoMSmDleTCv":818997786309,"AiUwwMvrZ7MO":415355173364,"kzrqdL08LMub":485122358949,"iUTZ5vGQtanQ":433879659987,"LcJuOvWpL4mL":490515978423,"SwYtkEepvIDa":476080339137,"fwDtTbk1apW8":151732950143,"0JQEBtF8Bu8J":211569362232,"9WwzxQ510erN":920505266853,"MCjFCmfGqHkj":943736762782,"mBo6B1bFYgC6":178932148256,"wY20qBRBMkSn":430632192524,"AnGVOw29C7gh":433746014040,"eoZiCu5wZA2R":553021263317,"fUlYGheq5bSR":475959726879,"Pk5S3gBfcjaj":380726675690,"bNVGJxwVBBum":887214462346,"3M6Z5EPD0T9X":580572445616,"QpmZJIrZ7IKo":665523231609,"kjFDmqpqCO1j":66328468385,"gWxYh9vwI3MS":818680323595,"eopakw85B9gt":897645114001,"yBMXblHOcmDc":564115725456,"j8hFMO2tl43P":823474652556,"6sR68IA4ThwO":950345203024,"DeJE43lfzwvH":398078695861,"fVhJ5XzP628J":748278117612,"mDiTSRqpsG9E":392300467655,"kFoKw7h2wcRy":848673197405,"rDnijhSwF69y":748473011799,"I8ba9OJZOCRf":140052165661,"ENFOsSDl680w":591171035635,"JWFjzGEPmLjq":501366067897,"c2VGhEr8Frgo":979717727312,"kL5klUhaV0XY":299423106848,"Y1c4UcBNJ2jJ":696880665297,"LaLgyRfThuad":557230198488,"SdO5gqOcs789":419593268327,"vfKvlgTXwHsu":556178076416,"TJaiAeONyAyB":86847956871,"ftdfN5m8z1IJ":300044099885,"MvJL8jgddRxB":489156744652,"mq9wkavO69se":422554034698,"JjjpnqAtE4XJ":631498534426,"L3gANIWL9PiM":162836522298,"wSbyF2Gwy02k":817586352865,"wcvob6EaXi55":526852041591,"7ouoDSZkIQsx":823771988977,"Buu2RNGCrv4v":130570063537,"W8Air3cRlSbW":13253629011,"6Ytsaeuhr1vq":317207351739,"tQnJCiuZykml":150734500020,"vxjdJNbgYtRM":695075572680,"txZJsL5UQIGu":394253807467,"5fLaE6VKvXaR":951220177967,"ezpW9saDZRF6":290191814604,"Gd6pSgC0tnYv":987472187512,"yuCRdmtzRknE":500662891335,"eG8PJxlLUNYZ":163666969908,"GjgS8PvLoIPX":510518129371,"Kb2o7Xa9zJT3":947297639558,"Hki46LUo0217":478356958914,"mWcsXOo73mcU":887060425129,"qfyCpjDAUqv7":859396976279,"ZoZstuvY5J8o":119290215360,"hyyX4Uzylyw8":744597586700,"93UsKk320YMd":879888110649,"ORoJGm74nQFZ":209083846636,"kgvfznnjukXy":496568306228,"5c04adUOKvH6":199495737616,"HoFRvJVXGB7s":73945840379,"J7gv4sao3d8g":75243763113,"1Jt6pWof6saZ":613504299529,"adAJVxJZaUc0":748949590597,"wLUkcFTJdjIk":281686246863,"khvso3t2kpuj":878029929580,"LlFIjdzZFPTU":758385082743,"cHTW2n0GigsS":619007799890,"EV1ulwtTUWed":880102088052,"25T3MV89ghtT":546932378899,"fXlpPB7Aiwx8":326349118655,"Bvp9XVC33PcU":294111806187,"02EcJBVHGT9S":65256929486,"HSjUBlNBfgqX":539713465782,"rLqw0hgKehkj":623822837189,"3Ir0bjXD1Swc":340622609758,"iFNOHreiJUB8":729380969285,"1qk4brUkSHdv":103750007860,"q43uZ3L4MfWO":948764153085,"EdmiymQwVd1H":265760962460,"dWyaHJu35tCD":807896607886,"TRKBJVftQduo":395729889202,"TXYCW38TiLuA":920401611667,"DRorZXxHfKFf":798171955369,"WzxwJyvnurUU":854802457656,"JwT4g0hLMNqk":977475566494,"itLJZJbYuKbI":631106470164,"zwD93nmAurUD":274862640880,"j58XSehgD1Vf":560987711382,"YerCupirgE2p":53623882376,"nKATiTcuEGv0":40456227720,"Bf5GyRYtfe8K":215279235640,"2IZYU0AP8kcp":568800531429,"rvm48Jism0sq":37037473297,"ydzsDL5kcACC":460767649178,"L71yzfiFLL5B":971978463386,"W71NJ2WSehfU":880269854912,"EPsgbZz59nJe":589028168178,"70fg8F41BUFm":66337131461,"DBEYQaIGXn2y":837058927066,"vHknDGFf1EmZ":577007151650,"NbVyE1NfI6ag":284108944481,"p1cREIeNvONP":848594216285,"RKm8ayIz72zb":242490688505,"sgG8EgH0HF5U":777998994561,"nuKzVWMntNOr":126443405042,"ql3GRd4JfwUe":400986146286,"zjyUXU2dFFo4":500887596480,"v3a00MmDNVVT":220730822605,"8jcDnT0rTFxH":447680837866,"zVEOjuk1rJcG":860549804052,"6uqlPnkF5SEx":634086427451,"IP29FtOZDxzT":21505368185,"Cv7FMM2TtSYH":451918653593,"XwuXUYjuVoP1":49310259094,"3csC4T6UIK6S":886682829658,"o3d93K9C2tmQ":274202790655,"zqEs8HRZQBoz":469982956835,"PglJ5dUhiNi5":750832242604,"b9F5kmdAuUT5":679045826395,"bRTaaQLX0o2b":470151950665,"vd32gbvLNkUX":423175481862,"WrZ34YrFEZV7":623238046564,"LMo5b2FDE9kt":51745218928,"u1EOHBwnhkxl":311305353539,"niVaNTo1wUag":545690388142,"WjuSkvVOBVZ3":734816239677,"AMEgND4s37aA":813508539851,"nBQs5fWdcTKM":365973124180,"KErualT8qlKG":365782401861,"J509cKk9zspE":499501654701,"q0X9CbOeDq64":696651204199,"Dpd0p1qK5EnU":761053485442,"2qtoh7jtApyO":500621172726,"Qc4Ij0NfVZjc":626276678345,"gmgqdpxk1Qvs":322110121124,"XEguj1mXwCJB":440527313404,"PNlcXGW7pOpp":607321929270,"SqJq2B2o4z8U":279092750681,"SPBjT7Tcy4yT":548524331320,"0dyUcQ6e1ey2":116760712572,"eI2hMfOHdNRH":823573768714,"4vhZScqzGveP":342890624277,"9G8g1mVPhYyi":363386855998,"ZlXYlbUR8Z4g":441498094643,"kqvyhVyJZu6Z":79867883990,"jvOxJBbPXH0R":589418041590,"8eZQCUFlrfFZ":982230203732,"IC0rxSnjMWb2":447567850683,"zVfrkjzB9nd7":535906184203,"2I9WBrbMQBFv":351967595457,"aOzNsU624PY1":635139128873,"QFQyqEnCb3hn":219880176781,"eyHO48oV0spM":916241419632,"e3zw17PDqbrZ":926609212941,"twfCC6WEo6Td":970381381509,"i5HrEB1TpNYd":308689526197,"WvzRYKY9mcG7":495912563427,"yt9hw3DDJICy":486345435649,"6u2bPC2qPOV0":959736960673,"GGvohxNvbFiJ":573958351496,"PYtyeeehkLO0":441420089395,"WrW1pwyX61Ai":90517234258,"4EB4DjZkm4bx":138288353171,"nZ5w9pjySq2i":984727737053,"FVhGMKwfnzuw":715079083460,"OOAFogIb5JrM":455293713325,"l2xyo4k2eHLr":107991861172,"4to9YGvcJbWF":431198476390,"mTwPbMoQnuKG":265608303824,"v9e76lt0HhRp":149594879493,"WJdyAXoa7Nkv":55663484680,"9ScBzeH90161":922956437299,"uxgB1FKQP9pl":843460599970,"eg7YVKPyEUXe":20247649956,"Ft39nMlqSI3U":371632796621,"DrBdWHoZqDSF":615827759075,"cXXIH2mrHI6u":440963435671,"IFvr2pO8g0UD":985324013394,"Jb1n9CjfPcy7":323690264362,"OdJ8aWUgpSgn":798587185933,"3iR7VtGbApWT":133955429055,"YW7DKyk4ufQ4":505619988925,"kJvLaZGsx57x":79380653014,"ByywEkfIfR5S":474785517436,"VWJSWNXZxTYs":7673999681,"Y6ek6aOKjELc":645087852170,"bDfGOlCYXKgs":493245702947,"ayaKZS0PILND":483775885385,"UQWUxvxBclRE":454965118725,"9tycoYF2uYUK":794116314306,"ticIeSDPANNA":283928034516,"i6edu0NJcvhI":125619752561,"10T5ecYoqfoi":290554404460,"XoHV7AcrTEwQ":846690734127,"iftg44eCMo2v":365873131833,"vy0Q3AtkCPmB":456410897848,"VjNq5uHJBuqs":21417024839,"gKpTw0XxwfzD":557700624986,"57b2o8wvR4YA":185448927601,"2aPe2XBmNMs2":186003715826,"19WKr2liHFv8":214264409196,"v7HjObEvuS7L":33006343044,"9ZBajTk1Hk1h":637879817756,"QxjwpgOUpO2k":594302529834,"XoS7feusjAUh":35438853089,"k0Gl9trGX30K":286837598537,"gursDCMQGdbF":605934279892,"RZ8IcPMGvHxH":342910328234,"PXykCfVYF4jU":84433211891,"5kqGmlaIW6CR":315882169475,"ozhMhJPNPwUP":819567941729,"tF0WNBg2UZeR":402518176839,"JJuTDP3N0sg7":603556401363,"lxBAxdjMbv7H":803826447114,"3x2V5i3Y4T6B":103030829616,"H80Gt6TmQdoV":301653643709,"51DOjhmZ3l53":481266915956,"aXbBYSchZq5E":600489345164,"RPAnM6xMjPKn":793127830003,"N1vNwMnf4vN6":411194050514,"3b5y1Pad6N44":581916033166,"Nyrgg8VsFVte":238966429124,"vtpuVE4HQb7e":462535399262,"UNlynk5XHfIy":178605710743,"KrydvcI42sVz":316961794373,"2YcIUUF6eDXO":648294207838,"AtTLTEGsrhUz":415960418868,"KBiLE3PjQr7W":423218632074,"vI4zGOpBKEPC":251706985224,"27JwDPIrRdu5":107066640769,"FBj2Wfo5pEfL":264719617178,"3tZw0Qve9lPK":56070018573,"zw5hLjCGjBNF":280075856227,"9fYivcaFNkmr":280716753227,"9qTpDXl0sk4A":421777543024,"nsj1NOCB6UaS":774647425894,"Ea1rRJw8LiEX":784768563552,"Uv5WdJbzPnOn":755256591599,"0sBw9F785Hg0":404831454008,"44ZUOsWa4PCA":743169940135,"Q2EuhgS9dANs":852394382945,"a4Pdden57FNZ":975695575545,"tETIoyne27Kv":721347226844,"zzKQb4oIjBdR":385643638516,"0KEu6BFuW0dZ":789674928344,"Sv7t7LCapeAU":421563810564,"TPIaMCB732VI":592865342644,"8fBP8qxtJV5h":131704492332,"lDX3M3X3uwyR":699621998779,"CXQgLnY0bVo2":382975288165,"9EjcfQSLrTlP":699953238082,"0DBAVY7sLdV8":453986298740,"kYcIFxpOA3L5":42290151380,"HbLxjzj7PLiB":923598999341,"Ml4fqY6ePmhk":329234222974,"HeD3AWCWLxwd":560466506840,"QcCSdktE6k8Q":528583577832,"TqMp6a5hZyLS":293890731453,"e84vsR6fi2AO":651243784374,"sBheYaJK3pzr":726597846013,"3qPOHQE8EB5V":61473420536,"yEexsYWdgNm8":153176579574,"zljugSmNWz85":864313651055,"GyhKIrKEA9KI":345642617686,"n2UBSZybowDe":311855203052,"zXlUCswgVcHq":488790029095,"Y8nWst3Gsvp6":681614491356,"RLix573rGdmp":237823671438,"JgrspfSbWq3m":113661262038,"jm1f3yaDpZ28":500047817157,"2KgISOUBKPD0":70705678150,"hRSVWKl4keI7":925661465464,"6Kg7e9TPs8UA":952445653323,"FZ6DZUNNAsav":870524990799,"WDwzyZZolrwt":445738040654,"njoRijsqoslT":312599372786,"PqGe1SOKGmuT":449789366711,"1AfMo7HvtxPo":572169414554,"d35o2VpQ4R8G":130294343141,"KcWw7C29aWNF":111558688110,"KijDG7xY2mTg":826932377825,"iouBYhd34LgR":878671905191,"fZuaxhqW6PpW":318516643210,"4vygtVoQR9Ph":606370372279,"Gb8Tn0kZgNls":501827252001,"nCHatY9DcWRz":366569385514,"rAIachEpor2I":166455589233,"0KHhLgqCunXG":382707758355,"Tv5xsRCwP0we":672346248401,"iRHccLkWt2OZ":458519391483,"uNlNLqtyyc7r":370658307010,"wgNfyTLz07ba":954451121813,"kM4CTJgQKgvJ":641867139963,"RY4xJyULnL8W":451229901070,"CfuIz61A0hLQ":909978820739,"mOaXD3qEIEPZ":827739683975,"F4vYolPKhs3b":104846160989,"riVO51hykNqV":496308948101,"kEAT3SGpgiO9":260359481499,"T57aPijkq4Xj":661192421681,"r5plMgKXhaIt":974392981066,"lNhV87UFbGUG":579411788774,"hWplqBxnpnXd":997219438450,"dpXMaanK2OZH":624604437712,"RH2p7wRHBOaz":880477388639,"KKa33EDv0Aro":792002903527,"ltUsUGEEaJCU":342553458680,"hlZewH1DfWv2":770245379126,"D6gKOT7oMzmO":601811687046,"syi523WLIcCJ":545762724201,"9pOvxbF1K0wp":177149568049,"nawlUUmciAD9":761340168256,"0J9aYBLkvFU4":482504421964,"5YaWzofLRvUx":425420166351,"UFuw51J58715":914090799239,"2Ip0MVb5V8ZV":874211433311,"ZdgYvn7hZe3c":939222607030,"Zy4WfUUUSPuz":519808441048,"SK5Em7OkZN7Y":311962224964,"3OkvRMMaXmY5":267364082294,"GdjuhNN2knPF":22667079252,"fmdWoLPXjWV1":918943290968,"NzZwcIQBLIMn":127205749045,"Z56xv4OGPA3l":474542490210,"WydhKqOFiwm4":563220766975,"bM4N7TpnSIKR":616624570410,"HR2Vr9EZ1BJw":923144202029,"3cKFUUL1q7og":804575887152,"RUEFrqL7useI":511792519089,"i7F1ogi8a7Yl":337712290786,"GGiEtYNuKNdx":136284669349,"5wfdyH5n2eig":632618794405,"WoXTaWv6PNlj":917175101930,"4WRzS8w4Wkmz":702218464866,"cT2BKJhpN65w":990168036153,"H978iKukvBou":253155645671,"4Kxn2oxNcJVS":590193378006,"UJkvJ4DFSDaa":510709343333,"rO68Gcm4YXTN":446658658214,"Z8DEHMtAfufz":11179179887,"ZJjC66h0r7of":742238604563,"MQRqE2BoNTLl":227987002962,"JMFWb1yOw2bX":174515714088,"usnyUgqgnwMa":896374962870,"aVr3w114kjIW":456740055459,"6cvyXvjcbmnw":241062867164,"diTkGzKjFt4b":940609648713,"yPcT0fMEg060":690015175290,"MjYYfSrAkqcW":125127356470,"WsWpGfLFOsqe":465107519049,"Jbvcj2jBurx4":395038703955,"4fcVZOZLaTn2":972096543218,"EPob6KLFW7CU":513873102136,"cTNEJPZ7B6nc":649589825331,"oeU1MDGdSifh":119379650118,"MDN5OWm6enLA":584060932866,"gaQx6uM3XSzj":993951043542,"0bctDq8Z8fKb":974044708952,"Hm7EKe4sl7gT":871173871695,"voMufW0bUhd7":89311386532,"lkcGrJUktM2f":242443171467,"uxSbdkphMOId":750439923693,"XFhMxRFf6DlM":772943820388,"Jw2BsUiWlYvm":632072552521,"1stxJTj0Ca4N":441719920602,"iZ8rvEBX05fr":963250775377,"olR4foEn73bp":201700847899,"pVl6gHXhiFo9":398456104175,"HtmXygSTAnq0":187632660430,"ap1pH38PucG3":409403795048,"hBmXoba5ypaq":300655654807,"PuMogJHL8ZNx":595435149555,"djjhGuZ79zMY":854093791555,"bnSsUrsz5EwP":497637517613,"UxrGeo7GExPV":567866909185,"QHbcEore2EQw":181257057116,"mo4lGWH3k4KE":150713511876,"UJzS1nntbaWc":440120366008,"UhGDNstMZvNi":181715515866,"9MAd4WBHIQQA":47610419655,"Fn7sSwcu1oWc":559623980034,"Awjb9IsEKs45":749550755345,"zEQfUu3ptYlU":595045589210,"uQ1ajYwubXOP":597448365068,"TNxkXxuvRo2F":773309592628,"s9ThmubwD6wn":17149226789,"ADtVwweP4mqY":827204527106,"M4Sn9ALmRH04":468949546801,"6rKfPVRGYg9t":502772545601,"R6heexabE1aB":345057840039,"S0iQLLUQhr7y":941357367935,"OY4bUTjBbDJs":359619166362,"RneINz6MaEMW":78177222501,"2YxApnqL1l8G":161615433723,"79GlstkXUqzn":408994991199,"JX1hIOqgeULm":253217582000,"UcBHMVfI4i5D":383865505326,"QMbfdnbK5W4A":290835239661,"cYGhieoECAJ0":867707660391,"xxINatgDXRs7":492980033895,"8pdngrHnCeEE":214065685377,"9tByTjL1pg8m":998952291834,"lD5wRCOYfKmO":720265739110,"UU8eJJnyo3Ss":858441445471,"9NKstakzUyCn":522924084958,"PlRemRraTats":962183810371,"fmhhuNOUWIeU":467270239679,"9Vo0Gre1hBs1":455413127596,"YLegHPMjfy62":788322477293,"7TjkcZ6iE6Dw":588298097598,"YyLvPTXYWcPB":998226487284,"HoMO7YtKJgVU":622152710431,"v4k1dBY4fm1A":781515277342,"0ck5dl6RlsA2":374289308578,"3NNwzHHRtrcl":435594163146,"nu9oCHst9xLh":30992443268,"oieQcp5cCYVD":423530656068,"2kHJGmV4buul":349264379202,"vBqa042VItx3":890268178831,"GHWQNI5wpPcl":372681446254,"u3uIjAZbzMQP":712272738850,"QLyyrJXHsDed":93153061774,"BNoedMEqYZAe":394490490049,"lOcd54rk3Qaa":712519570108,"ddwtOxEiyYxh":811984493879,"N1mfhEeRL7DP":15988336268,"r6mgOt1B8itp":597234053064,"g26JIgx6g8Cx":78155964200,"RCkRP5a1437G":709664567188,"eyAe2RI9dkz8":507266994675,"NHgJWxHpExLx":824484559963,"llGuMpQqDrfV":327350034658,"OYAH4PXCqhpH":799736246118,"ObnLdOFaePoR":19978863786,"BjZ15zxLJY7d":562770905608,"DhZuZ6mwJk1A":587160203951,"6381tuXZ6BMg":731070704058,"XYe2f0HAfX7J":716426824197,"mfdV2cYandoC":304382553525,"rQqhlxARH4dx":774674962824,"mCcL8LhsYu8k":38269096502,"t32oPc1n9yKN":276857919589,"EkGzwJnDePL3":85135702268,"4SHetvxZw1Ug":520546250344,"ZvfZwYfrzTEY":105798583662,"8yTo13Grss2Z":31521422533,"SUzyeSuQSr3T":895098986075,"fSgvfAW4DdEj":934729865570,"XJ7V1ht2mXUf":98706563007,"vNK1BVTpZL4g":14655706073,"Vu8EHsMh24ya":73492378898,"07iWiYH38Qcd":425643114919,"UVgau5lUuWtM":321778938282,"CFY0bQTRByjx":515596982159,"crZhCsJDDgea":188562708590,"M5B4NBIt58MM":524673105237,"IeuSBKNwYiXb":806403404335,"4QzzqQHLH2Pk":377595907147,"YHvkZfGmEIfT":602353554911,"hr5JdT2DRAU5":953130525538,"iL9PxMPNf794":947464344948,"VzVMixepEV1H":258396061686,"pX4xNYniGKQV":515334150725,"2SMOwaKJWTWa":568214895188,"LTHdXhAzuTAs":604996431100,"Flw5KjPch2v3":879417377509,"iDbtEZJwPP8L":170933159112,"85LGjuovfBFZ":500748994110,"G62xdhfwhvca":573985946866,"HdjjSBu67FO4":474111100019,"TbZNUe3xZ2vr":748673817369,"HpylvHpbgVAc":79795923708,"Ly203CrbotV4":536374073299,"TT0ATj9Op2ks":532860104702,"29KCfDp5iX4i":282492213271,"v3MC5LuwF7PP":716130569371,"dnqiWggFsd1W":321384825236,"kWIuUycYS9rQ":973316016717,"NanQsko4DRmI":330918712198,"GXdcdYg7mfMe":887606419531,"Pasb5fxflLcu":316400255543,"8709q52py4oV":552831381409,"WihTBsZnPE6u":802552293381,"aqn2h7V2ISZ4":205631562122,"z0o2iKjy48hF":116096344000,"YP4t8xtAULHI":521821982723,"AtGQoGFICg5h":260477201640,"1PYMMPTWna78":788091013956,"OlGHRXExrMBJ":548106628345,"SXzdoU3HSJ8Q":468819659675,"WfEzr3yQ4KRV":828074995155,"bQJy5QeMxuoi":496378398373,"4CclVohBO1SJ":539587843997,"jW6c8XoNsFvH":178932159575,"aQ6MqIjinKjJ":937724294368,"NehTsnWUtATt":102887187071,"jxbZmf1jp2WY":443694847461,"BVwm0Uk97LIe":152662692104,"aFHtrrV59CQT":432168545600,"8DhvVjsHcrqT":561310584291,"nfKKU0oviCyi":166661283987,"9LwT4yn16LKL":325863252999,"qozALnJ2k6Fr":39059393572,"snLFUcbTtQub":419272416622,"Tesc3wvtUjFX":409031533477,"b5vLXRsA9ybE":160532725145,"sePun0vGFSmC":856709657615,"XWpijrSLRF1y":724333429219,"8RN1anc4MUDj":495234358723,"8GI0mPD6GfUu":30830475779,"K7orx4Wnyetm":354609487969,"NYF85ElZoaS9":818095023602,"3CPpoLWcctoQ":502283577403,"ItlnX2B7hQpx":328284364585,"zqebDJpViuMt":4482086374,"iASgCvGlaFxF":56948307636,"gFoXU3jhjs9f":717095618894,"kyZOSfRcPwDC":120936480642,"I8i4L1Xzb7gF":147484004558,"VeCLUr2olVZo":917380893024,"cCSnA1CdeSHh":644769443067,"STWgYNssOVtL":590705072759,"UvIG7w9rH5tX":620268083564,"9uy3uudZG3oB":93103144311,"jBTtV0a9paA3":224213025470,"tGw3UBu4iT7C":764425838204,"gGcQuaKfseCF":279120403098,"oPju4UUSUHCe":70908212172,"6npTDpfoCmht":887743409078,"822AODri1e0r":741751348941,"Js6135tpm6qQ":667614375373,"P12zountzdoz":394216495489,"rxT2GwjtjO1j":109490038616,"vCVHin668MVm":132946837218,"f1gDbp72Rzwf":922408026944,"YR2MkJOh0EKt":477404164042,"HA1Vmbk33Soh":212717117225,"eArSn59DyF9M":676763290323,"Mw7UBr0LKbJH":385463166512,"Tqf4yALaeMp2":200022421609,"zhh4ls8Dd28L":739015508172,"BswlIp9tnNEQ":681530168435,"ptcO0P3ZwJdb":789998988132,"19VCiCs3Auab":834198972105,"86j1VZpAbXgM":786194954016,"U7hPgq8IocnQ":205592574923,"RdXBVfblPRzi":771799579381,"wUITI6OfD5ef":907377648853,"yC0QGyhAmtXJ":537257514355,"KytzyDOiS2QO":965109440175,"ZL1maZ6LuZ4V":803486278299,"UvXN3fG91v1U":30597095838,"76Y2hBM1Q12z":878416874541,"q70jmmLlFw6X":407695065824,"vFMD83ODOB5E":720716477890,"NjMGcXWnqFtD":834002654235,"MbOsYUY23quH":993102958308,"1pqqkHf9ED1c":370363054654,"tZfFDEYhhweg":990052886650,"bFASv2sxzfeC":317668656918,"rb799dwqp5rM":656281128994,"ZDvI9p9Zkhxc":304899346760,"vm44i95PMPnv":879795457474,"u7bFWukdHDex":379628724788,"Lb3yHkj6S9P9":919027934745,"IBDNxTg5XMWn":657790372603,"7mj7EyTFgNjl":140177011656,"dEGFkBtccuYt":261376329616,"aydS6W8DrCFJ":328599564718,"zwkcO2mzoNp0":977953961010,"53PqyE6v1Hd4":754103419414,"StUctZ7Wvnbt":289752467067,"PFf2BSkS04ra":584322124662,"U23M23HYL2MV":444441180753,"V5qkgtL5AiTG":650108340966,"YIQZE24qx1nQ":871442889012,"OJl5DiD0feRk":942138819679,"TRBw1JF17vr7":638966330590,"0w36PARbASn9":127988647752,"CCwVF0CRuNqA":128227077593,"aNXEGrpXjon5":15878998789,"6IZtDx116Z4S":512418448032,"39Re166qYkU0":342105049697,"TbCQ0lU6SQEF":419581556228,"cz8bMwAmIA9v":616846757131,"WSZEz6fPhVtm":609200272732,"Zza7Y0k3pFVd":862870735528,"kBzqMhR12lR8":479067024994,"hMe6ZApZiXRa":519177111798,"7muU3R7unjkh":494919805198,"QIJcZbnYKYj2":405661323498,"sDxz1ajvrzRH":990307216142,"TaC7rkyjrDg0":965241983308,"tlKdwcNvvjbz":412027288498,"e6QUuTDhm63F":46383108918,"p82AUFIJ529C":147673605580,"CnTT7cglniCg":345001466181,"NVUlukv828CS":176262127339,"esvzCunIUogD":131577668128,"ZwEzZGiTvzGR":910533922267,"VCWpCVUV4eQ6":717645214386,"7LY03BifP9Er":59478289443,"EVcccM0odMYB":656201586646,"VvVeAAn9635a":913602573416,"41h7tn8FEbeJ":258717139899,"BfgSkkpQVebc":423511699470,"yuFWCay6EKhJ":943850731791,"IXQaAhwE1OSd":642319421173,"JdzUdsnVTiyu":829912149934,"po1bETlT6Ttr":606630064380,"0eB8yu3UB0Jf":902827352918,"qX63lE0KfoyH":333125587303,"8GhCx01n4SsE":567209804839,"QgXY6Zutp9Br":963219741682,"lIDL7kEMz7B3":64639834144,"mwweQqc24gd0":260597601510,"QAldB5AtBF5c":278595888056,"RfCF1PuHYbFv":989985964119,"IGkhSBze0M96":475313299760,"MmFiCvFKiIGI":266722706881,"H7edvuv9Y0fc":897027728993,"6uzBi7DRMjO2":750975243719,"5jYapPHAC9OV":394440878081,"t5nwrQhESIJH":720326492846,"HMcO7emtXtID":133461796550,"eNWsaTdyPIfL":204089045822,"IYQvDkwngfWP":506969694618,"dnxVADTAuZLY":266939777962,"FnhPPxmirftE":660823541886,"cwxZu5ruBo2l":650347898835,"tB7P1Byy7VxJ":798933320081,"VutsEVmsUkhC":571371295563,"eJUJKteVDnxp":639319780437,"4GO0VjpPsLtD":71298846580,"zPXgVfG1WXhp":479225114487,"RwbYgOODSCDk":256916295532,"dB53dSkXetSI":742502212247,"GRhAjxpZPT1Z":584717954145,"pqH426g55Z9b":996386165718,"GaW2mvdMDWZl":260549712569,"jjVKbeoYyqzh":54031834322,"tpcvfe9ope4L":478788054906,"f5U8V0CeH9Sj":431906971855,"3Kx7VbaC1yT0":948242211057,"dynTs2wQY9HX":816963703636,"Ou6hVDsw1Udo":392154545174,"kGezjhlC2jaw":923714373259,"pxXs3KYtQkos":802187501804,"Fzq8ghfL4rdI":339651183347,"35sIvlMcKZLh":401254013750,"pxMEa6FVikXx":259862411473,"uGA2xnylietd":571590033907,"blV8NVEXzh2U":179547103345,"RZSudfRb4CcI":683466693466,"fDP9m1vBIe34":517502614303,"mzGdSJJZdKR4":441120306456,"sN2wDTk4XIbX":210506002183,"0YmBmIiGmVWn":834916588255,"oz97kPlxOKLg":246049510883,"6UoK2NdmfzwW":458145366903,"cqym0mXBiPn2":297891597258,"TaYqDn7njNbQ":821243310764,"50P73oPDktOH":89786071094,"UQD8KsnYURjE":723465658268,"b0DynQVAopTf":23522827017,"XZ41FUCOaWcM":952897219228,"vhbvFJHG6v1b":510139703513,"SN0lWM56gXLA":346733252152,"mnwEdTQj2o4B":572497078124,"5qfSXNLv20j4":767391354154,"gsStELI1qwRx":275661846901,"hsiib7CVNJf9":714285152807,"ePiGswiNB7y6":76983539874,"byinYrcjNr0g":81101356931,"MsDsyeTDiPCM":9961097383,"EGIFOQK3jeOf":907122373186,"q6xRjtZHNjHM":808663394057,"LO3cslNn8Qbq":764013711034,"tB4z0WHrQWDr":934620781876,"tSMG12wVaOlo":154603847610,"oiDAlom8Htjq":521940442678,"lF3nTW8YuQRO":13668675059,"WzFad8Crw7iP":343598076843,"CyE60wPEIUR5":541504687198,"j5tqE8oj3Rug":355069093828,"i1nohotOxOci":248930515331,"YFcrXVMFYczO":488155483015,"Uzn9roEJrRdh":250645972061,"CVasUO0zV2Y0":409717254951,"nBGfxcrlzNQ7":646043277399,"HMKpklvcPOyA":958905371587,"CQdpZoe7gXD7":887154902409,"iNwJua8kd5yj":160156983469,"nDVr2Y4rEOsm":673273909785,"0F6Fv1SBUKoi":300660739729,"ODlRv9feg4RL":63349157959,"9x2sTa6DL7Ui":343835787536,"stc50riQexwv":273223951959,"U59lvsQZWrxs":368649167413,"cK7RRWEzs9c6":217075568472,"kkYAuXxJ491s":366243306148,"f3VeiRiEPnkn":12765581508,"d6gDbfrNSRtH":839230137392,"m6gysYP42URO":812374264724,"SUHjIH5ykYlh":919409545812,"GDNw6crBRoU9":660014476685,"Ek3rPq55ZU6t":136135520980,"j5tL9o2K2nLx":59027328252,"R22NOQx5mI4K":401705333360,"Y9SnwxzwKjJ7":565323814487,"X6AfpUZWRKxe":215480993587,"rgsjat4OnQaF":811266705390,"dQGbijMRPdGV":802271030304,"bew0A9mhibHA":415715766060,"PkES1uGoGAlr":457575291663,"c5FVL70aDz9w":71396360650,"JysnC48F8BEO":299909102938,"gRb2DhuDcYig":820729573317,"aHqCEC7czT7v":277965774397,"f6zOsuFyhbEe":951178088993,"pJAlZW1XGvvr":826861866702,"HAexaDAswC0R":323977723524,"v6JZXXa9lHOP":24369646520,"JyyVmzwyFe3F":608554599563,"EnUC7CapknXR":195828712738,"HGft37E72RUZ":185019310448,"ddDYidrcmoT9":192407721183,"QSg4WGOqq920":93063995465,"ZoWrvSgSN2QB":632217176632,"l7JPfCeSjzZ3":64189626416,"8qPp4KCKymJl":915819589795,"6SfdU03VLkqA":768778386186,"RSulZTuOHSnO":158857587074,"PWMWmhA9boKF":115339738716,"FtFc2gAUp3Ck":602579796281,"YmyG6IHflv3I":791690521488,"i12oZFIs0Cp5":258509284888,"2ibgMxfhQApJ":800833584729,"bQN5FoeChfti":222508371247,"3qIHtBhw6F1c":527704847929,"beebbngj78p1":212965584631,"31wsPnZVBc14":64276242801,"z0LnyJq5toAl":504709480546,"BAnpRnIh00mf":832873022589,"GVhQIPoOV80r":786637699627,"l83Gwc9uXkK7":409606318388,"SleA7FinwDlN":496567004022,"kvSn5jYVokCU":642471155905,"B2ygRws6OJAy":189454075121,"eiaUynLy2lHF":770873667704,"AWa5W8kWW4nt":597350304262,"nDoPk3oFKCQI":85240512629,"hLFBSmYilzPn":753722591159,"UJKa4jqnTsz1":529285850193,"QbYhIDwDsO16":951710354117,"Sfbpbq0abYYZ":4664125733,"YU5eUs1o49cA":560289375280,"eCKTSGf7xxUt":670243854543,"QPGa1GXb65pA":204392657359,"gKBRimCtcH43":690317897825,"jFR2skBtXpcu":354636144802,"2LGqCk7MDTko":807682349634,"2jjL4Hd8Sfz2":457675265952,"2QzA1g45wROP":820289139067,"ccaF0S6myBGO":108914860131,"90FAhQQdQ6ij":392568542598,"kSbFJj4PmgGs":549091215290,"QqLk42wjfqkN":571536934310,"HvyxIGIYTf3f":864902288761,"xcDpAHTrYVcw":87988971751,"zTcbtEoLSmPC":396993440504,"lhoq0u17CMXs":766096250200,"xz2gEIT2pOCy":975071472438,"0X8mOmYw8OEh":91239643576,"if55VHKMlndl":946995752102,"9YLTorGSKetP":267366646681,"rzNnl3iCmXWw":70833154260,"oSxqPiPncjlv":808402748750,"sliyxBXNlwVL":868947703785,"kogIHVXD0SEK":913824071363,"RNSzH0ZYIkiB":452677696168,"KI3bWNCdiQqp":257648061496,"VvW8w7Cq0Dre":261391452188,"UBNwTp7qAOie":764302237838,"Ya3gwP2sWVXl":851742670208,"lxhdCQJtPtCT":338889075232,"W9QbqKnlP3h4":505158631266,"dlBQ3aLK4MyE":506426874381,"mH7yHP3SFBUJ":834004571425,"5ui7bLJB8YYK":825319175569,"mo8ggxvXnoyQ":588533199279,"JZRJOjiFA6xZ":363374621402,"XZ0LbfVAPu4e":906454202350,"SsG8AiNGh0GX":647293346853,"TXyKjxz83q6O":598032380768,"JxPMFTUV6IKh":961661399626,"6YbG0NXe8dmF":213571492124,"x6Fza0KxI6h5":472348284318,"5ERvv6EDsLd0":713512952509,"qdWfuwmAA34u":431619014991,"qbK455aforf1":609358251935,"qbP6dICetQKf":983364049745,"QbEvzgUHnX2z":424104291344,"PTnXfn685C8b":200072002497,"w4oLlbuxKDSV":839266672293,"yS9vdyuJvXTj":613749207912,"tQ3RxDL4xRVz":884494751897,"n6efrX4SyBmj":378433379978,"dQ4kIsTRrVcG":824508293773,"B8bqBYYqSzSk":819578084055,"Zesd39x4eGTF":491592573073,"JSE2k6W37Ymt":339055645805,"NqivtsmibSj2":363327963594,"z5DHfZDulSXi":50631165436,"SxjraZVlRkPr":876327905131,"JvWGoScqFH8k":910001342516,"hwyga34nFTlF":497254910838,"IZw5vNh63bUR":202732154869,"YUyK2pLnmZOz":767165505645,"xhFXaRBk7aI6":168323820826,"9UAM8FcR9AjY":508325955087,"k9yOAieKX7es":893167175238,"Ao6XP3DsPuVJ":737354856197,"OFSoqKTyxpjZ":857961027049,"fSkKdHFGl8yg":943786508356,"2hQuW7CVY8GX":825291518154,"xKpKzv2rQpDo":130308466237,"yqETpV2xDSYd":514126025053,"lJDb0kjqeYxP":578250691660,"pa0qyJejLVxm":351338395503,"wwIzBMLI0uEM":270457841809,"3dYVANqZQMf7":268553180404,"foZ623Yxtjal":603990696553,"XUqmzFgJSFV2":463089189381,"7TcwzM99E4KH":960591653587,"CTSkgbQF1yD4":305352117905,"BcenBcMzNmk3":490569731524,"xRt2AQApTvvc":264159575398,"9LVFgVCit23C":753304892908,"6wLsXoBopaG1":111657693057,"oJ7nWUHAb8ep":590855317745,"7VZJMcyBjzrE":434975095235,"WXvKaCzwNEaX":350284471094,"Kk23cFu5xiAS":627400121696,"Q9AZDbF5K51h":725781924132,"8Zlg26h6Ghi6":134219824168,"L29Y2O5zRr4u":174122377489,"AaDb5Lbg42Xp":681139246639,"4rHKniudWh56":426558612959,"7s9LpjJNPPBC":656462043227,"oCLOWG1YV8xR":405812913084,"xIN3QCurYkA6":378593578913,"6YyBYwz4skcD":820339075016,"QmMP2PJTK085":618389339093,"HeZLBqPFfoZw":50252075206,"6iAGdkCUj3A4":68060773254,"NenS4fEsq8Ec":361493721192,"2iK47i1oC5Ki":8700720679,"uXhBaw1JBCix":337111759356,"rdiNZ09PUyb4":244915808416,"dauMwmynfvD4":645149224969,"Mr7peX4F8fND":444298175674,"WXaiRTA5KVek":489811750089,"O5IHE0fNa8SF":269237933872,"u4WFQhcPIE0a":520160938555,"CHtacK1b7ydk":453160153016,"1d8x9Hc3EwyI":24785292480,"F8BnvSlFEGO0":644667156072,"Vt78TRS7ezMU":141402752391,"uc7mBQwx31zH":580113253293,"vsGupZIq6m7e":170987180029,"fU7gPwLlUZP6":340714796797,"j7R38sQHHF46":7844845997,"wcXOJLVm2EF4":986634045636,"nRdnfbrnOaDP":212263972290,"uVd2nl7njnz3":77397595200,"UrrqEfwnO4FH":62855387706,"l8NA4Xe7G5SK":328046570576,"h1WqnTqfhN70":984100229433,"zuQ2gKav6sfv":173709657811,"TalshDpQlmQq":936421547121,"1oP6a3YVwexG":241934813971,"TlajKVE9DTW4":900484975432,"IlhePc0LPzPm":398462976344,"TOynMiLk60ae":108232409862,"VK8euGLu0ZTW":185811520254,"mTerODS99sVy":945352397480,"8GVZqZhpXZV2":337666935722,"KIizDDRzcOql":4030206123,"uFHs0oykJWj7":307410538121,"R7uMHrrv6M4s":190600542452,"vsWTN2vZyFVo":153288335967,"7aqntshw80ls":372585444206,"gqWp8sf20Zcv":993883879111,"MjWbYLH8GL8I":487240615823,"KygarMAl6kcx":504120546919,"46VKy8vmZold":737944883012,"oCKGSQAeK4vi":315408390603,"A5TWQ44HZAnN":815742246556,"p5EQqhQZt4Bw":770032320551,"jObv0IA6ykT0":922455595723,"1jKaRqW4mEaE":610125406787,"MWexUYK64ZqP":86477782650,"QiNmD6c7iGT7":66466475499,"HZRlA0I1cZjK":995556414996,"bExgJvaoa7V1":469627033098,"M2jCfCOVsVOj":791637611170,"hYTrT28RY8mf":369106117189,"NeXxZq1SLwoR":503075440942,"AJubS8EPeTWm":476864955988,"aJQRA2UL4lFR":588167155911,"SludmdsPKYUu":212231628283,"FERDwzaPdaKD":259686923254,"2g4WCUBUd7mn":284510815645,"50eZTIQVSDXh":610420000749,"DdHW6iO3Pa04":777557404644,"FfkrkTAhubeS":843814906056,"3jfVfysVNnMq":132461597455,"xx9La0Ibvpjc":450520023873,"kEyrhe3gZz98":472636879788,"VzyyZ2WJSCmE":328303953559,"2CPVaD4sb1BO":17413467242,"PSHQf4IXJi3I":332496435333,"UEl3Qz4K1PHK":314598831085,"kiPnYxDwMhKi":830876751059,"Z5SdepeRJPOr":722534618128,"QSsKfCzoWMCE":805631839421,"8T166JXBQVcK":287030078,"rVhGDQsYW6kI":524011416466,"g72hJM6MZnvZ":167414746769,"A0iGYl2rIa23":670856135008,"ZVEz1LmOyBYs":650204030170,"aybQkLrfzGjI":615088003257,"kheQbX7mq2wD":722048953666,"io8cl4KI34Ut":471844180847,"PawtA308Wftl":444263951286,"F20Gps5GrmPn":165299766749,"Zz9RmSnhTS2d":521223264998,"tCjTDwOq1atF":15997948011,"d352R4z8r663":799476824065,"jQsforPBYl0R":86209512990,"B1uPMmdMbj2i":999634613730,"eOTuGXr1N1Nz":94050681632,"CzpjuJkqc9SW":168583778439,"1suDDkYSy3IX":706221574800,"j1999DRhsxYZ":582975102704,"qahVmqR6cYhz":109678776210,"5kV6KCnv9Kj3":485172934753,"n6txyHpiECTP":679692624178,"26OGJewnF0xE":317402069868,"vQhHTc3vhwDg":896277244875,"TqbD0SEF0102":536126281977,"bFrJ9fp0y18D":641803010610,"03FWf7FtgigA":455105523385,"dDG3prhb45tg":943680245750,"aFAKDIYcyi1B":932966486795,"nIOOuPWnLIfc":402045422442,"FxelbRq1P9s1":108467497840,"HFaHE1JDg26X":720437106004,"efsfM7PmF4ts":493662746904,"xaSj5iBtzPib":835825275324,"9rpMGY3zFoq0":974937494818,"paIyPGKA0P8v":723933989739,"AsNTk6rsRKWC":967320250871,"LmU0ojE4Xd20":517136764172,"SPfhj09NfwLH":366229179594,"jKgaI77yux5y":111700675161,"KjuV565j21Ni":210611023923,"BBkSK8kmhBYD":595777911926,"xz1FZUh4vMKx":68420006316,"9KxLfbcGjp5e":582765257347,"XtKWLVS1ZAs6":265607008664,"nMkqLQTxBhTW":26939171366,"LwHf2IWNAxLb":668368919503,"5DaMV0BfpVko":546892059228,"r1PsmT9408mp":51759494635,"r7gfmZNrkHkz":257793080915,"MC1ba6Ih5xI8":945589962717,"tBEYmKkWBnSv":773926938892,"txyOLGIcX11i":801801326890,"ENSjWAJybNrP":2324211901,"29g6HUNHNYgi":695411476175,"y7MhPuPBDFt6":904905362326,"3Qp67mQKmAPp":559704887306,"ejv30kpQZhfa":837644380699,"oV3hIgaDPsra":26388158808,"MHQt9GFYGpCr":74801333391,"S7zNDdfD5Jdu":335709041137,"5FnOi2gh4tF3":646579257299,"lp3QQsA3TP4t":15130149412,"YKzmLNSRPWOC":235017909173,"DSPcKwDlICSG":510084483903,"weG0gp92DL3i":39210911537,"eHg4S5BRpjLb":194416931958,"OC7ZT1JTAAWY":95441358282,"taul5q3reHTa":690273843007,"NnxZuqcnW70j":828712928845,"nUOJYkKucvAf":534578082750,"YAF8WFhvhFyK":918449829011,"D0nBszF6VjNF":528392250594,"UrGKbsRuuVl9":921814214169,"eicrB5dS3nEV":693753966877,"EeXxegdCur4t":463745728040,"bBZDA6AAZqbw":334790637109,"9JbixWYjAxts":994609674646,"hqyZsYXCCyHm":831199811240,"RDgt5A9X3LGK":730374052647,"GFzgqPVHzj36":139330566226,"UOSFPXUmOn4x":530131967886,"O5lERITfjtxM":69068921703,"3jNt07rOqpDN":166291977716,"L6IniLfZHFY0":446947658288,"syekZOep4Tg7":30278761873,"bXoUhTWcolr5":604424014880,"ac042LGt6clH":809466694624,"yQI1thCdsN7n":870901446536,"0ewCv2Fjzm1d":254235736114,"JRxWLmo02x93":64001582890,"4NfrIN1oafTD":27896711601,"OEvzemwcQQj0":567635035378,"IrwlIjSZt1eg":638286842784,"M1tfChfwdQ8N":105566661293,"nBvUqqqJKvY8":277261652153,"0dekYsHTNiG6":967201229489,"MrN8WKIUBtVu":696549028260,"MDoqIPp6hP0v":158523088619,"WiDsOKvfAfmC":315809755461,"vLwDdt18PDK7":318668050920,"H5RJm8d15GEE":550221070759,"bboSO1N0QBGc":307777633544,"aa4BzVLNzfjm":732386265518,"ipQPeuWWjTSY":970536703392,"79Ij4fd7gEaO":96186566887,"VLuh9qlB9IZm":919945959208,"MXxXa9e154aY":646811683198,"EXSYLV8kfdvp":308693907633,"gBY0gPDne8oZ":811135454014,"ZnwxADrzG6wI":330232086792,"15hu5GVshfz9":678588870712,"n5qj4D72lSgw":429407548295,"NXcRsKv7GrO3":797528717840,"PfeSQ5y8SIWB":725470493049,"XQVtppalrEBM":243846556086,"O2urVyLTra96":998665928087,"cVHV5MowabZL":513040601451,"3lQ3c0XBD19s":301981217822,"BxoOcdVdx1Su":112082346532,"WmIus4S6V3m2":692736910022,"BO9lUyAUVA27":358210144206,"tceGGXot8FcW":159234739689,"o5WLNhBsLfyi":419538290629,"uttJlMU4jbpL":125503077138,"VZNvAGzTAaCS":858488724465,"19PesxQhWagM":502270837370,"Zuq9RdiiJgt5":668350802124,"kNRRLeCBRQuN":241818118644,"uggeEpoJmZ3N":650556408783,"KD7ngR6F8Ydr":594373725977,"8fPcd8hOXpMJ":297680861753,"ebOHlQOGDFN5":474419107503,"PUg3I5sPYzz7":877495461249,"UHyea4jZIyRF":786101363280,"mdPrmTe1et53":68017278591,"ucfap7zH38zs":948205002255,"QNInQTn791nl":738791928574,"JrgEbBkHKB9a":475050761430,"zwDuBGYzQcqe":262978721637,"jjTSJOdyueqd":220089211354,"26tU7UE47ibj":873317814185,"LPNrcKAt7E3J":275425060832,"4walNfLQUtUP":289273021023,"PwMc1384mco1":375395287014,"S2GlhprAIwO6":346616349825,"85lG7ckvI7Fw":4478930172,"r5qOAn02xcC6":47004416862,"ebsGQp58gXxX":851306593246,"EfQ4xcCw0ivZ":930985706593,"nz9Rc9jnuiPB":114260051196,"I8Bxj2GvB2cT":39036236974,"XeWqfdhjbn4S":762425780641,"qTggfPigM1vj":426950286495,"2jRL8ehhgv1e":711926426287,"2puTU5LbEIky":955686969158,"xwpotKysA1MB":511577027330,"LVHT3NBTeHjR":926792611459,"1ztlUIlFudMZ":702658731791,"ySb09qqnAKr3":801883135513,"ltGjwnT8MKwv":226872460405,"GH7lvbCQgiyh":316198986186,"n25XDuuCWoT9":479853442969,"i3WlC5lKTlkv":638654478357,"JUy9WHtjMz0R":660904832648,"25hv9RiXMJhU":944667067643,"WfN6o2XUMV3L":523573691044,"kC6Jq9Ep1dJB":619428228414,"4niBTGfVtVWl":871290167280,"VHWuHni1ZWHw":46342273998,"9Gr0ZNTaSJ9x":159984429688,"5iqwaMfsC44I":818990601587,"ssx6sBFUVRml":447226703199,"utONfsk3qKlF":328804864583,"YZiq00ztWNBU":259723635015,"nuzumzrMGsmT":153018138461,"ts1XZiPryHGH":561527567793,"qeqGVSBeLlVQ":588912428437,"KMeQlscKxS8V":410688142215,"pnz8DMF8KDxj":118236246328,"GGgJlNMqrILm":368629014850,"Lde5tmeSOiwQ":979091886805,"tUyOSVennSOJ":808015033895,"RP479pKgAL4i":392769555295,"bZQkgt3Br9uw":30414385318,"n9H4l6EECtNA":356369892441,"GBCWp3qEiJXX":666543894715,"Nds2ousKkXIf":191850211751,"Bidy0dOZtHEQ":396048821543,"yqSudm7MJSbI":807559900666,"FxzYvooA9eDK":458743743205,"fynH8orpO0dc":169188967787,"6SpzUruWBkGq":503226794351,"kWNPAvPjCzQv":2560460792,"96RsX7TduEiE":746202319587,"jE47Q6F0VCzb":698028671303,"VYJbArsMKCei":635433841050,"oSQSDBk6vgnb":486136849006,"nOuHdWYZqHhO":210201660767,"EaoGtfGFidxN":604728162896,"dRViG1iAVn2B":205893256354,"4TWOMX07Cl7M":727076942044,"r5sqRWUxovml":979081022499,"OLrRzU1rBewc":165761274619,"6n0Xf0im6sIP":953926088791,"e7vud7A6SGZj":387721284223,"oN8jZxbJPrjk":807781097738,"GD3lZG9Tfy7w":848604295657,"FBM508cWZU56":198185451370,"cD1I3mVudpHN":213514679756,"e79JEPSYUr0N":312333724935,"B6oz0B90okou":139134101392,"n0OMsh2APnry":433876441740,"TqFbJSH6vr9z":437920029037,"0Jla8dTMNbeH":486398301756,"RXoAjusEyP44":641839188694,"N3XBa8WYoJ0l":996012525211,"CSRKPZ8EqH22":650829078692,"EYqoSG71boAQ":345528832743,"XMLn9vbDNOKU":827411554029,"px76KfbD5kBH":778037603971,"wfigjE6QXdDd":349790796225,"ECwSGPUIgNhW":960061870017,"kRJfAjVEWazc":286479488963,"MuoVLJK3uJnu":57496396006,"09Hl6xmWG8Di":125147660664,"qdmXvH2aBINi":652698097526,"5FPnNpH1lkcd":715547718150,"qWlq3GV9D0gY":899318746627,"fVbo8cj3f9Rz":152014620192,"nRwvmX48jHRl":542676794737,"Nar4QS3Te91c":85339531830,"qBdnWtNSRuSz":15789049269,"E5pPvFlUcNfY":5925018568,"QSRCx6ECGOcN":49846465775,"s4y8rMFe0qiO":996052059071,"fOL3S3BZXCV3":623687565122,"fav6cz25ksYC":319684092628,"I4lXq6kcqwCU":362734238714,"nL8etwfnDgAM":729525710845,"jvoRMDHUllsw":857886370878,"Bj3zW9fCE6hJ":453237331166,"ljWWkVAmw3ws":787097572938,"WN38uTBXSFU4":374115046993,"fsDUwelaxuVv":432671249014,"XCtwQXtH777E":696560976261,"6mRQJtrWpX5v":909763571509,"57ITOfAskCW6":47180190449,"jTYKH6BucqIh":28364100652,"U8mOqL4KZQh8":934672689244,"GBPWhQKJLxBr":660742492239,"nPuS65rtCiLX":169701444952,"cUqnadN2FiVL":417417912187,"XMOw8K59Q2YF":528708670248,"jAch7AO3yuVz":258207970752,"Nb1OEpQewMoT":104550617174,"Lu1o1PvFyPEF":70868794265,"D195X6ml3mCP":127846302483,"fXpvDevo6Ij2":840306039888,"OHdu25nss1Dg":56056532431,"o3W6Y7CsXjIY":156536644811,"KKlmRrzcohHX":355856619493,"0wRAngwh42MP":62087109269,"TgwHncloX9XE":999702714625,"4BFQCP2f5tF7":436685417721,"9qXonkt7B6Fr":28832046600,"KLIJw5rFyLmd":380930179635,"lBiMVp3KAezc":52810537717,"Z7hetIgssmyv":583410302548,"aXiEstQCykqN":631302744138,"XiZ0ZwbnImtF":236142632394,"RNHUVqBjXc2M":964966072472,"v61aJ8wcVTyJ":437161323619,"5Ejy9FqQTaNr":115561460194,"JfCEBQYjeDlS":301115686033,"zvhelsGNkzIh":304422275847,"R0EzVN6j3tpl":996610596193,"fm6usNewBboV":870103224424,"Wp1yazc8UIVF":370060709579,"2lYQAFRbUVaV":881599141074,"Xh6j4uhe0JGJ":745477306327,"5rUlKsx1ygRv":415809965264,"biRbax50uZ9W":979174925496,"un3fbJQgkCt6":493629379835,"T701tupWjUs0":803355541749,"YknqBSHvHKpL":936477581719,"fXOQlizPNZ2T":407729297270,"Iutme3VAuBPJ":132993198627,"ugad7phhM390":381615202326,"bfH8YP0jyVkd":185046277844,"FZ02uWzvAKK8":540979689560,"pZlCJGuFAP2T":617398807076,"snCRus8v4h4d":134976705534,"KBcutSKq0GZI":166154255560,"JcCOT0D9rWcw":306736196408,"2Dd0m7BjUmev":669666491369,"OqZNwSq9U9Md":258028570356,"Yw0E0BBZMxiJ":617527810904,"l6KzTK7y1okL":279160810036,"IsiOduPtDYRw":61295730926,"LfnR7nlDRJVy":425151796652,"XJUDclo2GKof":724364750477,"qbIgTgml2Ylf":884620575668,"Rzpx55raPb5T":757412682184,"4cMFteUOQNDa":934133007044,"RVCuk1PlSxfT":492360762020,"8q0Ve6CeClbF":143454608816,"Pq17ZEJIuX0I":929970111531,"P7c6th4KiTe3":900030612867,"pEjMsWr2SFpG":725639414683,"xRDIurKeIGer":328237661981,"i2gzj3aymhSL":522843459566,"AWcFSu6By8J5":207227170200,"fcKe2l4hgGSZ":814877781041,"jqp3IKdqJbBH":102798846981,"0LYbiUiN5u1e":132345322271,"XnEjDrb4hs3b":432331910860,"LE9ucrfiK42r":385579076073,"RPrJ6IPahrX9":589275618936,"hnCpZ3kzPEvP":395148968441,"Dm81FjulLLHs":948726868604,"VtHEKdIwiZsY":355983466248,"xjHjvN45On9n":148489236437,"NNGr4u3MWZip":66020504159,"xSJbAfY4xCIF":978757172343,"O7V1thQfu32B":578655772215,"LCNjTzRbnaZ0":833090944143,"wvqoEotcwtAR":662727922303,"MIEOYQ75MUyE":152546492013,"8YvwKxRWjDGU":639621938132,"bBJeDChGPgZj":609491358840,"BeFsZzlumzzZ":765518941606,"eiR2q8g8mBHL":270758345526,"jhd7y3tEQ5Bd":979892298605,"k3ctSz78O8wF":383891926226,"dg63Xu7ibecU":487686238390,"yMg3zLU5KCDU":165509725503,"j4gKN18jEHz1":713535242081,"09FeN6tNWtgq":374113048869,"J1G8rY69TQI2":594882938296,"bjpbNmtiL4VW":787340375318,"Aq17Jx1lGDD9":106180966350,"TxIfk0pj8c57":2403497986,"RwfFpsNgu2K9":788054297649,"I6A4Zy4FV1hj":860394567013,"9RPUnZN6R4K5":694077716216,"bBegLDDnUQ52":156396103923,"240WRsQpzkWy":443678627029,"didPOLnZxQQq":656774874006,"bIVYjd0ZnZ9p":686085429520,"nxMRpbh6kUqr":136396001171,"ygnhLqz6Ehob":98759438390,"P8sIIYPsnlgH":375526913914,"lP6ZXL53uo7W":722269901961,"psXt921QSCdb":981726063348,"p8mtFFH2h5tE":662248593114,"57ODjc0mH0iN":644495697015,"gx6ukW5vqXWV":201747338462,"p2XmUKWzftpi":460538053905,"3nQi2Xl1nidY":994499018256,"n4ziSxfvjtSZ":195216648320,"foR4yUfZJs9h":502373068328,"dP4aiAarhE50":100177203069,"4zQ1KeqiC8Sz":696933846602,"tsp4dj3ZOrss":40726873198,"iZs4HAB95JjY":740024386025,"PZ9e7589SXse":358015323853,"6EyS7ZnxtIUB":420900323678,"5s5gvtLQ0ffv":53426630874,"lHcDkcl9w5iI":23733783538,"z8jFQB9s4j53":756319095131,"sC8tCejWrt61":75390825051,"hBbhL1KYqC7R":391983466578,"j9Efzjic77m4":708064754633,"X7CedHNdolwj":217580568335,"TWle4fWorVGC":669117893005,"bfRH6Ro5HeG7":646353694745,"VWE89gD1X8gO":147701125579,"rgsyjOWtCnKy":207021204178,"MQiIDjRumgjY":545485473894,"i2MMsbMoGbVl":52515010733,"YmuGQ48mq6Ai":951132261361,"RcqKiOA9E6Ky":268439872978,"eeojeG63LgbM":822906082688,"SEOxzS7Cec2N":586050657827,"12fTrHwR9QVW":80023019885,"khPVGlebXszp":154209952482,"qs5u8jLlyxmu":681290060087,"vlZrxQgewbGB":101385746867,"0OL9uSgzXBuV":597815788488,"5aloHniGnwhQ":593698937725,"bBp6NLDrmhAy":927161689583,"eTe0sPNAQ4Kw":898437237540,"rIBRBxwmfdYB":767893790368,"6RfcosC1zIhC":952364101086,"pwCKuJztMvll":717767056358,"wX7mMb1mnzXU":652568628009,"NI3wPUJRC9SI":287959942951,"kktMv48AgJzi":760759539691,"MJKC3lfSSrjK":869581579544,"BvvFQu8eYyVh":236518901114,"KdjGr2zKHNDi":669037228242,"NSqWZd8RwWPh":507781056594,"f9Zb6uI8QRPB":663489063912,"oL0vgsGRx1dN":565447502647,"rGByP2KhcGz4":49160567412,"OHh4n01slLcz":620833141883,"5Hj7FCbRG1ga":381280499086,"y3GOoHe4D0Vk":936008617314,"yaY4gF16On4q":10278925790,"tesIqrhHHsgX":538371712509,"RaeSWmaqzUQ9":580948439623,"ng4e04crLOJj":516156564138,"tlTvEdYuMFGi":188882848239,"YL3YJld4YUiz":123451641516,"Sm8oLDBSc2w5":186544780890,"CufZOeBDvriI":118214040304,"kvCiRwCQfUtJ":438459032572,"uOTpBXZ0b4t5":294432726277,"uvF9M0OMHDqe":739578967445,"l7A0Yw254x0A":280486605700,"zAKNsFXrpoOT":306132254612,"BU6lK0i1vWfA":500858278523,"Kbt16shIFNiL":655460330841,"abb7PdL8h6YI":978259430996,"4nua7zIPIIVH":353103053043,"K6IxT6AOHiVG":71607004624,"b8tEb2pTbmt6":71170843322,"kdFmBXGHrzPK":361604741389,"ORmb0eEEBTGq":397324798432,"qe9yQNBHxjj3":886891019210,"cEPAXwV6rCPn":789130279320,"G0zautenQfLY":861742031123,"5LXR61lZoNZd":572286383661,"nPaQ4calzlmD":897739870469,"Bjujnv5gOhIj":466151101727,"UD1fcJEAkHdk":14246672063,"3khjrF34Igqa":562093908691,"SPQ5OYWZs4xS":756641380232,"qRzVS190ZUS1":829730197904,"XEy4QHRvlMvF":761184014263,"sNqyJ2kEN9aQ":127912158244,"xan2SdWFRIe9":558422648458,"zYirmlwRDmyO":973002415420,"boC9460b9Awu":6053910364,"693SgaKvS2BU":320766865384,"ToCwSSkiE2DS":250279816465,"yWhQYfoXYCPq":17821308761,"MDh1yoOUJKzr":434043477369,"rTDxhYJiMCtV":683155311318,"sdmrfqCZZi3F":927617297026,"7Zs2mVT8iw3Z":193303622880,"k8fQudMRwcwS":86666843462,"HoKirG4E13x4":791048897360,"cISetizwi2vn":657713760069,"hdQx2Me10RZC":436267606647,"KJGPZvqFCYZj":975288902284,"kUpPdMfQ9ZR5":40814226778,"uGtpuNEWDicm":633145655578,"diQlzhG0aJ4v":989805921090,"EorVAzejp0cn":478378534309,"CKxasmc01XVK":212954853288,"PQEiRQj95zIf":77672707270,"XaVJ1t9HSB9K":29196058920,"gamlvtiXbYtf":183250905081,"WCRO9Axt07TQ":924658509982,"ZY4nHYzHlA2n":564663587161,"LcaiCQEFC8z9":919294652933,"FJErlNV4zE1a":273235513757,"VtS9AYENf9Qu":245440807716,"3FTXZMjEN4Jf":836524387388,"5QmwSNnvh9Wf":517587229707,"ZfLIDjEQrLaW":371903516953,"XCSpcPHuyH2I":526476241360,"iscvrpLcoKCs":731569973896,"efBwXOU22dt9":52855228503,"crBlegCU2c6V":59672004409,"tMvhPeI4Ykcn":560583601703,"96oQxhxs97Y1":483449992617,"14iPhHko7tWR":739368361365,"VXgTeAhB1FmR":637300023187,"FftW35jLvxnm":579653579426,"ACpG1sGXaJul":960958533923,"iblDTsYji6CN":853795383900,"jDyEGer0SOR7":279090615092,"0wCCHdt5PpKy":512544251655,"yZc2I2TNqOak":782512885472,"J6bxgZckamn3":952559176650,"fltzVqDFxqWy":493440554377,"GvO4YHo2uYCV":87813193691,"GDoAj1ZHZqzc":473036393854,"SmvcVYRN0Cw9":389304168891,"00fZ2CKRnIio":18325267593,"wkgUdrGnYuPT":381280690539,"rgjwzuXBF0Gy":39534441668,"W2pej1Fz5Umw":115402635590,"kTPM5Q73vMzF":445686050071,"Y7GJWiwvS6do":367228659418,"TqPHI8La8yhJ":327591861819,"PWseZ1LrT1WR":26448103459,"dVqJa3N3xgvk":703699754354,"ZvVmmJ5df47k":986084360720,"JjypUkB6ZwxD":869074152052,"UTqJpqkn9tA6":131186436352,"qgP2cZC9uS7F":387364403998,"hu6GthOqagOg":987723075937,"XxB2nqw9ac8F":80039941602,"IwXnVdA6Z6nX":641873704062,"hNJtolUJEaCh":394737703189,"CBhulfZ3w3C6":472676004818,"ukH21f972xNc":761730420673,"TXOPaQ8OuhXQ":594071374062,"71DGqBJfdBiU":833902165271,"pX2IZenEICOq":385298568175,"zB7G010cHDrK":831673621310,"ltoc38ZOa6Zr":994114870772,"nt9u36KIPeQs":785637873386,"ols6aPLzevuz":560417355686,"71arWA4gbk0m":549169510058,"SqoJMOhhN5Sr":995287542093,"HrNIvWEZwQ0k":92920264956,"DK3pVRzzbhDx":278666988886,"MssEJTHYl7FT":536510391024,"1y3Vy8u3n7ud":796094537112,"kGEuwHzk51Tv":956218002533,"MnorVdxBiBtu":995331688444,"AGlAqOHcD2nN":964103727590,"YCTJHFGh4wut":948596160304,"tBEGMDPe70I4":672935526375,"2FaTmNQGThfg":140540132280,"CfCaxmeQcQCm":555264682431,"K33e906RWUvW":351066003270,"1qlZj1Y7Rhqu":691906327854,"Os3ltmCKwShs":773072662253,"woWDdSEUPNG5":876229736105,"D1eqe81ab2hC":709380842458,"PxFHkiIY36Dz":568358026890,"LFLGLzfGltc5":760925823429,"mp2nvtiXcOCY":383164915777,"YVP61YsDjk6c":105407038701,"4VaapFNtz42f":127495813729,"OIB6ZcgJakbB":13482141144,"sL5wQlrh42PB":378250975776,"SSpeNmswbmlY":508962474994,"AF5TN5xD7cfx":67903067426,"FU9IWoBUeG4Q":988522475898,"jlGzp6s0G1B6":276895610766,"JbJMD0Xad3TQ":596498103729,"GSWYFZvnncX2":916629591507,"98hnm2VoBysg":310755974013,"4qt1x6PEVY18":565444542130,"RmYsKcR4vt9h":374819600719,"Qe0fpdvr6jci":397917257337,"TcVp8HxEhTkT":535403306138,"PjRr2oOPddKB":799138300823,"RZVLvSRjqV2r":732621831033,"BNJ5FxG8zNU7":47069179111,"M9CnbDXPtMue":352340997327,"ABtnDdSg0fEX":58671421358,"95AYqblHuQFj":67178252494,"9rbK2IKiegbs":524854381406,"xW48wHzxb6PL":464304596957,"mihEtkFL2Odj":798773530461,"O4uJjG5mCEGN":524241069341,"zgBJszuHIcW9":695971128925,"WxRRwJsIHd0W":694804334554,"hE3dNNqQT8IF":556802616643,"ssFF5SHSxeCM":829360874486,"t1O6cQCLglQk":874573784453,"6XlS2hMo9m8z":211991298350,"rqR2fNBWzlyd":782320175430,"3CO5IBQccEil":305622781789,"75eRv6vDU64s":536648673718,"f44bcCCGklak":437852509905,"QR9rqjoBt3bv":86951984827,"L5ce7fqKnY9F":105974707822,"8uoTPuqetnQZ":669203162047,"wJCGQgua9VAM":712038721021,"E6AtLtj9ZBRX":220406524407,"oOtmWvfZnKYV":515416679595,"KBmYmo9iCfp1":716726765468,"vuUUA9lfpmyp":986425165353,"NAqsJCI7LtBX":448451313790,"xEqaDxmezOOP":888184777316,"o2BLIuS4ix7Z":171977784373,"IRW7mYTn1vKo":400365394637,"4x4aWOGucrlJ":706699743633,"GX7UdAlquD8T":124364714100,"oOWL4DUanadD":37739005119,"FHvrSEQLQw8k":104457463976,"JNB1J6HiUgJA":171344581580,"661zS3buB8sH":796792691922,"KFl4zpm9Tv9W":103631120359,"RzrYxTjnGAq9":555705307828,"OioCURixorUm":81275085540,"Xm1NZ8ahcJo1":976041232346,"9XcYyH9WX1S2":1846774444,"kTDJeyy5VnLZ":253183982840,"2HWydfm4aEcp":446935256232,"rVWfXEBo2BZD":269102479184,"OyKGiN3imBI7":658851817581,"wPO0zqrDumWD":655683882543,"oXwrdRTnge8x":188505996744,"mRPVHPFxDVd9":92774133714,"RVMvDki5OBOI":669968787219,"NWJOmHBbLBYc":623422214683,"MIFJ8isg8oOK":552605104798,"XuDmVv2nwlR6":89993459336,"UAKik9NSEEt6":97689456162,"ejY1AcHU8FxO":933092848351,"iQSmZhn2iL0B":184235105352,"ZUz9x7mCaV0d":110721262994,"NzhIJgtRa5Mm":164605194127,"3FEM7kHEUFrB":454364238093,"v3U7Lu9LHxJD":358808600888,"BTvyumboCBkf":812782485418,"mEHJ8ehlcV6D":588625061421,"0UMmIgTCXDi4":36335131175,"ch5WiLofQQXI":913547947322,"nAr3pF4bZDUa":886426520951,"buAqCoKv0h5U":787323750706,"6BEa4P0Fk9VM":213097785336,"Wtrq8crElZ69":709203561818,"jLsRdICLSiXl":412553971793,"ColbP4b3GjSr":206322128878,"a6ojfo9Spg1K":44421628304,"eHKicHIDGU4P":742289490208,"9AWoLrn397Hp":138750251870,"2s9QT6SHy1Cs":792237297092,"zncQhMQ2esgq":529412957742,"SNBphYyud9vr":883186515098,"MMYkBtTiZBJ0":338806718415,"00s78iw0oNZw":104111975647,"hBLYrZ3pZTcd":69480140768,"uClvgUkmWt1x":23084029968,"5VLyeHpj4S5H":895627196677,"9QUXuFLF8kob":912487063123,"OLouLpgUxnNc":73950125004,"kk4Uc53puCsz":766983349604,"bcqFKHe5xGTE":117885207282,"FMUgLb3OUlqA":287877926944,"1l62ApO7Ro2J":530793295959,"wRAfXEj79pOV":419565741232,"vZFR2GykrgvV":475665425832,"21kQHHQqIty4":365668526736,"cYaRw0CvYifq":908345381592,"QKJs3VrT34PA":644116087678,"b7rGnj6EGVEN":838401805692,"FiVys8pzOdFb":442872602198,"joe7IDOSkyHf":765193259499,"1SpksD167BlJ":686092432490,"RcYUhBaXD2ip":149598038191,"7dtJd873tKj0":213981530054,"7hmVCErPIGTV":237723592821,"cKQJklnXBwk8":487288152131,"tbIKtRaiqavp":213355138489,"2BHr42znXsl6":502063374993,"MwqEJ5JWO5T0":477958442573,"EVBN93wppP5A":712782713961,"wPKGuNHhNmqs":951668476865,"3949KPNrVCWm":225323658692,"PYqBpERulGY3":484366600882,"qQgurPTpOGPe":983493249461,"U51LdwhAuSS6":501543156656,"cwezbH39OETG":653321335979,"zG0mXFTshWvl":705343309413,"WMXPaMpO4vG9":64401662109,"1uIQDSkUHWlX":887482271349,"v6fDkbEsrCpB":109334413320,"16bGTsuXkIWI":213230933356,"XNiAixooo33d":913811607604,"Uw0D2Tr0Sbvi":490970047572,"KhBM1SoEQTjB":578126187758,"ikm9EbCWOV1A":114181123670,"QnznhK4hgp4G":52585992633,"0DulwdPnQ9KJ":635146712068,"sSJKIEJm4xW5":489187632454,"xqtjBDfPWUMk":302327080637,"kv9SvtFIMTyy":371385133080,"6PdFYmzyOYa0":592502128324,"0kpqeKXmRl14":121713530284,"V6oMK8h63phi":469999012312,"wtUF9dpZAo3f":246387711139,"nAaceUIA4ZfU":799023154829,"lVsnJnR2kgl3":523398271381,"zeXTgacKomjl":998181967124,"Y9OIeW92t5TZ":238229866536,"2q0b1XvsRPBO":77649532266,"FMgZycuSBv22":73323882785,"oh9nhqfNUZBv":454586950882,"bP82HfVlASfA":743312990509,"JfiwOhRoGsQs":214924605575,"vFADutZbkPwN":371094151731,"DsHfVh3MnWpu":543810487668,"fdGfjdeJDl8N":418506706828,"LO869XLhQsQp":209851127393,"bYuAAiin9P2S":210392302238,"zV3Omv9q1IWU":134099915744,"W2cB5KJVryVp":361219863439,"0SRhLJ89IpPh":679810927650,"po1oRL4yMnvg":804900455706,"j3EwqIlIJL73":150480440809,"dCHsOKTWkMtS":500065506681,"By0S3zGE4Hu7":176531836186,"OMZvrEi9uKny":440572097561,"MbFnwgD0fUED":548349101379,"E4YzKBMUMDxN":323401583742,"XWBhc5Im6uIf":599317296589,"GUoLpig7F43d":164094207987,"zB4GahT93ORH":706265260722,"jafXTq32pZNT":716258818713,"WzFCK5c7b4Ln":897387348041,"44aAExHc7GrE":134981373939,"636aPsMzm8r1":381093620803,"7lWHvflYVDS0":130455499025,"zMzimGZbGGKA":249358402350,"q72YayE4g9tt":432475821534,"5NL2646htVe4":776995824137,"iQsJavrxeMkf":477862467133,"zn1KfPYJDRxq":655330058668,"at8bQyZU5qEo":877121638919,"Yd7A26q50QSj":928357930294,"0ClZcFdiojqJ":86272219990,"o9UbbeKWpMKD":990658460121,"9qryn1QRh2gM":512010356484,"YBd4WuH7LYPG":715673726226,"7Pe0AFkS9ZPI":145362288009,"ZqBtQpJLUsOZ":164471005141,"ETYPcaHAXYH7":626324536670,"K6S10Xr3Oy8u":638089154436,"o7mGdAjdLnWI":240223852047,"14xwZnXekVqz":243164468404,"gIlc17AFr3hf":612988254193,"iMsAWQ1lY5fL":347998404510,"LnlDz41zF0lC":458255870228,"TE8wNZSNDnIv":497036119646,"IIkHajfTU46g":49070574278,"3kItmucDvf8b":26927728554,"RXJ6y5aKvMxb":634217575278,"4rMlrOWmYPvS":614317882781,"sFhILfLSmFda":183235515726,"Eu9Vo5W2n394":607235700732,"0lurOi7oBckx":239209043370,"VppmcjGGmPgo":62970974021,"0n3NnWBVDSim":342301380876,"o3Q3Rip7DcFq":383631998709,"JnshPtljOvM4":50658105143,"EiJdVK3uVLTu":601652895863,"9MbCEXUkIbJk":882836050885,"cDpVecatscHM":998544401824,"Hot50Bw9p6R6":998123558798,"enLvJphD7cf8":161596209687,"5sHOkyAHLIwV":376909029235,"TBZZ9jETJIxo":740959985034,"zYgrZ20UxCSD":904348145995,"w13mTOa5jC0y":277528651565,"1Q6kFfQeUcPk":241029612993,"xDFj7oaPiJPH":574695398385,"Ofm2FF2pAFrD":827398750250,"8lMmV1VBJ54a":780253347509,"0UVHxMOIJjIa":336447212256,"bu0HvyNuRVhL":311170929385,"2KEAuycRwNpR":99076050066,"yFso86N2PlT5":754485240835,"tExoHhsYPHwV":51891302995,"GzJgN1CNtTRE":710720120835,"cCDy3jvJ7QIu":212187988688,"MsrvI6bKOXXW":718085046248,"dT71rwE3xvnp":101786579519,"HJETeMwWAtfV":239644165190,"XwOMLCRovVUI":38429786425,"pRsvXwGN9hSk":191449547820,"1wgn2fAmDKr6":828737778291,"T29b6eVMQkol":915234149836,"7FTEUENnyn9F":421517639730,"jmBeW6YMx9fc":645935016466,"QweGNZ2Iamij":840793578253,"EDWSjUFTmZl3":254373431664,"0WnR7Fr5vyrL":140347175992,"z4Grlf2FsbO7":6716415859,"bs30imBGcQpW":488696143587,"vuONnPHIAtoO":365519490450,"qCnnVZT3ItYq":629857033412,"GMMbezyAUZ9C":479038447706,"kDe7eSn5m0r6":310845763329,"fkamzTfKtQm2":322265456100,"LNQzmLO3K2ai":705450299778,"HUFFf0sSpa35":856480174047,"xnHzltRNbQ4U":405782466091,"HpTDUhmnMNwI":175367889584,"icfkg0KrKrGY":942144420261,"baerqasicgm8":103865965112,"2xkpk1ruqck0":842181953910,"uDyX1pEebCGS":129752574308,"340CIj4MWqO8":987516997107,"0yg2nmBVrkD2":853516424354,"YR8227bR9t3c":69000647625,"ZoFS8UtePoQ5":606904428970,"PQwZCT7FEcgA":972205204372,"vSIG24biKPzH":707872442939,"GsWIAkK76xmb":346001378403,"oAQ6gWzWNMcA":228521767554,"5nZIErhKl6nm":16150828874,"UE84fc4bfnUZ":874461183828,"RBUzJw2gSQng":212583871268,"QzaPV9QOlp2U":857261218092,"FzLs3ctsxrZM":515680117209,"P38O1vxqUjYl":797800836046,"FGhJGTumdqrb":628557009021,"3rN0ApekzdKj":538481815358,"2R1Q2ko7kt1I":767232648414,"hDh47B4MyJ6N":182572665994,"w88ccp830dsm":518229799393,"FzCX0d29jacK":843199284351,"X9G16S6XznMr":569614671930,"jGJTOZrRDohY":437675562625,"t2cRYRPYPOpZ":121832432525,"HcOdhPrOmgY4":821411547827,"vO9EY44gfIGp":672668114497,"3UX3ggVU5DrE":665698764903,"gPfGP6VxK4vO":758035147476,"OfflsQh0VQP3":24124324269,"740ye85rAJoI":940040236786,"Ui8TcjF0Fimb":814088557113,"d5ueR8tb1O2g":325528881870,"0Q9irUyYpIR9":682418567570,"tQaY4douUhjJ":24568525211,"aLfAmc3EL0tQ":245688164548,"yXU40XbU1Fus":25515375486,"0PpKU2ZGKvtv":725515528021,"ebXbMc28zDBG":679664935938,"PLPTe5MiSiwL":41307395123,"rS0OpyZglU8k":551430216335,"fmedauqeGc90":270108437034,"LLuZDqLKq9pq":866238864919,"nvb4ATKBN4pG":327630011470,"fz7hjtpbWbdX":656884886958,"kerM1oG9F1hY":266579476375,"BoglhO2uspOd":59797870637,"U2AiiOmtanMd":526477343528,"2BXtTpyh4swR":409945000696,"lp9DnvMYkMta":212482930530,"ipQgX4pbrotb":58691394284,"QkibHpMCNIJg":943930664348,"yLDlIKcj4p1m":854990725605,"KQH3ruwKoQG5":90129089133,"5TiJxPMrgfLo":832263261356,"ksLOPP9T59IO":378392640301,"f1wtY1P1KtOu":639715604396,"xcnGR5NgHlCT":947699615672,"nS12TWe9abhp":930238045051,"YMzJGSQQpqsO":301565406457,"MJDa6QeuHcA5":336101158904,"rUojqHqGeJaN":216534077602,"xYwZIHogFy75":522377623234,"7Q4PrjwLrk1f":70084745551,"QViC3xCgc7fv":2886366429,"sjTPOU7fRw7v":494071768462,"YXGbZRLFsNmD":197439595353,"nzqrULhssbxI":986462352931,"rHpzJ6RwiU4z":104011311168,"MWDeShK4tBT0":724803276645,"nReswiy348hV":522913006680,"KyZzACZ1I4OV":686740273209,"bZtFPnIITGb3":872969863831,"gMk6Og3mTbxk":839624122925,"6nF5jK4FZutt":311424961112,"tzripBc5g2i9":987006621751,"eMxmL79i5JFv":477611693606,"x6rALKahcnib":829476668514,"XF65qYo71Jo0":71434346280,"VcSye6BkbuUl":668882267531,"JEjTvhMOsfjv":487276455727,"zDdVngkmzwYN":900386264275,"LKKEakDzvH84":269043349404,"5w2kelE5VkdE":366338199819,"G4OaxpbYhMum":827602247725,"3OzbJ6CAR5bu":644949551950,"AlhlNEC51Sc8":842097265400,"Y8BoVNwy5Tlw":716670127125,"wH5gtGNJ45ze":852035751291,"SePprIfvheYI":837724922385,"GGeWKt7hw6bu":214406664813,"0XwpuntwRy20":569460666741,"ZkRCleIg9png":446637351892,"84qAo02SaFRu":443630348706,"Y0hjHVGJ0UCN":344468951575,"qSIU3qFDdUFh":896568713491,"5loizeaELPNT":452289027347,"bP6cx9jyGSYY":135938016178,"Ybk7L7Njiipl":55012400527,"E2alIXDG7JiG":849120108385,"aO45c4ikwFW5":385923756064,"ANBzLd4FcPzh":334528757409,"SoBErUJzZ650":291582938371,"90WCjNiGQrOs":717909239700,"yQd5oy6jqyU7":598751270109,"fubHh68RM9qw":568615644216,"8bZgi9vrF7fQ":547367804769,"DndrIlaRQOBa":277988310974,"8q0Bj3K9VaoA":145288692418,"Kg9gFyCbqm8X":577841325236,"DTo2XhrrVYAz":219246031211,"FxAwi0i1GNVT":852699206244,"M7dfXKwn4KLf":164002285908,"ny9yH0uyaFl8":293241656672,"IuzUqJsGIHLp":204453548659,"Lb6HUEFINHlN":439525659306,"lRCixH7hUbkv":470178396767,"4IqXNBJ8TZC9":567183600425,"0wpM7Aumlfd0":753162418750,"p0L3Fh07iNLk":235359336910,"C3WaLWIVOrPM":127483065870,"B3BIULRbSZFE":529282774633,"SbYVqVfaxrhm":299213633213,"7FvJdxnYYvLw":171736808298,"CMLT85QZGFKk":535973972798,"NqhFmiEjvF4c":389934188179,"kwrkNUQ7xmMk":548089827242,"em7hysMtSKND":887084327339,"cedMGUMbbtNx":888563843816,"IDN7INrwpJqN":761183063030,"A4ZC63QflxXp":236107942779,"4DES28iOSiAn":364784394661,"1ccaAhWC0ZOh":652450833099,"ZRyRha172Ant":648899396381,"c2uOUJyJhq80":289869862029,"nwRKiyIfefED":961985009818,"LLKGxZLDlAN1":167582374820,"4Imy95VzehtR":526398079050,"oRo8ac5XxUzf":292939410319,"lzQHh8I9kDLO":682918823532,"6ZrhAWAzozZE":285849384684,"pmtcM08672hk":957777045595,"HogqwR5WtXh7":713545933600,"UIgKoq7gY2aM":873107086228,"72I50S6VdzoE":816035907770,"6TzENR3GrQP7":10324416729,"QXiCJ6ongdvn":771842220357,"aivW5YEM0kDs":133911878448,"5U5MAoviPmUQ":616956421734,"OGoTFy3R7ms0":969129012471,"7oJTJPHjtjEd":696719185468,"VYjUCg7PDFXY":643979727660,"cOv5gW1Zmrkc":848717562620,"UyRaBlSO7EGf":572678002999,"UefvKVO1aEep":682205467163,"xIJzlt4MCKKp":753772808158,"ngONK1RbjxcS":67769113938,"AaubCWhSHdZS":670139482626,"ReEpN0zHBDe7":951281962088,"At6lP0WdmmOs":223272945737,"PxjIrMnL62Wq":289424052267,"y2KxsPewnxn8":810716464093,"tit0yVoUL6Q4":17203758459,"OMddH3lAcTPb":289118080463,"sAPZw44oUq89":30422930168,"F3294RLXTMxe":413919016718,"GGjfP2xFcGSg":93044120127,"JeM2sCJZ2LTo":966952665374,"CG4WaksBm5P9":991632814255,"C7LKtYT4mtz0":796690931369,"bR3B6Fw0sXGw":207786229250,"097RVftEU4pC":225900395681,"dBUfTQTOEFQC":562075076211,"K8SLLTFDJJdB":433301612954,"m4urASGPRSGS":518514806503,"JeKDAhvfdYzk":952585584917,"pKuLMFIwJdCA":728778680897,"4MBGconAIb2i":406494221946,"fVQLsdKlo2bU":309570284908,"Nj6Ufqv2Gta5":165426000859,"54N81YCsTORQ":798761640242,"s3h0anQJBL8S":971406395319,"gpjeEUu9KDlj":526382143987,"RuXh2px2lCq6":22136814812,"tbMHuPL44UAv":242307209511,"UJw3vJMq8SVY":569921567783,"3SR7luPeGKBd":659036055952,"TDRw271nUtA2":406069886516,"8L5a4VC1J3WV":2956732773,"XNq550YK6X1b":528558805794,"bsQjERuEr3si":773609590580,"Kitl8n7OSPGF":835381749719,"1YejAS97epy1":872064477465,"mSVRs4LVtxVB":631437132518,"3Yv8UA10Z3Xq":494657853546,"40qi0iYtMda0":127409347791,"lRQrgUYLIgur":197667848276,"bG4um5KJACUM":882349562557,"OZI92t23brCM":443218480000,"WITzzNg2hNqn":79197929796,"kSXwHIz6y3Pe":42543515705,"R1dhBBODk5UV":877034439721,"Z2qtwmG5ofez":933210229596,"QNRSdPxhaThC":251911076398,"kVAbigdGRROz":575008467436,"kbyEiQSBOAeA":280507445502,"Bc5PDChSwn1a":443034346733,"HyWGtwsD6839":953984304107,"jrVF5wJZVcgJ":422481236092,"Jtc1YL6JJGMb":65455142065,"HQqC2Z7p9DKh":962498280186,"QcL5YMP0AZKO":943512754750,"cqdUEtWgXSDm":88234089672,"cNYPa0VUdXl9":554468628117,"OjTY1KPHZjA9":153196691304,"9ZPZGQUNrML6":265059980462,"yNoyWOY4cfSG":993836926325,"8P0Z6r3fRA2H":933839992457,"3DGIMOuxGEfT":66484336081,"lhuRF2bcjdKG":273763518562,"oj6l4Ya2uBV9":673174714460,"DvT8Wr9Y2Eas":155713443947,"KyuEXasm3Ppa":193404550909,"rS6Z8qJPpz6T":766937255887,"2XMln2BfSL1y":347106238293,"PVY6uY8Oz4B4":501948544434,"HahtLbLPLmsg":296920355112,"D0rwS4YvELu4":915094684796,"SgwS9A78ZqKb":784591028127,"gBZ2wQbFz8ms":361350406479,"p8H9V8kXoGyd":814672563452,"xEe9bbcmPx1C":283095635478,"SPks6lz4AjKY":115159182865,"BNPznj5Wpyc8":806273748544,"Xw7CvR3UYiKC":540224828408,"fu4IHMIfGLme":134020202345,"az8wWmdc9IQg":630662034690,"buA1N46X0GOZ":521187855123,"q7XKiPxXNObX":46375770192,"B06Eqz3glS3H":538764519798,"9RngQhodcWKq":519265945859,"WPt9Ny1lZwWX":598180343668,"MvJIJ5dorBkq":189780328845,"FTzASSoecuQb":572060876420,"Ll5zuxKdBE2E":440447704034,"Lzlc4ryXlNvl":248651043897,"vavhTbLkIeye":387181501515,"yobNRFZOu4cd":808676514051,"N8l8VKbHZfS1":191852055560,"gRP4vjyKqgo5":676792915292,"LqNblk0uWTjM":640817987102,"xa1LYMeUF9Jk":428944904134,"sIhERTp4qaiS":429025342799,"BwdppZoAIMGw":578594918120,"CFmZAaXmBmqH":40476744884,"FS5WUuJ9wMeA":466181886213,"irtGHLiFSL0m":921418575731,"r96eEOQ5Wes1":368373209446,"IbvrXafx3OH9":202897511616,"95WMvcaYKA10":529974773870,"uMxneyrAY6oX":778674049726,"C3dbFWUMHXyu":402803463246,"t58UaQv8soG9":683641534982,"fGBUBZCZ8bDH":830114600181,"eFPMY4hIYGJL":689842049002,"tJ8yYIXN2Shk":813484971254,"wF6RgxCLbJEH":279903240944,"raq0XnZvDpCa":230941342907,"wjDAYosQE35D":838455461262,"L7OBBUOfGkam":510478362150,"sFpAU4b9WQfY":274719541045,"UT4fVSQAtRpS":309421935649,"WIhJoDBvC0dy":242567867914,"y3oezSNGMl3z":195733580029,"FpQ3szwlIUvD":431807119035,"6EiDGzwSyNxU":241009799446,"VZkKaU9EDTXy":841962274596,"RqYhTK49ndGT":232008043919,"anXNyOdcPrON":116214451670,"201MWEo33Elc":998439340622,"eVDFAx6JN0pm":979989951762,"g4QQiN8c0yHn":193760172893,"1oCIvmMrcDjA":902661090446,"ZPCs89zP96i6":279011019282,"W4pDh35yklz4":101676205216,"sZnZDvMfojsG":695316068132,"QYXUQZ6H0dqr":265076685922,"0dA0swfYLyCT":340462602130,"PWsjh9azGxcf":189199362462,"efwHdFm3EZPP":573935504790,"2yGgrQ1YYsnk":81909358375,"ymHhuV7YrDSR":274518823424,"SdCVvyKPxQnI":310429129851,"CRaeBybQ353q":525216719451,"njiM8WiSvS5E":756316287745,"epN7LmMADsRE":907291274510,"4sW4woJ2PpKf":6954339652,"QVlFeEPmTzC8":69688172072,"LWaR5kJCS5do":792015806462,"uW24EhGdz4rP":850182166283,"ENheHDvRPlzJ":228635431928,"ia1HUUOXX3mZ":462215910028,"IgPRVBwNImcR":534456365879,"xyvuEnffOqqT":381837725701,"Yx8QhioZPjTw":748208566977,"vZtpl2Obsdjq":740712420349,"YQwZEKDralpy":384555726289,"zi1VppH2FF0D":581091649640,"bhhRoaYXCrRU":81396687761,"LIEw5izeZhSH":981317466886,"ass6SkuniXNr":245041774643,"RbfMGy1Qis4U":582425090981,"nT3bG0nCMKqL":481087907098,"v28Ey5wuq36r":731386460017,"FMDYV57xtaFv":470055244733,"qB1xnm0sstgx":318918133142,"l6sN8tGzYQAT":493039528598,"5Yd1FcgvINm1":873897811055,"NhpWs6OoqSNK":705151537287,"HGd2mI92aPY6":172008186036,"2x8wxU1y7HHW":748456413932,"saVGpNeJTE51":494751664714,"WOOW1aD39zeP":221356469159,"R3D9oiESiAJC":353072449681,"c2pmcnqNpKDU":105289370888,"teBJq2eGb8NT":655944435156,"sSvb5vYlPaLr":276809188919,"YcEemtYhdV3j":788456305168,"OG6g5i0jhE5Z":638130836941,"EZv5XWISxZK9":919792318196,"M0xGv4PSYLLj":18709192383,"0uRcDYzu9pPo":569877107993,"gfcL39wYieL6":383466948483,"FXKfmRrP0OUO":598247976014,"yN1kJ4i46zbm":659223779646,"wZ03c3O3qFN9":879999851859,"sJYWKUArgZ6z":201681313598,"yMOBrxHSVdJH":773216427339,"rk3Vu9HD66UD":925605315510,"05HBy50Z6j2f":533166122680,"u6neoePgm7uF":745631602572,"K3ReOjg8F9ag":978485149610,"wKh0wKqzkrdP":965262477768,"uPrU6XUnenOQ":944756227333,"5IobghFRPGM0":408333302232,"o22suZesnM8y":37353847803,"jFbOh53j1fTg":989278028084,"n24NuUeIPPal":771944660098,"XxNiqYQhPk1Q":575621824218,"q7pZKCWtITC5":193835372736,"PV0WHp0qKEbc":938998087602,"IWcLvslFkR5q":829659944836,"RdA1aARypX6f":796161677633,"XvTynRGtYh8c":335988930654,"rB16GM1oQFEn":408326444300,"8VYzkEIHomtz":72470171627,"mrVhNJbyf5YU":167737220141,"7Veh4Z7ozHvJ":387593814618,"ULYJSVI4UcQh":873212915092,"2bJqBQMHL8Wo":705883225524,"c9dhTypdupa3":218123838570,"RlykNtJ3bj1q":84425203672,"zRVBomKI0USG":668364793248,"sYArXBtiM29P":900144368041,"pIGvTeQoJ0wz":513430097011,"y1JgcoKGYlfl":60455690720,"H58be6HlFfFF":736903420618,"koEnbagPr9Ol":466224695557,"1LziEAWwfRYm":972176488184,"tJ800TRVpwoJ":440377438417,"ZkVRhmCbMHOH":826635297337,"s0Y2XVOZMLMs":552639458109,"P7pGlPIkKBzR":954864200121,"yfL1iHNgRm70":718251531423,"pUjydvvFJ8UA":491716078576,"eE8QSw0qoOt2":61642916665,"H1f8563sYikv":5828016095,"6bCLBZr2T6hl":783664570054,"GdlH9GDvpN4W":441564703312,"jVwf9qFGKn4h":15034188558,"VZBm84PtbOjz":542540053281,"SQMT5A4SbPWs":174906292276,"NJvN89sSXcYk":812808793613,"a5f4foD50Zl3":810326426570,"vTpNEk1EINPm":814908873946,"kvSFfVOgr8Jf":172860777744,"3gZplhZEJUlh":551755490817,"KlDIPIATBTto":171403998383,"z2P1qoTcolgL":411209362363,"4caVW7GWMtJF":681528694272,"RLLitdyZyzJS":661659992721,"a8jmhf0hZOTc":713690249936,"XDQv0SqYLOI1":910049972553,"MLvle5RqtSaZ":111843971857,"BpB1AoUP8yas":826614353860,"YNChsb7h05vJ":983942718953,"R0eN1LKqFa9o":480071014636,"UPkODQSehqCO":899558624309,"kZdAt544AZfH":386148860289,"rZCCRYvl396U":277896340300,"Ghj5F8DPAw6T":243197998106,"eHLPhLqEBhvn":394745328206,"e13zSCpjN5p4":255914666367,"vHcXnLeWyHi3":463181832688,"zetv4YNuGs7o":610623351130,"Sr6ec9CmItAR":643598240752,"A1maBuGJELK3":947609086697,"ackPGZGRza1X":560434629904,"4tWuKW6nvdTE":258662540241,"FLYJLwgWgwdA":521682215724,"YgmPQLjRpPug":925190889365,"Y6p4V25EZMFM":248761677982,"z3am2t6fnJ42":18165379413,"Glo0w6t2rrrn":856190218109,"YcWo8LSYVOKI":294744763570,"Q8JKGK9Qtmq8":39258573501,"AoyawARnebxA":778162428787,"ZcoH44IFK5q3":386979046510,"NqL7wfttojXj":193619215820,"2RLzp4U5do9O":177507206197,"AWZX5bFfYM43":613049837002,"KOu47grBwDeV":546574117316,"yTHMeDb2Vp4X":940714748854,"vlD1uSIDTkOl":346184036040,"fgmyeVA2G2hK":632968521435,"j89DjiqwG347":620932405146,"thMV49L9OLGM":306998169226,"w2QrsOdI8q4b":413368574535,"H6l4UgIgTcpQ":209512152612,"LkQHaaukTxgC":155011586852,"vDqtebEfAn2s":67298222610,"8F1YaX37ZtFY":528325435439,"LU4kYOrQ7j57":697219201737,"sg4gfLKLHOZt":482179219049,"5PBVS6Y7lor6":918359288402,"O428tPXRicVZ":13713955375,"jx0KwOTLbeg9":210997559514,"16P0HJcr7spX":492517286638,"JNpkUWUqqIEi":71833172636,"xtlKKaYEEB6l":290572783007,"T7vs2epzxo53":344671145107,"vf9sBQxapUyo":226577614851,"8T6a8dpGwoG6":178265888441,"z6lDPYROwXSc":202387818671,"1XVVlRWMPjK0":778522395450,"AoRRulZZeSNI":62413470505,"06qxiWQxzVvK":350156867320,"hvpSlSPeEPSY":851164637798,"BUw8ILo3iiBC":870510684155,"cFx2J7I89AB8":562143855562,"dRZpWAWQg5qS":377844532529,"C7bKuFWUUbf9":61797084858,"lPCj8j0vxNaX":167426037587,"wbmnGh63Q4V4":545402064742,"UF8r9WpBorF9":825647133579,"zbGrnWDetZz6":662055211794,"MiivnoRnaBNQ":733848490695,"nWn36uwNaB9c":552993956428,"oKDabgV7o1Lx":634225727027,"kji3l08Be1hI":515825833087,"pvSq7ePFTki7":909169799032,"o55rUjUSoOVB":523515099827,"9UXin55asDj6":467282042172,"HsJBndIxeIVa":289893746727,"KILfTsFTYOPK":154167912237,"NMtudSBDHxv0":201869844834,"ZhtrfVTIvO9s":550389591128,"fGjs1BLlok0N":607013367509,"r0yVEgy6u0om":37777261453,"1nzxaS6EE8bt":642371362922,"DtaplOHebZYX":226097473863,"XSvkz5PHqSly":589334463580,"f1q8ZgXd1cIL":747210683211,"XHHGHCw060aY":101730879762,"tvlNfvj3cqew":766389662181,"2STGmXJ3q26s":405937320690,"46qIHWAro6mw":140989503626,"ayf4xzLMzpsf":963241146773,"zskcyN3GMMKE":617046860753,"qvtDZTN6ZRIp":827307112229,"gIhjVCNEuXP4":753026808525,"3WbnBS5LoS93":582663484409,"EoPp7Fml6InN":604675824350,"Lhi6Vj7qBDB2":738596398575,"aI2It3D7fLqe":407204864830,"jD2JApCvd2v4":962566012209,"oKjstimg09uB":216055665918,"cxMxbAWd7KPj":476457953342,"FQ9paWtScwMU":69812156943,"okaVi6IeXnz9":176505443504,"tN0CwiXVLjHJ":863984843430,"FUoecSJCN1NY":545372616445,"Q4KjxrBgtH9k":983780940453,"IvqmoFJUyrrE":992395817520,"2O2AOYtILPRZ":658906495095,"EjrO9HkCwB7j":364516306295,"YVKoWUQ2W5JP":352913789013,"rUNpbK9TFI1R":846761044345,"C8OgJoWd3lBU":863579784775,"5CM1Vh4NEDz5":293297909218,"kxtxud87tPMH":148047857179,"8iN9igHuRoI8":510069171873,"zPsoUGTXdumO":329382130797,"NfVDlJjJlVa3":116996018427,"GpuDZRhSYo7w":296077513429,"Xl8K4rvp75ed":507525255942,"yDv7kZHl6nq6":721613119032,"ClYl8nBAiGgZ":497322346644,"uoH3MQHsMso5":872455083183,"FbI0Il7Gyh4M":584124989970,"DMZ6g6RbOYjt":458381674794,"uJZBGGWWkyjY":512149211934,"pDrL8M4tQkom":476241118696,"EtqxvSkMv4lm":778492147461,"CpsDgh1TErj0":569395931740,"zTallIzR0BL6":701635228243,"p5U3sH9KEtDx":307842675741,"ETPRBhHdzf4G":481772466751,"FxOoA3pVk9a6":790259822697,"deXykFyrpIYM":615337258910,"Wr1EpG7isftR":955324498965,"RD6qjVC7aI8q":246646650344,"luebkXI47rdp":830758799838,"CS7JJS1RljKa":163472475244,"qmCcZXR3lyqp":52917801395,"1jGzAwqcNWqn":691982954104,"SJ0AGOtekuwm":423948342565,"PcI2ThGcLwxy":421507316432,"E5W8FclIt8l7":256799108903,"tZbJiG4W8CIG":319788508966,"ex8CZVVLrFSl":97134665787,"Sk4XolIvlzSr":232784542171,"6l92nPtN1cJh":673275857952,"INR4xZM5oRUR":448639635351,"45FuNDx39mIz":658789555430,"zhvEVAuvuJUI":178488142304,"vCe4vLFrrV3z":262874939977,"iuE9ZinfOY19":246748543539,"ZKI55SQrKeBi":675812985194,"shzHbWuo59pg":722628172258,"py4H0nrxi0Jr":101064426089,"jaKgOiDJ6wd9":83749176806,"Zx4tgLw4l2DT":429247347080,"FXBxuvYzcDEz":698576557927,"dYby6KP8rHnR":692868067479,"UarJvamtQKe9":260174176710,"StfiDU0oT96i":600828090789,"xcVeZWD1fI4b":51107174570,"ygxfSqFSbLMl":858832710144,"BM5nLUcKwQxD":905557642991,"xXDL4XBg80Wu":260132925491,"sIeSyWsI3E02":267766212439,"bHcfPOqPXtKv":986868496782,"CLASJGMjrDNO":790406949145,"3o6i1uklvnFi":983113483319,"EtY6x3i0WGTQ":701796863712,"PxAHyjmMSTMd":677059423484,"MPsBd5qVH0Ge":860772528977,"3RX0oP54fLBY":45396521211,"hCrUQjcdu74X":734044164890,"MISn3VpAWl49":458287689189,"iXPQDLCWleNj":441266612184,"w2Uns8DAwPMR":386643977162,"gm765eyZLiO4":956350600136,"toBvdHEC2G7T":150768258692,"ftLyXby6sA2y":851249260392,"sZGasBQVwdN0":631033145569,"dPrbflSTFzGm":319490012989,"kIPhf8kMGvu0":426159116964,"COcLYNZ4lZis":18580048410,"OdukOWEVx2X0":552601759981,"XqZrTv1gJY72":796669492750,"tT8An8JHdkYH":604947998096,"ceAoFCnKsEcg":154522360513,"etlLX7bh3UPs":853556953402,"Kn5wDaZqwMgR":598085435394,"voCa2a6qYguI":611780315368,"gYfYQeL4qnwz":823970906278,"D2v7HMzUHQof":533957397521,"PhppySIKspzQ":169892467150,"4Eo0JVigWe7Z":226204483534,"LobXyhbNxqkD":431868739010,"m02obr67DwOp":929871220051,"HsJ0vr3WWpyI":768131300889,"nVNu2zd8hpom":303554272077,"Kke4nLVGbWCi":388235599025,"w7dn93DRYqpi":258227096383,"gIwVDz4cL6Ud":865597004289,"jimrfq5NZno7":926740886063,"LUUZHcqJzCi5":615161334047,"vo8VpHkmRS6H":97489446463,"fT6c5GNGS4Wx":873936224198,"ZOzOmCsH8dp2":669792827576,"5URN5Len4LbV":814974542542,"ipftIx08raZ6":867219162556,"g3yycDaEjav1":307125547033,"toxPHAJW7HW6":413643811219,"ltsMZMGXqbOW":695955814694,"2hE70ftNGEdS":492189891343,"YdwuR3MHgQFz":972150338262,"OT5pIxipB2iJ":296355959099,"pIXRNtseEXwp":564744730968,"koPHrTTRJJoe":417123479253,"7n0Pkt2Wwq8A":629825733010,"PWtnAnPw0NZP":986935975764,"wnxrGrykhw89":804435758471,"qTIBY5EzRa2d":455352812064,"YeKoBwq42yUk":97421173889,"45G54ojLa75n":862423450603,"vlIXzLUlDj6b":104806386601,"xO2IYYLVZLHA":429494229668,"QdIP6geHVA78":209982005341,"Jz2NNDRxudHj":811294590940,"0lNQrrAvX8BB":249456627972,"4AjIGUjFvKK7":273061767059,"Atj6cA6U6rw8":275575952802,"l7olI4KxpHNb":697453697176,"Sv0uAGFNufr7":237430523312,"HgVZdIH6yzXy":970513303258,"R4DzCEVIFNSQ":305984299437,"BGeC4yQKa9u6":524862498265,"1ZbjjFkv2jHL":719473329386,"HPElbnFBMBRE":552344257881,"986FoxohS8d0":938933304335,"GRKyblhIt2Le":276739710360,"iODvIVrSeb1q":243176155783,"EBYwVxF75yp1":612983953110,"l5GJc7PRCkXR":129380531973,"ZEOX8b12vg2o":619951702931,"4xPCNPPfXizh":810714061934,"rEmCeXfpnLEy":137868872072,"UkLsUBHdQOCg":237563724304,"z7XWVIJDOs9R":526780511524,"5OifykmaLSkM":339100842243,"fZgkE3Og0ipK":109649021918,"aFlrqH3tc2FH":748749949012,"AWcmVJoZclLu":918568242324,"I7N5Ni1fji39":333293891809,"glE4Chs3Axp5":426705935208,"EfJbMcbMILNn":3118618789,"bM1lk6VN4iI3":793178811595,"aIIIIU192nS0":235298272394,"9VpI9YvTmSbL":784830191962,"xYg0AvGBZad2":688085061824,"dvCWNYtqvLmj":357756152053,"IaS6ONNkpge9":391469262589,"7gydecyTXy5z":173502802465,"HtQGbcKCWE06":204328125736,"pKT1dwW5RI7R":621189791606,"yYmFqi79KUHv":967345354856,"itINTvgnK1JW":166278621285,"ueisVGWuSlnp":849899146664,"juRTYzEMieH3":167788119535,"CmVtNB1ODftg":961315451842,"atAnOn9bpR7N":205959056184,"CRSJ46Y3WXGH":84261544961,"vWuL0m7615du":43564587509,"9qJY1EtJ5enH":784064159907,"WRJo21LNCV92":761473758534,"9t2VXcaZwY07":665593497304,"tWljyWkyXxYn":979084306129,"CgiUpQaQYbpP":63337115100,"OhLsaQd3oumT":91992779433,"XITAS3l8FsVo":112492777555,"RWNm2kYp20ju":72049467726,"pnssvFnXBcdx":715326349203,"SY6zp3LzNTNF":585696865833,"0BLH2IMDtzUg":897985560956,"XoN0FYmByDfm":830055325592,"bUbKU6Zy5Tnf":691995211829,"6vccnWUlYKG9":65663101204,"Wn5pwDJkQJhf":458219675281,"9K9sTIcjOjyh":531409495695,"XVfprGbinRSx":644509448449,"Yx1F3BkQysH5":407694248302,"D6kRYpGeoAK2":324876153203,"HfvQAW56lvV0":529453006109,"RCSuCZV9mh8z":19852035036,"IUA34HKHKGfC":335421686269,"e7WD8aLCVj5Y":191581856755,"2iYcmLP6jBTy":699039875000,"e7G7gm84hZ8S":104246878071,"xqDWKJObFrnp":458803146404,"eQ5zKIuWloCo":483304182987,"0eUO0BB3SLsB":998505270096,"EAtfQRVmUvOZ":79481822107,"fD9R8CLqJ0ku":163992430835,"haVYDwYW4r4b":219021606080,"LWxFU0mfgLNz":335645128302,"72AfFEkNKudu":779673563788,"4AEsd8PWBgXh":476110011430,"kjPhD0mKZiT2":797300702148,"KAwbAH4RSzFz":203900642944,"Iop9tf5A4oLj":828917047566,"K1V56xJxpLmC":90813737410,"GngtXTcW1Qst":642380368292,"QKZ6OQYU1C2T":746208499934,"L6WCdOncvQsr":631930027328,"6Id9VdBXrsL6":432205891831,"Jw0403h9wUJN":496374853736,"BOr2HaaJmPSK":760241347305,"sTXn94wgZmFy":921910218617,"7D0wtDUmy3ND":924086989617,"XdQW2R3DA39G":762065750993,"tP8XwpnO0BYk":70064158387,"XktTtpMEZ5P4":195296025481,"Qiw3ywRYrxIe":101740472089,"a0UyxNQ9pBsY":354683545722,"DJeenr4AB1Q3":76918569996,"SQmfqQgGhHoy":267982186315,"eEK06obvNzEK":308188189444,"e4VklrNFwOAn":609837324271,"ic8CTKV28e5k":133256407539,"izBLQP3HgXuU":204863500938,"E9gmxGEEdUE2":92095666664,"ldPYtjmc3nsP":748638730744,"CaSWq5FgrDAa":139898807087,"LsRzoMKtXWrn":727362988925,"Ts6bVMMv9KW9":274620184357,"ZiAQmMLu84xD":292118843259,"ADdGunjlGFli":805324326263,"OKeH9x0mGjXR":567686644572,"ojWv3yHuvCnX":183165647622,"3tycLDu9J1oR":766589587755,"YRXU7wdKylYP":61505713907,"bevXikqOIuF4":419186024533,"4DpFGxVY8fee":402486657122,"acRj2AvZNZGl":941681463441,"28aB4oZGNwzO":735195009429,"2M8CLHoHb5bn":328948081102,"7EceRtpW7AEk":276503828372,"KRmQeMjW05PY":227528934237,"CHQwThMmqNlx":190535240698,"npxjMpmhCBqX":173715335185,"mkwgJ9o37M3s":332060844888,"Ny2lLHEf5g3E":664493344494,"fRzvq9FyRwoR":728728725921,"t8s2HG5LmO3W":11114792576,"lHiUDNYdeaZf":911234027473,"i0Cj6MSQ3hzd":208528590290,"KPonnN8tnhyD":948524671541,"J0t9EFbsmOrU":265636989753,"8j5f1hlYvCZx":550022177718,"d8sKNqSDmgyj":985641108765,"8XnznpVP0la1":920879803715,"vaIPHwb4l0oy":734027778775,"43hUtN5GQJLP":895738359298,"Opj9iqEDPYDs":651480582942,"U2mKU6zLkeHv":9146974236,"wu21zJF3MdKe":947997186683,"rVXDS1rLg0zI":20196312238,"5hZo6Xx9vHP2":408864572839,"ET9BBMe0JQnf":476837245310,"ESg9KOGIIby4":892674920796,"3OtT49pjj4BE":437256951967,"yrBBI3GaGgLr":750551274077,"VIK7XlSRUy4F":379445862958,"FJcjAfOFhUKv":146365441936,"gY824Q1WU0RS":271634640834,"necoSViTWtTp":53838637255,"7fOFyILEaHCX":739406832718,"zU1M17Gjn8lN":19510015057,"WGBMVmGs2KJz":64725096344,"10JLXefCk64g":83482148949,"leYUM44TY31j":251891201932,"9eIELtIBSyRl":249518750001,"KcPVQB6C2LWG":483303624740,"dnVrco7YCinC":304842219631,"XzWqwERgea3z":370612312924,"laAtDmsQBWoh":134090897233,"1GhhtFDljtn4":806431821127,"linXlplItghd":69672103808,"Y4HoS6GvBzOu":79193591431,"FAyPH5X7Wr5k":511583843539,"Q51SrKH8RbY8":331632934699,"XQ63xTemHpIu":810904590720,"aq0gWnxHzSN7":779396113672,"FxUOv01sgVLJ":60053270292,"inDU7fckt9od":194924445555,"w7yY9YKfhA7T":86870204720,"XDYhSPY3NIs7":501124655025,"UQksNFiQtDQM":842650005550,"NopgRB6ZXlqg":729227468843,"rdX998EAs4Ds":631601246470,"tjJdoZnwucpa":76357214224,"UIX2Mbx2eFIZ":302115241839,"lEuQpbQSxEta":713763126945,"YW2fUaJvzROW":284861075421,"f6pIEvGjFOo5":779172237880,"Cn1l9lfO8Q0M":448220823856,"kYxmpLw5tZEy":471431596314,"iL77diwj5fv9":699975896087,"reIvwmZIbcc1":982567663199,"oQaUvjoR3147":982749054348,"INxEC5XTrr4U":490863844105,"t9hc4BFR5flS":523537235198,"SXBsJMk4FfWa":192613416481,"ErLE32LXKVxL":146042876561,"mIpIpPCxW3KX":728707509374,"c9fU6NoSqd66":703369574614,"5rsWDBDrueaF":959433351244,"b3KspvCIC7ro":226022406795,"BavGZBVi40ld":690190456896,"tsGBQgkmuoqi":760452363670,"dwRxuB9zbQRG":100710954344,"A9WdcWig5IpW":736655808506,"WZ8aRXY6ppJi":150541694832,"Fh2lhQL7m3zW":33939699862,"1PoUei05QOQN":413961020563,"8PjkbdC3koei":966790521321,"Gjql4oIscePT":593379114602,"PluzY0DYHeYn":561038148906,"ecZdCtvLtprh":717951977381,"pGYVikbnm93D":168619641530,"EsMb7EYypMwy":9587753791,"lYRohwij0SO8":241225215413,"0aaGpbJzqamL":490939479956,"qdmKTtvvbvZk":498568374910,"VYGHP06uA2DA":542395095941,"vnNMIEYGEbeW":480891641109,"OrRqg5PuMsWx":458106731411,"vLYyiQr1I5Du":738232181412,"G1Pa6BD2HPcJ":731499012393,"G3QsOyuRGNMO":124190041468,"1CgnVUGq7BFT":401683203578,"tflJw5cm1dTm":434031695974,"kOalELBnbbRO":100642816481,"E9MhlxwSnfV2":271776037010,"StAR9LSqEIZg":153815190838,"3b25ZmwNJJzK":998837742377,"QR4j4pyu0SuI":335170772223,"cR93d7o5ugBk":674496020409,"kEFtybI3CH0P":148625723046,"fj5pj691yJ4C":232309959474,"sTYQ8817fxap":774079477404,"8VQ6GeNmnzMg":908470873874,"kqWo4PxmuDLp":828870567773,"9eK5Piw52MMR":304963259561,"1xsZcMqwpMvN":361012301057,"At2cB5GUptwr":237192113331,"xUz5CRDtxGes":798625653497,"7sXKmALyhe5t":411689622004,"HEX7wjWzTPKd":812769715431,"aZCVBD64CByY":884511851920,"l5OzCTxGtz7U":657453963510,"bDzrVpuUzCcl":361097693324,"VbJLdWiRgoct":682290771397,"dMKc752U8WHg":714020150159,"eI4kSByCPhDt":419411482802,"eNJ7TFh8SeVy":963817831704,"5TfgBUlBFeSk":916925406379,"kvGyvnxbVevK":203804981250,"quLqlv2krbKy":595737056556,"qDJ22bEq6fLd":330088353450,"2AcURklngdBk":973487729201,"F5dF6oBux78K":795538527129,"CaaenWrkIuGc":216313288526,"vA6Uu8qnFFgF":45709985169,"NcEmlRK4tGiI":287931649116,"iqcjAW5C9m1e":338551727852,"oMUmDFzcrR2j":475609354062,"3jAqSSwjSUFL":860212826429,"gSaxzFHofVDc":323246600662,"iN6VN7WYOH6O":67319692696,"r7os0EPdsVW9":337245737219,"jTrBPEK9wG73":302457465333,"2p0qX6AQ2wpd":849207259489,"9k9ZKaqrMGcv":162090740058,"TU10Zn2OzLzo":534108989122,"IKbPEvLyDjFk":164627395512,"hW56lIUIKB0Z":868337611022,"6EPQK4HFUXsU":709117219884,"STTfbgtbinwd":137343633084,"OZ58w8J6iVJQ":945526095952,"RdKZx28w0ok4":397368758423,"36lsHZsal52O":890300132696,"HYuGRnuNHIyZ":780844804653,"xIu9asnfxJBh":792606634664,"WLkmDdVf2CJK":301740406218,"8gq3g7XQLchN":970109688073,"uPNU1UzXfSyZ":562607824214,"nhdURZwk9CnV":393364782321,"8proNe1sgec3":87986293796,"a1Nf9czKPsU6":86202219420,"j6BCvnC4a35D":896536049796,"gLPc04cEbJrj":65161782658,"oUzPG5GvH4Gb":661380575069,"Al0k23RR7lRm":754237898654,"ts4kY29MZCGi":468898213695,"QUwasqz7UcVC":482407451578,"k1bzDCqiIVwE":439090457849,"9pvXbz19gBMz":756552097974,"A9AihrCAdvlK":247307385811,"CESlKZQ5zx5t":195739232632,"kttSRwJeTyEk":51249888803,"ppgQ15RucNfA":267615863517,"JDL4FKUJtfhJ":196307700819,"xWMJO1YpXSxM":120930728145,"J73wxYsFjG29":559807016105,"1zO8PcMZ36LZ":587600374583,"sRN9RYGZ9FaM":727036111856,"13N0LsNXITUT":975050241120,"3BZw8n5EWcst":764328449491,"4wrq4v7uyPLc":193714967428,"CfXNjy6wj2J3":606813526887,"LS30WAqORsND":587110432165,"Q0PJrX02isvB":939502010395,"qPxaG5OzjJZ7":259072397826,"Zp5Ox9ug9EpQ":54788193994,"8O8U5QVh99Yn":902102475238,"Zs2EE5CdYeFI":547022320720,"Kye64D5xuR3y":997436216125,"MudaZbpCoZlS":301757994592,"uSM6CXcllWiG":141846646964,"p0uEyy7EMgag":72945091148,"7e0T7hX0BWj8":687278804194,"NCByMXhZBnMD":31719324458,"ePOaAiZGXmZp":221466905361,"h1doffkJlJ58":462635091844,"85s7TURGgtHZ":68409525133,"zg5XZgmrjJfd":323168060628,"gd2UdSIOui1K":520464129268,"0PYTbKtQg7kH":404573542834,"P4GtpyDBMCqV":197646586824,"Y4Zxu9j4yjia":799582011399,"8AoLNVX5LkGd":136484004951,"MGZWjYspodmy":531668554581,"lQLiSsR2dRev":231556084519,"Y22yPDlCC1Ya":243626840115,"Y5Jw36aHDMoQ":650458204475,"WcRkN7nUJYE9":843080053406,"geS7sYEJWKNv":683833964098,"MepeR18GAGG5":529543694812,"mzRzbwbE9L30":581481971947,"Ksw8mY0p8G1i":319496992753,"8m9hBZtldAHb":976416045553,"KRQ2BWK0ebFl":94334403834,"cYDrS3woIncB":255339305289,"wpKIwxaKDLR3":810256283813,"K1fkG9VDCDNC":348325254554,"eggrer7vTC7x":117047107566,"Em0QTyf1xhfr":906503022410,"qjMV20gFwL7e":674044541342,"CKCDazjJBcJO":937615086834,"yHrtVpgAPveF":885235163796,"ZrEvs8Sgmr3U":838755067087,"HnQfzEKhnvSC":987329934705,"8CVigzTyjafW":848900115083,"ZIrE2tVGDwiA":348800697452,"jqGKdaIVxHI0":70670588694,"GDklPK7ZQI4W":258210835295,"fs15p3z72Oyp":517939683722,"ruonPep7rEIF":419002969070,"MQiMkQOiQ3PQ":25748691509,"gZssszP0goQm":909586965757,"0sf7loOWWHNu":163578053251,"M518zinguqhj":54762887003,"yH4WKmgX86Kv":676431099965,"8OGGhM6aIawK":1818553396,"W63hhANEsdX9":179530053936,"T1R0Kwm4FGv7":935288945303,"FOJqMGxTdvaz":255543567574,"0FnZrU1PPsTu":477476531124,"RYNTxNnqSCKc":415224069170,"dvwdrby9XxsY":294649864157,"bfdxNu3REdjh":862888699039,"prN9qT36hO3O":776213054840,"5UeZM0VXbLdW":883999763359,"7dVNNvW83VuL":440966866693,"kXIW5p8RZ91n":98062772919,"4ygZSHVmZJWS":467457758321,"YsLkNi4fqPqx":570136684108,"sGRyWqJR721P":345552125558,"3T1rK7fTucVZ":17540284756,"Z2rmrk5yChsv":774944374615,"hlPtmq65KFV4":463679337625,"NOp76K9K0HV3":689292068006,"lVi8tOobtsK5":953339866817,"QjI3Frib6lm4":791674017223,"PhwnEDU7Dr28":888154150494,"paNWg04SQaSh":334772386658,"7P3vDWrM2wJy":77066090562,"orokZ1FPpdIG":566689779332,"i0mryTB7k4Q6":729053463992,"QOK4F4zjy76e":868675223767,"8VMMHc0Mx8Nc":125675287750,"3Jh5i2KkK1jG":554023282091,"tc9uSlz3mHss":374983016407,"7BIKvoD9QZgU":227542943084,"2jT1ZmjsZofc":911786000475,"hMb5bTgxgnNi":911275852172,"Ufa62qKgnmg0":361109938769,"k7a41j5jOXqU":117811749152,"uCWeY9Ki5Vsx":895866028117,"0us9xXefv3iO":285431530829,"NB2JHjJ2KSTR":783564486652,"Qk89odUvJ8u0":184646828684,"uSZcUrOawwYs":372805411762,"4NCS5oyq4fyz":134476734880,"US36IuxmoQGn":399824295258,"6wi95loVKsal":197016252314,"fVBBRsb8AFcb":956427206529,"ku60t7yJKWMt":8688085905,"rmHvI9HY1PzH":864906486884,"MDXgeZnMDbrV":545587408973,"05C9gbsAKiEG":850692422285,"gq6gN5ia9bCv":123668231318,"xESVcI2VKoHo":188309958280,"kj690JwdLaNc":419402602288,"HS6yretAEOjJ":299480997239,"pcHwxEC3SsNU":192925243350,"32rCTtnV7diW":305671255937,"UZ4KE8ljwEZG":252342314484,"EEiXxhdXWd2b":859569694084,"5TrlGOQ2Y4XZ":942277151054,"xbkVszO0yaT7":142869654561,"Q5AYyxTasht1":491066877323,"Ad1xxXxLzX1P":532764285223,"auy8peoSm9I3":973844440539,"wXoR7D2FsD6R":911017862326,"be8xOc9Epi3b":603902244252,"8xnsreZGv6SL":56208816322,"hvKuzGTtbXSV":391051801409,"I0RHdY137EUC":101460659080,"mHgZd2JsFWLF":507882153174,"2WAYbDlYyITX":549677670459,"P9dbhan1nu7w":104571530888,"ptaAJULMFRkt":335178047199,"jQoaVkiYkK6n":986776590379,"QudvNIBkZI7Y":967112517083,"0UNBaNniKRI6":682313696886,"8cL8tYdyJQPT":423456596515,"6kiZpfrgU2QZ":320171026736,"ynFIj9uOExsu":247188271674,"NXFxZ6voEyUX":580048979562,"APqnxSBIXVpH":223639475349,"efxPNcjCV0UP":26829576449,"n5aRKE2JxGzB":755262516967,"6XBcTdJj4hFY":609757219057,"jz1I9fgHOyEi":741398194853,"HQkLexfCmxpY":567164992033,"E0roqoi4L3pc":408481639370,"FCpvKy65y0rx":493953037045,"d9xJE395jaUz":477025312952,"AbOLDa2CMT0U":659674532816,"mKw2uUgk12Um":142420882508,"mVQ5vR42xPkl":788038536446,"04aGJKL5mOaJ":306208239387,"b7xSkuwx3jqE":76264189261,"xVikVLieouIe":668784769715,"XVFBPHpD2uXO":468419158932,"oxo8xoDhyDLF":73146874929,"VnKqWmi5TSh9":203983506766,"aXdq6VRknd2x":346023884430,"KBBlmnqPYnyG":217250312788,"QPY2NpcBwSCj":104072869348,"GZ3QaJ8LIJhq":706395910718,"a3HH8oIpeOdU":71321899152,"4zct8FNID5T3":362607947791,"dbNkePW1zOKn":788336311902,"owiVQYybqD8n":934606699104,"uhzn6H7cyOMX":326297391345,"jk25WT848FSt":674095271780,"YjCiQZDvx0YI":864219921773,"xBnsGCP424LX":381171802912,"02MouZMqFJAo":73050362091,"fEJyvqvT6jCz":764245521281,"H3kFLOjuQZOW":339069928234,"abBzZ0neBL72":829910944538,"ZaLeN9LJwupx":908846032334,"ysLZbMonnNRK":119102881691,"q61VSgYGRFej":655348431533,"F103LoQjseHc":869331757556,"hoJDJ8KI7Zet":760399114612,"ozpBu2jUJTRm":599117478194,"N1OX60hJnMpl":188736904024,"D76bWk4U7zBa":946796727018,"J1SqOumzLnbb":182579094018,"JGzcDdPIF5ba":771164764770,"xUnqAqyZYsxc":75735943118,"a3NqBWgdljD0":627996341406,"j0xVznMDpf0L":854276334551,"LynwI9iVOYAP":824694339325,"dy2UipNHckVJ":24761973481,"o98SzHB09omT":860757933951,"2zq4OF2ndwpD":69510568406,"s4ICwI8t5bEz":79645053246,"A8n8GAXEn75R":264250446925,"gFTHwcZQrYRR":106032618299,"aAhXcuEeAn5b":969676644944,"KIKVZVgxT5Dt":359311025248,"3Fp7kL0jsUm5":869226799172,"gemlaAisaw45":250823691069,"G6W9v9hSJljc":299478384012,"tmvlID10PrzO":278494639319,"9xwuX2RUaNrd":631665709691,"Mqz2dBxse6Y3":821833803468,"FHlovhqjQxCM":468842752904,"x8kEDT7Nd7fk":519915796164,"xKEDfOpICuNn":738215344260,"LZZjWsZMQQT6":217036937332,"N2ellKAPI6P6":771458900152,"IgBc5B2aotq2":583494560586,"7CWGGBgzFi2f":591234515017,"D3NLyFkNzG9o":127610425170,"NeX80G2eDODL":871362388318,"uxSvy2Z11YKa":354353460136,"Hcv0W6JOjyNT":512165936392,"lwW8YX9n3uk5":526495203127,"Yko4bK8GkrW9":664880675156,"9U6I5lndSPka":490447751323,"RkPdRYCSj6J1":723532646032,"RaNForeD3Xyf":470726265031,"DNCVATK5nqjZ":902374516742,"lfWCJcaTsDfj":920405223858,"9IsVWbT7r7u7":257424882354,"VmvbZp1Agcbl":939110774631,"shhDQkLBFQp8":344438784120,"NKvAkulylhGN":36611039572,"pTLIiX7bHvyT":767927086232,"SHhqp5NCUDmn":431547234893,"lSkWjd9TyGEi":967581723974,"wKNGra6qPu0f":446434517726,"1AtqP7kY0Nyi":4415708075,"Ej5NutsvHAsX":216842898478,"Hp5FYznUmrPe":471774374866,"tUqsPyMXySyW":284317034333,"dN8q1l3KgIAi":445520293391,"257FW3QODPXL":606608335246,"7ErbgxBgCigw":789832799343,"NGpwmK4qT0Mw":420997788368,"ERgy7R0DVxl7":388683361597,"1NAeFwtVvIJj":926674820378,"MKHmFM6SrwX9":558868273915,"D7ppZJkMBmgZ":544248447921,"bzdtYrG3vnLF":878752967720,"wSxqnN3p6qdj":242535589701,"FW0EQQEaWauZ":946701448707,"50OdthRkw3QZ":941692669707,"MhEJnM14Zxx3":938135130865,"oYZba1DIEomJ":120276347402,"NR1Kl0La3Df6":851387118076,"eI8zs0nwhIXa":996191865244,"t5cBBqb7koV2":952582456331,"zgfepsSZV025":900511584155,"xmeigiWoUFNn":997230328355,"s61XEax6iQEY":191960226764,"tgNWdpqYIkCQ":784143197612,"k8Z7WKWSz8L6":256385708232,"F7SCPfyreHR9":633393397047,"fanillJo3YEn":720760291007,"YjX6y2KDsPGj":665731367487,"qaAOkvWXHYfW":108111532480,"jUMXglRdj5uL":773714789508,"divUvUqqjzdI":339307997663,"NC6NwGIkdyWS":742450765461,"IaUY4QsCbTS8":759970077177,"X8jZP0eyBn4F":68649408258,"DG1KiR6x2fkV":905646860945,"yzEQT73iAwtz":524839697313,"7F1krEqWMmeE":769122667857,"QwVWT6tcj472":604507632941,"WwneGIS1VXau":758902193774,"GL5Jb2Fbex2x":258629348922,"4ncW2XsyadIt":104256125667,"1zB4fbZ0XZ5l":33428581305,"SXUEDsNb9q2v":70498134322,"bChF6I5vtK8r":504546273386,"uxExeeSZYKUM":866791086205,"pzLX432rL1Gs":799770169738,"7DMjZ63IXSU0":204280627135,"s6uMulpcWJnx":494103518415,"A0wLUPcikX6F":80918227717,"sskxtAlmjBSz":746811747481,"uD0riUPdC4oK":625968148323,"JdFeI8u3AldH":771781233658,"5hUHUYDTRqDX":503748519679,"NO5ndtIJSP9g":90740308645,"8JYqBTCQlvbQ":996153665552,"pbP6lUeCQMZD":238337883480,"61FgfVTAUTH4":701078704871,"7YTtzD72FWBd":161668353525,"u1PEwO60xBu2":377878160149,"MsWE6QfCsw8l":536312795279,"3oLAJiKngDqt":731338662293,"kAdWC8viCb8s":611930116865,"ez9fEQ1jlHda":48194300598,"6b9AkITeBkxX":339588788078,"uN3Weltvvirw":101026849233,"JA0EdimNDQem":728979048213,"LNy2UzMJarBh":406992800108,"SFkuUzKBfEwG":976859845665,"Oeib4i4VtaMr":776994807837,"O6hBi6KGm1W3":844213656637,"nKKhkP44tvUg":741464152520,"nAMaQH0wukVZ":463165226580,"TAI6e0Dl2Vma":214544568878,"GqPFfkimNH8A":208375360999,"bCHepwkEf1qp":222633177128,"7LYEebKtw50K":602715701926,"GGEeKDfO9acv":918766771992,"Pti3TFidWhpO":176516796269,"3c8fk2R8Q2ms":108027799027,"qfnS51uHoUuM":117535980318,"DyVOED5Xkc2I":796982023778,"HeU4B72I53As":256379021054,"4h2h5gIwwJhY":141782826457,"azLdX0sladhn":190963569650,"TbFgAsRSJ11p":814916685536,"6fxc4WOSe5ZD":321184670296,"0gPw4mjQrNUS":146646611221,"ELZgiskJYRdR":848299975803,"cATiOnsvD3Nd":501148467220,"3qGxn5PsupcR":434706089366,"DvWNBvVOJXUS":528105276558,"pppam2OD7so1":139298019777,"YQSypBwyJgur":976151167283,"f9f2G2LltoRM":784101738817,"x80TVUmHw4Sv":248832688996,"PS5rGzAn7BJ7":322597871420,"lNfWTdkUgirY":19415475552,"H2Dsip6dvudM":446110108165,"QTLJDFrVQl8b":89246695807,"qUVv9QlTqBKM":465257491077,"lLRdimtQ5uYu":793245667809,"g7QftHLFRkJs":163541105843,"ooOAQ0aJMVAb":431946241202,"Ec8k0vNMWwaE":849440643367,"ppfxMPYVmxDv":144483896344,"u3mnO2KRhzpC":901559289985,"qtta35YxBkHr":326996901838,"RxdU7DougVPZ":636972499199,"6eOo4wqmpiUt":134936791759,"MRfCFB3mtPx1":454548806547,"Htpew94yACnt":313285914823,"oZZEce1ZMh3l":730451426996,"fiZaJuiKC1Gx":474940140384,"CuP2dkYTAghO":658170948211,"UF8nKG468roG":493241489334,"c6oXRsxEcfXH":17689529760,"X96f3RhpV6MR":976024051874,"AHkc0n0f5nw5":459937598909,"zHOSVwvWpnRR":956083132007,"DxiOsfVlOGQU":360904973084,"YntbFmJxFUjb":397397966294,"4xoDBjqCzkMk":197618759352,"D2mn5IpZiSKm":453456483993,"Y5IpQoW4ffRB":720239408352,"VdAhNnLPoOcX":749076390756,"cb0T5XazDz3b":899540562877,"PiPttqwp4ho7":183291783775,"E132wNxYkj9u":847248959248,"W1m6Ot8E59Am":248961434948,"ouWc9dOQ4bPs":994840002050,"Di7XME3M4PKX":869713098046,"7y4aoft7GCeb":351483105034,"193mO5OLil0s":129887281319,"MXtvxoWyZuYB":348114805568,"CboTaEe7IDMB":157419979464,"qRAtTUOYRsVS":435400055932,"vJ8BMnge5fEO":686303957726,"q5PgDEBKpLKS":705763945624,"f1kmnA19NjSq":71729413027,"GXDoyY1tORRE":158234632505,"A4b30aI6jZUb":843148104249,"aw39dhfQAlYm":328535152175,"mXozqJfsR7Eb":93528621817,"PMcmBVLEns8L":632555360033,"ZVsRZgkyxChL":956671444638,"pgP8kPn597gV":522494518844,"6R5VkLkjucX1":517093149978,"cZpm9OAwO3RE":522273801931,"YTKMo2AD1AQ0":896555628962,"vE6oF96eXdhK":443912550777,"H9gnlu6Ny8zR":381527421242,"DFS7qFSenFt5":694063464666,"NW4yFkYiqhfp":272652995382,"ZgsnFIjNimlB":532856757485,"PMqR6rOFL507":408870962914,"uonjDVGSTdsq":890220224760,"DvuMZbKXVT2z":353911405449,"lOhms0QUYj5Y":474428159515,"JBXOiM4TRm2X":404104423840,"riU1vw6Y4iq8":400372995433,"KO5m1ET72bLW":233070196697,"oQmLMraMD5nE":228908802060,"aq2SmBSqy0wW":32579963867,"KL2ZoHHQb30T":824717642729,"pGzp8qJslq9W":833861588596,"6Mw4OqYaMrcE":58007313232,"uWVMWY1r99Tf":69047751778,"SNLfJYR4U0nC":121499860934,"iqzCj5qIl5qW":15498708498,"r1GkOz68ncEL":497755515564,"8Yt0WcNU9bIG":33926471176,"z6D9bMTFBQuc":577148201541,"oMIOpsblmoTF":529818647831,"vNVUofvsTLHt":354381064604,"RIl4gjVvX8iK":81457156823,"YhBRo9D8c7oE":39430765148,"LFhc5xM19xLz":267602407100,"a3z88NvKZW6D":172880484530,"SVwVKd59rWYO":629231881278,"30XeKEDCZBZ9":298056738934,"IvPwwY6LTxuv":988201219725,"sGgyWX26cpol":896574903207,"Cc8qZUYtpRaV":302245511547,"RwNeJkXts3XC":421502745416,"aspGwBWLpwBK":19153353051,"sOP8HEKoGs9x":876979429957,"TzSD97qqFaNY":325328127951,"srIT0FntlgrL":271123389661,"QH1rgb91XP0m":620976381755,"G6R3ucFhvY5U":773002292777,"a2ph7QX1zWOo":284534985398,"UIEdvVDf4x9q":736188514622,"8P01sJL4K5Au":363061347250,"SPx10d5EEnHZ":677288075523,"F5LF3n38aIX4":629752405907,"b7yx3yEMoBRy":83073294775,"NTARxDQjahI8":919378877883,"NpgYZdCRoMCm":244224686390,"EyszOoT7jtot":857690175598,"EqaG13CSAgcS":312366939866,"VEXKbDBwMRnu":186313507893,"ijPvdu7UvvRC":191398243376,"ZjqggBINDdBI":8883129474,"iGTM8qi1RBnJ":136875691790,"61PkhHVUnG8X":107267123436,"jAqjLPFvACzb":655109127827,"NBtqwMqLbXfs":686454493315,"KDzyXWfD1ZVd":697191936944,"tK7OVuTqi2Ex":126858193585,"XvM4ogsEKWNX":603127326316,"Zs71pt3JGElK":770019904941,"Aao1oedBYgxk":474037434149,"7MsZV5VqtVzJ":739415222371,"zhnhGxG1yTHm":539062277128,"ZM1xO8JVakR8":362179050439,"jnndzj4WaM2L":946892458024,"qUzyOtrjhQis":848485164135,"EqOOla1D3UiA":47262644520,"RI0YBCfHd2Zs":830483045195,"2N04rveqE5Jp":108472362618,"Kb7HCSZuCioq":391267477258,"GDpS8BSIgkor":818568139494,"QSwMmKbseWDI":817127535903,"4WhSozJPyXz5":789742483971,"1zOfwEKk65S6":295259458106,"Y1k2NNwAEXF8":190548517435,"dbA9a6QyS9h6":674015421800,"l3LXNmCzp0GY":29875082555,"X9KX2t72kXKy":697085994021,"AdfjSXZQI8Vy":392630042087,"07RRZ39rfnY0":492691702915,"IVqs7ymobzIM":272454546754,"buZNkY8F12Xm":604382584269,"cyWctWrnvOC3":788397572674,"PgpcCywZGdm9":465117123581,"YAahbJQ29r9x":144882189920,"rhi3ELbgFZsX":42494297417,"zlqTn6RXdpNW":817275985197,"nV8Myl2uc9yM":841201093098,"7G3deSMsEpss":396299608739,"xR31HbeTN5do":405476743782,"FWTEfQ7kTLLl":28598301935,"Rz02G9KD0saC":991900770574,"zhTTYv7mXd0n":881996040234,"4MXb3ZqMLh2x":834808989611,"SaW6xSbRODHn":931057526619,"1VOH1vyo6dVb":275316141553,"9fRk1Mw5oksM":302734128823,"a7NLHgyUSf6P":463689158985,"vdctvF0f2ioz":638211308458,"JuD1nLzTWlOF":506166898411,"3WWpeG5GIFb0":296129833671,"aHozn4euUXGv":505787439962,"SyNi2ZCmgfSh":301265451337,"CT8u78IxkQBE":938838775317,"tMc6cuWsimm2":374282898881,"XHUTZP2GMSLc":794430898007,"pqEe2bUIb7oM":401731748483,"uuUDN2ofobB2":772148630317,"NrbVkU9LfkCj":699419188348,"pN29RPhyNCkp":87134561622,"jrvPtX1gMbeF":361479931676,"25x4CL1MLwZS":726372566468,"pirFGeOlYhCB":67729108011,"zeT4qwhfA7cU":618604915995,"KVSfwLTl0K2d":111456993363,"FZm4rYtMPEr5":882014998000,"cKnuI46u7kIW":926549931435,"D1B1apvCxMur":685882769694,"vX9RGnYxVbID":187286964349,"Jgje5yDSS0A5":968590008809,"7ANLaDJEx2j3":398700579306,"gbeenuWEoUGs":550974097965,"8H1BX83fXiHa":387891122134,"l1FX0LLQ8k9Y":460266973458,"wNjzi3zCPcLl":661810517536,"S7MMdYC6sI6W":911092178399,"HNS3xedEvp5d":134521357698,"4v6Qbg9AEyMi":264152893801,"jQKcbdodispB":2651953011,"19dsSu8zUsdT":991501784362,"m7FnieXqI4EY":307064407117,"77mceLuY1aLj":358166377346,"V4LgVsiXAs0n":425980949639,"ntvyLFAiGdKu":513702138481,"Vr1K3NrvEwRV":80737151460,"jq10yDKyq6H7":313713171864,"PmCWEt4auJvf":469753673851,"hMN52mXQzlVA":734046127713,"35VauZU6wUoq":887650765370,"wsYQG2YYWBbw":907459068540,"dNPRysjL8uOH":588766152341,"j5ftrxzioMHe":341146521605,"0pyVX2U15J2R":112819816065,"oFP7WKzv5Lq5":145113339713,"tX5brteHH9TH":110475213578,"7Zk3S5qq6NWk":606614508760,"BlqeAg2RM3Tb":97672737389,"7DLDqXEGLlx8":837636763141,"2eGmu1ewzg3r":187868288765,"LjoVkKPWxDEl":583253671141,"NBRM1Qu676Fd":567144007759,"MboMc5JaRloP":288970092295,"VE5q98whs1qD":683313216644,"BJNMA4Aalk7I":148662596403,"C20qWOeMQdmt":259499420494,"exM06zdfDVlk":259957737067,"OsdICy4i4c4l":657996463008,"ug7Apc3wSZsa":79015541887,"Jckr7ZtNEgjw":718782163522,"PGmEbhKICFeG":427547477783,"tUKjqWeyZyvh":875009854397,"5IKH1wmOtow5":335404271035,"fyyCxNumSgCs":181046075793,"lt5dQcdhlVXE":904066195612,"ohEcuB6mJleh":501328321626,"3YvznSqyeQhK":503587160490,"hyPltjw3hvfd":324470135207,"WiAcevY7lKSw":464409205397,"QTFNEnhlfkSx":776197390996,"cvElYzKO2vsg":406422229535,"rnt2Fyd5YYlV":136960165684,"Ell2zBZPNmB4":702644665859,"4tziekLKj8bC":989500244508,"2jnMGW5ZQxJW":79993723297,"9L9njgD2TUaQ":391101846049,"RCGD2WYXcIlV":674303236729,"1qQnJWNmduPu":34979233723,"XmjrWBR5Gb7D":42302774556,"UWjPH9caO0ex":950842740726,"HofB4DSMuw8n":861857413014,"RnERH0Gqfd8U":54378588464,"7CdN4pkUu4g3":976836869010,"YqOsGQtYVAuS":595157326490,"gNWw5eYW0w9D":159389815101,"lVZFdKiFFCzl":959817456766,"lOp6sLgvxPqg":6114462092,"boQmNZ6pkVvF":732274004242,"C98zolw62OWm":469769938606,"gdU5SfhFV3Qp":629419151084,"I1pksNB8kVVS":386929759193,"uGm4ZI6PEaDM":951415882593,"25tPsHWMXkq0":18488914873,"Hs1PeZcu2zG2":431235747155,"8MNNd2uWnTCB":65767431800,"9geazHhhtEdT":153792142001,"vrBRRLNc81x7":40221355263,"bfuGIVt7cu9r":327138859750,"sQwu6Kn7L5Ah":440345388567,"aVLUxxIZ3m2p":662150443000,"OqUiblfCTFTo":515580755507,"HP88WBMkOiq1":320014484859,"C0VJUMplvRGm":393566995982,"gGEtuUlNbzlG":290355321342,"Qtt024IGXehy":987607742175,"ovRDHgs9q2Oc":21036698128,"BhXWhkas8cIS":72565444676,"ZYs7GD0zJGI2":72053007484,"XM3cSnjH9vSe":286256043789,"Nbi5mvjChLep":950998177235,"siIVfgk0wNXo":379268372283,"kql4sYBINfgY":917580480138,"6y75qnV3u4N6":610456138002,"bb3aeeHFZ31R":147447524890,"uyoLJjk39vle":595632185310,"gaRi6OhNIOds":644290943111,"f8YIYqnWhGES":989486965400,"vdrQL2Ax0E18":649613881268,"9Q1drFmds5rC":409056825631,"j6L5xqnU956o":488936369131,"hPXFbsFPdq0E":970219880097,"0iTb3H9u9dDP":62659475970,"8r2ypxx8zOMn":199779299192,"7GM6C4uBMUmO":16491020356,"9jOqb3U5lZTN":74603729510,"aBDYMCNiZJ5X":252034995777,"WJvAVWm1yvNH":844299052491,"tR6R8FD07Sgk":394181280636,"BlqUM01V8MSf":738756452558,"4w5CxF9slthx":929592217957,"gO24Nn2qsroW":65511285118,"OxvIpy3Eb4Kc":821471611787,"gF7jXmDq1MVQ":545298849142,"tViBYA2YESuC":423700398600,"07H8CdnQazGy":229378381955,"3s1sjbFZY2LZ":21594618003,"9ySK3hNiVj5O":221982630491,"Z5rZZwwFmsJS":309040679308,"YIalJBT6rQCi":199461155772,"Y4U5kFyiVZ1n":693490658461,"9OOqyWVtqw8U":758539085272,"uckfDQ3G8Hjc":387911926874,"lO8zM5DNLtrf":404162212366,"W1aEK9WsjGCg":944168532621,"92zdLRbgl20C":767824751706,"eLj9c6UUk9g6":644230917119,"oPCAsFDcAUVd":594948952409,"X5senXkuhLqN":634138358250,"7wlqEAoLhLuT":98144286156,"Rs2trmrCjn2e":100609403442,"aSiQdytZ54ms":35095408485,"2k0BrotP89DA":566383905694,"7b0K1A8SpwLp":301857366442,"zpIUn8hDwSuL":603726460702,"i0OYEaieXYJM":956031561569,"lhu34rOcoF73":405577645816,"bQvgYUYYwVHP":664796842042,"5EmX0RmVTo1a":298237154064,"ULAFyqq08y6M":733561678444,"dcswAKKDpU9g":488362549227,"WWO3XyyetQC8":566625300701,"I0vUksTwG7PL":66850873549,"8fAzAjR06d1K":618806492516,"EtIAMImX05xO":598663177281,"9aVCaqB2HJCE":389199955766,"8SN3Iey1IIjZ":392416346654,"4APRNTPSUIZZ":689249346068,"1so96956XVou":197006897131,"94j1Et9jODvD":135579791594,"tHVPK4dQIw70":152713233407,"yofgb07FrEE2":314038487901,"Or6XVX8i0djc":981493904007,"UdM89sBLYJFn":802375169889,"6GPLoEwCGIyw":845944969945,"ukzbCEvLFGNr":484213304503,"ZY5sYFIkyI3T":217219626770,"4dNzKTrHu8aQ":674142800651,"kPkc2ca7ggSS":394700313128,"EW4GoIzSpLGh":610312930017,"kw6iUn8J8twa":471796820043,"srS4gmTiHB35":766262079025,"VDIGJ2yrMtVF":170162936940,"2JhkI2IaKSmD":714781574106,"zjQeYD59y1eh":734789799269,"aHlXmf7Y7xYg":419468592734,"JaUKV9LXGZAn":85030420668,"r0SPdk0qM3PG":885391541328,"e3bp1ktPBSZ4":330530381833,"SLRV8BbVzVDH":172745038746,"crWmDIfDehxN":313595835819,"r1grxEvSOy92":956170945771,"KzcBiE3l9NAC":777789145718,"9q9CeuQKLuYV":769252468257,"YP3AxLa8vcRv":319062519599,"735PFPokQazh":868039118309,"tKSSjPSYrIRI":774505936484,"QX0reHBL107G":737088898189,"5McqP8vVHFdH":567388359244,"rRkx7vNOv3x8":882460791945,"l4KHqWlg27Hw":767955031291,"QBUilBD8uzTv":889632093031,"s3ZVQe3TBUsB":105137306931,"vmdPoh0B8sMH":571390631931,"iGMRftsgjT0t":138614212945,"EYuBDrTaZyNn":436368095797,"qZgGxVE9dDUM":507227515613,"t55kWf0SsT4I":703970694230,"PLo06xRajQZc":492789891680,"T008p06gHejA":404709094696,"sJqFQxxlxFpn":251619071504,"Y2spyP7FKMnL":272944989338,"O7zI8zfuDYhu":878739386767,"HR5r4S3lCPtL":270348697653,"JDqIzESA1Z8A":779830305288,"4KsERfVuzl5q":721327859200,"zRd53zf6TZWC":295411912175,"IvVcqzIN1lah":696512102981,"j3IvqOyvn3z3":241965242662,"3DX8zctrAkwI":419836789119,"CERJnx2GTt74":162876518239,"JFl9cTiDie8G":927475834112,"xVSCNiLeeGjj":199050091653,"47c96yY5xgdD":674558418123,"HlAa4TaHl4Uz":320918210935,"BgEyLFhfleka":718143945586,"wEqpTFBspXTt":890378307522,"2ipHVld2a7dS":7054473412,"5uARivKsW0tB":115731346936,"peeJBXXyji78":110941647121,"Hz8n9FoSqQ5Z":483026000646,"59UX2RooOyR1":227306150843,"WjL6BCuN5lh7":675497225435,"u46tpTaPetHX":564754381046,"227Jhm02qcsv":870650144339,"7ddE2FyeFkKb":243052261931,"vLI7Zay448mB":687529997324,"GsB1FHOoxebw":296136784571,"60z9SmR4jQiT":255124111679,"rUm6tEsgRzYT":416077945594,"nOJB8GJz3Oae":331006786099,"Y0896CJjGXKz":623433085984,"XW0BklTDdZL8":740033582134,"Blvtotzruqlt":669395445938,"MzFeXkqYN1Kd":646384041839,"3lJSvnZiqG4Y":110507993525,"IKX2nnSjJhW6":154053840439,"kHJ0RGM0WEx6":801841533903,"I3okZuKNWyF8":4569322823,"sGyPbwtMSlmL":421054066928,"Xspx4AjolfNX":678922632875,"kEav9UnSTcSY":447550094628,"akwGmoVFHMLN":574544300698,"u0MqVsg9zEmU":55481882383,"JdskyYR8llW9":452253706653,"lZtMbcRlOfjk":577551511691,"XjThNj7aNZWX":825274164789,"zaMU2YEPyF2X":131454113213,"AogTqcabN1Z9":130195259284,"TFuk6t9719kL":846656414124,"2h3bQN5iO9SU":604518532549,"yYSA8Q53KnsN":752923361701,"nXB6TB8lVt1P":427156872565,"gyXyy81k735u":192382657929,"gG53CPXx7Wlm":80355220446,"8jHhhjK7V116":538497206129,"53ykYcPVuPJz":945738704651,"GbuoZdBScndw":103171547055,"GUWi6UtzG8Rn":240561598523,"haipRI8Ayima":822119059604,"kxIR7OZnJiib":796236532842,"VyQ0VmOGquLk":945410706845,"CommUrTTSV5o":144793603438,"1DaAxwY5LVDS":118168484938,"YUIrSOhkJ4DK":944658133388,"PqpBXlAWMBrn":386685852399,"PG1iJmy2uy7W":770079176944,"OewpweepvUx7":156141891532,"lfugNGQVVrKC":157014772597,"ia1Z6FL9TWU6":883048137881,"EMWHaSHpWfCG":670385109734,"sNcEkPVb1z4S":716603320177,"p9N6Urqeoqe7":396075800917,"NEwN01Mljhwv":952945660188,"HebgK8TSCizs":583625555349,"83XZUMPhiFMq":238962121522,"a6TnhNeXLjzA":139612885501,"ghnZ8AeIbRp3":40705916627,"n5CXJG50ILjJ":716553747215,"hXuAIY3eicg8":244091023776,"OBhYErTXfYz3":776679568199,"TAy00oQGVspC":828881920702,"DSscqHOVugJ2":637468836435,"8RlYScXXi2fa":88528711256,"meYK9YQvZcer":248657700905,"1Mm0Qsq4tvnv":473570845544,"EjenRlsT6gkx":711658516252,"VBh3PPWGZqW2":35992096158,"KAIkaZgXAw5z":629693244730,"bZznxWvGxTq0":141162123693,"S9f8GuCdSouB":981684976021,"YIJ6NPyr9CBQ":243079622380,"UAm2ovPRFiWt":936670701580,"cenQ6hZM5xqp":360174819440,"CSQd3VfiqHK5":781298987478,"Lo5KxXiJdIIm":565751248566,"g5iHFnrCGb8D":111251680367,"hsrZ1uHDQiNe":66810935563,"Q5ImjzVIAOon":758578128011,"aS5lJk9d4gA4":136787768143,"BEWki5MuQCc6":466188275864,"Xc0qmDro6uvs":294100822631,"snk0x3ank8jb":591229827645,"RhlhqlPGUAdF":912111782114,"Hcjk7ujSYS7v":13856571812,"fcXHnt9YnIVE":7132067004,"DcaiFoPCRzHM":369260194506,"3XDRk6GEKouu":469325990375,"bfCKA9LwlVUg":174487044332,"o0o0KEeAag4A":863219122573,"qoBV87a4kWUw":80592443219,"LKTjf50j4YUf":278867200901,"GsTh53NxH8sy":485577657215,"DnQkxATzgsjT":750763763341,"q2Q2M3CSe5if":492783194394,"ucP4wkzMl682":407226369701,"suw8Ar9Pqu8u":535110242490,"jIbnGnFlxYLm":674822337534,"2TMg4MqiD6Xp":947344292780,"Zcp3PrhBOk5n":209075195469,"MmqXkfTWyvbq":873529831249,"iAVDyPa6s1Cj":562619965570,"KdIKlr2sPR8n":737781791394,"5apfTEadkuT9":597621796194,"jq78xgCAwGgz":937993757028,"gGiN2CoDie0z":906269451508,"M4WaSfmvWOaj":401989936586,"ZNhy39JAbWJi":11418886132,"q7vXYcNBOmbU":946454245581,"HzmtTkTOJ1zS":910460752638,"Eke8LBcV5X9J":855079002081,"5eYRfWABoAI4":409967343219,"kqlNC9ZX9ycI":475415615302,"GCBDmxPCvzOS":737781471339,"q5Qs0feTBaqa":63701952086,"qvJFpMkwWF8p":322031462196,"RPgJjxAQUMmw":271760435982,"ezJ9Rw24g7cr":971395376761,"kLq0gisEyjOZ":675911054658,"8ljgbL94qvm0":879720915131,"DW20akgN5GYy":128426202180,"G9EUcPwLILNZ":667252973255,"ReR7x4UicMQ5":323942614938,"vTbvn2b6uk28":396336560431,"MvfN1et52bFy":589391378985,"CV7jdrwK4Oii":168367606440,"SsYRXyp99fU5":987164634913,"dZny8BsLLn6v":239016977559,"zu1iIBnwUeS9":666519594165,"8YXk109DyLbz":535691312326,"fGRKW51NPzza":704102014391,"CQTMZCZpgZVz":797942956282,"dxm8luVu2WXV":669188313107,"HvPfXowxwGIe":369659409920,"RBTtVzgszMMg":997252691333,"qJWhxp63A2gm":881679870121,"f80xvFIl4MVf":997755113866,"I4CRC7Vs7X7P":265712196281,"4TUkKbriUSPx":143711008341,"hXw3RsbP9Nna":463701155061,"3tLudxHi3vM4":510441782606,"tpwCEpBiXbMs":87679093716,"sI4InTUcf3jF":321946501771,"IaYUopx7M4qM":87369480154,"GSdg0QLx2GN4":640515527279,"ptUaL1KzOyNH":903508635760,"Q47EUkSYyiWA":526515174169,"6EyrA6igyxnV":209767486605,"BR4ToWOzDDv9":439711584684,"yBaqnI9Eh8zg":182823262266,"cxf1CJ4mIfFM":492482680487,"vedRJTS7e008":740577255860,"dJ1N3FcQO2cX":437329463541,"BTNRZQvRxuDi":680248698521,"TubwC5ztCTAc":719965286081,"sIIWnfJYQCci":535140049585,"tnqoaFtcfDmS":298265217494,"6ecEVnVYmxyC":753740853300,"LBmjfGxoBXHH":652020502387,"zPbEeQonVFPZ":841779181770,"qEq5OmQozbgl":618977922699,"cJzDknOcA22w":124386823848,"lni95sOYP5q3":686776672638,"eRgtAZHxQUjl":772165847886,"Q66HqMEn9AVY":742247257440,"AQijZ0GjRy5n":65991749915,"caGVfsbxzprP":316151021304,"OsyYMLVTJZpW":202463279983,"n43ektEfQ0wo":635156726739,"yWzuwTAj17n2":632157199606,"uZ36MAlSU621":842235444997,"0w6jUuLKbqxm":651910804628,"eUbMOgOJCI0o":695491308268,"J0vbQKF7OlPH":121918524901,"6e6nWJKuE9AH":262107961028,"aYkc4iGGoX9q":404637547559,"hb0YvOmjFGW2":903469425925,"xsEtS7FMJZcQ":341716438114,"JJvesuiSQ67s":240703178212,"Q5sX1psEobbh":447441079143,"2yWhvJPAzyFp":502383791392,"oSgRa5t3kDYZ":934109233010,"lohS0hyWx3AV":206122062757,"kigBc7crl0tD":435928971342,"TCws9DEUlyPF":277419907179,"eY1KPCcizUsr":553839513717,"MG93XsxO89IR":297169009567,"o9WaCwRV0Agx":207219806446,"n1xIcY9OhAKd":230010984414,"Bu1xlMuDYyPc":563016927882,"YHCqr3EuBLlv":468381055519,"ANFcaP9UmI6e":373822990867,"jekeyQPZB4Mn":123603300076,"Zqzs1T3hRpdf":78572133208,"QbXRMGoBjtPw":988777398357,"7oCHiFfNi0Bh":407623517266,"pf9LToKtk8ws":219431070926,"zEwYdog9Mv60":641150650678,"Ooby8ujBxBTx":312408926619,"VxwQkY5yghny":268543551199,"wWQ0vlb89RCL":505985805374,"9aCSLpCkLzGt":50945227529,"SkDX735bHU6p":892339202543,"urpTJ70L1bIQ":574258933270,"5wZkyWe4w2GY":403613468376,"4MtoPpKR4dkq":433562663241,"siLHj5CMN28H":501801682331,"0RSirXuxX9nc":694946001972,"sL11EffEGbIv":341283662992,"OgcP7abpg2YH":670962600091,"pyqLRs3zJrh0":28827910437,"BW5H1E0FnCyv":326824202105,"IqsZmKZyq79V":789013168149,"Omaxxt4Drvu3":874344046148,"ZRnpaZDSvgLL":632855406576,"WnyO1e7ctoYs":212725611763,"SznK5JK7Q6Iw":83111745222,"FKQtPTomtBs7":506104854050,"hjNFMW2xdldp":376535174943,"OshHXVNfTzpo":434416256882,"Ih9TD0GXwW11":541754396626,"sQ6xZckCXUaS":151589301842,"qyI8YTSa5a6d":783154623398,"2mji50DjlnkQ":454931008462,"sH5glSfP4hwy":97806925381,"DOW9v3xdCGQv":626235063158,"3AFXba4gF1x4":435442846981,"I9wurwF1SPCm":625896351672,"uVhMT12kFhDP":516631620471,"Q3hSyUpPTyqp":89143853274,"1FX58wQZpnwe":645540519704,"BTTAwpE3wezh":585121509428,"24TCdDkcR2uB":138265525693,"FTISGeSN6j5K":237177994505,"a36T1yZ7jPGl":480271853363,"ZXQDflLFjn4a":223472842280,"8Fs3QKHi1Nxi":876337828438,"l2ZSql6C5eEq":948081393121,"wEB1Ycp9i8uZ":49783765683,"Trb0kWU7of7m":8695375460,"1QOAtAEovL61":170572446341,"WJ7DT3RtYLaB":57374603324,"rX2m6sEMyf6k":98823433227,"BrF34HBjaccL":229191686074,"VTHwFgCn7xJd":484031949263,"V13qT5EjitDR":484687764346,"m5fPzgWY1J8w":689435346175,"llLAehaSjNS8":792977973798,"8HF8iHfiMQuL":360545375628,"eJLgxYmW8rCk":526574118335,"EOSSL2kBl7cA":484444561255,"wvtno29rHTrR":687780832631,"CjVDyYBGub13":19953689293,"umIBGrrVO9Yj":685642554439,"m4cpxzdZHPxt":626636114008,"MEpznngiqgh8":775968155405,"TMODSbsfVUge":739007234592,"qbXuCbPi3nfi":92694318918,"aPUatco7iqVQ":105715802990,"YurOkC6hKluG":619609763525,"0evwx7JQyo8a":408513592395,"DMCMjRHOmsAj":291350474249,"8U4u8QKQp6aC":977460061831,"uGeWBBT0oJqY":47004035511,"TAJMxYC326wN":551748376835,"8PX7MziKfO4O":139519459087,"d5rf8pVCIgC0":448430248048,"X2QyQVEb1Zn0":547839199944,"kWpAGaLEhcgS":522952593879,"6eoTsKwbn7LZ":376571696788,"KLLBFFqvPydh":109268378531,"evYaYGN7BtbU":155941062165,"gSIgS1rml1FH":175394951034,"5IUVyMKbWpC7":352256372672,"UXdQeIQg5PVH":539526707422,"7Bbpj4BIGED5":661095377232,"Zfyryc4RB2lr":271197632854,"EqTNhZ1nkK1P":947979333904,"ASFBgCdBIOhm":957174064795,"VuTuOWLJyGHU":296823253732,"gH1AbHEoGk0A":544660948643,"1atMrtS86pOi":201101289078,"v7kwuD0T9Yok":238882860321,"PraAuwN96824":256773860774,"zCJwm39k6rBP":248983799376,"z7W75YaZTQnM":817787041145,"iH9Cl1jYBWww":176117959754,"Jgv3HDBQ716a":416848003529,"gASPbOTcNBAJ":292280153391,"9dKGDOxr926s":925751424170,"BYEq8IclrWD2":167910127197,"Lr6X6btu7Gie":468562503092,"ObKJPrT437WG":689936008113,"YeBMC9FPlLkT":4641261866,"Yljje2WNu8O1":328117541254,"TV8sUchxHlOi":74594908943,"9IiYkKfLpGWW":358302706616,"YDDAT6KeIXAR":454961975258,"AJsl57KR83R4":380524121765,"au5vU0Cg4R6a":670535007737,"puwGihCpH76p":354652468916,"ubPSXKQ9Gsgc":636239495137,"VSC2TzFy4JLb":883033352778,"C11xfdyzwQpW":417309321394,"ffoq5rBZGboy":342706449522,"sHt96Zb14KuC":891708679768,"sosAZi99TwZp":440076180741,"iavsEZhLTS0e":914570619505,"FE6RWkIfb4M5":975117123000,"hw4Nj1WvyOhm":909537052491,"Ccd8Lad9RvRu":29646763018,"MFt4YOTBOwzl":374325939846,"whrn9y8EqMod":500042411050,"WGMIvcoLjINF":700151597872,"cLRC9dVcwLJa":666139402586,"P96fGtk9XXyf":819843337406,"fgAA7xqdvw15":291495577135,"qkfg2uysdEdr":334607781669,"XBs5p6d87DQd":102019947552,"7A95b2IRfppu":612763757594,"Pu6HRWDJRndJ":92048822138,"Q57hCigw0fOl":398052836747,"60gRGvJU4IAC":26960014065,"694jBfbYVwcq":535999842993,"r23Jfoh4PcqR":434079425394,"JFTGah3kL09v":706130629600,"pWLJNh9H4pte":747055159035,"JBlSwByn7AG2":870200661287,"N6gUAObmXUyT":250442147731,"rmL9yvBVTtsb":915949220174,"Acs7OTVynhA6":495131827964,"eC1Vfu5dBn4E":345429008010,"cwBzHVAd5WU1":862554838328,"zd0U8kCVhkDH":745827212007,"e6BA2fSazzOu":709848523904,"6CNc9QkC81A9":332700899414,"6ZpiTXIJZyj8":124933797804,"VtjjvjKyFxfL":180756645410,"x8AzuGXGPZyE":127280698763,"ISnX2gKMTusW":7131553621,"44yBU06NRtS0":174572166316,"cknD02JYQqIR":179138094890,"NvMMw4oiw3z7":154253589379,"qmP6fVYPdFT3":893311752775,"aCsdPwiPwkd8":166444961092,"YMBAy0nYEHI2":798663596129,"mPhA89gJFdim":277760372055,"Hn6FtGMHRBFx":310788562254,"HhFJE3Xyha5Q":31793824238,"lKKPhj9Ib2S2":721516972859,"c74ndgrAkQkV":847498922118,"k3nP2Oq4sXwn":500238012848,"lBLNJy1dEVKJ":413424922825,"GQf2yk4k5Agf":830218275609,"hwIitXacGwDz":267573340538,"KmlAhRemd35m":948408500293,"fGTPGQUc4Paq":479428503602,"HYvcKdxEoKEi":748621745598,"dIFWO0ypvMF6":14817244584,"sA7h0rwOCvV7":786023404868,"4T8iPFCc5Mus":295521131169,"WtYsoewSInAO":960975890522,"O5zTeLT6MvoA":430441192741,"9wg4xMeCW0Dq":109540791099,"Z6bxf0JLvP2L":783846051318,"6x6WGg4ji1PV":995028512226,"S9Ld9emYL7Uy":598584534048,"C6a0Zfvthtaj":478231461794,"PDrrPRmPkvh1":204829511957,"kjpvs0H9zh5D":103876463508,"mVSrtVBPYoe1":931503243943,"daPbwH1G7Zt1":186916902554,"XRD7ZKJ71FTz":786709293616,"sRsdRVNTYMVX":253014185428,"aAZD7tHHhkIl":114216535166,"cHbLsIiqIVEO":379838692126,"kBUZsVab0Qai":606588886765,"vG5nJZoIINVG":192921451128,"i8XrK8FwTfmV":537144438258,"0ONzHph7LFwW":207925773211,"MFweuaiLnwwz":822231235181,"Gu2RYVhOFByn":550079974532,"RfKmuy9OETFv":157444123195,"11jK8RG9GgF4":803113353109,"upGCEiSFBhFA":125757704266,"hzRQ4Rc2hy92":316535709545,"guqUi4zBZWbp":223154865780,"Bw3ftIOW0Z64":784495377744,"jf7eSyxuEpsV":259209478465,"Ac1ebda3Opfm":940165312891,"ts0iGyz4c6CG":374741945431,"dgzyZkWhEkQr":397338156143,"f8iArucZ8zqw":805689191118,"ViSh9CLybJ1K":124104411557,"slWYDcKEBzBr":676782184829,"aju5d3bvT9fH":816095142423,"kK5giozMEbQA":656389548686,"54hjaSxxRdBp":167908651806,"qjOIkR9MjWdJ":801104659805,"7OQVszQNaHn6":556284300710,"eMh626vET9AN":132844720697,"cfYeVyjN8q5R":826187362044,"30LWhxdaX52a":479805849285,"xJP0x9aS9g1I":104143525793,"JPpbYIt5GTKq":575592578900,"Mb3V4rJJWtwr":8597204207,"n2Iay5oAk8aP":828013116816,"rqJAX2jnH1Dh":680999968381,"zQoOWSc5UAqv":407956558676,"7IkoM9d9ubaF":451200418406,"iRSKU3403bFT":727615160993,"Grq0OpFIu55e":481395058293,"w12XqNw3YVpK":341848619571,"0avLLvXOXCmS":633133443104,"pNqe4x6eF8KX":442488099701,"ir2aiHUq4l8T":230400294449,"FjEKtwSqxS5w":448438833171,"Xy3eKMXToF9g":384727758251,"Su8CgeCVHPWC":845650748388,"iZJTCnLtEfpu":390864158891,"ZNxIU5P9vyEJ":664848684997,"13BtxrpycQHB":185887546388,"nYtCjmMc9l7H":69462559578,"n8xm6nVK7fF0":870409775783,"YActmnkFDkkf":675714871735,"5gTGNAiMstFx":272186513520,"7jripvcHCYEg":156402413896,"adVInkwrbm9K":617523362347,"TvCZe3cXcijt":852105132954,"jokPKMmR4kJZ":788460616660,"XcZZFifXoqZ7":963064190985,"902N8yxmiDAR":75044036615,"hOCzfPvpKlZY":497630834400,"XeyU5lZcJrV2":930730600065,"aD93hYt8KsNn":344648067100,"Bzzqtu1gAnzZ":403758209533,"d9SN3Cftnz1L":808860808444,"doZa1BwZaJbG":829678782810,"cc1xsMZFm7WS":220330757029,"ZyRs5EhlmUFJ":394772756265,"vOp3DrwxXvjG":327794006610,"fpVyIsiBSx0c":377640934238,"WBrRejziCtGu":374410158081,"En3MbGOClE1v":349564182750,"r2JMjxTfJpRN":400785520077,"NNM4lMpqb0hR":806132132880,"nRKidDnYub5g":242526058219,"qv9FKYBMCeNC":877100834660,"ZErqVTqNdM8C":519204338799,"GSucRdHl3bD7":57094073919,"A4oFzDkMkLeC":189345259500,"VEPUOQtHMcDY":492500159486,"zBPVm6l3pJ6n":889556780040,"xTLuzrKD0j4U":106915916322,"XtA9ZI7hG8BY":190595215531,"aIoENgviomx6":584728268483,"plh70Djg6e3D":481018701881,"rX1z1Ec4uB2M":254099094543,"b13dEHx6hXlk":244157269980,"214uLjRtKwPy":941938085040,"BRPyjN4pvTwa":786091560656,"eyGHKOCdvA7w":788252755655,"9cLCZGgBTnxT":6207552501,"EQ5S67XKJmuO":345410906558,"HpEX7DCtyl96":442834339401,"4IyDRxR1Ho51":148945921673,"OQEfajP6YIy7":348070893861,"7rXA6TAPyBzq":659539336851,"S946Z2CRtB7C":135214828228,"1nnxObBoqDS7":837812681182,"WNNyszIxYVmO":8533241599,"rBPlG6Zxevz2":504273301063,"2Iyml7oxJpou":867141976127,"Lds9vcsi4emb":298721441864,"CC1EZ9T2iqYi":214497558928,"q0HjWUeE6O4I":396379974770,"qetdvA5bL4Mb":320432604535,"yrNZjAHVpaHr":362386098245,"TMeNAUl4eLax":559513786409,"IMtGudE4ObIB":313550660077,"7o6cMe3jGKAK":785312259419,"ZcqmW8GaRkUg":257165382236,"NF6h8Ss6UIXJ":494989347528,"qYKDhOpD3k2G":677245752171,"qJIn7kNXo4AL":139639475471,"yTObnecbrbSd":51141694862,"jzRrLXEu0TAr":171509860714,"fu3H22cUPlUv":86406444007,"VbLmbYC79h7w":209312114142,"20sqC2TLNzDq":19781859247,"1cYhSIIVURUx":276668729328,"kf5pEIIjnKRG":499520993548,"HdKkhhlYiKWI":571115852357,"diwUmwMDoq6Y":747365971679,"EoC1M3zPAXOG":887802409811,"MvKnAqWL8YTC":352813752192,"mb1iXnqJ3oxU":280797144031,"MrA7ue9Bj6QE":698425840989,"HajXOC01lAFH":329680256225,"6WiKrjIekcRo":464665599473,"KZwsmj1G1gTp":256675956118,"GWL4FEdGC6qQ":10189306181,"Fc686Gy6WVQP":954350316102,"DshZYrivZGyD":834509589617,"I8ZH1YvZwgqG":253249750173,"D3aw109fVN8M":708951909033,"RFg6RUbE3kDe":839771510513,"mZGIgmoyGFWJ":184987962038,"2Foa2W8em8Q5":460838198244,"eF0KqFiCZxCZ":990682329674,"fZNrKAWywAqr":38358645775,"B2f0uPblFW8g":233285803818,"5dEUp99EkgS7":87573747365,"kVQp35y4LLpa":756363579433,"0KwtZrgnYiS2":137357643246,"fiTsVo6gkIRt":42174645873,"IXUWnnEPozrU":937503542178,"JGPatSV5v1jT":945809837188,"IulWhz3ZOlCU":552895226240,"DdxkCV45pcTY":12046512739,"qSLO8AL7DOMy":818530301607,"X4zr9bxj7aon":957253208462,"MltldG66vt3h":317618444184,"pVAqqgXSVAhZ":635005210846,"OqVay3LtxEby":561169265465,"JYsVFFDfUwvp":808704700356,"GiKi1sM5I8eP":622339796203,"3yujwdaSPbQs":969947371364,"B3c3UXoaL1t5":621977853424,"ELJHevhrRHJj":110836887294,"LjO3Mfg1MlYh":579562496884,"C83yXammTgyU":445761148152,"rZBPBPNtseVn":716290628910,"Z3ARFEh1Lh9r":197741083133,"uDmeHspjvYSb":601567860894,"pgWiMmuvc1nq":911402019042,"OKG8dTv33rbv":553380581852,"dIj7cOrYaG7V":755165654056,"oWC3Q3G7qkZ1":472449304986,"gaKfu0jQGr70":451410973432,"75lV2094Bn1r":159513892784,"MWWaRmW4e6bw":341980719697,"mDd21OmSa0Im":695068006281,"XYuB5OYaTIBz":561120684564,"AvzvLmo5cbRW":364822802278,"Fr1RUVMAD2Al":275903464454,"5t4GactdLncH":716964363353,"JXyUbdJIqTgH":316685303972,"UNtfpIIP4w0U":422274239003,"wI6MgkgMuAKD":481341194184,"Z6WD54FEjyaT":836230292046,"qDxYidifBeRh":142016511311,"JvgCpdCpTboB":680747488328,"Q9mJGGo3aatI":761020107485,"nBvFUhmJu9QT":864630721987,"SLVWlRDBSjNg":104032246169,"Jr5Czosvzl0u":885534851693,"AXIDgtSgxNDv":715347156007,"Fuwa4CNGAIk8":817492218278,"yZHzWO6KYs8o":163579153606,"BZcMMuUjPs8N":395617011584,"1G2ubkfEXyfT":801736854331,"hln5t6saWKiI":690001730658,"2sXaOFbJJ2SD":715355437268,"NiTQ5ZHZuj6f":669903218526,"ZKWGMUkp3hdI":911243343234,"ghGrRQfnjMvi":281138237183,"d5TgEdJM9gZu":926551762772,"MToWmErSyJjP":985682184647,"m1wHrA5NVhoi":248468226466,"LboVpHYbP9R5":79538718880,"TtsO2gGIPQVi":379278213798,"mv5tVE4nZqLq":426981726319,"6FN7PakQo0uE":26855342492,"KXTP9IK0zz4O":823948914033,"tVpCU00LtEH0":183839988304,"cPbZYUI2L0s0":191736061921,"3lyRtzfrcUH1":371840435564,"wbml2t5pzr4G":505562112768,"U713qutJu7JW":596844770466,"eqHljqyoWnSt":303385806429,"COLp8I5dDMSu":472210297666,"RhvSndbi4YcP":634648667640,"Y9VywrC8yhjC":925754427246,"qHrwnsZvyzjd":685719142378,"52AahPVRocPr":929200653660,"XnOx2vor1gzj":568591262442,"XeT6IJhPdcxM":550636650717,"DLMVPC325epp":853194168506,"IPYF2TvglrxC":407327601202,"r41HGiGjyIFk":61708209544,"lgsB05vhP9Y6":316605595635,"JPxh9WMwZ9fo":585885411874,"fNoyzT8WMhZE":117494312417,"URNBaRVA6ET1":786670063141,"1gMbCAliDhNE":19266782875,"ZlLFqNvfayqs":85268012760,"QMndz7bIePhC":123044496719,"8QTz6zdimL59":524739238176,"vDr2SUHMkSZq":891356679889,"ogHT9kQr7rqM":444024149589,"OdYa7ntQSw3m":418341516018,"tEwtaElyosvc":359880309454,"RihTC5ee29fK":535445544654,"0XbeExj3Rd4b":400926653919,"EALubFN4ASKU":564876509271,"mM7gFg3bQoNp":384214569066,"bPQDjukJTqB7":16935567763,"okhZT9Mfi9eU":280640869854,"pmRnN5KwlvP6":408290673589,"vQKqYXNvwPHe":983102579023,"14NPXt481RLJ":872704931690,"nX5OyxvrQxho":347024347312,"tsXMMlWfPjOK":80496759571,"FsS1iTOfIIP3":204034885216,"7cUnNZSqEi2u":265187435597,"yOrE3ExQV73o":521206976706,"ooD7LbCSU5NZ":683611275772,"WMsQ4ugsJcvS":766636907995,"ghTIrfE2SkCo":861038579730,"sKoIclpRIB5N":964331323311,"U51bal3Hgfqw":573308276975,"47AsKPjchjRi":700164249761,"UdogWm2dNusI":542811982622,"an93Kq1j7ETN":772053671941,"zNYJAhSuklHB":883526398261,"IPPhaDv0MXqb":384880507887,"YaLKKnP2ZLeO":50058705221,"wWaujgf4OGxO":732362285025,"qdw2zlaBx8Be":313720805445,"MpnI83i4Acpa":941244108073,"pfxahDZRKolh":143182864741,"kYESzszRpa6n":546908165253,"f7xqOjlXIzos":32093055214,"F60wWgSTycZt":986312496819,"GzLhbbHp8CrZ":155312972260,"U6FreqNavYCO":884361051196,"O9MBsJHh0yLW":614358068330,"9fo7JobMJAXc":667208741794,"1Gz0UTGUI2OE":429550085415,"U1mKQcwYUugA":320006649947,"MRFmuxaHcxLn":508129376891,"Jw13v3KxSuD6":143036164471,"SWxfLdGm7Wz5":972474825885,"WujMJsgwnNGh":775311167799,"d5SxoYbtQzCi":105691642679,"FikrgIf57V05":611012546777,"6Cw5XIHTYiMH":835458033940,"CjWOFXEpPWXI":192642761987,"HmYV3Wqziqlj":914665056359,"tr1o7QEgGTsG":248695720422,"pVaNC7NKTCIw":984379212037,"I9D7I8WWXCSF":737917389102,"M4SwmMz2gDn4":289470784279,"iocc1fvYd3cV":48773608073,"YJR2SG9KCaOb":843918961615,"KkICaKljS6Vy":261770456870,"0TK7jOmXkeEl":517622305470,"h69OOlr9Nwmn":755488715158,"ZzRGk676BPQY":679201919152,"PbT3SBZo8S4G":673569982620,"nbGWA0M2KDaR":45061252736,"MgA0RStsqNyP":780075224747,"qRBFHInhJLBY":259126045170,"cOUUkpsKz60X":807179077380,"pbkllKIhVbMH":611141916645,"jwWL9b9dQXTZ":716778486085,"f3mvuqdIOq2j":946111010710,"u75eDXIqOyGT":338805446043,"h79QhcGlRRHw":775832877468,"w8OYxt7BIhh5":520955441764,"9TsHQrv14Shn":874785459199,"yeOUhrdkC0Uf":106373094522,"S7EcWvSZOIV2":872040725226,"IHyiJf9uZWrF":50770862979,"YXxPVbnbluIg":840098371261,"trTqz2pN1GcV":188171418066,"hXq79ykwceys":263797299544,"Fc8hBNwCbByD":681623707068,"j6Gr5quhm3pc":415870485604,"Iy5RbHtAf0b9":913441354089,"xkHeYW62Q7Zw":512358203033,"RfNxFkSVrd3z":417924801285,"27ilZCPD5fuF":697367089573,"ciCnT6qqtulx":553453173866,"VtJbiW2NRyNU":487969519572,"yQH8MvlH8kBA":231629996654,"gKqDoUVaJkKV":165191020190,"TyXBUFoX5RJD":66505163196,"xuUa8dSvoAmb":35925498030,"jK0GwcCXj2rZ":749826272659,"D6cwBw98v9aX":696009707546,"JYWAhoLKk745":213838065073,"lliDO8bhY3Sh":367278134721,"8g7jOTR6TKYI":820034725395,"J48bIJdz5zGz":911506409522,"Z3maz4ug5hR1":43062304384,"H0O7IKo2hy5o":61817467152,"sZM0gEWPSH1v":702975498288,"ZEdADrENOdBy":955838021131,"aglBj9AdgHqL":986707986777,"4AYl94HxMiR1":197143599822,"iPVg4PQNjwUC":10254500399,"aYXTz7ZnTTIu":925388180176,"Hbv78ezpBLyz":94247563218,"blnpKifNEDu3":293906549328,"nFqATG6P7rRV":298394073205,"DZMRZ7Jh9WBh":587425536426,"Hn30x3oSyCCS":60633871993,"ygTyOAcivt1u":75533078878,"fxSp6X0Krl66":638693124572,"rE7zkilL5fJH":500549192733,"qXqnqM78Prcu":226206789815,"MZ6SVXe9BiQu":355168600493,"mtrgtdMKOkLr":617795566551,"Ocxj92Mjj7yY":743156847930,"acG6BzYdqkB9":330318772956,"wf68QL6umzBY":337029716328,"2jStyY3NPdTJ":818304206391,"YEvZ7UX0Hju9":783541959324,"5RKtpHcT9rIu":9928558465,"p8AjrE2SkueD":683862317861,"KKyRP938fkLQ":275244817950,"tPzxifmN4e09":832551415113,"t4GMC3Krbtgk":370028750386,"qf25D23wW5Ac":726778790063,"PNvo0MRf8ZIW":603129664535,"4luOcaH7f49o":785205642816,"AA6QSNidsOch":523068439598,"2e5ZVmlkj94i":383837328523,"04WAx0FtXvlp":990462104536,"afV2qMRwtaQ7":333770881444,"bhazJ3N7w6cA":633921990323,"c90GMHFGX662":380568437462,"xAB00Y7jfpff":999574052087,"KNVaZlEXOWUV":800030878552,"8gmsdT5xD5Qx":193350148899,"dAcJ8ftztmnn":962325900609,"QNg0wRuQufZJ":520354374150,"dnyOEuqGDFwV":790311815714,"kWd83MElv3oG":654290252121,"B8WOMcJ18hzl":769513553064,"gVPGdRLCvCaP":316092266610,"9YPIGmwGrsOX":908338105601,"Fe3UVfgLzVdd":272425618305,"O8Dxzo1FtWcm":983909788023,"fDLGhy0RmqyI":920178997134,"kixee7cRp0ym":264230623438,"E1YF3Anwtb4P":9681322391,"AaVWJQJW0NGD":728659770255,"cDqI89tYzH0X":87510818856,"VfQkHhvlUvFC":817514587761,"fUsFxQCUMXqw":530634458946,"oLPrLyQ06s8E":632242971305,"CEdfWdjpklGg":551976993170,"T0hQYHHnCQt2":934643259514,"XJo23TX5ZoiY":335749697533,"AUaBckMQS5zU":67226347547,"9FqZgyvE5zt2":45273507673,"ZcpO2skA8a2H":191262133824,"BPKCopoNfxBM":765850323345,"1TCqsbQ4KWcr":175465482121,"hOXs3wKkzeIq":268410244102,"FgeKKgFAYX9I":562783887347,"nFg6RvEA4EPo":24506651703,"jy8LixVbsw00":566847368679,"D3wtDM72MEb4":488452283475,"Zroe1NmMCZKq":134046728682,"Qmln4ER9sPqn":808991693491,"jHAFwBLynOoR":507571260443,"sUGeNUgjRHzM":675750530227,"WhPQUf1gfhjG":553266498024,"W69LEq7xUE5d":360921976978,"ly4Z9nKmW1jA":163160544581,"UnIdwthQ5xZX":190295130022,"Eeklt99DfBv6":66374148269,"hjNDIPw3FeMA":131855859725,"mQmPfg9NirDP":759011452903,"b9jkigRJJeD0":623412723539,"jCi5gfkCpQDT":778145632915,"jKQWcjbwikg4":558426255234,"mV7Es0Tn3NP4":473527350842,"QRZbL6kcNgTp":938052293292,"rTKfUabuSBhQ":757853163642,"eAtgqJckOWWf":51038358238,"ef6hBcnZyesE":662915805857,"wkm93wsecjgh":531390089061,"T24kjtwTk0SF":245424794162,"msTK8TT99Eij":391139786993,"NPWA3oq8UOXd":627021185406,"rXi538t3kaV0":966202583981,"xHb3TB7a5ERg":998350741350,"OESII8tIflIH":548034352235,"EOgoEt4IMrVN":671589237161,"khijphzYi9y7":80507890371,"OiiVUWmPrvUa":681166593537,"zr4d1guXCOOY":991869927827,"ILf2ruFLYhyx":656195088004,"StfSUdJORVgv":823896730128,"v5Kd7jx7wOhr":37831868525,"mRB0cEjhn0bx":832216396216,"lA5mQCWindyQ":961591360846,"00UOKL9amn3M":921321850819,"FtEbSwnBcFE7":556123048039,"ufWRpW5Y5R3s":409187700055,"6RQftlSRVQMg":653803984057,"hm0x3Mf66v6F":323581747739,"JbarNtKxxz2x":17100112128,"iSkB1isxo3yC":375871554400,"hPUVSUMTtWbt":903981622618,"MX6zKy1hJ6j3":445845095058,"5drgy8CrAZjr":106802045212,"oUGSU1OrmD3p":289534498174,"tHNUUCRetXJ3":100146576936,"SYBGSwiAMpin":742886663070,"8IW7ax0inFCA":816038768696,"FYEIEWwxYZDI":854658311703,"fWtdruPYdMvr":247910868256,"LqxKxhXkIDyw":422268281906,"P8COthLiLxH1":567244548878,"Nc2pgmGEvMhX":234055597588,"9qdTbzIjXaEL":988481124488,"zM3oQoxBPwEY":573980529696,"6VenZoaqCSRm":586788671754,"iJjJzcsLfEG6":350078704528,"TgXnZIaOMDJ4":452987497131,"v5zjQXYPWn8a":61653857600,"87ZesLqESCoq":926798121763,"8HJVKD8TSTUJ":55336770021,"7UN0x9oyG2iM":455953317013,"VxJNMzAiep9Q":512596479278,"ma1VFImUu2jK":468419428241,"Q7MgqL49Ck6Q":597769364056,"yEF93TlFhbt4":793025718761,"vN3jwwmjiExU":320168321325,"IK9ZOAqwD19L":44289759846,"Y24ZwYmnsCrB":599424455368,"sFsgPlKjtnDf":578835030997,"DnV3dnBSe7cG":369277331604,"lpkKrLFzRUn8":551477524258,"0GGFmVPh1yJR":582519644789,"pkX1jSwRr7ui":495815045468,"uoOKGhPbP9yJ":849579383672,"9xlHEdkwgGf8":130435063477,"kuoIK9GEAOhk":741824091469,"QKH7t4ZTBOTm":930557993110,"rcKOhwiLrhk3":824814279299,"yvg5URSNOopc":905720063786,"ohWYVGNfZynE":941966352299,"hzD7QGL31cFT":584025459600,"Qq7OmMIiAB9k":285524156359,"JQiRNlb2olK3":370826981824,"SghZsPCZwiWw":636442077302,"YPTX1ALT2vDf":186214204853,"QIxQlpydORK8":655900060858,"bDaQ3Sgrj5OT":362630963268,"rkCEh6rqIaSB":174418967441,"Bi2EctJzALZY":925616876283,"RLV2QYeeiyxO":448407025606,"dJTFG4pAiniB":914454923602,"BlqR4DRjTrv6":21581161682,"CbaafBmohMXM":625659951835,"EAD46AodYF0X":18946488276,"UccHluFkaSgP":795318897867,"zWSn8cNMFU9J":188395073119,"CtThNA1OXWiA":321921786519,"ygB0Ke1V5ndA":450820063580,"5I2e6fo441O1":194296273254,"gsJwDtSO0BQh":622551392548,"8ajp4eMJrk9A":192310231541,"8EAdmqzaygRE":357597119921,"kVCvHgfSPUTS":927807051842,"aofCtUeSOydu":751543589835,"sFShbXYcctOV":753358226837,"bYZsndmkqvGt":631018486539,"Dn2w0JXNONCW":363826200565,"VIRPgfYLcY2e":199124423979,"cEDkMDQjUvXr":902334174568,"eoCBMVIMYx9u":283440538554,"xR9Kj6bSQazS":922943401302,"yBJyAa3TwOcp":754266498632,"PTqdppxWbjFM":766547169537,"XPvropSxal8h":516119846933,"DoeDZlk7aiVX":649745860662,"uSm0kqBD9ni4":725353120369,"erUwiB07XyDT":406930310965,"bUEryTi40PLk":968145789198,"M2uzpcN8EDIT":415653605165,"IVX0idQpIH6T":580203037040,"ByNZGrxVerMI":966409962959,"XoSpYU0EgQhp":804221793027,"aXD5OJM79Bqy":957505815850,"q4EHKE0vUDRj":618841548911,"D8GKKYNqQOpf":603674999673,"Gbs0nnzsX2Bz":898425016013,"BjChauMh0273":507508320723,"OUsAHkn1nUlQ":239469875324,"BKHw0cBhyPPu":15594489863,"VvsMWMsYjkw0":8939687231,"unj9ccURNdTX":763035977334,"ItHtYM5FTWO6":801601381774,"MUONl1KW4fKY":320068566414,"fdRtqM8kpJZo":61342314401,"HavgPz56wBll":213483566141,"WYxkEE4trlJK":383568086922,"HsiJtWaHXwrc":748691686521,"AmDtl62X5iuI":143735997552,"zRlAXtXYUizz":737140836919,"xGuRRQBQ3q0H":348219309786,"HskKzGw0wMSy":610112494365,"UaQDoyB6fk3W":344428183882,"dOgt0AYbENZS":153797941640,"UOvMxkLbI3ia":88793073371,"FKtRTg3akMi7":31963560776,"n1w6QwKEHlNd":737604746335,"4UGBjdWcxWrP":23142351653,"sLEhFxxxy4gw":854721879612,"48cbBPd41dOb":825370460863,"K1OBwuO8FlUa":860060348498,"E4TEOCqU6IFi":856293290663,"i0DtLarRknla":472372450775,"umXsDq9m1g5k":160411938387,"mgicPImhJoiI":206464873972,"GKX4O0B1J5aZ":919209630980,"S3yTdDriu1QG":843736311303,"QG2JZiALkwuY":315130979676,"wzFZE6JCIzXj":21892006407,"WrCcqN6foKaI":576632443522,"8Xr2ltLTn0Dy":174933677709,"mK5FWeDGeh76":369949476088,"Dra2YiFUAiWs":261049021219,"p17d6KXCyhHD":893889616085,"AHWo22AdnmAm":331437483184,"fmDzyDUocgEk":566022224942,"UbkAw0f9dtvk":32832111754,"6hJXDcxhAICo":799154520771,"SJpeREv7iEhg":265644287440,"SmghHDZuzXTE":519257990052,"UdYCXr1SgcN8":869480919927,"u1RkDMQE22GT":777577563847,"Z80RBufx6xxo":20324450805,"XEvprfiG2ZgU":317672918742,"ChFztSSeFG1M":642818632721,"tTne2sDqGIGB":716705923500,"qSxlWeyCx8nM":90090260724,"RrMshRBgsKwO":189675776946,"B9F0dbW14RWY":276186919321,"3Vn2Sp9Ap4S2":897009275653,"4PJnmO7mpqu9":627088627888,"WG5ePWbKTLQm":994111791000,"h493RdmYq4q4":805198947160,"A9TK3tnbH6o0":932735780698,"MSK1kAO9sR8Y":624613186062,"oYieLYUzd6iW":756260935971,"qcxjTesE9XIp":332179965187,"eTEPaULarFiY":864989120612,"MGP0EcVDCcvt":502498100005,"0iiP4Hq7g91f":67500838820,"IPQNHU65evm1":912723466333,"wCjezw35Keya":576010608803,"eEx1if8oRaTd":750296516271,"TuGr6Xg7vKzI":602876473799,"XanibMDp33H3":708362130260,"WpEdISlRbthY":740482624500,"O96xsfY25JNR":726804516659,"BXpfNC99sKrQ":422087151011,"nUqloEiZp99B":298927635483,"LjaravjcUIQI":92185342340,"UUBRXUzMcrgS":747442033036,"4SfN0kji1iq6":762850939876,"2KgsUuAjf6oD":851486295929,"gjnIucFxgMUW":678706816501,"z6mYsXf55Yte":114325937256,"FxRyrzKIOzxh":845728283210,"bYbyjM7HFaLe":745669853467,"2hf2E5MQMM1C":91245098433,"eoYZwdbQIRBO":406956775188,"rdrmgNxO0nvE":780317121910,"rTkE7fIHvTPA":387574422712,"pY2bSqs61EBH":856378059912,"UztSzvjdCs2w":870107823092,"YjRhANmor2Nk":570260573306,"NoPyCJ4ZfmuU":914278528786,"VzFoqhXsiIzj":139528221330,"fzVG8ZykKk2o":421876573884,"ItCEv5XGBW5e":753687475024,"k9uUSIkV7s4J":208091315373,"6ospJGcfBtRG":525877881842,"Adzi5mj8SAET":994958735931,"OA91Zfv5zhpm":954864496086,"Pcox4OcCEN6J":189709058366,"Ji4rqmVcFYRw":837877071172,"HwygJLbSOuV1":526223238182,"XIM8kCqqHbC0":42577083244,"e692B79bkWJA":845439975498,"CYCBzIsE9dCI":59245350613,"GU6dhl5KavxI":898795540215,"WR7epx5guQV4":396299059193,"zUHXL1FIoxfj":195060865478,"7OQJ6M9NacVG":592439290497,"lnGpmzav8qLm":287995564493,"pD5MMpV2KMNb":120885128142,"v5IvXKV1JrGD":755044685079,"9eHlFOOn38vI":223179045433,"Bc8pisLTZRwT":601532949396,"DbLrWjcHfeBt":706488817386,"bkgKCsNO4frI":415736457728,"g5SCamuautZt":604977777776,"9RLYi4VvBrVm":718143355314,"3tj5lxUIgEGH":223819991222,"sM4eFSOeXmX4":310549630500,"d31KlJgviqDS":211315338022,"YK2JWOhPYLJ5":821630482677,"cyfi97Cb8gmZ":255232694170,"noWbxJTnXYQr":545990341935,"amgDmjGMc9Gf":263657012678,"3NXoGuYqlTiy":642504661686,"3dJYKZG6UNJY":849210766767,"XcK7RD39i0mP":643334875574,"pk98BXFl5vcF":528765880841,"qIQz4gyVg7FJ":874217033744,"bAWH6yn6SZJL":682352115456,"uH7XMg8u7QLn":94033436866,"LJBdfZR2LI5A":993254390662,"6mByg2993Zrj":413421502279,"o4IgqoAOKl4r":63574583877,"Inp6QYaDh2AZ":682752075249,"1E2sAeuwTUfd":325060095409,"jK3SLOW4YvE7":245567364767,"DEo2fgaep1RY":230528804269,"42cOeWc5DxPL":473460750014,"eLuCS6B6Hc1f":950593316231,"mDHpsSF88nKB":774017791321,"mz1CrY4wRpq6":431489180887,"vpky2zCp0Fvm":86198495918,"taLggYxVGqH8":960931614806,"eXOjPIzNLrXA":621476246002,"z2iOQOYLLHkb":480825450713,"lj8XnuOwxAIm":583922699074,"mLNL48CxgTVn":733874954711,"jN8RNzu2UsZ8":892379100412,"LamsDYys13Ml":583757366198,"FpUOXiiVd2ND":545508080771,"ExgDwxeQRcPE":346797130580,"VIUl3hTFyt0x":745716495028,"DMmjIcDXnyhv":279430596328,"AYV0RX3uHm9A":778518306891,"wIAd0Yeytr3h":382182580367,"vTOptikoWNqK":917665542080,"0hVtEOY7HXXS":957700987154,"cet4EZAiQS4H":721969096611,"wTPlrNGZhZh7":72493969920,"Y2NyEV75jtIb":57214945896,"upfEPNHVOwQT":378953661777,"p0OMYv4k29c0":589492202332,"DMiRyKf3ymcZ":755667277447,"WgZvmsnYUUKv":622877841496,"fm49EDEYLCAj":536113434928,"nkJvioee4g4y":341238591125,"rKNy13RZISfj":434446646840,"gxZxlsDvnKze":811519989323,"MRUTuNJVBNfz":867940833567,"LAJ0mooISQIY":573964739513,"YdiIzwRQd1WZ":887407320306,"hVVdpio1r96U":464451473801,"aW1tpA8eHlko":584249666430,"aD3U3IYzRXao":759345044530,"sJwKn7YwsI83":235422739861,"debFOUM0FVDX":189778798498,"VbIh1BKgqeCD":123476049160,"wAT7OSXhdEQV":423230904029,"lYTOd7ueOhaf":308740268836,"VRiguTCGX1xK":826481374193,"ZMRIXySgCkm3":20682930182,"AOuSXhm6dStZ":262657307717,"uZzioD5k3yi3":862811045731,"EuIoZKOfEs77":315326656572,"JKxCyBYzIrTI":533584071368,"ilWxFlLE9Uxd":887473623702,"uNESVGhI0xx4":780738182232,"buZGT6w5gaTn":702651253902,"ZW5MC3UcukRe":689008261729,"Sfxb4i1GOj1v":619060775292,"Yf0vDvdTnHq6":133010148313,"ZueJ0R502App":433167555107,"S1Vh7v5ND4FT":762250696662,"129NaAsPiqpD":363305594156,"fQ2zAFjtminS":191491353807,"FXfifTMXI8ec":199629444927,"B9BXDANNzPdG":788882545397,"bIQKhkuP8A9g":892808126752,"21b2iptj4rtq":180666686832,"ibl8G4DCMLKo":767163377131,"fpaThcB59Sbg":930853753611,"ag4Qm8G5pEIE":358997043197,"mapCnXGETVaO":887365348124,"O3VslVBXZmX3":229138579816,"coHDPGJRAeDA":520862882034,"mIekFnbJSx0M":634253605552,"vrZLE1UCl60l":561507380878,"7n8utAA3Jl39":888955756782,"x6YdQMAji4Ll":547053483801,"svQN1PV8l3IG":799535166630,"MO4zWIrfjeTc":345649713938,"YFIJQHDqAkSt":291118956431,"ENRwodv5pakO":82673230100,"rxbbvkUBLSgj":332334308122,"hz3yHhLajPZb":693303530501,"TU8ye88OFwzd":901708401299,"JzUrRfkeLfU7":409633884050,"sSY7gkEA0Nt3":288212365046,"2TOFGvT2WPRe":633719986414,"1O4NYDc4YvLQ":251506813098,"DO0opvFnc1Vp":734678168255,"vmPvMLX1AIbO":41813386660,"76xPh17c2Nwi":800645358931,"gaAUPgT5fZX7":734045179353,"pyxK7jMBxlUw":895113767221,"zNRobnZ2jRAq":176885555613,"9KJoF663WUQf":832150933271,"zZqJbYTGqwdn":109173221201,"Z8Z74TQmT8gA":600958344799,"6lkQMJXB5Ds0":862592347936,"On0yumFKHW87":578764352241,"MMtGP7daWJPo":727686244873,"twdgqiCkUdfS":510018809147,"RNVCQrTYxsea":376695668996,"Cina5ZTRkTA3":816116435508,"388a9N9vsHru":697057046831,"tNtOmuA1oei8":241766210451,"qfL7Tt8ZiCkn":173782285419,"9hrad98iPnHJ":697729897072,"0GIsQR0PGmbD":796767489428,"aUef4mbXh84y":659349511915,"eSo96AG2kXTX":228412784634,"uh2HGc8aH5zb":40439061661,"p3GmUCHsLzRs":161611013429,"NyXZVfjNRNjp":818328434408,"6cVgMzQGc5Vb":999428399544,"41TTeFVhklOs":864498550418,"DSk7iMWui82H":291969084719,"Vlujw6fhOu4d":273810211849,"QsDKkSpRyKd3":472730245224,"GK23smlc7h6T":405476896878,"XC8hk2jcv0ZL":328820070541,"AY5eo8FVfLKU":354718175157,"fpzVM97pLtEV":623730641088,"3gFGAcKLzFZz":779406561182,"dkP5nQhjo3WU":554834771124,"VBXCcQDYJRXR":826848421973,"vhI78rj8e6IW":807418976635,"GXdKW5xQ2rQX":78542450540,"2d2QXCwHnGlc":931697068932,"XISOVTOHmzPN":119767286057,"XZqcm1yCWzxA":611674199950,"RqyId31B4kEE":736744736466,"UX8Py2YswPjF":5911639032,"i7TxAAZQopJ1":447597565326,"wZT6OlcSyqng":439856404846,"sThcx3X2R3CL":409936479551,"V1AIMS3ee2b3":411737357151,"iqOC0FAlmViU":395399659071,"GE7v2SOcexbd":579568771342,"1h7xSY8hIHU8":903049093852,"X2vX2g0C0Y2y":480164695346,"RmdNfdaIawzN":564015974684,"INEvVNPOnzWQ":397845334645,"csxHOhX4M6Oi":670831973302,"GuwTFdP2RfAD":548977553492,"1FT6zs0BPiBS":282892786348,"RjW5nUMAXzoe":936803249265,"qXszqIaoJ8N3":800329742168,"itbBetA7ppfu":220747563425,"UAHSU2xvkPNI":495393673578,"CHSST96MlWqd":446122311160,"XWD85cgk1JKh":218901040644,"rvomc3f3dbsV":698442193572,"nnt6CoDpHEKB":467211401059,"nRr25DpJKNde":209247781744,"psOkB7RWqjRr":759734270934,"OWVJtukrGkJf":843177807925,"YUdyfjz4I3up":81360346407,"cNwOWDJgzQ68":430616886567,"ZqugGNZAmx4T":977934369756,"b1OUa83VxVnO":67246801527,"5U3NR9l5W8Vw":24564273694,"SP27AI5U4tfU":529538866700,"KDHpAeAdGb2Y":777115439194,"q8LVg9ZrsFRs":293981802433,"SoiQRx6f0jBn":686512938033,"CCyrp8aXVVwD":160181934919,"cjhqmZimg60e":937686353193,"mwTs6GgpcEca":250025552663,"rRURiJe2Os8W":57977371726,"t3VvVEkFFBWE":357245992934,"1bHhQDpxzwnm":108451678635,"Fs22DjWGuUrM":406314773533,"UkNHZ42ap8sx":42697710,"FhiOv4w4Vyq7":505495342463,"5sPAzoN3hTdq":552797633042,"CAcROIpV1OSA":347522795535,"S4DudUGBe3AW":471460515831,"XFmdt0t6btoP":698982990350,"B28et5wCiJg5":463191943873,"byWmRoPScvhu":427973349988,"l2qBYt1o3NPV":142660738338,"xMgkvkzLLEMM":515893485222,"XgqPezVPMvj9":18604442453,"ofEBw0Ln07g5":18208882266,"5HorNzXqaY3q":646433810528,"ns5mpGS3SeZb":617319497133,"XovrBQYYsyLv":616925688274,"zkHTMDfHd4s0":756188323876,"1l85juCLj2sa":553077719206,"Q212pqziLyvo":365906444859,"p309BMnR09bh":654964014249,"ccAOVrkFICpo":417741868438,"V1eD8H680CpK":650519322576,"WSOXMFaR5ZMQ":241718598618,"LVCrKIjXhoVu":781200873385,"O3XHDnv7gwci":101983046071,"hgTexP0QO5Mq":759065271003,"kdWZMJZ3B7qD":638793895605,"u5gFRfTT1mrl":229411381426,"1a0yXzX8IsIf":865220440335,"2XG0UlJ89YTb":579971695699,"eTDpjzJQmbwd":873124900559,"nxFFAvewPby6":642164392931,"r2hduNF3guhr":983145002238,"1lQStdaoCAms":98358271737,"A50sJicW5qZw":812869193819,"3W5G3rBbgTcE":208734464509,"tpBbu9uDaBHf":707925346709,"iTHrMGl2aoie":736035278185,"XNw3MujoePCe":371414013032,"gtEWnI7EcdDl":34921429814,"9cGlET3Mbhxl":967787257309,"rTvkSgKvljjv":559317441610,"y7EIIg2OKNKy":191828082264,"EDZRwvymNTt0":919051044311,"n238L5sOsJMP":600270654063,"rToGnnb2YLx9":112800737261,"z78C8Bl6LsUp":15405646521,"LViXUj4U4xtN":286381077143,"JmTh7NiXOSt3":327467727386,"87HXZklMg97m":398860879020,"a9YIBeUiKz1o":202298409609,"Kyt7o4qlcZzB":67373325267,"e4akG17lhNkt":304252975287,"iGvpoeC4D2WZ":604018182389,"GudDfeHu7PH0":863082319095,"2Jr0nEJU1pfk":5933179737,"QmqKNPy2ZNBg":985400529461,"SzTgIVyuyapR":884016395881,"EUr0Ne77PRmx":673724960716,"yKvRF7tFt69m":315809182625,"Cqb9peyPePFE":210594828878,"NsBOeeneH125":859345901175,"QZ4Efak1IMLk":252612823421,"dBXTuq1mn1xT":676238307719,"PGU0lCjB92AT":683980933422,"SQ067cKrgVFU":765423436676,"DbvRSMkZfCeP":591177525422,"BeLrQAZzWWrZ":180482824129,"56vvHtzUs8ud":17988532749,"Q07qvzDrLlhf":740675448465,"dVw3zPotZ7sD":223680727503,"s7rVvr46Sxmp":439915397254,"bVgdbvQ3Qypb":22955747435,"plmhX9mAixD7":309509708468,"1f703QByDVJ0":551239781700,"OkazAsRG7JNT":290238908207,"kphQhXC5rbkW":403901518531,"VUYm94K8kBQV":912647229606,"YAgtmsLVKvdd":30249580805,"TSuqIuaoYQ7D":182925681862,"C8zMU4YfYHJU":276199390160,"RFjc1U3v8ACB":273799529599,"n9LRAjYYCRjY":817746759715,"l19ebZ0g0yTq":408746911534,"a6hp7hMRvYZ0":343447837741,"m71elfpbyOEy":235131820984,"ajNgnabZiYy5":49721164224,"a9NfQYwvEFRl":826873020931,"kTqZNMiTz0fh":280868520906,"1ic6gJxs54dS":698752399830,"66ddvFd0AcbV":954269726350,"rEH1eUC8ETD8":120541502544,"PpIAm8to4rAD":781031933168,"lhKRTtdDu3dt":320741321161,"PbfZycwnzalf":615129873584,"LgyJsK8rczb1":295145009257,"qBz9OCsxMMvX":160402208765,"UFduSxkzdp6V":680977772364,"lst3Zxl7EyBY":257540979885,"qxGKPKLbMASn":831237359981,"i9PxXmYprn7r":160894838140,"2IpOpkystcdb":180343451549,"kFCaovf5Nupx":442446594631,"doc6CJry5Yz2":362128252858,"P53Ced4iD9Ow":555654282788,"EHCaZ3PpsRzs":209652987219,"gnf4zXypJcUc":229052651091,"nTLjOh3UD4Lx":552327883853,"A4uNkzqLbV6n":177490998962,"69RbgTjkvBmv":552922721528,"y6oNCTubVlkx":732675033485,"c7GHYax1eUnT":675428528695,"Cwl77B5nXBlL":820412242621,"0WnHPtYSlTyu":404148608331,"p5rKr1nF5MJr":826566058906,"AvPqUA9PcINO":422459010363,"qJJPVkLSZlp7":125107683573,"Hz1ij7vZpGyu":579196650730,"xXLRi2PA4SvP":278473538370,"9TxyABtejES6":718130382456,"LCcoSWdcKGMi":878648721086,"X7wxcU9Im1jf":661973810237,"ME2XWfeyvJ4C":596086058119,"ixJZXLOmdgFG":893325970446,"Ro767oXhxS7C":204661764029,"Y0LUqXLNIdDA":743708125364,"k5xs85h4JaRC":938621660746,"Q1X1iVJ8bK4U":864684896523,"QS78nzBuEbCj":960323831876,"uIMfw5MshLs8":704356616393,"yXke0r8zTkvu":416277693243,"qWpyzBFShWvZ":973501368724,"BvRgNwXeynxB":501141975873,"yfRLnTMifmOq":634022617328,"7sBPREj5EOKF":620022045801,"DYKeTXRCZAzr":353043071705,"9UBaWGvgR0uh":729304584452,"eMm4eRG5dan0":447547210295,"VMAuz5v0dCH3":713650344007,"HBLU0z7cDWiX":724692663985,"12vFYVcPrJWs":241513314398,"ADhDGAU2cRxs":97177833283,"e6eeCqexMCIe":176015294657,"w6RxUAexGdQk":471697331919,"OZuycXumHzFg":916404279834,"3RDSBuEYcssC":450275768645,"p1adQY8QykOy":104334134705,"41i9x2xZE3x2":617463164649,"5dMeanKFBGUx":295504892298,"vFryigD4IFZD":755509208057,"cjqA5PO0nRY7":689175536575,"g9UEAbn1VWGd":325159547113,"3qwP5VtVfjTs":606385332245,"DxZaq1Nycd0w":661659150042,"O9CX7gdpB9DW":949415189610,"uwqtzaz1vGJH":103932115928,"ravZa6bgCfpg":381357870384,"5cJKxSGYvyGI":541216053175,"LMJoEV1pT6FU":376361096194,"65vvmRDlszwz":204771404619,"A4Exn0uathBX":441445679140,"Uz2RGjlEWSr7":462915897588,"HPzhaUwlIeIs":572715424322,"C5Q5JF7Js5Kb":196984651426,"MVKivEqNM9z7":223229927979,"KALocSLIbSsB":692116932539,"941kZ9GZYObP":816773036916,"c6j0AEcBnbxM":4134451839,"5IMKN7hdtLwW":824607941909,"cwNFF35lLrEb":514881922943,"Wcm0FNS9sOip":942481251901,"AKNjCYetXUKB":854780575065,"i5YVfcWXsrYt":506521842810,"rg5UZhOYQ6Z0":482132554806,"9T8du58jtoE9":129238214224,"AAscR28tWyu3":417414839090,"p33tjMDxxS7G":16594494850,"OHO85NOhDLOv":125959509528,"ooePQlslfJvn":738474372353,"0yRKurGLoSQJ":41441841431,"ZpcNRf2EqMwD":760553326689,"zZISNUc0q2aj":830050730619,"7EIyvTwlUWue":86321587125,"wAQf7hcMp9Hg":997443710166,"8XCKcWjIaA1L":474447065049,"kqb0LmtfwYGX":893150482184,"ITUWwdFlSFc5":62265398143,"5N3H7ohuvuNr":870221774822,"Ifk6T1Hqn5Sc":479881576290,"MDgB428cOvzL":446222660559,"vb8RUpqJvmZb":841855461133,"l6n4omFXEVvg":699073323183,"gCYnzPDpPr1w":277720960449,"rgKldKr7BrYX":970845349522,"Vb6mSQNfLOBV":114525875222,"PdHioEpE8Gqn":866740277758,"gYx225WVBSOg":306095802962,"W1agSYjg7gWj":481795488323,"Tqy6ZeezWQwn":808735305592,"2orCa1I72nqY":764607574280,"mjzRfwR1FeoZ":501328846446,"xwSVBwoKlpDf":103316573853,"hAXJZvt3SD3H":850658738177,"CFtRF6wFuAgg":322171212714,"oKlpcwqlYq9y":19164770579,"CbjQXiZ3FAtM":172017087771,"GxUsDg093sP8":420518795306,"dfgbBquBdQ42":586481870582,"YtTXbYDyXiKC":980057172586,"W9lVOyl2cPS0":14001560537,"FiKsjBYOdE3Y":848987563855,"dX6Qknx0t8TO":854653592919,"oSiuCLA7NZRD":811509126171,"dmlz5LMlyE9m":344877057310,"XaoflVgNo3nJ":265746721933,"Ftv1X9xmUIhS":522445400950,"JuK0m0aCc9kg":300575008277,"rGINHFonuIwo":115316608497,"Z14vJrJVR9Mq":821389611726,"0FZz5he3zlyS":720600223889,"weLkqYmvM5x0":18035284154,"qiKst8SkOgTP":397437890575,"EfqHNZr8ixOa":158823010310,"7D7hZ7ghvAmQ":487233562508,"GoUCcHG2LMGK":896137808987,"u3UXk1kCVuI6":890821296254,"8vKHVY8Lg5pT":269954100697,"iu1Vbwsm6xJX":735555192634,"kCvuHwPDIAkH":627565889365,"tdFk8YnakrIT":891903136230,"DaSpB48somHU":719352151949,"5sKPyOyQIupu":512108466950,"54vSeKHHJv47":777234458760,"R4m6tzzelPG2":297599109834,"nNczn0DM9Z15":618031898820,"xhLwbd9LlWQs":919379228199,"r5NaLLEo8zRh":746173042676,"juoOw91IumAo":681694023573,"oPdSx1CLPHCn":340185596125,"JRxEoDofCqqU":329692423224,"gmq3IiC9rEfp":370697921751,"Ed6zaCz7jHM3":755976532835,"0qxw2CsoRL4N":926159909214,"CVaCznQqaabk":570694582917,"PNfv3YhMg8Ox":934787740877,"FNJrl0ceBdHE":26690982794,"Wd8v2fTL8i55":14743399701,"Tquc60ko5cum":188397498829,"eOZbBLMyjQpP":705982622606,"ded7vmp6zYl0":273421698808,"S5Pdf9sKgg3Z":601275673265,"nj0y1wskHziv":16706776587,"vVSZE1BoH36C":764231967343,"3oRxH86BBT7T":816391382831,"rg7jREfBSXPm":132678803530,"06qWIUJxn7Tu":120763260934,"8wKKc5hXiBzS":972689326690,"3ZIkW0Lo5n77":463692407865,"Y9NefYNaDj2y":429558860294,"pQXDXgqRB7ar":47234446999,"WpcwVPjAtuU5":953678283628,"0EELOOCDAqye":330418804508,"iOOxoipYdxTn":276797271729,"XiuYGU0HJBdK":650901301004,"MaLHKHf45ZsU":110233188057,"BhDSGLgbVwr7":496700790215,"hZJWux5lSYqZ":305863547454,"51AdNEkdTGvs":258948477842,"lt8bc2koFK2q":3949142839,"eO4xOX4jEEb1":173717297695,"rvjNa6mJQbqY":680829040383,"LKny6LCkL76A":402064440413,"Eq5kJk10IUvT":525199404862,"IAT9D8JbbD51":63804152711,"38PZjrgNEvnU":424165335403,"Casefwu7F1mf":415264131516,"jupOfr27V1ZN":539102766200,"jPwq5WF0upzS":908394697767,"pKS17ckpECdd":383026151942,"lZJOW4G9GNqh":95262288560,"MBi0CcvVinlG":321244923997,"7CzrSPKmyy9i":889265106859,"pbTDoDypN25Q":260065717719,"ii8b9XGNbyNT":656961454560,"OhxkfsQXPxXC":496138651047,"ZEEzNq5TYv4V":582064978299,"xqBm1j56MtNw":285436101678,"TRklYD0KwFpu":175014705758,"nS9n3f2LSs9Q":523092643189,"UuZkkG7DSV9H":233501993650,"WFb9Gta15DJm":25483555623,"KideCStNpmTV":71809736467,"iuMSnJkqcVwM":88197453625,"63i5xjV5L2MV":222233442466,"G9jgsSvRZHxI":462409839136,"gVDCktVik7jS":706580810682,"ek0MuAkU1Ft1":592120498462,"FFR3LJQQbUlt":743728141196,"87gJgm9LURMb":100906086786,"iZg1czG3djV3":888045930217,"TCu54V3PsoUg":609502971806,"onioCLepAwyI":748779457433,"47jdSxYFpRJR":260191810655,"Porzga4qWWDc":352595613469,"PmHMeWBXsSXh":468924166800,"Wwuo7nYUbGxh":254091239456,"W5Z7B4TE5eFV":253680824012,"VNTL9O8uxPCO":685739707490,"HueR9gBHjdQc":582310385869,"YhLBzj4xE83L":786463434999,"ItTF2jEuk2ht":332847343119,"BKkxDqgMCdVA":578137715380,"RsM6PE5AeayO":5625950021,"2j0z5cXveyGn":131108602177,"yE4iCaEzF5Er":649835297357,"3ioCHBTYhwym":239441931113,"Rbd46zcFVWth":6775286354,"on8nChIFJJGn":267251338391,"11hhZSQwx4p6":262171923578,"PjkeN810gjIn":450241484339,"awlgcuMXuXAe":104123899741,"AdcHlX92niWa":857758895373,"JBMtXKq1X1Fz":724044484514,"vkRqCbaDNPQD":524329795810,"Q3dHyO04PilQ":902888891082,"2Yzuo9MElNEV":280919335937,"1mDhbQmdYo3U":205713667351,"RHjvhXyHYYse":610489117600,"chSbLP5sbDae":896668127056,"X04Tr5py56Zu":758044981838,"UVAgOeOnWpMH":712898426194,"iKVNpsZUMTUF":172641436003,"jV1AphvY9IdR":14946064609,"P06zLCGGcH5S":346746935367,"5CqnfkoAbJ12":906337697045,"K7wJ0TdswUR2":626046138472,"Ofqe0dcZukwG":606237624483,"cTMWwbIgUP4a":173333230023,"oCVitWIzKjOv":425812006611,"pRXODDgkDrn0":885446249846,"rBy5N12HGbOz":610243742968,"vWFy8WIQ09N6":441153833811,"bDE4pie45sAT":221382914499,"bT76OPiOOTaN":66293245019,"vMzvpro18lXM":325835398661,"6z1bUWCx6bHJ":469850751488,"pSmf8BMOZiOO":422277627189,"ruddDsDYDNJO":245790086593,"nqnlCLAFtn7D":812563115690,"7HLw3EpzHKAS":622935656466,"SJWx60o0gYg7":276838907461,"9ALCeFttv2Qq":157598332833,"M3OR15zVLfDB":749950131799,"UCY9Jaw979B4":783453251143,"oSBi3LNEOA0L":193150458667,"pAvJMe0ZVuQs":377902703921,"FrthLyuPiBez":892837200755,"jVuqG5QyBFct":581330767019,"woEftVK2X2oa":866785028989,"71vATa7nL5Si":420762924551,"pH77lZEftaDA":682566450818,"apncP3moL25q":353881972906,"6dLDCQY7bEL6":821526505095,"KqZysUbEmUF7":356930621133,"v3wMSXBa8bin":686914645416,"4fHv8vrH0nCF":510792246525,"2oL9ATk7GZXW":368181989196,"1qXSDY78GdJ8":601270962309,"nxyg2xvY6fp1":550086831486,"nleAECi3WfVA":837597848977,"u4fF2VcVtLGK":762939216388,"MZDg3mRRKDDf":528182810634,"9KLO8bzBa4bv":899513158263,"gQR2t1yfq7hD":24773423189,"st1ECM5mUbuy":180483554709,"nE4g5ZG3NZTS":535906608053,"Hx68NbUPO4WK":300537279926,"9K5EvoNMHTlO":922014808146,"d69flqUAv4w6":929417414760,"TtTR31KvGOmx":396289145233,"9bW20o1qqZxj":351167154903,"YkWCLzueA3AD":122849303856,"ZsVmjsySOBIr":785738177665,"8CbKyu2WWys6":182632071201,"Snn3jFcjDI3m":11227534657,"lxsc4AyHWWZb":645294706505,"tZ4OkPdxNneD":987290028376,"BtiLLLWW5rJQ":87642838744,"vGlmnaJEUQgm":698536316648,"y7AXn6ajXPpX":358389572582,"1TpeInGfozSB":519335062129,"7kW9GpWLmehB":848144553842,"tY527GLJJ7CG":881716436818,"d6IfOpEJvKA1":141614020646,"87dx0kdCbL2D":638187056754,"Xsv1KeakCja4":19719237916,"UrAA62gM7N37":517057384546,"WHLsE2iqLD87":49451893573,"DzgD7JlkzQNA":543455517362,"n7cnOWxAf2bS":730461633999,"ovdHlYUqzzvc":859181384245,"50ptk0hwoj8J":225666490925,"mOO7g5Bh1LhD":588818899253,"LSz80vD8SDGU":678075271368,"YMTnBGj4Bmui":791892169315,"1NPisfUReFxf":825523215934,"uFSXJ0Lrfo7C":373798952409,"c7wqIhQGFgNf":904428486593,"TvYs7dJLeqOE":395890900602,"PnpmSD5JMkrS":742998097122,"Trkusi59vIel":153566175448,"8ZFceglcQK8y":152937947032,"YZBnSJi03CNX":571071009600,"RmmrhAypttWD":531325170416,"x29Tnon0P4TD":993442417596,"aqAZiJy9VUxx":324162551796,"TZNHQC4gkRzs":543437393275,"9tkZOkaAZzs7":569800720025,"m3SJEnuy2uOS":224332967627,"eQA4Gy1arGzl":8677876588,"LK6uDR27YSIP":997651634623,"uimPtdAZ1Wmg":637955748580,"0IJrk61neI48":988893452252,"5T3ZDnVWccUu":237414186443,"7MBgSS01EbN3":341151247445,"xao1BeuZmYBP":299419757464,"85HBkQoAdCYx":910620079132,"VPcyiuVoMW9L":816667822456,"CesKW8uxhOFR":472169045010,"OnHpBvbNjDan":191293855385,"KEfU4W6F3le4":280013384561,"dqkdp80qrRJw":272244047472,"BE1PWwBs9u0k":755978311073,"7tuDRJpW4TxY":110533830695,"KYprxlh81dqR":486343211508,"Xvzsz4EFEkSv":177461862891,"EePL4792PT11":449616704473,"Um3hpdlon8iy":392208823628,"Dovobw2dbloY":569443422254,"T5y9z317Aoaz":487926479750,"CRiqtZGfye55":552094071047,"B1J2NOzFzJMb":663405538272,"18HRU7Qta6hR":822280780712,"RP3q6X7RBzeV":442199571073,"ohAlviCYuyh4":549693228401,"VRC2zZXQMD6F":846370812451,"2oXqpOKZxv3L":955383446454,"MEYUxHegGZRC":583777768066,"TZSb7WPWYWmi":429032407143,"LslqV4zbXX60":329040861758,"tpDOmoFjpI05":26433799333,"CbT9Aq4l5W7Z":619990379212,"Z8g3usVGQqqe":734730366611,"43ye4YOXpTVd":715787044526,"yqjbJPWlEoGN":924482971697,"lYvVEn4FywUz":429241574388,"tpPzxcYDNabc":849461714585,"pRmtexk3ia79":143993560919,"IjpPnB6fuLWv":304130931343,"BGwDNpllLAj9":180994246727,"JoiBhokA2hcI":700758871913,"8WFlRcjwyEOz":782833016874,"JGBpkGYRWh23":716804790925,"yL8fJS5fhoCZ":381406662191,"Enfea44Y14Xk":334337225231,"pHwdS6xSb9uR":221174380910,"YerWmsMC9xhU":308673494592,"tglO9CKQN9DF":981629827538,"AqXCjQ4ic3tn":219214752236,"ws6KOvj7O1Pf":632249100409,"GEAG5HCy677g":140272823588,"Yf7563YEy8g4":86914086834,"cieqC9oY2Zvh":968707439357,"RDWmRmiDbgyz":855363390810,"H12mnqlyy7FL":389307016127,"f7uFlU7kqpXp":484555144175,"UeDOrbY0M4mf":186695425368,"6wOB4uMq2MYG":772144222824,"fTyv2VQt3b3s":838632250589,"33vz9uhIGpID":161859234113,"zI4F1RKaEnyu":566066469228,"MgpBqa9yOmA1":842657690360,"ljF8yzfea3Z1":468740986998,"UbapKbh8SDoK":343631505939,"kjUMqexAwCJh":534375455331,"uHFtbaULXMSO":434149956508,"VGc0lRuXAGtA":121684841643,"beGOPDId8rs8":977005256217,"WZNHcMI5zkWD":192339804677,"kNQUPkG8QV7I":869093027918,"iJbVyl4Tg4PI":600740156666,"5MxwgSXAha7F":643184574515,"yOHCAKfa9MLO":219509012870,"WguRhmJDUzy8":439854918363,"T5WXbfTKBgXs":413757842709,"bYX8f6bIFrfz":83911097470,"HcV4brPi9eLj":434975918421,"q0T3YGkdYJ0P":44415913603,"h1uPAzJpHiMV":384910504337,"FErG8YBSzhPa":194963033805,"KIhmwpULlu62":582697748050,"m8t10pAu4kNC":684292431107,"rjWsxbDD2tBy":792401464622,"pjdCGL7h9VaM":634547545515,"PhJO5fzEAqLT":274647272878,"uC8iJyyOvOKB":276965134179,"NdGW6Hb3N7kD":899596937427,"kRZ61PQ0qq88":832286314229,"6EkGeGeFwzfH":962930831483,"G3vWSn4clp8t":763603527592,"18l1hfGxXzGz":776549188457,"Q1x6S7veGS3Z":352097089192,"yvnsqD4ovIjv":990734321896,"bj2u45nV9S2X":187321318117,"g5AUzUd3YYDD":278891067268,"oy2ZTdzqdxgv":212003060582,"1I5iFyTyt8iC":967373826668,"nYqE9RTYr6dE":616508286842,"ElE9ytPjWG9L":186387234153,"xuj1RGEnKDeA":557494188758,"mrucIaVgwfNf":322053239225,"nKVtZSxsMb2L":168108608414,"CIWJNN2IlASt":401398206695,"WM7VLTvFl93F":386727162747,"bZt9bXeCjJQl":617168791291,"qAu1gdhVGE44":466319054306,"VzAI0jyL4OCq":642583365870,"RtXUSna0ZFZP":779495595693,"53Dl7xQ6W8RU":636584397215,"LW3ayQQOYeNv":227438363177,"wukT6Wwq2Cbz":468706317382,"K5vdT32X2nk4":291704740635,"uUHqKf7R9W2l":534284626842,"oHzDEuDDvspf":604442435337,"XhcIZXJYDOiX":670460016607,"Ibsm5HirlDvm":578987864766,"RQxoG0da9j6A":957900849552,"igpeYgdljKiF":403918605962,"CIbA6I8NUdPO":376891055746,"J26FNAfC0s5j":981458139539,"ynbsNei56QbR":822764190218,"WJX6vZ31hjmJ":812469177902,"mf5Ty3K4Sc0E":74976402718,"Q0cHRdlXI76J":367590725808,"tMHAUohGXgBB":290520694540,"RSORQMWbc6EJ":506941868702,"h0QJ73xP9b5J":908238182585,"PncY8AtaPzAW":645961899584,"wC2H836YQJWQ":810295996558,"srq8pLNOrchJ":142109663320,"9p17O09TC6jm":239296671840,"JaJwKLLFInNL":413935653059,"8g9CmZxPrUiZ":277229702384,"BhexQpffGS11":495166587543,"nU8Fas9YQebQ":419055853282,"2tXld6dLir55":318315011446,"RHnnv3eQe0B2":101399292351,"AE6qZkzEzrrO":547526670338,"0KVbCO5gWho9":468481847617,"xBKoZVBHi5A8":188723403434,"UHOraPDRK7sD":378924191520,"KNz5oCKSkZIq":742349577663,"CeofcR8kLI9i":387987342925,"srqRRxDK7w7e":569103140143,"r0ckp0eGkUGt":226327227071,"6vxAFA3Vicyx":690073620518,"tZRJ2MsIoafh":24176536810,"JL0t6p8YoF9g":948861829137,"DDiI2hVeO1Jr":578488279903,"XTXBxLiaCl7i":567760525519,"jQZsoITEDH2U":528708398587,"BfEtdObTnEnp":511681407171,"Cn6NfERCQ9XU":39104561734,"bgVvVS8tqSUi":898108325810,"LQm2uK3nQORm":682603924277,"yj3euN6fe7Hw":330893928954,"IJmDMFvmPPdm":818494532840,"iSYO4l1hGcCC":91877279987,"DFpZL39qxlCN":495737231969,"JBkiuU2rVfoP":140088340948,"ojYSMYqA0Vko":448148084570,"VGFVHo4JGDsf":369856718826,"ytDgCjdtNWu4":961484138204,"WA6iBkmF2cpI":398067711374,"84HjN1bMEflE":110025681787,"JFVWl3I6vx7o":171292027703,"VRbYfqHGxeeU":239465042985,"gZWypiyuVBpk":421076994577,"8xcJUjou3ljp":879682389678,"BqlnawSOEPZD":682816885341,"cvbtG2GRSNad":743547716671,"qxv3itIfyNrA":450420286599,"4oC9vFwyh64B":266074976881,"fQJg5xk6EjZB":765770732438,"JfoATVyfRjBt":415378320422,"u5vo8TxecaaI":668629687556,"mGquERiT5sye":383746078950,"NO8SN0qh9TMg":966594822216,"rnRSSYFBUVdB":342279352648,"binuca6bEEa3":659206559578,"fBCP6vBAq4Kc":896901358610,"wK6zExazIAX1":290037126474,"GJGZRxKERMml":109789927794,"ZtBKyMkpN6vx":293992754455,"O9o9MSbSYUnw":396466768437,"wlvAlK0ZDsdD":7335757689,"4WwJkfqQx9xF":554756809884,"mwBU3Q8zrfNh":814995615082,"nTizA802qJ3q":971168841729,"D8BFGVYjV8ek":805972273788,"s5qO8A5Ko0kp":226721713182,"z8hqyugiefNN":159116546156,"qBsNexOWv2x6":15385303625,"eoGixaFloMyU":420056655265,"59Ls0azDBkrS":967020077767,"DxqY99dekMqN":452598078227,"ZeOL0whZRHbv":172481641012,"e52uEpMdGgh4":567400643701,"jGpmi69UWd49":735432853617,"LMVqBLoJW9Jp":690459470519,"Pldl2GFr0pQp":586176615370,"tFqhlQpXwyH0":35960718172,"yjWlYk4A4ooF":142668628506,"oOKq9OFCZyEQ":953177876794,"L5P2cl1B9IYt":294788374924,"azb9IPif1MWC":869675104203,"dbMu0ywQ6tJH":593578246811,"YMQuHPGwRM0m":860462427222,"1b9Sm0VSrhDf":79108657946,"BIRAMtkN7zpK":801194405548,"qh1QkkKFodIt":544370276361,"zVX2B9vrQhkr":226516363413,"D6NP0EhFBKB1":390861589622,"hSm6rrEx7gnk":467366483474,"FNz3J1NogoCJ":184194267434,"O9Esdr1J00kb":636464876578,"L5cTo8eNPdWM":158529532341,"7BUtc0VuVGHs":898020601622,"PNAvAMrB0lwV":456800679581,"ShfOYIdFv9FX":551694358914,"c5FoYEFzu6fJ":635777994242,"oZZI3u7yroV7":154764604782,"4lkYfogo0bpG":568868931979,"fF4SHUYeAyri":613014722780,"ePNVSjua4eLR":809204001142,"eJR9ix3zUrDe":726237197973,"b61EgJ7y4SM5":262207510660,"5tUJsRC9rueH":836003307975,"dvHbKaF5XK8e":699538564179,"acJNyhyZRrve":912877798625,"Z77fssOOwj04":668030138370,"2jOtI107aL6b":130430718210,"upwchXXUvrcj":897130152829,"pVDxgZWUBnX5":625679485133,"tTuPjVo2hLZo":756360523738,"HyK4yV7hl5ea":768919956504,"o9UivP44vTRy":160828200655,"df6bDtqOP3md":230466408191,"ixaM4oyBsjU1":40581746461,"xN8e6ek3kd7a":422710848460,"xeVP7f2O9mjJ":1994356827,"DBeMaKdShdRH":396761325891,"B7aoAylLoFsV":801208413877,"uMJhvLIp5qFS":492312279245,"jHvpKfQkWsLn":287778800973,"oYvCAjYKW5nr":545462698597,"kkF3R1w04hOp":846174005893,"wuopxCj4pZAr":282403682553,"kADKvFPUiM2S":652149192334,"DiNyLJe2FK1f":496851689326,"RE36fHXhAmxm":56431850906,"xKqOU71BH5pY":43947276505,"a4Hc4WUwQlkq":287548468142,"sLrj19y7iuxL":659938960776,"qJ6clTLEiiaq":720804984374,"bfCbnj0aC1EW":856351464491,"9Qhr0Eu5nFHI":992272345025,"Q3vW8ZDWEOcL":241750165601,"NAo5gvkmiWLc":451809695908,"Y7ir978CS4iY":38656680840,"LBLcDa2xvUv8":56815520658,"puzD0Cc4SKiL":733680787198,"eNAxWjBeTnFa":605777178778,"LxRo9bD1GA4d":807056201563,"h24ea1OdXFwL":429773892885,"KTHK9BSwYlxO":314666210556,"zXtO6jHxQ8rG":334326656006,"mbmFQDuG3oQp":796922834685,"7XnawVXADEGp":467844824700,"gHsiBrLt9qya":715438096844,"iCZatcy6Cj3Z":748444134083,"Mm4DoLqyDk5f":397568172594,"tB8xpqFNaRAG":797444826913,"dqaovCECuWgP":503621067756,"p2DXsNY1UVOX":197354630552,"3vxrIW83CdHH":965110339249,"FTr6IB8IwgsE":960233878401,"AuqePTafv74A":779922404830,"PbJPi6jfmQpE":582440984164,"Y5XFHqbxbHPf":86986819285,"WXUOFq0wCtqS":454905222800,"l163Op7iKlKD":563475301371,"jHGjZ9ts3jfe":82201133955,"DvyYEoJFxEaq":481209616800,"4BnAEVMAY2zd":235008774004,"EEEpChml5auu":168377600874,"z8rk4aejcHJF":646928187936,"8dkYURKX90Kr":942016634934,"du3fNfiYj7R9":286997171653,"a0M5HSr6JJjM":486385486059,"PB3Gs5T83TAH":145728204118,"SDHMedYymAi4":915517867402,"YgFtNLMoGwnJ":306228815711,"8qiLYnBtB6cr":947379710783,"pTuX65PxGWkg":549055057962,"nF6lMCmO3kuc":622229227264,"kXQeUylCUsnf":138348597509,"IFqUPnwqnLzN":958466350501,"3k3qee8DNUOA":366104287264,"YVem2evZdwB7":183238576072,"08MGwyRKjpix":908489025750,"Yx0XRsMSky7H":30505732338,"MzCzr5Q3HxzA":657045132539,"KtoVJW1ZzmKo":738297520764,"yuqQ3xZ7EdQr":463029256103,"qhPzVCyNZIfH":411428367200,"1ZGoFg9DSdGo":548417470424,"QfoWIzmoJuBs":93213723548,"Cwv46stWrdTM":285835912663,"AFRBReWSG2yK":788085285215,"DHKNEbPMgvFy":160488307331,"NkIbrL09riZg":605802897790,"SSYODBnjOOlv":387099299884,"e9B8eQXy200H":144863139206,"XbM0TvcPQWP5":760214105662,"JQtsl6bboQns":330545030158,"AoyxCtrMnLEH":420218900456,"UruDTp7JYPyf":137835908029,"qBPegkTI7lb0":292122604833,"sm8t6BGzWknS":283107981205,"Of7uHGeDOO3y":338523238230,"QWEMDJ4LFUHk":304836912566,"vMl4iJer5XmP":280024904979,"PPUkpeg16nhN":119537601425,"jh1sSYJ8tjNy":425072711881,"UmWUWbLbasvi":982733229834,"a5Vf7bjCUiMo":983873575096,"NcXJbX1AjyT1":871814309243,"IwWrhmb7Km0N":514873352788,"NwTGuWOrwTfw":94655304031,"S4uwCoHK40W2":924814069536,"2LuEOfaKlXG3":641798206553,"k5OxuzbYEui2":377704257506,"mWqaCiJ8lIkz":565336063334,"rjquN0BHMzsi":920916716518,"P2Ft9zrYdOaB":739597128019,"r6FpQOBuzodi":61464663159,"lHisMgcS10zj":786515918145,"kGBDzLeHwzsf":670952580653,"ubHbMvV5Px63":34172386188,"bOieQBDklnis":228561997770,"Ls0aygfJc9Br":704374869562,"fURRZWPWuiYm":671378008626,"vlVCKTUxChYZ":195220871331,"9BaZYt3HX8sH":659513141512,"Uw56lT0PGYAY":670801282873,"O0XaiBPjVt6x":302436173314,"HEt47G9KTMzO":476477454199,"PzsgE0YAjalE":745592887719,"SFcMy4AaKl6d":494209270122,"xxrdRNbWIkWE":681996778266,"ogQKBarEfrTA":364324934479,"Yok50Mc0tbPp":36028071408,"OOl9Nv57H8Bl":218325982947,"meMIUOQdDGqJ":141116762854,"y3sSRs4qoOVL":401958915764,"Qr5vwyYypMON":569409062224,"mBr4Xjm3rsJJ":889381619272,"AYBx9I0G05rl":773506410828,"Pmam6jxecBgp":89008050220,"thzGC316m3ay":363886037167,"jLczM4LcHKG6":33746588099,"rg90sJYoIBWz":641491193174,"2oKAohXSczYu":255742391732,"l9For0Fqacwz":337523476306,"samUxBfPWgmI":406370687927,"VmcSMD2DhnJk":484343237069,"GyGkN8696P6P":731326413231,"hYQPzSWeT3rF":981319595234,"wdMovYZ8MoqV":418400118890,"TLPvODD82Wxz":721323730718,"MYCfMS1Siti4":493374711518,"X2hNnGoFlIwB":297940822353,"NTnoyXK9yuyf":781015206154,"TyXpk3Eiwn1k":585106424371,"HrCSymD1loK5":782292049568,"KrAOBKXqVuhC":198099178051,"pphjyVi8FeM7":89369273603,"gg40nCj5yxLl":118423461704,"7EWsIZ3bw1Jo":430856122149,"huz8pg4eIzGj":532811447907,"p8WUWpnZbcdi":512424500280,"3Q5Fpc9hB3GN":662197267706,"0lZtxrXRbCoC":21187986163,"5YB9r09Mnifb":82415988001,"GO8gJm0Su8rx":350430133998,"qnkFeNiWASqP":735934350166,"Q0zTLmwVYBjt":242787984157,"Towimvx2wR7c":571207761790,"PnLuKthUHLuw":651760146898,"df3P0CQcIeqd":385464673495,"Pe01D7u6MYFm":923217933483,"j1eSwO94C6nz":223559535788,"vaMgxfKeltvq":791773688363,"VFUrmjwGgier":836687960493,"ynYMkpoiUlLV":241386465230,"zDf0TdpRsMEB":276967768895,"Nx9TCYCwmmOD":285703844622,"V146QKpvZ79A":192907561915,"bj1Ry12EWfZ1":192594305875,"o8iE00wZXdii":88286835303,"pHg3XkEKguBW":838420131355,"mhwyBuS0mYq9":295344162617,"t4lPfjbLBkA4":901961362107,"z6IpiYnJcmgC":741734556810,"AOdHb0MPA21x":905680670344,"pxJNjQEKiSJg":132747000380,"rNHt73HKyzEk":589983816932,"DS8yqymmcyxb":881378181589,"TSULpQiwRcKl":859349469503,"vC8rPeS5DQh3":732245592693,"ojRu33H4n3VI":877741045579,"HlSXCrdNkUKH":712597210978,"ZMeyF9XZT0gP":718132409033,"CtCE0SCslrKJ":761504262837,"VQZrVxaO19Vd":12786265877,"IiTBXhesDDCu":646816754124,"FTUrfMJ21wVV":398315906873,"s0GBAYWDNstf":587351834841,"tZ5W4Kwm8ni0":63268692243,"aKzv5M4VlPwA":982374550559,"VbPBq9fjLCaV":376573952187,"x2ch55GgISmw":31664015256,"BCd51NEpY1ZX":289987357481,"iqMePC7cArbd":470669484361,"a811H1lwfC1m":712278715116,"7lb2VUe0eZK0":698784660476,"ZRTPHhye4gi7":222813843308,"cuKBiSUWebJn":926338284032,"vNHheSsVFNGF":361439279727,"4mKGg3BoQUqh":422253716786,"1kHj3xOLYocD":330403700577,"vyQIqsfKhJne":833239995917,"IGlrlWmgVzVj":10607203629,"gZr040c5FDGe":164761002611,"1PAUvNPPzJt5":516773559947,"raDkn7yc7JAs":713030334855,"cVEP450bIa6D":199111997596,"39M1g1fp02gv":304826103931,"Lhz8AjkXK8v0":515061032412,"NqjYMkS8j1fG":840155735976,"6CvKyDG8hrjW":740651466346,"yVMHzf3oM0MR":668466098631,"f98jWme5FeKB":800859716484,"txEUtIg63Vwm":999975052348,"Xhwk94xCcCC6":440969619062,"pYIAqs7laKrc":983204419565,"HYmyDMssOLGj":23341248322,"PPrah9U5gFZd":322147373732,"ABecT5q6Qd8L":252040783320,"O97p0ZzbtFyd":687136236327,"bpfXQaheLXkO":317584378798,"7em6kR8B8avx":191365797587,"DPWM5CDXABUL":279604501024,"qmIdF8AnFEJk":904238161220,"IRHbQxPM5nI2":399728713313,"RUhUL0ROuSlJ":389955212245,"njiMWOsyUmLI":223390311585,"KnZJ9wqBTqG1":771225140962,"t9OMw0CicVn0":824680069290,"YlxLcLIWzdVt":252625228959,"CayVqBvfzcyA":844797733209,"nmRpaEejOHyI":85388770111,"g44MmlKauvLP":632772838108,"cYXpKJkenPDc":76621459302,"rmfMCPVJnGT8":33996395154,"TQrn8AEAkqMx":681610131064,"Nl8ynsNoI3sc":426336072402,"4vJd8uSoA5dm":15702707493,"IqjuaQatNjSV":77574266094,"v4z3AiDEMZVa":856155846392,"sJlxYvC3Xd33":503706125685,"yIbT4WsfPh3X":19603588525,"LnAQ8UtMX8zT":456812827011,"fA0vuvWmpU3K":559333890918,"hCxr178atl3Z":381330429271,"a2lLw4FgjlJI":25003688708,"3ikaGJmzrr4f":252819844045,"KGX3SBDY0thH":851553285201,"1VeXlKD7W6tl":173809805664,"i4iET0h9Pgp8":422783070975,"Iso3e0jgsvju":536762346854,"nyehcIqz0soN":389654953506,"1oqkKkAetPui":85996045440,"hY8gHGxr5wi4":330842039164,"aSkhRhms2YC8":745281860632,"XqTqib0GCUc8":907086786536,"Oa09l79sQTpm":734061273795,"6uePfWrZiRtM":537314205422,"487SFvBtXAkO":788447194731,"rTNr7q6ky0oi":841129524621,"240Xxvoe3EKS":48947991659,"6Way7ZgWd5UM":890284271103,"3knlI3H3hFec":444675634019,"b7nqMV8ri7Or":145518715892,"UacbVQApcSkK":132067827293,"JcCKwKnaH23q":287159045861,"0JjZPsgejfMe":47143683168,"0coe7x1gLebm":400420556791,"gKbTqhBq6BP8":539474279968,"8Zz1qj0Duu31":460090707931,"Qm4JD4rdvWWP":763537608947,"Jm376DdZBXuK":172353906485,"d0QmM7uAPjI8":554223565989,"63mcY4pndgK4":250328569827,"t9hvdlbSxO3G":92920771378,"MdKnGxkNaVha":647142579682,"FEDeRytAv1h8":370311879235,"hFFq5kMPr4zh":290517932612,"TRFqsXlLwNrp":194922290632,"PdahAMcASrxm":296213025492,"wl0fTLAsfxWk":604588375162,"Uua3n7HvVPXg":199760625526,"59hsqnKzM4IQ":476324533976,"J1VCS98Y3qLH":153083547812,"DMMOrRqNKRn5":892542378666,"9wrMofuOuLPV":867592154992,"kKcfeSH91vZM":28810430118,"36iU1h0aSj4U":561841862290,"1YfRAP6PLdBS":344512359696,"bOq5MZ44hQXJ":421717963986,"mtDwoCrKTTCS":929265524290,"mAOWS335Ly4D":469629457145,"bFUahHMG9nQr":838534827342,"bXMQzXt3HuPW":44782994334,"7IyM2S8Fp7r9":948325609744,"YaMTVqKwyZ1l":151196573470,"hhudTqHeFvhs":53822116028,"Sm97ILWvRhLM":246471737194,"QzMyL0fV4bir":151052042239,"OK1ACPUuR62O":979741802665,"u4H3dp9XDx01":75057057865,"Czcm74iaP4jR":400270507938,"X0qxJcTE1MZK":352180329183,"mIOjtMXCEAQY":926519707549,"YttQ40qoAPTp":692318523231,"w0fvhpPzcsf3":203729706216,"MZaXYDqiiFeN":348048851569,"sGfcL9shXV3S":690149365140,"wEYH9Mtkr5Cp":561567817939,"zELBoQz6PwqS":499297715214,"UJTMktKpBoU4":772575941119,"ldHPFU1PRDtj":150421026362,"Pf7YGD3eGLGd":903686180949,"50Q0kppzb9SS":668006857159,"aGUxsB4I8Tme":162248201522,"ARuWe7BQVI5e":152529465188,"0HUA16ITK44D":61978100242,"3GEhzNm1powr":839952240189,"GIYUfEbcfEl1":914813018824,"JP7GhgarfY59":465098970174,"PWkOZ1EOwv5x":988156820558,"HPbWQK1n89CL":69492598921,"kj9Qb5Ca7Tte":87261346861,"bLMqSeTaxB3Z":73388575755,"hTAGyDawjNyi":747990963153,"DuJCPay9H0ZD":671083500149,"HBlkAgRUudyA":2999715038,"zT21z9sfOzas":827322474385,"TqesbV80JiKy":969693414528,"cYE6LFX0nN5y":938431167391,"Ovtxcacth3M5":999002106554,"fSEyenvmLyJj":305565010386,"M6lXJ0HG37zd":262356471379,"dE7O2NjWb74K":519775416233,"oVUOtW7kwdNJ":214590895786,"mEIXUO0Oz1Zy":267460922937,"v7rblxFtFQBI":658282849111,"PTC7s2PKFJ90":91157836187,"cv5drYPXpDqs":236788792275,"34dD1XtYebxY":670451127795,"MNrij5LqvnXa":700926588257,"5tRi6AROb12C":880599008281,"6vk78zE477xk":109388327201,"9jpDqiBFoU5R":865333832212,"xr3JH4XNbs9V":673569950173,"2GV2ftftNBkp":431532905123,"KpUx9cc9WhqW":937036608474,"pf6bw0QkGAam":506356539697,"udkuXHUUJTvS":579337543318,"MbQgBhuhOKst":834998347728,"SUr5sATAbc2a":842938515076,"uJLhezf4S0eg":33528849531,"XQ6uLpN3Phpr":1343521557,"QCnDDOujf3ar":788620706241,"j5eRnbcJTCfN":60462365900,"xVVRQazVGTW9":503225610524,"BMd4CTF9twSq":705011673666,"XFAiCirkAAYs":293428159749,"shpTXwugKKty":457151671858,"zipuHoddu8zq":269253388502,"OBVZqN72GiOV":754055991060,"KQy92O8jDZ4Q":135642553222,"s05dHmxDbJIi":757178051543,"hB5x0wSoXJBy":694674016742,"6k431Vi7KHPl":708590234656,"U64yLdEm7gua":579411686628,"s3Gs9KPtSrya":659711119836,"6GJqdQhQ442x":45474025092,"wdxi6Vqsfbtd":48460570144,"O8K5R3RPt4HS":461215276169,"UKUhVZZOh57G":211740121318,"J1OdLeeMEHsq":309255361381,"M5Wxdt0sGYNI":358007366916,"QZZsoGtfpDUq":717136557301,"SwIW3mfEgg20":123309507058,"7h4LAYzS44Ay":512130681746,"ZHih0zYRMXd5":140799891465,"8oXOrBEFMIWa":724692241033,"4GVSaZKUTojK":325944616620,"8OaE16ZId0Dp":514798550236,"WcuINtntldz3":554045585044,"plbf1DrvFyol":871031898546,"6FVb3fXliM6l":136057237017,"gdg7xJT2QS0f":617190799805,"mdvgtpRAaVZF":860652039873,"0zMu2jP9k3Op":767887914992,"w6Rwi6OVM3SZ":957783323937,"NLwWn7E9GXxr":851173665006,"Q1tpMYMAjTmB":714386904249,"0DsbhT2l45Wf":717883063371,"OHubLe2HyDDj":209061921755,"QyjOmHt4B60G":393075091810,"7Ot5JPlsjip6":302173475094,"mxAGf6v5UjkU":556377643680,"6uUertryRFbe":883882192193,"wb83Krh3tsJ8":916996103928,"2kx51YtJ6Q3r":824351803917,"oZHg5zrlcktf":554925713592,"jDSfvhe1KK83":287844181296,"NPviSiHzSjoV":63326033036,"gCLraIPFv9mC":624523354088,"CiW8JkX7b3aR":119123359384,"ErV6RB6URYgd":452759133181,"FfzI3eJ7ZlX2":179664547370,"LhsxjNrr5YwE":158140422694,"KywIIJXHvMe0":321896272004,"zxf8g9rCOs5l":79905142096,"qaD8QgYxX26C":215353010273,"26Sra8uzC2ut":23406162699,"GCAeEZp9t18O":629590103392,"MOhwU5D4YhJL":189913706025,"v4fyBFbeYmqV":240468904291,"R7z2evoeZXCO":609175522440,"QvB6xfdRnYG2":689298907477,"OI1a7r9VyXzf":17497020640,"pmmb3LrHOjdi":981550513460,"U2cdhkI7eNCM":963859434386,"BXmPbw2MdDJT":581201473869,"PRmXa4jS0eKi":752308366297,"wAfKcDOvtF89":653926064773,"gObw6nRBhy82":326543832829,"aUL0LsH0gE9g":879257795716,"GiYdEtSuIGZz":686729683515,"G3LXivLMjGSQ":499654386471,"735hawI2ChfL":313270255221,"lRixEBhgaICz":134406138780,"d8Ax5tonz1IK":908137524601,"vFaDUjO3r8Yp":543611923190,"oQaTpVu9GvQU":182362661753,"qmJwYGKWaXOb":398904573865,"WlTNMnqXftGy":83738740808,"gjDOni3h0D1D":712761020868,"fLJq6wPYWq8v":929411710905,"ac2f3c3D3QXg":180360208610,"eTTJWztQnXSu":748702727145,"rxXP70zRGBoL":469999548006,"rM86oA4HcH4Z":539726324946,"9XdEBLOJV2Yx":207119521821,"u97biA4jopO5":682375622249,"neI2m2lLhIGo":86132568462,"Q8nljYO9eDb8":483081455563,"ABDw8eiO3tv0":891435580386,"9OXXmSxGxVVq":76733247056,"gVSHgxyBIRmC":379439619707,"GuAFE1Kt4G1t":545496531109,"YjLV6fLPC21V":781751393957,"XCVxviPKnLzC":842362771888,"VtzLge25ItGN":901375478528,"egFbcMV7kgPN":580418219088,"maEuzvYWLTpb":725393122854,"9OhmqHNNUltZ":47928385887,"DfGpL9Mq73ZW":689972836293,"Z36lbyveT5IE":647330184924,"qKxIOr7Eemu4":468981169857,"KvDWjyrd6u17":946909428964,"j3Hk9ikLixzO":200846977376,"azILnvLbFr0d":669134543162,"A3HkJ3O5CLTu":398033977309,"VRL9V77a1ahA":159117515933,"bD4BKK1DXRsC":238540771089,"fBQzUSwr4aen":341302193625,"y9PbGuUbpDgW":526068230440,"aFixImcaUtlz":558927392073,"HysMfBeZu0DW":452059873236,"gITQ116uVWIT":20624711273,"ZQsmKh3i48or":301016000324,"INZWjfqvuCMC":595764870731,"010XNw2AjtYb":164838171747,"tatOCCEENvfL":88872185692,"cupNa1bAEHb6":599254732350,"8zqrKuBvEggo":652732631657,"XwpwWYPW856z":271741488946,"kxSd5GsfuV3H":276441057651,"QpEO10mtPTPf":281636140898,"XL0ZcZYUsBLw":952955051986,"2LeTeiU0IYth":406947947232,"yxEIWewkmgXX":460758537776,"TZXZQUjGW9dU":964357551988,"2NP2r1iJnrLe":28965681024,"HxcikuUAOA1z":483006047408,"qzZ9xQcNzy7Q":864140068078,"Ot6QW6JJOAAY":801576957590,"gER9aFv4oqrv":449668934915,"5MaSZW7lEZvr":108652455817,"wP0lDIP3IujM":153116870892,"Q8JdkGsu62AZ":34578474172,"7jZCguKZ2A0b":158100144756,"AZ9tTEgiysmW":966916058500,"3WDqe7hBp5er":795851093732,"ylfPZmIlLWpj":238551911856,"LC4INfZDD5C3":630041277017,"067TaVMTCfSz":992386869017,"SnVjasaFhbDN":848188839489,"7GdkmV1391tE":490189063989,"UH8xRuovvI8D":358314109589,"2GMY7B9sF3cU":874296766721,"f9COGb9DpErd":735287982068,"soWNHFlkPUw2":882432658029,"22OpqaenNFDr":203082829591,"yVaM45AYRq8H":707491067376,"gexWLH4ztgfp":446516067709,"unjgamq9p55v":964506418740,"qc2O9r1mSlBf":370582186156,"5vTLunYBITWL":165312179030,"uUw4MXT4C5XU":628647734420,"AhCwSRpZloIC":414100161818,"PamByrR6sCAi":82111867211,"pKNHG1lTPW8Z":638800992766,"tqhTwrV4tY3J":283135297253,"VIwyZCsTJdcq":383773295375,"CxrwPU5XsATa":631239821033,"t3dxi8mS4jVl":421132628434,"rhUvzGtIxM9I":479376089920,"0EbOwZsxqy7g":349275475496,"qP2A7lx2ftA6":462373211824,"nrBUjWBmZzxT":996172672663,"cQMnYiFL3qGI":824294279984,"JhfNzrIbq2JD":393257844466,"BC9wH25dMr8u":597739602074,"jKdANavJGX3c":527888558497,"H104990ew627":480460294842,"xyeZ5vOvIxKW":428916743728,"NWuzUOPEc71n":606093382094,"13DzmSgIamdD":333221474944,"EXpIuIJda1Kb":511761942720,"JqMFiQeNocEW":54465309981,"Rs3HTMHsETMD":829165356530,"GL1cACilCrSV":930467038033,"L1NJuh4tH9Td":33537889495,"lqucQrHDeGJa":880025289348,"nonyDHyM2u3l":219460891022,"dNrsyt6eeGPJ":934490605681,"CyoPfjH5LYot":498642751470,"SjQ6uZGxirvB":47118126409,"SnbGe9AByb85":898376643016,"RW0s84q0GUfN":97494269398,"FVBdPCqhWpU6":871115431328,"SXOFUPuiZRAF":283701998050,"VuS4LtB2IHwt":584223686090,"hYYluYnil0TB":767356121487,"PDAgbix8f2P1":996561669622,"Br5wIywvyok1":969240788464,"gnzaFKOoVyBR":916722712301,"YK1FrmSivXHE":466240161483,"yPgjz7v6f7pD":745488858987,"mCWGqGHO0Pvn":468873410693,"O8BpZxTMglWU":363440845432,"Fuudhi8IOW3J":218623777160,"OPtAqRol1ISs":949555034921,"mPe8IJHRAqx0":673588657117,"4TMak7vd9PD1":304016439350,"3wYYM1V0HQQB":198992377823,"vbAthG5T3nuH":484838219243,"uGofzJUGJoSo":486887724441,"7Yqpq3zmsuQe":754497302544,"44yowEVITZ9f":802251912713,"o5Qocns5z0TN":620409345829,"oyn0RilyQqkP":847277749124,"xcotc1UCYU8z":642086548316,"ZHtH5PxSjUBC":282209497090,"BkFbaTx5GeEo":664326752466,"zeZjfWX4ZDZQ":153359022944,"GEn7IESVobMU":974764515520,"zd1Up6z1XrTZ":840266320433,"gkUZ0FKvm6Ku":238337979662,"3SSvMACSAzQQ":380772247602,"DJVHQP2MVxlC":190639254263,"WRuFL7oPtaFu":857866848907,"oB4lOXr3jbgr":311060869115,"vT0gdFVvmuZX":500269430697,"1dMSI3QwYKMy":242036758877,"cD2CO2HPKHGV":493945749814,"wjqiysFGdqF9":788996622330,"ilwjcJzbFgAY":108476897043,"ZOmxjqQhmm09":93757153692,"M4gIIulNHNRK":79907729275,"69bNfO2v9lK4":912218477185,"Nd8B6e2eWwRm":239153082679,"952IzRXC3FvD":615864209678,"noQnceusukqt":792161263065,"3bVvcT0pWSc0":166854659938,"LplcqhuxJa7s":376317483868,"zCi0Mm1OHUWr":911670130834,"YAjS9qFVIcgh":484178405643,"7WD8jQchca3P":71007287767,"AGsbISXGsdIQ":632314766524,"uxsE2f0nZDxL":264707922954,"VMRLRMS5v9nC":493247703058,"M10FT0kby9RJ":451820804288,"tjBKdjbsiRpa":412181679114,"coXJWZsBnXah":443774900870,"ZyVMQ4RtFrO1":636539852017,"uICNZZYyDFBK":532625777751,"E4ESL7qKVdtV":531848179894,"ocJelm5KV1Dz":813494072607,"plRhUHxEdDMx":490525094102,"uNrT1jVym4EL":386616798513,"B8da4rlaCnik":721216488146,"vji5DtoMAAUK":247307847778,"mQ2h4tYLQFrR":57214824618,"0Ov4x3o71TVS":975737722085,"ATfompCEDY6h":575504601865,"5xJxvBPznEFO":672958640928,"Q2IA1gXIDfdH":151095788662,"Tt1ph2K1G97v":516867193496,"JdVQEWRiISSb":295800473835,"FzySzdHMchXR":712543683432,"X5JuLML2Y2Av":68530817398,"Zh2j9zgeiUf7":49318647177,"9zuCGkuUnlb5":757256167335,"hx5zGDRW66j6":785979879395,"lExqufZnZU8b":773783135465,"4fsr2Av5RkRj":61708534950,"RxvLMFMDzEXX":741152725418,"jz3It1renfhp":661124320560,"vXiGOLWg3TWe":205784710863,"n8YA8U3ekAkS":137860319271,"TfUmbe2s7ftR":422059006702,"1mLz10RPlE6A":244781313806,"sJFqniuqGbA1":732825095138,"ADz8YW7l8EbS":559366513388,"C4O50FTDVfJw":103574403927,"e9MBQhQLW7rF":78493108342,"gADWw8kpWpyw":781708891092,"xDKoO1zhkcDh":384338451889,"s14l67IYrY0V":940021580,"lcX40B6ObHpO":977127411004,"uhKboGIPDtIG":810993356077,"nKXOOzAlEoM2":753590673398,"CDlQL1b1GkuC":49381910163,"piSAPo9Uge1c":487520462151,"7LHMu3hDgSB0":345180380356,"MyJi2Gz2aumu":733883847179,"FEvR7usTQvev":317678927153,"lwsUHHO9KirH":573448606768,"o1MOeXnneSfK":39375668889,"G0T4XxNRzaxw":519501288930,"WSHOw83OpMiz":844802863181,"iLp6ZlSEJ9RP":917291520508,"9lMwFKeiHI1n":358679631349,"SgEjpH7vkyfQ":94811494126,"ytU9dTKgJaCw":905635195781,"hNSLnj489gCX":435733753057,"dJTIEPrckHbC":19798587951,"TbOB17lX5A9t":289183867268,"hZcFANETYJ1j":691195019194,"CLMDXFaRirSW":261457918067,"FPwcFbczWDVN":759814854694,"A26Xti4q8hD5":352710144930,"6Zg0NOqj4a0Z":864950809467,"kR8jSwR2zSss":822052544011,"5bcoHIQVSfnJ":760990137397,"P3mEblMo4bdo":28441108719,"G88kd2aaygfh":594063921230,"mbdimqVn8Hl4":939252503410,"2vmuxWg041tU":490829550717,"MGsyZIJTVHZx":409922676257,"bBo0N01spWcN":536706175089,"rAsCRC2FjuXp":746900346954,"4Hv8mJJPxivJ":945054275367,"ZOVbSipQFFhi":648212073431,"BKAiA9w8OMNq":102760072375,"n4ZZAVKjYwL4":178356374369,"P8ZVenHkzvLV":377545767647,"8kVzSqYkzrrr":468242622378,"a8NpAWz8MjYV":931236969614,"cNhhUP2ZkHx9":412312888277,"q8GApjXBbKjw":257137809364,"iNg6dwydjjnF":756466107707,"ITmG78OeYGzM":550128035404,"uSkSP9zWCJxt":589182717318,"fyEbrzGZ6Aq9":296453431088,"7g6fqc89MMYu":560867273829,"00wAIKJ5nIbF":510702855947,"MKdWbNoTpR8d":708832562232,"944YywYNTplJ":297267017289,"weMtVEpup8AB":638731078391,"qc2w2Lz58pzo":674298678555,"6BLsbwFINrKV":737587109537,"7WvHy60IM5n1":126810561118,"YGNqsU6GQgKF":416262521723,"hIXkT2vYTxAG":839388609662,"4dnsfEVtdImY":299349048360,"lJXaZoxl5kqS":821287219503,"q2uANCdp0ggB":519465309890,"trXDxcTYuxX7":907211420071,"i6zea12rkQ6T":264706030349,"MIHmiwL00K9p":99935272940,"fZU92pCjo35m":195145946619,"NragjwShRA3k":685397142437,"59fgQWU5fiMi":163088386541,"znrDiTIExHBq":529305529940,"tCKFjaFicxw2":760850362555,"jcKnjBot9YJa":334155110033,"1AK0zApt6bjV":432050472529,"2jJ8nE2T48Qw":302972316133,"yHwjLE9uovbO":382179980178,"3dibKVqqr3oR":599972465868,"67cpy8gtV0A1":869874205038,"3fmDhN0NPg7r":795882452216,"NeEEBru7PDvC":992452027010,"UiC3iWTIElLU":649967053349,"2QrznHhUHh7F":464782078158,"XErwHsu8Ngyw":636704189259,"Rgi8mzzYHw6N":736489389799,"OkQL1lUCsx8j":835553573196,"RAlvhornUPem":592537746032,"X1eBQVPSQmc4":73536992214,"9IadzGgw2VK0":724482407365,"zUi5AbJQT8l8":186577981318,"spsmJgPWSVuG":584644053909,"FbJ6wSod9jTQ":576211109710,"qIQdtPaDyDiO":820480375318,"YBByk8WRPgrR":123917785038,"TpzXOVnuiemf":336112871558,"dcUi2TuptAy4":204111353438,"HYuKIVplsDOs":144646522413,"ChZ5Vo6avtya":605969671697,"X14DPFWo17ll":948734102746,"gY6ZpW48bKoO":378627097146,"ag7TftDutfdX":313362220010,"0yDhEwljOGOx":77779309691,"3rf9DPDOjLYq":108664057172,"uxOrCTfgo5Bd":169804482654,"Ld6VvnphjBSQ":540402037510,"IcJRggbVKMDy":787417204421,"34zol1nzbN0t":886746077605,"1fFHfZr0dCd5":755169279158,"Y5Ya7MG4XFrC":737806467862,"mbZTKn1Em9Xv":885994337176,"YhKyTt8w59DD":218577617668,"hqbKHDtFk9PU":559926427277,"BJQo4C5rxMR6":976121810604,"vMzmUKPuRfEA":890345247443,"of4oBYVuAVpl":600013579020,"kjNjHWcpUhy8":185652268773,"LiNehL6zn77V":101807359733,"cx94UUrNurCn":814856483034,"4ruwFt19kb5v":300805303490,"wNobODWQgu5Z":354793961528,"UMjDdIMETSIQ":721244097887,"1IWTqSEs1ZXD":97035525801,"pis1JVRyggyT":638430097631,"7bNQZNuLHtfJ":149805226691,"4Qm67F9mhHpO":489938801314,"G5MVq9k29BKL":715607090142,"K9eMF6duKy85":598407860512,"qtur1X5e8vgM":392365512571,"bPMHnJTdTEcG":235911192003,"1MMyMKKwpTwM":7788578834,"w0nBA2tMiM2E":764400317216,"AoUP0At0Wp56":246567263527,"nc3pGnIzdTbn":902377466198,"SoDK3ykk64Ue":394508670656,"wPu1I3kFfQj0":3645158587,"Jye0sJaBxHvB":201421584323,"x9X6vhlu2od8":166760756894,"gOI5iW9ydo9i":945520370047,"2NrgCWJoKQbl":391345357855,"bMT4RgSndnit":426162100655,"lC2Xl8bQoVzp":816423828352,"3dXiBwbpIxC9":625030905800,"pHoLpZ6fxuPs":513759232438,"4pHZeyizl2xX":650098368421,"27s23Ts7AC6o":759789982992,"XV0wQH1YpxKD":360234892734,"zDnN6lX2ycM9":581312659422,"QDKZn6pYXzBs":952488483270,"AOxe99755imK":125253974123,"FuqCtPVeGtSz":279786856430,"WwG4VrpmhIqI":926952450071,"yTWRV5BLPOW7":74405745837,"S3fVyYlLdTpu":820139216190,"1Nxza4sCzMbv":964882911251,"b7w4H5ZICbEz":377211039950,"6eZyLrFnZty5":725498618244,"VYlzpsKe346K":97070715234,"ort8m397jis2":424299566137,"dXx215pdxv0B":633866581044,"5ECPiQqe5eDa":474381993774,"i0S0OAeUZel5":131529986875,"hACbmnmwHRNm":891041755245,"rxmSW9PK7T5V":591343300393,"HmJKooGGxOW4":799647780019,"ED6seqiP14Do":912077760176,"WNqYTj5viI0n":939247419319,"MksPfWdKGNuI":51717105057,"JoG82CqbolXr":194401977838,"fHUihaJCvxvl":436249735724,"r7gKyEXJuS1a":1265809562,"4lTknwNXdiax":189537773778,"E8ZYCYFu3YmO":670795988941,"CZ4E70PKleut":931694261825,"I8j31yWz4Gzg":328839453449,"wDv4hDD9UGcL":571561505570,"DZuSUGIoy9Ty":765125634872,"xltdlmFOVXWG":711577915105,"Gu17jHaj4T68":406022119262,"VEaXhfnQXZD0":675314987140,"Ep69TJayb7g5":324874910432,"1yw3hkvzUUDh":913309065021,"oHZncF7annIC":826430715687,"McIwObr9LbBh":737956189601,"mKf8Q6rdRd01":227221010630,"cZrNG6QAJ02Z":334395936512,"i8WpMXKEjB9i":173891192354,"ARpCffnRjb9k":268213879617,"domqY4rDs4NT":896130111661,"xAcpawS0mgvy":149409191436,"pJQG8CrHAQ06":935648001966,"vXXDHRRPIULT":201638590325,"vKvEJYtYGnqn":789151712453,"jmdIK297vjG5":736652914516,"Gs3IPNi0avZk":70275836473,"8yLjyTDyfdkU":951041690022,"0EgJNYOJkZcZ":590489180,"raSdSCNTA1EQ":50910163596,"kSVdiOuD0gKb":470717603968,"dD0b1GRd3Qrt":983013665942,"FbrQkDuIX0NN":992440868221,"HznBgaNnvyCE":578851051576,"2bC5OXDiGpoy":673202383614,"LeMoKRyUP2ED":498398343871,"ctc0OcvOxUSH":868083244072,"EJBaTPnSGUCM":798584366440,"XYR9BnmmFirn":556555274452,"NYP8flW9QksP":102842483021,"OFqpFUGURS7c":457560820999,"WXRkpjAKbhwy":783171024418,"4onaYEEMhHVJ":212814574362,"hXNaACSGdbr6":888109517217,"G1KiLOChb9I6":591742578991,"jp4SfaEc6oJ6":741870283640,"c0ZoSA4xHj9D":73440859054,"XRvf2Lny2AzC":637892275715,"KbREt6r4wQk0":383358943090,"i7R4y0ruyqET":477041577808,"DBFnisIJCvp2":218260873526,"tLCuaOLG8wp3":882406258046,"uN38RomBD8oJ":900089693521,"q7AD5DBemxrq":485451774587,"JPPqjeqIC9fm":191081155808,"mO3vXzYxo4gh":714044649995,"9GZlm4WRJs3L":483926221260,"UZjdR4Pcd5uG":673156854347,"wee6qv1Duh2w":47701065389,"k2T2D03Vhh6O":959747986492,"3a0gplYIc8Xb":709850723126,"bWh09u4Is8av":984150019891,"oAuPKyOLJEPr":401392978222,"R0eQJFbBLAgC":471143195503,"g6qvwJb4Qaug":163436044117,"GKO6sWegbE5p":486159292736,"2DMlSREJRxq5":448458431917,"hLqOsQmFXzHz":893497821229,"QkXBpLNG4GH6":431077071715,"pmgp0Xouvqnv":729464933772,"4KxGcogbcBOa":692815520009,"xJNYpkDMouAs":999124788365,"FDd33n4BdR0B":383664135084,"z0I9D3KzrdkE":603714565833,"IxnKHPl8ULsO":703168029550,"pkXxdkkEYn9Y":246080243684,"cIsVSnIav22d":550056118396,"bVd7BtKGXlVR":974748706838,"VZxA4rFj9vWq":90700069680,"n1mJ1Zyo3DOa":926888123650,"RZ1nLBm1IsbG":269047427491,"iSRb4qGhOi8q":8742932138,"NfaLOuEH4e5v":687709862512,"i2crDslY0b3q":967788398050,"NEfY0pjSsDa1":777155588102,"8XqYSFUvBAKz":98439406042,"GWaHMl6MdW1t":447263895458,"qsbyDn2i8FBZ":150929984692,"bhTSKs1RmwYQ":180186352530,"38GckHhsPk1j":719147088719,"HufGQ1v50L6R":149472370145,"8J50YosnZ8lI":994362127155,"qVTTKXrAxAHQ":757052973493,"uFaEPSJunAGf":447542803061,"RGZcr5KIYj3T":877806920392,"vVl7KvzNl3Nj":335153927959,"a0d5IvjN1mR6":368038692339,"psXl8JTdwUBy":859566152469,"moU4Ntvs47sQ":855561263808,"fn5GG0uElh71":981381523023,"uZl8VMV94AOv":549822051192,"SgQrfYEyC30F":331437999887,"HdFsZZ2dX5Ce":841692109909,"KLmw8mNLfWdT":291040831996,"chsDjpEHNRMR":65686109718,"EBcDrBIWJhsp":857812940782,"8y9eBH5Nyx1x":985211100564,"5ahYUkChgPlj":610539451162,"Bl6ZS8Qs5yi2":755367497397,"MmkIojpzGtJ8":23308353409,"zGwZb3UNthoL":241393882512,"z5R5V6ux2Iz5":674566449348,"eykdNvy32IFt":847817328081,"dTjdLFgh1jVX":9018000342,"7ODvYPvIStUZ":28301214210,"05V1H0IXyWME":164614601918,"kvCAJ680Xo2N":994206526979,"SYrzpke7OkEA":510910296972,"CWmCIzzh0ssv":380194575532,"9uNxVLT9HaOA":969432674496,"1gHwylhWUHru":335077437532,"GR60jCPtG6Lr":97560685683,"zWEb0enpmcL5":536122002412,"B9cK7IvbguXv":766062794551,"q2xOTeUpaHfl":678002301458,"2TCX7MXyDJhI":470138789886,"vnzEm7Qk2wFZ":539432476329,"7KJEcE3BbFv2":159485385541,"6ompQ2kSsLHt":439923792616,"HlBj2TOThQYH":709648050376,"ouXVRNlaDCds":95427984165,"4AU7bSup7VxF":408229583403,"SJdMVjD2OWug":846523059529,"4xHMoaf2Fxhx":586123228674,"V1KYjUNmXyEC":593669426032,"6KyiHQVYbwy8":222757853495,"NXOjYwrfrtoa":292841035343,"cBUPC8zC9hzZ":298185360836,"4ts3VmZgyKIp":518896590259,"dXbsClLjppon":625485705918,"pHA3Cu4KrjcA":711328311448,"G4FFFf5auOua":909402411802,"1V02g9FVDDEe":747160401148,"M7bnVb16SLZw":722220372274,"Zfu0bPLD6G0V":186198503367,"8BkgkMcuYH50":34776110427,"RsPz7ZLbd3EQ":451776272734,"6tGcjGCcPeal":725626882832,"yrBvMwYWnGtT":280978201208,"S2oKpcAoeM6L":255089983754,"lfMr759538cc":587815755159,"LovjohtkciMO":931563243067,"HDtTR0XHH7N9":438299252000,"mG0SIvCzA1yE":800346515142,"nQCf7gSGeDCl":806137776259,"jB9e2FDHC3be":944431611425,"M93FaxJ8KIL7":708196200871,"RA0VPXJdK6Lq":713294452037,"7YyQp0iZctKz":719370520301,"cbEa4p9qA8wd":240370687173,"rwLlk9eVJ07o":89956361399,"ykGDP9FuPJKw":497093653627,"kOx7X1sks8Rv":12376938155,"ZwNyrl1U4wB6":189826455406,"I2Ay5jhRz7dj":191685742602,"Mok1fcprvKoK":587288899778,"jNMcSabHhhtJ":44095002082,"FHdtt6Gyvi72":556499241660,"f5NeAmwn9vqD":993399779600,"1hFVGkB8pdKS":327810167345,"CoRUomEjYJfj":936282835798,"Z8x1uAzXFkhu":932168670514,"vErOnYer0ZSp":575648912121,"uVc1FBH2QDir":900534092746,"eaBbLnldmVc6":529873430894,"ofn01kliXgEl":547076944090,"YwoLYYXcOQO3":58937055800,"zUrIpNIFVnCQ":986258028460,"4JulzKmjgAkQ":196748695079,"abd3qZxuUPWb":14540638731,"8HwR7VV0vt7L":21978274600,"tlEeQ8jC3ECy":438099957680,"BYRCcQdCb6s2":147147734049,"AFZgylwBN4jH":766757500243,"ohfqwmN8qPtA":239025969508,"ixZGmuRSOIkS":757386233758,"wQi7hKmSkdT2":179753312862,"4d3eyOovsia3":578101141985,"3zS2uzCyksSl":696107701514,"iJ392vYXFe9X":55590648115,"6kAbC2vcCdlv":319017997830,"txLc7bTat8PH":148477964873,"OnbwR7FcvZ1c":856886452987,"Wa7fQo8yRSjQ":958148881903,"S6ELhs8UIlKz":105249453456,"M5QHnXmF03AZ":394782517070,"txnZHRaBXpWS":498269320962,"BFHwvurL2HbP":182677964216,"VCWU3wZiEbhI":385630825840,"kYd7BjKlWNZi":288328759078,"hlrikXu3Ey1o":652768337043,"YFDx9qumTjmT":229071818656,"lEuklRA6O5kZ":594217410634,"1M0cSiwv1qzp":964887883763,"r0bP6J8bL7MS":420224222058,"hHFRzNYEJtwh":649413131290,"OWnSFNqmkvTt":15954600831,"SGsLcaxoDqlF":483414598429,"JfcV4sB6gYHL":821588166503,"AfNtL5KnnJQE":426654644296,"UzjuhEDUrisQ":582084558087,"wXr5JhACRXPj":702571066584,"SI6L23hXGxgJ":635181333547,"6y8ScxHvYZSQ":77470084634,"XDM8rbemzMws":702021536322,"sD2Ac5FT2IkI":254206631257,"S6xbe0SF8IZo":872648853466,"79TMgBDXaovh":454016472747,"QtieJMneJdwC":196790460471,"1zPUNm0C6IXw":336393531560,"3bsZx9zBBFL1":918696307429,"3R1PLpmVuGr7":188541744436,"Dv9uYYUY8kqG":828169376936,"4Girkd4mr1G2":831314327584,"S2Xz0ENtdAYA":19579787376,"NRiiqOhxzijI":255288682521,"2jPTbzqFkH5s":418295863681,"GYPDFU3cEauM":609396390343,"9bBKlQn0fTrS":806155637776,"DiGY3pMidnV7":457510196258,"XtSXik6FyjGr":302242929055,"Q9PNF9gT7kq7":505976045102,"06DrzQ110it9":345393723062,"MwxUqM1ybvSP":40412510056,"z95Ol0vrjgvt":288044900186,"ljJtt3J5eeFW":185020525497,"NZmw29mjKZvj":184504033605,"TumIC0HdhgEt":506889276083,"e636ckpL5eQA":967713749772,"D3KIfzlrJEfP":942041092759,"4irElBZR1AJk":344991125743,"jc2gHsYccbj0":174766331182,"FTfzcOIF2ypc":261299781533,"DsqyrO30EsCK":86504492903,"0FWzwQ1deNjB":774612042103,"JFABHgVqB5vi":259152151283,"5FmjHmZng3PJ":643524813761,"D06E6kUfpVLo":586042138841,"CTzBeiFggdkd":257124049191,"REDu9FJcLIAN":822914996013,"Sa4Tmt1ku9oZ":112938541009,"JhJo3mmUHVgt":390969693453,"u6x1sTtxKn4k":730629727875,"FrjUXTVgvYgj":282901286035,"SQEriRvAyeTm":68220491448,"ermXDdloqqra":800178814900,"9WZ3tY0pWWdL":637629458924,"Tbqv9BU3uffd":901175524046,"w6E7l9zBcOhu":731741828894,"y7y5Jo2PaS15":410343670080,"9ynaowXxslEa":710602717095,"LKS0t1zRrczo":375615670787,"uTXh2wZWuUFm":685921969971,"4vzLrdf0FuV4":432700716970,"TZI3tWpWMU45":982792311206,"7BMgdezmHl2L":379940260118,"SQs9e0ind5kt":608491124737,"gep3P6HYJ2k8":73655367947,"PXuqyedhCnZW":896345561592,"ySriluplxuN9":138578184075,"llNH7G5gbx2L":374975858843,"QUNnIBlguZEY":136028561256,"sgswr73zxD4X":723386516792,"MpYEMo3bE2xG":946114316643,"BRuBiTB55HUO":661403242496,"F8ay2htlhfMU":922651276702,"U7PoWgL73uyP":782982462297,"zfRiQ5lh6Bzo":251112573367,"vAig62K66YVX":982447165808,"8MYjtHxB1pOY":151150413302,"LrStZzvFFJ9U":549787701477,"6J0H1BcldgRO":828593730791,"DOMEt4QQgVyC":619557544646,"6y1vZIHIJmSG":506931790380,"3qbRcxM9IBt4":261245655430,"bRr0DuH0EhI7":406563998192,"nfLhoOMpYapk":958207770806,"FoqBSlKWjLRa":669154596407,"JmT9m5lpJ3nG":823266496735,"zDpccg6BWWKc":932205197413,"vSu3hQfaZzPU":205404193450,"F27UMKK5hwsp":739113221021,"iuGmri7LvRqH":609600981584,"c9vIPjJKdqMz":860900339385,"rXgjRFcryaFO":643122127824,"6lPGcc01vol3":317450570874,"qgbYskrHgqbc":396515586091,"FpTYWcYio5Hi":578247184580,"ngtIYDl3aiRn":821299160067,"ghSIQEE98wxq":789714598182,"BI7SObtZaqtJ":617737667992,"eNn4bQB6CdDJ":871668204454,"Q921n8IHohUT":813555418626,"tG10YmfRRoGI":528204609120,"3S4rdjEBRr1J":384619742460,"Ie8paripOxtR":283232806808,"wFfPDj39Zs9j":23043051221,"zNVsSzBmXJ4c":4737197047,"jR53v7nXrm1t":521181120280,"qcrrv4JAzh9G":782048979053,"zHEiRfbdV2Zi":603317202994,"0a8Iw8yVhq7T":643798303017,"TjwJwLc5yg43":819971379213,"788pZE7GYzxI":850009819435,"YIQL8TZsZhC0":282981242227,"46uka7CeX6hf":556295284429,"t2pKPytoRwpS":885629983139,"0MLDRpwnVJqd":942894825631,"GUAeplTfmKd1":743118068881,"WJs7LBrQQdH8":882017113394,"fRxhzDkRZbrU":595141375262,"upjDRKp4mvq5":146471440655,"yicDGXL0N4FF":64987848200,"68Qw3XEoIxUY":886987213022,"e7uM83bXcO90":692777871552,"WVw2L0aePMam":655176863560,"jk4Tddk0n5Ql":396237523191,"EMbUsIKYWhW1":463853826894,"aPyWZ3kbA5wh":435336808252,"ws9Vdm37durJ":871656544731,"GgEH07sUrrss":301982048831,"6XaZjCih4g4C":681869321255,"YAaOiVSW95VE":40356861573,"oZpfhpwkCT2W":793131430785,"WT6tAhkjg7xT":52219948905,"yAehEPSFVgn1":791998856721,"APLanDCSkxtL":871073884785,"a2V7HgOz1rsS":428446070959,"Oqiy4RHBPZVk":568049217357,"ftQCAiLU2F7B":941345076450,"uvSssRkcnu1r":463093580552,"XpnaxfxEMtbB":816941809539,"vW464eCVYP5F":567781845521,"fENzEPPlnn8b":849422201174,"EFXKDOYfhUbz":534111510532,"SlWfMteBI06W":705458966909,"JKztrSETOU6Z":143288192351,"L5zQjZ1oWvA8":725229096974,"irHnwo5aVfSy":401767714171,"IYFzOcbaq5eI":576280554066,"S2Rj2u8Mv4Vc":167491521663,"FxQN5TfTXbI1":544050884263,"KYdsXy7jkvOp":131602324650,"gtFAFFCcNdsh":579576929563,"cTOQ49htqxyb":372197038850,"d5FFx5RX1oEv":778946789424,"IllZ4wmedPNd":672716004990,"whLU0hHUTY0M":220043109717,"cSZ0JCwGDnz1":849494561693,"wY81DPFVQnvr":703196669723,"7ZXFyLMrQlJ1":245637628859,"YwPf4t3VKCgS":928631517474,"8M9P6wAVg1OM":656491234863,"FvcTnpfdSzvA":777279017164,"wSKpEDs4uY8b":406034752471,"38ST64Aa7BaN":191929155700,"dSX8k8sCkxOv":885874553739,"n75zole5p62Z":148479350550,"Xb2Yw1g3Spg6":325149644188,"bHCEoCJBJVJB":941122027203,"LTkSbH25Y5Jo":255786284787,"Gti4sRgnMOgH":920337782770,"mGqX7OUs8HPG":886571869139,"hNo3ZLhlFlZS":325648132938,"Nb2eg4RIPAwi":26492406794,"Pmbb757MilSf":424074355139,"e0Qapvy0XBQx":556622381888,"HVxOqxiYC0Qy":252782805822,"M35YbEi1zKuZ":364073474957,"5ulhiithTQCK":729283952595,"cQvKE3ZXUyFt":890888761171,"sKm4UjJ1cUyD":449964407108,"NUCqBHCBaTIe":187057554315,"7sn9YkDCgb4m":564665428922,"hPhA8rkiXvcf":255476811169,"3Uw8RpErELz8":289565538013,"fy0QfveNWCpE":984276760139,"hINBB4t6cfpO":900420268687,"FVs7qymPi0R6":589774216310,"EOxXn70cyNTX":642374253579,"t7FYGhVI8mM3":117090465935,"FgsvSZMSJ7YU":892074417922,"MuF8fsAmKsoG":766331487361,"LqYWQ3KZrTAL":28854025972,"Gx4eyuGeurwJ":781464589628,"OX0orKesXUWp":138599268120,"f09DPRTK0roe":636956397221,"FeJejydmCZpb":876738299146,"WD1NtdtQGQMh":750033714289,"9EMjOk3bKlmw":855672762802,"7A8509nmb31s":842401039908,"mvU6RZKwUcPB":952360895281,"7SLabDJmToug":494449644972,"3paoHoOaGXb3":852692140660,"ogscjJEMiGaA":718365336958,"Hr8NvrthLusp":436845487929,"2m6sPERbJdWz":873924142169,"IFh4ihM5ex88":901318542705,"DJnw5Irp8clo":89454903942,"XzbjFvSNTK0C":735026013016,"24B6eqHaxMYe":423752064327,"5ufeHTALAxFC":439838496963,"fPIQ84MwmXIH":63894028822,"yqJbtWs8Ezqg":154400087827,"Fy421yVWCfAl":133452296559,"beJ4x0h5tJKN":351196652080,"GYsxytmGYNVj":699903378739,"WhxXygDNyu9N":200084296152,"w7yeWcUI9RxQ":361577666681,"ktkZJmOlBs5t":250120504055,"EuYrZKBF5itq":435853626339,"wqDLd7qtlE4u":975247176078,"mEFf4Ns7DGnG":245998185275,"xa5h7zZy4LLz":481192729153,"hT5gNGGgEMRW":483673154831,"KWHZZJtT59zW":194685184359,"wKMspJVWkgLq":177655803845,"HTjhSp0Wv64N":365116251300,"XcTDSmAWfpZb":649760137394,"ZIiGosAq34LN":134559568376,"BAjyb9suq9Ei":139102253422,"h4he5NxIXuVR":615757530827,"QH7WrYk4NlEy":28340581644,"zK4jcSP1dLxz":166031411411,"00h1MslHIIS5":581682464340,"8bJVrIyPd9dh":802702857701,"7kLMnbhTCtJR":520079166572,"BXolwIFSzUcC":504734804264,"Ua0ZSRN9YCUl":718910457232,"Igbo1HIq6VYZ":650544982964,"PK5A9VyxJO6B":195885632489,"FrXdM5KVpIAd":905392647209,"6JQ2knfbih47":182148286317,"GhPt81ufxgyQ":71275510791,"Tn9103evXBYy":217158005756,"vGLVJaRlJWbZ":453590557287,"3bzvyI51lHaN":57388827897,"tF5J0Woq21OF":515523973101,"s7lQojCLhULY":249302788609,"dLPOtfohcbIm":373536724271,"sjxgVVUbyBKr":456011213406,"WuqSFJeWHX7G":327767281924,"wFJBE7sz1Tym":636580523613,"iAs2rd7bVCjy":854675628069,"n1Cr8DM7KlhJ":665988069292,"cRXgfQFRExIk":648937527095,"Xag2v3ptzoJ7":369164289767,"9X4X4QOgf88Y":474349645693,"bKySQPVQocrM":172705132519,"cqhccmfSh79H":834519730852,"FLoLFgefs7gH":700693037840,"5IoEdDtjG9C9":635299561581,"YCgR41wDLF8O":474412917122,"OmYuyqt6CdmG":367989418151,"GwrvS1TqG7YW":691150605482,"zOqBmYDjiTrZ":254780064429,"Oh1sSshY0RXC":528813978201,"uv9foLJWmQrh":940301481437,"fZe6va5tzdNa":744015264850,"m5c9lXhunKnl":92493744676,"zGO0J8oxxNcM":970857763989,"Ri1VLBEJnaTR":46252152593,"V1MpNAVzdZM6":688176919810,"8rqebsgGxLCQ":537491305930,"bjtUUD311p7r":26664794668,"8BIJQ3I9uJBK":210429186269,"i2BDLBRNaJ4p":760667541709,"ZDPdygzCPVWB":176415205730,"lPyU7iP7FYjK":445293901023,"DePCpCbZoxFd":395428827935,"L122o4T1E0Cj":169047936072,"FzlCRtMvRigu":506622832167,"nReIdSUE7GLx":303924774012,"T2VQCGebvMox":328608946736,"U4kSvQUubbez":693046611457,"yYKizDcchGtI":607758671011,"Szms9wiYhcyv":255113926708,"s9uaTlaEHGYb":897740990587,"crtPuDfMkURn":735592869506,"xnfhcMbNpPay":926747318581,"xb8Xm2LE269t":169277548524,"OUCN3GkJEmqJ":598662109615,"XfNNps6pC1KY":907277356233,"15k9ESvOuEYv":803830650610,"jyvKTg4Lxi1B":708946209630,"eg3aBentUsyO":43761844032,"8E9ozEtwULzh":763790387513,"xgbQpapOe2jb":819916611064,"gSNWfePcvzYv":617478096308,"6E3QmNvuZmTN":516205615207,"L6pcfqh1lF8T":543699119288,"cvv2GTIx8yJg":784940280724,"TQcZzPV3dTEc":242668189414,"EZDwgqTS13lB":30405844650,"IupdzwMq8QD2":868893547129,"cLBKs3dCOYa6":648965149135,"zjApaN0Q9IWp":26969557104,"YfGpkCRTAR3O":722972097544,"tBraGjF89f8o":872896014635,"T1AzyVUgnfQZ":394636885870,"AR8T1Z2L0hQm":112678527832,"AJsT8jeNwkdv":905298803709,"cpbbE8vbOngO":422661210547,"bWQ6iQzeBcLc":521515745395,"J346BgX2jRQ3":362864187977,"o4XnQznnddoC":625777936627,"fSBj20qOHYFA":852807748557,"F4g4vzyploOF":888502139759,"kKz52FTJX4at":872073496507,"hWmPrKISiw9w":108476177882,"ZpNMXCSem69b":485508564655,"brpKWTaDch8o":325902366626,"1O32H3rJvaFt":862622338835,"Wta2ZYgyjErt":823269138034,"4vkFKKTjz7ne":441923616993,"T1gnGBp9M0BV":682535949524,"nLUfHgnVku1N":622397047146,"oLATalYd0FKp":493083351575,"5zJG7EmqKdP6":207016011883,"6InhyYxFSvIv":463998992164,"05zj8LyewlG9":197213409443,"WQGtZXFhZWLq":293194236023,"PNBqX47dW31y":346756774922,"4ffW56q4imbG":496217226949,"S7VV9YAGQQcC":193807044169,"Fh1eNcdDozv7":719698240799,"owLN125SN8Xs":433742743302,"w9M3arpTsxOw":28670866092,"tDHsDaNlrdwa":682837409517,"mlgiF6EjHqkj":206191710458,"zLG3Pn2sKcFD":110274962792,"5kGDjEoUDfC1":33625485039,"QEZHpEQtUsN5":700971753819,"FtcPTDZEY8df":627486862742,"cg9ej1XclMGZ":536178177366,"tgxEpsuzYkSP":912247199105,"KJJFzDO5oIti":949230779105,"ITtO5MMAEu50":282941419722,"FtsuAvn3dF4d":638012057992,"kX8bUAe2ChRL":885921827876,"lTKaVZ4yiME4":867414806240,"L8PXjqWfJ5dR":230977787988,"O8tYcUYeewps":823754647286,"6eLEbpiCHeKe":913554524927,"znGsuiVr4d1B":224814570135,"YtuzgUVYQLbN":11979734248,"t5sZbVOEZA1a":414922746634,"M1jdozsTCEww":904175723986,"kOrSnfXFNqkN":384938801185,"DigJz4TDn6v4":774764075087,"lS17QVdHUP6K":283693337074,"ZU6Bqmosxppj":663348800445,"KkXgAb4uh9aH":578057809108,"vNdY7rOJep3j":826709152310,"OJmhKHs2ST82":582779352838,"JQT4ofDnvjCy":89474063857,"LLV76428UUhW":850921088886,"psW8mZgB1pdd":98672631808,"vMAu6YKupMu5":53724009276,"ROQQ3pJmfxJB":320745697262,"xTn4sYM01cc2":422595999602,"MvpT37rH3e4C":576640536388,"lePfswjHuWFO":992990980815,"7VIOoIdQdMNp":501088361440,"x43SmJzOCPzl":445723110982,"p0oweNroaJZJ":467011946203,"310iNI3EDWuB":99572508700,"FPmIyXPL6gCN":526841725167,"PcFDI3t9xpMC":70904861855,"Woy85Wj7SYym":649176081028,"tt4KKzzhNDyD":213762516196,"7PhO9YvaLETn":894394330684,"ZGuV6CU4kofd":722599903420,"mhgcjUtyJPlG":487732677294,"kh65D1Sp6ZCi":2197629997,"HKppp50HgSwt":503252278230,"6FwWsw3JztUJ":583725881160,"WdU0NMSUWfS4":522821216710,"tpz8IdPeZLB2":436642453003,"l0foEZNozPWd":241911806619,"p9yaG40MSzG5":614234305156,"GIHs3lqsCj0b":399094741238,"K2eOo9eRYuV7":416860255128,"0kstalotw6Du":329642905399,"SfEytOE10a1B":645179553184,"bDhdeuq8VwEn":399746612,"YMUTbOsseaHu":740019371569,"aEmW3AS5Kg14":516750256134,"14I1fX1Qrep3":629332129514,"itBTnXfy7FMf":733230666594,"tWD1CSm0Oi2P":50090218943,"1Jlms1L24mAh":753928910272,"8JQ4xh9Gsv32":308631134358,"1FhBPMp8DLW5":732049787648,"pHVVJzNeMKXC":148174676923,"B4WjpIvgas2E":865281471833,"jGpGueMyEV9J":613413316756,"BawPSEJTpIVe":942322116249,"NrUO9fhtwVCp":441902742172,"mFDKoL2OJPTZ":805910804020,"7pUhDkuy3h1I":190878566095,"k725ZKd8cGLN":707840129348,"SarMyevtTYl7":539970111497,"3K6DSaYAWrsr":525238192781,"DQSHNbKlTIMC":170846989551,"Y6DFOif0rqLa":797898297651,"a3MPRYNT0rVu":740022621101,"FSjHLnXzMTC3":506679429075,"Y6wb305YjkJv":911550745365,"pvuf7La1epY3":318625061130,"kKlvl7HInkqX":12206216894,"SnaFa2e5Phhz":298429078563,"sYwrVVH2dx13":146335585172,"RwB7EGZw6Wtn":679287630546,"kmWOw4fSXZVg":379762820157,"DcLUM2LPKJ0M":442107018997,"gXWIzcYY6bow":663820884989,"O3RUC6s6X11Q":73497941841,"PdjufhIoZt6O":771551747301,"yLVIh5UFAyLX":174091523524,"3nJR30SVKr7z":581314645898,"IcQGpprC67VI":830496797002,"Cwwe85AOCnkN":71395105853,"9ZHe4khQHBqA":373214534646,"K9YijXMpLt3B":357530373147,"p4FyOVPqFvAZ":111198944454,"8TZE1tDxKEe2":976320660622,"2heGU9pOyVZ1":784603586089,"PDu2hd65ZGfr":384522190545,"7JhvdtkmbNaE":535425987874,"qLxP8hdNZi7U":657349075432,"u3L4ocUJq6Uc":831741470237,"UwduL7RfdwUo":496775213854,"N9sHthFmdboQ":507588902369,"rE2uprcHB8vQ":75657124520,"E8Q4Dj10taLG":925106975287,"HUZKYC9cc33v":66411066894,"NPLCI2VTvVNZ":578616789665,"t5QIeF686OXJ":396496296251,"CcJQSMj9i4Me":300119869769,"nLxSYDbaYMN7":995789630602,"eDAmXX8EAOJw":838671346613,"4MnbnuG4QIEb":362771549727,"zVcYJbbUSZps":729108643896,"Yp4tKjMMTFRd":192828027943,"15OGbv9l73j9":306017952769,"C3QvxOf6I8EW":484829405932,"1SWCW7fwGoGN":953413201518,"jP99WYCojlcp":101508351888,"LorfOwhANFPL":607245661120,"zVmJBaX1d8gH":240722698406,"V4hPvvv1sPJr":431356221312,"a7zeCDu2zV41":530240911808,"tDfmf4BNeAFV":386346159562,"lMrdbs9pbqSM":830249826931,"KoDyKU8CcIAa":655301330807,"IZwnihtJyhIl":144037175233,"Fw1AL6nor8TB":369522154156,"wE9PpkdL2FW3":315537960440,"wGJ812yL93GA":157242298666,"Iq8vC9X9MVKi":668005260593,"95g6NRO7ySwk":292716104137,"4dHRqtLYfWBE":579113024887,"bswi3gwgoTzH":93275650937,"3JwL4RRUXI7S":823750537238,"gxDYheeugig2":178369332980,"QaBnCvVvReRm":34287685229,"5MBwgTtv3HDc":561115876780,"jqmsN5t8fySh":217225369074,"gqA60CWk4Djz":634002777977,"1hxcW64GQkIB":145030652769,"r0opnW3keZ70":963454675483,"7SX7Vyu6VEi0":508104705001,"ly4KYEfx17Kt":381733446026,"ioHL1zz6ELd8":135752175763,"wQJ2zAzwP9M2":35693862607,"bAuSP4Vo7r80":183508437333,"dBSGLeUKwa6C":191849948777,"MxNbIrYbkvVi":425744692766,"87KNLGypBwOC":871602405911,"SF0dmu32VpEl":753303967742,"Ooho61aUAOy4":937439093908,"5vh9kt2AEZgm":952137258971,"cD048CgKIMPj":727505951199,"no3Lb9ybBAP6":399230892230,"bTR49bpZFAsF":692341959477,"hRsWGoUJh9wm":778265023312,"Pm8wAopwa5Fm":488062133956,"TbslvZEnFZet":386109479535,"Yj4lXIR4FvDU":298319097851,"XvKE3AEjXBYO":306678511351,"Kzp7cJvk2ovB":173152490497,"k1hUnS5L86fB":865758737787,"uo5duKKmth34":604970598609,"BVgQ7ORgywGj":818377707812,"HOWURAOthUxv":86314877145,"jqIzTb2LvI8r":51178878141,"B9UZpMJqLOsP":417804059085,"5CLYEdK4pn2k":289198922532,"P7Or2MMshyFh":964645105423,"Y3b5TMq49kOU":205299739026,"odovE1YJWfeR":27143872529,"3Vpq3lHwsfMK":536892179386,"f4Tp5R6Of0Lm":427430543415,"aQ0giOLtaPlU":497039991153,"o5WmZHWasDKs":894628412986,"pUhtq39BmAB2":749264195193,"zatqlX3Fygay":100888766204,"O1on8hm6ilGV":356682125068,"oiKcQJ7imm8Z":622717191502,"ZIaNU6xKnX8i":947384110713,"c5HApZNxubTz":348194601828,"KdWR0uJFSgni":578674053325,"3WwXAHoFCfzo":627856014279,"erjQZl9WlJhe":589368246469,"BHfothr1x3Oz":169922211239,"NVDGFFp8PDz4":668798816121,"jKgEyeURC2VG":496318518187,"7Zvfc7Z6gjCk":764590558086,"FbWaGPiPfO8u":966727672906,"AVFLfJSwE7UA":367100869182,"YWzW2rYCYg1o":107811145775,"NYuRXICYydqH":533162437431,"DzhGeMQ7UE1U":974281590479,"dr9lw96up0j4":592709221059,"ASw59F8NKFjC":573494371180,"0dfF1B6pJdpA":82593942419,"5YLLeWGMcH0S":758251076377,"qEEOeImo2tpY":698103851080,"kXZt0ma9UkXF":821554906936,"YLbwQFNKbArb":16891298803,"tz7GoiVr3Hln":611122044757,"FMkfJpzeTgJQ":764226103552,"t9YI7Qc6Ca7a":46199324440,"0ITNnATpJRav":233451229986,"Ylq4qwAJtSsO":997846291700,"BEGJVOWExMzQ":239861657323,"rIpenIT6W3Cq":380420675096,"7aObGgsbWzuo":190291690166,"2eFGltVJlMvr":60612110325,"8OkFy47ZdZzW":700652054363,"RnxcuuyeuUE7":93470368642,"EOXvhWFOpqNt":923072360244,"5F2BOFecGIEI":651533634421,"pZco7oPsQbfV":596276092191,"0kz4rsWFS0oh":428579017458,"nC9rAdO068aw":357180557083,"tiwSgiDYNbAB":189273107334,"lfBlt6g8klCP":586124294357,"W0cITfr6yNij":605717759623,"yyl2VF9zQzck":489302913181,"lzdZF3f0Yny5":975510782783,"BtC1arIGViT8":665521407500,"4bD2U2kGngaV":342953103441,"Fw7yyZH4pWA9":209516854909,"eYIDIrmCuDcU":574418391141,"40sK4T0oDwXw":412542548409,"xuWxurRt2mcl":273928018222,"C2RO91auX6UL":136412417869,"YX3lqCOXRBCf":328375260026,"AIGFbCWwaT48":280411652605,"9PShzDyXFV58":857616775210,"zG217uQKTKOa":105404459620,"uJGpIrYpa46p":863750986746,"RNawhwaRByoc":241431524757,"m9XiEjDk8fuR":259476893673,"w1tDG3XoOFG6":251603837252,"hiab2YruOtCl":66938956000,"IVaR5nwoUUjL":980247858198,"Fwrooitd9sZC":551883909159,"jGMmOPQGQDKS":85330676436,"7hheLHADRxh0":761713530630,"Vevw6VGSFZZJ":267006444593,"WJ25ouWUMLQp":607846920927,"nHHGzAI0WTMw":488702101791,"gjAkOGhoXWlz":174568126186,"aEZBUiwT0Rab":499865458032,"JscDAMEJTS9O":674603476193,"Mvn4KV6u2yu7":998666099918,"1sWslJ5ABIA4":706926193487,"7l0YSDqDmVyt":144688316876,"hX5wnZ9o3TZ5":565155002428,"g7RT0Lt3Iknq":798830078688,"q5rsR6tG6yCW":708840948395,"DiC4ItHwxVoI":508220726364,"BiClro1OMDmr":374933715997,"XKwzZbljz4kd":179678750034,"b9ufwDRzKBeZ":689584141397,"nppYVty3V6l5":839367213713,"kaevnyrK7hRW":437309039252,"KfrRkjg4Gyhz":314830931623,"GwPlMWkfdF39":48566540040,"8mT36qz1Kmk9":493184846660,"Md242MAPBOnK":68510658596,"pJHRY9iBKoJP":818709298922,"YWqWLWGrFoSb":331970152161,"RtPp27mEJE7o":172362536033,"A61VFynKd7Q4":833055645200,"MpMRr5wubp4R":984111596593,"DabWVHncV43E":339313677703,"0v2gnjezJMSS":241893515608,"nzMpG7d5ib0x":121016490032,"W6eRAEABNFE3":116194039356,"aVakB8H3vJfC":663926364455,"8NsAMpB3RAZX":728918260073,"6f49Yx32Mp6P":897698214504,"ZpRskaZSziqf":599131176818,"ieG95CYPuZCq":644918209715,"Eqnc3VQDnJKu":361586641881,"csvQCHMfSsTI":799555524834,"yVKDj4pvDq1c":166032163578,"sLijZDVZORgE":397098154559,"36U0yq85ZSHM":931345740010,"dZWdZu1vP980":169708056806,"9BaCzLuIdqCV":73642643910,"y2oAYTrRcTJ4":201630624257,"rAAWy8Geqfsy":740082186311,"IqRxeFqzmfeo":697257239701,"uHy7pKhiGlJU":769572842888,"PuZQ2aAzQJQf":618287450516,"upspR5joffqq":292717096583,"k03cqmbBflo9":10027702548,"IyCnILNEVKwt":722263225862,"txeamnO0Tbfq":816933034834,"2mQDNwTUANmu":230993548862,"aoKSpDYO5U5I":439211715800,"jWAz7TPQHX7N":669623720649,"XvjgYzV3JKrh":903464771713,"ZPBRVaX5RSSk":238151311064,"239yenKSZLfS":184903478809,"if7xpAhT09Qf":387835576193,"AiQunPKSqs0g":168454471304,"gLTF02HJlayO":670669906656,"nezKjgT7Snho":321344580430,"pVsuYn7opDOz":84846794186,"kfndqM40j9M6":784217654792,"EzPZ2673jZKQ":115818844077,"1ahFloeDyBgn":64217367523,"9pA2yqYC8g8o":589674454866,"ee1qcA5We0v0":730590072328,"0cILCF1k5fzi":376499600253,"xTfLKZKasc4g":212748705649,"79kHpfMT0ss4":914046709573,"xKCgPOhceJEE":68298311686,"WbrMoPirniAF":534649532114,"sT9V0q9UXdIy":981874495458,"m2dWxOH0TyI1":731992322382,"7CdFhdqAT26X":645523361131,"cXgPfL1XUqsB":735306936059,"H7TttUQLMpSg":389386501361,"uhsAQ5rmeKt1":152851234560,"VtMxWHEXKlmD":233102876474,"aZRFjFoW6RpU":10031247846,"MuYpWQTkwEyM":892443302112,"5ToJryqLQG04":454349181821,"W4otKV6lOyUN":912133244011,"wPy2MCWFSviz":609507518442,"XoD9Uhd2dV9b":462341536753,"6hvo1hpq1HLN":352284980613,"IHlbAmoPByaZ":547723375142,"UBiub0BUmzCx":785700179592,"isV1au1YIghn":87807273156,"W66YIyzLhMJw":242184265165,"wCfP1BRpYkZO":871734847700,"FcfaAPuryO0H":547170606526,"Cbghk2qfRk2l":834455616370,"qdL7XTDNkTzW":714522315911,"k0CD8QAhQsjI":979017145967,"Cs5rMog32RjE":483346004593,"p4jGLTwGHIUc":942670999398,"i5G9XYXem4fU":878596092015,"od8CeiRNm8NV":781150450606,"K9wxtQCP2U2w":92373077935,"L2wQhSQVLyjo":491269219605,"iaTqXiKuY8px":857773645289,"QEF3DKQLWi4J":509534294639,"FZQvzYVvThJQ":988482108467,"hwX1E1E2Dcnm":225507420670,"dfL9cUlk7bxm":81115059484,"xFCKlT6wtOs1":147249131227,"IdxzfBfwOipk":122860647353,"RptdJhIjMySi":438981647071,"ry7GKlxPYi64":382269286954,"1E27TfiRucKQ":86581306089,"c9W6USJBKxQY":850920142784,"W6eeCH8WEUKl":646749908223,"JKTRYIfBnYPB":338048381143,"Vz2Rs7VmFrqE":297529144678,"Lw2fAVBGFWD6":572959722735,"ZxkgexTSeyPA":564443100945,"bO3AlqJHaaWi":104693876088,"p2SxcKG45c2k":128870987719,"q5BHCso0e8Up":322995294465,"P3LreSzBvS58":909818311194,"ysgYrkKaTiZC":393021246216,"RsWdkm6N9LBU":851209746692,"b7tfeANg4fjP":953301232113,"SBqjZswSGxoj":255650556532,"A3f0eGd2aDxN":111423723193,"eTSv6yv4zdC4":396935237944,"cC6QQ8FmdZN4":340679784825,"6apAEcUz6AyO":988330282053,"9wCtPLs0idEo":602064386420,"SHS4r31TMdFL":69737541572,"pOsmIglhYV1Y":212918100482,"bDFmzespoHuJ":149897771986,"R350BoJA9AP8":773800701308,"uttX8XWcZ0Y5":158869384185,"WkmtVM3Y2uMF":554602892667,"aZgmN9GewejF":486079877930,"ESQSTNxhggA8":925794451916,"bq5ZE6Ff79ST":482914430525,"ehg7YCtZsr2N":833540702115,"1O7BKgXiFSC1":908042520418,"vboB4Le1f9v3":612806198454,"spW0G293Bmp5":931080922986,"RTP3QFZJwfB2":951768468076,"YxRpXPZH549Z":380228982883,"842XP2YqT5cR":964014459490,"xIPmH11lJraP":81731812735,"byu3zX3Jwy4R":28779673867,"rybLj5GtIUy7":54808928373,"nytBF8y1LBPx":805203953091,"SYcnRlir5Q3L":873138453269,"qoy0QtXoYiiX":356401493485,"W72wK8gXCKT1":573170749572,"Zs7jf9SlkpMR":561738032135,"nQMJAk29w0cI":796496228527,"p30BWY5jMhPJ":190680053023,"yjSLBKRDMUQ0":977917381462,"Zst1AS1qF6RU":959830157756,"sMgCyo2ulbZf":345357445160,"zrV67iM0davc":971396361463,"5XZq99uQiIB9":999668919397,"BccgKG12JO6P":664349311226,"rFCkP6hicWWP":867542466952,"zAZNDlnNcIK8":152587045357,"UK8TWzzXbjNN":22382415654,"bgbq23d9kSPj":358237805473,"Fhw7bOZkjTcQ":64993763193,"UXeoCotnY98m":954313011390,"yKg1f4qaigDE":553221808024,"LsvQCfREPfkd":812642834111,"ZxoX5C0MYFWY":954017458919,"izptMKXCpLoM":57465119913,"ku6XDMJ5VhBk":39569402527,"IFadBFIXN5jg":105727799112,"7SGQKf7DspUh":270656629075,"TA8agbznrff3":590773020306,"pNIPmJUvDDXd":917602811487,"she81i2G1IJe":838992021321,"lYW6NbuPYJrT":227403990948,"xBKIy4Wsk3uc":393772444802,"wfJzIgZwnbS5":605604984803,"jCkY3by4P9VM":918021698093,"eXvisRqz4Yn7":142996234241,"VoVECgWLAjof":850626565501,"gBKbBZ1lalzX":539862552054,"wPrfmynAPwZk":362402541112,"jHahh44lPdYu":784951229734,"fxS12oPr3TX0":387275147049,"nFLGmaOqvmoz":588036241422,"RVlPktzqKpTr":374895150578,"jM5i98MSffdq":312842370818,"OQo3hlLFz69C":925124354390,"VceSVd6YLjP9":418754763780,"bAoYLL6x7yfU":991732407161,"7M95r6rObzEW":478890570897,"HumcqTADz5Z6":592783188417,"ldYjNgXZzJ3h":510745049367,"6Kxl6BvXRUTZ":80308629980,"YrUqZ09JYGqa":522066633591,"4bHl4XMAJxDr":427156621302,"w2o6CJiLhS6B":176986030424,"HSfaLuyW6NVk":541087014473,"UaUSgYfGICbl":988424377229,"D3BfaiJaWIuU":188521280406,"BnQxzLWcGXE8":853984779815,"9mvnYmlTG2u3":460660394142,"idCzBR4aoaPp":3672760718,"CbD2CVr8IOOf":652632427268,"GvI4eDE4E7zF":939869437283,"OBg3r2Vu9kHL":871458280740,"YTQFiDUGlVcw":398645971663,"GPma0rRXLJZT":489682668155,"xfmeCXjN0EjD":388150296823,"VN7VBfoGYuUQ":275571748340,"1l3f5Njj0mNC":405646654445,"f8LLapJ7W2Z0":516324196006,"JaLcLSYevLpw":745800186553,"ppUSVDrW4DsG":677936673270,"lBb5DxEpXYCm":146015235294,"61W58BJk8fNw":933109425304,"bRdcApdMeCGD":251822671282,"D6fuxUJOhLut":949566713972,"cx0HFYgBF8Bz":727417463146,"Gh6hYR1I4XPj":540799998994,"RY49zYPAeoNu":314036298901,"VCBYm1NsZqkp":658920016059,"dztJrEnQkU5C":816653187827,"oPSaACmxnkey":575981929698,"FCljDRFnP0Gn":773164695594,"Cx2M2vX1DNK4":401820468963,"MPfpLVyP7pu3":977944045877,"okX8VILFmWe9":20023021283,"UcC9c67bm1bo":327892909879,"Yxi6BNnXixuz":172702709064,"6YJ8P2wpq0Yr":970973949327,"7Ietjqyv82Z8":128780038047,"XeE5CWdLfqS2":918282253232,"fIIwqEIqwaeu":983558903680,"ic3vyFLeYSQ2":886283908411,"Do0sO9gIqYZL":219107101920,"9EJT02ovbYib":743223217694,"oroqBf5KH2zD":28221673495,"Ba7nilT35xS2":418100930867,"fA7u7D7NboXe":642662797393,"JwH3jAuL14WL":461010113517,"SkOWrM5TMDmB":501159870157,"DOY9mENWEKIv":44914431512,"GcJNyYKgtG4u":734302633051,"jJ6aRnIXNSlf":278112942892,"20oWUHHkqJe3":237148882564,"P3CFfPI9WQ0h":498616476542,"C9myOn3XlMpx":316080215622,"2LIK9TyfOR7B":796589462660,"4fhhgJY0Fa31":970729033929,"G60rWpMfJJKp":74568968748,"3gnBBVJgY4iu":933519902757,"ozoiIRSiAQRp":875331609693,"SnfutcAHPEOE":708215614121,"EyClGNsYKMJp":852762760045,"lhBufq2ooo3r":706516600627,"tpBE8XNe0FlS":465484606898,"o3tePipaXCyT":785043536439,"pG8xGvDYZ1Zv":515507497982,"ooaYe4Un0XhK":992800992337,"VdVENxI5OpWA":51519395607,"gZqOFgcd0IQk":883606682077,"vK1msv8KF3YW":860068365335,"9FADirgmS505":90229537832,"UjWUdXuW12fi":880576792854,"4HgMWmUQspEq":416163148065,"H1SIocmZWDMG":788928623199,"jIXxzpx4E4Rd":117828374018,"9bByu4yQFAnT":158360559127,"9ogApCf8TKqX":617465638929,"RmHpHsUT7eTx":633384580252,"N1fjCL5gc9wx":806724827093,"0db2UJK6z6Pr":516730406846,"nlJDrrmRqz8c":187699207320,"SnJtbLioKbeK":814118232294,"71RUFOLzyUGc":212510231570,"fzh6T5pauaQs":644753757082,"5ThAfcv9X1Nf":841382352429,"vEsDa2X1ddvZ":612017859230,"4Zj8GHQphfBe":361480367744,"BkhNFC5dbi3p":105518148094,"bb0ECyIkTVz1":772464138653,"pWuKnmC8Y8AO":4933803445,"6pivqxxRcvae":966505849205,"v9SVzTZGXhV9":850432124066,"V9FyGlNAb39z":674231542245,"E6IAwCgHhb0h":917551394587,"zXKjhlfw2hhH":717999032659,"kT2Ugm3HpiAO":184027046131,"KwiA2ePMq78J":804883210671,"SjzrR9cmODOL":928116764118,"OhqhHQuDvfqm":684495848407,"xUGdaxdE3G2m":462377793360,"LWvUWIMWKyyo":850667160742,"Iv5YYHJiE8vK":39396472966,"EcHnHkBpSCWr":616693689805,"CR1uGe2ZVi1Z":897360273957,"ZQhLlHxHir9Y":344335558718,"1jmm93a7mIc2":587327053450,"OtcAEYkRHAy4":110295000097,"SWydbZu8yKMY":90803442085,"adtW84EZr1lc":795370095062,"bzp4QBVQPzNS":903501359024,"qWVTWuRw1bGV":893627013321,"7HGp3ygynYVq":68920609763,"cLV9jruOiOLh":231621869998,"dfJGQocnc0RT":934096983838,"NWPHT4kwMR8u":616573496129,"qMycTQ5KrfzZ":321031494770,"GR3MYU7IekSX":529958301194,"jgYDBeEvv2RT":269621554621,"1VrpAAWNZCvz":58068987914,"vIKKQxCMgEgC":485635556111,"9dXLe0K2aIu4":307928168574,"9rRVi6qNo2TR":550093738730,"NarnjmnxcSV7":429833063795,"GabT5iA76U8h":400620168690,"5wksJRXQqd8V":240564570564,"ueK1Eb1ExsPn":952301228675,"PsfrFWDMOp4Q":283541358742,"5bSV4INqoU2q":814241129528,"pdg24NJZe8mA":938161156829,"f8KOqwP7ebPQ":420489566635,"5GnQIdUGUMuB":830113307069,"xNYTLGNTigTs":895539601028,"CowIdqDLBPlb":8846465736,"rUQMHuv8QfzX":559859829398,"6CivW4Z6xzFr":876697914982,"M9frzlCboc0l":503605043517,"YJcNcUo1ryqf":715567722850,"FbpOCrKtRu86":220846126254,"qV4bDA7H7dej":391370141687,"Bd5GzSjhn6TN":786369049705,"vr4SsvLNL14u":877969643885,"aTv2PG33kSNs":430535005196,"MDsIyx1VPCTF":17244728884,"NqTKR7bzkeNK":778260020447,"Sfuywhw21fqM":733713785403,"DxZAOKxwBrHG":234802627791,"7PpGzrWZuPAU":588312969526,"91tN3cU6NkRA":530329895875,"RxEffOlMH5mG":70868765450,"WoxtiYJVRUyR":664749948099,"h6RC8vhZZgAS":456574280422,"mclf8zGpVY6z":603705475085,"bmyNBe8VmXnE":461747707060,"QxZYrWsSGe3N":371960948762,"p6xnd3WLPds2":892609472211,"KybsDfVDFnlV":590851548007,"rr68HjRknro0":530636956591,"GzOMclkXhfAW":5750492208,"LPAAvn5t8God":126426006460,"sYlZmBCOTCVM":322080998650,"tt6XMVbTYcau":661997220006,"HKtlMAETyRZz":250043051678,"9r7HfSJRl7dA":700906633860,"ywQvCeyuweaM":635645657766,"QwBu6sgEJBXK":732076635892,"en8tfPXCHQew":725211738854,"lMLPkY0ZNLQ9":505795267955,"5TOrqCrH8Zna":42844746379,"b7eX2MGFCClm":189366657893,"VuLqZfXh8GMO":142338217533,"389D9O72tsdN":648952632623,"HTCBXzajlJq2":207143541643,"TWglByMnHRhj":420338518124,"XHQ7Ya9gtkjN":186200342182,"KrvQhCb1rTzB":41963905150,"K2YcZZmuWYIl":248762486268,"aSFhlMW947TS":863243671985,"lmNYsrkGr1v2":403801666549,"daw7GhF6L3mH":333792281721,"n03Ck5KwDl5g":526988973570,"h2ktW0EN4Rk1":402687470645,"ijQARaPhg8q8":481095243122,"JWsDFdjKoYnA":608176949177,"pEyufSKgHAHQ":816486689469,"rqWnun3y3O8l":447330292780,"VMpQuInzFk23":555852122872,"j12YaJtLPoOd":815446458993,"vgkRd8tC8ylS":123720020870,"gp3hUSlqTDn1":979559046712,"bux6aUix6SUY":121293041309,"s12KtfCrcVk1":773716629882,"BfXJlDvpJmd4":942993396468,"FH1BMFkMHWbX":986733370266,"pPbJSMOU6bZ6":545651344841,"XehxxIn1lcZL":912932306142,"HM6udSCIIFPe":390531075383,"X7cKFg2x3Bst":356507846846,"uubEDFhedv5O":915874890520,"9QR12ezKfN6m":789685267667,"nW0PaFZnBAr8":938411557518,"Wp84aOubSRKi":407090859614,"Q2Lt24FElXbu":819496822168,"UwcDMNl9U6ZD":990435205156,"BH3U4Fi8XDUu":937950511143,"MUgR73CUOs3H":257574156335,"5Xxyj9DTPiTr":404619830204,"ErIjuXhkxYMR":296463462564,"CrLDq2mgI6fX":54384500904,"tX09n9hPzRBH":454414376837,"wlTG7TJYLKx2":623073034174,"jnT0g1XcMgQM":605387754814,"P67VIS5eIbZp":913632388251,"5XxFoF8gSkYX":931970587196,"HMhtRskAjiCR":475082542129,"m8j9TjTZ73Eh":775774971066,"3BstI48O7JDz":445901970010,"uBWOPaFuznyV":534457594365,"vm1IPm4SLH3Q":140847822391,"vMvFS1AmcREM":289117210421,"pASAeh0ueB3T":447476024147,"XyXyvfv7IPe0":254225474138,"pQ1d9UlkxANo":643345514059,"k28DDsKGJFEM":898856821095,"EtmNfaeSCIOG":303669553831,"Vcmtxw9zKM0u":675325580238,"XTxdUeCbkVrh":223659964267,"wgPMH7yWQPLC":521077764858,"40kJcIDXXad8":559130664555,"iOkuBNKoZFme":876260979351,"ZtLJP8TbYElV":934407667819,"82CRIAGzCoK6":373829244195,"DPuLHnx67orf":167393613839,"UNzZkvkG9XOi":589818144923,"8GEZsCkc5LrL":626308904767,"UxtAB7RqVR5j":145471110303,"pM975cIZPewV":588427111609,"7ahAxmnvfr2a":339216989073,"pCfMbUlHK1Cs":102873579206,"1fTAYats8jnK":687047485190,"gv4rtOGoyTHf":660755249335,"auR6UbuJoht8":47997014581,"WGbeN0gq7akI":745347336849,"Ulzbpnlsit9U":28547791732,"xqK8Owgc3msO":340405008972,"vqVbPJLITp3h":234709927304,"vdw0DrIRo6Rk":742474312302,"3sdNRC4okpLi":455449902889,"rYKQt26R6fAE":705086568483,"RZ8M5CMNzJwE":670617751830,"Vys2gBeSFqGc":724372751485,"m8dVhYxPh31k":27919320101,"Yi02gYon94kR":525871279093,"EoPwFZTESd3F":325664663778,"wamX9Vh4nxEq":549378012345,"VORgon3LZjHW":953808927023,"o4y7NyGGLNgb":273229531669,"y9XI4VzNRWAr":369272703541,"rnojaonwhUtt":244409394839,"n9KGnKGT6hI8":149694838147,"8RgXEWW0GOPu":1453182605,"bp9dpV7PlJ57":412275695584,"KU05wi5KNVAB":906158947633,"qt2JyLQLLZhm":854843845418,"U6c1woQw11yA":647303481194,"EuREcMTvUREc":789617983959,"seAfr69MLAf1":468296685534,"TB4Yva5sz6pX":538729288704,"HUtCwf7ELPFk":191614908601,"Kc8P6DMmg43F":516524041112,"vEqpQpmMsQzc":751062292889,"fWtjqtuqSBsB":339562687854,"9LOAcGvnz96H":794243194844,"whFLmR0rXmww":258027429158,"BE86SKbatNHG":870861287397,"xAgea2KMb9Ve":292233961389,"UC4sICdSb4Fr":262494538044,"lbhzqxtCxcVF":91471035506,"QK2zH5ZhAJBh":390862718231,"1c8eoug1uj0k":158574865836,"k4VSctQG3pgh":337959718819,"L668VUqmFfn0":791961099855,"VxfRwTsHpSVe":694778014921,"uyH2UQfNMMrT":745252038169,"TLaVkQO2iLbr":943032880104,"OEeaCDggTA87":818094716737,"GjnHgsTa4IPk":555595651371,"iAfY1wS7FkT7":523436673070,"ARz7VhmD6q0t":924876691403,"cD2D3bNcHKIp":404023391290,"MBEtgbzdcl4X":386998239834,"9jNOgF5iAUzz":825917036148,"0M8a2XpTazIp":554946546648,"G5qF5AQyAgLZ":414659739982,"1BEQ3cFeexFN":236165959645,"QxoepsqEci55":651028723118,"dqCazhpK5PxK":659007477875,"Ok7sUfLpW62s":324263112766,"CiDY0FoiEeAY":632678029836,"Z5Yen1zQWNfm":465379697640,"d5qgrCKgqmXN":441539953074,"8ovTjiHsaTIy":874713355648,"t2WOirugc8Ob":266231328948,"vLVdUIkAqU5y":956632766175,"xAV4Mb5kkqaX":873490407421,"dyhPaDcpxvY9":191002538155,"CsyaonGBAqVb":564809822542,"6ABAfJAwK2hS":543161526951,"Ye2pASfuwaS9":136717103626,"spVpWS4Ps1PK":825444992963,"CcxIrFFbybTX":542254834319,"rs8AmeTdWdi2":321196268307,"PQDt1mcrckHs":812253248503,"MHJDeZ9Gp6kD":516852768068,"lVjxWyOnCaie":231933373359,"ovOShBHvtsee":995809929459,"UH4G1wJMChmX":590670177977,"ZSlAzf1X9DaD":516582937710,"qetMtIj9IdGZ":903676359161,"ETHVtI197nU9":606027348299,"m0HFG8NUedm0":393346470434,"SZrhWQqTXQu6":501978856571,"nSVIjaok5U5h":520716099780,"KkrwMXOdBSqs":146462214348,"R5PcnAxsmE9a":910574197293,"j4QBSvUZmv2l":974933170807,"xMc1M2SMaZa0":479056585794,"TRwuOop3w91O":579364225235,"TuRVw9rcx3NS":811187749720,"zW4jz0wy9wYo":198994515150,"xcmrX8kildWr":511365754351,"lFZZ3QprUHUy":566624340648,"FCWvGrivwI1l":25591044610,"tr9fibfltUlr":54709378278,"Jon2vwuqGWak":912401005526,"IN4vwnYeLgQT":532434378766,"SllS527jyNiE":527381061802,"plFRkkAvHmiB":369115418574,"Jqvat9IYRByl":327239215096,"zqbmGxNLK42q":74826785517,"IbFYiqKpOqLc":17213426468,"e92WCgg1IRST":190737308260,"v2X6gloBSXmK":923671205877,"DT4uGed59tlP":579859769262,"FYlArLiVFimS":309079494260,"cbyqdhuGoQuU":96553724566,"4qKpU2cItdAK":34211191391,"pQaDTq6QZjdv":13287383572,"TMsZCLeKbDeD":516802301535,"qlJQeOItmshq":67987325077,"H8Ya9AJrKHBG":919717477536,"b4r8tjJePjGr":886789309071,"HMGU29xgxRk9":843087390533,"9NbNBIZn6msR":195298006827,"xJWlVeVTE4Jz":992758670720,"3pcok01RpAWI":349137193980,"7JUFzjGxYSMC":456777374918,"tuPIQjmEuVP3":679629938112,"oUPOYWORkFuo":627911417226,"7mlzyyG0yqQw":395937525095,"H4UIW3Cvg9gX":505153422757,"CagNx3h4RE15":931266330126,"Julz2jbCwUAU":233470916430,"T6mbS1xoMCxr":367507380868,"cTvF0nHseDhr":161569302875,"zS2OPh9udUS7":246472342543,"kUViDGGt737i":12894154398,"mPZXa0O2KnHM":786884307619,"Zic7V0aTOVma":805437849480,"NqTTN1bkkYsc":747688778451,"1RwE7KYEJOmp":814352936699,"dRN3YI5SHoh6":543641947946,"8MeEeYlWGRID":789583212644,"8FjZAwvmmOHi":630549573553,"pKTHyZZCy8oi":931972133246,"Ur7iuAgylv6r":750488133189,"aMvYPCof3hV9":244616873309,"XLa5jPPKkGUZ":848911664572,"N4EZ2CZigHAm":53299797363,"Nt0kQrJZi0EX":254414817812,"0W52wSKUlyv7":110515467161,"LhoRkgMTLhWo":204912449323,"uMYEnLUtkmDD":709233421396,"8iJJtoqSFJIG":565085224133,"AKqNd5NhBv9T":911762465517,"SPk0HXrsQmTD":435972929293,"UyeVeBRmXgCA":945896509690,"K9TR3IsCGYhu":436245245088,"CF3lOzpBquPT":965815200891,"NMNLOC8kfMws":341561587669,"PyEeqt532k74":980596815220,"eRALD9GD1hdy":987602006441,"8HGOSgcERJTu":745028449077,"ATljkjBSvPlv":160653273259,"VcMe0WjztNY6":922554902043,"c2arLAewOmPQ":629810872616,"rPAAOxBMVAcn":616401568763,"xTVXjjWv8Jkf":348608610130,"2Zd95pMvjsQU":525343794087,"7D7gqXatLetX":310562040180,"PvLhDuIGfAUl":894726699274,"PK4JgamCV4QE":206774102607,"cdrALtUKWHgo":161120448377,"3aHvV41ZLcNA":149972288490,"zB7gm8jCeWQJ":886286299871,"pDmxuKQSfEyA":617797470372,"kxiOrs89urxi":567742254414,"wEMWmwC4qngM":375466973223,"04mpAJQq0ntP":395858449052,"QmgOLR55zgit":360567218342,"847xPHcA4BVZ":360441891315,"wqjn6LXGUdC8":513036587102,"yT7Pi3nDL8IT":631583629177,"MFYBbvDnOs1W":524645902777,"fvnWalJsEjEl":500398232911,"7SiIpbTqONKz":678129747366,"BZswmKkxH8JB":292966807600,"n0ZMzTHRz0Al":499697451486,"voBhNewsWwQv":297706659679,"YYavi8HssENk":464579999242,"HpNppat2Iyv8":286625671463,"bhmLqPNX3qOA":123597640592,"mKoPrkc1KZv7":7383103941,"0en8QjjPXdi1":274882569678,"UnaeypZjazd8":627091495649,"BxgpRMXMyrkA":733332040504,"hlW71CzNfsxh":593965147390,"LIJEE3ybMIpi":587518667386,"J9y9BNonRrFp":593865679766,"SuiBmEFhvbNL":335660868964,"qWjTRmea1iQ0":541430077552,"fK6Xhxtfndgu":687139449730,"XxHksy7UU1Gu":583283652282,"z1fSGsaoXCsR":338173711037,"RxaEoa138uCJ":173253195274,"XivFpWC5v8Zp":502063616676,"BNE9rDdqwyq9":410476627536,"tj943BU1Dew3":496027835185,"unfqDSFal0g1":1468105692,"YmdjA52pIn7h":549845069139,"KhnCMq22jwqc":90158859949,"uvP4EWis5Hbz":772034907174,"Zgw9amqrsz6W":295710041880,"P7jsESTe3GAV":497435458261,"w99av5ink2rp":249310482336,"acuFxZPhJ99R":604839910083,"WBRhYr1OtOLy":810817544021,"u5Jfszat7IcS":601653667119,"DyJLOoAzKlXP":382104490395,"UjAh6bs8I5ht":361383881500,"apVcmCHzTJZj":700245483994,"WiM7k3VoN0eS":794543952269,"HEa7PRXOF4I3":654707918167,"A5RbhDLVjvsC":588858635428,"I0gFksmnCcK0":965184356692,"JLhIZMuKXmVP":358944721492,"T38eoAHaLuYk":247492223874,"cdWXMC4WGYgO":157428197915,"rrczBkJCrp29":958169278184,"NfyezliqrAVy":129659204960,"uritD5QhwAlG":668010169153,"0JUfJzzLV6vX":848101014136,"Kl30fMCVelow":90577852249,"TqrzLpJGWLH4":331716826241,"x1CLD0JCTXeZ":781023891110,"9InsllAXhmkJ":331146682922,"oWepHER0L0Ll":115516281990,"q0euuCBi0qxz":640009575039,"yWv8R5ngGzGk":411685345205,"22yC7mde4FT2":782180008374,"HsnJI4bgXrhG":892486745194,"4BXmhgsEouQa":929489383607,"dvFyuw2N41WK":584458112319,"d3cpPJhjhqoN":696446665923,"6i7MnpU6lGhF":124680890970,"Ue3cxtbLA4Ff":319404793582,"BCFAbP0G3QYR":382048165146,"cmg15K8S4wkp":413316612751,"9D8EWIHdCgmp":140662940732,"hVKH2NQCCN2j":114011291309,"6CRb0MMlSA3Q":22154107855,"pgg8farBx7xg":655438972069,"2ONbMS2P1t9q":46284483247,"ZpCYUg9MeDyd":884534437989,"I6BVs9oFpWnL":396582515495,"GriqnWEHyeu3":43595962244,"Mltnp9jj06IP":357047333985,"7AicdbcmV5DK":970534807480,"tzqoFN5XXD8b":233324454181,"urtIFI3AbMJW":994872167596,"xopAQL0fyIPb":820750671153,"i1oytJUAxkai":554134964719,"mifIjQpVpKBU":971399130164,"iwNnazEOtb9K":59977172647,"0GFAeclMI7fm":675272668344,"CjVWxk8CngJa":508942724058,"AyUuOMNlKVvE":521862372690,"8QsUMsDf39Du":279241545209,"LeGQJZXtxHB1":826720223376,"51cqrLZnhwdl":433447240937,"L78DgwEpLZzv":412592856003,"jAyGmqvSB23Y":331958342122,"8YDu3cOiXDb1":286610839961,"BPtv3S4hreOY":525927245988,"OBypve9Up7Qa":411565321299,"OQLN6R5AwDx8":362082186671,"njqHVYhWK4Qx":324873185937,"39XfgyZLjTP3":98942552689,"2mBcAA5tH9Fg":861473700591,"KHCUoOoIsIWV":796301907968,"xKjWBF9d6bZ1":282172944123,"D3MvaJ2B4ayv":720281722424,"OKGYklgvTXDV":107596326247,"BNU4yzwq69Yn":636488128185,"yo2hh5TcMU1a":36481907085,"9LwMTvBEBkao":493901450091,"IvEV4MTG3fce":331655220447,"JZZZtyGKPT6o":661476528047,"0JmDVoG4l7fj":528557197440,"dFA4t2BzJQuY":348878860399,"dds4Wcr4LQhf":143374503898,"49xr879HksEv":801876608210,"MsBpKBA0bDoc":780540585713,"VgZwzRRPlGpE":126501022819,"6HgsGw8F3hXI":383369230452,"ZxCKqec67984":602783468003,"XgZfxI7TJJLq":850105880024,"Bdv2v2NqlqMX":550110583542,"eYxkCdegkuVB":529244455581,"EatnlBRsYVkZ":779829462550,"7cVR8vQXxage":853171720744,"FYHHoCtLT38h":678905349589,"xwWXRO5w3k8A":679536206505,"1WjtTiBqnJgx":650036112462,"rPNOLFSfnZb4":952206444493,"acD9IxcMA4KU":906437877100,"ELCTPzNHx4mI":308721369401,"1ZQBR5dRHlPr":473053368738,"g4TjEtSjvq0i":65135793923,"qOeianT2fDcd":361871023396,"NgMDrxTkUSQX":277423941364,"owAg4qhHpkTH":374595699803,"DciPO63bGot1":641121950012,"sCQEVNbrRTn4":107489025613,"Q3ILvk1gJFwM":984660061006,"gtPdJ0Y0cYym":194019234491,"sfa1e2lteIL0":732915212246,"Jejf2leSlYol":759218803992,"1GgAFQDfDMkH":4059134495,"dVVwzYVQXbNN":224975924434,"7rbX3kyX8OOt":977109515164,"d2c07EDmI0Ex":870597681711,"UkP4z65Qr4ZN":101166197801,"C96GEECVMZzu":564320022518,"mdVEdVry1LWE":456235616722,"aKWjWyPHbonG":570439328869,"aF3gS88wXY6d":49063060775,"q6Qwzh6UUEky":196288718952,"QkYp93TjzoOU":892900174189,"DYuTsfbLBksl":507387493215,"gGaQEgGH4juw":800882284860,"VKnK1EoKxnNJ":509789919287,"duyslc8dfTTS":483220544528,"nR0XcF0BCgyB":620481529029,"BRvoPGvNon3s":868422821872,"hAeWfJd7tIbq":367782209535,"hCgUZkckD8gN":403078020269,"QJHgUOMVVzXm":400284387926,"LLRwipabvXi4":260038355015,"R0IFhWNUbMcX":782184059016,"9tkEx6LYUEAr":830795489277,"4HZfYj12SD3x":776810554238,"eD8x3hLpEtCt":240328867263,"XZ1MM6wP0WIT":919967294183,"Uuo6ThRyicH8":689251364013,"5oqBo08d0Y4v":764501732401,"f8kP7318sJ27":707482509672,"CBXkOtTHdQ9v":782194306576,"uRy3RvehZ9Ki":844009715097,"9CrsPqFuCFzk":625568838346,"7HGnLbFnYSHk":660442976815,"bSPlcsNjJJYm":560030345043,"FzCDTDFLkt2x":656195001334,"gTrCL1PA9e4j":53370737832,"wfeI88KpQd3R":787824410870,"BMzowHgm0elx":775962460050,"Q4Sq11LsLsNT":786229644052,"FIp6ryx1Hr2Z":839709915889,"DxU6GtB9qVzt":40835179582,"nc78FQN2uNP7":628247058220,"vbONjwH7IKWK":362004181565,"yGwIsPcyVoA0":475724350371,"HusCuK5PYpo5":801818311995,"nSErPT3H48hR":977653288424,"7bP0pb5qAfjO":160545733439,"v846kqHcsacA":916927663565,"GEJQc236OsNL":642627954894,"M564OoshwR1N":29447775489,"3FXmTIP8rZfZ":180146503264,"RcnYbK8vEdWJ":359991932879,"lwvAY0KDUlP5":992870303264,"2Xqvo0gp8SU2":549338390356,"0zTh2wnYbp8j":205297907386,"FeIFl6mixudZ":650944689885,"qY5Bq8WoGV0y":483003467760,"Fte8Y8jReoli":221390235864,"Ygi1XCtA6fwE":564171589052,"0Gnv4NhIrX8f":124995117143,"JHCyYjPE4ssj":123239222223,"lxzTHROIWopw":914192533905,"9ZaRIJaVf1Hq":990936860837,"HaXmgiHGGMV3":268813909913,"SwP8YnOFnOwJ":844883658322,"NwxJrb2TXOYu":312542089596,"uLR1Bp3di3Vp":671001360329,"HhYvjigmqF9H":424948176510,"e59vt8UFKpjT":923395193847,"qeUv8x9azF9G":184367644675,"sNt4X3gg84tf":539212498875,"TQ7Ae3Nnx828":801805961032,"vhJjREomLQgu":351323703759,"Y6pMoEySEBb6":786102853707,"JXHYVF69Ypf4":332146320216,"doo4WUTy1gmM":465775838576,"kfYQ2wwJni5y":260182694370,"Erz5yp2VVK3X":425939127173,"iqgGc9qI5Loc":959502867517,"GwRv4fLkJ9UA":85139565547,"1fYZjGcWwJqh":165233684201,"fh6rI1bEftgu":698475876432,"18wm0uSIEKfE":512033055814,"EGLlH1Yq6j9I":481762033438,"qdr6GnGhR1QN":278963977529,"mFMA7LOXpGY7":958463630785,"mcxCkRLs1lHM":744772509965,"QFGZZ3843RZY":497151288581,"vNU92CLcFb2r":362230960433,"Pe2k8rKxeC7c":417177290561,"UQ3ush09pTAu":746533944060,"oc4pzxhiYZol":238642735771,"mImurwxlOCUy":922813171960,"0QQAwhy6hjgs":867331642734,"t0hiWLlCHsyM":28034330398,"1XSSHvFRClJQ":848066147046,"3YDktnYZsi5T":790426621769,"ho1OhVQWYIVo":411487209187,"7kDAea7R8aFi":886890014019,"OPuC3llulnJD":907264965628,"WCwFXS1FSLkw":983662342934,"mGdN8K6vyeh7":772071198953,"0F5vxmmMru2j":745227570516,"iJI72aC2psm0":472447894314,"Zw8HvcgZjF2H":680494359623,"a0YWbKN8IZEY":848468961860,"RZwJ4N5qvrg8":905139348589,"WwTbRfR0CCiF":747170884093,"WX8laq7yJykJ":788862810528,"f2hj7uqWv4yS":831480740921,"vk1wdVYa6cRe":938510643042,"hE3uW5XBKvzC":468786463161,"NFUWkmlQEnxS":606704302041,"uhQDvAAORnF9":399350423018,"TRvn2gU5HYVs":986376624037,"8TwuvbIP0s3B":198534280843,"gjUhTMcZWsPn":88158102051,"s7EyJiTkKEHn":180914406312,"EzZ2ry632Zle":815426426371,"R5z36hAiCK4z":878911653236,"LVE2mVMzylw2":525067739826,"w2b31w0bSZeN":952259753331,"YlkZxoL7gwOb":786758269134,"cvuBxeG3uxH3":486033822008,"Idz0pIicd52X":390479905262,"viqeEd1Kitt1":286250492759,"1MRJV6IMxFVl":404899920105,"D2KcKKC2AfZZ":245179359637,"IHYNW6HZxeBV":776737150017,"d7klWcnXJfCT":713654285512,"JuU6ARPxSFfN":356722536698,"qQWaYDgMiapf":191559794847,"Thl5jEskRSHk":476827591399,"knYDKrwvnlM6":340604390408,"pSZYvkVMQ0cQ":402960133371,"BbfkTstl9Fu3":833627513551,"i0UTUl16snmR":664769157966,"mvbmISWYlRTG":79113552411,"GHDHXMG1Izjc":631859460668,"IM8uzFtE0TQU":915586350631,"WHZX1Ax3vhz2":608988035801,"VqJRtShYY62S":130298178016,"MnpSynaB2Glx":840140779713,"EUHoFcyKxkMx":791924531924,"IC1ZqINa5CrJ":842102745411,"z4Z9Ijc6rR5i":12201419733,"UecNniGxTVV0":206084364810,"IoUL2h57pHRf":148093822023,"hR19TD7vKStV":272887745371,"lQoJxLfdCcXq":424300759690,"rjfIDk61snAq":328091405291,"nKKNfgzvSPNb":854184864371,"p2etn0IlsAS1":991984693932,"jViUVhCjea90":360663709076,"8OqqSAKvrDdZ":782890311405,"XCOUvWSZcV4c":80196008740,"OiBMlAOrzf56":628533869771,"autwsYs1wsWC":402405966007,"t60PrHxtbtWd":843795922137,"wxb3JFEBwafL":898209057048,"RW5QkHuBcbwj":639452166668,"zj2OcjK8XuPZ":643265118952,"vLnd80OgUxMv":850662887875,"ofZYGvzCmpso":542673669598,"6aT4UuUPRCw0":292795045366,"YOnGI4XNBjnk":818335532237,"yxafRImTb9hZ":973357855081,"ISv01Jc9U6hj":284418196960,"vBbakBg5tdi1":754172851481,"HGWKS0d0DdF5":245802324185,"C0X6dwZuweZk":163340507700,"DfRA7CS0O7MU":479297281241,"hfW1RsO2DIbb":37651812108,"MZgzlFrt0BDm":248345723023,"BD6DcXRE2XX9":197430893574,"kILx85zNtdm1":495035355203,"1raaOi9JhZ58":78079681490,"jrlH3FzM0gNA":874222046381,"k2A7IxAA8gdO":549806039991,"k0n5mqeKrZ3V":868510885838,"4vXNON8k96Tg":171388323286,"PRhMjZAAhcvm":811423804129,"OVyPy6KwEVSC":330552147287,"JwmnY5QP4OOy":33381635452,"nwt3hA7KbwED":56475483782,"glcuIND9BywD":816866861183,"JXfSzcAU6wNt":434200592567,"pUazXa9lIvUq":353648524930,"okQV4Uk18aSB":712740674281,"IOp5h0kUMpDv":631432255986,"GcwUMRqo2IXs":469846305181,"8MbadYR4c2k4":364073503998,"QfG5ZqnW5bTR":453068776291,"s3ARsDpQMeCG":990169649933,"smxizjIuzvpZ":124166655980,"qDgjMFTiwZdo":277433892619,"rX8rECMb6QOL":98604440594,"DIlWgWf0ACex":586223083919,"l0VfszVouxQE":893793424824,"2Y2ShF6HWIlI":913177318463,"dbYqHh0oxIUQ":24711607465,"lWDKVYQQ8ZzS":156125151826,"0iTHx5F5lG4Z":573358449408,"onln41YEOWD2":300976928982,"HXqNhOsKxssP":656874631514,"xhpovT4FKS0a":845578233416,"cFIp4NMox7uf":34021961065,"Kn4qCvSS3pKY":344963065664,"qWJFXKR9gP2L":758048386290,"PgLvY96TPKGi":301969796756,"2mTU3iTqni3U":303721449400,"sL4aQvQblQr3":376434796295,"BkxqT5IRhSOB":120533346669,"MQdQXfyLWo39":445175584168,"LGz3k7bsSuR0":679617107224,"Az3zdXo4kkcm":660972215314,"P88J16h0xzzl":630156164025,"KgyJxHZa9X1I":465635419660,"70d0xvhvPFBz":966327039710,"JwAFA1X33TWo":519764225021,"v3eUisnoD4aB":161461142229,"mP6dGJAikSXq":53532068374,"4qvKdg6hzEzv":425784803738,"aM9Zhl48YHiI":478635078653,"f78jWu7Tnq6g":306815761471,"x6qAxaWr3M61":524921929273,"gaBUrQkaqubG":748237864517,"7wnJNt2pbbwv":889784431286,"1e6ksE17Jcel":474066333900,"dPdoGV06vUhq":392574509661,"grnEKgqtREY4":318919746489,"ROPu3j9CQgWE":152813910063,"6tRa41Lr4zOE":443443765362,"bkB1xWeAdYUl":31284207272,"YTuy0sjgMG8I":148909709831,"Axpb7FZUZB0y":405680438536,"KXdx5nNlOO5y":903380212290,"oNPkX5J7TeON":235412657947,"VLfACMJdRCiz":471207993042,"dbstQ03ZPXjK":560445771782,"VCV5rtiMgJFE":326791383892,"K9lOlRrgHCK2":886697272730,"nVh2B680N7x6":417143437444,"QduH7JrRhtq3":612638797985,"sZTGqm2DVA7G":64628703358,"tkscK26zBywj":15056335797,"Pq5ck675VGYb":550699909199,"C6GEXl31II70":188148371502,"ad0CZhjfRLxM":739900991542,"mrWAqaspJF5W":729086437944,"y5zEiz9z33tP":17681072552,"PebS9bVrfcVP":795117164435,"C31OQF7igtpy":525868889575,"Z9gVcOsWrnfE":200909772404,"anPP4WCtcTry":199362087228,"69wajPgtSOZJ":506525611191,"8589HsfhnpES":47323244743,"MDkKtVGMjC88":614466262323,"tWZlnIvs5ZFH":905073750600,"CWL347oJxLyM":486255014933,"W77T6Hrr7iQk":939647945593,"ECNrQZFL2NIG":363604034530,"21Ba44nSoLq3":498368061493,"Vwv6FFn6eexo":936485766544,"9hEJUrIpJT6Q":480176497019,"edNCVnLCJfQ6":810514031723,"Pl8VT22YTt4L":102529463350,"31EDTi4d0jck":866290702134,"YnHPdVhmmkjL":645800243963,"Lonx3NP9jCmi":747989055044,"U6VuMbKxjdEP":166078177772,"XuX6gPXi9DUt":83239151802,"rESCKjHWLcbg":849632074909,"wQOEAb9iYV1G":288557678750,"140nlXNWumoT":302521990555,"yDFVexd7PWEb":40830547517,"rhYwIJWFyCbw":73599157978,"4YcPZJVBTBwO":457252225263,"roNnzfJjZLjY":549075006072,"lWKDg30YoOvY":773980279361,"3L9QC0IyCISJ":230274013581,"3mKkwhccJWUG":987927507431,"a9iu4emW5rYs":84438134996,"f53kCbTtxvj0":326760654571,"0xUDUftFpCA8":508945208063,"affmjOPSzhhP":535794303364,"we2RXNOboNAK":473524394132,"586zyhkRE5Kv":128007003579,"adLbWjBcuYxa":411961489825,"eErRkHQuMD8T":769759000699,"4FRJj5Shye7U":262132964796,"lIic8C34x3UG":907464630026,"CMb7zLIOJcNC":773335066268,"roTxPuJEOpqk":881343717864,"gBO0RsMAaNYe":51754767867,"FOhWOyp9JwOt":449392044549,"iYTHdHT6XOhl":490657574603,"MY6uBKnJIovg":887491567537,"WYHoRzcvVg6P":729910230011,"WEzTTR6RT7OT":276039912204,"1AfIeJp0i4Dr":375608956202,"SURHAwh4wcPk":354954274523,"rD1h2aXTeUaY":81324376410,"OZ02PHywhvzZ":187606961824,"PliQhraRiAb7":720061411857,"VLFAbOmRWpJM":549186125922,"6PN8suXYBtB7":968814692967,"RN9X5YGWfe1f":859349185893,"0YZuIA9oq2Ay":362333959074,"GIWGcv1QqLTG":107985251938,"jHGr54E3xN51":503265012553,"EFdTxQFDcrDc":750029324226,"qPSuEEte0S8o":414180633346,"IzcrGonDu1jG":866931944327,"2j5fUqgpzfgw":139189878221,"qqciI5RZEAZq":597187997313,"ky6GNGtzUqoY":869193856050,"MQBD7ejtc3cC":652889027666,"ItOZ7QvXoVHh":732799729824,"27FbCuhWkCPC":394867772609,"UVj7q8zb1nEg":670615391842,"gOJlq0zAqQhj":275854550293,"MhXd8LYDsqU6":680579514263,"JACmvRMWycBE":118697572865,"awmYgeMosxQL":672550999496,"lpliBA3BD1Ny":112326990851,"zT9JuwC1PWxt":16317066652,"8aWAEB2I55Yj":499942349197,"3lNiee94u1S1":275653343818,"C7Lq7tJMUiif":988915398205,"s8qwawDbkbQh":216250210097,"MSqtknCTuJFM":27141308789,"Q6PbbFIi0iKJ":195560546333,"mUHWIO67jsbk":634519433505,"vU2HgNyyMTZA":351439355390,"YzWFWFlNPhyo":13327354801,"XuHpKSJ35Ax0":394257317272,"fs2vU6Sbd33G":754900458393,"s7ICiwCon84k":224937099862,"YZyaLc1WlikT":129892819813,"oeHthrpWqY3r":277654804924,"Od9YBtiYfvLN":481986257184,"sabcjVKgs0Ll":335034820211,"j2LH8uKmEjAr":906809157589,"NNygTKHDeEnc":976509231249,"le7PAIaCHnL0":164833636454,"tjMvfFxwYddT":326676566870,"fZtHbbosjCBx":778076537790,"sFvmpKdxUqmt":780017746798,"wpSDuklcEkc9":290073863475,"oExd45N9CtIY":915099002585,"hIMYobohW0HS":864252797995,"6pmFii21YoRh":288218057338,"Y5MAvVMDwHKw":928599338239,"VAcF4Peaxhhn":19741599718,"ELoBx0yf1fJO":31706758693,"BoCW1ZkYeANc":924989747520,"dKB6QvVHDij5":804431384498,"jaqKv9X7BngY":979146082971,"iNIjj8Y6M85e":317444625987,"7C9jiQsIPiwA":985605642850,"EbBq8gnaY5qp":792557696554,"sNGwfX71CQQ8":741124551875,"zv8uqXVwSoY2":501842053122,"GgldlFWbidhq":215701109169,"LVbSUYxNCri0":475962969287,"vAKD6PtSzqKc":853343140049,"LNR0LIVvRam8":329482607768,"k9v7zWzTUJpp":969040013631,"u3q9mJqneWMp":105962562399,"gcwkAx5PDvP8":148800971934,"E0RSVVKwEGxX":659452539121,"utbzPpOF4XKG":972208547238,"wdMlGdpGQcMZ":172284498343,"FKINc2khRY9W":644042195513,"DnWbUo9x1Tpu":840194568677,"PyjQayHcApRa":978093771230,"wLYW8wFIRyOl":726081135608,"ETKvQtb48RRe":373237320811,"ppbKF4qTOgSy":981414790778,"5XfV1Ehcmcnn":277717121902,"P4YyxaDn27jw":389414515774,"M45BWg81FmaX":468241132090,"nATevzsRWkmj":344484722449,"XbqSc1lLl7B9":515386460147,"webnsVe85Nu3":45061730288,"GJILJPxzQYRV":138783787071,"zDkfnfIvM1uK":75614762436,"pfXF7DPYy2HP":447095850410,"Z91CD4m9xzSk":85358708480,"qrvMUZ1YDEMl":144538191098,"OxP6ICOWabZV":382463659735,"KKJSkdZdGndU":206604801047,"Idsuqy1JikDB":11017782194,"HAXg6kLZtZNt":353069615939,"fbBE5Aed98q6":500075732899,"lDsC5NBZbXsJ":617258999073,"MTgwqaIFBNfe":136479200995,"xGRZZWeFYWzM":376332754732,"boARjOuLrcU5":691155821898,"Z2MkVkUuUpOT":358804454436,"EV4uTbCI0Krq":707455250476,"KiQ2KLG5qnZS":368929771048,"7W3FeRd0HDi3":570650653160,"lxDhA3hCb0Vy":882718213916,"bIsCnb22kU91":779847331040,"CC53UnFZ6Als":358559652480,"9fqvwZNjqbnR":713445232876,"FK2YgHn85wQg":340254743805,"XmeEmhgCscnu":535474603730,"Og7vorEvYxkj":661084391610,"5TiIvVQ7Jket":641651947003,"xdn3CFqSA2sw":280027875149,"NnhpDNR0uzDp":499974529066,"FWAMatWqeyQ2":849085327046,"2orZccxp53D4":334713146605,"XHly9kH1QtmE":968095651719,"a2lyIQQ15FsF":644656575622,"wHBqIg3FfLsh":712539929112,"ca7G4QrPiWED":678274252117,"oBVjvEPA58jS":567798597284,"1ofqFTV6z9dt":340970079622,"JFSlDDLvoyzp":981549835049,"AiV95iJm14bm":921843524678,"x5zStW6SNAY5":566736973291,"0i1lx5yoJhEN":14546651216,"t9J70FUkPCt2":661501341610,"5onl9lYGiTUS":18816618834,"mh5o8tUTfDgS":976031252483,"cYytLgfMePAu":514884126033,"JmgIhMJf7ccR":262036788210,"uKcMW6dydmxv":496279937545,"aNKodtEogY7X":870075268968,"mcsy9Pt0AZlu":942203946786,"Sw9v2bibnZzM":180473289375,"kd3akUoW1Lob":852566612611,"AXWoeuWgBFSQ":870699701546,"Q6Uhbxf3XyeV":831040421061,"lPxCGQOUfxS2":77380298525,"Fg1TcigAh6YT":244425889013,"evSld7Lsdcz9":93658471879,"scvtT3CXOMmh":411598275661,"PNHAmlYy8qEn":695053004111,"C6hWPI5mNbpp":664320097123,"1TxSFm0LSKYW":823542070430,"AwgArCOh79Cv":49778171181,"NBwcl7eGDUCl":87929138696,"I2d3HgBskI96":386429552853,"0X7pyLeLV3mq":346489809061,"epuq3qBtX9KT":517738621139,"K9S4HEU5RoqO":808161672134,"67DslMEMPIDP":823575385790,"KhVwV1ePOj2g":88484847909,"Ew3lw5Wuxzet":371054661256,"TMqOs9QfYYme":217912479532,"x0rMxcHTPC3x":519292799687,"P8JgWLTXlyrn":604852474767,"zTtIqDXSsPry":337721253528,"zJT6m8G4dISI":711834638966,"Xj1ChtZqyHDg":171987368184,"pqy0zuXrYZT6":696300466049,"TPhj2wV07Bfe":283263707372,"uaINpehHntbb":998232012308,"828ZrppRLrpr":305098385048,"2JpHhpBFCKmE":166732787099,"I5GxtEY8D0oq":258082438564,"N78rZk7pQ6vd":766875377062,"gBEiLQ9cvPKx":457668575931,"T94qODYcJt4X":618547399373,"w4zv6RWiUWzN":492133958497,"aFQ58kDe1S6L":539788304910,"hjV5wMHAYBW8":273294727857,"uvA7K6TRmjZw":796720720874,"KU1NCgFvBSwX":298125713736,"nHCQiK6fmroF":140240410617,"fx15LjKT7ClL":825895521433,"HG1L99BgTm3R":782138157906,"0tJmSKJDgr6Z":503357708618,"kOAP1y1mpxQD":403006014371,"00HwE7syoYAp":336293673434,"h9elAGlad2kA":359692335736,"2sf9ed8wTb8V":639477782257,"mhNSneIeacKT":847473703301,"861fqYMqX10Y":805189902530,"OH5fjTawCyY4":841033221995,"eY92patBTIwK":155659846452,"PMTFl5YnsFDf":897331750919,"G9fe1D3P5Kmd":659774703044,"y0FMu61ORWTp":688442972338,"W7QdpB08mPJ7":230162463188,"x57YcFu6lrQf":532625039868,"FprVJhoI4407":53262608579,"DNCLHUpT3i89":168465990863,"8txpe2EFQeeg":611399717575,"QlpIit60AYt5":142020350528,"XI93lmJos5uO":853142187705,"gLLmyatn1lx4":385832575880,"0F0Edks1MVtD":321940210436,"oZto5p7QFIkc":94156488770,"FbvcyziVMADS":450174380646,"PbRpBvhjhS8u":323699555326,"X9D3NBz24HkO":791628006113,"3ISxCLopJL8Z":777229633860,"rLnMSCiiyK0x":470128760597,"fUGchXZ2aAzn":683703597778,"8HpzHdrBUdKJ":277943826927,"MBKWVipTwCRJ":239583975067,"7ae3K90AY6ke":336163896437,"M3RwxevACNEi":110564522392,"qmdVcfgVjNEb":609807929669,"9ARsuDx3XNSJ":268509182400,"AJsDo7cW3Ntq":657669870337,"jBvizHiBZIKO":860277011342,"CblVs19n1oUZ":414752230852,"tvtngL7UcG64":358327309261,"plWavAZW0wC5":520409837386,"n0v09jx8XT1J":863529683816,"YQigAs0iHdlD":697557782966,"7bMNVU2sSkDj":874553353759,"LRmvyhroQf0a":824882955226,"I1qlz9XB6QhO":452181395658,"FtWsSFTRqvM2":232346180115,"Olp4ZAIksmlQ":345228283228,"uhkDCk6aKYps":194060558818,"eDUsbwQ00pV9":152614455888,"WNCyb0DZGkZI":372474235068,"Brzh9n1alcsf":788083578411,"mBNeyeSLAEcR":824490274606,"KGvdnwFXK36p":160030332296,"C1Ab1PjaAVH3":706360700875,"wNO054Bj701r":734918001438,"fR22X8ypi8F5":471543058148,"XwGikHMH9yoH":443456923003,"jYPY6MZ69xBo":193948671104,"e9fUlNNpyVNb":684006230058,"Rszd9mReCYbc":98723673991,"lkIfvePQmohB":208181298255,"qBIdDkr9aoJp":305075667835,"HVgtly1jQhwl":545794439280,"izRuwEE37CCp":160930172860,"fSTvknsDt94q":170710314702,"5baXon0ClI19":174186907324,"wY5rbTch32vh":580120039251,"8Zhlv5uVK5KN":703965231008,"qKy5RSnwmjDk":82062943113,"6DUIHTWikZsu":620129964312,"x40rfGer95yB":187632328055,"TpKc7H9IVGGP":349360227508,"ZXAaOCvd1Dcr":966621825354,"6COSI77bl9td":56784572161,"TNl5Solpbueq":823244206388,"Hi81fPRVW1lQ":531103608020,"Yu2lABjPcFnI":175165374353,"5MzsyQa8UF3D":736884965037,"CSATYwOyNQ1M":917655825646,"SZMC3wjrpgta":659624461507,"P9zbDAeTkWk5":68944984653,"bPpVvyflssZd":655379493804,"Hx60Hbv2zUek":150535440363,"8o1dqwdL1vGU":210386240678,"dsBAdPcQm92w":340109187988,"EwLMYiyhcFBz":49044805959,"iJIIugKhDUfj":781780628696,"lFepXVXm6uqH":699251459723,"ELLASpMKkLSG":530526329179,"WCzHnfQG45JQ":288121011960,"6vogrj5psvac":549729486837,"Kyx1cbySkWCW":482378655850,"RXrdxaTds3kH":915219033518,"5l7C923UXIl8":206472634650,"p6vvSCXnEGh6":127930397026,"o0hNFvAtO7Zw":401003677063,"YBAwV97IBtFO":906393472945,"Eg7TQ2TGBDTC":602110717938,"EHGxrwh4dCNY":7890497872,"TcH5i1dsvi3S":743613247060,"PS82tVIOroOk":507456292000,"jVWJZCrU19cJ":156051930747,"Z3dXQ5qXyIBI":421888763485,"PDBeEo2fh88n":452527574445,"dWkJCxUsDR0h":255635685247,"8H26sPKjFFdN":430278111146,"oCDjZwIZWrKc":954517236606,"SO47daxUTHxS":930304890799,"MIuTHSEyzqAq":678436753713,"amryOQsfK4n8":339053176037,"f8nKNKbcozQX":584973253790,"KrI4MhGer8VY":160659108338,"DRYKOJyTZXZm":181982200550,"oDwfUbUWeG4n":45808975333,"uoPYAY2wzgM6":248796711425,"P43pMaVRpSLe":466965800997,"s2R194nAjvHD":575167686716,"tSOoxhqELvb9":711275130321,"4msZexvw4jCQ":401351719610,"GmE2ciBDelSK":875798378227,"vJClkOVQ9pjc":705646383256,"gr8PZFasDiac":865797835423,"g3hkc9BPuDN7":537280622328,"djoDUKHDDmew":257903474378,"iHcD5ITf4lXb":550561964502,"klo83Ovyhjqn":342025644742,"ucHpzXG1fQYc":768971709704,"DK5teFj81aVe":725916275455,"2cd0VJXaf1LT":583680253619,"pJfSF9NRqCXg":670290359824,"qThXNlyv4hIW":597202045530,"hzi4Wn1Bmpel":740810234896,"iEvmOZiq1uw1":789436018831,"SaDhvt501jDK":712107435388,"cD2k2E2Hg7YN":392011388718,"E4xvwkyRjLmu":641021625111,"5S6Sn6N8NaFR":892199293707,"OkgQ3n4Qi8xM":492730777967,"P3rlqbfy8qXd":706799041981,"6e5UuK5PYF84":955655877848,"eC0wYKq4JKQD":461141515406,"uEhfKR8iD168":892014043967,"1cf1tUdr8vyo":458482470638,"de2pV3xEIypA":630150690516,"fyDOnvDCtuxr":748129926990,"y37j6W4Y2O28":15485280951,"RnjN1nFDsebt":979737329596,"fUicFPJzTYCq":65954072322,"iWQtFVGBtyPc":981321022292,"hjvb8ozh4ZvD":780750310280,"eTKf9zx5rsm8":341466587974,"irj7oD71T9xR":837219333462,"O2lB2rAYprUa":586843743541,"J70h0SC0Zq8f":231227312725,"FmfSWUsGKgSC":959323450831,"jKAgjfiV65Yt":326510118572,"ZxXvevATGWis":670125278256,"WlEK2aDNDu3K":725770538368,"ZJPc8dggCqNR":185370735559,"qt43IXW1MFXQ":238487040456,"Z4o6IeEy3w9C":61308770967,"XbBh4eL7JU1v":414930579053,"CaDfjFP3c0HV":527719542406,"xy6EUq9GPDh1":771168498492,"RNG1LbtmNfIu":201536214794,"FnEjirC9WZZL":153142574536,"RZ3qNswbaqJO":557261866919,"mkobQ86skLGI":693139302845,"N6Us6JpFOxZu":426551576168,"VLvYCr0wtq8N":509616081457,"h3SDneZmVyYD":149314185372,"z3S30llySpzW":947586745950,"4xBZGWITomgH":96830465015,"mj4uUYGOTLks":628233926185,"KL8HC09pDos6":460639428228,"9Tkik9TzLbYQ":701026303598,"d0iqEdR2JpAV":135775230809,"cbPgrSHWQ09q":938170887651,"zm5wFHIed98k":365871470539,"kYolUr67dMVm":583313639468,"xSOKyw1dDjUd":966985272020,"6XtSY30Th1HB":259035892991,"y5qrORuk2k9Q":809883538796,"fmw6iSz1LzhD":889124050715,"7q9zM5vpU5Tv":572746214048,"gC68uV57D20g":202316962453,"cMmNsjfineaA":960638562604,"rqO9zyydqLAA":227783826875,"6prAOSHc6git":50819863477,"JYDldv4AOkMw":957503144504,"iIqI2aFPXJ8h":953570127886,"x3KuHL3r1Iai":183715538177,"7V2q2xSKnGbb":476493517299,"mUYUtutYK2z9":61571311409,"5rFvRByID4a8":453755639078,"D6jVbAUAHR5A":177633491438,"jvuMhs1Vj3Hj":929805655062,"HyQ62llpmFEO":340209187523,"ehiVhEcr42Cz":88760742034,"g1jOGrPMIgID":130595222177,"s11MlVtr8CIe":889919731636,"pYQpGswWBwvj":294324580870,"wbJHB7Du17ci":631727168668,"Slx9rCpiOkhQ":549271899872,"k1W2ufw8OTWz":80484277985,"YCcNL39lCYhS":343566019322,"KaCGo0ReRSH8":241707761800,"NKH0Hk9vG0ai":935719624,"o8ikXwO3zCgL":162043613206,"mWx6Xzv4lPEz":876785559711,"4JebmMA9oCiM":324127893488,"gsPPV7qfr1jd":860986093287,"vlI2bwnUs4ab":714772340528,"xf11tihq5CBD":324780299285,"dVh8xQoOOiNl":291794775609,"cN1J9J3r6LOe":398482494892,"QD1dOdrjgkBW":903926539777,"mrYQRhM0es6L":420564132295,"KrQVzGUUaAt9":554064640653,"ZVVNY9Fz5NsM":944477571933,"gGRHiGQUdLB5":275913615610,"6IML3RJlVxkp":134257718706,"KhtPkKm0SOoY":601230060165,"TPlAnsBZJ9l5":139094293868,"75Wfgc24W41c":494637525772,"RarkYzPFxWtO":91623483919,"40toR8fMJwPR":48725220043,"I43sXT5kJBha":55721567536,"Cu2WJPUPHy52":753015047026,"nbRMUJjGdDPt":696710167370,"6TwKsJMpy6EC":904308237466,"yRfRPga82JxL":881603187974,"KzdpsVFow1Gz":896921293975,"hIcMuMl1sikx":876803980882,"I2C0d0ZQ83hl":537489907914,"frpSqZ2cSNML":845178096883,"21q3KIjuLlyX":524711916368,"191OQYkZ4fh7":780323315641,"hmn0d5ztCQq6":406982338295,"TmmwTUZKHtP8":59229815801,"mzJFYLzG0Qa9":468971678762,"YsdzHpJQsl6p":496532723257,"3OGC7pOgK7kg":136803293646,"Y9unDo6LlsxP":223505645142,"mmETMtGaxHHH":193614113069,"SDoxILgLFK3w":171796825790,"MFKn9dIflSmx":717543801151,"DcszkSZxtYjL":714775164749,"qa0oBsebKizQ":98923068522,"Bf6FtD9by6ij":993780484519,"g7KNbxgaz9Jv":146504614197,"FRsMJu8wmMXG":911787338109,"cgHEhZmEmAeC":841312541123,"hRObI22PHPHt":672054153038,"gtJ9drp2qavH":193585991727,"G1kUW4FP1mLS":761681099446,"liyTQySs2ik5":686640461684,"wPalxg0ZEqUa":163372288188,"YEGr2kU5fD8u":156776991352,"fjVANXHMhKph":58394372989,"nYEoB6eyQqif":849320619842,"ZbhPptrG5GDW":649226617936,"t3s1yqvvbY2b":750072868687,"PTB0NNxto6zo":334721025792,"kkKvueJG6442":792456544061,"2gQeLS4wMrXW":642938597572,"4m3Nq2OaZNkp":677587822647,"eWDtLWMvpTsK":78173601537,"a9XG1eKqZifj":916479360717,"jyWH0OQ7CRqh":435873962225,"MYc0Oc05kKfP":906088239043,"yGfO9t8kP4Tb":629058862611,"akJCRPwypkfm":460344910049,"keUNXzKeE9c9":717658702908,"VnOYx3ldehYL":599820365424,"QAnwXc8UHrWd":359283993229,"pBSBUJp32GRC":70014673891,"5mMGoxDvK9wR":999817351275,"QaUPBvcSiaSm":560520262458,"pEcfrH1UmuHb":312549683546,"wVGNrA6K9WLF":151876358828,"qIqsSvcDyCDy":459271084772,"NzIBDxh0hP68":908134384006,"D2q6fzSfHlKE":568378567948,"KDKqAhy2Xlx2":332264520211,"D5KlGfvnL7AV":554328175312,"Zcdvwto34ZN2":531360172492,"OHlejgyhzBn0":79098406910,"EuCcKayDyHed":627643237592,"5xmTbI8Wu5QO":446093044427,"8fzzhRij8rIU":233306225960,"gl7uOXtHmyvw":432233264622,"LRGVyY4UOSdD":369929435462,"wdRiJTMFtRFx":645421714743,"tsOVvbrYxwxz":579309464182,"VbqYwzOa7N9s":766516056020,"ysGrkKwpV3YQ":616053584680,"7NPZkM5dzH8T":573327293905,"gsUzRQkBFBzi":718345957585,"1cV9dPJMBTLd":412370230601,"VBoHog5o81Zw":505067709085,"19MRul985D1o":15408506314,"rbBBLGg0H8Zt":444620284114,"aX1KbdLEv1nR":429724265602,"4E4Op6CpgFP6":744327707296,"Mg6dgY0K6Ofi":150472170945,"NLenXXAsYvwg":544679250749,"W6ce7RN58Itk":106591668300,"lpuaS09X312E":413022173631,"5e1q0Tn49Lmr":6086776507,"RHoa8FkztmWv":447642192934,"M6CYZW5M7tyE":11835225740,"3rRpMIfI8aL6":95743579314,"zH2jkZi3mrne":462708737044,"VnekxX6y566v":213821594409,"iYoPihc3atmo":720918257796,"xD6sRhozNMtY":544448493505,"a5S5vwQRFGfO":549972041929,"DWjMyMrKHyb2":343377863020,"sabGfT7uzmFa":955309386433,"hRleDyWAzXAO":961590788808,"vwDn7UsuXRgH":818987632636,"aBNynzxBl4CP":880063779611,"23WXVnssuPNV":686867058257,"unthYhjNaA12":466466871950,"OAnawvrzKU95":442512094109,"ZPar6DP6eps6":362081618430,"LO0rFofr6FPY":215239875168,"cww2awkOaGGx":778460732062,"I5Ekhlvoxxjy":303991672725,"xYP7YoL6pBSJ":677217772066,"b1TXdgAYEUsj":762412663681,"sUcLVN5Qj4sD":585830068234,"gavpGtds6JZV":323927400142,"fWUhGuz0DoBc":229428635031,"EU3oLd9p2sYr":41068588886,"uVQvbIm9DF0E":621821167921,"QLl8J2mzitcL":873314954157,"HG2PrarBTZaW":18474461070,"GgiKbVJuxk5I":166433803150,"vkJ6Z4JbDqZv":193449224004,"L1qVhA4l3kzy":198976508327,"jA6TrIW538Eh":956180700294,"zuLqIMpw0xK0":330915269001,"Y9TDe2fmd87j":107933751033,"UZNhgaucTF1s":210934290404,"VoAV7ciOpO6e":960863831295,"2c3YMhANCC35":658774213341,"r0KZK5VIn6tI":195313049766,"eWm9d68hMS1g":850534076394,"4UIjjeut0HYh":632857311412,"ee7dj0xNlbM9":101695661159,"JhShjeQEPPgp":117672074838,"vJz0TsTcxpmu":95382769463,"2TYqYzqDX38i":680562009628,"aLoJfmSG3dM1":384295123189,"1ouPJ4aLzakO":133995330064,"JQ21LRVJkXJ9":393881717576,"GFNAarOM40TR":678397255427,"Pzgbfya0g7kY":999020746947,"hTpXnnYccrZU":418693007784,"o8N0EVhdWi5E":310994572643,"JXVrYcfIRyh0":392554480809,"33AVUWpDjCuL":629224038043,"Vfu98u1o4vvz":412052523890,"uOVchJJpwSeY":170060270242,"WMFr7uUUOAuR":593119246382,"k9A2UmZI1lQR":163218941503,"u4YCaUMCAuok":357134852221,"BXqOxnKlAC09":45050730512,"zb2Fpeb7czsB":702148446558,"4E2WLIw00ye2":758515784576,"06KAQlR6fH8N":342258279476,"WTpkPSYv4blV":951316087070,"MmivJ0CZGV3E":301562758973,"bs12eqC6XIYP":195964513602,"byNqJHGUb43q":460305571073,"LwCzxQabpeeI":357047124985,"sBUM8tpBomcJ":109601651497,"q7yKEP7CanjF":396198204857,"EpxDvhAD6L0z":125254396364,"xQFX03Xsegeq":818358003959,"nJXlQb5kfPk2":669499737994,"tfPvyuc8PU1C":379024672191,"G3Gi8j2aXlrE":43711937939,"SkJ04TJ8CpPH":526642523105,"1tqX7wjsuAaM":145418300241,"QJr2Bd6Mhwly":198997319892,"ZKLJXEcYZwGT":652012834096,"6XL1juxSlsvr":233662964002,"2YuCQLJ9LdjJ":459384940202,"dC6Swd5N7EUE":391656544944,"rFMWDzENaoB7":948415591220,"yBJdJIUbX1DV":367249453446,"cXvXr5cFMZqh":487427479371,"ouv73jE8IhpK":887303695123,"0xio7zruqwqx":230380181621,"gABxEgqFxCt1":567860692402,"OpWCLHIazuHU":913017842483,"P5XVNlb1W9Rq":194589804324,"PMloydJs7cmt":369484178720,"8tc5NvdSFQ2p":434981810457,"XQBtBteC5zWc":492248746338,"L58UFe7NRbHd":919102503488,"rxkUDNbcgW6g":897621111668,"SYyiYWz6OHVX":496385725627,"r6wgc0SWxQk1":88066074871,"fpstKZoAhwVS":800942538834,"w6WsRBcUGcQw":1870422211,"YVn7f39V6OmM":179989911630,"GeCEuV4PfVhn":709275592256,"zUtzPORUYfG6":518068677437,"RFQ3VOxAh50T":110944834789,"rlFbOhKT5odb":835764796740,"X2BGIQkEuD3Z":413703840145,"xa8wWK7tOtTS":547065413940,"XaIMcRHZNWnz":996601575714,"EaoAvxRiRGWj":903700536647,"gEW39qHCsYxU":718302159858,"fSYEpfsXZQ9v":547844337163,"dATIGx0sGIzw":786771275234,"Oafrnxy4TZ93":214972391978,"K3X9uexadg5z":929363539042,"CongmGExMpwu":828159596543,"4Ox6vMLK4hCp":617729264387,"nN5i9b2aTFko":426151511532,"htExFM14rGg0":378198836545,"PMWcieBfntXH":176006716021,"V3hfIibiplv5":317674408900,"0lVxLI8uWhgD":528978146355,"QJKSdO17vPsR":6784338622,"HRFGSQGK614V":395561371097,"26NccReOLVEQ":851291913939,"uPV91JdwJTp9":310020353170,"1zfcxcUx3FHY":500711515816,"jxH2AtLxl1EP":490720971961,"BeQwVBSm90WU":388300045102,"F9tKm0XMjmzl":416824493835,"t9rHlfYADswx":157225977835,"PTYwgpR49hTm":868249910361,"s8LhTpu62iIZ":418823937803,"SQVAVIO23bOP":887176054502,"kKEM5VabgxQC":136839995448,"E9soWo3Dvyml":595162819951,"TrPrrzAzj6eV":431175371658,"kuk0qjWc7GEy":583793338705,"f2B2YhY2VyZ3":618670944602,"2HALepv5oQki":185432491047,"CTGkm7uhHaIY":623044046357,"RB4nv1QkfWC1":643646966489,"SRvotxFVFrnP":663474008058,"vjKT0wx1Esqc":246008815136,"xP6ZAgXC2rwL":744364479151,"z7Yw1Q2fkjH5":28864938455,"10jcrLTLIjXf":752871116551,"gcFVbuaG5Hkr":461337750580,"scXpHnQvyPxK":868607462270,"l6mhpthglU7O":350996888358,"T6QzYGC3Ri9o":600306544037,"aSWMTCQfzwLz":354353994903,"bLWFBVgdoopj":781599660439,"do0E0I8OuQOx":112818543367,"8yMXVUW57qJZ":526825842583,"LZqHFTBZoNiY":698339622491,"BNBUcN0mzeQT":334451927811,"Tgl2BRipF45N":999406639972,"B2GkaShynzv7":971207246676,"VmLg1OPK9dFi":417036980754,"mq5Az6orzdAn":172269062864,"Rsa6P661WVkc":368049920434,"cYHXARaj2FBo":777943048929,"c9ZmQYH1Fl6B":459855853162,"Tve5nfdllgLH":91479649381,"xHlAOf8yKRSy":402065629784,"5naAQtjYaYZ6":279856246175,"WQ6ADLZPtoEw":446593150830,"3IF8JyhnUHBy":213079481357,"LJJrQUSa4hso":222737844357,"rxGPAeBHZ4G1":799603019029,"7IDriBvjEnCk":6021293911,"zep9QJMyf5Aw":720335738198,"RnweKy6e9hag":744929005669,"MlwRebrBLZds":443842614507,"h49G6QWegvVQ":480511143601,"V9TT9Me23qNK":836431425687,"eXmrWmKqr4aD":674563207054,"iu6CnfNsiPMH":656880763777,"kfZVXIChJqxd":448675962173,"lOniw3ub7FWj":924976075698,"zVmBm1jsiM6O":139112437319,"5bIc1quBPr3O":200142092300,"5XQWip6bMw8y":322370870629,"0TagpEzcJZ9j":919784214936,"TA8Mv1u5Hwyn":30256043766,"vQfjYzwdWKIA":5557113320,"KZYbYKEjOoiU":263118989191,"tyY01nmg0qg5":844107168624,"cKmrTO0T7hDW":936000761643,"OHQB0BFf6arL":63343564080,"Q2ssULKf0OHs":1650218228,"LXrWkZ2IC714":623119249494,"e4C6vaAQ5Sim":618524750416,"xz4ZATcp9zun":170337234077,"QES9TRgWOnOr":866464733371,"PIejKRhaioDK":611541922496,"boLT3uH5RqH1":783571776708,"mbGCVoXuh9Kz":895307394964,"SeYDjCWM4o9W":79767557875,"Y69MF7LExZPq":110496550708,"CFznfDULYyG0":92725209813,"H5N8nGA5ll5K":255791666076,"VCk8mpfiFuPo":813716019689,"vVv5jpuSw2pz":368612125141,"b5OScQY0lXTP":759192455434,"CNErXfUGxx4N":462094362070,"dRolItjgTyHy":806081253853,"tY91S936itfm":99390330997,"k32AjqqaPaLf":177396563180,"1AfTMjxEVZAE":381411223545,"wF4f8tyjOqbK":537710023864,"jeRyON2soDfT":454757840389,"PImm3STnEySI":600817269025,"mSYUdE3IHdD1":551983727146,"DG3OwcV7SDBx":463359897826,"4AfexR2DElph":187941265178,"SlzoAElPKLfQ":345169129836,"TZk00ZtfFm8c":148551618723,"cpGtW3j3v4We":119464481392,"3AQqSIFBRik9":27297989922,"KYe2mBSkLyuJ":18167733890,"jGqw9H0MgqP0":946954521497,"pcdwNc0NY3c5":224598146667,"U7RDnFvudXP4":93997280892,"Cs3dOG0jxtpd":748809167283,"kVNfxVouTg7L":823574531841,"oCBGiiGIvsqc":258590898517,"cSowhrVvBNEv":612718171818,"67eSQ15Dfle5":833022277986,"k3QbeVLEwg3I":14677566357,"5yZFGv8E0Kr7":726739555743,"U4rt9Q2BDtnr":884884884649,"5D8sgv6GPaCY":295437293513,"9P4BGP2aqvlq":660282235877,"XBiSBaFn0vZi":431798359150,"zIVq3ImJH7Vo":15667249445,"4Ya1vWXsvYrk":901801108657,"YePuYTzpiLND":682477940342,"8jp1UZi7kNjn":477924957473,"qpCPRi1zwISD":235916131750,"9TIjBj3j2QOX":920896795467,"v9gjIAlJrmEz":256352743489,"UfqiOwC7ZZW7":796216128385,"TR1NA3ByeJ9S":30520553019,"5LHSPUL53Z9t":434528727904,"Wohiap0Wttdz":381657067908,"QngKHXX5tEEJ":337647343574,"jsA45K9WflNb":214634942068,"jyJ2eGuCmxSi":536384546786,"MG741HgjQZDB":546901295732,"YlFpimZo5dcf":815023420216,"4YlngRjAbzKQ":502094594453,"D1aDzL6Llon7":851451035765,"nnedMMYsyAXf":582046617930,"qr1iuNdiUNV1":972345472434,"aSp2v0Wipl51":948526413160,"ZX2ZZoAaSmFo":866745413384,"FQ5631MXU43Y":61948866873,"ocVS9mLLdcm0":712302374116,"yPdvIjU9UHAU":936770509755,"Ttt03b1JW4zP":945935850448,"1Rdg8YlxulCm":53977658935,"xlX7MuX698aa":38846826997,"UTjTIZo6s2hk":38565649901,"8KuxCGDoiEau":537077556250,"XUfEAoYVjgGH":952310634900,"45WLro5cP5O9":820146844960,"f4ufEhtv3X0V":527651381486,"SNqXHYuWrZpi":435273062281,"qEvlksCzpbem":61422623680,"zIJUqU3Rf3sK":496813378785,"cgTSeKTTtXef":914787567358,"FkxjtPVgOlb4":983166953421,"eaGxkbsqOesr":792234541135,"3VeouWSUIAem":873081743208,"er6kN5KatBGP":477878117813,"W4mnEzUWQE4w":797611749477,"uyT5oDlAYVXd":380465751139,"kI9f0qIViMxg":596186572495,"90C3kB2MLVGm":630210767952,"60tJlmMyILgm":542412114633,"8mzfcVB9tRY0":698138064894,"LRkvyuKW2L1g":894661605062,"Udiic0D6N52V":38254231016,"WJobMMss4rpc":419028766161,"21s8Y8IksC5r":632124059675,"JFElOUreYXi3":712974775301,"AmDvpvrzWRQ8":342535361621,"brFgZf3rFTqp":597628034283,"oF7bwtngwXCB":740207337091,"IrpcPTa5bvTU":302616857230,"1PlYmQP2hm6R":291212737909,"jJzfe5OkDOe5":853313301749,"3s47WVUAe1D3":819822444644,"vjBbWRbazAZ3":373841258929,"2j5Z4noI0UrN":799609257066,"NbBmQq1tKY2W":960756467750,"tFU95kE8tvSW":1176384949,"1FcRFKgRe1Db":992964533225,"NHyrInW4eaLo":129655204236,"HEcPeLWdpvDK":712834875835,"4nmS8swbs9nZ":376763627292,"vLbGMVDufNQT":561384214799,"vInUYHbsaMW8":45889961882,"SOiFshVqtmxU":421689266637,"cmhYJh7prMNk":249326724494,"CAb5xn0tTKFj":501377152201,"1gERfzdiqWqX":239508144972,"x8rwZi7WORwU":207702024559,"Sb3ChD5Mhawz":529231718484,"IkvQ56ueleMG":168901758465,"5FNhheWyGDFm":610210060492,"xD0c4n8KRKG2":582159443578,"OnEyC5cDTudS":367786516001,"TUTmocs1kC5x":438005957133,"KuvtKK5RlN7K":10318443332,"DbSKwcjj3ipW":310094172858,"kB6YpfWXeGFb":338419987730,"NjvxzrPAHJjK":486295188058,"uBIhGiBzoHzV":653713755452,"k42wxvTAzxY9":217658414018,"DOlzWDQ5o2M8":842583356288,"KglJE8v29eKO":517998054320,"pJrsrlHPEZ3Z":26392743369,"BuW0XMQdjrBM":762292165063,"M9jXmQ4F4Fa5":658647027119,"vxMVE27b2fvI":684854379721,"OArSI7PvfwlR":665195531882,"IlL5MgJinCbL":630992060995,"mtRaPZh1bM43":429103995541,"hHABjnA25SYU":586968478836,"Nm1EqQ9iHmLT":211086797054,"2tiOwSc0ItuS":604213027717,"S1ZgYzeMjcxE":549381111656,"UiIyfDQlX1Wb":816958479533,"jUvud71wzGzz":836609602106,"QOddk1skRTdO":337943288117,"mG7krCjQlRWq":34424385551,"r1vrs4B9Y6tj":454236058893,"OYyzxuXv2vfU":188734697602,"Ku8jcpfxOX4k":600019668226,"ZvYxrJuRDRSJ":818117353091,"udWHImKTaMts":280175733512,"QdDK8j9vx4P5":592426757532,"pJVoPE9kF5U9":706774395715,"A83zyK73DR9b":534210966299,"MERJnH069PJl":126399003810,"MR0Of4jhmpjo":245367331905,"2TtMG8EvfvNG":321591018375,"YWomTomOb3Br":23851981430,"d8LkEzXpbCMy":647668823918,"X0quKtoyUfP4":10922811172,"6aZQXoLdlfwx":439172469405,"VCXlNlOEgFOq":290364548582,"iAd8FsLs45IB":725411546225,"wZPGOLGcoXXt":999470360660,"J1kz7TYOOB7I":7463162451,"dCKafz0KDzWI":902460157030,"H08OOwJG6jDj":360422446858,"p8imuJ7SHS35":344936284221,"UXh1AwLw52Z6":208003695108,"XisBftgAl31i":987429425842,"Dv2T7AV9vXYK":770210839615,"rUFMPokNFblS":110204608880,"akXA921ZO3z2":666621648087,"CqQVivkQsxko":516348777637,"nMcDXlUCV4sV":301589298285,"h8HKpvSu9xyF":666058191774,"syk9s0TkoALb":756937133384,"Ae0kTIR5K6iq":441370549069,"JfBTT4r5QfHw":316109197870,"VAx49JF2dVZ0":194802142063,"3U9uLrQmO6pY":983155517583,"ONE5I11SJI04":683232993058,"R9RO1KyF5tuT":697374022287,"dq8lsvsjY4oE":936962041340,"nnZF2h3VPFiy":991502458096,"rLsjTwjXAiLE":209969317720,"LJf2dngqYJUH":419066632579,"K3w3KgZiARbm":851654257590,"yKFnTvC4A6I1":31237902311,"xgwiXJTDI3jg":509992924001,"EFwZPgYAwyc8":666669076225,"jQNP20UsGc8p":565214237381,"ksNJR5LLfVqr":617791124390,"9moKSbNdnJCM":174768742849,"VPXibiNrjQFB":982948533573,"epCcs3wP1fne":451975968592,"Vuza2y2rFeVH":743174734933,"6psreFFLSyjY":703918689357,"nXLvcVTuFc2T":179798555536,"tsdNh4kVmCkW":633573607438,"8fRp8AGExHrs":372899978962,"NKj0TGGEzmxc":398544151427,"fKx37dJvuKXT":436015539251,"Wkd14LQNalOr":498876154108,"t4TrbbVw5sXT":66693671538,"Szr4dfKfGuHT":659639324255,"PTFiZNu4yJSX":687496183762,"UIzc3S5IihWe":32600908450,"9aeT3Cey2YF8":528535687101,"SBAfzEeJD6ZQ":543455130328,"DR96zjpMqp02":257026882089,"gw08ppf63fFs":389997075140,"QQbrYsIGAh5B":985227325541,"vmm0Zq3374U4":769702855874,"qGUK6Q2tDsRt":584815295194,"MU2hYiu9oV9M":358689483407,"tEesIRAySnNo":421254633850,"tGZZNiRkxbEa":847322828887,"AKAiOpcqjGJS":365227846173,"OFL9LGIjKXEm":633818809336,"SjWMTToScEtN":23660856652,"HSnBR7ynqk3k":117730457293,"IilbrnKF42ux":957746859467,"U0m5kBTiyHx9":874152914200,"FR7CEQIlNeSB":683705053272,"3mHxD8Xf4SlN":863773583332,"t224uKg1ivmg":281360376988,"T5jIR8k5AjOT":806436421138,"DLHrfSvjrpbB":699323561668,"WnGSYbViJFhz":475583928709,"O8ynOXrHWhxM":223824778979,"UajG23Qjs8mA":422750787943,"S3q2FI0zMDKa":377545152772,"bBnfuaBVpIxn":466890354385,"j1uPrLNkDVG3":212062807879,"7fqGkWsKCaSD":974653039596,"5nE2BKAByXfz":206679319752,"mF75xOA7vC35":189758913177,"OOlskAs1BRFa":699087174141,"CUNVKSiuxtZ3":521545562836,"fnv6bYq4BPcE":755344651731,"PaHocat91rwl":31986978816,"dwLtIMOUMfym":287606605074,"bzt0MyK2lCPf":959896164145,"6we2XF36RyDY":620426296161,"Jo9Eg6uhibXz":574210337756,"jI4wjIBSxpmz":642388504007,"2UcgfbxJcgQk":752630659421,"FQH4p1qQx1Lp":993227461512,"P45Ofrn0Gcb7":51535899672,"7n3xjSOhadKn":823667572534,"nZYKcEiG4ngw":471416822150,"czdHSGe7Mw69":256797655465,"WUw0N0sP2OVe":941933108438,"pizs00iGe4YX":457896417965,"BnUXqFlhv1T5":693463074870,"a8RdNsXbakok":41037442812,"a6mGQfGu7afI":246467322588,"damtQWQdfrSp":821479861311,"AHn8ViBTH4a0":506034344087,"CZh057iUSTng":539705975257,"1IJJiruvONwW":518340007965,"AjVd5kT6fu0I":803726442428,"tufIOTbptvD9":498010509514,"l0yrY2glEZdY":65769974439,"pLDXuQyiBbY5":74360450206,"RHXC7LdQruKl":920126073932,"Qsq4xOTOgDdk":509688182499,"4fuRcdh3pIyM":424186495913,"RePyMnUb1IFz":495493088403,"BfHWero07sBA":406343356033,"8vdxYVAFeh1S":623833565658,"ChMnU0CuvOXH":263824153998,"eHHXOAbp3JsW":437228062376,"g7qg9F8LAh7u":280851632328,"jAVK1tqGtiOB":649719963247,"QFBXrLReNSjy":935468859211,"iD5TaOBdwV54":415697116441,"PbGPwikj1NBJ":663558524984,"Wa4202HcPIJj":591290952602,"bMDZqprhRP0U":657579160263,"ejWPit8slKwO":586267877957,"JaOTVYYUtP3c":168164767644,"uP0GBTxRF72i":863594969998,"mZzsn92OqgVP":614080983446,"BH9jQEHLSovn":664053590571,"lG4Fy3AsiJ6Z":625767690082,"ux4rRyYdC8g0":841694145304,"nMdJuCdsMtw8":419584429534,"0wPJQ91PFUHN":383761166622,"c4WqPPiFv6Ob":321418884907,"ixA7MEF3hmOM":609292312332,"q7r9vkhE5y4d":244220327394,"4eN6VltwDEcQ":241922890157,"tID0A58VGdsK":17364816292,"KpTyAIG4Zwnl":432432336774,"tovl7FglpoJD":840488124974,"NqmgKd7FIoGz":108402543091,"BNKXhq8GTmVh":234455802592,"ug0KuV56OlzZ":870510539082,"TvYiisL6FaIW":342598627546,"5nDF0eWsUwwh":849490491875,"Hes5poDie5VT":99668787158,"kkS4R33uxPOe":860200644262,"ijxnLD0L1wNU":696777211240,"KKlAgb9Iiic7":178283248823,"FuS7ZrZhLS3M":383437510014,"UJbbFqIUvGFt":781223037979,"PWQ1gDBoKQTq":811572433146,"VLvz5huCKUKY":837498080333,"fImJ6VyaAnxD":335202461191,"WQjcsI6l2BqN":261247118586,"B3AJYIfVyprx":676078516543,"cvc0CayOn7sw":66135333617,"iXChSNL3nkXp":5087569770,"RbYPmZIbX7Kk":206458975051,"7c7z2UkQ5ZDY":492315491224,"inmleYOOrSjI":44965432523,"FnsRoOnraTR6":46025906933,"Z3ogcOb6Adfr":8920344485,"nQHORj5QCKlC":543641716055,"D7oxwt9NN8wA":452008778143,"AX1XiRmamKgO":727209676301,"NxhcCSSWiks0":753563516763,"Mscng8dzuSWb":920795441355,"zP8ubP98wMQc":780495341566,"um8FcvlQrPq9":978141522058,"9zD52jDHLeWj":380696321715,"APU5tdYbLq8l":778615802922,"arOw6j6s5aH7":315988943409,"jrGo8OFh275j":649085689339,"5Yce8y9z1v8X":816425145513,"x79RQlZG5G1J":769025826096,"IRlt4xcXO6Pb":53039073600,"zrYkEuhskLyA":276144132199,"oO1reDgzlAUc":723923302451,"NpSMUgmqwbpp":430185443219,"13qJJ68F4nlg":417579505755,"5khpBK8Slcum":40754626342,"ehlS4qaAyaRL":203484257257,"mEDazJ7lK5vI":475712867485,"K6hNepjHLbO5":106737428772,"hj8OuiUo7d3h":934665554061,"e1tUOSYsjaBz":703637882343,"euYr6dMnWaLd":972345917223,"m4sb5qUwaeRf":764040495490,"vRfP490dMetj":801186030905,"Mt70QOeB8vpc":601249443029,"Cc1YO9szX4NN":200218449802,"LYdxP0Ijj70b":873561744096,"4uh9CI0Z98TC":462187497268,"DHAwfe9Abk8z":694018056459,"y4BG0VSzIADh":710056859428,"DMgSEyPJTPGV":177393326303,"V95vpQsixwJV":280312013165,"SnW0lzZAnV1o":842242056713,"HseW8dD3eECP":730978278492,"iDAE9BuC9yT6":831379407776,"Z6qn2flZ0aIH":343653518034,"tv8rezuVKa55":161961226121,"TRg305xhEgik":90775168408,"ApJmXBvVz24j":406381879968,"4WtcNnUrIQJV":692864627027,"8628cq7w7KAf":332637373467,"hQVUVNlwUR0A":482134184030,"hSKPIeHtzngS":195933266858,"jTPudpFfKKFT":857159257828,"jPP7Mp0wjPnP":547852964531,"2MidRQDNFOb5":761081800371,"HctK7CecTThB":555318976100,"9uJzspVQtomS":718860979179,"8gL98LjwztQC":290246318689,"J1mcL85sLXiC":272007603653,"k5WKdvndF0TL":345720237911,"coOvgW7gP8es":628840643289,"zEmAC4q1UmvW":920088747907,"CuaToJxYwdI8":145289542401,"TU3lVUDkx2fk":77593363706,"3p3IcvvGxs2D":624744261351,"T6l8ifjeTHBj":112291844313,"oddDN2F7SgE2":451461036133,"689WGByC1NWx":782509489714,"YDJj6frws031":875085615956,"tCSjrkq6hPDi":99165205181,"NiBPyW2GSemj":824242217857,"AJeoBsLwaPKv":74348974213,"HxU1KUeSL0Cb":599458616050,"btXyJ3eRjsin":123283056155,"IWUY1RcOi7jo":252003300484,"mgaiWZvNae2m":607235324232,"GCjhjIVG4Dso":744455279469,"AOrHjIZCCdeo":692504224836,"Q1I2zfqruU0m":788124332460,"gzjIPKnXJ7zB":24226606164,"2Gsi0RWMEc3I":898352014202,"qp2tt8x65hw5":299795930698,"HmbSUurbtVhQ":244886450054,"lS4LSFMCLRym":179991762639,"Z9PErJnjArFs":186858274874,"GxD95RTIH0K2":523691497158,"uNLimkexGwPk":276214966447,"Itrq6HARa677":474521719890,"QoS1amJG3Jkv":954951289934,"9Qdq4KDLEnvz":565790170515,"hSLKiv3Khl3U":588797397631,"06yxN5DttYNh":984058737055,"LKNy8HWZ23bA":528929337499,"Al0DvhbKbaoC":784187677782,"noJsdbDRdSnB":463920632650,"SoKbskdCZ0HV":657169581110,"8OqU5DLHIj3O":590025599371,"qK76Xt4vkKu1":889661551589,"r9JWwf8l6Orn":296282033647,"NBcfEOwkRCMG":696235040895,"g26TFdf73jlT":60985513974,"Tt5YGArBihB7":83644963383,"ZLog43I53cMH":663918188881,"IM6HVJogOC8f":333041790166,"BSXTdKqSpFlK":47234569104,"FlmBwTuYfp13":241560350553,"0Ak1ydzCJkns":743829541414,"G8umYz2d3oZr":951252095987,"sX4JGZ0qj2Y6":285186589129,"Uf0Kcd9BG5gh":388449851705,"PxRbgucu5zfk":610868412498,"mC9nwbIP3hXq":740531935744,"nJmYIFgy0HMk":265989049621,"mP8SIRr2aMXp":806363336937,"M5nFEVgGwPJm":217673078592,"p3zmOeIPMd0l":346311411997,"d8O8igE83opb":826221177501,"chI7uCiNE4kE":382117810980,"gXwaDTte8eVl":569500344450,"dMY4GjBAIofK":385915312402,"vWsF4fl1lX03":843159771701,"RhlmlHv9r5Aw":26585938860,"aFkifWOWtUP8":932798549520,"XEB3nTnq1SS2":804647854573,"QsI4JvUngibg":548359416592,"CrJrYavzaL6Q":262901208507,"TrsBtBxvniol":760302692588,"seinYMc4vlWv":885169673744,"EN6UkRZM4x6h":942447681232,"6XmeXUSN8cLt":533735165481,"6uRZVCd2uc05":268853598322,"CeYfRjD67A6v":55899882244,"fXmeqiFgL3zl":170072831429,"mqlxcRHjT5ZO":668066816465,"tcxJ0oljZhly":301971229726,"zh0BJIvPWDBw":640343465764,"nXl2991cb604":530848094719,"O8JNnUnoDkjn":629614468478,"hJcuLJGkU9Tv":309419273826,"xvAz2eDWLIV0":621377542874,"qDTfocjKH35U":330017657425,"gGEsYGROqvO2":822254959590,"bYK289cLPw7Q":147061884646,"tN1zMLrVA7bM":558003448142,"ibdraTMtuOyI":984798165014,"8rWTl04h5j64":366929713998,"ld4T48eRK8Md":112982018293,"c8ujNH5HBe1d":291491134635,"fuyfKCDajuJG":279430993516,"01M22w5mfrzh":122241615699,"gVJ9eoQi3Bc9":244620963920,"BHuFhIUQwG24":966091865691,"VVayqt0U8ndJ":933051457380,"leJ5AcyogEq1":986056308557,"pCcPYSARRPGC":251237500098,"GvxkMVYvUQhV":453701590619,"qR7kT02c1jYQ":817842703861,"hJkMNNKnX1Ub":328983305141,"m8Ttx6dIiwO4":249063868379,"StpnISEmESGg":935517048843,"ikSdAVudUPJE":664435924358,"XjivFTtQsXu1":650524401090,"rwmJrcEoaPPP":18025395886,"xGv93gWkBDbc":266020920546,"szYIIh6oJHEW":755036496182,"kQUpUvQCi10N":260949626904,"mHYoboAz8rn0":47084290481,"72GtS0iw7F11":853713090586,"sAQglAzp0DbX":842406248349,"kV9n5ifkpJ3w":224702180665,"bjwzydXX674F":410653016906,"W57vXn7KwjZQ":603380236429,"JlGW8Q4ojcGk":212230758240,"T2eOaHFFkIFM":629652565046,"sLaftew9LOMm":854545732215,"qIVFaleTUfms":839943755147,"S45xm4LORdcX":278706525022,"09DTHDb78EFh":49781857939,"whtdrKjbzWkb":111462309997,"XyWJvGECZAXB":54551553108,"ZmrQfbM29iXV":437095787093,"joUntDVwhxxb":201261837310,"RvtnP8RwNDT1":550168507786,"TQS1Zoy2cJUJ":656351284615,"1aKq5gp0eBwr":349810717971,"DCRbTrPWgjxg":381703560536,"0NFV5IXckMgK":14695224596,"hqdcLTXKNtcK":504681997945,"30ktEk3kCQnb":172355950868,"iSjxeW4d5blM":488255756351,"crqP52qSrMdy":711716272483,"16Df6V5SVc9h":611234900556,"GLmyZeD4EA1x":4736009126,"FAiUjYRGqqHq":881342583561,"j6ZyApt6Flty":724248952366,"piDf1nENhMBb":617066589598,"wpOIjPvNTjmX":308849093344,"GpfAwnlQ4rrP":124010638883,"dJOrisnlbr44":424210720101,"x5EYP3trha0s":190225179948,"KSst70pA6EM4":869591105740,"7nCsnY7Zg6Pj":279281836994,"GEPmDBVmHMOt":467274820886,"czSkNyN0U8ND":880021668201,"dpi8AYvI9HYh":680836081051,"TsOkeIrSMhwO":16248443037,"537e1CAFiMee":141126182412,"qTXPZeMPpKPr":12908915454,"jujlCP3teYuT":327226359039,"CLASZPdeEQrS":29627426188,"bcrhaY90Sb24":77672020671,"A3i4ZhEMEtso":135946879480,"3DDtVFPQ9QOV":733627301844,"MHZuYYvAFa22":918828887399,"w1InTc4tPTHI":787702909968,"dvO1SjUatItu":952378050198,"sQQOnREw1fS6":910795847299,"Q0AG6Y54D9Q0":321137896468,"IzFD8RmBQfoe":949733510386,"r7IQPvjyVub5":155981465224,"h1wRMWkdUJUU":707070643731,"wNjpksVr0nG5":795384649727,"3ac133V8q2KH":440161556880,"qrVfKu9ryoFc":570490961410,"tt3TMkwEZwFF":58802395591,"IFDQBQ6FLaYw":168353307262,"VzJT9cZQrNW9":272317306239,"cPJNpBkSLUP3":843632116561,"I7pZataXZNb9":16192546911,"wvKcBVEXvj1q":98450040799,"LrSZ2MPcllQ3":10017605970,"Y2DsHgbiOj0I":347239219575,"aZoMYi9IRWTv":188598649860,"IuDCXzQU2CQ3":926043403349,"TtAPzmKC58Om":365416031606,"Egpnb85RfHtE":602318085481,"lotNpmwXjZkS":676456015187,"qCUW1jFYHqzZ":447746924504,"2zhsj44SQHoV":144094884419,"BjmFUZVIkJXD":103968901604,"eRKcW3XdxIQ8":924522754797,"664JpsyVrUwJ":880627091957,"L4BtBkpUSJh3":952896399469,"A3OiWDTtGqmk":597434914627,"690tSo0hAJSA":852611138629,"tseBM9epfTtv":225304509089,"n9y0WJ1h8uRB":769903053836,"lxj9O2emuW3I":941586452531,"e3h8J6lf9OUt":363654778766,"hySnsFOKhUNH":270937679637,"BPV6OxRbK0TK":835764588881,"56MHG1HKkfYU":583452039712,"jut4UeXp8Dnt":682011461804,"3Q9P2CgUjnQ7":624723347101,"PY0h2aihHVvp":302678838381,"ZAYoNgzvSMdk":933076539664,"tJml8uZctf0L":720909641029,"hl6cyK1LOofH":987496798996,"rQrcchj7tkCX":319682296863,"xg3YYDxPbHQk":923607316820,"6zsd46PSbyZ6":59270565611,"XqBs0v3IrM31":462654470038,"2WJFK4G4mB6B":338684061174,"ywFJBFEAhSpU":382863507339,"iaWbpO3FTwcX":687612276276,"z1Jb75Lm2JTo":456826333765,"qjV3km3geF8x":809647045837,"jk3QkPhuT6lM":531607411879,"tGK6iWccUvFB":121037878637,"xfmgQQ1uEfNU":602748809186,"uen2bwvWAoEW":52405965371,"yPMgjDLzDXRg":653539725361,"WdZFfP2cVDfR":834341386538,"81ozKytMxKOg":719486282062,"ZZbOQMBpuE6d":518840912431,"rBJpSB5iYIPn":113435250876,"i085ARFY6jPu":804747613972,"yeCAmbOSeSLz":771648305976,"1vbVLTqJ0RA3":371923873481,"TdCcuH1RLA2m":851315714694,"um1RHIFqnzmO":655461814931,"Xkf3HyIt7sRW":100785954398,"IqhjwenICJRY":809123672122,"Jn9YL9udUU9u":442936974151,"dHxwTqPQ18z9":960105220315,"2GCu8xNxERZk":74433277282,"aXlPCxAsV6fx":618405688499,"NG5YkjM0Ak8f":333922589443,"zfUeOzfNYmWn":165772728718,"dvTYVevr98Ww":83629021355,"FsvEl4hQbraj":157634552907,"K7bHwshwHo7c":265600125004,"7ITbNsHQtVgA":300562078557,"sFL6AfUP0z1t":778093734951,"AkdKywFPvvpe":862021628222,"OL6L6oeEQ9fO":595907757599,"sUqs7vqhUfsb":469734507253,"WXL1ZxGSwKR4":184766863750,"ICyNF0b8wrY5":191710538909,"3xEKcOA40C3G":586033566800,"tJIsaKKzy6L4":748890032474,"gFEJ5q10pIMU":211053208063,"XrnhXp4U2FFv":713620149167,"sAMLuYAY0MBh":22057570123,"N6C5x10MPeCF":108072678299,"2ezr59Jzsoqy":246281022635,"v3j3k7cLNSzr":905692049620,"uTXyARnUEJDT":83311965813,"FojgsuIjJYPp":156552063738,"4giMx6sGLKAa":648768369111,"iT9faV8pQBRh":287009127327,"5dWuRfih34fc":548355609205,"KjRDCusykVZ3":477432152193,"tu28PiAeCb1S":726523235480,"k5k8iFKoVOUo":663147116585,"m9qmzDRkmERA":821684812337,"ZGNFOsFhJ4Rb":783233871778,"iAMVnf4KZ73K":44134233642,"iq0ohdCYNh43":975640960315,"wrR8JzNTt9hS":237482581460,"mclXmpuTilDw":511175414340,"15UOrkAIPMBd":6574111280,"0QuitLOwmsDk":987529483958,"n83HJxQtuBUz":373734014276,"8C2yfSd9K3Aq":717600518632,"GrsI0MF3Cbp3":838257140528,"1vp05Of8coTs":208855041762,"LAAFsn0zi3fa":863246773643,"cV4GDHcvOLOR":933077687242,"FAMMFzE14pxl":464489124032,"e2dJ4ZdLpCme":34415836897,"9gTqrG24orFU":684408029876,"StCw7nG3GRFF":732639637916,"ixN95mQzDhHi":788940459964,"sDMMfIMXX4pH":500445646439,"ny3AqDlifQvw":596095322847,"vihHRNFUdBe9":875769833027,"59EiPhNvVK7T":564050074327,"qzn77ZO80CLg":174012935270,"oFaftngbxpoD":558001978637,"NWeMpjmI3Jrc":946050445005,"c1Ht7t9C84zz":475568946833,"wFaAye9c5yBJ":526814106450,"SIJZibhTskF7":699992613868,"vLL08UebBlRg":79139765624,"UZ8Jrl8czqs1":915496624071,"8oqyI6XDnrUN":211224030418,"1gKWc2SB2Odu":794207735651,"R8z7ZiX0hYIS":55272139796,"OeqH8nV8gJjV":504359592115,"dDWxJHaHoqVI":585816349320,"mFtcfTaCj0ld":444909421739,"bpMPLtDhfBdz":567667332169,"jW5ciaOlUHCn":319198695411,"Eh37alZYR2xS":963324899192,"6IKSAHAsUM5E":459199789759,"wWzvxtrqO3a2":772402239983,"xB6pKi54iLib":174799995560,"leOaKMakFLc0":302672493594,"sRazpdXz8bIN":513237246734,"fryWbUJYMWwG":915906940725,"qW0bGbuCCDf9":224955591028,"D107kddTBgjO":284774182609,"olbp0udouzGO":837795680709,"UF83FLZznG0q":468446236348,"QHC4cALMe6xg":467840563578,"Pp7AwleWRPDl":647272145870,"Q3a8WTMcZWFV":802107413905,"OaK6cV0auDa6":775594186372,"Kujk99CaHcM3":30965959839,"0fcPdIIHoeWp":759224450452,"eHWeCwrGzUYi":722039648420,"sL5xEn7tVwqj":618749991832,"Jn7StCG1ttdc":1608029921,"CoLWCod5uf0b":344153709567,"Gqq7b4SmB9jl":402797579174,"eqnhVOWoH0ZK":261684226251,"nfRSFU4Y899u":83251592873,"UMrUB2NvblJH":655016475041,"cj4hvUbxePFy":503002382317,"1zmDVV9VcQ33":553938437727,"jfeNgu1XOWrI":352468630474,"DmJbEo7avQnZ":369102784353,"h8xwijBqgP31":336930801455,"e3jqKAg3Yf0Z":191407550436,"PHjC2FOx33fQ":373453809217,"EzVvzduvEgfD":235382442835,"3fB0IhBCSkKs":887724294690,"b72psTmTWgX9":721321037906,"Gy6TFZUfPp85":932204496614,"ChBCTwzodoBk":898496286399,"IB4XmrBDvtcf":149977715099,"GMBPdEWm2OGs":593620977868,"g0MCxfFDVncR":137067653979,"v9ZEs03fK1HZ":577195835968,"SG1cL6ztCI7s":237525272313,"eyU2QFKy0jdq":736128570788,"wDqcrI15hsvi":806480900615,"aLGM29cWEGnn":280057397278,"iWymOdhGD90f":675076244442,"YmUcyC1kHMO8":213147925296,"YMkjFP2o6KDw":874275459605,"RvEcZLPc2H2K":669670678005,"jtdMqTmCAcUm":22773620819,"xT4ZeOmjYu5T":168108590849,"WS73oRc4YOuM":11449309854,"HwdCZu6KlDCs":330310571122,"k0Rr5YU6DrIa":462615856670,"8Uh4AomcH5Xy":186841786002,"E39hGA3TaPac":860213235539,"dhxZ1smuldmD":16716910251,"DUhrkbPHJZVq":386936247470,"yMPCDRNW99GA":830464602274,"emMa4sPUsYzW":65727223030,"xuaYYouSH3eL":771844709603,"aQUbXhcc81ve":57803512139,"Idkm2m6DwH5B":374376851413,"gAppib3Dj0pM":943740679872,"C6I0UqOqC9qP":392174351991,"pgkwDTBHBeSk":476231684310,"wCEWpyxTKd0R":64606427678,"51SowoxZan83":419432973728,"1y6Z6I3XYa69":185339788748,"rIonGYhSgvIK":182614386287,"ax8KQ8Y6rBDi":277772534060,"4gwAbvwGJszs":487010267287,"qV6N7wxNCtBu":962648153130,"txDxGkUx7r0o":954801679840,"TR5pVLGcOhYw":833379001138,"r5Az19VwaxEP":578802936041,"Izr10g1DPVQW":338750361997,"cVp6uQXmq0oS":465527723720,"oHbVUVsVsk30":136233403664,"bo1YSHixOBOY":592887108128,"E7reKahfvyBj":320357809740,"eUQWLavJnfJL":294253532951,"P1hl07tgrYra":919605183444,"7mgkwEMD3K9C":53008173193,"mqUcpYiYI9Rg":111018581920,"GEcfZEis6fNx":285598491925,"BJtV4s1pIvKR":682136894606,"e0ZkMk3dqhUP":947850850915,"FyX46synt2ru":153991526964,"S1Jxu3sRbKaA":355293046291,"gtNLiMv0M7TP":280849162530,"Y852jHCogsUE":989591086025,"ijgkOhUsm8VS":842780964368,"kq5aTzA5lRuh":910434378163,"9NC9DH8iBuDg":679193751941,"wP9vWyhfDI9C":231616789975,"n4AZqhiemOBv":372078705369,"RZzj4ElfkL0A":866824860650,"xOShrBLBiOMx":338047236879,"tQN8ovf311Ru":674451465048,"Mya29qCMX8QK":270355750265,"SOiMQvjxbhQd":948712640308,"JHajURRaWh6b":963751344218,"lAhC0RE5YL8W":211653379579,"QzOaULgZ1kld":299400492848,"7cohOEkFBiJt":760764496334,"CFscb6UNEWKk":206052817510,"tMr1iMZo4HUs":419435558480,"rFud0POEbfvZ":819630866727,"7vS5zX19PI2H":82839837409,"IvKzO3cVZaCa":744594074128,"7tFFL4ACj3mG":814643589735,"C5GijSguTSYf":324351161835,"EccJc9tVvr0w":672899198255,"K8AHzINRn3k4":346809961649,"z4WLCB2PTiBX":357343284917,"47SuffgDo6PM":699413823825,"OJWuUvVfbzVq":162275276067,"fcLpq0zCVmbD":287920555400,"omyBWsutqr3N":101962714832,"uuul13Ibaqvc":506976545978,"QughRnLPRvSl":115944500162,"ikxp3TGjtk3G":944500656748,"l4UTb9NRggvI":12363236301,"Vx6h93b1wF9T":158051133347,"0RO5jSGXFeFV":829949437372,"oImBIajbSMnT":282242430648,"lJ6QYgf85UYM":51179222592,"uuYzw0PWjXdW":370533960182,"1afIPm5uPhcz":406768242262,"LodmjlgaLJQK":107756430096,"0BpGRuk6IvfJ":770638836733,"0SP1IwFmp0ct":435682324578,"VvoIz3OPjJ8y":568073177489,"nG5fCXtn3nK3":471592861576,"kMTm5BRDvpuk":49155105335,"08KKnrPjcfkZ":453102587182,"ONynELEKuyaS":748009221834,"cuGKOr3kj1Nu":46893325527,"Dh4JaOy4pBRW":762129706310,"il7jtm6Ybv2j":633078117186,"wXK7EdfCypDd":635233761599,"8OApcIld5F97":477957037674,"9ZdxgEmQO3KY":423934605336,"LKkUhls9jh2t":688139300846,"Obspu794lZAI":804492914610,"VoVUrkGLDeoi":902682006821,"dCFkJ6VTfKH5":513554261091,"d9hUtK8dsBxp":150260201594,"ki40Or10e01k":50546069172,"DdMrTfoNqdcP":262761968020,"UYq1NaybTBkv":504564926526,"yLLGg4Na33Wa":374605207999,"q778Ngcbdtk9":680459309909,"BbZCbRrcol8Z":359506430286,"QhpBeXjc9fVS":634883127814,"I7SbEx6k9Usq":444760793737,"vzoJG2I9NPqz":318057108447,"6Zpibljq5Gbj":299512900964,"wyqNQYVZlqVr":487613307195,"cWdeHQKJ6tTg":67690449273,"Ch1f45yX1dAM":816530025285,"P9hqIejtrsnq":287044943646,"Fumv3UElHRm8":677507183035,"uUGbF2FV74Ul":948186767021,"oJNLcXRRenfl":531452377892,"DvKIfTHVAic3":359489433676,"9sYOVUMnYdAe":631804852606,"ZMy8HoUlUjV6":730781335065,"sgq9g2JjISUy":602315016630,"cHfPCqNc8S3F":696485186694,"EW9PGN7rkZq3":802940424759,"jMWfixfsVSrN":533372651762,"bqSqiLDEdKEA":566503874992,"hbojDvYo92la":868355506568,"sKMHIjDk8YUG":641391398905,"tQUhGei22qSa":684556629386,"NxdHxhfqwqi8":38758419277,"izOEzzJ8NUVC":439406421992,"U1wuxxhWONnE":756104025446,"6KsngogTn6Hf":774927013905,"3sT27pnPjrWO":539346891831,"MC7jXRRWHMhC":214081150645,"Dzbjmj7WXJqF":610524785003,"jAU41mxf3Kt3":105221931442,"oc0CVek7UhSp":851097535320,"qyn986aJ1nbG":120628744439,"z5fRhv426Egl":962993015041,"Sq3JiuyppsxF":334235025990,"fDMORuAHePOZ":221889734320,"2RRkHbc0kHQN":455940992429,"mWNKVXG0xPwC":29863957603,"x0o3MXM5Ranh":499758499774,"ItCxDuK2txak":969655478006,"e0Lh2Kc4At7R":332708319679,"SHmGpvsYIwrC":959858423176,"7S77kiLRwQpN":611940994143,"ESH1fe9hIQYN":673120081218,"mQ6wakpBQWWJ":232005523363,"iC0f06yIZBhc":924717197671,"RKdsUw72xHSW":377399364985,"sDXGWiWeILjo":776272220693,"3UpgsiNC4ACO":372712764103,"MN6ua0W7ykba":64207445795,"8Gfmd2te8irU":436127963590,"aWfu0cVwDrlX":40420683274,"pl0eIupI0F0i":690350226200,"pg6lN8b5j8JY":559160073190,"DxEHJUHXiWqi":971229715878,"BFBlgmheYEOU":281151068337,"MUMG1NVNd04D":95602759088,"lXTN5ofKn2hS":85978391144,"ApP1kBJH49lM":34528282066,"CZYr4mXJrwcD":352193749912,"1tPLXqXBnRw3":109066504795,"LdP8CV8KK9gd":447471875852,"9xH6jvb7k1kx":609044785464,"lbN4Ul8rJxUB":774372890891,"Z8cwPUdl3o8k":108960765098,"hnFt1HbkdpIp":434033937309,"8h9Yxi18QQqG":328322561196,"98Ogf158bdsY":100818733944,"iNbXi8hUi2hz":967717288588,"bAmlMuTtX9pF":919689217983,"UmQZh17Z7G4s":242989077755,"pjziGWRrPfal":837910681678,"XzGss8YB6q2A":566678062763,"rXHoT2IWKFSq":764974000883,"4fzuOsWmnoNv":998534361564,"FJgzmYXOI7SN":6898427990,"mEl0IUkeBaJM":514302429470,"gWQIGyJUuLEM":691719495285,"qfIgu1BHqwKm":612427687907,"3UVyOfKy9fQj":126804600041,"xGrfMpdFUOTe":869272526581,"vSW9k1864BpB":802584576433,"KoduuSFMHyEi":532007150467,"7VP8Uj44yk0O":182385085922,"sS9Jdr2hBbzr":771371689605,"ARMt5qKsVezI":559236181672,"BtuCBwl39jyB":909651211407,"IHs5OlVFHYlj":860494436069,"RR8K4SPBJvwq":370240036576,"VuZO9byvumgc":562176644117,"I5YyuZMqtvk6":250319134098,"fsDxIhbRKify":720290687973,"lk0nOZFDS7ss":35858544425,"Xl0OKfDqiUlt":857383242148,"zTTdgNkQOYqB":771475711987,"80DZhJ7t02ie":471868230989,"TJ8dWgOaXltd":813685953556,"h0JwTvrwmstx":494519750261,"DeWX5xKYkZkH":456274307609,"5620omUfmEgT":436614813432,"AAzkOCynH10k":750760092905,"qeLi7iromB1Q":94272745321,"qvrmxSPiJ8ou":953061589597,"mzkDdBISkw6r":582582947742,"E0UfPnsyq02Q":157985388813,"TyfrsNRkzHP7":869147989184,"pf16XlaUvR5V":147725984016,"E39Xl7PXtm6r":529400143206,"DZEoZUVDZWAB":363715006819,"cjAJUe8oITLV":291855182604,"Bj0sdyh0JMiC":939573794309,"AXTHyRwFSyYZ":601593541658,"NqBLSbIAffo0":646937965230,"yBBSYOOcyB0t":159995012399,"pHh7E63wy6DZ":781390527592,"ihawv4E31xag":770838354224,"UUOPiRnZoLJn":258985896002,"mA1qyXCe1uzt":233339950265,"O2CnVsyBwSNB":463271269095,"9TW44cBgQKoI":998206051885,"8vuLHodBZWFp":466712701466,"WqfBOSGqbseB":453190073228,"PYxtTanVwPms":215004383686,"yZ6DnPZTOa8v":669344461313,"sGk4HEb6T7RL":753936540215,"Kd1tv1HvZM6E":241006829195,"l4BItFGCZilC":332882494444,"YbmY9FsoddZq":155502325795,"wgocICWvrTh0":386299973425,"eQwy16PDpiAQ":732082657568,"AyOYQDYjH3ZS":186546114940,"pxxfA1tHdKpo":8852226795,"wckTikcQ7xvg":484765351925,"PhrpcBlf4PqI":818338054109,"PsvcstJG9tTe":181384797550,"wED4LQAwDG0q":547013033226,"nk0SBL54pAHq":714355110187,"GAsk1DhRSc4Y":895374629994,"noJ8DO3C5SMj":702839266583,"w74nefn8mnFP":920453870391,"rLQMenleg82l":721633261364,"Uwbsdb4vpLOY":670351876506,"LOiJWyN9GNkf":56894577152,"m8UtK43ZFsyX":120269394231,"704X0LPQOrRq":869501452147,"pzCW8rE8Im6L":729137897161,"m8NtAZ73qEsy":907471116500,"LtU17lHYwcxw":763803851862,"McDUSnfxrPGY":945814788726,"OqY7LFISNMhG":139213552716,"nCQd4m9DcVe6":322525503069,"WceXSNh1TKbj":904092637101,"BKFJmeAMyYOW":116862402056,"BSOLB2WSGiqS":909583563666,"MmwN2Bm2TQ0o":415187443117,"Qmj59gamePBe":726635232023,"sNRzGnbWcy4c":675449419324,"O0mA5XrHa0PH":439867166222,"43oILC5Q1vpb":938927486890,"FvM7xAnn5RKB":635361280199,"LTtusxgt1cTd":564745545832,"uOzVplmHCDcL":728886630333,"l5LLCyUHqTBW":367438090749,"vJ7HnzkzTt6B":741453197848,"HpH3gyZKS2KD":117890841328,"7RW9KrofA8Xg":467105790242,"AdVgtEihUx3i":454075910077,"n0bqy43h1Uuc":159230177195,"y8Nnjp3XeeYQ":302188837795,"eiEpfkTzuFwp":199927602237,"0cRSfrnZM2w4":652315951050,"ACKG1SPgtjsD":63432766985,"WHczN33xj42U":600297179609,"hRr4Ki8uO6it":819295133067,"tGz7ukGV7Pya":368933755469,"vP4gYZyh47iV":540445894851,"8DivnUOiYyDm":673938608454,"UBYyJVpkO2sV":944788956355,"ixzp3IIr0kVg":444365834145,"9nN7KxzpqUwr":510521350430,"E2Wj8CsEUsbh":116041366004,"iI7tPExwbZA6":285260354142,"aLtr63A6RL0t":438366279191,"K8iyRzG4wndv":368853960518,"lvXx56QJ4XeS":393621474868,"LeLedYYt76vJ":304026026045,"z6gaUCX6N5Vw":383412208158,"0SQ0Zbgk9E2V":96892793801,"J3XcOjXc8ldK":365987237361,"FlIve1pGE8UY":89102384311,"bCt6XkRvUSiy":665919533363,"szR6oXc6HnJT":202171794633,"7WR6qIxOrFjX":744575204575,"FbnhmllUD5mJ":207925253498,"DAl51QbuVsP2":794100431211,"GrxUolZwtqPQ":188642768445,"LqrmVoo9PYdT":484830195829,"bSV40htLhEXR":817900463094,"N8jLL1NU7Mbb":493787696705,"WuQqOwHBS7SS":666500096436,"Ea5s5I0UHZKD":694221387879,"1fWYF5Yxmope":468350143253,"iZ25UqH8nm4o":679354038772,"RL7zbZUY4Nto":537447906918,"dXoAvXtJXpVV":743483150607,"EVTtiVeDWr0S":903324173989,"Jge1y0o09bab":402400630381,"ZR83ygtr9iZF":260730021117,"fzLddTyPFpPY":815129002023,"9RnMnL3wslkj":220393302011,"iEoZ9ujAfX66":407402948621,"hG9uSisIkvXc":612516489730,"l7M4L6DazFnH":181920057444,"MHICafKcnMu4":831624962330,"aISafsopHYZq":868978970508,"Qt7K5xDRwdAe":665901064218,"q7xtf3klaXBP":340603186559,"oKiMBHT0SrI3":582397328055,"0hjDXvm2dZzn":494777013927,"Rl27gYvebQLg":265846762450,"Ia3vbMLmSaJw":714965950396,"Ajz1L8gKMFbO":658623988832,"FqHaVk2abYCY":676640881553,"cdVYw5i2glrB":771696025527,"2ZG4s6r45j6b":382257591646,"FY8JLbbSktWw":272423998664,"d9m5XT2sC9wC":290593484476,"PkUutOcNM2Nz":834977290212,"cnEmNpNGL1mD":219786243696,"VYlOIxr3qGO1":152655285366,"kIfsUNXDaX6b":869218404913,"pGAde0AXSqzv":557908700789,"TgW812MlRXPx":579912283147,"94tsjenteOjr":974507460893,"M6FhbXmAXggQ":913492598288,"qCUkmxlnprOl":390073434194,"nWIyW244aEdo":785377327582,"eJ5CbxpvBhV4":492616487581,"sAXdj3Qdelkc":878072773865,"hRxSHTSddgCi":849464249915,"la59HJcoJjmF":804146236285,"jzOdovlswh0s":720693966503,"hYk6nz70FofX":595433873516,"dv762bQRtjIK":532703145185,"6NhxPumYjJSA":877216104849,"Sij5aFWcIHZZ":273002339305,"bzzLLYrgOczD":741931037945,"Ppic4OrbMmXG":593917853355,"4GdIP2VPi5ON":916218448005,"05vv5oAKFxci":44709671239,"A24t3gYwiav6":13970072641,"R678lk2LPKyS":711057601864,"EJ3908SCe2ju":162952696773,"1CFuJbtWEyWt":501060650071,"U5qmtkLBdJt4":283030241003,"FhIFFGaetuF1":232014621192,"sjA48DvTmBK2":421765648608,"b1fSRq2OeWin":993534599121,"IwqXdcsmKuES":785085886408,"f5swTg1Rzzss":278143761804,"VMoRgTIY32VK":524318725968,"lSQeZMsUnF9W":593767795215,"E1sFSgQwytc4":943452627411,"qx0Ki9YpMoxJ":970461350506,"zxaLb5evvuAp":2688743158,"E1y7jGLH0MnN":485239711529,"N5dmkYrPhw6U":910171396533,"obG62YalZnu9":425968513619,"YJFKRRUttaGw":956371573017,"INtHN770EiYg":384538658961,"RR32jAs1FYjJ":774420745773,"GtMScXEKMuYE":680901034558,"nLwkZHESv177":162636858237,"8eVDcDADOFro":410433727876,"n9laaTBRokL0":638336809058,"orFr1TOcycSz":802786390004,"Kt2V8vO4mstu":558186376407,"SttLZ76F732R":468982576190,"tedXVblyFBPE":539691110099,"epYtq59rMTBV":346317164163,"Dae2efxPLyKr":7551291242,"qFcfQfZWEC8N":496358673178,"LLwXTvCEr7Xd":717223370846,"eifLV9uteFo7":969778620965,"tjBtPvx4pUAE":706007192900,"fSx8gQTaJVYn":61822093312,"dw6U1GNNhaFe":986715614274,"gcjrMBb8Iqgs":196453990201,"VnvNvJUED3Re":566175065972,"660Yh9N5rZRN":383537453195,"GdkpvRCVlZqO":523849660408,"Cv7NIQzJlluq":551350801731,"IeHL6CPLWeee":136621475944,"NrIWFXSaHYmo":83636563567,"j09yNdIDctpf":329470815817,"Oa36dd38HNie":211109807557,"r3S1ZTVZmcbv":586500294938,"TH8wOcgdIV2Y":852343066988,"yXOutF7x7fpG":8484829460,"hSG7EMEj5Naz":367249819966,"9vsaec6yarwy":513043773988,"ibzTvjJwNO5U":916248566954,"QpRhFEgwqn6D":179698253911,"tC9IV8KS1CHJ":363386028133,"bKE3ApICp4AJ":603879972327,"KnGbHTEGcnQ7":712173166791,"C54tYVTDR83o":85387128271,"nL8ZurvtjhFu":870784672669,"Ltm5NbtiZ2X7":942704316965,"CAgheftXsoT7":962505485986,"FaAM4W9P2Chv":848295893256,"Fdsi2zjY54hz":310476134809,"KPJuKUTDoUlm":625710631511,"EjwejgD3Ymfj":53394235073,"O9TjbKZw20ro":115020275162,"l0Q8k6zUx2sX":792684053137,"CEYJ2A51hVrV":853210615407,"K2Gngz1qAVJk":391298038455,"kk3saewY03Ro":850332588777,"z0Ctn20EuS00":312946718992,"1siwaHJ98LN5":219070847217,"oVIsTgiAOVjp":429452489873,"8s0ltJH7shdN":543757932932,"OThdSvTQysQl":455785703374,"ABbvNO7TE8nT":186927231081,"kc4YvuRWPB5r":677520471207,"b9OBAGAB3r4d":904893648839,"8ICT8PqWJdVr":322912548709,"iPcaf6ILtleT":713829046824,"XCXfQaDZKhAM":727382222006,"X1KfIt8VjMs8":135370228078,"LghbuMIzwn2O":254184887302,"8FF7kVDB2ByN":394704490239,"qcVI1XosdQrY":953713564508,"9DPWItDqUiN2":166911459917,"XpFxZ2WBNYMb":536330433216,"2SxqqseKxjf7":896564203558,"fs3KaW5JrcVV":604390772031,"BBeTTHbeTR6S":817136506516,"ueVX8rGU0yRD":276127216568,"fwczBMkCCHYQ":361631748678,"7JcYxCBuSoCx":389686104255,"yU5wi0TUAxrE":103399293730,"CY3dsK5Xlr7T":736332574220,"e84yjimEx80o":282526092698,"LJ6Fck26YA31":771637900577,"qaoprYVIbc1H":152535623752,"Q9OIZqMrFyiD":309153102980,"9UNdwyGjsc9i":578280269583,"nXD8efUaV69w":307160691347,"JE9A1LZbUtYd":853987581428,"RAsw78oXulIH":470836751954,"pUe4AbRW725b":955636962273,"3GxDQzoztfES":644780874107,"0ph4bvYAAdxP":951913383673,"dPr3aypClF2e":65465484678,"xRcjWN6wAJ4X":358466172532,"TKaunJJm2HiE":119614906254,"l6ctO1iqvu2s":458646325345,"tAfpgBAWXSm1":817852026610,"JZWEPk8gb3pv":308056003898,"73wqjuuIVZ1F":477518070962,"XEsOCjbBRqgJ":62466479955,"nHKcnPNnNC06":944313586808,"MGpH8F0iCgVe":622048766805,"gxZyJaUxIhDQ":672405607541,"D0ETyQpBtDbM":929741263593,"aO7HYpfTk6Wk":840059624028,"5TeDxqqp9Nnv":140446126806,"HqPKtkNjQdt4":268102691977,"kK5ZQ2kowpwn":566589373564,"QO6kflNppQAM":364975284407,"ZqjcTstD0YTS":369483856576,"IzvI3mzlUPZA":217681576634,"8IzwLgRCRhyz":891668228747,"DLAJhFbfEDML":638331042460,"kfLv19ie0CHX":673934187505,"1nibnD5QfcMO":114844069038,"GnB4ZuU4WnZn":969850284970,"lpqR2Z9ldVZF":448060053648,"u1sjH4ak1Y4F":936658916023,"IFuhXTyndJRC":552643754133,"fk4CcvY3XUmO":405674563228,"mIBkx20n4kNe":872913178845,"fU6XrS2KMK1K":560490916819,"wJoKZCC6nIIi":938380892824,"njaci9v8AW79":955382499597,"SjBzd8IyaoQd":163872720385,"Pt7XCE1boVVK":816173456447,"bm8SXiHjaNcL":187055612759,"7L6xUannwOzz":150809225075,"JfPsyF3j79Uo":444077712571,"5eR6MOwqlbTf":509136765029,"NErzNuEX69fm":289472958491,"iUr0zWe6lmVK":665139419876,"ScELoRPjhSR8":142938564396,"8akEd2Wieq4y":24790885499,"Sh6ry4AXK0Sf":615594552660,"ad11meayLKbK":311069813206,"ODgGK7PMVDbJ":857439429889,"JHjJ6e2ylriG":602114383573,"0WjgwNMtaVDC":5859640347,"7PUtmmnhDXlS":851595101346,"o1BunCyRwmE3":112460734279,"8VzHi5w2l4n6":762162726183,"BQlE5a70ZLNF":260177187204,"aRkmzKcV5mn2":905854626342,"MbrFUfh215B6":201121239376,"4LCGTgSNnVxb":334624229901,"03RPU6hwL5zl":377218408701,"MtwyhOukoJnK":114828865949,"CPtLS7vb2uDC":482799252458,"vap0TQQDYeVJ":601603804136,"6sSrcCiIDXnh":638811453552,"iEliT15yVARw":751730973559,"gezwmpvcaNbY":651773340760,"U4XMonl8zPyB":375363179525,"9QuKluocJ77z":345402692979,"ZSTEunrCpSVJ":994992443703,"73sMU4yuAWc6":897405234392,"Jz9YLdRZyBFP":113900980116,"c5l5GZnCpril":454590429727,"DRA6llKLMTW4":120711499178,"JnNbo3uZ3PkQ":167131951863,"JBmg2wubpcg5":456748140268,"enFymBuZLUfW":919915204944,"o85YWfv20Zwr":75355509598,"ZiUiyEzQ5Tox":862962977014,"UIpVCdumThET":391431410358,"k1wR50IJ9iRP":83329499877,"aHXId36u0AfT":351366888583,"yfFJASjJptJG":74226691811,"dxLXeE43nPkx":364000814441,"pjcPzmwe5Xzl":473105023924,"rvCC4mTzrKIX":732570038616,"gbsy5cyYZbah":324722316374,"XKegCcUr5pYs":492282117387,"2EXi5yTsrq8J":399084156544,"xEnau4ZFjidw":560075376059,"khJNTSDq37vV":767837961807,"NH82zFcLeXPM":306336047322,"GIFOABJsk9Qb":45821249360,"0g0ejQ11LvWQ":541917627695,"VzHPuub5HrGI":680195008651,"tjBI810rl80i":486022457525,"n2OtiLmGRzPE":734983761375,"IhxYywLEgv18":471364481284,"clUY9hIHQWc7":109129238403,"tw4PC8dLCKbO":447066295933,"qmO1DyqLe0f2":650526413906,"bIaD0usH2Bh0":158526699106,"AfrlwS7voVhj":159407476431,"EHXGXJAzLU29":337564395090,"MUYJNyxCb0wE":599113021662,"9wNJyfJEz8YN":935492134355,"1pzllouLQw2s":81627229936,"4FHSV5YRIlIf":345376291266,"EmrjMLO24sDw":63104486427,"vHIWDBQCsoKO":258784782020,"goY1TLjHvte4":963155710538,"FX5oCbKKY3wm":717152607990,"kSxJsZpDqWHH":480246440765,"OYBnBR38rDZb":704086702257,"NesG5K8Erdqp":368316518722,"Kvdw304qiih3":626624815126,"FVYJgVsGFxwd":28063477701,"IvT6kvdquxm5":702284174798,"mLQaDVMVdtKA":960997619223,"gabfzrQEbyUb":357786947906,"0KfcTcrRVr4G":867814997496,"iLd2jzCzUVmz":861275067152,"KAzOVmVxDvRZ":935266400523,"mOgnREAlTx7D":660117937276,"INQ1ZvQOol2h":790362378239,"2ZFY0R6dFZyQ":547728939968,"Hm08izlrnthH":634144992973,"2GS8wAYopisN":252281065076,"UFVM63ArFdvE":352430018129,"x4QG0QQtyWmt":635441323586,"8Nm0Lbfv3Ux6":987035327480,"mB0vcTFY7Tfm":222439100937,"kDj5ejcxoQNm":339229402685,"sTvfEdgLZzoX":830116564576,"ACUNX4rgSiwZ":737758873351,"TIqOzhkS1VKl":72491500719,"NdXrxdAT955V":28663598619,"vKKsFhuxoepu":316420521547,"bAaYeItJkKyd":967188776709,"bz7mnM9TL06h":482856610621,"hvh286evQfBB":717303409740,"ObGVZHktoq6m":153958508995,"x5fqgHbAFjcm":972283184102,"gBILCNxHDHKU":82692037521,"5D3whNZL6n6e":883782194351,"dNyDW6JbOz8L":58165444437,"fE9uGgS847b2":130226079566,"jBdtfJJ1jBJg":837000910159,"QCZ0jUlIKhIQ":49044639019,"Z9uQCEz9G4aT":404030277947,"GCgFiYuP5e3t":502657736108,"1gz8Z6CNswmd":379273236552,"WIjnYrfx1r43":13948917413,"crkHm7gjpVNM":770067404825,"c3muNBNFk9nm":26132817071,"tyxWEDiv5oW4":733615468596,"bANtDBDSmjHV":373205132458,"Ozk7WGmBByOb":747363503138,"L413XuFGGetZ":802628633165,"OebuBjcsHLZG":208155648746,"rIrOJzzIki1N":104001898367,"m2DeNRU7Pi3o":429059357012,"vlAnf2MQquUn":680874532841,"L3NB8k2HCmFD":741032730274,"bGzCDbeJFeec":737056210352,"cEEMJPIf5DMt":215046686265,"nDPNqnzliUki":579806868225,"IRSsp6NKgwAV":103982068986,"6NPih9pYGrf8":36785674214,"uxYxoggtDAvb":410725813885,"1ldoSg6jeX3g":79339255397,"3KAWfN8VvCsT":309365137999,"oFCH2jxNiQqC":709816034751,"W0bhmeonQ52o":233474307742,"EywO17s6QvOe":831628320042,"CoXy1MQWXFNA":905741524859,"NWleEsjQPeOl":900300284550,"HGHU0fOHAXro":635746504631,"vuFcLgpB7Sjj":329691626708,"TnmoRPe8aKWt":249546166607,"FUlLKVXvjhoF":862462648665,"axtJoyab0ht6":367241828792,"vzubvYKusmBg":307380965171,"r9CsTIArzBEo":92619803728,"0Nn4IGyN4XOV":219402248160,"gsroVeIjSdbF":900057217486,"5T1cYa8ocOjo":845446378315,"Njyft4USfaHa":767177357253,"khSKScJZf305":689922304720,"u3uXPH5ABKOx":918656914763,"yZ4u1Wn3nmHW":305098104472,"gSEXSQ9fu0PW":827103003826,"CHRqH9LcnNht":819148629692,"ipwezS3fSjT3":798699744843,"T3maIUpcUeBA":963992517701,"PXgYkDEGtbyx":629757817944,"IcxDyEdYt97q":930191769681,"qPNv5lUXipGL":451220368575,"J8lN8ODlCEOp":317097430544,"9CBR06GzwlR4":541739512440,"Yim9qvzzvI1l":43503831244,"m8lkDDQD0DP5":386108105866,"bTjRp2Aqxj0d":486018661525,"oXVBe5iDeXp5":25196670670,"2RzHd2m7KAjs":715800073756,"l8KvDn5HBGhj":357118252508,"CvSZQp27A3XH":806682309359,"0VsDj11QbqdR":39814932631,"ELkhsJF26hOR":573004441054,"tdACWyGvPChj":625959045851,"Ua2h96axaOKd":393191788855,"k9ckWKmVZ5E4":244897235269,"tj51aZBvJWJE":969617645381,"aHriITL5YIvO":193564445908,"hXlCKbzsehLc":479685774156,"CijMqQ5s5sHs":129037384871,"eBHhxb2Z3vHf":787472997531,"Ch6DsyDzxVPA":489177760435,"P36EtffzBqVD":728636826989,"UmVvhvNjMul2":628845503372,"UNqPepnvt3JR":526748796095,"xPF1K7ZYZcIk":753665889233,"MPSPxTqsj29r":623375191785,"aolk9pVESZr6":681878087557,"Vsm1EmRSSFYq":121561245884,"RHgn2mFwhMpG":140657759457,"JPOZoGgSmBeO":206711926996,"U9TjjLOPBTTS":959559713235,"kSIS1eUEK2xg":792809188321,"ORYoKJftIJzZ":165921478537,"nK3WQkLCWQwH":583583176153,"J9y6sey9davC":979257619244,"ZnrK471CR0an":807534234720,"bJ5cUrAS9Zf2":860792499277,"j8kb4MZncRrm":795090244175,"GktR0DYAr2rC":663425738781,"7TJ0vx6Sex6p":324645997290,"TbRYvdafksQZ":28519217513,"OngHiZJ8zcHN":557057138834,"GvemgomGs9Hw":120790764910,"HUEZbElTeGkC":794858618237,"bt8hYDoW3sqA":678437899900,"PtmBrXXY1Ndo":17369166743,"E7ob2FVsFYv9":507472756468,"GclkpCJVgI9b":221273742801,"IHL5zXTfLdLw":288709373349,"mSD92hMBuYPx":284470806731,"gFmgUfB819U9":993764367368,"28ziABmlVY4M":684579254675,"kmvVv7wiBNC7":16566016676,"0OqWl9O6FOkX":995844146705,"4GgWKFGtWbTt":338011997349,"R5778vVYgN5v":695148962892,"v4r2AFLBF0Kh":113314639813,"3gRqvP93B6O4":537019421279,"WMXywvsnWh0F":279733694174,"Hg9VxCBFiKMh":892644448581,"Q8stepzWknZT":115206327204,"WsBzc3ogLUy6":218068046488,"BJyqZdDKceZI":98609727007,"6l5DxtaeZ44A":436614091984,"yl1ImmKqjg8K":522754373069,"xFpB76aCTiQn":545820113082,"6LSIGSUOOK3p":94812250115,"XQ0hUTf4ZpzW":237897059067,"Oae2kWDRyXEh":451817674486,"JAbhONanzYdB":967490993635,"QhKZHwKw6OBO":816949867246,"Goj0ZzOCZyQz":144530195750,"0NfiNOQqszdU":442149913152,"cFx1WILyDdVp":956171847903,"doVXKuZy7N3Z":737506858822,"tpaLuJybpKKg":737099206463,"tqoVXLfSP3Po":129360540805,"VRspDjlL6I2P":959772111495,"FGcNY5RDIcFi":368845690228,"qn3583ciggnC":885420933147,"ifb2ySC2GTSw":916923778832,"PTmxbhWqrVVA":833410270644,"LzEJavD6Aht9":616072631724,"Zvtt3tslz7aA":71678390555,"9yR5tDAn2LgZ":855709553216,"kDYExdOEPEDk":503386557151,"WO6CXYL57vZb":165613366817,"KgOrDb51PFzy":401418338713,"PDdYcCKhLSmH":443271631686,"rMrrQ3sRRW2K":108947877,"ANruDRbe2dAU":767223393574,"iyaJtKJsXkl2":87966421625,"FaDV5iVH5Ykf":959490536580,"mBBQlZw2R7qu":398215336312,"8WjYEakvU51O":839877035241,"hRylOHP6Geox":435876052792,"7yLeDsmWyfLu":687544954413,"qQ6xLrOcxAfw":722321030321,"wPaES89btBVk":41700619630,"EReZRUaOnSEk":648847740599,"tnaoVipFYUxp":131538038852,"CBJwYn4V0YSy":773173081792,"hxy7E9dxvnX5":650506859641,"ngrQR8tY1RbX":187284091428,"noLwQiqdctLf":22602592600,"wjb5xNHLUOAi":26713859154,"IG7NgCpoUlMv":99905896020,"OdyfKLk7z5fG":832256381668,"0Dm49lSi9kix":921164728889,"XtJnv7zFBNv2":648617413099,"Dc643gydFNNH":963571650880,"6FfFGR6jCCiz":279164122018,"bSgcY2kbynLr":217756639823,"zKPJY7BJUXU4":439316779089,"mP1ryFisWhHg":868530420385,"DB6Z8DwGJCuI":299381966884,"cZd42h0mnvM9":73461415568,"qz1QP4pHDDo0":386790757747,"8KE4HTRAhNRo":999599668451,"xUcVNPFVJaK7":446663494664,"lpx2Ym92JquG":427973543017,"e2o2pDiwetrn":682333889919,"7OMmuWKkatLi":906730491064,"5y8bQfq2L9C4":79622771931,"oIjFc95Vv6uq":724937112416,"LnDfrB9Lwxki":506564499687,"VbTP0i5g4Jci":951530624191,"F8rthn9Cl8FA":127440116014,"PAtUzCQlzfrb":250667995804,"KtCSORAliUcR":877749584472,"olHj19Yv5kQO":346904672141,"2XwsfBOFxjnu":132272616113,"T3f4UqOFcgqQ":736710782524,"085zv6wgXi4W":854328541148,"81CB9WKIWsia":627977918650,"P6kklno1X1qM":311296170123,"Xi8R3EvpwHbB":922699629265,"Pga789yrhA9d":75621531988,"Lu1pE55yWVbk":683855544212,"soj9F1Gg4aLG":763795006524,"v7H4CQFnprZy":683752833824,"I3ZOTUBPDIyq":319502732677,"dzIipbZ506iz":474153876086,"xoMu7kmNJgp5":386270192756,"o2MxyRIvIAen":742064526656,"7BqLKfWX0vUO":228117402744,"ylo2QHmHiNzG":953152865213,"2NEIlwb27EEJ":701611702598,"GfjMwxye8rKB":178704142925,"YeDUvz8q4O5C":188143832876,"PDJccxBW0M3e":820183081370,"3k60MfxnC8yj":799088199693,"xK77bvPPvwjn":571421267864,"UlbU3GF3j2p1":829614620094,"owzxcNoO0F3M":371730815564,"fRPmsjOgfNzT":121479489008,"RNc4T3T9qg5u":877150959449,"XHkY5AVupigl":992320379183,"a1P3JseYMJt9":879075865316,"o17dWbhB6hZ2":56173628596,"d0mEqTG4W544":685059637896,"P0YQs8UbAJlh":292007580127,"ZZ6udFfW0YmO":911213598724,"uEPiyd5OzQJt":193783394145,"PEWRcEp59RGy":702339685550,"EtxCiJKjexfQ":922227048210,"zHyjEaZVrCTn":125361585230,"NKsdUU48wzFY":988826381810,"oht9lzlkSMIN":741646570092,"LL48TpKqyoVl":33446527854,"fIVyiwmpJ8wc":189194327131,"nzAEgFrTqGys":554638255891,"gm90U9tO0wJa":396654835608,"OEKcZUUTIz9s":994742411701,"Iy868jAAvcUg":269524128971,"S3RGXSHssKBv":835540001524,"mroplhDZW6E3":501894373847,"dcyJAYltOku9":398117226577,"utTjBcqu9AF2":371761353531,"BTOerCeUGVE5":704453302694,"qGh2Wn6KPeb6":346486777431,"tCk0hFsK6Ewc":712842954140,"vi7NBagdXxKj":677755389774,"w4vGwZVkyUAF":136314897219,"gDUZqKC041oj":572133181343,"I84RWJOUswsG":305157846346,"OimdLvWXny3r":54992863340,"vmHiw1YlCsXU":291600985791,"o6G2Je74oqwy":437811952638,"af1Q0HxyyCzV":256920809999,"8TKL1zqlepBW":822533409852,"tOuPdnrjpehu":817297588058,"O4PfghyYHwlB":583640965495,"rimQ3vsZVQIz":919176648811,"YrEGpTRoPErV":882536024074,"ry4DPf1HV8fA":40943413775,"koHIYLXC0QUB":348658409996,"qqoSr0IQ5dnm":853238201499,"a8GpCuyC3tVZ":242406315781,"0ZC5UATMR3Jz":597667325215,"e8Sljw3CbpBW":375640907028,"q7L5lY8AXNA1":726039633197,"3L2wLa5ZblVR":591913610931,"4F3cl2Jn3mA0":277794680625,"67xVk1dwTpVP":745663431134,"kClgs7D0JWwb":476471147338,"MeYVEBcO8lvb":944881482462,"adYPvNrPbRew":851147956322,"8t7YM8LVC739":431749789217,"7sGQyECzPFRf":371359739840,"0Wrw0zArSvNB":900809775232,"fId4OPQBjpVY":661575110869,"OCswM20KTeSM":820943033901,"vvm8TwhQy3mo":141404780301,"BqliOOjkl1BB":310742426700,"0CWRfiAkivP9":371609615027,"eRghsK51vpPe":996068672266,"YZliTCZufksS":135420676285,"np0gajnwf28g":332987891144,"cFl8Tm1mBK6v":946395989473,"khiL2LGkXb3V":481233611177,"bobiGQcTjPGm":624496244759,"3ELr2eLcnjLj":74427252583,"FymDPGQHWzPH":85116499339,"QrFZhAFjRhVI":380283468424,"YRdZMGvtphhf":667580376803,"uk66OVDI9Dtx":676792900572,"a0kwgqRY7Ety":613590040482,"YziNmuXIDqS6":719957436107,"uFEkVNBfe029":583359122507,"gcj2fDjwc8Rp":437900899223,"TdsXHi8T2UVt":490865991763,"z91NBw3W13Kr":976475604145,"aARqjjgpDBD6":633731013278,"9clj7DOrmtNY":504550494221,"G7jn6dhdx71a":540604404767,"Nh607eKd2V4P":605036159397,"ihEDRq4YHyXt":371936565758,"3CLEN3k2qEOq":877775852274,"RFfE0VuZJLEw":480598831965,"2u0hHPkQb7w6":563106326288,"qD8EgP0FA7S8":35343136744,"lZzYUmeng4Jf":748875062668,"Js1xGHjVBd1j":139621758701,"Z5y9sEVJMTgf":333457347606,"si0nvzjyAcd4":982581321463,"Z4RJAFPuHIbR":390583181789,"EFWf92jb3EYr":196228364497,"qAMcJ65fZlJY":147955167954,"JlT0CutVcFQ9":233938457936,"vR8O2KD6rKt0":606551888594,"TFHKyVnKvWcr":343139904830,"gaGlxhrqejr8":61129429591,"bwjzWJpqCAsB":455046863336,"mD9ilBqDzSfQ":494444952355,"FtJB9HXxXFuW":133164294068,"nxEsq2UJwdhS":477875823966,"y5jQxbWtpH77":582654860972,"kSinFMgYGBKw":307406376988,"ZUlzrNFWRbGk":245594914883,"7e57gd9bh3Zw":215311432567,"RqyT2r8Cdcot":307895623559,"BCfpBhmvXclP":657802821551,"1vfnK3o15zMq":878642400215,"12BWaRV5TY80":978232775736,"OVVxkwiLVNwn":388992542066,"W6jg0wcavTk1":585577399334,"8C2wYxr9kThT":13186098224,"pOtoYdy9pCMA":170875905700,"kxvhY0xuGeg3":912717513973,"RKQSn412trC0":75427271529,"rEddAQgnDn8K":751463571316,"XTTgjd2m64A5":310632656590,"SK1Cu18FYClb":304774244100,"oPRN60acX8wN":619852810309,"ISFmjhlbWNXr":219819283322,"FcBPD1wtShv9":210378858070,"LXKjQ7OXnxOU":320311550811,"fPVxHxHAQaHU":290551340132,"5QPqsx1VUGof":705666558787,"ocgrKZ4Rw9Sm":526485301259,"xOdiu9OW5xOw":210333069620,"6s00gEC0eMAK":954432783223,"fUa8zKFxTzQv":622915256879,"h8qWU7QAH1Lg":971646336589,"n5w8i6ZHsvhj":715950917548,"ZceTPhZHsWVj":479427339992,"A9oe3OIoijmt":9278410437,"9dYPNxqWtWvi":58808141084,"EFl4Jm837jzV":429400897345,"QVr6QkVC43JM":347929058509,"XhqKfmkqmGcQ":856907992176,"hisP24qO814m":402347174457,"zRVdf3BQAGlB":561088578917,"yuOLTf8hz9lC":615929485822,"I8UBHUJRPYo7":225888454578,"1ykkymTedm3x":706318190967,"TA6dG4XNBY6Y":462501407000,"PeHI5zMQec2D":391245797781,"Wv65xZweXO65":217945015313,"FBGFljhTMU5i":497960520182,"iMqjuuAa9P6K":401040059384,"fglAaE6xHm3I":934445598694,"ehMczVVWcLfV":969520273733,"t44X0lMOWIGh":181941108018,"1AEHoY68x350":971201221514,"LYtmqmIt0dVz":728077533705,"dwcDaVRouZsv":754085552048,"vrqghvdVarFw":847570080416,"DTfI39eAzg2N":568976429599,"Kh1kYZpHpnfT":551318815117,"ZKQ6Qk6nGeXm":195434192968,"bV8sWAa1qREM":857188895660,"gqy4XReA2dQX":685078607843,"dYN9VRvtJTEy":152478496375,"X37KkDwCrLFw":121511513255,"FJMRAUExdVKO":545572449286,"Vo3V05OK36mt":647254758521,"3o8xAD7nTAYo":99177533397,"LuxvOAaU82gK":939207663026,"PJOhzufBhDEu":688088622531,"9Xpb7iIOK2ma":184437717084,"c9hSrSbdgB10":310144982019,"bOZPbTNjKid9":51594581568,"PkXZifv0hho9":930233508709,"JpuuHC47BICn":473002538457,"vRpDk2Qm0RhE":533474393780,"Fkh9orAqNC9L":473136724806,"yeRdYKzZENgn":609695632732,"arGJBI4wgBzP":5426811146,"ZdmH1VmG2K3o":608619748779,"p3VgJaBrLHqe":371574186340,"xDUc3MzPLH5B":891680982089,"NB1faWz5Xgvt":947474916811,"M4UMeXPrCe6s":767092595642,"XBQ6xKUyBf4L":512658453724,"fZeEpWA0Stv4":681747310070,"0XBTvp4vebPZ":452239238410,"klBLBDassQEp":622691730927,"hhDkz1LPmfg7":77967604119,"2nC7mHIPfLFc":130377036316,"Xy93D2IXtBBw":124469267942,"yndMiPM22Op2":665273735637,"XXVitjmAujck":133646344250,"XttW6JthBQaZ":775691104528,"yotbmdkdtZNv":914739910838,"LgMyjDWYVlwo":734896935785,"6WzNPunRDdUR":577714864972,"qyRUbRpp4LZe":656776620829,"c816ucrVYcmK":643810429194,"7ap50LPdmdWV":709587391380,"xtai8n2CPVRw":673946221495,"BURQwWt2xYqg":229707461025,"Bj8irI7xkoF7":591558754481,"fmUyPq75e1MV":332077593741,"vRpyqf4uGK8x":674877240937,"CupQHQF59wRR":917585867892,"1SOIbzIMYR30":421381420311,"jvwDiFFFhCTf":283897807420,"AeCf1iFfxARx":594307005685,"g0yQwsUhN8La":844032920317,"KKb2qio8m3sl":360809978348,"BNwqyJQncL1l":516166361090,"Cf0W1i10RP8s":551485964307,"c9NoYjTQ1NOo":843470872196,"ILrOii6xgfVJ":560618500611,"7WyRDo015eNb":884342789424,"hvg9WeZ2HnbM":167365675826,"qeFz9FcxOuOg":496869839434,"hE0xLs4XJl0S":195469312016,"dWwoYWltQFfQ":341258861396,"baeQNgMUV63Z":381540439517,"2ngkSVDyHXo6":975499322214,"v7rGBydCknMT":107933410529,"3nQS0I57RABB":543587542819,"8dJXIBfd4QTA":427105548906,"Ab6LmaiXmyLc":588636053586,"zhKsNWMFdIch":579841735361,"rML1KVpCvMUG":457011617159,"ul1YHOoJtkKS":237485028691,"j3elkoOtH0LI":716728836234,"SYLCqnV0XOes":175206285758,"odJmeKXsbD9e":393295279870,"g8tXaECs7mdX":972783010972,"luJTWyfBqltc":62292666275,"BQvtRGoOBXRM":327185789786,"z8mWRNJ7b04Q":99632612456,"5PsHuYVjys0K":352508532877,"6ln0lP6v3ayX":235230730685,"q0ZTXElHOiSm":43274547959,"36R6eykuetHP":59585648953,"MFoiENGwKAYd":620566932217,"dKUrZlfNZ38N":205689875118,"ICbCO749CxuW":356110388946,"noxNVD3yGmEO":532573753505,"QpLppdtaLhnF":923108803767,"0Fu1Fe1xwTRJ":465715452233,"oXjxk3FnBeMJ":384528296492,"YIO3xI1bNRb4":83905752310,"4oDKH3vrn9Yb":511221769783,"lZQCE9phNhkd":923655457320,"3eaSTB5wPtya":736645974370,"caiMTiYGFZ3u":633742844545,"JTtWQNPXytLU":284789531743,"ZSgnfhq3elEa":498192936031,"9yaBx2BhbCGD":58222820305,"Ep5CY7Z8eytl":34805349299,"B1G3j4OEqEH1":8954835360,"5LAHDHujgs9C":103534094697,"X378RqXRTbYa":954718737365,"tVqUOWfEJE0t":287258713128,"wNGjrj6bBY7J":403884128086,"FJZUPUshsOwI":126197803994,"rIy4sM4zl49x":895632485355,"9DGQo6WkmKFb":557172239187,"tAP9Zet1wGyM":519452684035,"ZKenqMh00i7r":331989229484,"emADQN4Aaf4p":969511392630,"bbDCEmWRWi1g":817650772810,"QszvydoGACLW":876623373438,"cSLAukA0Vv3d":680293450856,"wEtR1WH1f5lk":522860953131,"xHrgzxL31Ven":88214339457,"pAJ9hFLK0jS8":598147421234,"p6DPOykeR6m2":136483774981,"u7JGpiola0Nx":398241221769,"2y5sTbDG93Z0":229340465194,"8TyrnlrkYEVF":561972614145,"rXoNfEQfJvbF":513576063146,"6GsPTil8t0Ve":635218675499,"G31xF1TUA1ui":523832330053,"dy3yT7gEr0Ja":955588742277,"MbAnC1qrxP4t":891092213521,"MDGNOQO4UCPr":728442135656,"NuyuhzfJIVXH":23582326493,"RBsOtfSq5FOc":858971462951,"7LlZji6TrHpK":484017007565,"eUAcIhjnCAhF":199039096791,"oPaBoLSPVPsR":373464390874,"8bZy3G5gGRmR":411846640950,"iisZx2vGyIIP":529300780858,"qbyvdh9o5nAN":934155827429,"u2LeNUEiYqef":437163720783,"l233ka7yTQQ7":484071736718,"z8x7SSZqHXhh":488955254926,"yBZhBa26JrBV":621309691526,"HWXncFrqe0v8":972044000230,"woW2tgMLfPg2":622167101581,"lmozAOViTzYH":356070621281,"TcP60TifJmsK":19405094121,"7o2nbsvfP5tc":967187308603,"lV9wAIBm8e7p":513415389173,"pYWmhh7bDEPz":908436678643,"N8ZqNO07agV2":293595690970,"BIM33psyTA6h":467830849274,"QcuA2i84KRUH":912164980294,"yOjomCsXlPq3":73902581554,"9dq0HtJevgVx":387101897068,"jDpGTyXTVAYN":699881333115,"UvBEPyfvC2VL":606720537915,"G7L5Cgcjv96i":612720250921,"u2QJPa30L5a5":339851989112,"w4dOmt7Bmo1x":817701962545,"HLYQSobIw7Qk":326221980604,"uqGcTaXBAkVd":511937819605,"n7pGfgqCPZXF":450067612070,"OwNfbvVs3wkp":878203563080,"YGK0Le0qcBQt":985163149253,"TAVLVgIWekcs":346279292001,"ReiWuWsk6qu7":418176718788,"kDABc0FlHvTG":455477632470,"SMNYuWErdnpx":472362606552,"sj0mZXegREmM":687878375611,"GiNzOB28Imgy":126471876228,"6e0d1izQrmSp":119153908652,"cbJjkrNIaaFX":46888874796,"ROTwz0ZMfJbS":975541321319,"c7HjcSlnBztV":295335985938,"jIHDJN03ol9D":136248120441,"8VD2J4CK2dFN":322513746592,"SPFXq7Qii1qG":481777789420,"ml79eIZE9q85":921222558491,"ZT4y2rcXiF1P":241133767735,"Gm07d7NKyCJm":318151611183,"2PlJZ6DoLKH4":885297742145,"VADL3UKyiYYe":328832176136,"Y5SsrIevFhLC":427168304430,"4lMzYfKLBPW7":879018310601,"4219IKQZzvD0":337987820272,"kufd9aEkumDX":173992031428,"9ZZL8vg2UxE6":376982254255,"w7FNfO37cnfg":253553154753,"fSkx548sns0J":922781059974,"uGzwydqlcr68":234415485217,"HT6yMa2EZ8ol":234158378068,"qdNq3LkIEHC2":505859356925,"YRIbRNYkfmsq":389789233568,"lV8xkM5gcWQY":709923314657,"EY7EqQfot2z4":192958976159,"c5NGtMvPi57u":966511484723,"gpBPK0uook3C":194712972458,"tUFBhEsQCRGS":150673728628,"HByv1uCL1joY":510140451382,"kuvH7S4tS5xc":945772522192,"zSMh2PEpCWIP":318826628799,"laZ7mQFpCgF9":976935619474,"57A9CtTBlWqg":638624172809,"mYfKMKCW4CPt":601430545433,"jHeXghpV1c5P":580198745464,"lbBM2CDNlHEL":610232561749,"w28mbajFSXvk":238082805741,"mntRIMqkXHfn":701402111178,"DeDOwmrE2iQa":467765742697,"d9MZ7BDrliq3":199938792255,"eWZtS6JKOiB8":502160017822,"HQ5VomidG6CZ":720579385084,"dJeL03zyO5B5":931549779579,"Ypo0uSaeRNXJ":460958549790,"bYROcMrFJI0N":421468522911,"SNRzC1k2c7Zp":148057682839,"GgeeBQv0cQI1":737283409555,"NklH0CObb0P2":614121137773,"k9JtBEy0KeCG":762977288932,"9ydrod4CtLS9":135799321115,"ZuBpKY6CbHP2":544521636038,"xLrGJB6Qia0R":291597025473,"6MyY5O2f54BY":205102765881,"tOywl69tYu8e":927535592555,"2XQWzxCPJENX":390664923977,"3mt0IeLCiqES":694635859860,"bpJLJrlkygIl":781838452439,"40TMOvLotpin":191933927872,"aPrArAUh8i7Y":975210251480,"SWfyjj9kaqFI":583967472609,"UtStTY81ty5o":663394916830,"24YzQGAKY828":397345799150,"xgOexFoL5ap2":389526538512,"KTfW1tiamTaV":470587401996,"ZTiGuL8xYKAi":570455563126,"jKwwXZGVUGOC":301271295645,"fJDXFYdmopK2":140906220933,"cGLDGTSyuLOj":682135586417,"L2Nf5KPGOhxr":153197869367,"uohqNMdTKnWN":483798847100,"5C5qxx2Mbwba":688213621542,"f0gZ1ngXEE5u":524258305626,"Nwgvvm4ZszR1":919773159951,"UzSP61C1d2Fk":900512386909,"VjrmTE4m95vZ":483503818393,"9NOz8pSfBOAA":558333841561,"w8EOxvlvQAx4":405440417460,"3Tvz7CtoF1eP":669898068560,"S9mQ39M7zDXt":779456393309,"7G5fghAfXFjl":65767851844,"0IITliT2NvMm":280339629353,"UeqzgVBMz1MY":545285869135,"KBKFWGOwfwgT":467187972291,"M9z9WK0Fx5Vw":694132804705,"352pN4h7xE43":752670931980,"yO3w1GEVHYxI":488698454353,"oWUtyOIlRFo0":427195567258,"vNqIJiwoSlw8":762611996063,"PDAH9RH1Zw3u":565549494695,"3UJlL0R3l98Y":842358795656,"CK1VzGKLekW2":478087246173,"MucW4pMEQ2yA":786712271692,"1uyigt7yArcW":108741492375,"I6pTA7omqDgd":709123995997,"nVxqzQDMLkBY":398729655453,"v80yUyn3KhBZ":60869739283,"Vxmk2QNh9hsJ":74144643882,"OqcimP1EjemE":415535510493,"HI8YOqE9shtB":73307875377,"O4l7mKPPgkwO":157509327018,"YwUPbLRcBQJQ":16021272412,"4gVQXch19DKt":756641386359,"4ve1wgx0TfBC":577206438939,"O0xGQSLdLRZg":625947332836,"8om5ycfzFc2n":965079846407,"rNcJJ71g8g1q":321786820366,"XS9xxhD9navV":794393506861,"1hLYQlwb3nHO":161634147477,"a4YnOQb6I36a":740142624703,"EPLBD7Jx7Ax7":394819206610,"MkpJL6rNG3R8":174566887240,"q4XVodMIkdDp":71688405781,"wP2NsLGFltrl":785920277283,"BWfsFSjXU5We":342841580431,"09Y2xYrumSFz":898705682793,"XOpdzY9CdgWC":469814838663,"Gyp8ESvA18CT":592316171160,"zsngVeszXfy2":498415950459,"dvigPHx2w7nL":800438490652,"l1WlkiHP7sjq":667443521672,"jwrQBEyuCrBc":919093001145,"txMYrQOAq8Q6":396092875049,"mO4UiYoQwe1C":388544657779,"Os9WqqvXLr63":998955238780,"ZGkSKNTRJdPp":414488899660,"Yag5bQ5yXk93":351022528384,"yOlr8HftE78w":382059764483,"hcLRg5nP8GXy":954856605981,"pBclo2LkrphA":831504136320,"zWM4FdNqltQ8":521700319816,"xbakcMk3sLUw":951126800945,"xpshGKZbtVg4":221364077245,"kWYD1Rtz2R9i":701477541669,"Imsou2eeAAop":915994143216,"ghfw7HSFYrKJ":677543433627,"afzKdsSjMYKB":888028474534,"4SILQGqaDFgu":888208330337,"Y9AvdJBt4dxC":906995777467,"i4xO9jJsMho0":513309367773,"npSWKmyJIenD":260758634903,"8p4fqGLTVIug":866601404368,"YBswdrAuIDWH":230829056752,"FB8xL49D0PdO":780261437486,"y7ttfdF13GME":884368350492,"QiREIRlhcdGQ":112584599175,"vhURXZbzQCrH":637554051973,"0BjyRQoh6xzg":654059869229,"PC9hm1YpkjhQ":712066999956,"TgFFLFIOBLtL":696435058742,"oDKjK46cGwmP":14300975921,"6R1nyDT18g7v":621612184828,"dD0BSeRJg4Et":852918028175,"cO3j4cCFpsen":90787707708,"QD7ubuRZ8V7N":349588079200,"7zVU07KKTpbw":494739406932,"fqPoe7ZD3lSK":292454133968,"XiO3kIkZP4kK":813109932351,"jDTH3UxeMxoG":838312603870,"TXtakxLFIsGZ":519638391594,"9WgECcKfjwj8":711657174521,"pGKIkoBZgZOl":319696662007,"Z6SuWJS1mZEm":597866332254,"44aLu2dltrda":433861297754,"LtTYrMmPyb11":519278862159,"zUL6CbYLQ9Du":470337107216,"Qrru0qXadcyX":82734033277,"yrYRYqn7zltD":326052326261,"G8Z9IYFLoAo4":112729410618,"XSI2sXfr8078":420270305089,"dvnVvVl2sRF6":561672241296,"xptgDEJ4uhrS":941232983010,"3BGVc00ORRIE":3399906289,"UpWaehnODUWg":475065640016,"7oitK01Vt22d":131586875077,"q3Pum33ziXSv":302963748222,"phxdnpEFhVBz":3501710339,"nMbFkdgKtTN5":287644119734,"uhyGNDZN1YOa":120790000685,"HsWVIvkIvWCd":23339709806,"AGYApjUYLyca":274285143018,"PurktqS4EpWj":338504014921,"JUu0JahAPim6":528505235033,"6fM5ZwWKyVAf":290371812472,"vdNGGYPS5dx7":915685843879,"DZ5JHVWWJMO5":296892434699,"AajDdQ8bkf6F":402506602810,"o47VTj3RCu4w":827057527178,"UwWv1BitwgZZ":152032582074,"rByCtB5d1ZiX":23668801098,"Tm9qNNsP5toT":579400504947,"fhQWYhE6Go0S":922237872425,"nCp7hjdPcHGN":437568780321,"QOklJqFc1Q6I":993553610807,"mh9Perc4Lx0A":133467326984,"li1x66O4An6w":198132196249,"22Tq4iaSXgov":782469377269,"7rxLTiUguixf":521831560582,"c5axyY3W0Ju0":427298974968,"EnfeLSj9DLS5":268534892017,"zMrFhZPk6lEr":472057592802,"aUyhr0yVeEU1":543970719613,"guoXuhQqvFTw":889445498985,"Qdz996LTajtE":977769796306,"rDiuaqSFtsTM":739983960091,"Empitv8lt1Mu":511362911515,"EE8ItH5GE19C":678345859033,"zuscNYk2jL0V":452772376849,"IpyJvV5oEInE":939159796549,"3iiYcIYD0BS0":892339257844,"SiZNLzq5XvFf":155346353114,"q9obKXGPW0SE":23793934283,"f3QApV8HEuUn":28414571345,"WOTQNIGXGOHv":629274095861,"tCRJyTsd5x1E":832697330074,"dii8ntxPb1A4":570430173709,"TDd571pXHBEY":915963442831,"kcjQJ7VFRIf8":58346474422,"f1H9o01vlhTN":528037618579,"6rSnHXrMGrtH":116941639964,"PQ6IiWaQpbrF":324445863040,"XUiQ64Fcd15o":779563931280,"5ndwPgIe0sTq":409624782090,"ew6ySc59K3hy":940307993247,"qCcKZl0kJvzu":377838297620,"bZkd9MLRSRnJ":969065641316,"DvWbjEXLInQe":819718268021,"9BjSYrUqTKus":846741259555,"OwsDJe9v7CEz":704668280564,"XFgZ8AIobEVy":54823409685,"09sftphLXhxV":740893761772,"HZYKrJLzro0S":733006282756,"Y1LHjUaXRPT4":475148106197,"um7jKIw0yGsa":580684442807,"tGUMnZJ3J0Di":811254175784,"hHt36SJaRF9K":936563927601,"Nbsp6NzLoVye":936929497786,"EaQFJEy4ljZz":610295655582,"Iq620JxCHTDR":170715216002,"avwhBYtykdkK":669327597809,"VTNTA5pzN2qU":285696682591,"WGenlgqgfoGK":233944558874,"SDQpV1NJZHTS":406517980082,"v8R5YPvSjdUh":981740603687,"NKplVAnsBtuP":777069965597,"knJWOXI7LPzi":709077614713,"iilbafQUIfeQ":836830605876,"VOgwFYAWvGXk":156869172927,"8tK4E75NgJVm":259307328091,"ZIICsahZvHz9":894596934091,"UPeXVNGO9yL2":778297914133,"hlwaJpIA41qO":848195800147,"XuILJhdDQQRW":746744951382,"CGkKDvG4qNin":709414454721,"Z3gtnCMZ0OeR":382737972583,"ejgX3t3IaOg5":978309154877,"smxQtfzEuhmL":483129110855,"nMw6HftJzLto":695380609164,"GOfgtqIWuCaW":191843563259,"BFaYD4lLDtqn":921237005163,"QSxlXmU9wsNw":612594851508,"I6iNAy75MGXq":542917826642,"oRzD98udSCW6":346389876500,"O5MEG6C0fSHU":17739384149,"DWxzqW9MywkX":900834234,"p6C4LexfbNRX":101044646536,"Fb4ErTvnfh7h":286797521310,"O3Htw6sYviuL":439696452332,"0sKcTYR8G1zO":602460826465,"Sb7el4PqA7co":908444135783,"3cJwakhfW9Cq":495357198530,"wpPiWB8Yc6w4":45349880115,"LkxKkqHLZDI4":674180952971,"Ug56Rkem2PTU":712858347364,"hxAcd5lz0TXF":502090391146,"ufgUCL7Ehp5P":584238909263,"DnV8TPFNmW1R":929926772824,"VFIByoT7pEqQ":135865641770,"P87YVV8dXBay":810429332350,"DPZ4qaeETMBE":644886806759,"GjFhHjW5G9bs":318020814719,"tpGKNnhuPW51":888744796613,"DhzduOdwz6iB":279792874818,"cbMgR2USagcz":713888226835,"07I8V8KwPuo3":269459357060,"tQ4tD19PXXOP":352948449676,"3EaYGe76KrcJ":131481076559,"gIKgWuXCsUzQ":221379308149,"B5lx8VKC3bD2":423256389983,"vdz9kVU2paUd":346218112049,"MMz0FlRQCiTP":267476304431,"PsSIJDg0hZdB":892397293911,"aV6XlQslaz3C":611413503264,"xyHHOx8ZuEeo":614872968832,"PWmaeIJ5piMA":768995128751,"xiF0V7ScoMRN":104969892894,"nWDhY2dF9nl8":633527675651,"bbemFLVbbIWz":844065903926,"EFLSLsadzIiy":143988389170,"VHyXLKZ9Eb98":886901720234,"c4FjH991QUd2":680825360060,"mZA9TJ0QqBXP":287931604507,"IoHzRGZBQY66":696310202686,"k33p75tNSv04":265372723277,"KvWcPTVFtVsH":167682523454,"x36fg2zFm3hM":701144327299,"JRXg7NT74Alf":520035224116,"UGZZO4olJUl2":363187175504,"CBW2dEBP5W1s":158948402911,"epUxzoYQaVOp":620326052783,"AUygx9pwd2UK":159299980744,"SQNz9IcYuvzN":932015657714,"96KzFnCqH04u":482761469667,"HqWE6PsH0DWf":193426698177,"OxuoaTbZo64s":677768076622,"6xl37Kjq5rn9":894782193083,"87sdmtMcOqCm":851253186244,"ggsR7VIyzp0W":498924818345,"h6B21sj7efNJ":631780683496,"iuroFYAlxeYp":847340566033,"T386GXW9Xq1r":721278115160,"qSaU0pRasaJp":843762723693,"KaoPrmR7sXW8":421979811963,"hIRTPW4tdeXY":302432448230,"07b2QJQyKT0X":562174866937,"ik7e58oDlZL9":339157150474,"PEW9qqq6mOKZ":33406807442,"uAwCzGtthBgE":61487120927,"EEe7AkZIl9kk":716364389790,"PyfAwvbVIVk7":808536064979,"WtzqJbYR2Jc5":269170682488,"2RStMKgzBGBw":712719825923,"OqkClvLE6CGo":151870036110,"J2dIHfpbRny4":340028936448,"qj5TKc7ktGdx":127434107968,"kIEkUwRzzwW4":47128891368,"d6lqRkq4REPf":914271807564,"lrIMvT3mk1w3":932377778876,"9tFBTu1sHWNW":464357777974,"d1o1CFV5d3b0":815461631548,"NoJMjJvRJCRN":277706946455,"52j3gCyyZtj4":815907018012,"LySMP1oWLbiU":524555175972,"WEPmQhqzKUtN":727100789560,"Ld9tuQxiFU3c":415703393323,"D2SyRBpKuej6":408954560560,"ws2jWuSGo2ak":504023206643,"Taj3YcbiExJs":246285404941,"GfNvYKCZudOI":954281470848,"VybYjRv3UREE":981386778522,"ot1Blm73oQL5":106219409876,"nEcHqSS3aCwt":463361718904,"RhCXueNLmUIo":822955324270,"ELPwsAi1Mwt1":563375126345,"tRbsNQKlY3Zv":516927000267,"bMjNwMJKr8ps":304576992964,"y5BZsn5JjAPh":147595979128,"uYMH9wXBYimZ":232869942318,"I0aaCsvBLgFY":308331072437,"IfPFRHXrzgX1":821176406908,"H2QNCorGjP7J":984219180808,"yZkWfXUSQPdO":686413796372,"mghU39oRrqHR":195416970683,"lHTJpZMnui7n":124560533628,"vuSAKcrc4ctD":587213087819,"49O2HNt8kj3M":558499588421,"FbVx0Q7nVuJG":830508334385,"ceifD3yZVx4P":425497610931,"7WqHwdEuohrW":100805751297,"w6frBcCq627J":402559461835,"D2KFXChbN0aJ":11999045383,"ucc0WMvgAtNC":591773908241,"6pmaieTm27yc":750551644807,"XXRH5WfrCa9d":664324431972,"o8s6oKZ1pkYH":194807894460,"ZmJpuaEzCRUv":866538177867,"NREEhTLmkHgE":545253090539,"FvsRZnqr1Xb4":463001629472,"3Uv62BuwJNhf":251239718261,"BzYnJ9Wjpt0h":678082527673,"FxlwQTM85Z9n":695197429467,"gxSY18uaeGI7":895850371411,"Tt8M8ZNSbvS0":888581311235,"dzQnQXDAe077":279643818693,"dRHFx2rspeN5":920038148061,"6NQ7bO9otCAi":502425605704,"jiaXhJAXk7tE":435131459756,"g5E7v175o8gh":116260726059,"wscokAiemnKD":141070731272,"MSTtlbzG26AN":925530017667,"sZ6rcBIgAm1k":42590478878,"CW5nWRxbyZdc":675328531013,"QtKqmjAzuzZx":322974350340,"Rj5eJbedy8p4":282744408880,"ndoHR72vluVs":292678736054,"YWfPtC1ny5Fg":934959070842,"8PI5hZStsak9":411583015890,"GLLYEeEFysy1":526530811689,"rVTpv6eIhdvS":564995506592,"rXBDwGtoOz5u":677207092961,"1sAFE7KDMwup":825472732619,"DmTxoUtYn4SA":947701008126,"6xv2RRcp9Cmy":86638149053,"tJ9AMc7tm4tJ":172665329100,"o5W1iP10mVib":41464073494,"z5XlJehEQDMA":644729844333,"2tYoQ45GCJAS":663780686858,"ugvN7k39WFDE":502555835817,"SfRHGgls6a0e":434301359038,"xbcODNnUCiva":362578412663,"pxsQHxWRG46o":194934083960,"J9Fb8wencC4f":952681022705,"EVTlEfwZpVfm":14465939625,"kF9uyNc1qfp3":322501974332,"i4YxGFBX8W8f":422373454031,"pCsPH3ZLefnA":864123004398,"dhgoKSViPtWv":825963215869,"vOVBiZde7RK8":54790132714,"ndvuhrFLMUXn":84860279258,"RmXKNU9YxjXc":993903905288,"LoFLyov7CyBd":732408106987,"5gtA0hVwSl1I":493497990395,"BW0g26OzRs8b":465615676139,"ZFDtTNiq8Lbl":818319629744,"6opf0eTUg7lJ":797594713995,"Nk9pyJf3vh2a":162190370934,"dYrcLnCTeT7b":728041813368,"xLkKG7JTFjbK":186374192381,"VzWutmAmZnM6":499827295917,"BJRsQASpSxp1":278413259987,"ftll6wvUH7z8":359606468929,"xzyxVK1t1lH7":893010247018,"y3Fpuk8avWnz":984512530419,"ehgKsmS5fToq":621268760942,"8tf0IQoaoeou":753889687488,"6r81BObPsgb9":476929631618,"3wbXa5qHiM8R":903200272767,"HKvu9CBnoKpA":512449332153,"3xio9pbFnkim":5223960665,"d7cOXvxEcLbf":342923813156,"uF88wJxcLeGc":334431081601,"UkWEAplsqghO":876371796179,"KHTMV6M6DM1Y":350561644489,"BheJiyGljlO9":660368604495,"206V2wtGeoti":9591902563,"LgwuaInm2Dag":595426558378,"1wmXipyVhYI9":323726776797,"aVswvsFye1Kc":881179654517,"NFvvIS1hoBtW":835640786524,"0MdgRXFQdj7Z":161783470704,"WCK0e4TwAs9F":53039523639,"Zu65z5FPagr6":448868358810,"y3EONDceFbQ9":652080880120,"eOu6CWHpl4WM":227925925421,"RDiSc0NanHPq":169369704188,"N87aXKRTkxUm":962352260927,"TJidU2fQiwF8":351021112632,"sAwSUf5aFpIB":176865537539,"KoNKW0AM4P3k":987487685144,"wnmNJKNraXo7":540748861669,"yuPaoCvJD3xt":153341021662,"YvjRNg52jno1":276737370613,"J7vbMjHsybrF":410124745174,"qmtIqbtO1khw":453826543248,"FhXdfk9jWJJr":51146469007,"GfnYNPBGyd8b":702036316611,"fpxeKS8V55XX":933041773742,"jm8683BmRmfW":401226173128,"YMKUPivFRgZl":577421202947,"hYOHWXv7DtBw":174653007028,"oBijP6nk4pP5":912056793956,"I5SkJBjj2Oes":501424669049,"Vn8m2jfJgApz":646998708072,"biPdd9tY3LRH":887231634550,"lX9PNGYWjgEx":814435274743,"lXHHkVUZcFpD":303728424274,"uURvtRxItdWa":498938002799,"IZijolibYaSF":974494523767,"IPjHrP7F1N87":117276692501,"py3gwXqNmJwF":677491633981,"X2dLxjoSFKVI":250521251482,"iBzqMlnrXrOh":480064897595,"neVNFXAZodDU":528761840057,"KqeMFoVRwhYI":270768156181,"ppqRsvZyMWPy":773095169431,"hKBNsk3LJHdy":115306673009,"5nN5jQ4WkE2p":319401069149,"OycYWuwK2qyy":978261022554,"pWdnuigyfhpB":98041645104,"85g52UbQzWDS":382583383845,"8xAP8UBilGg8":369073900792,"FnbBFYTsyKgf":197819508607,"jDb5CPgwZtWE":634782680173,"wSB9kVXwv8Mf":513202394526,"UBPZid3xqxpj":930152148981,"OmokgOVBRuFl":4781671927,"NnSvTNllY5GF":57834024309,"mUQcREw1BSXv":321361399256,"RVvbSRQP1Mim":192819068580,"qEXejDQ0as3v":224239536504,"IwFKDaMGfL0h":54113862716,"PI3WVJ0kfpBL":728317741193,"2hhG0OtRdhZ7":458909294495,"qL4kBpmLvw4U":840193968833,"oTq0PcYoZeQR":237465407148,"DIkpLE4NdRbl":717291987987,"cnEgI7wrV6rL":686946433509,"0TfTi8e3Q30q":394891523839,"HZkXx4P8YfxV":327365476622,"5YDIsZoNNDf9":258643352259,"PsoS6ZMpSnCS":420682461802,"3QHXCBFd1mXC":778864017538,"q4Q4sOs7wO0H":143691629089,"YUVcYZfpZfJh":435794355041,"YpmfIUwYLYvS":879898261050,"mlYosOFTJmWB":872482335787,"lHUMPFcorRKO":334935244886,"BOpnzFZ7TSvg":348049477376,"FLB4zs57WuYe":358148358626,"qNDxdQdnLvkO":588944170395,"OVMG3SHPjqdk":952144951421,"NyVTeZ74l4vJ":386703114065,"5D9mgMgxgU9e":541371511166,"sfGriPKIkQFJ":262853256425,"RbFLuVKCDW72":103638417097,"OsD80QFFCdej":290173296301,"XsA7iCfr90Yc":536462389366,"YPQBhWTceicC":7227548868,"YWB2LVcnubz3":933491452781,"n8TkKBIp4DZz":554330926066,"VdN1oCqekWQT":226137837576,"eAt4QLaS9W9q":287775451657,"SmRhvWrVHWuB":428793276773,"el0r42nR6ogE":954341141712,"oq4YrmLSrIEp":391679158785,"tP79w6vN2MNq":29938396475,"ytPpROynZF2L":650899040701,"bJEusXy9Nil0":331972481548,"sMVNFodXt0Sq":549682571449,"demJPZjrEcMq":214922497849,"7LE3A7F6hafC":838290049667,"QqNlld7KfiIa":61592474958,"EfaR5Gl83fto":178325810088,"eYSqvmgBC3ZF":621595294491,"I7c70xFbZeA1":151869254985,"r9U4rgyvDLhc":163135073382,"l2t7IjU0aJfd":997745289277,"PIyQg4jwtodJ":494490714399,"Zfl6Oo1CyCIg":482642777541,"yKTN3zbkO8qb":91794243281,"GZzDOC2NB2qF":508825583869,"ZEdQ5h4FwZj3":791331850578,"koHB7zWXan5c":344279292921,"xPdwiw89xOXq":894931247971,"aEqceQ8lLJ84":808790015178,"MUYCnxnwqsPd":246873363680,"TXYLQFBCmnNO":91198402199,"k2RTKWgNXeaC":174117777805,"9X4uoXvqA6Mj":282279640626,"jiErPkvm9Mif":137494140360,"WglHhVDFylb2":996753207402,"2W0z3aYw5f1k":66139556868,"dgDXlOuwUgou":89612563097,"Rstmkozxl826":348189044048,"vTPk62Z4BUPb":171308941887,"wLPIdzzOqTSJ":759167199098,"Id03QBjS5t1E":556218077598,"J5WY9rZjgU3I":667193882757,"PKdsG6Kxwbf0":800267960185,"PVaKdnpEc0Ji":784402634873,"bLcuYYG15AEN":89458630737,"c8POwpyiBO9D":564236799697,"bwOzfZuKxO0o":841874586297,"EJFlSCDUJDSc":935918346239,"zrdRsqWQPXkA":243563057270,"plGfUdYcrTr3":303013901134,"k048bdslJeRM":112570643644,"1msKP1Hu9FCF":29435569312,"dDqT3PvN6K4l":3992554630,"XCQfLeWxmcwi":912918109480,"tUc4FISYde2Z":586899988598,"Wlb8cNUcw1xC":190751218114,"FTzJt8i8pVSj":342397884817,"MzJ4TXWF5Y2A":191423402429,"5an7JYigzMe2":670264954761,"rLKeE3Nvcm25":159926163176,"uWRef4WbVtGf":962102493160,"1l93J5Mxe5Z3":477742636216,"UoUW1oBZ3VET":144039375184,"xwBbP9Wnkvtu":855502643301,"8REoFCjwaFbB":169130393893,"pR2mFQZncCVw":559031750731,"362dTEL2MYU1":319365635158,"kSmPmF62EUEZ":201577892869,"rTPehxpyhYQN":159221292027,"B3wPAw9Ty4Tv":345444954570,"hypyZd2uJAyo":399459544721,"C4lb4PukJXLu":207592946502,"gYbrbrUbhFyd":564212771462,"ZZQoZ0XkBGeR":631832145749,"QK6XldT21Wlh":396782887441,"5FpfHAvx9N2U":554635663338,"HObBUUcsnVxi":267677848017,"qcXmYkPNy9oU":706525096952,"fZiE7iowTEpv":563844836111,"V092F61ah4LM":597602210298,"dXTjmW0xpCWG":132823233986,"Gua6qlnRtsyJ":171542839403,"v8LHsCuHTlwV":925366194813,"BFwpsl2VATyh":586454121715,"C0s4vzDHcbxW":697236509764,"PL3UBPzStbPv":774834139494,"hitKc6qoOorH":962936140,"zfwJqfg1pDW8":733026541870,"0wVUEsFNbPP5":199130209586,"OTO5iOu94QXH":650991704244,"4goChK1422KV":875756835779,"fFJD2ZRrsehT":935851036586,"zQjpqdDav65g":349057811327,"eMzEvZDZytZU":815809018105,"M7zs7VUM2hhL":474594211713,"ELjY1o89pQXm":918227984008,"y0pJS7qHz3Z4":265316723341,"nyRR8SElFFvt":795787606762,"LvwIYUz4Od0T":191634213158,"JRYLrOWqUvoe":861286513543,"UwwGD005sLxt":990638798127,"cQMgpxeRH5Wr":338134650710,"30z6TO1OiifS":619964296055,"qCZ9iOjvJRUi":586904480057,"lsdjKwSnt22X":351939397534,"QIqil6Tu0LfD":785007794338,"0NtusxdG18ek":789634250340,"GTE6Ey320Ot4":410294903626,"t4hrSRIfNkH6":662637243335,"p9FQ3iQp7AIq":599693543185,"jwt71oFLZeam":640255990872,"agYNPeBiL5WK":477110490531,"XiCHNe6sxEyO":311059221293,"5Kekpkho7m9x":234256152548,"2ftd1QSEl2uQ":404042068472,"dvj549HZf7rv":228975432343,"NLJMBIbWuXHn":349742597153,"UuQf7oTWERRW":777941736683,"S8oeK9ugUWu4":509782715479,"oP1VM3gzIKW3":434496547011,"G8AVztnG3Dbi":878622861532,"8POWZ4978qDF":338768328344,"nGoiTnGvSvcH":894152516354,"MValL0ns8Z4L":430583423401,"my34DKlxW6IG":214517518566,"X4JsZikR648B":781555473467,"2CMeGIyFxLTg":148858637477,"Vh2X7rKMWHTq":29197757295,"5xdUJ9ZjC8Yw":235333440984,"LaQ71XTfX8Bv":2227017206,"i9w6lQrMuP7q":861069573213,"N27Za33tn0DZ":235001400363,"M2otydglknVP":784015086133,"JBWEbgvqtZvj":939206124493,"Cyo0lrTWSmZw":52010047472,"ZZ2IYvFMPfiP":704194456234,"XEhVGp8UoSL4":892535685008,"WZaOwz4kOgUI":218398694463,"aK7kPYPLhIOb":46253925056,"WWF4TpJbkEN9":602210114906,"T0qn1OfGt2L2":809938093285,"YTSmVnIaDHP1":88521448792,"RIHrIVXQuGIb":85262937872,"pZykl6UsgqzX":324701431492,"abnaWA4OC4VO":446808706759,"x7b5uuU3hV9z":741520123098,"CxprBXvitVdQ":490207291305,"beDMAWdLIlQ7":807868270545,"XpRg9Rv2YArq":186382775207,"u8XRrw74Ofrj":482689265632,"IUyFRv39MKHS":84153114936,"HHk1FIpN4YSc":429386700542,"QZiUuMu4FNzS":388397698738,"Pa8J8J0F2MV6":53468631168,"EE8hPmAcN11t":815650179112,"WCgH8vPFUzAn":920668856017,"Ndeqth9LDsHF":872791860223,"cxfe8Zokc65B":612902578398,"xAuTzdZ08Twj":473737094694,"nqgSbTUgB6vN":174862223275,"U1xYrdX8Ycsu":43415436516,"FFIGJywNz5K0":25546301540,"8W0VjeVOobhv":941345519051,"EEEfJ9j6QzvF":9957520272,"3qxt1mJoXoCH":756854142547,"QedD6IsATmEr":694270330622,"rETg2To6DF9g":768477409541,"pI5QCV2yOfzV":948503444391,"KDmcJW1cnXK9":179566694464,"of4u59ANNEZT":339070664922,"6oFGP02fKkfx":833274349470,"wZJLezU44GaR":3188242989,"QvnQlbgdF8DE":520083525546,"GlcIDc0lDJHS":305617711619,"HlaggXd0tjbg":315747194827,"BEHmvpDRrJN8":42957809393,"6b3uPOfs0Ukv":701119276818,"szhfr0sX2ZfB":662009571396,"mIFrQ5LG2M1X":531581590924,"t1qoNkOaFqCp":382959137209,"fjWDpJB47w6L":725019401775,"FtmpoyWo7B8P":230980261838,"5hWbvqFzP9j5":965734166063,"FQVAJrSmGrVC":554468453954,"XMVI23LkHdMc":859989880348,"Nyn4l0U7qihh":636271161517,"do3LgJCvY2GH":45285255217,"vXO82Xe8Sh6p":886709843925,"pKdUX774iBRp":296516112846,"C0USq65GgmBo":869142531973,"Gr6Jd96XeJJ0":575861815286,"nEUgkS7ArKou":825609927583,"4A3FJkCQSH56":36541772721,"4xb8zHU1CoFx":325003301004,"hCMqxfA2mKDF":882433732682,"0wJRjuG1xtRA":760491203436,"VmNQuWUSTNwu":508883973075,"Pcx7myW6q1Kt":921911179172,"ZdMNQTZzYvLy":264479066728,"xKEOELS5gRhs":255459750442,"mMJtULB8XRg0":262591149131,"pTQNHRNDibTc":891438127545,"YoPVOo1zi5xi":13632446220,"ykGKOjcMh5Tt":519994886475,"43Pv58YzAOij":703614365027,"rgrCnxdSsGqE":781985726058,"uLTw65hQeKrD":169025658138,"N7jADRfZnQNh":10550776799,"avJXqyTKJy07":271564993746,"GgAHVAimHPSi":66577474210,"9Swaa5FYFwCv":71987468049,"RGfXedbW3nst":352024376792,"45qoW8aojtn4":365488026372,"kYkZbtwtO2k1":600535965149,"nImvIo1qR2Bw":863855051226,"7GK8J7oKecQr":30585861266,"dF8hJWbPPRBh":765257764604,"9afaKxbAiYnr":146179250248,"B1x6ixkkUCBL":648701032883,"NNXENfp5bVSH":185479117946,"98rdBUOblGLr":488732558580,"YG0j4E6U5w3M":371894255420,"5y7Y0gh5UgmK":603386447771,"GFiHvM8zGZ90":874137143035,"XDZoZKLIrwj5":838704249044,"QJGaPBSsq2We":988373136053,"xdeWdnsFQchT":153005383859,"U9rM9UyKFvzm":524667948278,"Cl74IZrMEEM9":415507448231,"uZSOTz0fOdcw":232454235797,"3ClPVlD7YRjV":834616366008,"fRKfisHqxr82":160220122104,"m1OjwsRcbWVz":796053604902,"EyGlEBzdBBSo":911239189826,"LfejDpzzsf60":977168141875,"9OL4D2rkNoOW":917022843872,"fLDJCrRiPP6W":293162486679,"AK98VR9chFLV":473315221216,"eiQx8FXlBIKI":242123816021,"VJL3WQJItvUR":924104256524,"hSh04VZcwmI5":895662887248,"glU3p5wIRu3F":520215360578,"49X4NBhURlO0":938610302931,"x2du7gOx0tlh":692953438783,"JssV7LGA1myr":591532225058,"Yud5EPU2vX0A":633547067494,"ZrPLSXXH38Q8":880536599320,"5ed5KAXVfxqL":530087260407,"LHk071Nz95IU":799701528982,"tkAY1eaGk3Bp":508665616153,"IhVRcMDP8ZPr":703396251003,"d6jTrK5OTwYe":568342892769,"XDm2DDFDArPj":115719513763,"8DIJLXKwlG94":697152370005,"FYoxvOqdV6k9":583498094048,"hb0mLnYYUB3E":150977069696,"d3EgF6ezBGIA":742522316815,"NXalVjXqefPz":814198473185,"qMU4bt5Ph8s6":2778526952,"RIFSeHuBFKEQ":718008240593,"u1IHXpdmQ3N2":978716047811,"uhyoMEoaDOha":452635411413,"yzGxtw1bP0Sf":53986029946,"jDwseK2zqdnc":843718774434,"9DJqBJa4s5K3":970543256851,"hCMXxcxIaZyK":276879189160,"lLEThzh87MOI":722662462506,"YVTw8wZs6gxi":396830856232,"sxgSfhAl4SL5":892258835296,"bZHkvr6HgP06":23296530382,"frJRsK6tqDAK":757096329903,"xCPPkLQY7GzJ":414400535380,"EIvPMwgqReDm":713012195115,"SRNH9X8A9HI9":440227397943,"KzUoagTg541W":249292151127,"kj0pCRHY9aWN":55732656441,"ond8JDIq5Y59":903281301373,"Uw7IPyxyr7xq":405666101540,"1K3nR7CXyJgj":502300619550,"9qE5s3qsBONT":867368648353,"eeAFA7EMiHos":32912476597,"5GAfys5pAwqO":677122970021,"fqMG5aRiy9mV":96082392602,"iDJVwTuzOpoI":449354179633,"jrRwLlvJaEQp":353990833383,"xsY0KPCVm0wp":818079054029,"njOGr4CiFZIS":603531251919,"rgrzioAkodYR":525688762530,"xL4XqNlJpqCG":511780643003,"5FUtEME2Ksmq":682353760502,"73XuhD1Ew7d4":419626948107,"3Vo5beC5ioY9":9754583434,"D5q0vHrSQ4qG":533344507239,"C81pqVUtuz5T":604997658338,"tlG7AVZpNsje":538423927662,"DTXbjM1uj1K5":438367504865,"XArLc9QSFBlj":633750087855,"x8oMbWVCgvpr":327377706169,"HK63YA4uqv1f":744716313666,"hFZtRJYPzogJ":16457512677,"XRkZxjx6NvVv":226676489015,"lh8gDHObhpqs":615162607789,"1SqdsYp69Xrr":324188684293,"MQGV7xnMWyIw":158993191838,"0mnoaqnn2UJ2":99371988669,"cSxbzjxqDruJ":36740709029,"FVDQNy6rUTTR":810862017660,"nKpNxC2Ube8f":378122989781,"JWpxzYpJhCXO":900180145763,"5kJ38VlPAU97":905510163697,"zBUt0JgsxlWJ":867776525862,"a7Jez3yNxv1C":926196484182,"Rqfzoh9d2zbu":253955677009,"0nq3l0VN6vfA":200122759390,"GC5zs6WKFUCR":861962668055,"7qcnBkyVPhq9":252529952879,"YBuUDnVC5g8A":716256631760,"PkvZK896ucql":375539483646,"ikQTS90Ma0IE":77944571216,"kv4J0AprmEHI":937440778220,"7w3voAYTNHe9":3073371352,"z3dvbqUg9Yki":230569225628,"1eTA6jLT4nXq":311908407792,"sDOjTm56aHLc":443397041613,"9slV3kiCe1yh":248082560350,"TZAReFWbAsUn":304142552140,"famv65K0AmAm":665649463147,"zc3ImtKbETYd":830814111633,"TJ32uBCIZoDx":705387409646,"G1wsm2gKpgQP":605874875254,"CcakNJ8yDfqK":195407463122,"fw7RBbzaDuql":957015979992,"5sWxLg1x71X6":474324553149,"O4kBqDzt69aW":16751705027,"e2WfuxxjLSrb":54263992639,"v55Uimm0A0Nw":399989284514,"w4lEhPrG69G9":164028868014,"5w2dPwyQmko6":824013186679,"aw5p6b9fR7fS":764496922158,"7FygRdyhQwfQ":320545279883,"cgXkFdtQTDpS":834895030958,"HGxJ78835jGw":834575201126,"zoZ2fZ8d1Mtb":691307191820,"RqwveoA9xq6n":543890482098,"E1RRSjW20rgB":897044727293,"S4wxbSUgLf9W":897342381980,"asQb1FemJ0mg":825615564436,"iGFLippHINxu":185248317152,"J6nQnatj7hFf":620184553286,"4JogWu8FPMpP":106434957053,"MxBebj0RRKrT":360459191674,"fr7T3wIUYWZm":191110879408,"J65REz21qGQn":443890144608,"ZI4pHT3vdUjN":458876630994,"UYw311PcxbiV":7571787584,"GiJhBmgIRrug":197212186194,"lZE0xnqJLjri":649788712766,"eOe2Lx1cEV0d":815148899977,"hpXBe0p8O0Rr":51010644909,"ueGDuKLuumeI":6173480935,"LMK0f9NhcE6k":159056879016,"Y9QgXfLo775s":506950760970,"6WowPfUgoTbN":644018256816,"s8bSmat5a4iD":825576115887,"LKOswfA43r9m":427992197907,"hIRcxFtXpu8y":264018931596,"yXvkslG3xvz0":30074836951,"eLnVKSNg6Pks":562638439446,"XARPrYnt23UX":961616026100,"lZWf7470xGgo":109911234370,"L0acvy2o8VNu":726291915267,"8cPPDkI4FVvt":180481555024,"7VgH4o25uHy6":803216568842,"e4lv5GewIclB":872441420219,"tY4nKlRJCOmU":393090830728,"fk8EXkkDFCEw":167100683685,"cKdZSwVWQWh6":565115605976,"RVsaObFBJO9Q":471119818758,"w4zitlaNNBeY":23086460196,"OA0b7zAmVwzj":659698716740,"nu0J15c0TIoV":26148572977,"o8ytBfRRiLUK":662604899321,"LaDPoeFObYgW":506048577326,"y2U20NSYbWgu":473251152186,"4NALeZkfuBIL":929003152962,"4THjVxmEJQzL":59966667258,"eCRHYczs4dBU":192481982433,"xzzfyrzplptF":367967460557,"XqxAZRl45dOi":159025955708,"YjO15jBJGmdB":837686961821,"FmbNqoZcLGPR":645121115576,"uEPrrCfEE3e3":555807633354,"u9Syvwf6Dnrd":520541300640,"lRO0jifQ5LAv":926334891782,"nMIwFdHzh6Bj":196925130754,"E62YnAD8c7iV":98740285879,"23g9NuCNF0Nc":412239562319,"xfUbanNZTpxQ":687979242304,"2dX9nt2h2BIj":934030071272,"HUjqgQYj5AUG":562248590806,"KgljVWPV0fCT":387127565374,"YGxVVW2MieXE":666193048816,"16mJyZeLMYs7":585369465525,"mn0SqxS1p6LN":573603324870,"6vB5cYlP1XVa":222529820065,"F6dAcQt8aZBe":127685951422,"ghCzKnlIqjQe":285542807521,"IwktAn2CZ5xK":136450817919,"Rz5rDXb7KCv2":871905231831,"1gV4rNVN5GjP":255635104053,"aUhDvFlfPTYM":117468817035,"yeAtnJ8jQ9D0":623612821724,"N8ht2dIHP27s":982772175532,"H9g8TWO78USG":669107380095,"RitqJiYjgJiz":793898443872,"qjpD8xrS5Xwe":251803190214,"bMg1EbU0zTLh":682788754424,"EoYrkD4tiN7Y":739223144210,"utNZNnPvzZbo":510548223811,"vGFtXHHuSEHI":666891710790,"qQ1wLjyQlbVW":441022025078,"5oENP4LsGm5U":161829119677,"pDIVyALfnMJF":910816580274,"7e92YAiHMMwG":610507378437,"YVF9QOIe9Sbs":453423808206,"rEN2CaZsTLxP":942080406726,"xArKJ0WEN6KI":528025657036,"UTQuYS8zCogt":488234079383,"scMLb9ooltyn":790409353066,"E68Zg0yqTkjW":517321913236,"XiaN0hRkEl4U":471498067929,"dfyCEV67rOyR":336856244309,"omGaCaIeFjeT":199483180231,"CTu9IW2roqmE":732925002141,"WoOpOMyxcCNU":728839603953,"OetnaKp0KJ24":80074733676,"Bhr7FGzZTFQg":862229890798,"TN67cExqxHwg":802805765361,"t6T389pwTC4L":87119962192,"GgY1T4DYQgQg":229357140227,"t5yts9jW2v3x":219345474296,"nhTdSBiOQ3xP":295292620981,"5Dssv1w46fCV":846391744778,"9gzKyYk5RRrw":1988377205,"A3M6JyF0LNMc":735084091784,"EVePqQXh8Qz5":60448722956,"CfwGxpCH9ukV":619401679059,"AznzTTN8cERt":688445296710,"7qDEf7ny41Rk":190735465664,"2nLaW6oyJANs":720366376482,"7YDIOqYvKoBK":672786854371,"1mqI7SEa9fBs":196821867370,"e1LnPkneleg4":77922094070,"NhswjE8iqrRV":871243458766,"sKKzSRoTvq4i":817403443279,"si6l1Mb1IFCX":123355652362,"QQy5JMyJd1wC":172665054684,"RCi5MRXx22if":429152849745,"c95J9OSBhX7A":882014922609,"cPUu8S2rwZvO":900975890902,"jfJ91rLX12Cr":712600889144,"5Jwfvfi5ZRcz":943912511972,"ZibgAK8YhlVZ":191378217652,"5S06I27TjQWZ":340932629328,"5SF5fYTnNUeL":497636114931,"eD7ijKYnfCcp":958015630327,"FiX6VhOdtuyg":330608853719,"tC3BdzbCTPMU":380432266048,"CjkoISJnjDqT":645277790248,"hPto7ZgTxJ58":96735906987,"MOMJQP35GKWX":860458309916,"TH84NG6R9Fso":620643066738,"7hAcwfkp7FXU":185875597001,"d3t3rtyiLPDy":989158349251,"Ijz0DZGgglJm":840563743286,"TaIPPRbq3vt6":396002983101,"afzhGnXamJgn":317121437541,"5bklLQU0VXkH":768634126649,"Zsws90Z8WJVk":249345187233,"C9LNYqNTy7G7":66542816849,"kr03isT2YMfb":590671357845,"iSrHrUc4YKsB":606897578472,"RJnXoVw445Lx":624764269607,"qo1SI3LU6ESJ":579289116397,"Pz83HBtGiRAc":704710953119,"FcZWRzeyCfRo":111640701261,"l9MptefiJtcD":873342298964,"RHB1iUjg5UEN":983190140418,"jQzJ1GSOo0mD":396582876913,"bi71gu7lSk2a":107676955678,"12tYU7hJ2T79":897202150842,"Kd9ejCCS3s5A":304222293602,"gjV4QkjGsjku":613022085633,"PPyStwinJw65":211775893741,"Xsh4bD1982fd":281008510312,"mjOry2qlCOvy":578171442841,"QnQsh0E5lmsE":765692152186,"UHi4g5fe9tXh":328653852976,"y8wc7heP52bO":803230637197,"zEfBmgJozkWR":256517590580,"kUVYFj3gVRTX":128340318298,"72QwbYm0ZZ4n":816581975843,"psHTYoezDqQE":757918898236,"SzGnWuLM84EU":40970261021,"QQW9S5Vrisqt":964340945365,"6hNWVhHTS9LL":343639462996,"nxdnXJo1HjVh":308150919559,"2dT8Ff9VmZmH":764636195346,"ualemUinR6d2":504337707587,"d55D7DOz1aMa":793001340843,"BwePSFRhyNDH":587388136439,"qF7vMns9a8qY":321782300442,"INMAeLmYRoxf":67723761720,"HnkGdoAcA61A":210989200723,"J8ZuZQ3EGNQO":581387276961,"VIYGEpS61xML":678094766483,"W52adbtjY0m5":404039663999,"AZvgZS61EkP2":242849425451,"FpFUNmWM2JDb":908158147404,"aoOeh9tp1K2m":294738968643,"s5y1DNvtd73g":922075059742,"iSz4SiFCQNb6":148214116725,"mGQewEXEh3Ep":15352635767,"9NwSrQ78AMAH":320088867511,"SrQcZAE9wcZB":741995636652,"zk814J4M1CdM":349748604005,"f7drIIbumxpj":666204161196,"LPUg3fcXb7xm":998837708666,"xvLUGJamXviL":890797103081,"FPtAdOQUKGRh":278953127767,"KHW9eqAtTVAS":646550764,"YAVKwnvDLfnh":626462519059,"Hr1jobFNd3LS":194525717585,"jslHC4WfKaqF":929155633844,"YKzhmhrLYQQu":819359914739,"jV0NS2If8EU1":350311126932,"FxCUtSV2aE2b":699664335392,"zCdMCjrKVo0H":791136220017,"qACwdE4C5GUU":703968024301,"qUWKNzXt6Ru0":170676957150,"JBzo3enQDU2R":927409151936,"zqWcayGpJgy5":986236489318,"R4N7ZAeknOcP":601731636649,"GY4gFhfh2qhp":27821006142,"kcS64cCanTEx":953295547828,"Kq9Aat6RvQJN":436709255863,"sstMr7DZBjRK":433767422277,"GkGLsN4myjJm":217963841810,"mHPMVfqett09":915891785921,"y4U8j0lV5HSB":390165979545,"aWi0JTnvtq9n":899168072695,"TuZlfXHMhRZh":79532494957,"wcqovYUvKe7A":860154503586,"OOZfMUVzayen":157787690451,"HnpvJ2hzsMfN":293987402124,"0h44Y31KoDCP":945672080626,"fwXFbjKhX0mQ":996706448804,"WUDt40dxuzv8":345901142731,"b0ThjcbpOvpB":678044520946,"xb8PCXJ1MLx8":766848136056,"zZdpQGaohlrh":605737537192,"4rY5ZFaN74Uk":107431176226,"ACwphTiBmGHE":857872523787,"VHC0GxoE4QLF":364912655355,"8pJ3l0YCptjN":97245882048,"dJTCGfI7nl8g":330101302639,"TxcmUPO5bujR":529727127955,"hnkGus0Bi4sg":930147387711,"6xYBCONlynnO":638622993802,"28f4L5h2JYOB":610455738385,"11Jq5V3pRUjX":215901987797,"GxifuntuxJKH":517853599376,"jfx3wOd2EP1R":777246519333,"9gL0l70vsjwF":162676634977,"0vpfnX712mcq":971148551593,"FXCpP2Heaiup":758690624864,"jJAJZsXybm6b":654542667398,"Nbcqy3xap8pP":540199479868,"EiRVt3Qu9jIU":205527723726,"SFJvc54gKN0j":284647351611,"kWSgMUnYYKJX":69943717357,"jsNHVwJw8Iub":577953575939,"2yMOS6PjRXMW":868821386922,"HvGfvmyWEb0z":104709662206,"W3IBoOxlRqI0":137397090968,"CLWDaLem33jI":337682327664,"5D1KXNTVURBC":868381821110,"vIAuHDlbKKFj":699238594585,"jAJhg8lG9YAf":213984190786,"NsSDB1MYgzHX":815187279398,"LhTLIvsoe8Xn":986845028673,"vlZZFV0UuvjW":88168179548,"EPvJWoWukzDq":578305351871,"ZBIhKjaFgfRr":357609586302,"P9IHN4iWOSBl":130215041725,"e4nAmEMSey7l":729728889463,"IQRF4lmVu8iC":991338401164,"lSSAwD9Xvkud":204525493881,"RHl6g1JrjScF":30745221683,"QOV48u3OB5IJ":570363173984,"lqwJ5GPBWNsr":940141048096,"QZCALgTUDmoj":452442554725,"6ldkklDO869O":303568223785,"FMlwQP0pQhrb":920245104915,"44BLwfjKQhBN":568208991861,"k0theabc8kxx":128178154283,"yAvaUeTFP9bM":39931926857,"jgeDLCEZzw29":980046326508,"QEJybGJag10D":53500393153,"w8QgH7fOtMlq":519105439949,"fVhCK6KJRu2s":634540252539,"8yDWZWF33HuD":604355224393,"7IX6Ngcl85yw":529194807842,"r7h79Nr8UOCd":323213138793,"B0YFvBlArwsb":375240826506,"zt4j6USrfiQc":547715634679,"iZTO89DEgYve":872265738080,"sHoIHg4Kg9Or":698276702239,"BHt09nJDFZA7":217922063136,"MWeVa3oKlAca":717565104626,"wS2uevPo0JPV":36372321824,"nyZ7rabk23YA":815068386301,"xqlKZ6YwrLsQ":130376810773,"fsEbgNSM47ri":632228207491,"goICkoL4dgD1":819469235632,"wDLDbRNQ5vJv":191673973965,"T85yf4rMpkOH":614447801160,"SAJqUOW1Nj95":758416497490,"YEu319DHh4M8":176801691818,"6t72JeVZnHb2":730424463781,"DPgUCuwZFWno":573944905231,"2azOseEqqdBO":11170180363,"1bxoA06FGdLV":206248427240,"1nUxWWIWl35f":623118845995,"9Em2o4lnAydQ":385553721828,"eyct7rif0dIQ":598316643371,"kJtmXm85ICXX":220198787512,"gRQnfRq5oNRg":24291287917,"4gLOXng7STex":176592227465,"AuTHfJ5wyhNk":71083649303,"JbdiE4rq67RQ":114029351036,"HmICe3V0Mf9E":858621134902,"ev5jKfsn8HGA":903702234754,"cisgJ2Ct1ACv":281486163250,"lXg5TMUGMTqL":136667434540,"90O3lUHdnpEl":286895404633,"NwxvhM3iTJ5P":715978620341,"rzXGMQ2GJI4n":16281213751,"MlYCYPnPuI9h":345093880891,"tgOU9hRZIdCB":694041202486,"P2fRvpZR0tRH":60963518219,"jkgVYDlgFiFM":501333075744,"H7l6M7ZeNUQf":552794860071,"fDFZK10BiR1U":258922511465,"hGOMYRU5Cda4":6063650629,"HWXyNcHAJotQ":629299764036,"TqOvUSlPYY3t":221286052078,"tJ2nyJwOMD8o":492738909131,"v6WMHFqPsKpB":370386529909,"NZLh3DC6CxMK":978447111627,"YRwnZyg7Mfo5":965950995045,"mqhJuL2y8bol":312776771081,"V4T1hfsDfftN":243483620252,"4wqem8bqxycD":262964310581,"XWqzCJwVC4DO":123819398440,"ail3S4lqjAn8":437464351063,"ypo6kpCsX1JE":96031913358,"mhygMGNr2cNW":932556611558,"92jDzBYEPHdt":516633913366,"ei8QU8OIXlsK":320737754255,"x0I6PmpTDUnf":818086758573,"SrxK8wJha6Sm":813089039690,"gaqdIFufXirG":318611346140,"xypfLk17LXXy":697953897713,"HXfx1H6lUZXv":432916158594,"y8576RrRkEIs":164699553961,"eUtQ1uVfe8fb":381793376315,"FVlVLq5Tb4Ut":484885711448,"NvtqVJG8vEws":922929659660,"XVhzXvXVwdof":866831131666,"q56LVCtlIDVV":328036682231,"H1lPM05DYKZn":713087342429,"29foY51GZcDG":734461785443,"BiKb8iYyGgEj":528534837651,"UCfSqgN7lANF":513427791092,"Ri7M9oKAf8Il":410250852508,"m0ybnmJjTQy8":708653208538,"o8TSnabYoWQq":736761336395,"LiXaOo6Tvv0p":45748507053,"MUj84zdgIdvz":127739203907,"2dc525XlDesL":582630664268,"jXSWRtlxYSx9":359763797706,"7GFNaCtcq1Uo":764109231928,"dy2C8jbvZBC3":578534900258,"ok3q12srwYTa":790956800929,"E6Pc5fOyq0sY":54653570158,"mStUshIiTOKN":782322465681,"eW3OwEKaRpla":192871533584,"2ZoRFCje7izm":907113254931,"gl8a0WMnFb0O":935279300745,"rbk3I6lsxU9G":260126773707,"oruy0Rx4wQPS":582676324408,"8hWZ8xWAKlOX":495567554710,"tZksV91JYBv6":27056759527,"wN6S7jawCnHc":634648563467,"DCkupqLoRuZY":479908884670,"CR2vU8k3Zi3M":80584350605,"faCDzLGZrz4N":403967061455,"IYUlqKAEptNh":779362699751,"z9Qm6HgfzjFk":533581350928,"T3N2ldp4j8o1":474907023038,"evcc8z2jholQ":949447403364,"TF2kJTpkc2AU":335812115675,"oO7DNLC4D8OE":462679449659,"p2ImPcKGKbEA":686330183560,"sQr7w7NvAfDQ":283705628983,"WaVfzE0PptME":970808298887,"aKwrj5So19vX":984095324548,"emrMGYhgGDDP":11955748781,"WPvJVs5gYaL7":301810111670,"yfABezwjghGp":367763681553,"GUWu437Yhhmx":624981928828,"6H2M5lV1QoPU":824009780323,"3CO4Gw2ebE3c":242883076586,"wxA9xyICicyA":630814858851,"s9u0MLD9tEnW":597526889555,"MgDmq5DmEAzT":445073107579,"ner4kGc2CL1D":14477364008,"Dg4F1r0zesxk":679597869129,"QlPjWqoIEnaP":323966945085,"ILAUb41iRxV0":502877752078,"nfMTr9gN14HT":927378868994,"2AFukOMdojBY":863377324880,"spy9PY14CTl3":922588715416,"hKHMCGfiDjgJ":996257507735,"Wu4W9jntn0ea":837537021493,"JIX9UbjwupIq":707904475735,"8un188hroptk":815423151433,"41x9dKY9xcK0":199554689445,"ffN7QQLozvfN":94854634182,"73JSTdSTGWUI":569138162995,"V4hpDx8suR8l":338658001079,"cDE7ueaClkJo":64082898093,"CObTMOmNcXjj":994141959979,"SFNCOuj6AfYI":717969208446,"vHrItCR27Vvv":534941909437,"K6UqrAnTp5Di":930172731223,"mP1OjmapsWoi":130554056145,"bRbhdd0ofWN6":924043302925,"YIx82JkOGD6E":707115344946,"l1g7QxxWmaq8":759882768965,"g9wkJHfDpLWU":683642042119,"nVGlh4SRyN70":496574960824,"Bs71nYKSrTvc":455722997573,"QyOEFlBPs2YB":923150650662,"yl2KTijS4kIK":194247837432,"RD5ZjdRjLyUe":128735334413,"21l1Bj9nJuU5":234200518069,"nHY3D6xgLr6y":879899004692,"pdgnJvI47ThY":944385077617,"kCJ67J14oazn":433388724939,"lWwflRNtPyND":233301854806,"VNus0gV7RmSf":982970801917,"UwTV2xUcEfhh":912565877198,"PB5VLL61Dg1V":37233266132,"cWcKAQPg6rqo":250706814712,"tVvxrn7zXBns":854626197993,"H1RqLnIOR35e":381564930155,"8AQbY94zyqPn":180978433620,"v6kBCaO4T35t":424974568940,"ri1GI93D4qXI":414620873427,"ag8Z6UQXMPQD":891396266305,"37JEvCxijmJH":354294913844,"wecgwV0HBHHs":147510049875,"s3h9TKAtfymg":696395833774,"33pHmECXW46q":744102247443,"EpmNLPKfRFMs":50265028399,"U5FTWkmbRXx2":508240444868,"AQwXERYlkHZm":110378839651,"Qtt40jSPRoyU":377198883326,"iP7ynF9gDh0e":794433773979,"uGKImNMosyEW":22211158547,"4toO4NPICjHV":517874006021,"vqiLB4KIZCPL":196678586946,"cHw1QxQM1uU9":549927117903,"RnHGYVUIhjo7":999296880297,"J9yUAxPCyTqM":329860253244,"BYZTDFjS2mwc":451522383135,"twyrvpMpxgmP":90132964436,"fwEvw21V83Xg":178103809532,"yflSmOTCUoUz":189992249614,"HZZV0TfTi8bj":429587253576,"ZVYvolYoM3oU":473698728568,"ihAvIkk3p5vw":637115998673,"CAoGvyB0jsHa":760978889193,"GNsjJ5xjbKR0":359923569571,"sZzEDGYQIXZ1":323725978418,"uD1zOnRBxkPX":408887879970,"Ya0SBzTaWM3Z":237483095742,"Ky3xyFzIGtWN":463005518669,"OR2UO2UslOLu":764226772414,"ApS6K6Ysfaxt":372340595133,"yyGQCC3e4hME":902667230449,"VHijuN8EY9tn":425804805769,"TT7NkLSIKIU7":780698326789,"2fUEaYUxQCNs":101298430940,"ErBVOsXEHXgO":711746341088,"nZUgkl8TJ7wP":613324508826,"Z1DhbAts0IAJ":884834862872,"eaf4GA40X1d5":577635980937,"az40AatAfugA":426806520406,"LkMXuw2OWPjU":852938453200,"JWyXwmnemb08":597211099911,"pkD5axf0AOMA":988621653154,"DLoPzxFE8jjR":857349845633,"BWiLJXRiuHJP":168208481707,"FtoewRwEPlUX":74066236233,"WhZknrc89R0h":920374014032,"uO8zSZHsYkmh":891423603582,"15C6xJetK2Ps":984878545419,"xnnWBYVmh1xT":445145672171,"4ky9DKQtSZ28":859114103875,"N7TvXSma6VLL":299160928383,"yFxCEh1J5BPe":403797326283,"95ZyEb62suYH":71608626604,"TvlfyHfq7ubl":929010638998,"meyzLt4ycu25":707895036129,"I8UATSznrlz2":900269540109,"dnY2FK4U3VyY":324796794176,"7kJ4tdWaAqIg":48978446335,"70vmHNUA3WTG":997562815930,"4NfeItUh3FMn":23474566876,"xff9fgD3DoxC":807385612595,"mHuRKRie550V":400323596121,"ODGrZIwVZfKX":708451300802,"rg5JlzPPVmBx":201877041026,"jb5phZgw5AUK":104540557315,"301Lv6s9s2QD":253752012539,"NFZfE4VddD9H":22483162506,"fM83tgL2Q1he":14037927765,"jmjd6SaWSAL4":130456269835,"moqKi932yxiQ":312727221263,"ymEyawa2z6Ua":956089723288,"SwCpaD8EjGCA":18879187178,"PhAaeVT5R6rc":919407789970,"oorFS3RDv8Su":962311125646,"cVKLzJhKfFQn":158591066361,"4ACl8I4whfAu":461027954352,"vxs3eELNOm0C":890188394999,"MpAyssNKoe1w":415974196171,"kaLs789a19Q8":391996302410,"BlIQLN8xK0B8":427488851141,"onV2ZXEPi57S":56344025815,"NNGryHemgoB2":750177372785,"SBxjhyBrux3l":608607135613,"NLSs7qKGjzyv":526187556664,"HEbCarAdmMfb":63995030782,"fVbefUnMQQ8S":429105983544,"nvzeUOEdwmni":282406969529,"C61WYokGRzWw":546309939100,"BqOeEpAEQrxC":874187846176,"5uLX8XC7kQZm":735831210661,"JCnxGqPNMzod":787684506396,"CPqTjLFwn0aj":819143224675,"bp9vCyT02aNK":976402506970,"kFmyCVOOhgYB":699065612967,"bGfg6pSuBIJr":587848348302,"wQcCiPMhBoh0":316394948896,"ZutrAcuiQopS":985101912950,"ZNFMXfxx63Ka":706633217368,"vEzVUfXQAZVx":835125618703,"urV8OeWlK44o":8647063921,"tmboVETR0dim":88253883382,"8TuxzP66t5Ap":132643220398,"7QAkzIUZU2lf":189750173372,"INQ29mRiyBAf":327737629106,"eAx72Kr4L1TH":298774552535,"c74kCaqRVvVZ":582330821379,"jzojdMWd24bh":230750324458,"ndsBpGu71EW9":274145523251,"hqjLOag7E2Gt":307778642503,"FooQQP9HoMey":697561465713,"vxqSqG9c8xuS":25062330716,"ZAtlgtz2w2F1":669043085455,"Aw0JT6PM14Nu":673311538670,"ekLG99o2UUNu":782312850663,"wrrDPuAFzc8g":312236728630,"lZQymRyC09RG":102652921581,"gEPLgByIZaMd":920376798994,"Ce7pqTSTB1nD":605450177173,"ZNLfu1XYSGLY":737158455047,"1UOLUzcRrySQ":747144861856,"sCJrLyizhcuO":527111854414,"IOt1WF0UhDIT":214061365931,"rthmQkDJqAjL":487047738070,"wM1CcTxdOEF1":752679537690,"zdtruBd1Zlmg":26300820956,"m14ytRlfGuvc":174982705644,"3U1zebWqSoHq":299539888478,"ImVT1KYmotGL":555382776454,"VBREMyZSCLxk":792670520412,"AvOLNuPMggf8":885627217022,"NIdDhM2EWlhH":537901754275,"i63tkVFraKaT":84836929777,"KqlKrGoCUuPj":217861467967,"DGAA8djQUzRQ":314030457649,"qwXp9jXF1LnY":29820327115,"ClPpXzfcASgd":932965879114,"Qyf4uhH5Fgy0":185095676693,"9HeIUMVDeUsY":811163747563,"1lhm5ZzCX7aC":793905604518,"tjnrlrA3xvEe":42998691474,"xkzp5EUjZu7Q":470854441199,"gNwrKVJitoM4":604117046483,"r9p7QKTxaDzN":385635930344,"bgz2WpVDHwAQ":666671812664,"ci1Wc9tEg7gy":718986686823,"H787XNuCa57M":831138379168,"Yk9TRn8pYbwj":422470429263,"FV0Q5LU75lmx":518309242956,"R1Vu6sBIejXX":526450684813,"dfMXVarMIUvH":342698073556,"m9xcULEHJCaJ":124012514973,"ta9lzxKL8K1s":661655557796,"xN5w4VrkJLsY":255105211824,"GrrKDmDjxNRN":931936823487,"py9LthBswaBI":19061450924,"NbnFHfLrn2yS":294448007137,"SbLh3uHRs3uk":756952902199,"SON9LjLxBXMl":680659034211,"Q4SVWljcaOZC":399810099123,"bI0nOUo4i1RT":409466355757,"A8cbEB8E2SZb":733868567804,"bgjSHQfICR7H":202829322392,"iVkImEbvLbH4":924845670204,"WNFNQZly3N7K":225933039823,"KbCXYS9FPe4W":454337371870,"hDfLQYJn0eJP":376594114427,"UlQtDf1hnsaZ":952701139071,"QCnCKF7xVWDO":617265120111,"R7hocrciAm58":395450833985,"NzxUQEhjcH80":464937159304,"jFkJQtptg5O4":398147281611,"crJd7aVMMuhO":219016991935,"EqyiRF8cTIBt":617826064937,"lmcYtYpzwuaa":353302408700,"IHeYY3O2F3bG":700970449189,"bbGVfGeFyfcC":119133072250,"Zx936tvnfuej":49676804845,"rOKKsCyPed9v":544003320157,"2rmCiydIGbX5":815153537992,"uDvLoOKHIW6k":48609077090,"j1UsFQhHwtTV":376075753976,"MsTtHcY5W8Df":592072822527,"m8P0cyp2iNFM":638593174771,"iOMYVrzL9b5s":893596057550,"sJ278p4fDjaa":606969944820,"aT3YBryBqaVy":682739972822,"mwVCD6a5wuwJ":530105320857,"H4OC2vSuFcA7":775313098153,"Cq9C9YAzUWoT":212599825229,"eGGs3Aan4o98":374395955969,"IcgDd1JEgzPB":965333412239,"dtOBFq7IjEOv":431645390154,"BC7l6gxe3w4E":989058252287,"vgWcm3jDCLW0":681409664113,"t1Wmj8CQ9Vz5":432819250957,"Oqb0hpZnlgV5":96411012560,"C1lADniQfwHe":554490009717,"oRSmDJfcIQms":665228966935,"0t8dKzpnkpr4":420939566656,"ObUPNJhlKGSY":127947636742,"Kb5DjQcTNylh":933656712337,"YKlQkDrwjqIX":822144612390,"uX33ENSMohDs":175052582343,"X8cyFh8Ws8Jc":817831494600,"iYVq23S6iuZD":147818836689,"L9LxAeNMhnvL":435859966737,"Jo40ERPyTtkS":876660684388,"5GNuKAi0J7qq":211854353976,"ByYWox6pYX7O":146763143748,"9jRuDYVh3bkF":620837959050,"ZNcnc7lLt44S":940072000505,"YDis81UYzbpk":942852629779,"08vORxeP9xC6":468009846786,"D2qGZ6PpbqdU":650932231283,"HoC7Af3dUpXl":55897264123,"ZlcCKdZN5p7W":119967252650,"E4hrE2el15UP":124320820212,"aVPwzpThLr4f":164959652912,"YyhH9S6RAE1t":893564350059,"qkUom0FlGZzw":69491305022,"YoC0IPQ5x8yd":669100055285,"Jds1st91Fmhq":459939940429,"WVhpLzCSPy3V":591059727099,"cm1lkbugpnmX":281879311558,"ggG6vRdjWocl":792999900044,"rIclRxbixIaz":457391134031,"dskkxYwGPB1O":137736778813,"R7EzECvxddo2":650908876869,"xwcaDVlUOECM":28210712634,"U4sjFUURitBx":135750221301,"4mxQSc3MR6xZ":877016729152,"RmfGmXtDQm0o":75113046454,"AMood6PxmvzT":401839141270,"HmP4rAfqhe3m":582292565807,"Ug1nATAeIWfY":920198165354,"G6Jo7nyaNgYO":244428746339,"CY6IKE4jD5jd":473986238841,"4i6djuODvrae":51795058579,"xqtffVcAHvUy":631853629195,"OJfhvEASKHB4":552035953721,"hxyzr5lE4Fbt":580585776697,"b3TYYAgWLzP1":544807796652,"kQLU2ZeuqoWB":249436295352,"DcimbCXdHJGw":421936434128,"7m8SEoGU2ass":277496122608,"dr1cCveemTaf":321765855958,"V19oJRsQ2QWD":229863274798,"bTLWz7CgFfvP":989748890179,"4AYDZLhzKK8q":47504397468,"ShSwfdUHhmza":793510054371,"t7mPxLb5jXvE":63250723741,"90rxmnqx9Bd2":510831120348,"03pD0GtcRrii":139785832212,"XVTYAXnGynDR":810353708300,"w5oNI2xGUnOc":938864221998,"brmDcGSmW4U0":76256211714,"v6IoOQX0RlML":851142566377,"GwHnorcaHeeX":43092074935,"plRjw093sKMo":968796335907,"UjCxJMwAplxt":209712486180,"yJ9Cq3EesdnK":585084837864,"BIEXyklK5FHh":93845548067,"FlAQZaxBmjlZ":353675098993,"8UpIzdn4dHms":264680271190,"EhJYhs6CS7rr":442652557990,"iXNvLbhr2iOy":273072253676,"5cQiPlPoMdbB":779809054919,"ApL23Xhpberg":555089258110,"K2V9n6NbP7Jx":61616022927,"44yWhQSEvmH5":114831317566,"voeEILC2qyWk":472609843050,"WQEQ0RwHpFeO":79019418178,"EQtfGnMk5xgX":592793988663,"ygRIBqVTSxLj":162236175235,"ZBwfJXUCwAKP":344319906550,"sayMSrHNLdRB":430741874800,"wXoCVNT2Pr8x":292707200458,"JF4IRHk9n3l0":199808184053,"od7LIhAypXNc":743187050540,"fNNuhmSgPnCk":709100043544,"panof0qjxsjR":338874431989,"X6pHbTkXWfTl":454261105005,"y8KvutBvwIoa":717293706565,"JxHpMZiUkRLN":933021668174,"7MAWXRuVAAWp":958138343008,"CqzMUakNauXL":967627673254,"fAZVJoGAevDi":99147250170,"QOpNc0P7LVnm":867342522678,"5oddM6m4y97e":669315088251,"WGkkE86m7UXJ":985059049836,"NmApAP515EYL":643199785478,"RHCK3DXnCVkl":118688423002,"4VXxd0CsUaeG":164914531802,"8JLXg9Dmd1W3":524639025503,"Va16ZNB3LG7r":915044299484,"6Ue0QqwVWcrJ":98584692164,"tCIHgAHI2Jnm":795988551983,"am92jTklpUeN":495131573648,"0pUiCx0AgtyF":701459438720,"HMSR1Ngd9NAk":418684632109,"E8ceaWD70e1k":534604841471,"gCIKQ5gKK3ds":758424550087,"GkPslIWIcpQe":53968796261,"h57PxjgObqaw":183401491982,"aw2EwuVplDC2":483506754991,"JZQnBSonSNWl":713143017476,"IbdZHpqpUrjn":750829871212,"kcASyPV9ixv3":32561977047,"qbeAtooka9HX":82764775829,"m6ohI4x9KYQq":410394669480,"gkXOHGJ7hkwF":967878345128,"hrc3E2vfZdre":880178183958,"WnE0AG5teJOy":592864412098,"dTEggNsn6zTv":591954304615,"arcYtTcSpWsB":416483215113,"ReUCUbmCXx9a":535711543445,"GpJSxKv5EzhY":622385555942,"7kShoSWIoF8G":173633644369,"sSoS9twWb3De":223920872084,"i0dTxG1pPZ2p":757095148278,"Gh7MY0UYAJfG":259922054539,"7ZGbZo1fHB40":626715737786,"KU1XCdWDogWt":675996604205,"hRqWV9Xc1kMc":672961898401,"RJIkBZQD0ch6":406657238123,"DnKqS4kpNY4Y":377263976647,"Gagy7K7o5XOL":431965351230,"msestjynte6f":325800829310,"ljaUnz97EFq6":394532452109,"RkdGUxsABmr8":73078631214,"8JoT1IuWZ7uk":369993374853,"ALrw4wnLypW4":802778945796,"1ryUAgkBy7i7":371868502845,"oGr3OICgbW8m":387673283799,"I2CDDxavK97J":580980119226,"deeUh3yk3IfM":566746165644,"fkDPRrSvNebP":619059419594,"rb4wXGYHSVGq":620176673266,"xpOjHmCIhs2E":361910110747,"MZ7sF6HHFUqa":63382842813,"0789IVGzwIeE":445464346690,"24CGcgw3rZlk":881047302036,"umK7hn7tY1Qk":947291372820,"xcCbLCpJRQas":642472206923,"iT86CXpPnPhT":998708300238,"bCRVgMN3ub0N":314743516288,"HWUdTmDsH2yl":145890383518,"fYL3I3Trbg5z":291190295096,"LTL0Y71M0W14":81974066331,"RVJlzonijyBU":426488798548,"eEHJ8nE5zfOt":259872876440,"CT9tBCpz5Vzi":776356154668,"nQc9Vqlduzx0":570433154753,"gxY8tHJseN1Y":548647847921,"6kNPvc5B2caS":117098905014,"xFpct4jUPfag":291105213244,"yYtllfc51t6j":702987857911,"xvquC10pQgqG":991207090779,"nEHFdkw5yCop":915631287398,"fBr4MrsvQtHb":559990262769,"SMNWND3KHfOV":869807154101,"JwUFtvAnGWZP":435371951920,"tRLbpPTO5cl9":872427393371,"AXqLrlT9qHX4":360717178735,"aRTp7xkWCDSC":474232655075,"jwVF1bP84A0o":708264170019,"Oy0DIT303d2i":694531129383,"bt8mPqYRrKjL":94473984138,"H6CtguwtKiaC":502237390214,"dhr33yTmQyIg":371092130731,"JONVGV1qmlni":975204961417,"sIIQBZbTygk8":323996914995,"3EbcGAU8fwpq":684302357573,"XS8geJZQ1fyf":121013388917,"q8d4YcOGwS8X":895587824804,"fOmU8qEkYJ68":126207895230,"AWmLP7Ue7Gy1":616570429337,"K76KT2q4SVOd":399736248456,"mNfwCKXgR7R2":422544679266,"5GBHLFDioiyJ":306656086449,"H5qnGpAvGNdR":58671717862,"vyhajy0NtPzw":462140643411,"xNHbX2r07v2L":8444993978,"hK7Rc610HTbO":777346197016,"Xwlba2jjWl3t":93684089580,"xvc9IELLxgmI":65753792944,"5tf0nfxP6cPE":310844059588,"dxEjhIRCsS12":557849354159,"ReGRQfYR24ct":725328392747,"4iBidB0tFZDS":694403148710,"uxwgyJ8u4EGL":821949605564,"5zdM7MOWCT3M":70035969932,"sUxRJ1H0HTvB":528014689868,"5M6haI1ucJon":398933418354,"FhbGcUHzDeMd":21315913117,"TvA1n8sDJKwp":445370060617,"UnOuftjyXG8P":703225422924,"yCF7ll21deBk":561772546885,"pKZ2ODXSb5KG":366057477629,"xbvYRqJfl6Vt":427260933415,"pCnuYbgsRKyb":512951627991,"70qD9ffrpfkG":264456421486,"DXmtYNW04gg9":11478905625,"sV8MpwQzVL2G":911554617351,"DIT8zS3a1xjq":271703037744,"Tj3Y0Q0lg1cY":117105797774,"04F5o0L5QGdZ":124121822390,"rt3SgS3bk3CA":3126822118,"K99bVUF37ggH":950272269144,"zCbKnBQZJ4f1":695509319404,"wsTkn3gRkQdM":452248047839,"sD1yKcVnhMOz":946225663132,"CFKtQVuLk4lm":560167432375,"y4ludcDomTUE":561966410139,"33PLGfBopG7i":591160298779,"S5K5zBvkeaAz":844390815697,"kac1TIqMivgX":830316815961,"jhu7OG3zRiKq":372769053860,"SpAoMbSlvc2N":627112324751,"L4LHjGFksOfE":534703547278,"wUPOfrK5hFYi":691010598623,"bu8JteikBeVx":3601065498,"Eykt2kwdRwn3":594295134938,"m4zUebvtmhhp":581427847496,"rOxW4oBIliAL":977142892979,"6T9xHEULz4Tq":643262775314,"yWwegLez4C7L":660766168841,"8eRwJLDqy2EH":48748809561,"bQPQwsomnaHG":288292754456,"qr5hd3ro3oGS":68567002540,"hDCIxJfAj7mg":501452238269,"bVVDi4nyBfIs":235620475877,"LBlsgM1FI2PM":593395457889,"AJsoYMg5b3KF":75251176438,"miPK4FhPKSjT":560458765184,"ICHI4RfUgEEz":192964750366,"yR97gE99rXVt":811719294869,"3qfMZxFhgVQa":224403392401,"ImkHtaZBSWhL":43814437051,"wJQNPyPoE0Dy":842325314271,"hhbh2kvfYfAP":745687249809,"Iv0uwtIbuiA7":678802609696,"wQBb1nsDH4QV":698773427324,"rJysrCspYPTW":679057486894,"s66Bpq68bzX5":707336683428,"9HQTwWG6jtrR":76718512640,"lihAyn9Y5WhD":24478617,"aSmsZW8wK9bn":786888032986,"N47WzphmpdFv":435828637203,"SpNGsT1QqRvS":681297105975,"GQ8n1iH5v5VO":319004282976,"F4yQ6Ek6t7GI":327547047032,"1Ope3tBubgA0":885647306920,"pVQYGysWLREd":502466485278,"268yavb7i3ZC":755777043361,"N8B5FAhjZzzU":748460749697,"HaBoRxkf5cM4":800945735611,"YL4wg8XRqD6M":958287470481,"cF8G03me2s8K":902881396081,"0HVO0A9Lw9UI":481601485834,"PIB7T48PqCXe":381212404563,"brNUnbfmPhYb":848164727423,"EPvNIZSzq5FX":473736657734,"Pc9Mbkp7FYjN":760032217869,"DirlqqaNIOuK":501479119892,"VKdnmXhjFCzB":669373231768,"khfc0qLRkgD6":782522051360,"v9cH32ZZczlJ":731069366357,"YuGBuoWDoGRw":702785156062,"u1tsMY28dh3y":495750135802,"EvDvGfCtzpiW":415134918840,"q4Xa50OvG5fz":798835324506,"IRl3rwJaiLsH":826613913010,"rOTa8oYyyTZ5":776366865012,"ErNNIrN1ZkX0":931943652317,"VqPgHfeg6gy2":456511174886,"Q5YQCmQBR7MV":880031706143,"BtRfkkTyGhWi":517611442068,"rGxwnCvEn3A6":220072599530,"PK7W7tra9rmZ":417917246529,"zKt30FwIuxgT":931067511200,"MhuVgZigNR9i":647295868447,"z660DonAOq5G":885326753757,"HlHE7WCKaaDA":612233908998,"73nyHjL3OAH7":609357386007,"x1FPLmhpI98Q":81461363758,"fe3PIdncX1NT":519996318984,"hzreJyOjmr3W":940843488361,"EwDHvhVy1ZAh":646053229291,"WgypzvT8Obgs":76041249331,"gSMTMaghOeQt":217974827087,"NGb77Ki6z7VP":423792762870,"WyWQT8BMYe09":978925327971,"wmQmIcijvL0J":713629819309,"P7E0H12DsxXp":890913656285,"HStSY33sNtLH":406460182658,"LcFNgWayOU1m":808489875848,"ZgAc2l5a2hiQ":413837906044,"yxcrSSecFnuF":123881665250,"khWNJH0oATwu":597594752219,"jlIUauOrWl4E":880956038324,"6lULGWeRg7Oh":642261066650,"ThbS3CGRkVpQ":467782057242,"myTzPtSkvPaD":569136256151,"xVdAnkz7zXUz":861888128614,"4cvm2t7XfLTU":509966073426,"AHef6tBKkio1":449829672233,"YXiQbOc9tYSw":9092925681,"F4luuBJm6EoA":502682265566,"Ndk2f2ToaT6q":628049682461,"oC7MexRc2sxc":187152924067,"C932BtaQ8yAu":220414800387,"XpaDnNVDyJvb":621052259219,"XrHQDvxcPVen":567292202105,"Q0NBaddCSI1o":718462377818,"yNpnvNv9p4fV":959917659954,"LEKWu3ACGpco":862054782170,"8rbaEbU4S5mP":816365786878,"qOYPaBaCMG4k":3834980556,"Iw0AxLTOACfF":216468414134,"S3Rbae6Z6ECO":819766587697,"xVoWmtlQeTsz":627132675960,"zETi3bWhHNtd":738646406080,"VCermrmMZI9K":571926446000,"xlSU3L7DTcJm":645495876580,"dwJTJlrPpA1w":56890013146,"zVRGaYmYzuna":771170811630,"Nb5tV3ykvK91":213701054932,"DysGFcFpOOo5":267152908160,"1U6Y60YAcRes":449121360987,"yHn0DFQ2VFzx":868045397788,"RrZGRdd1MytA":461173499306,"v0HKYbN22Vwa":532756462520,"AkjYd1WJ73NF":748539069384,"a6iA7JKWcf52":616223499327,"0DpJzHbEgddE":67286729722,"zNalHDfmXB3W":480434460498,"nezuyXzVoOEd":767242887317,"1zvtYTWxRKs3":670112521486,"mPGhs4PAcBrj":4433466603,"sgeTboXKbDp7":40976567560,"Z81SJYPcLzwx":214993340293,"EJ61de8t7tf1":727396531416,"C7h5b6eAhZsQ":166816701903,"MddhIL0sv9w9":733644551992,"J28zWMwKbpBd":555685151783,"gGtdQvZuzzl1":802563356977,"CuHqSvW5ieXg":603203103887,"mK4qU76ytxG9":742320275475,"ZxDmogDolGhM":539340028799,"CLUnxMRa3EUa":939720573332,"TGMwOqfkMIlI":309378677723,"TvkUQHbwzGkm":720240685560,"8DyfMCcH4ARx":500497771132,"P3MvrjGYjb08":970022331999,"T9n4lj6QEdtd":174200880209,"sg3x1f4NrmSX":755450233616,"f2d93u9NloLF":564652077885,"5b71tRYFs2Fc":66271633225,"jkB5JDRwJzgy":783795539806,"u9jnxARFWLzJ":48745191167,"nLTWZ3DwQupF":387767887540,"9KTraqgEbIlV":293902377435,"rktHBYkXTz6I":540593494358,"ZGxEtAUowvwv":382654453399,"hKEpGLa4yNFp":735701651943,"3N5kkMX7Vbhv":296287883078,"19X3iCmjHRI4":267045344564,"UPL9XhchwyUt":310838035710,"Kjm2FITuQNp0":669941484577,"189nUkwdK9Y1":226150341729,"u5b1Q3YnYcDk":155102405608,"06doyczoG72G":534454085600,"dwZdWqbGZLU7":741421758128,"KqRnI8Rfb2Tw":605896406090,"avM9uI1Y6JPg":863409328771,"finubU0gU2NV":533034200888,"hTZo3mHktiEN":60447748470,"SNG3IaCOJMdI":184481493726,"5BcmRYNi1oqJ":357916853953,"EtWq53e1dZI8":960934923328,"oRRLngp5oFDh":312957288323,"PzHX0UqSI1Sj":157574721343,"qDRotCDm708H":410174555912,"w0TItgAyMUjZ":541537470719,"44b5A7bwENrg":858175224674,"79gza78XsKb9":336538974105,"eFw38hhuasFD":612515284529,"vxh0mZx9GYta":189175054044,"U1UNVZ4CFyKZ":597709055749,"cboiRgpWnrbr":663182707558,"MZwxefdkiXsK":416172247017,"1gs3xgQSbqVS":767142815747,"3ZagnRTK2Jub":535779814214,"sC8hnq0uCili":788289602697,"R3zgrreuVoyR":928437749351,"VhaGSElFiA1M":744333312524,"Gknb5MtnNWzQ":569805545430,"bhHhQGfo8VH1":330112315677,"0IS8j6B9llIF":212300368511,"EDyY0Pg8XUKZ":934191990390,"unwwtDo0mU9f":104506958998,"Vu4SWhgWwc34":535974908815,"60NL9tfzqALN":297674520050,"Z8RJaiYVztYP":379165616636,"WxaGS7lzWtIE":140508197776,"yeaYJiiWJkfP":841920819038,"sRjq94AKy7Nb":638138650266,"r7dXbBMqMTIy":367841236841,"IniAmS27nkf8":967851266262,"y4SK2XXVFFke":466003390939,"Ii0hdfFudTF7":609327574775,"6nBOe5FzmWck":15066678223,"SgohAp0DyGBl":595729784175,"nW87sJFXkkN0":813539984325,"wZX50We4oyGP":154234818808,"LyCkLkXhsVbz":27176252673,"P2zOoFglQOpV":623167908665,"1dp31oYOkFyN":363683084682,"IJP8ZH51i2DV":359393488572,"9ca1SvpqnskG":262491534758,"hsI3jjXrfWAs":255331809287,"PvU1QSwbrgqC":470164635884,"lPk1t0pdkI2k":5984526164,"u7KigJwtTfMC":267197030470,"Uhx0W7rSANZc":769595557256,"zcsxoLeu9CwJ":176542228899,"Z4PMZJEKyPzt":513921581352,"XqQcAL4thiZU":946392165383,"zP0eLQWyArIQ":447398973597,"2CH1Z31cbzO7":515402251772,"IGiQdWbaIzKL":57874535435,"kq31vCYuvUZn":524928701125,"ytbMHZDFzhhq":335408034746,"SHZhq7hIZeEX":501743672775,"7VikHcoQdMY3":350846330469,"PgQlCCGSWa1r":48079762364,"kxZI349eaQPz":673021948520,"vbUoAbg3YwA3":197685979327,"igOR2gPsn4l3":430415635084,"GS9HzOqwGePd":381612265007,"bbwj8Cw8wlcJ":79620411888,"23W0ui9ceTzh":573635218710,"vZQm8smqvCA9":170926770387,"B2HtPH8JALZc":31938837495,"SFBT9atO6bge":819837527585,"dpLuQ6ZFUGXP":610159495981,"dSgbvxNcQcV1":251266270611,"cqD4PpWGTHoH":485226431987,"qCblS0UUddP4":343943537927,"rGdT9ndydvDf":43921868538,"eIr3NTyKOHDZ":525046929809,"8HJuJYooCB2M":733196210523,"tkdR1kiSfc8V":133373959184,"x7GOIi3I1oTW":494031584177,"sgvQzlXR7S7I":636561552723,"yjYzJHf8n6ZK":877804533201,"IXiO3TEG3G9w":942841855779,"QRh70BQX3ONs":44180279150,"W5tRwv6yjvvB":588679412296,"mMb1AAuOHGcW":181851055613,"NK9ZoME9LBvf":622705769804,"G9mvysILsp6g":212101026164,"aeitmGRpeCXP":141743664034,"hM4XwpRemSws":636488531224,"U9dWJKnH3mJM":388221264652,"oY09l9rQ9y4W":212977711815,"txYYzG50h6qk":162628327985,"JWwVi1rwLEmh":638398626491,"EYhJIKF0TNKx":465387797336,"GRkcEaOgm9DJ":257594507313,"sOuUPCnkjfkO":346401358681,"GyOlwWKpfa9r":694359160594,"hkahv5UoVRxH":568827797962,"V3VLuisof2Dm":395220473079,"I4f7deV6jqpS":277205353922,"bfxtxDx9f8nA":356507057440,"2lR5B1wRyITG":978495163188,"2Qe8PNpvKwiz":207102172275,"9HX2LJ8I5v2D":63749730628,"eQMeHSYtWEqs":392675902796,"9Bs3LPMF4SQe":786318972614,"9C5ojH1dHdIa":512590751087,"yj5rLntXcnxZ":789493944407,"onycPMWtdIHC":851076654481,"0PP0DaPcyr2B":527815784941,"9IW7CtgYKZTS":950281290986,"GX5vfn03n20w":442975784407,"DPtXbL5eMW7m":321441377222,"9Kzu4mwokW6V":643868604896,"mP41WfTNHBO8":43726731915,"XJiGHWViJoa3":484619171744,"uXrDTyiWJriF":995127858573,"5ZgkyWHBGU3g":375902205098,"vtvoDOS2k6H6":160589666801,"e9en1Y7FqhLp":287426641906,"iVquocfqC0Md":294356309984,"3YGEwDCCanMY":592074217871,"BX9acsFEC4cX":883283077184,"qnXvdjVG8PtN":769703390558,"YxEDryKgtgTQ":492449137820,"JNUuxlNMJ7Ji":127831563765,"Rq5xSFOtZcaM":749211808303,"HUGgNaI4GVQO":384106523199,"pxxvS5JSMtAC":230840843753,"FLSYVlW2MKVB":914170114420,"41phOmaeAEYM":996849174633,"7OOm2PNzBzQN":407220172221,"hG7EUal8dV8G":321399280226,"AHwKnvIWyyqr":542699744880,"cJ8vHYakbDVS":999453889201,"x0czRQaoONzy":684676131018,"slB3kocbFAYS":912579333227,"Ksiu4xfPuGCa":117568561786,"zuzjwVYd5HiI":81204010416,"zk9dZ02OjMxh":221256346075,"p0wKttYo2fMN":186297602316,"W7KBkbWEVgLa":38797210497,"EFDWMf3Mk00b":449259641769,"FaJ0EHIMj1hE":530545446007,"fCuJFp1lOvfl":205950298468,"0AKemO22ueA1":834122369733,"sDnLlhS0WKWp":412088191001,"ltDj71uVWwcc":582708818133,"GIQmflLNvvLF":253196435170,"xcIOYXhzn2QR":131290246827,"QSbUUooFUv6q":40371156931,"uirpZf4FpjYI":505884438363,"kkKb0MzqIxtk":290516959749,"MqXOAiHPN4vs":354454558420,"kEsUnJ1toWLk":67770035519,"sx5dxsJtwaWb":866182208516,"dXnYROYcJDOi":121425972861,"o6fEmtxU2aDi":597490811248,"HZBnqko0ek5W":512835434464,"8TrqRhwozDXG":645514618069,"rADipEA6iPlD":885213275865,"IZWq3Ms1ucDz":624816398594,"pJc7lYCHFntx":361524948929,"5TARPa7wlRbp":779413371390,"OhfOowhjCX8v":818330139953,"R18E5gCdGgLe":742379452427,"ESo9uIB4ojrP":794983159818,"4HeGsEwzccaQ":825826687846,"iughXi31tc9X":806766023984,"s6Mst8TAHrLd":417967841676,"pY3b0EJy5DDA":90263322504,"f1UWXhPfeZGZ":917968781753,"lfYOgWnh52cI":161765985939,"CjcH4Rl43i8n":338520475503,"Q1PYEzqzqg5R":144214297244,"A9LcMZ90x8XS":202325023778,"9CWqRW9qjrkb":869536599407,"94M8aPckA6PP":118579656285,"Jx1c6zP7NY9J":8246682996,"dyljnAkYpwGQ":499520315226,"VlkUIaWgnTNa":235128876770,"feqTPji7410l":239552811350,"G8tWQpik6KCE":298223905027,"9GfyZvRCrtpA":532287551227,"BoKzzFzOmtjq":304143082582,"o3pY9qO9EuQF":248550702619,"KTsbKK1tXZqc":473109639928,"DwdC7JiUk3RG":59691192114,"IExupfJIA6Zc":361882749577,"79OefuN3MKiN":791227004474,"yO81K47HEndv":273058103533,"VtYLKnywpekS":225357810939,"yICeTDs1xJa8":726139485696,"fuKnEOgCoIdM":962295688681,"se4zWEWRbuzy":476699650749,"JoBg33cBap1F":235548560785,"28BY10q7Rz32":277655495764,"pgvqmqko8ZU1":792784915058,"fVw9UCC3JImB":538623502563,"ErfceNkAEMf7":685116717371,"2dXnYwXGkA7c":412542183424,"7esLopep80GS":239472427901,"pFvoqLPFDgjQ":679899995941,"gPpsKReqRk6S":551220716263,"0Svfi9R5XVlQ":423720406035,"LuhKl95tNzkr":535295891459,"Q4aLL6HuyHwZ":73277067470,"at1jPx0komjX":674112315036,"Es0Ntnvo5FHZ":680654145895,"nzWcvpyw7zmR":200451119842,"x1Eur8o9bzwJ":773042535485,"T8Q8YsJ1Ldab":294980854752,"tUjLVI4QdSaJ":185631184371,"2NT3X9UNCrGo":567035161218,"o48eEVZQWxQf":664472680016,"qhbroQL4wshu":275930392919,"PtMINto0oXJJ":271682693679,"hsQI2JXJFGx6":839469008258,"wQpJRaQOYC5k":701619560800,"MOfoBUKdv7A4":814187389881,"dRO74x5FjB0X":921454508736,"V4FAb5udpfyv":996886963342,"tKpNiHlbkE95":733678896818,"IbQg2LOHusOc":172783678259,"PByrlQaLL34t":189817801157,"nJC3MIQzHHo7":164956986701,"Wx6M8jn4wAyk":992643577124,"Z0qo8wUuODW2":598668239973,"RvvNE2vDY9Px":478753200882,"Rg76O8hN9KnB":570581954206,"a5URPFJppKGw":746366340637,"OD4aFSPeJ98Y":200148727512,"nOL9hoMxPKr9":168204295393,"yA0n1FViTH0S":769528232568,"dlZDCxWIl3oF":23258204191,"IoZUj4zQNeg5":361772877610,"uG4e5JuFAdDp":786376788200,"jRyv5NMpdnw3":997122062216,"mBaNuUeO6z0o":408326481441,"UDXI4qCg6hfS":404572022620,"xG9aVG8YqqyC":883740048313,"qwcRJ7oro4UT":840101805653,"nT5VIgE8diOy":887336354708,"7KjBiVyuMjcF":149906989595,"bJFvA32wQnaj":548229100849,"jw7NvEc1oUyT":807724037652,"Cg8YiV4zjzve":694956161563,"PCQjvieZp66e":15231940056,"3OOTH7uAFiFi":175717769229,"5vg7mPgZQEHJ":539026464098,"28MAuVhPrVEj":680566497140,"VMZEpS8djrcL":668323535102,"udZ3BP5clOlf":88907894640,"RJlB7pihwOUL":970033201905,"8dQi12e4S6WM":230290842786,"bsC6iE5Dk150":839291591824,"cRcXsjmOCoxu":668917356072,"tM758MwBo4lt":277055360007,"gVDvAe35CO3v":72658885378,"vESoCPhxLPr3":174088119631,"nQh5UZb84hIl":608912147436,"Hj4r6fwgjVQ3":69845152401,"YIoVxmjSEgJN":597564781547,"72zifh2Id6Xh":188060811886,"V11EcIA6S5Yz":160250587146,"K8BNwGHpkTKL":54462694335,"tVxnKIItGjcx":595650394570,"qHXqPwHhVyNn":388296417211,"ukHNlYYF04vb":792724464792,"MaY7xSWqsy6x":826987844471,"EvxSoHx1BNUe":303715913748,"1kYdpIl5d0L4":707759872675,"RgmeEcmPVG8j":498389528129,"Xr6ELE6xiZIQ":383757753953,"dBPjVyukEASY":86071776498,"MLy6yfoGVDlx":503483014376,"DI7V3Kd66jkB":322842790160,"68glDeyz8roG":569830418851,"Sugv2BpEIo7W":166136992928,"5GSInBsCEBdV":95657037567,"e8aKCVelMh9x":891114392280,"F1HRa54V4u98":546457645985,"e0C1nV5LvNzg":326327500797,"br54ZsgbUTaP":270939942067,"o5OfztVZQ8er":763267986518,"7k56ITXSgQUb":729397798283,"mKmPZdeRMfHu":996200572094,"VVe8GmjtTzP3":802902488600,"le7yUqmYB1mL":507146900631,"fbhj4YeAPw5f":37917320171,"dq9qXjkCJ5eQ":784742882042,"CC4g8FGlHNUU":508358924643,"Doc4zISmpvt3":734599878664,"Z1itZmvcwlZG":572680308148,"2k8V4fEIe1It":478974809641,"yml2eJRxxMLh":99606350084,"lZnaJ9gT9Hmg":652184625332,"dIfUDyH5OkaW":10492629451,"jweGPxpCwgSG":302836905199,"ffP18NXWW1EO":57114451777,"gIkde8ykzAuN":712748037503,"x8b1XzgdaQcQ":744891917341,"9G29IzNQVDh2":388632857483,"W1Nr3FExxKP2":248199194070,"8vSLtIGVhNqF":466370346170,"BpCrfCpwqm3a":819795684704,"KLvcVdeEwIgw":831429240803,"25c6uuVaMPvc":311522358731,"XkuRqx9Hl325":635123107365,"1pEs8Zb67KR7":206928260610,"QFNRpsABWMYH":744768698282,"CLewjEqPFLDR":731596103420,"dl8T4y0X8Gir":716445862708,"UCgXMaojxSrP":424385721618,"mS6iMjrGdfHY":139197888104,"g8MLLBCmqRjV":470185249791,"FuGjY6zrijbk":813045047031,"iUmW3ZIuUPaA":437559347700,"5Pdf2RSkKm24":925688430611,"rMujSmO1XZox":400737806304,"uY2TDwokVqUj":546756087098,"ALFmqKmylgFR":586089671708,"uEjEXlhHc9Nx":169124533870,"8KZWo9hAz4wy":62600220172,"61HHrVfOacA8":86496513738,"sup8YcnLsLtK":120226582605,"NrsFjkA1No9x":865085326268,"fnvevWuTamYL":405367829914,"gMk7GdtZgUdO":992076950150,"nr1Sx5uX4pJ4":977228884392,"gxhfOnBgl86b":343108286466,"TlFaYXsJk6bJ":114267262706,"4z10jBSKAmUp":728573869501,"30m5oI5Dx6zb":431356729516,"gzmt9yB8fbPm":102761440979,"PPLM7HnwFsgc":767999640365,"ciyUiloGeZO3":978896414606,"T8OBRqPeXCPH":227489336714,"V667AO3sjWMs":493390525644,"knufOOlWbAOD":253592969849,"sS7fvdPvpGbJ":929007247888,"RGNSN8MmmTsn":315071902343,"tjeuGfkOsYIp":730655428760,"xs4eNIBbsNOG":83344854215,"6ujq5jYv7xqm":216488042989,"8OkImPCeDrod":905223188677,"sIRFs13xcwCX":617430333790,"zElNMk9pJTRn":524951535710,"QlZu09HGVRXQ":630497273496,"Rcz4ZXsTwX1q":347241442525,"RHvT5BkSsVoy":208368120992,"5xWG6ZVMjWHZ":454402524303,"8LgDeS213kBt":846369454809,"bAfT22jSxGKy":569190312890,"tgt7pQl7XcUx":637211841917,"cSm7ZY8bz2sw":843387898198,"ZG565hhwq2Or":752809820855,"ORr0UWFDugXM":615788275732,"kNluiwErxnrB":598151477822,"mvMqk8cg0fRX":863078537835,"S69ytzckHcqt":996076701802,"W4haRDoTQaOR":376765439119,"NzKIt5HeSxKN":161778113835,"22ZnoSn5Re0p":508116055577,"YY6Seeke1nrN":785175004702,"HPWPw2s3NVRF":826974621835,"X4MpjQvAZGE1":269814855968,"TePjeEJe99NX":473939675860,"tpGBztKjUF3R":812993060270,"JnzFm3P9cxiB":564252819741,"IYNMKkpkdRC8":342774734889,"UEFd6jlfEIPD":231754013453,"dEbx913ZIa4r":251939835074,"K8EPI8tySwWe":184845722270,"uHKpyrO99uuh":722490255658,"AXW4zETsRLOd":928470736166,"KXYh2YlkfbfV":339636029448,"ynhm8TnSBFVy":988998072669,"IoCQKFoyLIhA":495862551813,"nHboLkdw5dmX":826608966094,"QMsBTcc1xkzH":347998411464,"stT4sc3FjXfj":144776370927,"t5p1kXlwEFTT":111241513766,"DmViwYW1hwjg":249810119832,"qcLtCRb7sL2S":805712718182,"HYZdp3kd4a2X":322098355799,"09vGwhN42Ji3":340896208211,"5RzvD75pBoFY":967631920512,"BCH66j0KNlmG":121227598024,"6HNgVapWdtXf":371576699341,"rVa4OeXFafGB":503761550998,"ZbZWQ62jg0yd":4341726585,"AgqvLLlhsHna":272390271566,"X5mw9PmZP12C":365348714154,"3yM53TCY1GEH":949814020204,"uilM23qz6v9X":859146750321,"4NgkFhEeVbGY":433835902999,"JEDY6G6TLxUK":509606394308,"9kMotZp3EWEn":720556899842,"FvYRp6bTu5Nz":804599069180,"Qwzurj0vjGiU":896302782993,"OxRqmLre0M51":811506663256,"n5Ecx2wFaONb":980749751345,"mihAyVB05ffj":659832945261,"kz5v8NFTgpKO":386735472594,"piJcKBpndU6B":149469445299,"obffcVowQHnB":270476169370,"73l0C6qPtDht":604054831890,"Ol3HeKVYMFcb":601123614106,"RnVhPsfpx0DM":455795708602,"Lx1LgP8NUHYP":959547370469,"7qMO774Vmad7":610470315299,"jv3hAeDRPl00":423296890617,"7pCG36EFuIqQ":606499316659,"LJNZ5Upup6eN":474418570381,"yMDu53joY4mT":203069267428,"RVS3AlDEPuew":295276892869,"Tf7qwzSLKDXz":118526642204,"9VZ0aKIxcKyQ":182164030876,"ol0xfzBXgzww":367667884140,"Zv4GIq9wzVt4":276128390183,"osWtxjM6L2ji":938693697964,"l1Hz124QnfAX":88005587759,"L1HTALCulagV":984817611062,"qKZSpoAzV7t1":419384412024,"uzZkibCFdC5x":622898530351,"GAa9JSHxLcur":827519264424,"Ab9egtfLcojV":152844067902,"fYxifZb135rT":3057477940,"2CWSGqNK9LAU":200948070884,"KN2AyUNPwKOh":808059107999,"Hhpcm1glozL7":374595915585,"DvAAG0c3Idw2":736136804253,"43jP1xkQqAwq":594057457888,"FiXm6lObBf51":466946261171,"OkO5GI7v1LNF":248786990553,"YC4wYCXiWYZI":648581546837,"u4EmlXzlcTc8":975062839414,"H5v3pBApiJ9f":831482983156,"vfW9iBiaRGul":176664948730,"BwkC61tQKQBn":576762420206,"vZu3MoUeqGBb":114022085077,"pBAZkN4qFe4a":516706680753,"RCcbyjXmW4Pe":35478228021,"aWlNG96B1paA":658132393710,"fHV6EGXxdLly":758656586949,"05tjEKzYvMFr":411480635203,"5RfQzZhD9nND":15246469989,"Exib6Ym304pc":235233796310,"8PAhljOn78Rb":405222774193,"ZkRiobc8CUNB":573407037090,"VJZlSHhBd6bc":675068340164,"9jrssGkaBVD8":426637104260,"Xn1NZfiCOEnh":574571280071,"xdY1vyOoD1Q2":856009968512,"ERr1bNo3NJy2":511180103716,"Frc0mw8nNlSX":849558312096,"yubdEBBhfCnY":510185575735,"69XV6qcw7pWH":16730021914,"geGAmpdGiAlr":586147910216,"Sb4X1OKzUDsl":273313910606,"0h8Gd9ujhlAA":600376838771,"7CdOLohkViAY":24362380502,"3dt0XQIDQcHB":586942802013,"WqIDgzTDQtQG":240835074881,"scZ8sh9sBwV8":784668126173,"8jLm74LrsxG5":358091141076,"gXfvI6AHaMPQ":715099124358,"lDNwh6HqZbYA":321391786134,"aIarRrGoqmJu":401940934636,"Fz6xHucr9egO":992738537212,"d4cOEAgvNBZh":208784112396,"Th3LPEbIahhs":813702545310,"nSYKKRdgqJMM":199203509292,"1mlchAzWCKfJ":698596084885,"nnDWuPgre82I":252047083209,"vBGUEEmH8Ana":206689355612,"T2721atVgeGw":581792764592,"8fXfVTWCYQ9s":896622801842,"1FlObn1DVSuN":846748305116,"cjxoRvx09l9s":961324300975,"dA6hRtZWV8ZI":248988371267,"6awqx2SoW0L6":65087158343,"sXakdtQCo9ml":207052663365,"nXXLoa0MIgxS":6657042269,"pQx2Er4GgmFX":141947011777,"7wp8DsrB0A70":76268459382,"UU7XrNRuv6r7":859480846253,"pIGRcdLLNmVv":880037269020,"xTNxMaAQ5dIt":59051197305,"NXMdAZUIoqNz":956822048513,"kB5mM7hhkSL3":219013976549,"9gXUKfYxk6vQ":401969280292,"DUmwiQIFD91G":168547290160,"yGm7116WV7dR":292814574735,"KbhMdfIMU6sN":797413470474,"Qj7IoU2wJX0V":405675276270,"pmazlSwvdAIk":754949466489,"NGBPaVuJfhAT":281301414655,"6uvdIRXJi7nH":500978316233,"LTlSLqqjqZuM":27715497048,"mR43tBt0E8aW":36961735360,"WQrAvTCTw1yJ":676431500493,"xWVRqcHe8suH":138681372170,"fuF17ihjJSgl":911313644808,"wtNXwRxhvFZ8":240770560898,"1hzaqIsay7G8":446123066245,"dA1Jo4uP2cfM":633255310127,"082xO6ford0u":942200484215,"XCaYc1Dsb95C":833850581476,"vd3xQ2s1f9C2":34630431150,"9MvONwCHzuQe":69004372950,"SS5UaOEp8U5y":252222165094,"eCUBcjXehatS":471224586525,"ewM2XwrjPtmY":786542830427,"dJUGl3YXZ0aj":730257558063,"PJsqA01YrtQ9":321680036974,"3sDYvuDTJr1U":342454320774,"0Og7O133DbBH":145096171877,"2qjscTVtzv8C":169118818145,"HguD1x91nbY0":522310346354,"zHFMThMRoOW6":99418674843,"aYMAIVO5fI39":793934656116,"8rS6Zt0ASNch":981862961332,"CxSmmYxYsItx":145023093290,"Rjh6YYYNrZNV":666278919894,"ZbOPp5EXVsEj":183726419373,"5yuyYy4X4W5L":466885280251,"Dpihz5ICCjwY":106356920635,"rHEC2er3Fwy5":143691584110,"bavoupoPaGTN":152065205615,"9yKaSmgzqh4U":224602670814,"H3UHpAKDZiqT":849100698536,"fHLwR38p8shN":890002046215,"Bqqj9yn0RRiq":890442248638,"Dbve8623kuVj":963566852442,"hAZdQf53zeo2":173121496896,"6u9uPSsNM9ug":233843060242,"QEIJYHsm2lmp":284172328005,"8EeiSTKQQ0MY":456023304173,"iprB9VNf8aIX":273996708167,"HP9QNlJGIXL6":254311162323,"Dh5hQf6HKmF3":739361098921,"vTDg1YzkeLEh":193735901014,"mvVmgIAIRaGD":585601842510,"y11Aw3luavq6":384444582795,"XWGOOk4dVS6l":65151894331,"NTBu7fG6gqnq":610630464130,"BiDiM0DOTk9x":677859517907,"rHWdWvzZS3la":761955450858,"24RoSYn9JGSh":949605147725,"cM3VrruoFK2a":910191537795,"HNtW1NpEV5la":585131947654,"1Rm7aaTIdmOW":373415674387,"He1NWwUXjK01":791742423857,"MLowLpHgyjvh":237272382043,"OgQVmycOJfdj":991931687577,"rvxoLH1xvooq":506827319358,"DU7Zdqe1BzDJ":351699843125,"PeIr7eksJHMh":714787396456,"R932LpurBp6a":851333702037,"o23P0YkJZU7b":770929937039,"2tVnPE1bDWA9":811790045609,"mfjUGKh5fGj2":157114000439,"hHwmAOq9QugL":831441228447,"FSzCcgq4mmiU":866350774845,"3WcLLdiglkuS":673473696325,"D3b4wOZHTSDt":977300594153,"iBwuNnUu4DV5":133408155306,"1YQewwHkkErF":207426685319,"nZrmwVfNbeMt":568299642216,"O6iCPEvypwkF":437397859102,"9MlgKq1KQvUc":960775420400,"eWtaQHnc2u1T":206036702495,"eFSt3OEO3Zus":142121484885,"0gL1wBV0VqNg":587231916210,"8TkkfYLFBz15":736593941787,"uL3Ys3uTfxhA":640340112040,"lRaKTPQqArEc":190049762653,"N76CZNgBfO5q":560357903023,"Hota7rLfoi46":468202592033,"Sht8WZz5y4o8":670320986755,"FpqcPfW7uN6B":427846059467,"seTfOgyoPlAV":488474896269,"bXPc8epTf3Cy":879760827553,"KQw55HvRDYHM":566061672425,"ykSJtpUXIwj7":289593094579,"nOzx0ZDJhoYk":482083996932,"SInal1nFF7rs":926125251532,"yCa8KoyUrwcd":351037943039,"1Pv2oPrPVt3D":847755341273,"OvzzCNvW4lPs":291935358387,"0Kef6Ey9GFOy":760025473254,"LSUG1rzLVlxP":131495738094,"D4z0omQROnpJ":375843316619,"TKGfOsVsd49z":166672890513,"zyt507qzXN5u":300206342669,"dECMmT1PnUSf":537505429597,"xTJ5O37FQjyW":493668598598,"flIiwSfgSPjv":84686820660,"PlK46ddSxO6h":999871881887,"VB7GOEyUjUKk":744933756440,"9xi6AiVy0WjP":845496158881,"fPHLztxEsTsA":381740565034,"SruJQpszoJTJ":377568713244,"Hy9P2F9f45RJ":893833737064,"C0Qfqk97yRzB":483040825690,"hjMpRCYR4pOx":106844440324,"4aYF4eWFdrjl":31742108507,"VVznQh6u24CI":639052260425,"ysi5XiuCTLMV":110002505102,"AcVAx5X2YIJU":310888967466,"ebeQuLpTa1mL":973921932754,"sglHNDRVny5U":228446538811,"4VNZwvaFzOfe":864621128982,"XO02gQCMWMti":811557890203,"PxZY4xknoHlO":855686146322,"Tn8VnSkryCIF":395557144679,"6wTOzGeq1oa1":357049241562,"Auxddssy4La0":122888167615,"mvYkb3aErt20":816356171026,"XMVJ4Jf2AdWw":139527964768,"5a18T3dLv1NH":928250471432,"7VAaI1O7hOAM":346283447357,"LDeV9fftXi6U":962543087146,"1YN8WbPDFoly":628979972523,"c1cIF8TF6qdZ":985954586082,"2nAGWUHwe4lZ":106177342730,"MdH9upmMoJ2F":476935966706,"qlFE26bI8xUK":598695774257,"1igctPMfgUiE":686923864439,"B17ol5UIOJBj":805525393459,"vVqd6zGkBGO9":713531332537,"V3pMpXp13gJS":379204426284,"8yhxjQuK8omJ":516344882647,"MHJgsPkoKTvC":519599990055,"Rmc72ks5Fksl":308756258645,"9NvfI5BEuYN7":71143318263,"TSF8K7K8BYCm":340693612523,"Z5Q4f2byMnVh":640635875249,"HPb0Z1O9jHhM":271888571967,"qzrj8gjeVkFf":298339640484,"DueiGLkX5AMA":84118406107,"Bw1lE0ncfzbJ":497802979099,"wlWmpngvhwHT":786874083681,"yGGpSTStAF0l":996817431997,"7vb3AtpsZCDn":285343522922,"SQ2tV9oPCdWp":716123476636,"4IAXbAy8htJl":8572034655,"hE5kSbc9h8vV":166460286117,"D5rpnkkmsNyn":283109925025,"Fszs5QKTvvoN":385109605536,"ijTtuGydNioo":594248959681,"ObRrYaiEZkNT":886392013901,"7EqXeAaEz12C":47979463637,"U98fjDqyXQIZ":952938701203,"gemH7UykRZ3O":313407481442,"oRuwuDALzoKz":396090204151,"Egj7X1VI6c6i":742157154793,"Ez5Bf7hfTFXu":776356155207,"mtgUHs4daLci":138047538934,"yRLDt4H5U8FH":19280574195,"50epK7VHjsSt":894940426987,"ubJ04RjMe1kl":162483192605,"YAngcX07OcAn":894055211149,"FwLWDAvjwRPe":556844086508,"TF5LSTfEUf0i":320271785430,"XGHyFnY4qgRk":363842162610,"AYfXgfBO33k5":64692275287,"MtUncEKD55H3":817106712400,"LUu5rRHegb9h":675299125607,"hXXPqfISq7kh":446626985925,"kIsbH67mJSyq":139458216118,"dSgpJ4MDodKy":90480157717,"eozV38nfWC8r":503366432550,"LDZFIMgVydqg":779336409413,"GWvy1tgwofLD":96297684443,"ml33x1Dly67e":623395142865,"pD68hnCbAP2h":639081192356,"IJhc4thdnhyR":841248438396,"RfDPLwnLwvcg":901589518100,"g88nD6PxC2mK":576548934572,"kMKo1wnD0jOq":28065955086,"BnEM21PEzu58":618961211507,"62eLTomcEiZH":23192207361,"4BmEoh5dOjRq":921451472014,"5mF3qwJzh13V":928347309260,"t0H5PmHW713m":592449189994,"ZORerMPkAZuo":262224928314,"LgCqFB6NNzgx":495518390415,"lpkQmXOanIyI":102503606040,"c6QnV3ogqxcN":607937124066,"1qXjbNHyTVwI":589176156612,"yRRBiqlfsCZ2":993180642383,"mmfr3ELZg7fD":853739436068,"rHiVaEpIYk7J":77737286554,"BJVQCC4nq0oI":210660403660,"9OzyqqWyhbYt":871174269933,"MrclUvRSiDUn":51479872673,"GZGL5EFCnrGq":111362107666,"Qtxz1FU9ubGd":839059886021,"ogclRtfhXJNI":304633169988,"wUWbLV1c9kLm":399544060402,"sj83Nmvbwrrx":111231939714,"RdPs8Ykw3J6r":534152576359,"SvVfSP0yXrNX":681887185870,"reO9hu9L7Kyr":666308442161,"cAg8vsv0aWNv":537139493300,"0uhJmT5uAAN7":514918345266,"A7I471tFXNPj":324847686799,"oGA7TYTyLn9e":25224931495,"pf3lIdXkxPuc":482164333366,"OFj6RfW1vx6l":531472830806,"3h0tdTcBkjw3":948650598836,"yl3Bkyah6MUK":796405287302,"SKiSstlZcDlr":189624033408,"gdV276BAbaKq":189897783982,"3buC8trrKbgk":242631624570,"2Rf6EwLMG99r":859726516464,"WimbUcYaqqEE":881530814418,"afcfn8yFE2hL":85523965537,"FJuTCkFutB3Z":588827200123,"k8A4sG7UrrFs":92382821109,"qfZTfhZQ0PDs":46115823648,"C4Ucn3FX2BaB":754223740584,"RMiZSm9o39Or":120667216677,"IUSvHB9CvNqY":963296509515,"y8nFDWZl0qzZ":185749418836,"OZxAXPWau26M":241535106396,"TIcpOtgrGbHB":550217745602,"NxzBA9DA0znf":12931782157,"RVmEVoxwSTkI":928975739646,"BtkyXtLwzte9":381401889042,"29z1ZC1r0wqn":10296486448,"pHvCad5QthGP":44152989049,"PRwGYEZh0lMy":838507040865,"rtkdNLBtm7nX":441341860815,"E0zpjJzwUaub":500679645357,"6UGDLVCcCO06":279461101575,"a5JlJHMIDpIn":698837115734,"VpJmzG4Z5X2N":890052594930,"Clc2UCx5DNTU":494159550229,"aWnk7HtDKyOT":108654670552,"wN4ePmrWj5dR":308452900956,"1sXg6VA5p0EC":896345388552,"cxsyW6D9kezO":957980090485,"HrG9YsrvtCN1":158947232304,"xrkGo5io2yea":75280439966,"3Y49gddhJTgT":74429673650,"z4XjHYtZ7y6q":338639459613,"n60g99VScFeZ":427421708119,"bbUzGWv5fOYS":188931262742,"CNQHXKtzbSRr":934751425067,"vzgYdKYsJ85Q":696776042363,"ttooB2Wm8QNl":579896194346,"eSC6udNLdqOI":256238859251,"JzQC80JG5EUU":611029116742,"EcB6sKxD4ACA":896903989606,"791I5PGHeK3c":673330861416,"J2UBZMKIXhH1":40712102293,"1YYXjNijY5MO":329364579826,"v9czKZS0IOoe":752639956596,"XER7r9FITOqA":891742823554,"vbNHGfeRaUqk":659617470935,"3o4FpuqlZfDF":403720419892,"25Gz0qASgjd5":635606767125,"YVnFXRunQyYx":458430569582,"Hk1OWT9iV9ql":938463776491,"AMB1hwcRRjWR":434734283471,"Z1aoTPSiw2Zc":134584451620,"uLueaqc8FXJK":342980958275,"V3kiMCPdntp4":380545323701,"IO1FrylS9jNk":744066558944,"muJBlL3jX7fo":889198045893,"jrEmgFOP5jEn":325332251712,"sH4XHgYdmyAJ":55390875226,"DmKKJBNEiWND":383466742746,"zP7RYIfCp91T":50409071206,"EkLHi3xhkSCl":827982923808,"bgL4VcyqnMsI":270805813458,"8pkQEnUEVJfD":578502615657,"vN94APeXwRJ9":799873813970,"PAqofLuXJS9d":492591059518,"8Vjixs1GgoUj":33656876178,"xcRyZ6UhU7vy":184447380365,"ryu9dXUOVBLR":67561979474,"0NBO8bfre8E7":675806466566,"DXl7HLFDUOmG":842069307261,"jneVPt4YMzIc":147814468117,"jKkLcv2Qu9rg":289483119029,"JsX2d6xfHxm1":998634669304,"CYIVxvnYTVpz":735797441970,"tk2T0gwFLcsL":212230789100,"EOQm0VSuhYTI":373193867017,"iYc9RcR28fLX":41686671917,"TGXGEy7Z99lW":552713858006,"27NMiXSb5F9c":408712827005,"UZdb6d6dhsCm":530958111871,"ZGLe0zxfVrWF":232495826550,"mdmAwSBd2A6V":866037674164,"KCPL7kRLFZIA":35127478816,"y2GUY0Ztw5c0":324315608576,"tOKSxblIIcBu":317437673232,"cloSIjCfMKed":158304047685,"E1Opo7apCPNV":953834416700,"o42kdlQc8L8E":49815574878,"gL8em7se4uj6":642982814728,"0hgCotCW81hm":171757927742,"9KnkYNbwOJFr":758438581729,"5tnHPoqvGDoU":938547326067,"uvQmlW6a0Ew5":258006192076,"kB9pxvrumNGF":18037270461,"KwfIyQNGQOLg":620442580963,"LjUM6iow5NS7":5688847730,"XQjtOkeaUeK9":849467869847,"HiVgF11MylzO":793846201980,"D6rm0BJ5sTRf":127612820130,"BLA4E2l0NWpV":500289691166,"7ZI9jBXD9FdA":14155218431,"ZrZGbECxZeVq":885931165079,"hWto5AZTA0N8":916543724985,"eJD4roPD9xA2":423590762211,"iaVCDwqmGjNS":804426389104,"7Go2u8zWPJWJ":836497789865,"mDQ6lDdp4m2q":725765329214,"rvzVBL4hHm4M":152440437123,"orA2Cbzf0PX0":529667855445,"2nto39HjsrSY":886260980399,"Z3rSFX3DpG2q":338016124563,"KCbYdWLzhUju":623836260241,"ogcmEptYpmFO":469196332432,"giDQUvTAunCQ":954977378228,"dfdH7KEc3b0Z":89891806728,"h9aSvJ1G9qf3":179862690649,"KdDNqsl6likb":152841242656,"Iad5fEPXjGtJ":291652629743,"wpK6D7Bkcq52":920218681629,"oeqa3OCuxJJY":786787423053,"e8FeaL9m7XpZ":938807315543,"JO8NWPbG0pAC":6390887786,"mU0solHBk9tE":562351506374,"7NEPAu6pICpU":188211872244,"vbEgnxfWGD7a":131117053139,"wKf2GfnrXDKH":618003163562,"4vPHlTfAdLjZ":614578305322,"xMZfelJhmbk7":197577665412,"7qcfeHgav4Mq":912062263585,"jesXgY97Asmu":295725338315,"Oy9yiyUwQgNl":87973285875,"36Ix1mBYCu7W":842019990253,"bZpM1IzP8PBx":800660925672,"fMksq5JIRCqt":876288654477,"rg2c3nV6nMb5":283772346464,"yxiS9C3NO2t5":662497590774,"cGymEw6Nu07Q":406594261599,"YSxPTrMtVgzg":559803617611,"JTwoWlkYmV3J":237860153915,"dqArleEpdyJb":830694152285,"HqVhmCfK7LsT":906852744737,"c9J9qGAEbzg9":514591263470,"xY7kQK9yXkmT":331807987245,"lVrQZIH317WS":639698899483,"nmD09QzF6cUJ":752598166019,"OjwNejL2Ec3d":84975355721,"SEgnpokgSALn":263822526490,"jO7ldnPZv6jF":347579897295,"RbWWBe99CkpQ":880600253014,"lT16GqgAQnMg":907543260919,"NDulfILxgZMk":610971006443,"l0vZi48bT6Fh":46822030929,"K9utIEC6Yqpq":264484793275,"0s0BsnsvZ0Yl":910104526552,"WCuXiPgHQsen":761140726722,"yvXXE6NH1bht":527879385088,"4b3KwpCMi9xl":150783854980,"bXgfzyMTC12y":389833224808,"n5sHPny8HW8Z":52278286604,"787n6tX9OUbC":136189971279,"RkOlOwoiTyS0":281578378525,"aJV8nF4gGddE":437405531201,"8nGXh1zZ4Oo8":482077843213,"DsegEXQPMOTg":740607527105,"uLPCAx450vPt":509463058556,"6aisTZqTOnOL":949416512072,"dgdP7MFiQoTW":444022056,"MLOlogMbZ3ko":149797121999,"CzLPs1OHYH2e":450213875607,"4DCPWQ2dlPaj":231729138203,"Ps8i7WssLqZC":852585678185,"Mi6hJ0hBVtAR":954916133176,"xF99yDVDRpz3":779490620914,"ThkDpCf9O5aS":865427679305,"wCwECP0m2Nec":571277726757,"vFh4JwwfbUzq":99944198623,"Igupcvd3OG53":946139140438,"N5qaDSjzG2P5":229029235961,"cslhG24g6kMa":240494059615,"cUth1CSQhfIt":539165353965,"onOyoj4m1aDK":682041489813,"X7n9ro6Evha4":463732904199,"2fLqFTUWxnJO":332363534066,"DTNEcmlZSlbg":39758869743,"i2PcklS9Htr9":33988625423,"KMG9QSODN8XO":557900743492,"OBognlQqoWuu":295715944810,"pRK1lbptuiEh":94681152608,"3JrCReFlkYVl":576982125618,"phZdiLx0TE1r":871736219055,"BARvRnGSgN75":486387588208,"Y6SIhZdoaoIv":6004236350,"IJfvtvbXJLx1":361129614765,"sNxi5QSXZOFx":597555778136,"YpxKyDIFuRTt":31388217628,"qIkAc652zlGX":616806453210,"2xyXMvhRdtfc":722652402640,"EMFmk55r1yZP":315756757389,"UNoVJFvCiV7x":808576039818,"5N8u1k2XPz9s":302679321422,"sbvWJNcKhBnd":259991921395,"tUUXqywV3aEf":424907391936,"2nCOV0ZozCwQ":270596610117,"eoLbGPNcNBvp":990945745195,"qzMNCu0yYwUv":416387140346,"w5pTWvr6lgkx":785360480784,"NZp3ZQVqw8yW":935885612203,"nkt6CxPNasGs":409303931167,"oveTJj6SJuNL":806039439893,"jnFwZz0KesfB":67652703139,"uw91WCcMnkfH":80794213427,"FFCzwHguuSt0":400617350443,"TsRHbSgABkII":944125862752,"WOlvwuGPfytn":696337459266,"cnamlrMOWhAL":143758424055,"QJVbwWcGaDgU":365016213431,"SBeaRFfkidCs":258823323048,"xGBVGnVLLWuC":411027558810,"3IwNBR4T8yeN":742080210727,"bO0bvYdH5krD":159087812226,"21sg7ih1OnLy":803500882454,"UXaHF24EznuZ":738135380413,"Nl8Za9Kww1Ti":570795643600,"12VOYBYz9oZU":975171756426,"lVUkvfc9lw0A":516271709909,"bKKYOLeKrfvS":779246422764,"cdnnomSOgTm4":663753668194,"ZWPupDCe7YM1":911012972412,"vc3xRknRvOq9":268059205514,"vDKr7SdcwKlm":792314010901,"J2sL36n3BfvN":580608702036,"nCDOk4TLKA1z":234322294489,"9qrmDrGY7f9M":268590352948,"NZxBuKz0PwMl":351616045284,"8vFtWNY6MQC7":490775772225,"4ukMlV2TxL9x":729714202916,"44d4AqygwbX2":711619578954,"xpg4D6dQyt7d":359648086542,"tUlnG1ZcLYzA":883131724192,"S4eei2AKd7AR":53671378034,"U7sVeow4TCdu":393410519472,"rablWySYcifz":908784300222,"MeJTnw9Zxwl9":60865171315,"myvmX98uElxZ":746194571138,"wfxocN5YlXDW":990037871326,"GVp7NQhxcFGR":635376839817,"6v52xtBKkHsT":404835200261,"CCflAk7y6E6s":560830009581,"88WyIvnQF5RT":613326739279,"QvPrX90bJlz1":985049900916,"IGAaIz9ibD78":659333458997,"wdGB8MayIWcV":25796552582,"nmGsdrFLjV7S":795726252025,"sbpud2PZKqWl":45374141415,"wGdjLv7GSvQ3":108474531382,"CFeXeO2AEUT1":246376146566,"nQLlIoHdpU1d":74365453744,"M4tfDa0n4Us3":444290826330,"KFQKwEtfhIkD":957310407934,"lSvX9Fd1p3wy":909202284455,"7AKmjDFtkRuj":795758645241,"Mu7eYJccnVZx":702450041751,"1jNP1O2YCje8":36202423587,"aJeY1kdCn7fZ":173709075849,"rHnG7Etfx4xa":773008448527,"23Rg0lvu27pi":90304712231,"Dzty2iJqPo4M":217895925276,"0wVKyvbPm03Z":986742664350,"TjZHXzUA9Rux":817288963258,"Gzr6AVGBB317":200644522562,"YPnMj6sOYBAB":61096917552,"2SYD31lbVcXF":788269588762,"zKMXANft1NMt":827265342733,"92SzegJiTMcj":439547957205,"ZboAH4iTXBLz":38988683633,"soyOdLRExmCX":257557468342,"clvlRh0EhEot":42279108005,"a9KeSew2vQDF":473386096224,"TgSVWtD8UBoi":170462191378,"kqZ1tAkJeAJC":675600749344,"1r1a2qAww6F1":136752564265,"J3mRcDeYfYD3":919580178156,"VwlQNSq1gbR7":940203701074,"K0MTKlwu5KV6":505628191211,"C7XiayYgeFIw":175856617344,"yvTnKlwGlcu8":642788799916,"cBlEbKyE4Big":671143131336,"zV5nuiZqCJ8R":41492415342,"SZH6QX2Y2o9S":145792235130,"v7mVeziXEN98":320085754428,"GJ4ESp7ahC2R":514414395036,"5aKRSyboS0mt":432940952616,"5d9b51LG84lm":854717710619,"AKyq9o99nRTO":422142304937,"RON4BKpLhqPs":63646464569,"T2ZNI7WJnPtP":215963509454,"eDzRZhELodpI":362914921456,"JZjOspRR1QPb":357145400053,"7tgTG19iZ8dG":322747184073,"NjiPnGrUguXn":694633536683,"FTLaA4obqcmu":309992716923,"PslzO2I8NoyN":302759380357,"jXgiFUw28h30":315344426978,"gghVLd9VPjvt":960939219709,"rOYqHA1yI6WK":163093015566,"PoyylxpDgcR5":534268908652,"p4eN7q2jW5m6":164308825788,"I7VgsEQst09h":196110966702,"mqib6OmsPAx9":375133455424,"Tpls5lE6cx67":508173341602,"EtNb64Oo2NHr":992231492944,"7pnGRZPiIIW0":83383211196,"xUqDj1z6bH0S":751163981460,"NGlLnS0LYxQH":570365480814,"onyHsmtYFLq8":417708033811,"xaeRuRgB3AwC":300471979793,"K5euWUOxCdij":540778492680,"wpOYCp3Ld2TE":395170555453,"pM0T71ANdYRn":680361606146,"Ux7601LPV46F":423135418824,"HIYu3JIGXNLl":940447991454,"UPGEeWsHdsm9":66322888412,"gUPBPDNsprr8":278858203984,"nlGiDQtkVUDV":728237062333,"MeJlr5buxVtk":616174980284,"gmiE5sow4G4z":291093978630,"gyjEqoPYgZZR":910238485704,"Fc7q1rIYVwEm":934042489350,"htzVqKKkjta5":977385460062,"k4nmuh6yGkxl":317408443511,"L5UBvSONia5s":330994996023,"BLjLjiFk4tRN":485490958826,"gq754VesyC7a":296759585815,"o10AKI5NQLn9":680500169728,"e2SiptY43EZQ":870936957544,"gcwVjaGJTbec":699623395242,"IPhIh3d8InsB":433387690885,"SscnIBFhZTZB":265612610632,"H2qw4U3oPv5D":959004612844,"lXj6BdNZfh4T":695278024138,"rZwcHoAxpKvY":360522889334,"c6xNjXuAzW9C":194218871348,"HIK0dtzrrRhQ":637291241293,"CKzS8fS2sEVt":452416999826,"AM0k40xiVPE4":687399622710,"0ZpMaWvkL9mo":171672508443,"TchI5c7RVtec":934456887722,"sS0kuireRoRZ":740504582107,"jm6Rj5vJ0GcC":123713049215,"1ZmlNvrl4VlT":277483556088,"keyzBFd4jmWi":882826607117,"FsGesH60o3SH":277982410935,"QATsrhCDAhZG":621007146671,"IJLxqe9dsE38":580823557027,"ApfgDKSlkOdZ":8516184591,"eXkaJJFmUuUz":72058889927,"A2NfbLBWbwcS":394236033916,"KYH8fAIKicTN":431549479291,"AF13oewRekMF":669669193514,"zkmjroEAnwjc":881751616836,"U4dGmoQPqNQP":931595300310,"9cU1MiSEhatc":196926370800,"pUHssyXQ2XAH":560497408630,"l29RbSvGzcX2":810626140671,"4lI5d36nffcr":541652247444,"taAWvZrHOm5a":319226416929,"2URLiMR5hDXQ":306141214801,"vYNNpdQPA4iW":904427675156,"rrzrquogEY8a":810535026970,"wqqv9tKmRuqp":20197868946,"1GNBhuyXM0LZ":110112462907,"hJdfp0l83GVn":511098242672,"N5hdlJ4W3WV2":877382869949,"rz6hsS5ifhAp":342917621806,"JprOAl8Xf5kK":231932493714,"aPI52SAnmYAP":108132486866,"ZE2J6stX1Upp":766548281076,"oiMchglt7MQv":212273490667,"2bj60nhH41WD":758760007318,"KF09VJ2zmH7x":684050379969,"kdpNVaga8sAU":568828549089,"iQzF6ttSWOPC":667070020229,"qMkifXrbj8Iw":234089038515,"gJtJIh2YevJN":498451095108,"jgd6eOfv9sCj":523858082529,"4Z96ncoJyife":849535040145,"S9AScu8uIz5U":43446322378,"vgutDKMyNoLk":80081870043,"yDqEKclNSLmq":73637381352,"a3kihYACce7J":65552744921,"QLoUnAWwsJ1U":488484044609,"wwGcRDyiN5sb":893433812870,"qi1WwXQKX2TX":370811353042,"wDIpvBIjebYu":740645890954,"4HR4yC9EObQx":911595945268,"6WravH1KPzoI":78513739116,"NZUGtDb8IOmW":979109764280,"7gxzR79t96Zp":33262287167,"aeoIYTrHLCG7":734721518282,"8wiyXzxbRf1H":47461354012,"3dCbyvmgdnyx":766729969345,"QUzlV1u8U2Kt":778132542063,"tgdt6oIOK1iO":680341539052,"z6EfJ2ZY3Mw5":698955691127,"PDGD8AMtfSEC":466948519725,"VF1bytpXA4os":741677721322,"LSI4Z9tmn3Nq":680346621521,"uEHzpBRSu4Gr":424115929010,"iQEcGpcfavGR":101998141486,"rDzX76Izptof":212774210284,"iKQeQRHfe0iB":285647403521,"PDndgFs6V9EC":611775995417,"VZ7ZtyKDa5r7":592237771905,"hqZKR4rnrMR0":438971680272,"uS6yxPEQQmNa":32312927049,"Vpg2o3wCHh3s":809654198924,"xkZiEgeNKggK":926087248118,"3PNwSuflmESW":228572227568,"A6uGCGBoh9pm":201752285517,"goWEyyVyu3oA":344839258520,"OQOYKwFfucCV":949516360612,"loUCqchELIQc":848976638293,"wevW41Se8oJ2":957784771807,"4wywTSbKlgiL":1991842274,"Y4pNu8lUAQVT":750855442680,"vu2kHr0OEDuc":602600835237,"LsowL0Ikd6C4":542422764081,"PZ5oggKPmQ5d":183278952441,"lqfvjmm1ycry":389941348545,"7JOZPgWZP0gO":191284049771,"9woyuvR8EbjW":924260198075,"UfgqkLREsYQM":260151827610,"kNE2rKhsbIWm":552244728550,"22QUyLKScZKF":181682147413,"pYlih9It0x6a":425360073944,"7lDBia7koF3l":820736204838,"nXdsJcnEDV62":166827706637,"psCbXKP8HmW1":428750699425,"JZTUm9pzpJyJ":888267414719,"cKINLe4CzMHs":208041533699,"NlTWVQkWPreG":779035610002,"JFAwTbZxVvIY":654668008362,"nPge3z1ghN5b":280148424674,"C7tOdP7YTOoT":121830801691,"txCD8YykeShS":912719341281,"JouxrwHxg8TF":278713589483,"aQU3YxogQ35M":731644981026,"yKImPhQoSuGJ":571109285570,"Y6xJPrKOjjMD":377856146411,"NjIeb3EenXV5":673761474867,"5pDU266v1ZJP":490017224897,"geSxVjYaLTPw":979697702172,"qYMiyHjIZScV":613014577881,"ubZIeE10muHl":729508273945,"olqv6njCK4QD":94673287944,"5YgZpZ3zc1yO":797714015462,"TNBSdrvxjQnA":94769190823,"ebsQk46MkeoY":227054401475,"LzT9UoSlWy2x":44001015876,"IfCMyM9FvNTj":474636493410,"DryIYLoAYNXy":18488338856,"ORrpZp3jBBdM":752382984295,"0S1pV2zOSIII":577618838512,"9MsNthegrcTw":531321088465,"H2R6gajuvzg8":143096409786,"1P5sa9PB6ouK":85174783060,"9RkmDJtbR2sC":892678481005,"maazewjWLAIG":977527165520,"Oog34etztjPJ":485411543199,"BNKix4AJKfgv":228817389405,"Ev3jAVNDRhiq":259037411275,"C2BKVefEaauR":691026837571,"HueUSnTEqFnC":15844390613,"4zJu25QBsXW4":448387394767,"M79ZOvQo7GXw":213682493686,"1T88KIxK6BMS":367481121123,"UzLO5KfxBbIk":682661068706,"5Y5SkVGNgWkn":704003903561,"404aHhFsA2FG":694794889696,"FdzFiRZpXkqu":845475383837,"BVcmwAbiFe93":88186618896,"0TmvxBpuhGgN":884121976617,"9ALnDtNZwvrb":526009700665,"HzSDOO9SmzU9":729617096790,"0NJnCsC996Ky":677997905198,"Ssf3UrRDBFfH":155831879296,"B90Y2omS1XWZ":253753754790,"pXdsiLh9zVeh":320379571332,"L8LNWYdGxB5J":468640927748,"xkWkM4w7QWKb":287630908359,"NFgsry8s80r9":324004184922,"WftkKdcNiDzS":344187719437,"MAqc4WRpZO9R":602114766971,"YhehB4uHHJra":555850237710,"FV2SjWmcxXBF":611955095495,"Jo2npFS0VeBl":142919172960,"3R5sY0VkAmMg":785736665433,"iNqoxVZt3GQB":512174771710,"OZSDOVMUnpRB":960328334376,"o5nlgiCnSuB0":334796907406,"OUbtfnCepX8m":270276400300,"X0Wc4wXoK0tw":234718511912,"JVUz9ZPBpHZd":313028604920,"Jt7xLcjWM9IP":814157986636,"7VN6XXjU5tfB":576071966288,"7lQ3amPZ4twE":945512899856,"hqVh4cK4pOI0":121656905551,"Lc73o0iyBK2E":399010912390,"Rr5zj2bgLGC4":665104665334,"TQlQV0ykmVcR":642638212938,"ZLejMFaXem0H":943998189433,"25wPOG8PvUMF":227336204401,"QpVGQWxXZhPJ":982231654238,"aMMXedzAPL1r":638140043420,"TgWu86NtRloh":888199912483,"4cTIBvWTGGRo":446933542881,"bSJqmEFzLQuE":674050514630,"7vJynguxVTYp":472720967950,"fSuppC17gblB":35742179742,"9R2xrlayRfJs":666039138514,"r6HNEJv1WWpC":219958827399,"VzFhrKsMQDzZ":743385977053,"Ld4ryNEbcay6":982132915897,"FY7h4BagO0h4":800764157684,"i8YRzpRMG3JV":772026391711,"cNxd001baRXj":850082597077,"y7yPE3VuKlQt":87894561396,"L2v2WFlgFZRO":203126985116,"yo6gGZ7K1ltI":353285903028,"kjlRKLptx2et":986971075231,"kS9Dlf1hDIAY":239260501251,"9FnrVxyspjcJ":255539935775,"V0oVmHSOZ59z":216277950571,"brIJNxfEy1nu":933632239715,"T0rbJ6FZPPou":468413805466,"sLeh0IHeccmz":789968341212,"jiMC4T01T1Tx":203050186196,"TZKg4Mao6W3t":797225486958,"O9BFcudGatWn":781316686974,"JfE2vWAr7XwY":126287872229,"oSgP1GTlziIX":596871619255,"8bOoilKn0SqK":418442254214,"xDRN6jWKeFqh":152414586404,"LZp9tm0PhBcV":690929225824,"KhVIuDMYn60k":902136467024,"P8JxlpWEGXoN":670868680297,"vUQYoubCWzlt":762892762754,"O0IaYU1j5kGi":496977018703,"N0zVmmAcXXMt":159228877620,"S0cF7dSr2o67":611931851489,"4xvj4qQJPYO6":106380447755,"TQwaQzBJBlj3":732613109601,"gNQ9KltUyxwP":26737430346,"fpVMgWFBAAYQ":441779183613,"s7ojmEwLybjP":77720239377,"BrBfKUyYnYLl":415406029160,"KdCxyuhRz4jn":595501190529,"GwrquHt5L01O":7799105967,"6TlhGNIqLAhr":21840111330,"VcioWrTQ4DRb":894770934707,"MV4mnF0d39jz":676026737502,"t6G4CI0USPnb":303232886295,"a1cSShFIgoPi":791361182714,"4OIQRwETvGai":459625508927,"J9kbvLCTp1VL":208423143272,"IGSsE45cqDkE":636855076040,"sbCZ6qEsvE7L":150472068256,"mOcvciBR91KE":168919514911,"RCllIrdy5M4z":529746604429,"cBPmfiCT6Buv":826325537381,"Cw3FFfecB6op":442267247433,"7hnSLSa0PCsf":306934468438,"yqD8rrCiVfV4":641657858651,"J7NfZ9QVufLP":312075947440,"G2qdJIs6v4cF":5916852926,"61AZoZsnWXA9":235752264883,"z6pIvirWVow9":69618479545,"elxMHY868eRN":510664444359,"UEZlbO6osuM7":977032865373,"XynAjDU5Ct3z":402465259410,"s8xzqTDwujjO":233106382133,"pIop8Es9wgSn":189819781473,"C23fng0jf6r8":389430963879,"3UIyoPiUsI1K":985370068153,"KOS0afJYemjv":496762785776,"M5Tdw769lHnF":160893177664,"Eh5ffEEa9FqV":550307143449,"DHsA6ijDY1oR":795619378344,"FTQpY8mkY4um":981342943720,"twHpkgtJN6iZ":123642178125,"Kh0m3Cg6HlCl":288007216865,"SbcpjRkeMHPf":135545216323,"iJDg5SVnFvUA":178154744879,"mtAjJGlSnsYj":4920279923,"dVHtIEIQCEk4":754726171890,"jp62LIF1ipZv":231545439425,"ict5o6juT78d":678245915025,"suancs1GpG4S":489866904522,"ZXyKl1dzmI8V":336858999135,"MTcxDa8i2Csu":737645972757,"McIsxKC7372T":218794132739,"CaHYatKyw4OQ":587916639283,"94jQ9IvLsobF":777441716363,"BjBiIdwnIDH4":133137284029,"bqgkCjFoHqMd":188348963480,"mezxTTibFoPA":525771675351,"Hyz4ooQqcdOr":72396121695,"rQTCL5XFvhrs":626829818539,"swJxK7Hzdg8o":195156966043,"YKfo0JAQEZQr":294135811394,"EPkZEwkiwXTE":174079242250,"iCZpthG3Zaua":911789909369,"tXwzTLmOCXh5":90036611776,"EALE5hnw3rA3":865508557438,"GyVCKtbGkE1c":391262895811,"c9NLFsOR5oEc":379935616072,"m3BD9tgB5Nxp":225740135240,"VAyQBWw6Mxop":938858771459,"yGa5KVpaCnUG":816850284170,"N1j3paSX9v8T":16458706751,"G84i1cY4hYQU":500614139597,"EQzGmnrfCUXV":342295998268,"095WsohF4Nse":506114321337,"hCjr6DHS5TNN":78736400174,"8kmQ1hjUCQNa":423771025120,"qtqf3Ff0U9wM":793565839920,"T1eaEMJLhEV1":778905960348,"CVYGwYmMzctl":209038774939,"U8D84hQhv2Hf":301781539003,"qdXziqhH6u58":397894442534,"w4u7WqXdW5qQ":539493469870,"JyoL9qLcXhpe":783726771898,"teIKmMUtpXAr":646216218655,"8L0tXcq76E0S":965987813540,"hg2KUQOHaT43":424163693816,"6S1YUlBrHiLa":895724893532,"Kf61XCbEItES":446484537713,"YmMQMPKTdsnt":812954599489,"jqCakSUapcxH":748000372965,"Qe8AU0V1Dwxy":713583329447,"gbAbNL2sUz1q":156426099934,"6A1Jue9BfZxC":226355468247,"CFsfiyk5oVI6":497008959775,"03sDb1XbVW9v":483294532851,"ajnA5jrgnOAY":818729673930,"p2zJHpLdFQGm":627544191454,"9P2Fst0O6GtV":335325040029,"GpsjXFmQDtOF":694427563052,"Puh9v6NVauHq":36673722611,"PSNmPQPbCGRA":512248020130,"KuciOb6MJQpp":210118803450,"YfXPX36vdn9z":948435641361,"bAyAqWQWTLf0":707872875596,"yf7Z0e8GNLwk":834004705959,"N65Z0p89UhDo":678400854376,"tK5kUBTYFlHR":402486745342,"ElNuRQGZoPFx":749158767399,"55CnddrIwLzS":459727908914,"eurL08LXkZfR":194955365039,"RaCTLxIBIK2x":84928208366,"Y3ftH8MMlRoQ":61216508323,"hfdaYNHUxVpa":60002942898,"EXGsid7xMyPZ":412911375639,"ZQAGrEHUzLlA":477579484942,"wjcxxe2gVxLP":208116471957,"Gj6ER9fUQ8sy":240429244400,"8DNEbwPvIF91":493261781322,"wOKMNUSFyl04":40682542321,"8laGWjYLIuIF":713976167675,"L8ScOxJFsQMe":165962520656,"My6mvZJt4zWe":820079318041,"W8S19CBwoI5w":784664338136,"gn9H9bKRnz2U":495433306190,"FIAOfAimqppy":214777329342,"mttpmp3b45jf":487913194791,"eRTZvyyrL0J7":727340233854,"j2iWlt1tOC8n":501301393302,"qbROpZydbJou":601280835829,"rNw159uzMO7F":455832013709,"9B7NPbppxCqe":601810560193,"bvkbb7bjnXYT":885038524257,"Y5Ea1ZfadWZi":320200958021,"14UnZofOwuZU":209408821261,"K2Mt6NZhGhxe":760255250771,"Oogi5Fayu39s":342872835330,"udmzVcsQ98Eu":751015675026,"9ZMsjNoudCzt":224490503310,"KVgwTzJSmEni":995511564938,"p8PsV5WQJH39":281338404335,"6fVB1dRFvxZg":125893109147,"5TWqmuLRHgeR":409675018941,"TVswYtKSPqsK":257022646876,"CNaL8Ob5lOLu":535137536717,"pArj2ZDHz9c1":154500617485,"ofG2b9HLxFMy":318169144564,"kGIBl5DJebxe":685481267945,"f8TEmtJUSgiz":412146638339,"lncdSWTPsf1P":426512892560,"1mfJPDPDPn0e":528052812710,"CTRug3TsOwli":506716819380,"zHmM4paQkvDM":538035169808,"3NCUqJA4tP0q":192001975116,"iJyilppUo88d":953487521639,"O1A5xbkmEh0S":585361494456,"m4LQ0GZFWi6P":642596896432,"4CfDgvJpeo21":369192644332,"GduvskbrNQJH":839578585560,"BjEJdgzeDqQS":622778992041,"0MBZxp1dU4J0":978057840015,"riYluomUioDh":851317283005,"7SZ9BkYIenh9":776999683558,"qEMjkXhFbioY":629488645008,"JmyS3Fnt1nqP":544057595122,"lj2ZdCRAMtaN":991825961591,"AnhrlG5dTFTH":288993784062,"uXTGZp5vY2Qr":778391329860,"BtaYHhctvPjb":820840079235,"7ieAB0RoESHw":672189273085,"gzCSEjO1OmeB":776297061844,"S9Y3ruTvynwT":532793058452,"Je6yR84mxRHx":208506895094,"sGFfPI2vhyGh":996795052407,"p8Rv2bIhLsdB":28413858591,"IYSpcZi19ArV":5031239371,"dbCAOgcAB7W8":695127339194,"lIWrUpQWMaae":159625423067,"rKufWbkpxR5t":625015356328,"wvjLsFCRmysN":757934531777,"RdfM6PF3hoTy":895608693727,"lhSZ9zu8Pzwe":597352439352,"rNktnh9rjSK0":792030378353,"aWS2HdVWB34Q":296876997741,"IRgpfjWuwjmH":978322363537,"AyQjFMAFOjnE":20994179195,"TjyiAp0WFFwh":621936009213,"NS9Q3Un5pjyt":276892718103,"Ix7scLXoVQKn":197199962416,"bmo1ywifHylk":938754093373,"XPecy1AsJav4":963785706045,"nz0hTYwVrx3k":574434711156,"d3ksmso0uJJp":561375225428,"1Y3ucQcUs19c":420669441460,"FH9DqdDkzKdK":891594128149,"oQ2Zwsgc92vX":155609354204,"mI5dU0N32z1X":150742608218,"KOAeaikOQkkY":769959343442,"qAgivl5dzMOH":768384732831,"73naiQTcxjf0":73299530697,"6IlYX4InOmyh":437656086008,"ZzTGuhCrGkxV":302334249263,"OgpyC47csH6E":335453799431,"vuUfk4QoEU92":809702656263,"5dFwtgvjAvwi":858988104088,"USi1MyShUbLk":271977638611,"JWKP5kEAVS1N":606693021598,"milYXRFZ0rkV":79318024489,"AWUCxesQoX4z":757226457485,"m79Zrq1SjZJ8":575192280682,"HV3veeVL82S9":201919992817,"0JhlfysGCVjy":677639306071,"beAOAAbQu1g1":115278741938,"3r3kgMrbLaJI":8901054400,"J8mwK35yQzlf":157354613062,"IsBI7leqtJML":900922504194,"Taeqs9UJmyiq":524342688947,"NWPVxs7eKTDh":261762465105,"bZJSpHHXfbqo":764588734654,"PqUYwfbq2Lrt":281466485153,"HM6UqV6oGgIQ":870409577447,"Ud5hNEEfYttp":36293101589,"IFcv10n8A2Wc":564381717066,"wO6XQw0ckvL9":736409119576,"sS2mCmmGKMdy":778798329342,"KzrNQmjBNdqZ":317065278523,"PEGsXeff03mU":463235002280,"LOeV3sC0ykIj":833995386636,"GMw58P8CM2K1":470015179587,"IB7hnaJihnYa":976392605770,"9DOHGsMxMpx8":458958868436,"68gbJcs0RTWk":6911081595,"cnTA70CORhVy":329904493969,"sbI6pL7KkkNx":623306545755,"Zm57RnYOqkKS":9697989489,"a2XKkCQz47Bj":38363535745,"NybTdpVhjpZO":815217321055,"Ty6fmLCGHmhI":820350956802,"mZ3IlQ6tL9PO":416864389498,"YnkEATLTnNE0":774106432438,"lfeocS8DS9PJ":814131570282,"iQrxtWqAS68a":250477711263,"2EwYZufXgIbg":367308004433,"Bxu89wRE0VEm":539028914382,"3GEnriZFdzkq":793079398757,"Gp32ZDCRMOQw":778745295750,"juzPEA8fYaei":887081236237,"gSxfHrXO6weV":244317459884,"QDdUtVGth7jX":401344366494,"yB2ICOmfTOiE":711095775746,"iqWJTe6bHl2R":833561902903,"dxr5rIFWtlsv":220165562065,"gqxLR2rW3P4Q":622140705433,"3pdRRwtnZHmk":925686646820,"0wN9QxF3cS5u":311956177829,"ARGWVtWjcdWf":995102090741,"7eq9H2wKsxbN":100685402992,"DTBzbfMlUSxY":516553492297,"kqDdcB1CtzBu":361030606627,"scmcg2r0wnq9":259254876949,"BH3Fr3d1gspL":530710700499,"YWQKr9OGeQf3":463792588754,"ViyfxOJL96Tp":922372908718,"lMYaFvfIA8HF":541941311640,"UZqLmjazEFl4":387156972273,"zOHAfoahRWRF":199383721973,"AIztSG8boszr":40229775062,"0Uk40tQtkyLq":964032976647,"ie4zbOS4MOkc":654546146436,"mEB2YzqZnLMy":122200575161,"fVIehBvujBxI":701753267464,"L7l1jiVO57la":630272531033,"mXFZpCpH79IU":920137244525,"iz53wskDLWPt":769854796323,"6fsuTzJHQtXX":598617203019,"JYDTr2iCPLMt":101823083558,"o0yNU9uaID9n":291197606534,"yadngs61Qfbg":972259258221,"XA6KpVwEbO4w":567468489295,"pAN6fWl6zMtx":342555050194,"hHCiJMLzwfU9":580233354568,"ElYm2T4jcE1M":904242834503,"RCwuyFSSKhWu":113237313194,"y1jcMrtNQDLf":292204854659,"NhFnZ4gBaJYq":235384421105,"VuvsXRwu340v":619915536404,"fBiOBOKWqaXw":737647217448,"hJhMaMm0G5KF":780122043334,"ZEJi0mNqn3o2":426460899734,"VZS1Xorv18TO":663574101392,"QFLrRtS5QPeY":961117477318,"8Lib8ql0KkiA":811907843591,"n1YE1SW1mdnk":234751670266,"0DKWzAtSomRa":402978899665,"HaDU8h2LUuYE":912944466742,"czJltPM9K3lr":863591197754,"oFBienb2WXdo":43943903635,"vYUmaUEZa0mJ":546067294068,"xuy8hzr7eAzq":587650544503,"xQQogpOakxll":441833272284,"TShl7sESmwzz":362627794138,"nMdLh1l9xpP6":249951787956,"2JGVH7cTFJzl":928691874447,"dwJooRLIBxQh":481598076316,"H55sRrEyoKeb":994090848633,"2bqkfmUipSnV":593214338367,"yIImpPXXejNU":116098219388,"vjIeQ5tggzHL":243617351162,"LNSG2qR98izT":96849021802,"jWdwjr2dyzLM":214254765026,"KfbngrDNe7kJ":802499718432,"plkwlf3E3NFB":147845823907,"5nlvOjTz6OKC":543805647458,"2DyghpI1r8fD":804754600425,"MRR0qbFKtTXu":509618614311,"J4hHNOMRs9U9":930437748335,"yM41DfHcY2Zi":498541271398,"ReoJVWPLigsQ":824015604529,"dEZ9MbRLlEzJ":128959240109,"krcy6EQMcxoW":458101114541,"5e6CiX5NJlli":10609608901,"xEwzlg7oGPzr":731176016880,"DHi7wE7y1msd":736899525678,"ant3uXkjVaYI":674964211646,"62undbHXtbZn":664639478688,"UhJp8X0ko5zZ":622146355676,"wWJwdtQMr0uT":823306300135,"NV31H9Y2mn7L":617300625025,"VPxls7P3Oo4Q":192863245706,"sJcv5lPTSbiv":162698350920,"u8j7lJGWmIeR":420533295392,"g3ynHUuYFeeJ":909451206345,"HWrHEwFAjPqk":943819976058,"kZ5NWoWZaTXh":887159793887,"vW7iJWCYHE74":745616284148,"c9aIs85ys8YI":822057855858,"zTGyKsAet3yI":190563326673,"I8YtCiWUWBvR":158926458827,"FiGBWhtclpHX":793731461324,"sizuJy56MHAF":513584722061,"R1o4sG9Zvds9":982135693154,"kyORBFRojySu":445172907435,"YkLcueDMYDEl":112487606809,"UhewD8FeYtUd":735590062404,"21mcW68KXCx4":674104548544,"DFErqDfbY62k":607826260042,"fS7QKWatkaFb":183371820914,"Oi9hZqhoy5nL":707905372250,"zNDyrcGqASAg":163182938250,"OOfR7s3XTQUv":756635546231,"0hqjrUOQVJ5H":641236428625,"x8j0dhZFntTg":373934713734,"Vqr9tx1EzQro":534954954517,"xvUoDYHZbSDb":254078100683,"0uxqJdKzVaNa":355486230621,"4ZNzhhwzDxKm":716919499001,"X8DCzoICTz7H":803390082743,"dEvp7mveeloB":503715616740,"ewdTr2qzruHw":41538933855,"jUlsS0Mtc5C8":395788000661,"kCA47Vf2C2GJ":257765073445,"pmtiT6sAM5sk":445353714108,"R9bIQDcVCcgV":961365770078,"pO9ucZYSuolv":122951142608,"5G596SKgew8o":529927824424,"mzHBk1G6Gmd5":793034825905,"lWIjBGga8aiO":530214062141,"UltLZXPDGHD6":739063778938,"MqyC8ykj9pd6":664473322947,"6psJseSKUqAG":369100729632,"jot33SQK6aAg":461678216702,"GN45KgB4UlPf":967550767748,"bZQTP8JrqNVn":727162547119,"1FYtwQbrdr9i":205511890942,"a5V6RvJqaU4j":544316045849,"OwRa0gugRC6S":561217072515,"Vnz5KuUkZjJT":321588804391,"qD9wbTVHF2oY":945153643247,"ZiKHAzDvqIAg":851806864629,"n2Z5tibHzTBd":367228316641,"baQ8OTGJJ4Qc":516747368905,"im3hZlYdq8Rz":357161317059,"w27Oi15jhMSt":745106154430,"wfarlfBE5VMh":200634077119,"X5AS0CisjVuw":409836105150,"yACW41DPFGAE":766590695131,"n0HiX8aTKh4x":62157873522,"0sTc1oGSicc4":668900047479,"XeETscjmkmrj":577385126007,"9hzn9lqawqOt":594878982261,"hJfg8oqC4rKx":729459677376,"qciJxGRDo1Xs":730797532352,"TEjnUok95YIH":891136438096,"XJGz5671xfZ1":549478035338,"m7CTT7WXb7ph":554917873811,"AJbe1EFG1VvW":652762662551,"UAfJVOmbTiXd":27298549310,"EsPseVdq6WzK":194983736154,"z34rauQNSye0":670277988566,"GEKJ5grUc3MC":452373347290,"5yKQ4BNjit2o":612196958492,"GZrXCiEumyBy":148579374151,"nB6OomU4S3Re":962699771696,"79aYHS11oGVY":21483661761,"1jJ4BIJy7gDQ":43981840981,"uct8TfmZqn2K":602101898715,"yRMtqBKUqnWT":47149540623,"PSIfO1RdHTpW":45595787482,"SAiPwILGRKxF":239190617704,"w7aurMxBtGwk":909232542398,"ziAi01ykdlpj":541213068162,"DFo7rCRHjjzX":970781484038,"cNkGhL6dAwms":58680823074,"B9iDUiiz5gDr":719644504945,"wnDwYtvLVWIR":617836725637,"SdUSd3fWPri4":413772267618,"4K3YU0RxEUP1":160879601098,"Hy9adUVW0Hko":163359432138,"tAM19SREwzet":969415573993,"OkghfYWcCKNk":435292831764,"tqqYFSUrYrwA":536954214627,"cSoeDDnIDpcS":318331078625,"paNe0AYHzwsJ":294728928565,"3RY1BbtNz1ZY":842636876916,"N8JEY1dJs79g":564138334097,"B6lTKZewwH0v":550412636131,"dYkeiO3lKkRJ":316204899645,"TkXe66C1WJ32":808069588990,"GbBY5nkEJFU7":931954666276,"O1iNgP6a4Hzf":941653298389,"cMUVCbRHzmzZ":101228323823,"iFY808sjYHzE":223944489376,"0uRsPgAbpd0n":797535407208,"3IfadQwqKZC3":712554931723,"0kBepwauQi3R":554638745294,"2Ax1t5rvpgg2":905742069562,"1UsSqo83uP4k":423965998417,"5bUKlyUuIWsD":400769913906,"v7niWoTmDYPN":753655314451,"gCkq6DgKwBLr":111166955661,"h4X4L8eLUBMW":114051242665,"1mSFN97diJja":461941330645,"IpDlGHFNN449":558281038435,"A40hAz1FleCz":590510471777,"GPXH32PXirdL":755725919895,"6h2AoguEYqum":393517852190,"Jh4MDceojlhd":444688440601,"N7Hif64n5JiN":582180529923,"uGvDOBQj5ib0":404610324080,"Qio6tX88jBCa":522041832715,"dP0QRWd18aoV":828523788838,"pAkEtmulz5xs":93564599935,"BaAr14XIS7AX":430424803161,"9VGo8G2fvESe":879840979086,"wxwxdUVTb5pw":963391943211,"Ifquse8WjxN7":424661198243,"IE8OFaaTkKsT":575147684137,"d18Ja1ZgPZv1":185719960869,"JnboO6LN33LQ":700379804443,"Satq58kmT0F8":17554102160,"4lG12T9JUV16":617246619099,"oWTIEShabWCh":686351429539,"zcDopmSTsFnj":829549209479,"GaEdjXFDh0yj":992758513037,"ClQ25VE3d76q":689229386681,"2WLayXb9n1mP":555307478435,"nWQP1DPCvzyZ":693972720014,"F5lOtbGNCzNS":394708300593,"bNYXCVy26fo1":154548708924,"EEgOlSE2Zd9L":4351641075,"9zmYbIGOSyxx":355191446492,"47Ysbxurjeqx":233621483681,"ggLmmKbbTcvF":662801895366,"OhfSvCuoYi5b":139114131905,"QVoQgHWQDn4W":917645465460,"Ii2pKYJXjmwJ":753907327269,"LMA79iObtDli":943633677258,"LHjucSaOWEBy":22006873888,"yHJCsILWi0kp":322857114744,"YfOZCuHsSc26":539516997226,"veN1bYTTXGjB":947445091969,"NJJJMSfwoPbM":462388024798,"87w0PMbqWzsa":96164664334,"zdoXptDXPDhG":406095498417,"o7JSJJqTOyt6":189398504829,"U93V9CNYiyfb":15819135231,"WQcKPZwdkeaY":315226413012,"Zfg3YAOtqldR":125550498217,"Kqvvx3Flyi6k":437715228680,"v4H9Ax3ThVBI":15425608392,"XtHh4UWVdKl8":501266412008,"dNJorCmnE6yq":377820979139,"DHc8f3jJ7JvJ":455942461050,"QjmoLDGGWNdw":758740323857,"hhu5Ga9BtwB6":341767506492,"UI61OMeLPWxz":239697021920,"gjsLQOxSQp0g":971775330724,"6LyGcHBOKh44":257518191203,"cHN8Clw9p9n7":486575483735,"4PTw7EDU8eOM":13147933679,"6bPYzsIc7nuV":856279337662,"MxsypXRMgMd5":345776233207,"2xOxCLMv5fqf":488089202438,"KXCl3nWusSXi":334763025990,"sE9mCF75m5V3":49502583400,"MDUKJphLQowN":512601407752,"k2aOEkHU2q5e":326446158795,"r2qAD1G6kbqf":952021554606,"HcyQWSkjBboQ":502508025461,"ux666CTG9Uqp":359899472068,"0GTCMXMFvJKF":917476456045,"Ja3aZeAHU9YY":440762684586,"ng8rlf7tIR8Q":65524578734,"q86CQ9qgoWfL":321563407032,"ypVgvVhaBpEM":927552240499,"EjnIQXhOyk7L":127028904909,"m7S4Iwx6tcnZ":233383013089,"runxQJ6hLR2f":562331478677,"ATV5czgHh7t6":481837807354,"CjXIUeAg44Qb":980864438125,"QDKwJqtGE474":59078487158,"Efrg0qkqAekq":926248815602,"pOsQipvB73FL":288262366828,"k7RhKYbIgz2d":371755087133,"s6mN7iK3uvKZ":518642887584,"Gr0layFR4B0j":684913843207,"TUJDKBKeTJjY":805016230030,"4ejSMtC9G5cv":375460515541,"lo1HVvZFKQEK":44586425991,"3cVIQiFyeZIM":883439989503,"TX9J4OTg3QsG":623336755236,"8cbLe1ZQqqX6":933670920342,"uXfh7UX18Xe9":824732739844,"MdCPkVQowigl":379996978155,"DYM3ejehsaev":711407935009,"zcayeh30pLP1":354870938512,"92gy9aNF93XL":535807734128,"0qH0gKdyMJhF":719320567972,"qevYAVCmxCUh":777085720783,"vUiAJ635TzQ5":499367844099,"lAfRbw2C2mCE":496911717693,"o4JzlVIfZO85":528167299153,"V60wjOaNXzt2":578311476069,"nt3w8Q9S59Ag":32161572762,"j6mPhwV2AApu":449950479920,"NHzFXO1dbACi":385213317630,"M1w0y9Yyp4Fm":380352265124,"PsTDZjw8GS3M":70651466127,"FYE89faQHJyb":67721410348,"votOeCUgwkIt":69304650148,"PbZ0lWE8GtwF":124686426156,"FSd5IXlMRnOW":992445331485,"jSTOLt24Hc19":371436385953,"22L4BaYuaBLb":578453944476,"EsURAsSc0FjA":894830348319,"EjfGAJ1lJdfz":951304305257,"xaJj1R7keZIE":643415921753,"AYHoIPhAuzfG":836489433593,"iOeAH3sRHvup":490374139037,"JRdXz0MwpMpK":27493820504,"PbnlCv3TNUh8":774011206563,"xXRiupfHyLpV":134381677450,"Bjy0GunMCtcA":964294739003,"svSB5doZkXQI":414227682322,"yOa61WP9rATt":992120377209,"C4Q9tODHkuKD":515777193443,"XjBKDvNYnEJR":743231955303,"fUihLpb8ROf4":270776907644,"ZS8k9bQZRoG8":357764486006,"YWfawtCjMGV8":597236143756,"lAtbJlx4XM2G":409241562957,"PiSAQkVgSmem":801229112213,"LIPSXJSBTG6h":674953250557,"lzaadha26SyT":435917882756,"gUZlT7m3eIHg":80527996507,"2WXDf8q9BuDm":456615937803,"nTQm7Qy79RBc":307011238298,"J9XtAMuywGQ6":899459457742,"JEyaFIRdWjrQ":337316329036,"Jpb4NGcYWIR8":530503882788,"DwSV6BtQKO1G":790374512799,"cMKW2tOfpDpJ":826435996395,"4Iy00eQILxTZ":22986724396,"YcySNTUEd66R":684050437491,"DyStqbuJxt39":374779508996,"bEg0GeBKOp1c":923978259995,"zL5jwcpJU9wY":509362804317,"GSomRVml6fju":642660055553,"lUO4uJdOiRhj":567886873860,"L82rgjq4kCLn":323197140333,"q62kRnQdoNO8":655451375092,"ZGV6s8CCWsen":666702408282,"beav5oDtaX1R":608466923211,"OK1qikLBQCU2":260769770902,"FBAULK8rhaXN":961474414441,"H6UuIzASqsHe":711801309927,"s0EzUcALrAbE":11755489286,"fsAjkg9oL8Mn":341151021449,"gfzkHQSn60tL":72401703433,"VzmnojGK9B6U":583468723153,"uRux7e1Bpu7n":699851723944,"wEykuakEU3YQ":2862086128,"p19kTFSN3m5i":457060543669,"xEthD948LjOV":982895650998,"chwy1B4Y4ycl":958580458615,"puanUPWA1o0n":757634101076,"Ef2Qc6R51TfG":761448810732,"4SXEin08VQy0":805021906691,"vrfZtebCQkS7":18894542217,"XrWai1WbLpv2":124973612161,"yv0Lr3n78bgu":897972224211,"VneiNBezPLVS":184495783741,"4GRdeDoqjBFp":697491521300,"fV5vANoJc3wt":112403500437,"16NmfLEsB6C9":329423224994,"nJaX6cMKFgwZ":756965953057,"vYJE2oypDG8U":115716069768,"2zfSUjaIRlME":521350383132,"4IJBotuc8rti":941284743753,"YtrQP4QRJAw6":936334183927,"BcNxpOap05Gr":560754020908,"ZX7HyA5P2mEA":673860332314,"amN2OoBnhTlT":689091845084,"BmLOut7sHoBI":807438804082,"va52ChNSeQfL":947089383925,"GHhoFKNnK18t":364987018204,"qk0Yu6wOFOi0":104231958656,"lliK8Bvueodr":632415855199,"R4PJlNHpuOBx":455989503922,"0wCzr96gVo2y":711098001430,"6tYqGitqiVBJ":487895784199,"EP1wAEUBxlxj":691376048373,"qGiUzMVUlHjn":309208467873,"WISthRvrbqrs":558802194078,"hYjGcUS95Tr3":942690575487,"ArYazselUpyV":191019472658,"Cxz45QPQ3GHq":240878426192,"zxpsZnMQ1kAs":76269137184,"0XGq1waLLtAA":719809260905,"okcuDwCYZzeF":715412341515,"bkXOF9Nu4ozJ":170884499414,"Nr4EoIFv70Hn":632662882435,"6ESkUB0zJsjT":577162648336,"mjmwm14DucG4":477703562032,"8MVK2TAjXIAe":423931285771,"JyJxDmpzICIy":999269491162,"VUoNxxy9Q0ZE":761381854916,"3QrfLvHWSuj8":808368426250,"etEAKiXT9o2i":936654097419,"4cpi1EsxTftC":374208098702,"u7BfnkIiE8rk":310833590678,"wVSCSaqJjoff":717554171515,"zEjD4LiJNsrI":489736117148,"5Its9T5W482R":909201260943,"4mvWHkjR7ciD":503201106916,"MQAvQANBQAtU":77181667773,"1LLytnyYaGSD":64164750454,"D9N1vuPUqtmY":14879044225,"1lML1ozacCGu":978274884814,"Wil2UfKA6OOJ":35196186951,"JW7OidqEkxZW":809129324970,"FZZb5s6Xke4r":678344925425,"uZLPTS9H6H0E":143704444965,"md1XSs9luJ6o":863898658199,"eqkp63BL9nDp":433269494167,"tR5QHtojU06i":925283352793,"gkSyiqKNGfaR":60082565441,"pF8wOebCyEaq":640474677820,"GzbmoIvwqtWc":815604156785,"RDT9U7HNhSjs":668632419318,"endY708KRbky":791618700814,"wABWa1RdCnYB":860939361378,"naapFFZ0T1df":941116273334,"rlxLefjRfnsV":554509392179,"0agIAJ2CwDKT":415108810852,"1LMKxHdQaNme":861929701539,"Vt2M00NmTqSu":781181231131,"IvCTjAet8Zod":692269748796,"FUTkvjWri78M":156068856952,"QsZZJy9LuZYN":328636623848,"fQeGUie6Jl1p":573811239074,"sDQmxiOKQuXT":55393518544,"W0CDKQSGyYAz":564408116252,"AzKe0pBooTSU":976718918643,"GNIEw55o2Jjm":504397808718,"4oZiPMBOn17L":249948802622,"REomNRNxUudW":104075176184,"d70zRvn8f18U":929722923991,"zeuXO9a4t7HG":584649013026,"Ru45Nofqy5NN":144425146624,"nJsD71YzvASB":681298599727,"8T3uwdp543ox":281577605153,"YSkmUlN30wmP":763146085532,"4RT9TTI4sBWH":463438588367,"y3KsEGUaY0Ww":762244609846,"r4SNp1IDKLs2":218551001235,"D3wVhcllO9pF":782833832528,"7DkQu0eZF72N":342705925692,"B0Igi3m7dUGJ":219566434423,"kwXmjBIgt5ZR":830427749820,"xkCCpqdvv65i":673046817286,"9Te3D2di38BQ":131997683179,"KIxO6odLlxcp":892390464418,"H6VJktYsU9rg":981440916261,"x1rwCPPldegc":743675819256,"BIaHcqnI0uQY":827326740837,"lSHjGG5xyl1U":666684602520,"3kRRo062pYom":461821103560,"UFxbQeufNBv5":149973260843,"d51HGSookaeU":854056358449,"BbAkdjyD3Z0B":605787746525,"TT2EklECechL":239765157733,"D8B2KoqL2bgP":95432914682,"qIb4LQPLUO8n":661933996145,"Ejm2mxN5CXc8":212314518862,"OOvk91tRoYDe":860179922665,"eTBH5aH39Xc1":950888643673,"XWhhKCqvXWCh":60344305949,"9OQ6IvULptW3":57607200658,"XbFwofSaiu5z":72621508385,"rTxKKXIjNXx4":436472732092,"NnmC8qJbDR2c":210140693875,"qURczACLBWbp":737960254726,"zU1j3zaIVNT3":195809169273,"q7opeoCMG0yC":562020387197,"bkhqYRjIZAan":648284329112,"QwABrr0IA0dG":334173383085,"AAhCgPC3gtVy":57912838359,"iRjLCxyCcj93":155117572515,"8R4vFGqplugd":27044169827,"0TwncYsGmI47":414751263635,"MtKtdf3RYYyI":808224483847,"db2WESJkSteG":994949254061,"sEpjSGIAOujh":397336926000,"cbROY6DmJYx4":712244353037,"HkQSo8ZKM77K":649923892506,"k7jrsuHGdpXA":89935000951,"vTD7b9tjuwrC":754742195978,"Q5Jpta6LCbzg":53060395403,"ftkbQ8aL52to":865669145683,"EDgq69huJJWR":403465138855,"vn4DuIwRv6k1":880245497648,"WjRuHaCjAmcV":867219086925,"wcdr8X6OTzYa":18569962278,"JFWv6JOWaxzy":790868390273,"sqlP2dlyfCYq":50147575670,"1dIZvDC51WBX":159282975343,"9lvbnQijAGIg":914887032029,"5chuEsSWaUke":278965771783,"Gh9neJVY09df":226715645962,"mYelJCYrYhzw":810311134419,"xdXojZkt9Ycw":859149521989,"kDpibT53wRFv":533476687904,"9FC07RguNaLz":885012211041,"KjN8skJe9IBH":5689145888,"xSLuOlWmgZVZ":63931839727,"Y8JW79xeDoHb":862159127189,"EHUFAUZX1PNH":973972248810,"aWq6ndJOGG0h":455070720535,"2WKUbBL7bE9E":70904944580,"LH23z4uWTr4L":982048545307,"GpADYkLvLjzR":6670817341,"DzyVFpi1Oj96":504680340531,"uxRFHCN9myjx":49044288999,"rwKrMmOJRcKQ":407661274554,"feB18tPpogR5":194193210897,"wnRwE991jK8g":107023971559,"gVlBNEAaTlPU":508222413159,"Bjgd0AXiurbx":125261873576,"2zODAt2gvaym":904524572174,"rjaT7jKJQvo2":967842865663,"0qElDsxrorod":822979912597,"sfmZpshWmq7Y":432636873814,"pYgrFWShh2my":382539530267,"BiUdTFRtYmCb":52142903782,"Dx0wXERqhaNF":887250095349,"f7CASzCk1kx9":42237881975,"gsShMHqCJeIe":388384384037,"vBD8QTHMlOXd":284297966263,"9CpMlR1di9XG":479673742705,"POz6NiqWzENm":214499611596,"GGxNhPJWug6Z":189277193973,"9zIi4ZqwFrSR":927874214502,"tUrr5UcEqnDk":308047030416,"qfd33T0uuDq5":273402328439,"3GOIk8QQrY2T":314355046621,"UOT0ZV7ZIBuJ":695313429273,"fewd3N4vDfNF":204264115376,"I6qTPu8Ny5r0":811419283042,"sDtbgvEbByvq":662444772230,"5fW1VKbbpZCo":381992008941,"3eBUPADm53V3":408818134260,"CFalDod8q5Ai":349590579165,"5Gts3yJ3Vo7x":463906938270,"XNF4FzGppHH8":412202850777,"31e7HFsWfijM":226593448252,"4JUqLgWJZhzb":439537102713,"sqft6R75en3z":979354991654,"OVSr3qnmndF3":786193401768,"mfGqluHDSv5n":239619891157,"Db2LY35lrY6l":526724682898,"hIhbiBYP73RY":767831752452,"FCciKxIZaMRj":693679022802,"zB8MoZspZn8G":291987634239,"3pt1R06Or65R":153950697571,"1b2DB5ChrSxt":425412950648,"GoH2LfHyss6E":876187842913,"oEkX0JUCwu0z":418875126245,"fkW2eFoYRHmV":376003719262,"2dX9e592uPw4":102683367889,"oWd3uKz5nA1c":480323935112,"DaTFuhlSfhvH":823405356680,"IkWIO2rN3MWE":481816400176,"pViXeU0tS2P4":942432199931,"dQ3OdpJ4O0cb":371501407994,"iGW57A71DXgS":196358213999,"5WCTCDu9otTP":874722077712,"YNOTqzShzWcJ":161714472788,"XWKaDKCyvIJ6":758238409713,"AEDsnXinwSwF":535473309226,"2WGgmKlWEkL7":844935757869,"v9qharMcvLkH":372744077774,"hpOPDwNMjnrG":912993562294,"nLMIuNvSwqPj":957940655632,"1vRP0JNVT0tw":829835343341,"RY3pq4OudgBm":182950858158,"I3uX80ShhR50":967279755093,"H3DfSNEY5fgq":786415374698,"Bkjy9yqYrf4f":17036049095,"j7SGjtZeJw8N":658587402633,"aij81LBk0PFt":633735017771,"o0JZQdMbc3di":7317350660,"e3LGwn3PeYVR":312918982335,"JIhgjOusTqSU":804029100943,"tMWC23omBvd1":210164501041,"jytG1teiCeno":468380107303,"C6mbUvqPw0nT":652927875958,"8m7d5yPy4SJj":668591625594,"EJLL2kjNszTG":246233314094,"j304gLbxGmaS":368297719387,"DNaeOEOwyi0M":232210117879,"raCoD9NjOtPX":316476436210,"4OECYZboOw9d":932914165540,"nw9DYlS1quH4":447993075506,"FDByAVbiI4i1":217026493743,"do4866ZkvVzf":930057605744,"oV05tfpO08LK":41814573683,"T5rf0wkfz5np":560297463587,"B5p0D5o2D3zO":350353666787,"i6fcsVXLL9Oi":816845634787,"JthM6NQoad5Z":901304869273,"ttORmGooc6OI":599963976966,"7suIqfaKPVkg":953232550240,"brlrxI0aIPaf":817634448012,"4cTE2bBK1J4k":898129493309,"tmKekBspDetY":223004901631,"k3ldNbQMSnPx":867460285223,"7EDoXn60Qqtj":129697156151,"AP32moksddSj":338080925053,"gJrraLNKM0bw":697260360218,"Cn0JvetuPywL":112937130638,"Pya9A9lNgVoo":603082905762,"UVxKylmRHDtI":602712020588,"Zy0ZjOpEdRpH":926882114262,"44XEdsOxEHkt":341067370501,"GnQqqRQf9wgM":166738961201,"pFm8yh1iAGmS":339830906475,"bZbxihgCdUXt":464846982036,"c87xIWSnBBMY":654504705668,"f6bsm53CBlXX":634375590046,"gheht0p4pVR5":666068035249,"1lHMILTu0QeX":298307536475,"2PuwlltHtwFL":775006621680,"TtDeOPoC6QOC":260557822461,"Nw37yQFv6LhL":438366198523,"fPDmCtcMkb4V":162966405188,"JTLcb0nyEndk":318325978635,"OMp2DgKHLRGl":933787918798,"StM9o8jJ3keg":535934091286,"h9hU93LgfoX5":575881504556,"YVpEgjZ2smw7":292199930491,"4bxA7Zrxdu1A":507624841675,"DNgF6OblzX8J":350590891126,"fEeN1r6a9Zuu":559223346732,"ood1ApyDT6Rk":789158031676,"SOXZIs9vhMj7":372064381642,"pVOE5gYjLe3Q":871629395338,"sLVpFlE4stN6":183692439862,"wzxvAy1sIsK1":166070503050,"K6VEhNh4xZ8F":922559888380,"8DCrvTATGFlP":648499302042,"x0Hl7cQcv94C":856727020832,"XMnZRw0BuEar":737865310957,"VdhsOzjqQtJk":462739628877,"Ccw6YLIHK54v":880845098613,"obWGdwjis3qG":148947328196,"M8lm7kZhJUJi":643645137037,"6ZHhDAhCohUX":370380182034,"6PcUDkO6dyH1":626331744219,"TSACa2jV2zyQ":519232897867,"u5VLrFJcYcYi":376393761162,"dwuqEqEAlG0n":314863184158,"RgMWENKNjTlE":522924999111,"hDa9dtHZDDYf":551518761313,"hl60sXCHmoM8":692235610529,"eMihP2rDUk8W":771209990672,"vDdJofJJgRGQ":294910852134,"QrQdB9drEqqG":856239928700,"PuumZdY3kyNz":754785026796,"wecYar8uXwaG":864638842009,"otNz7zveMoEb":228901441752,"UOIRbz3ZGXbn":257623950366,"IGx2masv9kgf":242708697858,"bR8JyLoAuVBd":445219229527,"zTtQBFY4Lr3i":613518121637,"QroInPsPuv6r":91196832654,"731zd1kIZSoR":496173317981,"riW74boDmQXl":220854109440,"eaaNhG4AfErN":32517146615,"VKD3OOLQI6Aa":552395661103,"8guIqB54HYLO":384087496585,"t6zyhhNnZ0zB":2835807872,"ckawHbXBo6zE":55939267636,"vNH3eyMXNjef":284059695244,"hpdLAQFBHX7h":496368256823,"AEAthc9lXTGT":916757178405,"9yzAsBKnMZ1W":467867813966,"aly2N2hpLmni":743854671449,"tQEecb1W3D9u":362877662080,"xQrO934fJW2F":753079405250,"JVGN2rPONGnF":826069019102,"YTudC8qU714L":880943348581,"S3Rfd4q3YlVs":368519188246,"jyPhFGWKqj9Q":417039103910,"bHV4FJvx1JIr":593766953399,"mxrd9WEBKip1":432763159906,"DqdNkPwFwLGF":830589603053,"VhTmQzvUIEl4":586193930670,"CqCrlvawcPo6":65038230558,"WPrZ4Te80LUj":906331691736,"sVHjwhTGztTi":940888098488,"zrMFwKLwf57G":703850764996,"O9v1tyVZJqZ7":550921534469,"kAa4fdRH9Fki":475890413815,"AQ96ltbruEw1":883571982174,"5YlQmfIQ02ub":892309910133,"mUaMKea19QRr":409644773851,"jbW1nlrcaDqQ":437050565067,"c4UXDb3BfJEk":921834498562,"TVS0dv0OOiue":411160135747,"isu07gWPiyi6":740651871401,"BBZ3Pgdvgayu":526458723852,"QuYCH36Cpeb7":368909593515,"pB0we0fvnsLn":410670108604,"PdoRHOr34zqu":481862948637,"sY5kvDsZ0Bbs":787653904317,"LA72NS0W62O8":371846244179,"aaFmSmOBX5AS":463912657380,"1uLiuJD5V4DN":189223991724,"Peb7CNRh1ngV":105667434743,"Jk2UMSVXXIOK":375975903787,"zj2LfsAX5Np7":677457832689,"Bqz1mKkvEQ9U":915696051941,"qSd06BDfm2rY":200641891715,"Qi0pxd7kAmYY":35803995623,"p0gQ4EuaUdlo":851908308082,"w9HkRBOKr5sG":777656393880,"Zc9PJpp2tYmR":672330324993,"3KGtSF5C32WV":330036663490,"xQpDerXEL0N8":758022181886,"D9oNjlkBf7SW":304098581112,"91YhyVSD9H4b":268176653766,"NAePdEbNBh3d":680306421467,"ix5DRUYk9dTe":67677566114,"61sC9X2C4PWZ":803115754729,"pwv8VA4pLF7C":2610580055,"aVnWrZs9Fysb":961720109975,"uT54XimOnD36":851923217752,"th1bZhIDrRKy":78296186822,"GiFr2NUnTlwq":161888622382,"IKcAa2vp0s50":494151287203,"NEv7VFsBD2DA":712621444530,"eRz4SHzJLYv5":348858014842,"eyUmLktLlemW":740586791921,"SLLr8hxotCjh":445270287784,"2smMMILNfzF2":864263393338,"Ooj6Ib9ILkLy":343323078412,"pAhzdyTxvC79":960332000504,"PBsUCG771pRt":924823089196,"l4OQkv1gD5HO":833766122595,"2YIJGVVZzhhp":180809862709,"DamsaaYQXdcN":69465377739,"wM6PgaoWVdDE":633600246596,"moP9KiqWntxy":729765704375,"0OIiqfqfQTQc":987358782195,"0Z8dkUi3QySS":200665578672,"vGcEGaHkQpnn":406287969851,"TDQtzrdPT0Wi":574576026085,"yrvBaPz0JPfN":970961493102,"vrkAV9AXy5g3":791683126103,"92tLTSaybzd7":973839848161,"NexwAPn3ySr2":787031677769,"0iZxIHqkoIzf":988158120764,"W51OBHaTCce6":991744288535,"zUYVBf1EW4eQ":903280185111,"3DvLTzbErEGo":169803232747,"JaQzcJjx3Vg6":452014171626,"RsIu3oP4qMlx":699656934982,"IpADJTNsOlib":846510639612,"mZOXHJkXAvpW":342334380928,"bifYicoYuT2Q":731852342610,"rouQNyw5IbIL":87954170537,"heDZRha7z58E":660462403084,"7NcaGDrHp9Ge":877725894550,"27TTNYO9uynU":173271794476,"TN99d7vk5f3s":244320284348,"8jB2d4Hy1aVY":130758054439,"191GQ4XSKU4j":723607331582,"89MpH7eB2Ei6":178349086869,"PFoASStIBrHJ":812463890791,"7FzjFVfTcbxC":572692479194,"kZu07LE9eOYu":572532835408,"yraUjhjsoyrG":550564544091,"6GAetq0EnExH":954933098917,"D0Atnk0m8b8F":623224596870,"sqe52wCRZ7Bq":605323577024,"cNqp6l7oAhAN":24665765046,"h4mb9Zce87My":540916401449,"SD6JPWKqUrGJ":675601179904,"HyGeQPjxogss":374824994299,"wwL554MKaOm2":40010344334,"SYNeGhuQug2u":828621148125,"8UpJEhKHaC7Z":263486380194,"EMgX4Rj35NcJ":804152430098,"jwsHQYtHSRJN":242790610434,"wJFDdMqA1qwR":957442947962,"1VRkhlsp16uD":139616704105,"ANM0HN3gkbsx":873956136050,"CHeE8XfYx8tl":401022748202,"3BsC3Y8yfLg7":908422086518,"p0CszIo2c7HJ":669650423149,"mfSFZ5dBijVm":618716216914,"JGjuBdMPav3p":645148427198,"k9DqYVe7nRMH":259518234854,"fc7zdgjbCpZX":782634021159,"O0rHNPE8oYbY":350107781780,"S9eCjZ2qQY7Z":531196782928,"cAHFTMNnsUuk":853858198689,"UPjqqKaGyKIQ":54947840314,"Xr6WcfuHTlX8":379259211499,"bCo4g3ZxV9Y7":853590948331,"FPOz0UKZrhgo":248857291917,"qYJeFdV6c9TU":981374858132,"HqZ5s4iz70B2":605613814012,"PvGRjJ6zLlSl":553039453849,"XeJDdnvODoH1":984840442430,"wqfH7vntxW3f":306272380312,"pcPXerJKbfnW":727117612746,"ylT6yHbVDLPc":727852114687,"xRUyLfdShFRB":350930196262,"OyONWpiiF4WZ":953944941959,"vaIOCLSVnMPh":300064472277,"c0ltfE2PJ3kO":681367409144,"KkCXxZ1cNdrM":383497328889,"hlz6pBgee9YN":578979599512,"QIFf0UTv372R":186958486055,"rDZ5BlczNGco":391462590262,"k4JvsKgiVT6U":380770112546,"knHq0vHPn2AT":89306776749,"xREfH3g22fFr":925556506751,"TDMQ68RPdrMu":508230119985,"4HGYlWl5odhW":756280920774,"FSyPGwrGNXXY":64376101778,"Hr0v1eSE126J":529296083263,"8iY7vXIX4mW1":805846488993,"kYrO2LnS5XmG":802202755291,"5hnK1BL0alla":776547569173,"09QYaZVuIAPR":698382466284,"PvJse1IbTC3q":177020251219,"JoQ9PHBE048F":232635352283,"nHjb3ZMAdFLi":142342038521,"8U5i1OoGPuT1":230450925890,"2gx5gRy2K339":837980494700,"0taWsp0xMgMO":900670559270,"ux4g2J2FSSIo":951086596130,"PCNcwWNReHYl":309531117000,"58iXDyyf5BWm":707505337534,"2szesqkF0oqc":527515460130,"FOz0yQLb4e1R":367040258026,"qFnbBO6FGsY5":713826762990,"70zhNCf6c5ow":391214800805,"c7AqijrEKC2C":314199056154,"VFBA4p8ZRCdx":455321360630,"WIS6pj6vDTsJ":690320336057,"xNepVWZNemuH":967269761643,"xhE3MpJ5iWUa":310591866326,"5M3QlPTvbshT":527474350566,"D2V1rLt66sfl":902401163096,"HUgXKROHXdup":588239242789,"chgvHz1xzFc4":897536235226,"pjJnJ1C1V1b6":498335591276,"qR6LMJhNgRXi":692175026152,"oEvffR24gJEe":440240774125,"kloCGNAmO9A0":317856701364,"jzGk4N5OsJWj":401393489610,"TaV0z5pa5Oss":346059660127,"95VL2eVbti8D":642252516878,"A3DXBKkt2QZR":7578561726,"p3ka5D2wSQQg":432256567115,"583L3Lrfg5dw":532130980418,"7AK2rn0Ss93u":714117412885,"5AKDqtLGlAB2":427414621439,"yKJTJHFkehUV":726627351258,"d9iSqMVuet7D":983269507934,"RZgKd7N5t3IN":862945011904,"jeVrroApYBz6":81207849578,"qx87bRd405AT":366356629721,"uOUlPuroLo42":992686231120,"PguKGIcYSqAe":585302840488,"1uOEykR82TJP":315158087513,"V1XdjUa8D4J9":750674755919,"VlQS8zcCGLHm":587847921875,"qGqARTzn2u0b":789832354140,"G2e5Qi1fOqTa":807753466116,"ouRC9bAniIGv":725563473970,"vOosrWZgqxpf":606586196311,"95tz5I33UEHb":641770523563,"kReB9k490sVa":234366446660,"hioY36oeYDPd":930964606472,"t3lZCffGfOEz":384175127679,"gRJVGgMlV9jq":156516034590,"z3aDaETObQiB":154631865213,"Iev0ZrVsjeM8":701381417385,"5tXlCaDZUqsF":439339455933,"pcrsM3zs1JBi":219677161755,"hylnjCmtG7zw":283936580726,"4K5yLop3HRH1":955395554468,"Jr6PN6vKqjG3":739951919419,"FkSSZrUVlUnb":916626356012,"0FK7Tq1jl4xD":211215103473,"Doy8ozg8Dooz":626347243524,"rIsv4L1kzQa5":938959427626,"LZHYawZHc0F1":273346835971,"iAm9qUYTy0W7":816963785040,"1J0PcPTgao7i":202151878006,"VktkCO4AsdAM":732907962213,"EEDSYzqpBWHK":289755770685,"UWjAjwlBxtSK":970326713940,"MlgvY1IaKC3r":772886216924,"2W4XFuamBXrf":271734648301,"9ypI7kNhHOTu":225427600262,"Q5eYgauDz5Jp":221238694791,"vndkBsMcUlnK":722207789847,"NgG99em4KWpW":362629039571,"jDHJESd4JUlR":198695150127,"oIyE6SW8Tg1P":828685528159,"ZYDTzezko8my":455481346564,"TR0FRUariA3m":439683772619,"UO1aKJEjRfl6":428144161961,"W2pYZDWXbnIK":108079574846,"siiDe9R1vkeh":477581402624,"bVSkTKncbbNR":833500123191,"xKhZsFa7cSoR":249878723139,"BQyhsp5unZQX":519468788806,"iu9KxqmkEX0n":606740230150,"Q05mgdacAaZr":392122125563,"6uE996uy38Hi":395922956766,"lVVPr1FekRnb":456926450404,"ZCj86PUI6A0E":364789806304,"UkHU8sKIsZEp":33330077409,"83wAdbCvGhBV":593863387834,"iDAVjrzct6KW":239138590639,"dHQajoODhaAz":240244660354,"mWd1JqRNmQOI":520543399227,"JtCdpCF2iYII":755885479514,"la6yFF8ON1Gu":899407750242,"hsJmzH1opgJD":302802073952,"6t8BSQjAStjv":204996901219,"66E2I9ewZb9N":20723288294,"lv6bQBCIyvq3":474733395617,"pbvIwZ6KH143":630101148745,"B38QHmNM3l1E":187468717396,"e1Vp9F4dkGxv":825104694360,"sy6b6byug8lu":65491589234,"U7LXMdOgsLBr":112305954222,"usIh8rsZ2lDg":676170172112,"6h377vlfRJkt":342154993332,"aKppkRjHb6xy":901695292221,"FCqzu3RNe8kN":399584664898,"ZSBbNLyWKS9e":584402025699,"ayazlh7Aifde":85177793704,"gMXyM7wRUEIn":585720310309,"oZPBTSfKqeFR":721562905018,"AWeqwPtcft0o":639870796127,"JzOXkay9ICkJ":897235250781,"qfXBOWS3bUdk":692720721238,"vVCjz5pnQ863":540840202230,"onnWRwcIWfny":804509441751,"oczpaSVesnS7":472294671965,"485c9TNDJu8T":544123203635,"CwWhjITMrNeZ":716475840362,"Cuk32anQySwM":678486414885,"DGO76DWL5aGW":464507169368,"VzmDqJvZ0cX8":610249072776,"ZlsceGX95YMv":36290718180,"KfIDOiHv64zx":277981712453,"ryjdk7oBb2HU":946186907184,"YxX2CYd4m5Va":887557948764,"mgN4Og9YTsy1":123799429594,"UQJmWXJb6RIS":260935708063,"LBoK0FFALS7F":200983030471,"xby1ocnoiWCk":217586037027,"rdP1wAgH7g2E":426791746443,"ZThX6hZtMpEy":440166748943,"VrzLYODC9bDj":258381544744,"vyAyxnhpBG2I":95470708160,"d9xkx6YWEuPL":363066282907,"JWw0uyPt7jIf":262166597534,"0HqlMWQhd1Nz":979142269106,"5WUyCuA8qDmu":330790152056,"HuGAdBCPj9Iz":409016005105,"k9kmAlJ5Pl5d":348392520934,"dveTo5EtYnPP":101198950367,"GC0j8Tk8Pigi":210826346024,"1qEcyXW6aq8r":727725773378,"0FuQijESZ9gV":174043789365,"qC7bOEdu9FAu":544583021157,"2nbEIaMuCbLS":851482350153,"FZTpDL4Ecn2l":509339133884,"AzEGQSeBkFWB":206169609746,"P0wFz6gyN4lV":31502152003,"f3DdRrztBthS":618687999558,"sDLPQbYua6dw":484077145113,"pWLSJLwY0wx8":651448686546,"IOZaWyMKYaHJ":523916528624,"8WU497SClRoP":52287121091,"umDGBd1QFH2w":161098841822,"igPEx57nNd6k":713923536882,"LEI4mf5DLs2j":377917105378,"FZJT6KIU94q4":615928297898,"cUBlc65gdiyf":699314553484,"YIgHO8H1SzhW":640977567838,"NOEucacv7Zhi":428571747300,"voOLLsva6ZPT":307646792924,"L7oNRNW7WlDV":868043580697,"UtEB0hBXR04i":969539925548,"Ve8qbXFxCkYL":238785987431,"Goa1i4yY3IVC":704913755474,"v5wNt4omLtK9":807556507316,"EJ9t61l2PrIr":306862850312,"cjiO3nGQHvtd":425844866054,"qXj3p01yODgE":98760877362,"92M1P0DRSLJ9":781229595898,"oubasmvOxnod":630833527346,"wtHvhOsFuPTR":760164194133,"GB4aHVrNeouk":565400112858,"iB2SDRhqTLie":809283850507,"j6MjxP4Kr8uE":115470071921,"AHk7qgrBIcnY":640623807126,"E8HA0y9Hwxzg":977125983855,"pe5xANYezADD":138742939080,"rZgFfrd8eMRz":222037889207,"CJ78505mZwa5":805026054223,"eiU5FghcVcQM":343312729749,"mGjh7RZv2dOp":740461774246,"IgZ2S8PGTRmf":765937620770,"WDd7BsD4wyfk":964121307935,"pkhyeZchKNvF":925669212461,"qYsB5E4kh1up":853435471209,"NqxwzlWiCaGK":397675631952,"82eXdxCCi4F8":645357171649,"CQkc7MLKoUly":630457710486,"HGbuIbky60Xr":790155303293,"fQqEtfTGZpaY":47602169676,"fGhrP3WQYQgm":474190296424,"OqjfOw4xNHmc":703429003632,"z7BTFB8HBZsR":498406261035,"hqMrTf7nEecy":227494618777,"RxZgcPQdI6Z7":312923646330,"aFLadnNchxCX":688742780415,"rq2VKPUmKbtJ":286358892332,"p6UHjSQg9kaT":139917945231,"cULtTOdhpLWj":406497796049,"hf8HzQ2KVZWK":149793500268,"0zumVkFpBtlZ":38669050013,"SVtFR5gGik2M":896791093254,"KDGSbdo0yE2u":96341709901,"wv8FzInx6ksh":698940341596,"gCBqfURrV3oW":442189424605,"cSCo23JakejG":781697246835,"oNlkFM2hwQ68":627727556627,"2Zdp5L0n6yyz":308583824837,"JrvAjnpgGWFG":860772247324,"9nKnXdUYK3Qi":429975182727,"dAo6wbu1GzHm":253093249774,"mMzuZFNRNm49":440687683071,"DmLQC9lpeb0Z":471160489201,"KH7KHhnvrEMd":9269242522,"CQNhgEtTtsjF":172049617689,"n45im88sK6LE":915467748927,"KSwcO7r1rBhA":427608771880,"48pEdWDT1r32":926105918133,"nVbkf5VbsdXa":113260626287,"11AqB6rVBEdO":507218431279,"9m3qIpqHuUqs":390077074297,"7CxAAyTxHbvm":21280039921,"UabteWI4TRUZ":554312373300,"QdMY5Oa5knNh":683292136604,"5WPet1Li9vui":618218120047,"joAeIzefgxSF":955073656563,"Rd3dPYtcRRpl":977359882446,"QWGxyI7rWtBZ":586138831123,"zJTI4xKHFoFb":164185896948,"m5Oq1ugdxzne":102332548109,"HNhJxA1nUhsW":567570282330,"oq8JOVOUZJah":465343938862,"UVZSpvyTcO4Y":99657375444,"RLCWhww2KOeX":3308694974,"t3W3HrHVsYA5":603946786922,"neZDyj0GJuvc":439179060704,"D1hwy1BI6gUk":452559549290,"EBSY4CUN6AV6":428847095798,"x80ZiJduegii":554803982219,"0R5xPbANyFj3":265841466465,"lr9q3LE7xsol":334343180789,"s2cZJZ1ZTAHQ":798036333241,"Dci5iUaIG0QV":656586243223,"GoMYMEJOUAil":178188704218,"9hiyG6fhE29S":741955165329,"JSAT6DcQHqCB":972070656661,"rFkhnqGmH9sN":103119777942,"Rejqz3hoDvqF":36462331007,"8XzSnRpFxIAo":658870915458,"RlzmJkHiDXYQ":706106690409,"11YGGRPcFxsM":52269201436,"EQ9szU9EDxmZ":713718077600,"bWt5TZ23Piki":237279396640,"tH91pmfIN9Cn":792990730225,"ObcJiNJDT09x":634651436955,"Th5eee4pucy9":470976134118,"U81WQd10PfuG":156277258849,"PBCMVaVhxtX6":431766722848,"5P4LtRMlTaLZ":394474262730,"X3zG9ovQJ6rA":409030639976,"rrkGPQdtG9Wf":623317632320,"CbjdSQPuKxdn":51565901110,"9VMmL1SeuCMt":128751018405,"zebG8OcaxMqT":143822926219,"U59CINdcLNG8":514114577359,"4OHSTvkXJLK5":229281134861,"RO8jleZatcxb":656865273830,"Eq2FsBLNcUnQ":344598678782,"kdD16Yu2J0D0":902758511781,"2CDwRtSTuS6u":460889020267,"cYaYd2isD868":781142220917,"PU9AXIBhTxpP":925506175533,"3KNpq9slYv9L":240608612269,"DYZNbyEvGoqZ":765432549583,"YoljzrjMO5z9":141018603843,"JVdbxxJA8c8c":122191286257,"SWCjVJkNW7Wl":405762001908,"OHM6qKtFvkki":974107310039,"HJ0At7jo4xOi":273533455182,"NV3fT58x9HXt":285209682523,"lI1wGEbJ6xG7":26790590030,"72CShxRmx42A":438988756490,"0Ow5CXggzLZl":487104816329,"Z31duZmZ0ppV":84570641579,"C6kZbF8mU7fB":736690229866,"YqwfxiymMqqE":858282207618,"rsOgi7pDDISZ":674192362600,"lwC6DxztbXSM":343486584284,"2i6MEaOON9Vg":842023588858,"nsmx1c8yrUox":178500628422,"HfNDGFygiirH":353840149240,"HtBdialHDJHS":996102934088,"TKI3y3nzyMz5":156140215259,"FUnLs0uKtaIr":715495884962,"KV83Qq3eSCic":954651662810,"b1kR5q7xeohM":284440466523,"kTTF1GnBnBDV":573398544654,"nXUxxwdH90OT":44937393189,"0MjpciLYEyQ4":20786371692,"Kallm0sH4duX":923406684847,"78fwFSEqEpUE":655520042939,"kuUfK0V9SaT2":331930029440,"LWpwGe20xf68":18284464885,"sz5I6AZl5G5F":601334383619,"tqLhDoYpOebT":571444091308,"adxbbJQb8Yvd":190158020136,"WuSC9mMKWzNt":497840734141,"KflEZcP3Q8yQ":543273093618,"CCGE3kz9W1nL":807680442160,"wfMCc0jtPBgG":41432845424,"YBwjj86Kv0Kt":887736647050,"BTPYsLtuwM9N":343842523697,"ngPWMdjxYAIl":534160291766,"OeE9aLirkOC6":241559273652,"Kw8oUD7eneoE":638714929929,"htLLqp2eMINh":408577791446,"Nw79OqcJfi7t":127589693722,"kT1RpOFoDCDI":405879181777,"Y35jQp7ebvQX":660848181808,"XJ228OB5TwLI":935300295275,"z8iY1bkYM9du":817917399915,"rWjqDJlq3xRk":825411115582,"FLhzT8B743Ia":689061860950,"7wg5N558gA3k":518934784951,"X77yRlfv4imA":309683805995,"HEKL87RgO8xH":386047643113,"BTkbBP006p0g":86009543,"fcaip8TGN5Rs":16756567508,"zj6XAckT6mz0":484366380816,"pqcYnAXKEmJ7":75958845654,"Pw3m1TcULZnT":581114207983,"qd9buJAuMkat":556693185854,"YHmUYkjDsdc7":498518873666,"glVBX5ag8WAR":684626477384,"Xs2nJ6SQDPxH":908973130275,"LoHx78NHX1QY":16794505407,"qWc86P6xK19o":573466878995,"qAfqjCuI60X5":64379546590,"OfapBLDiJVI8":464740768113,"yQMBt5YaoCAE":907384010884,"ZlgeUJI1d0f5":837972336279,"cCGOwj6geWlj":222245535684,"KFn2YkXtLj4K":997872780142,"B0UnPUueQXPb":152140189249,"QSssuFUjDFgV":161832019972,"Hd8ktBnTV8Eb":977223579405,"5I5yGeACA1Ps":32403700676,"Zpus6qIMlaPd":536409734738,"v1yHClDD1Axr":266838757027,"4bKyvhqxI1J0":631773120083,"93ibFOdHpV8i":33051910319,"dcjex5WqKlu4":117578165164,"PH0O9mpzdarL":263322388948,"9UN7XVJ2js3s":980983629323,"Vsi43mcQqhd2":116535284951,"NxFLVJ2NMSMq":25408999649,"IVLXAy3jnCZ3":692195505238,"jnMXuXTr1izd":456209168503,"VsARsncUkrO9":438602393920,"YW2LaJro5rqe":538878301524,"80KxBKUkMV6U":897632208048,"Gg9qDUsYjK3L":961616524623,"DxSbRkpR5ys6":577491461256,"oMtqeV9O920S":142392240005,"Nxnr3pAhc5of":455474432354,"MpoGGAiK5WyD":513965712932,"gs4VYxO2DBZH":606773806825,"fCNhGMjUb9NI":588345922594,"DHVlHupG9gyT":928448899670,"Xpjo2M26Z6YV":540546472479,"ihca2nmiGKEc":695414550169,"XjmCMISiRETd":558458420516,"FGrh68XoAhgk":732420558267,"Q30DGitESWmD":612601005618,"ur0ip9jHPr9E":450963518621,"9fafc4QPiS9v":680926138098,"lJe955ydbB3V":990415213587,"i3iP6S7Y94pq":43553897267,"izO1RRyTWdWK":151881675982,"womfWiN6pBX7":597641491318,"bi71IIBYnmpz":862392446148,"SCcJzCtFv6Io":342609514619,"dz4YPXTmg0mc":22604974018,"KspExCQWz8TL":50609192755,"ddI6gfPVbHSV":357582537904,"5ijhdx9eezV6":72735332383,"AwUCgWD9FWl2":395073337567,"QA2QnIW831Xz":962589768199,"Drj2PhTQaAVK":835846614714,"HZIWZ6Lr1dbf":839368565067,"WAg9na9WPKmr":50409078875,"jndU50gIUlPh":602918275841,"xUiKeFTkfZEN":514016837865,"0yVEJXlliqpz":829943156839,"gA2wt6sKmDCa":111181568157,"jJLWVfBTIPpx":729020380748,"zqTXkkuuNxju":168651459230,"wt6ZyVu1ygZi":138183040820,"TEE0ItkzO2Jf":655891975868,"zEQ9o1gQOmH0":815371315742,"y3DWuXZXU8ap":824741507489,"nBB2RMJLBzIZ":745817579479,"aKffKHxk4PgZ":475274819839,"yLDwpUd8vZEj":643296351233,"CeEiF7UtT6hZ":133418806642,"HmWKlf3BuR84":325245864228,"bsrZaXh2XNhH":375970730970,"6imScpHl4lHx":454009838732,"C6YJ7rHqdrCS":150607636472,"MFHQWB1eVE8T":124882377839,"Zar1r6EUryz6":348415075646,"agNgq0LSAQeZ":424338784987,"eIGPZIUJ9zMU":669480786222,"1Tfarxxotos0":307912463285,"zZBfNRJ8Pku8":206314670559,"lezFdRDm4zRE":576546219170,"Cvs5IvEsr51o":681802950926,"lRjrYlVuhhWu":820677329182,"WInLYOlySUyc":111718947562,"NwpCrSFNxt5V":374171374192,"Ynpz7Pji3J04":912486280476,"vUnigJGdubYY":8530122763,"Cj63hsqmkY71":900914272363,"7T5Fe1Zfhafh":898340994315,"pJea198uv8b4":604228215617,"nawD6ere5iYh":181254283333,"tCvo8JtGdKUV":575209130524,"Kpypmc1oCOYN":38868469259,"2YkUpletl7vG":102637992152,"F4zY2wveKUct":835004503215,"B39jhtETjpcO":535544178825,"RFx79xkoY0uF":495308399393,"JRXHldDF2Zas":105013964684,"Kth2MYz1lK4z":510060940054,"0hKaCQhPaXvd":896836143385,"ZyscOptElOeA":808491029538,"cy56VGzS0EAM":321147702248,"Vd6x2BcGNHpa":145304392503,"BRU9YCtpmLyc":522120179410,"hzD6YpPOYGlR":40251661903,"7843nHktdKqi":246961710740,"2tilx7TMlaam":354364075105,"Rvex4cnyuLXI":840400212494,"9IksuXMxRLD0":995495520624,"KawnQBMMAJl0":16967560124,"jpqfYqBJ1f6L":595217284987,"czuX65wXZpFM":982631873407,"1JwwWdk6ezqn":757865866441,"3ngXlRRdG4Qr":658180196958,"NfIMUMbL5LGW":255763967237,"Iqp0lDFiHbjC":373984308445,"fK8oI6s9ULiq":315744132751,"LFMlzuQPElwj":274353865388,"1GRtADbQhWKo":881792289963,"H2gi6K0snLvQ":33465254076,"Xy5n5KAYfijR":961326818798,"zSHaX5cJoud2":7928738268,"4njxyHq1QtWO":630330088162,"j7KNPuEHtdgY":977232323096,"TMNRi0sEWLmt":336211048018,"Tyt2hltCtqxq":922907504649,"bo78ZT9E4TIt":781143777718,"tM5TJHQnHDhE":151012270970,"Bb0FfiNgYJuh":557412549326,"jCvH1AjyMEUA":466997664028,"97MFZnTVwlys":615239841779,"qtJm6YU50EqM":347867640038,"Ng73ocDO7Lk5":349965057571,"5QNJZaiJDtRE":268101415722,"qdZqVdvshHWS":854665168063,"8bv4ZEp359gH":979700687676,"vMEXhnukUKMC":389732186797,"SmbGBaklO7aV":53097839099,"PExATcPHMJ1B":642958441300,"xfUij1lOCHuD":956558915755,"bbYHHhLZCh68":107868710898,"5hq85NG9bt5Y":418685238366,"CfFPVxMFbURO":720952524966,"836tynxmTSKO":501745065097,"yiQrXythJwtg":107934445182,"H0P2MYIQ5Rk6":619465944801,"VRypbdTx42jN":621565742129,"wiuxOsQQcqwd":290162716572,"8NEXkzHprKDc":141793870611,"fDYvbWtmRECQ":671852739901,"mDYwrW249Bp6":976726546354,"hd3dxgS5Lzgq":970191762453,"ZrjucTjoN28a":868092610096,"Ufub96x1IGLe":377181732829,"XuAdClpAELd2":977099480513,"M23VoFDSe9gm":437312599920,"yJAyAC5IyTjG":895622773231,"eXVFgbUB3h4a":304323986405,"9AL2jXyc9GBC":365778004685,"iBQzIOobdnP9":496523598518,"GPLkgqb87TnQ":179052736899,"Mcjba8sUys1n":852269741140,"MfVxBrpIdzh3":124612704582,"UIQXzv1NW5aQ":863698023195,"FWQpdxiEQWAj":642843573011,"rKupNWj7PXRd":706467694765,"n58fskMQMVfU":28056463844,"tPujORC2gASX":250398396837,"a6zNINbTstw1":225173296040,"Kcv9n0o870I5":348263489239,"TNPzbv1h0Ifg":445013478742,"0e9QBzXLRVq3":597942068679,"hvU6NKyxxxGe":42366064785,"Q4YY6cBlSb2t":568760485893,"l4CO6nergJhl":152425628502,"Hvi8vwHSU71A":906108738724,"SDnQN4yOUKmX":23863177625,"yoRhYjHibUOy":106200651958,"2PIyLnuWpHP8":901691425741,"uVe97smRodiG":702748647094,"N6UKiI6ngM5Y":24065045504,"iY2rizYRMDZV":203274193009,"NNoPKLlRH27h":575870312844,"6wAv2rq2v4CG":838178002169,"OI6d9374nO2w":897955812169,"rDV5UxKQw2ES":969553611404,"k82zL5QvEfrJ":907277572764,"4HNYFMl6oADA":158554672372,"JPS0WhqSefr9":728814694402,"dooKyPGUxoAp":755493907799,"27tW0NLkxYwW":288503521035,"o8MydCekOvOx":542480630113,"LHrp9NnzLqn9":841816963804,"ZSYjhd9thdCO":893489175275,"1f32yuC7W57k":797572480266,"vmuglwhB79UU":452536973898,"M0wUTasvq1Cg":374651511230,"WBBOvJmUxGoK":53597582660,"JPVGnBbYyfJk":549875866101,"abojw40rUQsO":683417388121,"AdU9btsuqvuN":913939329605,"6ydg4TvldYwV":841016737404,"lVH6vjYMda0C":166837050270,"MbFqap4oEQ4O":391644663000,"IE7YafQ9wQry":756137701805,"sMxjnvSTsoKG":335551524876,"jZEobyKqdzBw":776651223718,"q8B4vJOI2FRl":615101725042,"2ewmLUdLI2ur":808460419490,"GhSt0OEFJwLi":320762068698,"UuX7pg9MId7j":561952709576,"mG5lzGVXfjpB":586558939535,"kU9x9XeX86eU":243311775229,"Jkr99ypEmEl1":538352646384,"WXCZh9vYR0nd":528419685587,"i7knQsGagSUT":780993877347,"sBEdKYUK1a4K":154880323077,"yCxg0kMbbCdP":197671170050,"KkWno5Lm7dS1":404073119474,"m3SaqWC9RaiJ":650456462354,"9lc0sOYTL7ty":331812300151,"1ZCudzbK64Yv":404539544989,"HzIhNShLDZx8":272016358374,"5hI58DW1O2c5":777755615617,"bWvZTCu1WH7a":733894781530,"Wse4vt06cLSQ":93219788941,"O0DO27mvlKhj":950494016127,"gYbgiFYxvyMS":482824221971,"8CPOghphFnD5":242180201757,"PTArABORan5L":516587180922,"OCEcx8oH93EA":774549416451,"5uw8Osu4Fo1f":206620596034,"WEPzj1itJhlx":278644122390,"owxnBmdZOfMm":350689988422,"Wxy1wNvRjEh4":931456884706,"Ayc4x3C7YQv6":657717231148,"5MjmYdoQdzhM":110138721076,"ljWsIHnCG2Lt":975242473651,"67XsVsdHss0l":59069423402,"MpyeoqJ4pnBt":490906981275,"PeBWKEnHPZ4Z":139397469590,"VcW5bkfZ2CVt":989399533316,"ztHjJmWQV8M5":904126474349,"JrUuwBtXMn6n":23491554652,"Jc99L5HFO2AR":865026417294,"BJh5dSbgchsm":621088120128,"7HGxIco0zobd":172948605214,"z8oNUwYgK36z":424229866650,"z6pjdDLQ4Q54":411374092822,"yuK6PZw57jQL":298458138133,"3y5rmviakbOW":594082091952,"4fW2ce3UT4hN":369039876938,"VUNKE4dll7tv":516486798685,"kLfC4T4s7XPm":680068115628,"QRVOSbVdmeGT":768875961177,"V8QNaE8QDxxj":978241925701,"XObQa5DnEvyw":723412568872,"572O3eCjZFKL":493593179476,"lcsXOLZSp0R4":486773288891,"A2r5HYr5gvu1":298965736426,"BMdqETlmaG2F":579335834643,"ILHSuK4laEfr":777642247948,"ENGwyzmqSxG0":910340337505,"XlpH8MSube8P":356033199434,"qY6eFt6zPqYK":378148982516,"ZkPlzJNt4LIQ":322823415951,"5LhAcn9VwZU2":617166971309,"CHg30yITIjyq":644091550934,"2SNYy4iMmNJe":163289935519,"ehB6l7Pc4mZ5":842651665485,"UDGRotb02aZL":829238507045,"dJ0khGTQBN8J":377522638557,"ywCdxG3USKmg":60096648248,"TBmgSYXmdeuQ":554239273799,"jiqD16tKufDO":690479300955,"FdkwUqrGq8By":166226859194,"Zl35JUxIK0wx":52476917523,"iXiPT0FYgnrb":605529055553,"FJVzSJI3NT3X":518207476455,"NVzIOzDB3hnm":154556739364,"wHJMc0PvZfjc":350390945520,"AwmolUmen6Ov":689195307621,"uoYbR33PW4l6":731993769743,"Prc3BkzrEKQM":52196535795,"drreWUpJ8Sov":813885213492,"xeqx41rdQiy1":435718493289,"dzftTUnHuGEr":690152546638,"GfgXAcdOI8F8":661434843178,"ECxzfIM0mBOB":840693151344,"dTaWtHgH5WWb":679945155728,"JyDo5prbZ1jt":386264509514,"9r7igH8V30JC":92166434672,"0fyPfkYdnnop":890132284004,"qgtGKZO7l3ji":752559830082,"B905ou4nBilA":908434972613,"Sj1ygqOkr8Ah":858911101115,"5K0FcshUO2M5":309761459707,"BJMumzMgmQD5":671327128274,"kZtQghk1qSfD":851906341474,"WjJra2F1McDc":906271247722,"nE8aXfoAfC3g":854668546690,"jSk2cBgsHazD":611923224100,"hJD2Swqy71qK":927321230938,"fS7RlgoRfzvB":345251139931,"JkW8TaXCDyJ3":873806327634,"uyhntc47zoi0":268159583414,"QUv9nikOFXW9":606152001493,"amDs1Ma4iwcc":558055081220,"FDFXnpdXswFX":234922516908,"z3IO8LJvYhKN":626369811240,"NF1nX09lZDZa":549204600561,"RMFdk1D8gP9Q":753185667953,"8RDQxRZG9Lps":116978417135,"K4szcNAPbSf3":322343002528,"0sfeaOcpzfxw":668191375050,"9JDsCy96EVN9":319011331424,"MFqesJbR7MnI":383201103257,"X0IUhNrCnNwI":57121133107,"VXbfQmC02lvF":456677047462,"8n2juil7a7vI":520160234987,"uTqdqCe8PBJP":905072735043,"juAd4e9Wh3Kc":463375732849,"1M54fO1XTylo":823431885774,"escAAgUDb0el":814075943256,"BHYmPrMFXtNp":418465044633,"9P716x1qKFJE":160887327805,"dPKURaTdLJ5Z":725299068578,"6becPg5F9uC7":364107089900,"NmrOqadlneSw":559320257983,"FGHR5kToIKfL":396884138719,"7iCOB19cRYDY":347100002623,"p3ay3njK41ka":610835080547,"OjzNLkK2UiqE":906829927303,"poWGLZObZF8U":51739545056,"21H6yQzZu662":21921556317,"8dwp9GxiSAp4":965612667398,"cPzC1fKBp3Q8":334803966456,"LWsxKm4q52xu":397513676537,"u1IvyAhFuZ74":94691721303,"dU1lorCIhMr2":274252494127,"ObfdHzcoNmxG":229686826522,"P0w8kbIYQT9r":522120046751,"8kPqCjtO5tgm":564959664316,"3xjE05Zav6FB":990418519400,"ocjOb0KOKf2i":500556840167,"OaukB5H3jH27":20398842147,"7qIGikTIx8Lx":449995407098,"jdAOiK1YNoJi":58892816475,"zrflV2zDUXML":743516203416,"mi2zKE3mlTBS":528084216282,"5tP8wa4PlyPS":10862148594,"mEiui7nPpIDH":874081458572,"TV46YXnkyIiw":902142799746,"WWwExNyZeeAD":618427381316,"tsLDo2ZCRFtm":670848187143,"nk05EO4Iad9L":287864977143,"KXQldmoASVtu":895812668132,"IW9citoxwORh":109679728384,"5g5XE3BYwcIs":18230776567,"ylzXtmwBMPMj":887379589322,"4aQA9M2fhu2N":778829461583,"p6D8r6qvevC2":20266522042,"RyZOhvelAe8v":467793676053,"D5Jhj05sSGwW":786441176257,"PQGJ2mB2hQrF":295397828400,"ugcjSBqS2dAp":995520408119,"XxdK9N2czRmk":333260699943,"fDud6NEz7xV1":136342911678,"PLPnxXUwiPcD":459706835426,"WTd4rh0bLzBr":19272831444,"uEQeOVNl7OAD":41889477713,"tWk0XYfkViv2":488339094010,"QJGIzgYNOnoK":355744612866,"O9HbeQH5BWY4":153994355077,"2cFBWqQO9OaH":127564038178,"7hAm2YhGbpKR":194671230205,"fp1DMzEB0QSw":168313081558,"MAl7unIK1e2l":234975811768,"nZqpWMQmFDZw":672872449320,"PpPES8etlmIT":344535537465,"GoUylmczRNI7":729519157175,"dGAOsjVld5bP":421369560162,"uAZUgePWqbQO":990871998137,"Z4J3C54aVF2X":85956507702,"KjKZ3uY3qHAi":157789287428,"ukAHRKEPx7AN":894277601795,"SVaaWap4cc9g":443653076852,"CMcAHS7cAa3x":798722003503,"flDQbqcCfWxr":621811547356,"MFGmmmgpBadk":255915504855,"eooZq0oIcbis":898182582428,"M6cd51CHWROM":719228511651,"gFN7f3T1f7fj":43631762936,"eOkMrkDqwDZa":303860478762,"zaF0V7isvZ8r":5863337054,"PAntBQPqqFff":233160031235,"3wl3NhWxa21S":596291994350,"xzdPEQN2UD3W":487282745393,"PzD8lOl8FXqc":539001392969,"lNUCbQV09Ssi":722211368760,"f8aObb6Mxv60":17313556944,"t9BjycQRIAyW":10162200024,"rfTK8hhOLz8w":705405406449,"FN2QtMAWlkhQ":690835182634,"XqiD5LWUIKgt":577052072773,"ZcyA6LsQOyYm":37204248688,"WHtLNEwMnup8":101298036644,"DCKhosRvFi1r":284025232999,"jnSIOCDTgdDZ":889702333936,"OCiya9Ctjfs3":98932347436,"MhPnWllnaBL5":522080787560,"TSYMAQAZUO7i":556491436549,"0KPXGDAKCVW0":612153227155,"XkYsF1ZRFkEB":987262747440,"C23ZOodMFf3h":473620802368,"GUUDhsZX6o4L":152997506465,"glBehzOU858b":209929195018,"25T0MkSGuj66":459154636080,"qbWhRPG6hl8d":97404152079,"FDGobXhog1oU":369081446350,"4MUBy3fRo1Qz":253253611188,"A4bvBD39wiCZ":9927348677,"idNGavTcOS6b":225969487998,"CDIypAKzeTd7":497144367852,"zBtJgeJ0NV6p":652930303418,"oxqZnMDSAQSo":375085801322,"gx5aMXy6djO0":903827724465,"cakbEDZyw2ho":488594909850,"vY9YYC6mFAOb":800101785583,"Raprb43bsNle":124190755471,"UAfOAajfzwYe":687303365776,"n8iCcLp8VCiE":828409823861,"gmqqTynQgCgq":525003336813,"xZMyX80QkDsG":642553335037,"6oVXCHNDHakw":445711283727,"BAHYNOb7k1bq":406058786157,"kYqvS83G4MUd":488401493724,"bBfc0sOJ7xzg":83033853797,"e5ulMbSLQA7U":355351832855,"XplH9XWLWkJy":807721835487,"bFqSXIMyExpz":142095097215,"KuFOGWd2OUZA":335709093726,"IYDnVg4U9Pkz":832336717836,"zXo5DOMBInRJ":235596368634,"7STm8XRD6Qph":679122280920,"7RZkW8zFmWCE":800499201814,"Tz7NXLyH2do1":316882327909,"3ll9AuDLC2Ra":900300686682,"QWLDK1s5sDe9":733160259188,"PZifAX8ItqZZ":180796003511,"4DTA9juQQAPB":411375001339,"57CecborL6BI":241720368453,"OomLpqOWS92E":344602663491,"GRDeaGPIMcje":843157304331,"ccwQhzGxOidT":790286315828,"E82I3FLcGxwE":145519570469,"pVieXBOl9Z6y":848508127908,"JRnDvHBJXyv6":82639104693,"49YUZQgxhe3q":763254260784,"j4ellaIAoFzc":95557664227,"M67ZNxIICDjj":211107880333,"Lgariehb1huJ":281423725947,"3ecwCS2syv0a":667740471719,"PpHtyc7iQrUH":713312333877,"fmYutzghYZb2":78675695125,"GSn216nwVaek":327589733200,"b0iMT6ySqQMP":300132728296,"U6lygimuJwNN":183776803370,"4UESXlBXvYl3":770474067416,"JQF3HIKHs8v9":789441029478,"5Bs8IB7Mib65":143942062493,"8YMWqEhf8OAp":582482443592,"stuGu67Z95op":345711004320,"goEyt50ByoQE":546386452043,"Z5Km3VZ95cl6":466520885691,"Oq9lAM5RIYpr":973079959069,"pHyKzBlh9Ulp":860103921662,"q25E333WCoDD":611607861484,"eNsgFIDBRpMz":958961706598,"fgFCjn4dMfDK":536979450761,"tiYsk5iyfdUN":591271221449,"9vlo1L6DqEuD":825639827370,"XtMWJc3kKv3l":635045745811,"8Kp2vssJlJeM":426674660062,"SVwQBvVil0lX":415663816617,"eiahl8jBRqKs":189797137678,"GSsSPjxKmR9W":550259825073,"NUkh9dGvFJku":690497923030,"zB1ChG4qPIYQ":708657844050,"94kAqb79ixtG":782400007228,"TTVFu782FHtG":72198169865,"q8LMxdytN5g3":199054947014,"5Gp6jgVX0xNp":77376429641,"OneSjaU7gomk":275773089652,"4hPlRAYOXaAk":279849441974,"O3ddmBF9Kp3r":991227765627,"Zb13HRMkXrjP":807215376036,"EzYvdPQKL7ys":553726088464,"W0knScJmDbaL":547715546671,"vQWmki0OxBXN":622853468467,"O7Kzn1GPxc7a":581944659219,"JXyvjtxrZwBV":298222879733,"EzRRyEGQXA5c":345637753809,"OvtfFp3W4KDu":738017685501,"RuWrMj3phXib":727659195198,"6N3NYBtEagNI":260214327494,"DnLvzZPKmPDk":975620810514,"2fcPjNA3EKwn":626617773949,"2GhJbUlIt8G4":249524218421,"GsZls5YW8eUo":808970669841,"uCBExovqBSrC":507340093054,"8JIOmv25y210":29423772550,"tnYucI3NJh8Z":975079009197,"vr9vEtSKHyDV":807795659006,"ezjxplzMpYJb":48940510689,"S3KlNU2bQfdh":576251755893,"gR1NnjQt2G7b":234002070989,"mgbVFMxz6Hvv":488661805842,"TvfihQ1jVTJz":73510234080,"8lHd47mJXzun":273168705702,"JP1iC91I02W6":583676164173,"CJOFEAr6rd7v":79364045689,"poPhbbIh0Xyk":916039497655,"j2gx5p9EFLMA":899537010812,"HiR8bIJ1o2z9":937250426608,"9ZrnCdYrCfLI":636170382999,"Ww9mLgeUpGgx":89970075374,"qlWSIQ8FTJtB":160251885264,"WQI1Y2BQnFUl":382016518320,"1HUlfyG4sVAg":726319492235,"AsrRZiNQBKNF":510513232716,"HNjk7VjpALHB":892543919605,"XxNUiYrCn4SG":81110926180,"gThpV9C0RNtO":358369165034,"Pnbzw8ZsnyNL":706159901558,"1USIRqFUX2k5":312627107404,"W3amq9bSClew":237567236885,"ltplcLoPPJGd":156033099456,"PZ3u8NyzIeuW":104497415675,"NifAiAPJPSFV":544042552404,"548KAiIUQCNF":369508178582,"9rGZLozH762K":200560777824,"tL7hVstSui7o":128404182762,"MczU4TzVqydd":978407159491,"TfAwCJNtL68P":151930830850,"b8oCTs3pY6y0":463288770872,"0IWOms22Ku7h":947718157071,"sfg8pD4EtfSu":673886764917,"SGZhoZsYRDtD":951991630329,"rCOcAxFJJWN8":445488764865,"pF0Ta134g51z":248802873165,"tTZwFx6Ie82n":63782342245,"FJ1GRGedwqvL":766793880945,"lny0vvksuxhy":184580611712,"qf68L4g11zPE":61990979037,"pnEAJYKa5OJy":210320371455,"UaeGlylCfPoL":398492930691,"L0KiImm2hhxi":520830203616,"bO5mn6p5T4Cj":249866774246,"OEHU7B3SscCo":75425806008,"Oj40iTEImu1h":850441362995,"cE2JQjEy3NV0":521958920833,"53e3b6e6vZxy":295131328320,"9bphpzKmPSug":871140230636,"xaUgo1E5FMYX":128923256783,"jwfsbQ0C8ImR":101160037181,"6G5EH5DAaIVj":636437509318,"XxD2OLyWaAFK":409513521110,"6tfF7xSAzM44":238285359534,"zn06ORZKMYQ5":995263494427,"DrU4uDDgYyTM":110880290543,"AkdRY3iR4ovn":415150954346,"vMjCg7fOgj7E":664098409818,"khmWKSkVtp95":115168844471,"Flc69vfRULYW":195780556311,"UT8Gyd9oAnVe":112935542725,"pm94JXpRDEhV":811345846993,"s4EAOnmbXj5J":717267640946,"mQeckGVZcywo":237378919014,"yrRB7plPAuH2":39682183836,"0TA7Dej6UYha":450933701409,"7ZqXVT1adeEB":361082648476,"Y6Xmu4aWOUnm":671907877308,"HJwW5WNoPRxn":247973431658,"ewRSwbhcTwLF":811906441558,"se2iA3L3nSl9":774104241026,"nmg8ytlIkKOQ":573141881438,"k5fzSC9vMNrH":856299597679,"kjULb9dqqeVX":89753112039,"IetX72WqY4QB":953220821547,"QpjaFiQHf3n9":943629315286,"n1pNMgjAHQhv":164423034830,"XeFWSqnQHffO":618406336199,"SKMB1Bdmx71i":363215051469,"KGJKUMh0eP3D":935042140241,"J7Ui4i5kk5WV":392073189052,"hHBnVa5U3Oet":656237026791,"akuM2MRdNL3r":395437567317,"nEQP27RA8aLC":440541303026,"JjnPS36gl7Za":220837402046,"6eFq9PBNY44w":266729568859,"fCLSyQkveMbc":118033701462,"KBmrnIJmMtlo":612458469979,"Jrijvyr6GdS6":436158408281,"EqisEExbscZh":21901529338,"H2dugipKsBnw":239631072579,"AOWgZU19LYQv":420166668328,"yFwcBCwZ8TDq":126611126399,"J5VVkEn5cbab":470897946442,"U90GhB7wkxKF":785140066190,"STSu6VUcq8XS":962560869192,"inDd46pA488C":683047576712,"3gjEml6ohano":822506464157,"DZ2lzyNjqEux":492117307605,"DLVM67ZSmQJd":74844778718,"WVk1v2XbVtNf":367570565061,"PZgeAbeDcvOQ":945513237610,"DTNjAEmO7w7h":847074979885,"VvCbANcuAWH4":666186136416,"Wjc9xtWdPPa3":660831718971,"bhFrWTp4TM2C":394991018447,"dODAWAdIWqOx":628956819899,"D5g8x6MZgBiq":330645468234,"dMFAQHPE05LU":482548176100,"i4dgbqKzKReN":652749416545,"ipyvByU6W4rL":479357359900,"NdlGqqPyK5Ho":811172855894,"LUPx038n9C3J":641048067826,"XM8zoojEafsc":96261652319,"BPm2fm402cX1":337815815714,"CBOiVgdBvyLs":743468146950,"HrZwmOmWTloL":493812946709,"cMFHx9itayiV":866992254661,"tyWkOg98CQAV":7938042956,"IOdD0f4TYi8E":402027643954,"ZR7qD3joBak8":620025117476,"kSU8CIaPDjsd":901081839187,"cITs2ribyv7d":685644093352,"fuv7gI88BkQx":146729564994,"ERlzf4yLbxJM":735851656409,"K4B2VTDXq2ET":853450337105,"rleeszpJOPqr":385895388091,"lYN5596mbnH1":286499126059,"uIxYhjiWK2GD":506392596868,"qzeY6XrGjFRZ":94957088632,"zmu6PeHf5g5e":63379233799,"o0s16medQVPD":533889094207,"YgtihSz38n5G":881226935054,"GNetkdhGa20r":163983160873,"HcwcL6Odudff":1695948680,"AbArUSsx35n3":950428108228,"WXau2CZBE3MX":764457583116,"kBuLlpyF0l0D":592956347500,"Y5cU3syB2wpd":553288298657,"vfP4kST9V9J8":936308892679,"6djK6mYFSFo4":39211594462,"zwglOYOCupmT":27081056548,"mPO381yXlO8N":272206472625,"38yc8AFllrK3":812568324592,"sF2SIzDhOXL3":521681867322,"87vUkh46D4Jg":642345712484,"oXAV5y5dXs9R":32535192624,"mmbXcBHFjnx1":150621204855,"wRD3eD9l5KzU":723497362796,"R9jdaAA3pvfF":148516364928,"3XK3rNQN8sOe":609230802783,"ceR85pnsTbn2":636131941734,"BB1MMwBqe25Y":98612399575,"X4VlihwAyHuE":444838208260,"IN11wN2dud0b":771966284581,"M9tZDWAjbswE":219490283200,"jUBIJFM0DYE0":509367863517,"nsUQgKVNY33B":281048519960,"94sokbgy8wz6":829933612737,"eRXWvfFScwkg":511185128465,"Vvw7beiHhVsO":169087096167,"kMP9yOhhT2WM":53637248300,"aizcA5bixU8h":817667266394,"NvkpfKgwX74m":79385640608,"C2d5iG7ERplV":452019077858,"yvrwON4YbtXV":871047802891,"bZNZIIZqyCdT":895691877582,"bmh6M5qXZhiG":470598038967,"85FvHtKGIasT":68100842062,"Z6aJSP6bdhFH":800836988097,"rSU1bLYOaPQ3":637254106879,"Ugsl8Ahx968p":140066151428,"7ZZ6B0ergqDD":195731498283,"Csem5QxQ4rPZ":927156004639,"fbrNImlJ5nv5":471845984901,"AkiI6xdASKru":767616045621,"DHnGJdtQmbPs":273770860647,"YxjTDXsGKEuP":385328111534,"dtr7TY5OH4dl":99001920058,"xOxgO1HYNUPZ":467659596157,"MpfLxrlhvH83":552121588470,"dJ43pLiEXYLo":194150041079,"FfjuBMEp3JEK":585278169571,"V04EtIvxBDJu":897922884480,"0j5KTZkZl1l8":251569162123,"E4xr93MCVL3w":899757994446,"GHqL0ei7ZqNx":908060125158,"QsgJZc04fzAC":782276308290,"mf6BXIiMssjn":292389416325,"jTPaflnAFWaP":730132534275,"Ot1SL2ribqrn":79159438844,"AmdHmbZnWxtL":987948066705,"kMMzs5DTOjp4":534988077937,"HDFrOMSDk8Sz":29479535786,"nqHEbVgg8jCU":48581194988,"StnrYjXEHPex":190368498497,"8gHD6EbXFNcO":153754414451,"ZpCSioG6ZsXi":930791818727,"WTnLAv1gVUso":370289745007,"JlmJZTaHaLtN":202233950201,"KPtUcadwW5bS":347614938931,"PAWjQHhpwcgB":548682071167,"63Ik7gu8zooz":635769358447,"JXfNkHcnzN3m":713480699939,"M0KK79HmGzJ6":861534580517,"lIXChsXAVee3":250017488114,"zVQgwjbBdJ5r":518183423439,"IVVDEwCU2iS6":185066523650,"bTfI6y1va6x8":970922261527,"btkWGauUcn6d":827886626024,"mpHkyRpoPW6o":947607707865,"EPjIqk1Aq7M6":175166508999,"FqWNNdUJLQa0":779398916442,"gz0wcKPx9qzR":134117400071,"Xb7sWf3dqlSj":197291026643,"U6lT4s3n4hsw":329793591944,"DVCH5c4bkhqM":848911706104,"pu43rzAiN232":370768628792,"wlF0Z9pAmGuA":345528233359,"8QsoCRjDIjup":929298749929,"ww1jnbivLYl1":541896179513,"FwinVr9KPyaE":206345204430,"QuxWPMDArZhe":634763619728,"oWumdwZubKRs":117753990112,"mHNOfMCjEBjE":115852758211,"WLBJGvV96p2i":204367301587,"rRlyHftSmxcq":452322387990,"Z8CIaC3sZQTy":39299323605,"HrrT9t61A711":836465254945,"s3VRS6fmFrQs":816270676581,"asxCXiATAyA9":560906872855,"ZN28Secjyb94":268738935858,"baRdaQ7UQQbD":101419493676,"VLFJiCvJahMV":146786772762,"rO9IPzbIjpVN":34760617435,"7kDJtfUVn4EH":162775221157,"gXC3pT97bWMr":511947099117,"JhSSRePLi6Kv":598464175266,"feWyIZBYQYkc":298486961401,"zUfp7tgOJWZH":393141410819,"uIHSJkhD4mWg":90382741519,"pF5984G9eIm4":602336082972,"g7g88AXnLsDc":131777210350,"OHg1k0tVsQen":379119217062,"JtLG6j1IKkSA":818139718576,"lxZXu14dztXR":562052499217,"zKBp8gsNnCnv":599889022847,"vJQBTmBhPrHG":840096519845,"p507VeZUQd9f":621285888589,"St9BD1FfCR3A":852932899880,"uPyhjbCo9bpj":728691722241,"SX39u5BCDzMM":254747131938,"bzeUNbCdTDTl":454030023666,"rGKpqXj1Ib6B":142423840425,"HHSc5EnJhyre":50178899778,"ftb4oQJz6vrk":263939622140,"fVt3rY6Ppa4F":754073109445,"Z1yNROmpaps2":84966176381,"fw9YAGcPuQXE":893406552277,"W0HnzOxDp56C":781642998016,"23HChBpQh7Dt":613275784219,"F2AakAPJW3Os":413979496522,"m5lGF3EwQJh7":500997601477,"KdKUe8Lsywtt":729856372168,"izYEYDDoJiXm":531693642202,"bvhFhYzUGb5L":920016535191,"d0keOXh8OsfY":725884983802,"5ud1ff8nm3WL":713889421236,"KAy5K1Yy7PMv":225581231966,"vGx5JmnkyYcs":302917542996,"4bs6ebPxI6jI":258618424059,"YQZHLW2fzlsK":779808203723,"UO0nvRBHmlHW":382477282766,"ysqXrA65Gsyc":806613986417,"yVdW4IN9xdDU":555480599081,"K24sFc7t2Ua5":78843700906,"NU0B0SrgmwUX":414806300144,"J5o1fs3Pwhak":779846519750,"ghALU2Y5wL2T":870723395217,"N4CBkZDajNP1":508246342918,"9KSB5kY3ccfm":424779293920,"TtqaFF57PYRV":362073325247,"Q32K2CfJuf4L":667016816345,"lP2PxTYh3zX8":819963193922,"xvvY1rYmuM5x":763597118349,"TGl1FnK9wQm0":390788199346,"r3aXaBrDCODm":923278053564,"Hb0SYHGleQ1N":212562547612,"r7cYiLBiamyi":338509879391,"Vo0vFd5PHQLg":532653530447,"jsGjkhAZ2U2N":966769657383,"F9r1ZeirqKiE":434844484628,"r4d4MrLSjJZv":288766958196,"WAaZ3m6Q9yC4":134879443250,"jMKLc5osOQBw":310424660623,"QA5Xj3vz5w4Y":819132549179,"DFRSNNFgiyPu":316967649808,"wQi4fuWPFZw8":975994959978,"V3Gt4U4NwnhR":392895503051,"YIp5uwVtfyid":297203407276,"LgSBUhat6H8f":59789903100,"PjyokmsdCIu3":789088901795,"te3dL41O0zbD":907483955860,"10F0r6ywXb7i":353158832795,"0aPud4z3pH36":882766823198,"XQeB2OPtMu9x":294022004408,"9BTnQtBzhq7I":18389048344,"MJPuusRAC5WO":792204186611,"KIRi2jmoDBBU":440677911532,"TmhsrtmUU2P3":932149312011,"3OPjfJnNiL8j":102607704829,"voUi9n5kfcQr":730071396781,"TFZsuYSs2ZuE":881599927007,"ea67rhN1Qu1c":485561469962,"fjYIuLksxCpz":517099711323,"C1bo9rEGVVNT":476082862422,"zWiQL9aFYJ88":757364554864,"nGj3TT9TpOnC":482719961182,"65lQGMnwQf29":385988545637,"NkOW9RQ1sJFR":481233057578,"viPHYijq5Mxj":280724557835,"Mku04WlLd8Ac":12328091863,"qncwU5Z9EARp":307665222440,"xyF4LAKg5PRB":273485472232,"ws3cH3lLzBNo":578092407086,"zA4ltGY1GFEe":117826985576,"UYSxrs1bX3cO":526273909075,"hq9fID0Fa2RM":443855924066,"CVOgUUan8Jw3":275920914301,"tyUWOUfeZCOm":715201104929,"fThOYNwcB2gv":927948869786,"FoRzqVXt6wPV":325943104166,"fY5kU94wD2mT":223763315321,"66UgzC3l2GlB":956659137773,"Kq9wO7QM0832":671009515334,"Eyk0qgvO65Sz":601765632288,"10PjMxtsGj9W":642877382629,"PxTUt8ZNmz3C":304234716361,"3UPkcoPpoEkE":531432266143,"5yLvzdBtmYmM":449876395126,"2wtdvK8hunSm":945010343222,"9Whlq7SncdDn":18285866494,"qQB5beE2gqEx":911067089962,"KBxKBSMr37fn":288310889923,"RwutfOYMNy6S":763101647518,"2eOfuFVUtGbH":619464026627,"Ov540rD8jvM9":585529527454,"D5npblJs68Gb":531490641284,"SOzB1i9YSLrv":833156802994,"MPMtQ0asDl4O":825504038357,"MVW9ObEyUAPh":401033923239,"FdsDNuSRb52i":504786117817,"qpqgaHqgUVQ2":346333562683,"C7LKhFeRylQc":452288392528,"5ijXokPvNBU9":97563408001,"lItpQFsfhPVy":316972775701,"QlageGQbA0UP":169966226161,"m8zddeEB1EPd":836552938442,"NzHSVLyZPLM6":132829922358,"IfAegel3LPxW":869470181839,"vQAYWdRr4zw6":765064143096,"GoTHVYRiKkGs":675671997965,"INuZn6XsWhxp":724807468303,"6LROdCs6pyyB":676075111742,"9eyf5AJHg3Ft":257982586638,"dC9RJV6JiXJV":99981785347,"h6qVhtGmohbQ":176736208117,"CkNhfPBbnZWj":9482486777,"T9SsnG4NFeAa":764930927056,"CsQym8hIsHdr":167251712712,"pVEH7b33au47":903030117410,"bGOsk95rB2CC":947954331642,"lHmtbmBTOUD9":650534426222,"LMySq5qbZRhc":182984407125,"yLRUI1jkrPDw":559714658353,"or89BSECNXPo":302797877098,"X7EFYfZZ3xDU":254688096041,"3GZ4afN9OIhS":39492416525,"Dy98bKOBtK8l":280799519870,"bRnM4LTG80He":931903836176,"19ziFCLwEieT":554825327464,"gOB10jpB3AWk":701158629323,"8wBwJQ1Dfev4":565589309480,"ANl9beBZUSKp":100847319631,"RCOZJoD2Amu9":168278953500,"U23ouJ5Wp3iV":212970594014,"39Qood3mjfB1":813283101249,"kSIGD5O9sHG1":41918318736,"u30rk7YRdzxu":246344155970,"PzUycJ3DsLs0":58884092918,"LC1WIUWcMw18":777604422793,"NKC1RUyJ2IHs":5837565786,"8QUmzfVuLWU0":92768866752,"dzh3OYxYe1cq":627802196691,"3GyEYLonAv3C":688959358748,"6YEk8ADGryQb":821843002849,"g6oXzxTeIpGD":844318642798,"T0VObWQQCj5c":726660775799,"bjoORHR7D0WE":313781466178,"rbPGEkbK03ex":92104177426,"ZwQNDf6NfRKf":462411751959,"4pB9FiIxTrq6":956681827125,"DSNeE0bt1L0o":326242538542,"kSXtFMdrWnhN":932645139284,"87PWRAEMNr0B":496596859972,"eldeAfBExvQx":357817233017,"ymZECHf7iN2i":173743622677,"s8jvvmYfmSqs":659164627834,"vefiQsQQEPlw":743875340750,"1dFCrYWZZPBn":165100824102,"WefOVJaFJDcH":520656495633,"FD0UqZGZeE8X":750581342675,"ahDICu8KtaD7":101364828037,"SKi5SBUwXCnc":707267215061,"6RvvB2UDcOxh":15132046954,"zJkQ9yOy0R7x":566632007679,"NveZDoeERY6N":716669431329,"HKdW1KqqgVom":819971130576,"sQyPcJVPsZp9":771304035578,"jv2nZaiw4cjv":66347362267,"3dBxZSsOBY12":834135055997,"oFUawkKJmM0Z":189735225339,"Qa8htUR2EMlN":398908815441,"tmEctDs052tc":77003792902,"tAOzcYpzlImJ":636717342153,"bR3UDRQuENlW":715463621349,"9uY5HbT9JdL1":545758181175,"osiyQJqEzuBB":37155913812,"5oj3TheYXnvu":108469892068,"gDkgcqYPCptT":955623358092,"xaQj5pvSlffl":460544601160,"XUvmoXw2inQy":130257502553,"radxFpLJlNnx":139344519859,"M0H3WcKU1B8w":344071329948,"smVKmZ2eafGv":711576808015,"yY57kopBEn64":685069637940,"vGL6rWeoiLck":865202352437,"gReNHihasBzD":875028251379,"q9TdS1mvnpLt":391175892241,"n9uolHSAEMqv":169298264161,"DYONHR6B91uz":259795210731,"iT24IrEl8Esd":652927241287,"XR2IBIzlWKQK":18114499441,"AsceaCQGbxyA":105692239812,"NgPHAtzfRwRo":928264675019,"uLQc6tassnIn":723059523728,"VQke6hfq8urU":437960686560,"Pt8CQg1GzYmJ":770071417544,"jar44on7di8T":493504575634,"owETcAcBAbSk":164446097724,"4vtqa71OQQw8":246446824193,"LIY76hKR1bh7":684596642355,"e3dm25hCZ1U7":29625504958,"l084zB0OCjks":366401089101,"w5cuQTVOfvpG":124824560135,"oP1dtMz7kVC0":897386113597,"t5xSEE9g0tQR":263820900266,"qEOkGncO2LIL":294293522295,"damnnYrtd2Xj":235385916974,"Xv6Gfs4Gzibz":688500080902,"Bj94kIkjZoWd":816543787316,"QEWcX3UWe0Gr":981780563198,"RIyxJBpVVDyj":755459064369,"8xe0WgmRt7wr":568365660728,"LkYtpfcXLTgm":838917961495,"GD8vRv1tcAFV":231788499179,"ANj0Kb5zlGP3":84309317285,"5QB7xuPVZ0YY":388418128243,"JKGX4dAet80l":700672388721,"kAJsw5b90wb1":176047108858,"xMYdo1Hfjzt0":729844964440,"mFFyznig2aYk":181632229969,"piHFBBINaAkt":400155654139,"wq6vOLtDFEtO":755427649320,"KnUBQzdmGwFc":463803899575,"NzSsLZphRJrP":812778258174,"qO10D5CsLjok":197898443160,"J8btc5hcQEnv":536564621264,"BavjeC0el1YV":351535053409,"L0WzMEu6sTJm":153106630135,"zWS5CIwo4bin":545455392759,"VfJM45c8fFJI":902685671787,"VPUADJJscEYI":59346981919,"C70iFAQzBfIK":424486871000,"15YCDmbPBCFu":279154929511,"QbKKCXaQutKe":670283015435,"vDW8fmIwNkA6":750568989888,"9Z9tONZTiu44":278836104127,"GY4amAQkZmMo":192737564039,"Fn4RrYtPBNm8":301727226825,"pdxRkhnwh3p2":752088786623,"vycxyYuxVx59":47874455655,"uO3f4Xg3BHPI":962450035848,"oKcgq6w55suh":845353601456,"euEw7Jixh6x4":780190449098,"Qdw47kgLamv2":131710047024,"ncy0wgSI3Gi7":618846683503,"XAdoMXKeNsEH":171795020771,"DUMxqnt6uimz":389624074000,"7IV0LWp1W5AQ":699692024694,"xQ7dwY5kFjEe":716854671734,"Byvyj4YHIj9b":626105448693,"9CxYfb6GgE5W":400290049079,"0ZSCyQqi8CPB":882415132514,"7vqgPtLE6enG":399974549502,"9d6mgldZpQLG":166007554426,"BKTD59K22UYY":637803038956,"IZaadinCItFo":344483313840,"YVpS5Q6pVncj":219966297447,"0rHF4Rpfpa42":849598522967,"jqk1TpICUWxU":600500326996,"cm4zM6Cq12nz":276926061101,"ngz3bC01pPhk":874580186748,"sw8dnSDvRi07":725425038223,"pZTSEr64KPz7":96925155811,"nIseVbdvzovp":710447586632,"7Q9oyA9JyPc1":886629320515,"1gPUtXqahzGZ":263856290952,"yCt8bVi1xwgX":207630658917,"sfEHa8wNCRcS":178410812921,"pi3btOF5LL2m":623770655072,"MbmKLBp9dqiY":935486058851,"nn84vCIKcMqY":493120576006,"MZvHTHuVVc6y":736364963778,"WnTycQ7RBdwa":922376737212,"9ZTFWjdcRa9o":425093652056,"Ry9NMMoUKVwL":960425536618,"NktxXrsrV4BZ":297930098907,"pSK1zY1QvsXQ":49708132221,"ExLcXdZRqLmO":438898781191,"OJfFJLnenTnr":783483263556,"6DQsmiq4uv18":492799122989,"WORniJwx5XnY":795739634850,"9mJyilvKSkPM":279932732119,"i93SYcVs3S6P":82452659899,"hxxQR1QtUhV9":506278747749,"xPCKcWYY2DmC":64019787476,"HdAx6NGLMc91":381858276204,"FdjMhFaJowqH":900168196950,"iqhYd28FqA1X":796633371648,"EfIRcZcJANSB":832722048435,"H7l1L0FHjnon":146191930153,"08VeJe8fbOqf":692596255588,"YBo7qSr5Bgwq":661120161359,"roclDwkkiTWu":631146689684,"VO777pLDEILB":141331757961,"GgvEr9dDVbJ4":43636257683,"vleG7PMYz2W0":400186455952,"7HNgLlJcZJTI":807466793462,"blFTY1bjJKyu":356148134779,"C8NYwo1LQydk":918551741033,"CaGT22vkqfAb":747091675718,"t61e7URJzam2":224961593198,"G2qITv0SuQTe":592703918442,"sy225mTkORbO":996851963011,"2qM6byPYx5WR":708291306988,"L7NJDLWrIkrf":622784363368,"Xknuju43Vy1h":494510219921,"ta7RLFQ2zOWb":995603322726,"wm5EzSLvo0XT":507997353210,"7e2WiolZjYQN":913986529067,"rkwHMCOgipIU":422062022552,"T3A8akvUfX5W":963632808838,"y1zSLDhUqPpt":422480496280,"6hnwZfPsUz6s":294241715021,"i4m2jdiUxxid":913558261369,"jx6hkVYvm5p6":395730285704,"RQeuBeyx3S6M":181117605718,"Vf2G0x07du92":868301026402,"sigZke8OukQh":963383849768,"JM6F9mhOOITL":356157784013,"rA0nfQ7r1XG0":283343445021,"tUk7E2hG6EPr":422196276309,"ZA7NZsZJB1kg":914185771927,"OwSnQKin9SIh":502026371544,"cWIOsuLPjlm9":826670651933,"0vdfG02KQQfz":30595786813,"PmdvKHX4S5Wc":359198867631,"DEgBMZpjhc1l":594784202803,"F9b2KC1MoCXF":536500695340,"sLiWApRKxKH5":24732367284,"TGlifDbswCW3":453964121385,"PWK6z8mXtaCF":353311820551,"QmnNNw2ux51o":786263642037,"cSn7Bkfp20Bg":275316606161,"c42bcsXl1PZW":263816013824,"EBBhJeAP27Ma":911458000411,"oWFi2gqPgB7m":535682132848,"ypmFXDLpKwrK":778468995796,"n1VvrlOHbSGH":836656334505,"hb6lYT8Ohvud":935253478227,"SXGfwDXtQu01":297644952554,"U3jyrB3P4WxU":268440768484,"kcfndCufRgse":940430689008,"nJtEHOEKAa7v":273687894352,"mTol5eIRF7pF":165699786573,"n4RxuF5mouF3":328918991276,"wAohuKajGPvG":173693525230,"v6lUzfUIDk57":698236513214,"zUEr1S6ZYQfV":116576801349,"MSMu26yPATLh":418231695055,"TGS63AnUqAOK":75043263102,"hbEYolwCiCTM":696125848214,"EDoXQttG43X8":994347906911,"gNY8uW4DhSus":891910789445,"v7bQDsouTUrW":225889036168,"QBF9kH4jwSwE":907179342931,"n6HZ2EmRvIXP":776410849140,"TZSByh1sPFfZ":612036416656,"c7Y2tyFlj9mH":668707778609,"TEfX2bUjTmbq":573950115758,"CRZbFrYybKTL":574079343349,"nLxw4KjJblur":418216556309,"ejOPafPZrXtx":471439238438,"f1kXlIgjhqAj":38103144052,"RxtDJq5CzS5l":205131324820,"QIfRVQnLfbmi":831797673719,"u4hwPWj5y4C4":860998954454,"T1jL4Fon41y6":187301231215,"ItLXFIdPec1m":564834379552,"2Ivq9ObPWCPt":507345184679,"7ywpaQJs3axB":157262078775,"o1hw95tTecxF":365969648940,"AOit8OM8O1hN":594549576192,"bHbO8d5vpbou":778853775896,"15mLQdjcOt0K":629475357524,"MfP7P5apYA7Q":328792955087,"Hrrdfiz3KBGF":490566933643,"13zk2BKV64OT":978652826459,"siy6m83HGkuF":894109090782,"EuQE4nC5HoIG":829857242934,"QkVvia4twGCa":424502019619,"Nndi7JDbdrr4":566983650033,"rmjPK1OddTXs":389361386768,"PD9OeuvimthW":856033470108,"rqSHPdxfiSxF":334305378699,"5g3xkvyhSzUd":333344948314,"3blMfdfRp7o8":590429812802,"oGBSPBNhxiO3":116650678710,"pKeCjVZIWDGX":86142870971,"w28c3wHAY9mn":601112176716,"cg4W6rjiQu3Y":407904261211,"Olxz4YYAFZzc":334024182068,"cas3fAZIF78T":610325292706,"Oi0cW0HY0fsK":745574723090,"NBbpb2hVGq7d":134367467597,"hoMCHEr5d5gz":90486239025,"pCTjHUgLKSVG":643675133861,"xL5HTgHh0VhK":3744448314,"RfSxBn7xIkv3":502962251130,"KbiNOnCJYZ0S":390949295437,"gF2LTzmGuAHP":732897089505,"RG9rqrrVMCff":134362416912,"R9twaqC8y6B1":700532682272,"JgMfxn8augg6":175740721375,"raoqvMRHKU3y":777751786998,"E33rX9Yj5rXK":837256205132,"2P2bjno53FAF":218874711915,"xHgxz2Ny0css":625738603911,"pBR3YUHK3n8M":888279678972,"h30o7zxKOTFP":709510878912,"xneGLwl1JEtp":316189523856,"1PqlCSfQH4Hy":441632964177,"kQA8CApVxLI7":761750698764,"bfTo9kl07cXM":589457974515,"PPaTibP68hqU":307035693069,"aQzGu0junutV":532836875853,"ZHqDVri5HSvT":698856536169,"y3KJbN99cFug":761995829598,"v8bmlzClP6JT":183957710377,"jHe4dbtbFH5w":34080663453,"5TiwQUIsTpb2":629248220325,"uL9Lg2zWnFEl":511222726195,"gcqAmGAFLPoO":47739238315,"qNjWuj2Nsx94":555213001004,"KuMRlGTi1m9R":205043666687,"UWuC2FThVgzk":521297375821,"eN4oXUW0CUQW":149190414101,"39vfmVzjVEnb":152577995117,"9FNvuLvFt04X":587942799028,"1jxIxAaTNJlH":655980930166,"DyRHeVGONLrN":130210050862,"KJbVETYImyjz":866862747976,"BLEk7ATHfJeE":570057228303,"iNIuzp4EIWeg":223559099924,"iTuYWG9doXEy":255353706380,"V9THlm02yocN":865548558367,"WFyaNtcHxVhh":494012932434,"24abm0k4e9hg":448502874759,"B1N6IXlJR6he":688007900472,"e4vFalxkeYsO":548456014568,"s3uGwC6aGbdH":477952257089,"krNGy1VkG3EC":59201309331,"mr6SjJH2Y6e1":41879174956,"87ydJHXPhkCS":431385228545,"ZQ77Yv35nz4L":307987949649,"EfTdJlqs5sbi":724312387677,"wABft0D764Fq":622810804141,"1rOmvBLino8t":518303623721,"Ohebi6HZd8UK":610936671364,"e5bg5AtpmjYx":610597786737,"LO8nauCnn8MP":455127212884,"1BLZAFYRrCJV":198470849014,"CoHjeBfbRvH1":540436544159,"BUe3F7Ul7EMy":895374435901,"1d6dsi7PxGY3":334387477594,"EUyp7jizfu6y":661728440288,"tlP140VcGjtX":7039236538,"cc5w4hmkGoE0":550230916617,"tNaCAxlbEvXL":53570065250,"X0E77AY1EN80":688639596231,"1jxIMCOPcLZN":432011977332,"3y1p7VhlhqES":821653635211,"ke1osQ0NGb09":556867772611,"iworCJnVzLxj":202881099184,"KIPcv8DiOrT3":375403371244,"ilY4hczaUNwV":902244745555,"x1kMm90766Yo":412859440743,"fGuLd6ggAopm":970851516022,"Ijcs0dlmNR7u":463257245621,"ZgHIr3TCdJar":523363667741,"6bNdejMq7oaj":696193408908,"aOgB6OP4Go2Z":796240391738,"1l3gqDKu1imC":489345792018,"nVyRnr8sV5FQ":999820922483,"JExq1KrQyB6u":554286102108,"sVFoB5TsqVkW":85685298386,"MGwQ4ioRIgXl":605885342628,"YXs9jV1boBfp":289016725459,"8ttVTi2k4B9B":580329097161,"aCzr7ThCZJvv":423399113483,"KiINIIathfLR":41695733169,"CdcVgizPjCWa":564222721979,"OHwOCyZr5ja3":790869908116,"zXjVuREOqDoV":335000637065,"S4ucqvIUGL9q":265716505783,"2sHQscdXxr4t":325264873931,"JZmxaTpJQa2O":726709241200,"MkaA1lStceMi":539217533490,"BVml0FBrLSuz":362739706923,"mLxaCqui0OyS":117102635120,"isIDIv8hHRE8":118743296970,"GyFniMgevBds":321794415761,"GTf9IA8GfZwJ":270146405516,"ViiGuCU5LR6J":402521228798,"JLlOP4UrYTCA":350036725448,"FByYHp3bwkbo":859929047853,"zOEBO0b3rjEX":68473484201,"EJFA6s1AXgxy":674405721236,"VF21cW44Gleh":845298428430,"H88t22fg369g":700917441194,"C5wPNrBcCo2h":506580193758,"9CJD2lKSLtOJ":944212299753,"6xK5IqSuoo6t":762699315636,"hMUpnSjNQ7a8":437451085114,"gCxQfC5OvaDh":254352834828,"9K4bupWaoSD6":926840369302,"0E7LCuobf5CP":692934186956,"jk66e2G0cB9o":371472592906,"CRNc5Osa7rRK":580823733739,"qckCqTH8FjLD":105824213987,"InTeyshpglYw":894711715502,"gJf2o7wrT6wN":953970476640,"Jad6ZwzTtxx8":479889833386,"emdOMl42u4gX":530240970108,"aHN7TQrXxETR":533021296036,"YjVmBapIY5fU":157870071134,"6SlOPrXP5O2y":854368976933,"FJ0fzw9qcyDa":465673784473,"KY0Kwws3gjwF":250907464157,"pz2vbd2fG26A":711031183450,"hFxeAzPInoZG":697112982719,"HOzpz6a9lyv6":340892021746,"eRlcZbUK15AP":138241409977,"TqmV0xrmwqFs":115664471432,"1XWV3muYR0fR":332595088623,"Dyrbt79uEMPk":713321721413,"mL0am8N3hFDu":116889799412,"b3zqER3izfzn":390954363570,"f6zjJiuNwn4P":752210244063,"LNi55LOuzmno":896642117364,"nyV3fd9PGh2N":640441411369,"PSat9EgIgeMw":133360238713,"rO8e8UgbQdtf":259761528958,"8okDkhPxWlA4":731539471306,"ljxacEOUc4HT":209780419199,"9gXr4uzU1wp3":295095342883,"z3BbDmVYQhLp":153338782152,"yZUr0GNtupy7":881544577569,"6reYEGJg5mai":333074299520,"MDcr6DdT7mm9":965620355634,"fbhox4ZFPgNn":785415638975,"OxtYipRp2yK6":529270714821,"1WfT1tMpAVrA":723143790464,"RYR23kVqild6":596198071220,"bLq4EI3APBwG":396066691302,"st71VTSQxmyj":841967650006,"yumGCGRCGuy8":811936187990,"EdOEN1QnJGnF":401292470733,"6ZSdpEEWbU2h":854285413139,"TmjeDzxlTorb":342014817397,"wJYceTU8lnGT":11775293498,"7apvpPCnwVnk":328944092758,"bry6dyTTl5fu":303944755606,"hYCQTBGz9oWj":193261338998,"YgolrGVds4vj":986911611901,"P26aQAK7MN3Z":881092790974,"HiBW615GAxke":440305025201,"bMFh1ktpdlRD":336914889052,"XyzgOlcihfJl":189092411304,"DvqoPcxPJXBr":826856313721,"xASFvAuJ3oDD":73302650824,"du6PWpxxHTaq":540931535236,"MCUJE4ZPQMVL":915795469650,"2rlIEPj57h5L":564629016086,"a4thRKTRS4ud":756732065417,"zWBFXJzB25Eb":303285947148,"HFqS1s6hALNB":612792199572,"QMBYlX9MPItS":687075370190,"fDz2OmNFUJoA":864262384300,"z34kJrhOOnRo":359643910876,"ocaEcBvrJ53t":543789565915,"QQVcXCl5G9C7":726865400807,"mL0qKUBzlTY6":726071551212,"wPrpAvFhN2Dv":254230571710,"IHbROnx6ivI7":593929079326,"rUpTZSJWDPV0":498648220562,"w2lpAPIKAbJu":325400974851,"RIkldsG50rq4":808188557856,"7CCmL0KAfjTw":986355026001,"8fKkQJm5bLZt":745569948679,"DzaprHpfOtju":497007544575,"Lx705hstOc0U":646807351554,"MhYHMc0XqasE":409738935984,"7obZbsv4VXHR":553019586811,"34Vdv9IQeg8U":10109994279,"4F2ctszOz7rf":911489468547,"wvaLVWj1pBE6":135052410525,"8T07e385A6o9":170045370047,"8bboAa9OOTb7":668644827049,"XU0NHhvFfsH8":728487073514,"1xNb32MJMQnX":889552368543,"5l5XXWmUdWdv":320798601922,"4uhyUWniYleq":840221364069,"4UWHqyJSQUHX":481504878028,"2J9nYXaORtKj":943694237001,"G8bi2gpfRZ7P":995866933816,"aiodrUlGvw3x":453165268212,"QdWWV4hbG06O":726701713265,"x8JVys8cXRpI":565760132814,"8IyrqBay8a6D":453982145042,"BpxTAaa8xhoe":719516114192,"ljFy7QsCvywE":701532381688,"t1Lwm12zddqZ":980014081741,"fgmBuQRz46t9":466270217235,"VgwtJ2hvb9CG":684972234093,"bATKHPi3hK9r":825209037337,"hzvnQ9QycP7x":295319182341,"rnQsaBF8qjwQ":565824091459,"tJnrXiNn57Nd":270135129270,"1YwWyHeelFJc":155616963770,"Za6qT5HK9cSe":610854149724,"Dq1zYJ1uDcIu":950861752798,"sASjqs02mWWa":810541359421,"fewulaWZ58Dm":771928923701,"EMu3ZcNEGVfz":496454196624,"iPPA4rDn037a":278776749489,"zOi7FJmK0jKF":697945131561,"LkzR9BQk6ej7":552704674980,"JZmP4y7ZaXD7":670148331499,"7D9wrUwQgiWE":565410795624,"8WZORoKapjTw":131965998454,"7OyBZBHqfa0d":376429491644,"U4T9Diuq1ond":965899713642,"pwe7Th4BXTRD":40909239738,"f3ME6KwWa5PL":183372035808,"YP1nBUsDlXPE":307643410755,"GixZuDHNcU0u":373787036912,"nwGS5inAh1oo":531959894796,"txqr7OCi2LxS":193341401654,"E79ebvRKNrfm":925353640144,"KDWBQEF5rYBC":560597733232,"Cw7N56MPIdrl":133902323888,"MkAodfkpQi5v":78817083004,"ek9ZDkyVENzN":108509950629,"GlZyGYyTNPfk":795371733120,"aDPnUIWObtMe":481035668202,"7erPqOT8RdUM":554469598215,"9MgDFnqm5gQf":34612052828,"tg9CVHnAumPy":672848567676,"UfQOhX07K4Vc":114095431372,"Hqi09T70WwAr":791804553894,"PEskHfiebFfl":298018649718,"qAxFdbqXwIf1":993496466511,"koTKqirs9ND3":283508114729,"Xpkmk1LnKTQ1":830819408327,"T2mlbsSyyh06":631915245569,"1jRxMPuUlAER":963066736081,"87BdtS4bnl4v":539565545847,"9sa4RyrMtDV7":851942450334,"Bbf5MEc9nV2f":883203292291,"9Gy4DWx3F6vX":96345840871,"d15uio7FCWrl":912867638853,"VzqaF95farLZ":505923920749,"0l04b5mHpnUv":231462139566,"oCuap73BZild":299961525154,"8OebZGkUUVCh":230622653905,"f6QcDgQ9zsd7":622812704645,"77iGxSRKzUKd":652458078823,"nDdq8lo41DrQ":890597495494,"IhndPeR0VFgf":315038695881,"BI2lIKLQNSM0":80564049132,"3DmOx2eIA4OV":379194152117,"RCdbIwrDeIvr":381687358164,"mlp73RGJjFby":233537631446,"bkohzx5aQR4m":495061253361,"R1P6ZRTDnv9C":724515668237,"U42sI7ufG2S4":412307546238,"gmqJgRjWCQmf":578998782639,"HUAncHuUIFOR":398290609132,"d3JmGAymYFqx":903104298457,"HcgUjZxDtwMO":594234254,"7aHtgVPJfH8v":539223581447,"H7QERv0qjwOW":601666995897,"HC55LOdj6gpi":56897116163,"jo9IZWbqBaph":138907070227,"0no7GtNBYtbf":376590408267,"bcSZHqDW4WHb":689456232887,"eMFR59MgVBgC":944599965409,"Yy3k90A6tY4x":519961436933,"HGHSlPZlf6R9":250947082427,"WurAyHQjaXU1":8340069561,"ZpEWvLQoCPl1":267872628245,"Bp8MHPZprstW":119826188406,"9j6DXwjxB14Q":767110833967,"bqEZjCCDCp5h":542441701989,"0oPa0TKR3nRM":184323557009,"X6D09aa6zqFx":417264318602,"HQIVOHg69YJI":595729933067,"0C3NXoWcZbgE":437079350393,"133ppoR7n1Hm":426995309161,"TNdyBsHNqeP6":475990415317,"iFVs9mmOdr5O":97436530547,"0XYf5LAMONcY":947377192495,"Ue5IczYOiTpg":481300197601,"5mhdm6n5dGAW":486497584229,"50co9hKmcCRA":973031159513,"MfTZxJX5aYCP":174778987934,"9pqAbO1v5JxS":732121484373,"aMVSU7BZvPDS":943013392195,"NUrxsIoHw7Yd":925717373321,"JrgaNzKkG4R8":411558859746,"BvLiQP3HH2bJ":251169252487,"osusAdtDQWfF":797942499424,"LkHC4k8EcGZI":525050000986,"W8WGUBKVREKr":679618337880,"6d5uoRePd9Bb":859298162866,"GhLFaMaEi0Qg":478800552497,"EaOeZ2pxiTnM":403048854898,"t7YmNmQYHFKM":750807405168,"OK3AREtP6z7E":402741850833,"6HvkNACeIuaa":11375253015,"QV7P2Ebc92j9":372067900120,"OCXgyxSP55kW":603164418453,"p3398Rs7vLR4":976354827032,"292X46x0bcYT":76655788333,"sRm1Vhtyt240":525134208193,"C2CPKjEvhrOF":465689083585,"JQl0HxX25RdM":433395827375,"dE1wxOnI29pm":207816118656,"AhCoPHMx738B":93948778999,"CZQsy0YPQlP4":484287256341,"iY5YU1OZB4y1":744011199851,"7nJeYYHjJ3Ps":723174600228,"UFmT5yqECzqR":648711580536,"nPpLdOoNrlvh":635957107661,"9JIuG3RFW6xK":247388654491,"R9FTGN8RWUGN":391161564896,"jjxvMRrml7bv":582437428387,"UosVwr6OU0xV":77333678125,"VErKSS71pty7":350043613396,"uYBOauMHkwEX":822584007550,"4xyRDWm00Nag":184321725948,"nkbC8Lo8m2tz":679546344631,"mPQWql8IX6Wb":579338811120,"c2QogwlOrBOb":978771013603,"9wmvY62qJzEP":563414873241,"qp6ecS8c3sy4":21266555443,"zRJuNhmCMVzE":969755096864,"1u7icKHFNhmn":744161916429,"INxhEGckIs5p":600379356767,"jRzFZpXUNb0d":589462751731,"V10Hl0onoBC1":445604058140,"0YxbcTOzIZ2T":698038663432,"p9MxfAkwuyem":677129544818,"nDTHAYMQwVbq":548346786555,"GrPBLxuDZF6h":49599431218,"TgDdZSnz0PkY":567757213659,"1bE6K8n5CbIU":629161800769,"G8JbYuwsfdGH":761763638856,"mO9bGVF0cY5A":929784430699,"vEhu7oyistlx":186435692141,"eYUzBQNu0bnm":840070502455,"wbAqCAXFXJCQ":690742896956,"nHMo4KIC4oB6":447989576068,"vCw1vQDyw9ef":190174856315,"BB0zROtcxn2W":994334324266,"a1tFFo7DASxp":906845247617,"G6tlLKM0d2vD":422463273450,"pW17OtvqoRBx":595865011073,"oe7Qpu8foXz8":928436554353,"LkSm5zaECgdI":258269274187,"UlprxJHTad87":860512420552,"R2PCPG2fMpIv":555944792878,"kxyDczjWIKPa":266011660502,"rZ5scXyHD79O":305983126275,"rmb6oAvDYade":953068609429,"DpJ4fHwRaaBC":581344646988,"cYcInDA37YbQ":737470202792,"ItcDZsVkVK3n":758125702002,"Tte98ba0zsqN":754930022662,"h3J48lpeP3hV":737909047707,"D0F7NKx5TLrG":834424539639,"zz0h6C6evGeq":567620299109,"LA1rBehlSOMl":669087106279,"eRj4qwJHFZNc":859930825848,"SWdiW4RxKgRn":728168328070,"t3x4R6VatynP":205942869820,"mlecWjZPRzLC":955463812381,"xus4TJFDRAGb":788955347649,"VwYugJkUPLTW":745549031717,"UhabHJqzfjhO":990895354688,"FbWnIJlLCsZk":587743782673,"GmgLClUm0lpV":274797261759,"xTAsIaPCiBam":968748720303,"B4NZgMhrbAVL":386023686985,"NkC4rPqGzIdM":393253522876,"DFj7qtlXyRkX":367989077918,"gRLlr94xNAVv":977980284943,"O0U5NMjyeibN":32543428910,"LcFh34WaM8TM":99094687837,"N1zu1snu8htE":160576758554,"6g83Gws7fRVE":447268287700,"kLu0BqyfnOVb":335995752508,"MUWTJgtXGjXm":543126234272,"2p2ECsg0O8jM":502615819781,"dSu0Fa3pNkSl":115974037828,"EWoeGOcZmYlc":42936386316,"4WYfQHuQNPcy":313711876428,"0ty21YLUqa52":716289603264,"cUeWD2D17O2Z":765249991866,"ckqH0BhKviy5":871394279877,"pyHbAz5XRs0U":276919162122,"1ZA9vxEq1xbu":897166232617,"pvXjIUykKGAC":62908804856,"g0iIa9PYdK6F":912427714339,"yc4o0UpShjZ3":22462461296,"Jfwl4MqNb5M6":708007139277,"mFaMdaDgqRmy":462008908152,"614x8KrdPnzP":429824480865,"kf6R4CZXVSJj":48302234975,"PxQKbVytzm9W":487834049273,"O4V9lqeXNyJC":935640504327,"0R7AOoeR7Txm":672874023024,"7RKmoPBY03RS":633409205413,"ttG4KyevxRhY":558979426001,"WmibS205pku7":254127309665,"bVP3kSdWX4m4":415796675836,"l9ieSA71tQpF":303645454465,"B3xM8tP4btKI":234282780377,"VlrkSgdooSnA":486496806624,"ipHxZ4AB1yGL":558437658838,"Cv9WOTKwQIDh":962347397293,"F41ZgFYNHixn":925713671560,"vWnPOx8P36ib":224112459532,"04RHs7v1qe7K":282723494442,"ipEqaBkgUfJi":932366531665,"52ZdFZfyrJON":645261362567,"8vgYEqT5HpfT":476066928823,"zp7zTmMzCvKf":905279943721,"uB3JQzdQioCZ":677114905173,"X9nFo1KpZ1CK":325232174849,"YwXbm8T6fLeG":947005526762,"3Eolb6X4mRzM":684885563417,"Pkn2GDJTaxTA":44058288839,"zNDkYj6qtA3L":704292041006,"g7n6MrXb2rNf":492515023997,"wvqZPXfbAGv6":384588717357,"9387ZT2usRRW":461503458586,"TgbBkP1iDbKJ":995482131255,"BVllBTYhhU5u":124193088310,"BxTrpjoy8XaB":882414740517,"whIbTBLrAbRp":961990735983,"wihKQVqjsNm0":15289911431,"AZKh4rrgccbC":496978281747,"cFqnPc96t7se":675784355765,"QUc1HGNxgDH6":969729590994,"AtgWGdvKuhIS":433560509005,"r4qWXIUGdiZG":298061055198,"EhKHQvro4dBv":983889549812,"2aLgqGzOhc1R":944282761708,"gHmZ5uEpQI4o":68566457671,"IgcPvyvlBTgS":844116675385,"Jp2q6epOsiqH":141347645343,"o4biNemVzAuh":177536676681,"76v5RDFFPefJ":530772125196,"dxhFSn3Nsfg9":779775238647,"Laj5p7Ncrqlv":825521006609,"asmeLdqpqd8G":633476524766,"gvIXpKCfU0wd":881481932807,"3Auaj1kzJZPM":868026474949,"jTNCJ7WWgdbL":69743714071,"9SQddM236HxG":210591059724,"xcVFryGXWJBl":852818934103,"6dmEmKfKzghX":411554251937,"vexhG3KiK0S6":265233059710,"orJQXqwOOzJ8":571160848119,"nI4D1v7YcfW3":42837123449,"P191OmZ49PI2":840962882736,"fMiT1BnCIiQF":856602023080,"fzCG9xPdesdK":854195731886,"sgJOBF4KcgL7":946421768037,"ziRtrl625Dy5":104058056752,"1buYtbAgpjnS":283656035321,"zrwF3Om1c783":137789348957,"5bZGKtJYEUjc":439439067168,"evRDbt1vLYkh":637352782490,"pVJaeueymYJa":790077575124,"KB5JY1HVxf1O":812295982180,"lg7TxZFW2XRe":693657477045,"49FGpktNEn54":509723451893,"IIFcK1LX0jba":30320627030,"xNUNZZdk0UTk":403451641405,"qieR3ZYTIXg9":929496945003,"sKp9OHfsCA3X":531984200379,"jJO9YavDi01X":349668479779,"4ZuJGdDlgwEH":525169416307,"1lLewIt6DHSH":378420292848,"K3ddseLfFHHl":858655204444,"d9gCoYGWS3HO":954140128839,"m6a3XhiHxmxC":171337658956,"XcpTm71x8JyX":370501431941,"nJUdIXvAreOa":315955859428,"s4aIID3SipHc":418283134794,"E877sglmGvTY":165401594180,"SZVQwTSQCisa":440903908892,"4PbhBnCju7uQ":974225664840,"U2rl18hiDdsP":66632635443,"8NEO0ngJTsC6":8775910414,"o1Hq9kAqHcps":261665650767,"5oGYg8366JL1":578370936196,"3kzNXkpD5zeX":452666365939,"SF6caRVp1F2w":46287569247,"Tn8EENnMimBa":972730472516,"XPYc6ZYCMqvQ":832422680947,"7KlnPixlI4En":109454871316,"ksI9BFvy6gkm":798253778411,"BSpfeAyrkE1a":311957597508,"LgQCA9Y33Rn5":276462701945,"gYYs3dV36QNQ":249391882116,"KxsuMFfSdQDw":712804250398,"OPolQWSHNSn6":496087884802,"irWvrSlIrpwj":430234436513,"vJbJESOvNAio":812045078404,"aonOYrLvXzNt":901179727228,"GeytzJ4BnbaM":215965426025,"nsynyINAAiIJ":746447161359,"XL4QuyxWfIYh":802884926424,"1qaOn863Jp0j":647525088863,"X77yEiOxSt0d":505947790843,"VTHBhKR6dYCg":835469484654,"Nuu8Lqjue311":714322396997,"hn6ByErHtS8E":948219571888,"UE6zVKBFzd2Y":522765394239,"DxchlPQANr3M":480047893559,"HCoiey4m47pv":290304734200,"u305UIV1sKtr":641440087418,"EJGUxcaqnEyF":470397838932,"xvx3llEKZiIT":129173786945,"ij1AWGSrBjfQ":429208637106,"5GQ00diYl148":982194862685,"yY4NKaimNubA":153777652132,"yP9YIkQIEiGM":186943127991,"AmoSAoBHUfdi":634671594794,"13Blc4KGGhRA":746034409617,"VSLxITMJVbSk":884142566398,"T7DlkC0d0VAO":860478066407,"5KlLG2rNCf5x":773492804365,"Fbwml1IQseTQ":744328969355,"XBDMJsrj03PJ":218529112669,"9XPUiX1xp245":959543794047,"9ENAGAM3JFBO":244076996259,"MED6SyH6o3E6":514042358944,"UgshYZsm6s9K":662428882116,"tghBkJcSoAI1":749962482987,"4kEyFupQFht6":477286000611,"lTD4MEH2U1RJ":877697808612,"0BXHSTqXljrg":197516833062,"Miy7Q20ycsEp":403004508941,"VfHJHkxZ2jtg":884298330467,"ED26OHU57g5x":915376777733,"EZE65SNkCGWM":751295466847,"kpry03fxFGla":333678725295,"8UtYufM1EOuE":215137846690,"9qQkSPYy0Af1":277476500955,"2tUpRQezknW5":909532911108,"WDbeTcSzOFEX":414760322473,"tnITDdiulXo2":643668922884,"qJKPePTQx2Pw":116276722747,"yb3DE67QC5HS":704302580999,"hMbbh2ex9rvQ":441934527572,"TiqLPcLyuAp1":655052523064,"YQ9WNWNJJzrM":952679143580,"aG7zYlgONf7h":72360661004,"g9jQ92bpf6rs":92560784972,"Z4noqQzhb7Fb":973003723253,"6emR0IFinGDk":562978426641,"hWNGiWYLhtdq":19402970590,"0vG7Nz5BAxN2":449703705897,"HP1KW7O7jU1L":386797803369,"5nIvZKAcsAnL":844072910700,"4dEHPr4AP0YH":593605884146,"lXWCiVINgPa4":290122500533,"5AJsoYQkdcWX":323291436054,"0ePpUjN4YWC5":338108048838,"IJYPV9K6P7Cq":820249282914,"7JB0gWLP2Iik":140197111856,"mDojvtGhkXUf":588380375425,"pjWXiiRVGz0h":623701681999,"VBAO3ZMU9MNr":159322491654,"tmXUOASnCV3v":855791252227,"1anVqyHhXhxT":93218270540,"9o4d965CwTDD":584985087441,"ZPo3NA7bJgcl":723962011306,"L9n4VTetNYMp":681306205226,"uf1BYAYIhmPh":630939325628,"mlDdpoLhpsRN":417909326807,"SnJoZyFzMWgZ":544162357814,"t8krbDmc44Dg":881766239414,"ZChmjKsZWTu5":102796378884,"k2QbrKcwwU3X":971087158603,"YtwUoP5QAmzA":547277970165,"H56ry730Lucw":251453780765,"9vdmtpwCemc6":769248332903,"SVQ1UAXIz72r":852121982793,"fZ41fiDO6vjj":274153991435,"wx8Oti7glfa8":836805698664,"sSZKFt0SVmDB":990873691993,"KykZcfd985E1":77670271735,"mPC2o9RffEyn":607262422541,"mQ7Uud6ODp3S":944745475413,"uZFNnzTC9Sz1":225505838476,"dylGmwnCSAkw":161298224785,"zmnaPOh0TvbZ":117265224349,"Yty48TQPyto7":115303579015,"sFVspHYLy296":537743206521,"UYIZB1RU7sjX":994453473061,"1PdcuuZVrHRN":830904595175,"pXxel07U1vrU":483300043378,"CphHzWGfm25Q":44310189859,"IR3g2ut61h44":773562491310,"QPlkVxYhsWYi":108937920388,"CcuWsQTkIq5G":455251421612,"w0ILKOxGBPJR":130146867503,"LXwb0wTcskpg":730717273719,"4TWo7qbA6XNA":567161427039,"3mzfYo7MXnZI":228329159404,"vK64QqTwNEcA":266936369923,"Dabp2RXma24l":362887106852,"Cg7CrPIssFw9":381048877718,"gdQdgCo2TBcC":366373647082,"ejbullJYn9VS":254002700201,"0mBzCt2CAUzn":262768821686,"ANpZvosxULe5":331959871607,"9Ya4MkiH8tOT":712228532441,"98Vebb2H4l59":806115224404,"KO7VhoBr9mNB":817152492226,"ihSYbOQZXDyA":722715184542,"UPx9vmPM2gBN":676293401450,"WjBumH9j8YKl":784303589550,"Wi2BaqvSRXju":86728548308,"QsKj3VPPfmwm":493439570839,"tTR4G2yY6W2n":536702808431,"pnUAor15zV9I":280897600654,"IiGGiuaYg9hq":592893167283,"5bBn0Z2lgl9a":385535195268,"vuqPyAcX0H7y":515507451854,"An9NTxmu4osO":326612776590,"IqG0Fplc84ec":970315586301,"OAwVmeivQ5n0":165785008272,"SnrD5xbnhggg":636910193274,"lMPBxuszu2Rz":378473272226,"b3eG56eNUcsp":339098664481,"U8qHJre8CseK":531836987119,"XuVCrfm5LB22":489633177940,"sr3sb6zewOhz":550957336158,"UqRxhSAHo0Rm":237918502647,"FtvTiWxW52XL":732190568898,"Uh8iMpuU9ey0":399881343919,"vgYo3ryPgLTx":189322881306,"E2HiO60TVZd4":704713261221,"GVWvkWlwigB3":676414769250,"prqTgAo8rYRh":740482649267,"4LRoAxNJKxTE":27492624717,"hpLFttagP1K0":130577735214,"oAcJGEdKfPSC":867355706034,"xMM8hG5F9GcJ":829664886279,"l7uglu0xoeIa":288275024259,"0kCYNfoqkuws":209393912026,"lCN4EE1gG5na":793436233146,"vPvxK1yiRwPf":377342635069,"LkxqB1Ugm3DK":791127551030,"R1E7kgJSgN4d":949270331299,"KtqgRLhAtsWn":861840145403,"0ZUluxtqpISm":933783274483,"oGU5goEUuzmO":398520294060,"7UsTxsQHqmSo":939836518963,"odyhbXyjQZIA":411199002734,"5WUAhZ7MNwld":716614172623,"BqxrH10ajwZS":990351327949,"12cfdMrUBTgd":40322570880,"qxEyoeJrzqLR":700932889593,"0fnwkNMDcU4t":844706529945,"eYcN4D4GipXk":843930012406,"c8rYVg95WyTO":696143674724,"m7sOa2LxRTYV":490395330378,"M7Ou3hf9Fw3J":506606298147,"DcuOEZjBAZgt":559035843930,"RzroQb8L9Hcb":135188400503,"6CXzM9hMNsa6":352340218655,"C43Khc7cHYLV":518891642835,"kPz3pVSs0ZeU":837597520912,"cDRq1BoVsmta":430304876944,"7IZbyaDkgCS0":994153689769,"frXaMUIMpjwY":561626610686,"Q0udRJJ2lvbX":630124139034,"lqT2sOmDx1R1":273485788755,"Yhe58N10pVnG":266612254599,"29QFw1x0icEa":627701868865,"yivqrddQllbF":725343826703,"dTYBxKbBak86":759728616840,"K0O3zHNaZ3c5":63443137451,"743Jjtxwjdh3":134217600465,"JbMwpztWG6gb":838289887937,"uXGgLsDoxlgm":126421626950,"ZtXgCZjOi50x":769563098281,"YJa8mTn07S5B":958941722903,"A1wSVkYJZ5UR":230386522895,"MtsQocS2wbJq":529850563470,"pomjp7EZ4Pp6":169663303283,"d0ZET0on0z7s":884771596948,"qF5S8bgOUZ5g":141439442207,"EvVuHD99tT8p":764478597777,"lPFLpyjmzpOX":39103144006,"qVMXGyH1qktH":311180888105,"KvzMe0RNxeeu":322879404320,"oKu5m7oE0lj2":414753594454,"9g5ITAwuYJxK":981199209883,"d3BLsQHiXpuc":254634973083,"cW38qufviT0I":199162101654,"eQj2EWGriEpM":168781771448,"j92Q5HMuKsHj":15793708650,"7cczdKFeDNKO":91613316642,"Kyj7PmSt9ZjE":759013589863,"08gzT20PKfIS":825199330815,"b66JVjxEp7HY":767851133021,"2BTt34Fl4vul":635333611429,"QaAXmt6pptD5":566439248751,"thQUHOMRVDF6":901136463750,"E1cXMNSFahPg":707960987881,"Grc69mFnkbwE":535165259819,"vqlgxF8PBFmQ":46879460898,"izNYvRnSGtos":242130129742,"yl5PBmy8sryG":520962420502,"g7fSberHkqCR":892194948283,"iiiFAAr830Qq":591260879884,"7gHYnAMNP8nE":434218372132,"JD6iXwDgsx7F":661137038491,"0pvJZnV99tAs":601173920603,"gGuW9q1ZDMSj":937345807971,"SVj2OyMkmXNY":611209510186,"7V1Zrt7jCVa5":518371108512,"wvN1GWmZga8w":772296493475,"Acf6vi3X3CM8":599553746742,"Lz8dDw0d6mfm":619256034573,"zNynmJMRR723":648280602641,"8Tx6CmDOSRHx":585722208248,"W2l7H5WSN5bx":593199249381,"TB7rPjOGqA7Z":45633265825,"93V5XbhsDsKG":752857268505,"03pUJ2IW8WN6":377303957730,"a4OxNqTjKxtC":790756850024,"tar1ejgCZsnr":287620091397,"JgsNRxvImFE7":24776067448,"fzlY73L2tWBE":517764145779,"gCk0rRzkiugg":189328404020,"ikS2HTKs27CH":914830827242,"P9ugig9YQ3pL":245028207853,"ps6ncFrSwr1Y":449506498226,"He1qW2inPGmt":178400645853,"D3XfThFPQcsx":235964953108,"Z2Phg1XrxQma":104246609659,"oeRHNugx1VYo":365142014431,"vO2FigdE0au1":520620467850,"7AWi3zoi9lrR":505459322165,"Qq5MRLeOmlsk":793816331044,"elQCAobVl9NB":254886271482,"52CIStHrnijh":644473980195,"jpQjXbX0gLSV":44177951925,"CX3Rq5h7hxIj":803198036665,"oiiLbJH99gf2":260336975554,"rCXi6zod1N94":329286051287,"x4jq3ashWvNY":10588024519,"ROWhSiGo0fou":171584877750,"UVBZVNuCFEJK":638521392876,"kJitCz7WwDR7":719292527576,"tAlMyYmAeCh7":632771229856,"GvhdcS1y29l3":276583879173,"A6OeOb9RXC48":598378301537,"ar3XzD0f966x":916545580065,"wkqmP2MmYeKZ":277404662808,"t2o7ufPRWcDc":703070570638,"9FxYwlMf5rBA":413079371057,"9rsBPaqpXTcP":249827150186,"TAHPCB89JD4X":928384592822,"31opV0nOzNjj":367621128152,"FugY3zvhKd3y":249315236141,"anSQua5C1ekU":40541225158,"QLWcQ2HLlNnV":439269401855,"KC7H0G8DeRdF":881881953441,"psxJ7POYyqyo":92213685980,"taJYN6PujmoN":396313281649,"Pb7yBS8zSeAH":418697205130,"awWadG3ptENS":650388264833,"EuyPfrecuoTM":83222144071,"Ixkpw91FvPS2":611867420470,"mnH2DnTBtpkm":893736324486,"JEBVIQd1caK2":228296883435,"GNCsPcveW3cG":92776449204,"IahxmBuEmxet":617505820279,"g1lpkPcrwIW3":160996446298,"USpdS7yYShWb":42244864350,"yLCUZNkyZ70z":468441254473,"LWD2M0XAJCBO":434202195273,"d6jFyasZman9":801613926418,"MhHbLCvT46ho":659068693343,"MujcJk1or3rI":133139805848,"z0rbLzhh9Zn2":299601811808,"59VpNXQ5Dn74":863399860421,"lFAoIKYvXnlq":651263485942,"lmazdUwT8WJi":156966704946,"gU7SQO8PDu0I":345690934050,"DXLsOH1YpMBy":24105553393,"1CbXdlPvvJwV":701159373619,"RBAxPBdEdYL2":15032647957,"H0y2cXkllrf0":602319501737,"9zLuwYRJWAJX":41855323823,"v3lKZW09XVCM":355276213306,"o9pKrxRMqvhw":390852069639,"TcdFO9hLBqZc":943043221357,"yAE24rJSfCq2":98284386747,"6w9uEGLrkuNS":352426638415,"Jlue5yKM1r4a":924120075797,"frsVWEhghT0D":128586946263,"EOfes5kDUNsO":921126590184,"Zlav5fNvGBwl":975989348204,"Y9w1vd5afUaQ":674233960380,"AHZiVfdky89x":973505965461,"lqv4qqrX0cFR":272461552984,"XP4TbYP5Iscj":713766572705,"pQYngfYqHWFw":975374697941,"H5vOp3Kdu6Vp":154536591139,"iN6qXGm2imsU":496311651110,"57kRDrc6Olz3":586775290204,"fLcFVh719Kaf":885767163268,"MQb9K5gETPdg":845467594011,"ZjOaZhaH5o5Q":631322065631,"HcffZUJuUAdT":653200606221,"ll1GMAzoKddq":743832411983,"S8WeA9D5i0pt":104497480641,"OeGd4RH10mAH":869820314110,"xFEMkc3wHxEt":726554858344,"lH67YxTrylXw":956663436238,"PScpxmFXAVQV":495930838251,"vyvUhzZyxrq6":224908155957,"Q3CtH9iyeUTr":645683988785,"fdofiqDeAoMr":70158666815,"tDcWSjbTnolL":709425674691,"HMNWTcnZahXs":166179371039,"22IPQIdR5QsE":826833183625,"fvKLqgF91YPv":863173224210,"nUZ3kFo9YAW2":57491641546,"8P7gFMOTDSa9":82204453677,"dRXUgCOuW9HZ":829005594386,"zqPORz1e20G5":492051616809,"zlKufk1NPt6Y":423386287558,"QeqjPltIvGSR":578878526937,"GtEshY2ov3Fl":328507539712,"8CgnsjeEux1p":426449539331,"q9rCt9frd9lT":593161889243,"FEMIFbwUELqq":142194266190,"phTMcNrtTcLT":754461596990,"CpYrlGlQAbmv":780815831746,"uCqMQp91qvcO":563367282232,"1VWydfphWUXW":706182747601,"BXXHp1OyeLf2":331495746045,"1rcQ3qVC7d7C":822453468452,"eqbgk1CEOMPo":201861173876,"RgNacDWd3jYY":980192366645,"ctYB6WaiXYqS":488487936270,"mx30U0XEeJNR":974852760379,"EM95xV6MoS6z":411969524793,"BqKiAaicGzMf":139636734463,"jRFamsj7mr6M":547330301911,"lFkHgj5pP55Y":139162705368,"rGHnjVShvBZp":512183146013,"8XX9b15arIGp":315035619889,"armCFasK40dV":473623973794,"CeKjjOqTfdXP":861895354029,"9duyb64saMP6":277091163714,"XepaLDNw1AtI":952486194027,"oIRF2Hr7WjY2":925497291088,"WQRFEgv52TV0":808308801077,"ayX3lLvlmZsm":602332083066,"arE3ileZgbiA":567673762886,"BxgTjIsktvSX":109375862893,"UBK4UvVmmgIE":52548700946,"4sajWNYgWHl6":900529482191,"rsZp6q3nABCU":441563711011,"jXOkY7NntllN":10832748432,"pBgv93SZ2m2d":139766629888,"UHp1cnSajgnL":440226414075,"eCATpfJR7cQo":51725202333,"gsp2esida2Qf":157447562247,"hx3JojMorqIt":737266353076,"ESoXu8kyTUxd":270205786276,"njGzV4ExMAEm":767780279690,"ZKmBj0r5YGrv":799242423363,"aqB5C7AUqTWt":754246355391,"B827yxYOLUGV":59614327054,"wYls7VbklbEQ":483847056992,"VYNreuMy2vJv":68572702898,"SLzFKGR6Ktyn":841109221293,"Ad7dq7Mb3kHx":152214439965,"FuqVQDBrZMoE":887734498391,"X8eCM7seU22g":569221942137,"quz5PAHES8Ud":416180331349,"ETn9oSP8QBOt":498436294419,"twGhEm23qGyd":589016618647,"cjdDcwVMjJev":907000177546,"QPVUyk6veKgJ":519112079721,"pQ627LHJkJ0z":609559109719,"ZlqZcL63StJk":306589839377,"NYgdFj1GMPuW":216972710236,"jl3r47ArDv5V":461298064197,"4N7PgCWvLPek":495603982527,"siqEGBxIbLmV":435958903156,"MlWM1QCiRDEg":55984482879,"AcLct9CWNmlD":95116947564,"IdwGxw6pWbc3":670264809122,"4CtTZuHGKQbw":17916968036,"HJQyqXpP8hIH":942875249452,"BJVUt7xCDTIy":551521471139,"GIddOn4LAhp9":613874136796,"9Ucfhn0cMtP6":683265557214,"c8qYPL4CoHt6":279618697140,"0v7pCuFTWLkS":526960571587,"WTuZWqdXUnap":889206770608,"Q16i5OEyxvX1":160826106948,"6GojesHjmzqE":516906097759,"ZDgRZUywiddJ":749684832482,"TR15JFVFWcjc":720335655641,"b7J70JS0giki":404010130003,"gW2PppCk8g9d":720922922126,"at8FW4xjnL2k":46330774006,"EUHfXLDxMrFC":155476645123,"ERWX8ltAK9Z8":757683718396,"GWd8MQFQXoyz":507568265934,"FZrEtVziLeTb":287096214113,"05vmIAdxYPzD":573249513212,"jhe0CbRb8vD7":524199865470,"HUkp03h5IMEm":166766721461,"ZgjJUiJ0u2eU":226671485166,"ZILb1WKnxD5w":810224689204,"iZxnBHgWqpfE":245028628045,"DFStibCDZhGw":878305892733,"T95NYO3tGJGE":616485847044,"sGY5pTbq95Z2":486348754783,"0PGRSZamMJot":429320860919,"31J2ZfViB650":219378988913,"k6dbxK3U9eSK":75452501811,"KSQhPzXIKPTF":589148890341,"2wHClLetyx8e":185534441845,"fv4af9zGjbW3":695627945346,"nSuJcQG5WaJq":104097315415,"SwWkSXlXllcB":702054213997,"uXqa0PuRI4DJ":130915431948,"y97FK3DTtWub":635367440089,"WkB6kKqeQsND":576955712322,"0iKH4xHU4ugu":984809417098,"Oqd0L2KlhBpI":236889789587,"l26lS2jVnGoq":423195112098,"NShy9pYY44C7":316080227016,"KTY3nGS2CboY":65302943458,"nQbFqNHtBYUz":73050750150,"G0UXxqb8KI5q":753002651115,"gKVPzDtI6D5T":953934361126,"YTBgdlztLtF2":983530522951,"59UTCsd9m4vu":883597947985,"W2VxxSMPh322":33266108749,"x1ULTY2zWW20":810917239179,"fIS5P60uTXPr":784776188171,"TZN0lxcgP2gg":572064867363,"5g7vtXHZiQVB":161426078095,"FLW5HVl6Ii57":93303184320,"yVpOE6EJX2GY":544261837977,"mdaldy2eXE3y":604062592852,"5Dx6qgzm2cB9":387150056719,"o9IyLmFUzv6O":592202535795,"NzdFFvgrr4NY":191403540574,"SCuZaALqLT8B":257736723310,"gWhR0HdgRKme":297026721559,"fh4LWnq4GoBe":371634121267,"60o3W6ArLa1l":899896529984,"rJ2Jp4yOhHow":373491719835,"Lk75cFPoRQS7":732262831193,"63viJOmRnsrd":37924401145,"wODaKEhHtl8O":435989538575,"2DszBglgmGag":104145452249,"hdcPWhJoSg7V":684380466909,"h6kbVC4yvQDs":98961356670,"OMgckWV8ykHP":235184954607,"V1nFBv9vt35c":432323979886,"0lM5S0dM9B9Y":721544807826,"PAbPswm22ZMU":35958828425,"JNjnRa1muwCW":124829100985,"I50Sjkqw7JDt":366127758272,"oMmpssGbVcZO":973816123400,"pzvyGKGCg5je":212380168104,"YrCMz4e0qtaZ":706227748926,"LJOmeNdh0OjC":138424489567,"wyrjZ2VYC3np":956907550454,"XN98LBKv7mJc":717163265577,"0ZlAvcZGpPni":834018528414,"CMnO4bzFuIbk":918204372853,"GGsueVD3RPDj":943804865215,"FjnAS9HsubJy":428172303805,"MucLV2JWCZ5X":86551752264,"w4do5DXXItXS":701107473865,"abVr29eYMSqI":731776030436,"FyjqSKdcAk98":950500136978,"ltVBGXHc0aNS":498409166817,"ZtHwLcsJVBBi":859572739492,"B2g3xBkw3bST":256043410688,"6rxOBjlxW1JI":881217070105,"0nYxUqT1zoxH":273288696275,"G0KHfMTk4vWR":463520985277,"rRIdJqJHo2PX":57173759695,"pPGMLAupfvnp":878437048502,"dQg1YZN6NMTY":620695934428,"sJHXbXjTtTA4":93289593766,"TTWJXEm0BiYT":814878624964,"FvNqhZRXA0nR":639307437657,"Hk1M9nb6uAsn":374621874659,"30CPYh3gAFU8":916656593264,"7WjIuWPwIGiI":206940947127,"UzNoSFayH0Dc":676917941277,"I4rvrbNBSqHc":477415968175,"4FBnB4VPCU4p":107045648980,"TyFjMSnivnfZ":175762836141,"7ypaAS2apuum":880255075539,"hXJ1ONLgL990":944253340033,"IV9iUMkp3t3h":863435971098,"mn4PSUqsPwOT":271516581446,"YzrVEM6iuO6y":975094619922,"Jac08xwGgh6L":665955434628,"AsVutitjaNu2":65801560466,"DcK3wDVXFMOX":727287044825,"gffn7f7N9tAp":934227270276,"HMbpvbt3D80r":486214696852,"Sk7mIGG61ytA":778169385943,"whwAoY6nlY0h":902221447431,"9NBEZNsKmNjW":572524230519,"DxksvQ50llS5":980233349592,"lKC7RIIfCOdx":246223882294,"PAdrYid0fspp":646100660946,"Q64MPDNN5KAh":735759180375,"wwS9Rv31naES":657462333344,"Mq9TPxEvbNFa":145851445012,"ktO7iarWnymG":896249290167,"4BZPQZCkOsMd":953929795900,"CyFNoNRniisb":719306851427,"GS7t39QbrdHG":177682152982,"YfXWeVeJuqZI":853234094109,"oCAcXNjnA27J":565904006308,"XR43oCS94N0F":574724605099,"dMWETXE3YTaU":29819480851,"lIvmJ9HHqZCt":251201244194,"2l03TYuX6Z72":565199417585,"FWHTH7YtHLRu":174211249506,"AHsnCWOWAhP1":537993571116,"fkSfr50WCaqT":344212828966,"SvlOpNpEjFKP":17579006083,"bYrO6kXdWNd9":455333278894,"5GFSYAG5bLfm":273218066576,"I8Al2avaqrva":171211769703,"mUwPQRL2S8yr":237228265881,"DTDI73oN0h3z":119154667313,"NwVCUriAjAcn":876177364052,"dD9wBTaOUHSE":160627332955,"gvYc60FIQGyV":698937465018,"7LfKYjShA9fS":340823263568,"vWp4Nole1Js6":459091915181,"ktEIT2FUKYz6":148842999135,"hzIihp4ST1hT":698268497629,"yRt7UEjzs1hB":807767369528,"yRXVdckIdcAN":170235781468,"s59iZX4IKjxo":8601427852,"OGVv2Io1jjaD":432986060850,"CN5Nd55rPhZO":799443093233,"qCqOghfWohcx":554713786784,"XEpzcjH8fjYT":566487771338,"TSP7CqVhO6ew":475018437756,"eyZPgmt5StF2":936129360765,"xljsti9URAVN":102658282117,"tmkjX8ByNnhj":746301212188,"s1KoaA51yYfv":44125526290,"Qlw7hxLwsvFT":840252157871,"VlUXhLrtrvEh":435580957955,"xHHtLLar5OSJ":736784808286,"YQWaGEBjfk4D":890128027552,"1e7kLcDpgTmO":567919032989,"H6kLGvEWEru4":432701591234,"qyNOy6O9p4NI":752456240472,"ufOQQ3qzeRVR":179813053118,"6jdYNDpqw7tr":111938407376,"LFKRbrwTIjYK":346234326167,"Yv4C6hAY9Thd":477618496208,"nftHbYGOlrN1":25976442736,"VPVIL2D8zGTb":127866269529,"3lMFbVeRaAge":415940273252,"l5WpVVOBViYr":124022097905,"AN7BNwgXTJH8":310349093446,"uHdZqVwZR3Ii":647833435932,"N21ylnu9or1k":659984900777,"o2lBVxJXu0wZ":977234607250,"3u4o3Ky7Pk2O":564625686277,"m6635pkiAsU1":221361942061,"1rbS9keWDxPh":104412120310,"6z7WzFiXbah9":561999636625,"4XrXu56b4c5G":81561293881,"YTotqDVbuNyK":171145877669,"g1hekeE6JkNH":105539636512,"wkjKAC2rIb05":387437969765,"p1JvKwfB1daB":629919569019,"aEynmGulWP8F":591503953467,"DSgnTGilcDeM":182172050677,"HQogy1lYbgGy":795561979376,"dSeS0GQzQ8sf":815120989653,"j6MTITGk2dmE":74453473692,"3q8Q72PJAOTB":649324016719,"16Xwhxd0Ykoj":20595774155,"tUNKMJ28tKt7":453262248607,"9Lpe8UW6KPbX":812768197137,"nSxz5qImHOGG":409454733200,"NfnHBKPNuFF3":948965462482,"3MFUE2xuCZsb":791096063027,"eHGVLcMPcHNt":312798904389,"aFVnt6MpdrYf":799870907651,"txaGd8wMZ2sw":213388830118,"qTCXQzk8AtoZ":330331590194,"kNhAFOymqIYb":223941114080,"RqkwcEBJoBJ1":596887955520,"gXPLFU6F9EXE":40726969111,"YKIweRkjCQgI":467483000094,"imyZdUk2TdoK":722327343921,"6uoxWVyqI3lh":352900893268,"Xr7IyDTlstqr":246019564018,"qBkCy4IJIFXi":830775454261,"NZY31JqogbGV":609116148462,"Cvw0sb4fIzEh":157955933770,"TLTPLx6CQXZ2":390504842432,"I4YsRd5xsnVP":625651870199,"ZgLAcIbXadIh":165879453139,"oUBgTvtW8Eu7":370689682287,"HPlKOuisLaP8":648380103953,"wbquDBXuvxIg":280183688278,"9vPz5WKR9QRN":285718757773,"5upvNYymevTN":580056098840,"fxdxEyCL4lDO":321518707698,"TRLneFAhOrVu":840830823568,"MWgTlEZZapV0":650985276514,"hKTXMsnMlFbq":191909110634,"i1kRO1uhGF3p":578750779231,"bKq1WEMJCkM6":408323846147,"THQ5HQxDleH7":636042898023,"FQN7AW9ERq2X":70230246773,"RXMe7W53GTXl":834286326882,"xeOlBbZaU1wO":713994929790,"ntgL7jeivSlA":584569956432,"VppfwtLWMo7e":149229706115,"RlnnV9i55LfG":13893173561,"IwzF3aoz79SH":510085354718,"Ome8D3hA5f8f":269800659545,"QsqXG3hTZQif":156618363223,"n3B9eWdszq6D":572158688548,"pm8w1HwFeMBQ":194432540377,"rOUAFppAjv5x":553428523855,"g1Q2X7ze7RYQ":195869514053,"5AoKU2A9yKQC":378147029826,"vOYRQpkW4f2C":589720941555,"IqCW6GEOlsQD":622116077701,"PIrevN2bdkLY":13850203860,"0q4sQovyvKPk":955451759894,"8UUf3fYDb1DG":732506153470,"sbUBfQSUuFGd":151603627515,"rXFSE7g1e3yP":671301696623,"sdEoTwJbuHz2":141555777877,"t52El5BQGlOG":355845711557,"NRw2XQkGriC6":172897828345,"wxuwDXKTFPE3":679931879895,"Ew2Hwan17v6T":376888882684,"o4FoNORakZfI":670726680202,"Atpz57jGd5PB":519326270113,"2RhvGUezO9jd":88334685225,"ba4CFT67PNFa":875714639260,"wcYAiJ01pfiZ":602026835537,"D0csnRMUnkcz":921312446344,"yDz6jTOaPicT":675958888156,"GZbtHSYii0Bf":3418317359,"tbCos3NFACD1":538665288040,"z8ieN2OWoAuM":21479649780,"B4Kn0YEw1NXE":881550865822,"xcApm0uWYh52":246113680035,"A4wuarWxikB8":860231730278,"wFIEWHVlTftJ":445745511060,"diJnXLaN7M1p":370960986103,"dWN3XgpggoOP":43106289120,"6IB7cFXz8g1v":13538402580,"ALOaUsJ7Leh9":448621984039,"8NSqsNSL7hsV":303811800883,"7wU582JMle2y":714055975754,"ukiEHb1WqPPV":566102893666,"cLafzb8MilzQ":684363475079,"AIXtjFM9De1J":284739764551,"6xwQbjSVurOI":714166159508,"jtP8D9aWnmEA":65919201075,"1vdLCTip1ih5":617862319054,"iPypauv8QB2g":61285532769,"qV5QPhXeA2Mm":454032036136,"aUDRYgjU1hoB":276515035072,"4aX3W3utSV2v":657554952908,"DEdV3djnxMuM":107444778534,"EZ9D2uFFeLV3":729995750458,"7Y9SX54UqKQz":989756993860,"e2pWBCxpRVwf":260994703256,"pqt87ttorxNg":342441801178,"rwVunXPeaMZ3":500940969474,"V9yXeYURnghs":597148347884,"zw9twmYD1nwt":665974192179,"dddrdi5xtKJU":614043566240,"rno7noxm9xzW":967683036671,"7y7LK3oUBzQ0":228774772281,"wyFho0DmacPt":242973496915,"iv5WSxabFtvM":21864643760,"Xw4UWzc5D8xZ":700010229206,"IEoOLt6EabXH":869666311182,"xAcn0ZjQ0xnm":360674553887,"SJCJuiSZaWAy":505095801808,"xL0A8Ls3IlNh":643808804316,"qHNMt9ZLTHlA":503621305554,"4sNYUSdpz1xb":51833308505,"5Rur4wkK531I":245173922566,"XlIPm8ywnkto":863502447329,"T04i1yMpj9JX":417925448291,"CIkQruSIVI2T":367631368479,"ruPD4XAOrNHX":994758630962,"cneFa7VQMYP5":226710594035,"glLNJ9BsRLZE":105889112462,"X7jXK3kONDdM":386229496168,"0k832REtRWjN":263820182876,"zy1YVTJzA7YK":284897533594,"1yAH837Sqgon":683433706860,"VWLG6IDnpVvm":711429888753,"llnsJGSFoNJ2":190201969791,"678HmXh7KCMV":117869998120,"7csgclahzZ8r":43126937156,"YODgrz5C65WW":426958798672,"qn3nT2LKysOa":488596544887,"phVEtwSQLVpN":585949609090,"VPOl4nbSwwFX":132188528949,"Vzun3ezRqu4y":359323101407,"UTbgvOrGl2bv":878032479785,"fam5nQLfIQJM":898362606475,"1Jrd4dzJj4Tg":207214418440,"BBXrJAHOwWkC":678988945743,"uV2vM60TjCVx":210425862064,"v8g0JVpHTr0V":972934433221,"nSW1kALUGYv3":909847457204,"tVRsRbeAAciB":527048283731,"s6EiqF8Dq910":800837159382,"LJ431tP4V9bq":777864941062,"UlyagOe9Hf7S":845725324986,"rI1bAvs6J5xL":857836854861,"avJWRpm6t4uE":637634776566,"JeTl0x0JA4rh":862150628702,"M0Qg4OuA7Lur":672129223275,"mFvDL2HXtnL2":809721187034,"A875ybiWCoZF":859538586405,"o8evdxfT0uiV":709099771712,"PD10v7DRdVQ9":318029814209,"h0dPtbLyzuF8":740297769551,"1e9n420CL2dg":946347930991,"hXu20Yj5Tdfv":743128729408,"yfyIvgPR9pbC":693943020228,"QhTCNwpPlwz8":93078589158,"6DJmKiiQvg60":794851042575,"EtWNhOhMHiYh":334713932250,"jqzoqFISm4Yz":740955867099,"evAavi04UwAr":403379937100,"tUIjq2o5g8VP":585082377065,"kmIzpuQctwky":249498770187,"nVEpDxJW7zz7":646699824160,"I79Tou2fCIue":263076477733,"bnFQZ7eOjgsq":359162622155,"LObt7DWdvcEu":703852528164,"cldSpfiG96bi":265895220962,"hpWwZxHxMbAW":5608018916,"0CZYc7aRtdiv":58750028244,"F9y788hkxmE0":249062641950,"XpRzhGk56QL1":320347165656,"DafBQDhnXdfA":658068360117,"w2KPozYOIuUb":403980666806,"hud8wNBOh3sF":611079464802,"IahlSqllfq3G":816123912221,"AHDhM9jR10pY":189297720729,"uzWUh4WNa4We":545145449581,"AedfQ496AUGd":383359994832,"rYaTb1gjCiVX":978694961169,"t9G5xaIlwkvY":862847477364,"XUSLb7eHJo5L":447760938414,"1OC9JqzF4J10":639869824393,"lHB2ROS9CQhB":324910107850,"dBevK78g7cFf":22013544804,"qlS8Hby3bdNJ":139919168264,"uo9zuynXFm8j":255029921550,"ofpQADZWbLTI":618194997333,"ISMrtr6X88eS":472993320855,"wGyX2rn8zF6h":312195898217,"GzeNbw6VABzG":298938910822,"CQCHMBfIQo58":156249936501,"crNBxPd9s3Rx":18193560679,"RbbdKm3C75Wx":365109545436,"iFZ3K7krl7LW":640541967237,"HHgChQwik2ue":967262690592,"Qckp80eHo4iV":334201178822,"MdD07KAbQvz0":291155577449,"Nnk5U6m3QtAv":950861743253,"PI2GqaCIWlRN":174322366277,"3muSi1ZrHKbr":975752956885,"qzxnZunxxSHs":897889088281,"TExGwapi6LU8":473748379036,"JjXP3AJAbjtu":372774917549,"t8Ka6cxrfJGc":248761202558,"vZTkf84pUOP9":680949338253,"6tXqCIifrMlM":655009541387,"C1Av5TTTPmaD":146521515527,"L1sUO7vd8ULc":851753946405,"wbnxZRTo9wwG":878356334384,"YhGcnQGuA4HM":415052516995,"ZhLnB9jERHXo":769673683216,"4Rm0TAkzLEeg":752944100776,"PowaO9ilcIu8":49137705387,"fvOVPImLsnpn":619686124520,"1us18AnVcYkt":476174027844,"jUXIk4vCkXQt":873732111661,"LinJXPZVSnEZ":433844474765,"cBMgQxwLSIkg":439148462615,"JnbwXY0qUWGa":182608674957,"stiDx8BsxA3L":270137465051,"giiRGvkEZzyR":160733291568,"gV8Q1YZoyGdL":431290764816,"E8QCs6HcQnPp":980000612848,"Q1vOaTcadSOc":915551113417,"P0RHjSVMqF1k":92934641253,"GOkk9mrRCUiS":177608743875,"5GQ1nD6gPLUp":355430427294,"3FY8ne15xwJB":926535827206,"gmoV3xwB1h8m":467028851855,"ni0TCNb3uF1b":916719088785,"uFGDQd4wvjNT":404140249671,"WrMWPfNhZTgk":642605961628,"zlgxk6T1OqUv":560155820523,"YY5U35oiGEGU":161158315043,"PX8GjXVGdWIG":346903467256,"Tah6HpGBM0te":198120483852,"ZwT8ZOpzo1Up":270636630047,"quL8RC9jzdtu":746503322212,"zXtBWuebcAB6":37509570073,"GoyzuKV1zBjb":283823362926,"XiDbJuAqAtnj":188807612123,"vDhnmfA7jneC":17332094017,"57SYnyO8pg6S":792144184839,"woUl7TRRlxac":363466411865,"Ww3n3tG3Jb8c":776756146752,"j65hzjP12jmh":308654717393,"wzyl9zeMmFYq":581669085949,"i4aj33XdoIbb":422730217805,"FXZjXjRlQgrz":976000703290,"JtMBRkdiEgpX":823131160526,"Blgly7gi25xF":142230553970,"cDoBHj0mZP3Z":11946087555,"7ohKZxzckJPb":833326691022,"0D5QtxOXyT7W":476473967303,"E8QtjZaIvfNP":51983353027,"wcYh8vhUDqWj":508111936498,"gUehaoMwppPp":466354567340,"XIAkViTHNpnA":565007884845,"tFhp9KgIoFcX":468597769825,"EdcNEHlaKmZN":966806659932,"pdF9j5c0Bn8L":94713590663,"6BsAOk5St8or":897350650920,"9usJzesiRjdF":271953656920,"uiZgBaSlvpFg":562907662105,"YgmvGp3E58UU":70053330621,"brNUzaKn6Ehl":59397509902,"GcP3rlxXmbBF":910518692624,"6bYl9ejdMuVc":746776398390,"qWOCOL8yarDh":214939181544,"QI98kWleSE7R":176247261793,"zdWNGga6VJCE":68472931403,"LILK5pCVtLf0":559447342086,"7dEt0VZnmgzi":140215731130,"3G9oE4LdubEN":866645177212,"8bQ64wiZVGbd":471515909211,"tuwDoAJhmAr4":565750838365,"j7vKS5Qak5qL":17229748443,"EeAikXg5fHTU":51180082148,"BWibT5skAY5I":248414641078,"VtMbFVzBONR5":110801564328,"zwVTu3736oe2":97324796300,"7eTFfgoNpiSY":145586790510,"STs9CtPbmpdg":833573709489,"D67W7iCLpAVQ":176617256624,"Uod4J8nN5mtq":70843797701,"ny7Nyor8mNb8":181944888216,"P5zc4UXgsmHj":253757211836,"Yt0l2cNE0sxq":466885010365,"g1AsJijWg10v":906289841212,"HmgE90Y2hj8f":424100663706,"U3qXFH2agDiW":409341315994,"vUkqoAD1qPSq":848405690984,"DZzSNZ3x8zwF":926246856598,"Z5EsvgheR0iQ":265855124009,"PpCE3ETacXdT":3187833662,"j3um49Hjrjyg":928363246595,"ekZ56Lb8qAP1":159403770798,"Vu1Hb695EQwp":872816300903,"SwAXtpSLhEEH":772186859126,"hHkY5SCVkgBR":391362300872,"uP9zA2LWi2KN":441257967624,"3GTgqZU90XKx":529214338090,"d69diLEISfiy":158982417952,"KCjBE65lAW7L":599522138100,"gspT974PRd2K":949521355595,"1WL6kHr0BA1N":258439039799,"7WJyysTvRjex":54258413081,"rlIT9llfc0rA":855852556542,"VO4t5mdD4BkQ":792403279530,"gHBFgoUsn0D7":443145496040,"1AnE7weOjHsL":763285057284,"iRXg6daIiPSL":209516034733,"v2677gkFhVo5":906828255542,"G4KS56XUxPKy":154067248841,"AgytexnZiCR6":336704632452,"mukzAR54iFle":169066756276,"mzRaL3ij0tYd":867390507636,"aehYrhBIBnGe":541467091225,"twBffptuZTDg":782664776621,"soQb351LGqI3":36158637103,"HEZBLVpkmpW7":777348446501,"UeIwNidiMVO3":172602367329,"rRUvalzE6SRu":919770748643,"Drske2dizW06":892213851646,"7EaCydD9XeQn":607432216359,"D94WDMLrWYy5":561946661505,"Zu25vxLzvgtB":147771886161,"ASBiWiJVSuC9":588948406197,"A4DfvMdzJLbO":481676935845,"9MzA3QWsMCwj":777302266142,"FIwak80oCbJp":492107059936,"FpltOkXkjFTJ":951756872417,"BVkjtCiTu11j":332002378463,"AL15Ph7FCXhR":723326914584,"kqlpQWpUf9XR":381630896584,"iAufp4DsH2I8":925541935063,"ZnbGMUt0NvmE":761686994244,"fWNR1eyd74cV":796713924844,"2yVfB9c4aX8d":45003936524,"k3vopqCkMkEm":823690321389,"yQmyUM3K7Ir4":324646319068,"0RaOjPtjI2oO":487494627072,"Qeo7BOVCCEPy":383227551533,"oZ5Gv79eK8Ur":791164433091,"llsWRh9aFhx5":852911712115,"9s9uYJwjw95D":92325927064,"xz5L85c8oHYI":110761743935,"IpGV26evzHOu":362471343837,"ZCum9XqojAEN":762972679558,"kYAVlff6H6Us":983573823488,"xVj3wlKur3QU":177042015230,"SuCvfKk79zUm":854203366304,"kDSnfNQsdG8n":754725943785,"GkBB7rSi8xhI":436833757903,"ZYcbveuNbC9Z":292266503925,"zXFQa2xUqH2H":123759971461,"AXaoerT6QBBQ":642559712345,"vlFkCk6njyAv":164969055410,"jInpA7Tj6h65":485231485732,"fUTrjQQDGMvV":436420803216,"0IXHpIQQz4N1":879892651686,"GFxGjoOi1zY7":746425933755,"qdk8za0adiuo":302056609398,"YjS60RHz5f33":81804639594,"LuPZ4a4cDADs":862489274342,"y97k8IiHUI4V":173410656538,"le3zf11ltETz":894836235727,"NdiW4fg9cmCV":50527783617,"88q9qmw6SjqO":128309379335,"GZxV1z99JniE":900983810779,"mTRYMGaPvIka":363293701570,"IAlrCg83oCvv":467102613758,"MXL9YmbBecp8":887993493765,"8LHDmF2Jp8UN":633605129602,"jHgASbH2JUEY":827274680187,"YgGGOvsEcpV4":467798166522,"mqWIojTKBdpo":153778274552,"GPa3yEVcTpZK":446102541122,"VUoy3o5LHjdP":187819370148,"Mhtn25Q3Ovo2":434568473582,"bCmou4pPjrzI":750968229991,"ybBbfInMrA9y":330738058137,"uVhgMNSMgkEd":427132110155,"n9V1M8EnuWFo":438121897475,"kkdfGA09Tcto":137999530866,"Dm24F0BKjMP2":499022605362,"XBZ2Pc5ErrCE":290047539327,"83bkVM1fgnxG":722374477084,"S6JlLrt84Sax":46516128319,"HJsvKzTEHu9A":163800967239,"F8ophd0REfA6":992821697635,"dZT3vZHcQDZP":152203849417,"9cFIWRXHQyvx":772085040895,"dfJNuLqaf0tU":623107191745,"ZjWMWiXUM7UK":613347541271,"S95mt4nOS9Gn":526919690022,"U4NZndnGdLEN":93756690344,"56SKENyQUFv7":238222002706,"vBCBPDVBDNKt":57368539375,"jvi58qOiS3vf":133992407268,"kCspEoUanxRo":693356948574,"ZoqLKQ5u082u":442803770265,"gPythpKhWrFG":894604818978,"fXntGIoB2WrY":756588080250,"Pa7aJKy0NU6l":187659944360,"LHaTBJs8MJE5":521280059471,"aRvbR8Aoj4Qi":285912086064,"OM4l0dQuumZo":501813323539,"fRqe0T70gSyn":470686897684,"WNWgCBOsPorg":763502860581,"ZpFFHrhm8u6J":658047126933,"3CHM9jgEEvbN":674349805509,"YtLr9wExd4aY":653770129533,"mEZfniuq61Xe":825789652381,"IFdcQGrSNff6":65230648911,"n1DbM4G78gCj":959606637430,"IcLVbBcsINOQ":986918337524,"Rxz5ynPXCGaD":438310563234,"lwqbSKn2yhgP":700767101217,"pIJQVrcttp9E":583325762822,"3IWE1P9OPspV":465547152962,"TP5SD9LlnTeg":477618757215,"sysEVOYpyDV5":370296984228,"bNMBuIfCp2A1":681659196884,"6OwwBiALYkyC":847218721498,"EDd3aC4nRndZ":704706775479,"hlgvJp9zaVOE":213459437580,"VCnhPSWAB6rk":310815454896,"PmBdBUEPyCtK":653603539488,"H8KDNb3DXVk6":177791905357,"T2LhD0LNgpWF":986491664571,"WztJsF1ZL9A5":437650837533,"qzNXI5aSiXvV":58619601065,"UUam3eFUfdQQ":592225222135,"ThSvAwjDKpsD":199962566596,"Mkg4kHthL9fo":597391526923,"8566irb03GBC":197298171278,"qCvcsH0GjiZd":579505977382,"NRaHCctODz4f":278323604094,"QhUlxv7ZFAER":921312470042,"hlTKXtbhRu3a":448587052839,"gKSPvrP4rhIs":516144831587,"FxVejFduepEK":389859936618,"QKHfS4wAZJEE":106759187312,"t8Ic7AcZFgtl":983093811139,"Xd7xYRURT9Aj":414041909640,"kIItv3eE9mrQ":639705049637,"1l0gf1lRlA1Z":468594869489,"sU8KU4jfezQT":907987016274,"Rtnrx9jUs2n5":425154948172,"C42Yuh3LDFf3":162089608311,"zzMXC25Z4nOp":271782955857,"MC4BSlvZLZaS":590454264110,"JdfvgGbGaFIq":742390864208,"9YJnaAK1nRRP":170682941660,"ygZrrkIQrxZO":473771255618,"qSptR8ycjIpD":852479886850,"Hc1MUWhICM5f":805364600461,"2XYmFk0rafaI":126609503938,"zarxiu963a7u":969925424254,"OXNoZNkxJLQx":237089545437,"nQZZgzvqnQs2":469642007974,"S8wJLBb9gJIt":51464012867,"TtSyA6B6qxQt":770780182492,"fTpCigq2iZjF":23315649952,"rtIWnWEOot8V":519274676395,"aMRZv1JqRalI":931919016810,"Ad0enXAO8NH0":667427778808,"YEQKNUY4pVkg":669825188032,"9imOMlnUUwKd":763516386467,"Jzvj4QFjWLcg":447609455383,"ESDHsn7IteFa":485055869243,"b8A3YTpXmCwD":72246593699,"CI3k9Jkp75BE":367510703423,"QYZkv1zpTNOf":989911833620,"HmLPlitD3jxM":218323870331,"U5jlQJSqHO6B":561276537475,"WQgqjlAc0sac":269595834745,"rPO3wUAw5GqG":307230828924,"ZliBOj6vByq9":903106039348,"zjzTOVr4Lgu6":855342663799,"VWM3TyYlI4HG":284838833643,"IHK8PvFQViwb":218452285596,"aoXJQsiicU8N":977633229913,"W1fbyYCrfroF":954512810244,"dCYjbwcHOgvg":729443652274,"IwFSm4VPhAct":482866234388,"WAZrKoYBRr0O":366884205172,"mP1yki1hNpta":555242403529,"1iKUrTx3TnY3":651570586521,"G8327uPOyyXR":496055962210,"js5pjRTpLzNK":94685483635,"krQl4lR7pdb1":101878472489,"g14o48aV0JSy":189112330533,"VCWhEYheN44K":441725264141,"lJU0LzFEYWMI":883239569335,"syQpqFh7Fb5T":446631940621,"AnoDH5dx6a5P":423398515543,"5S58vUT6Zvhn":396504732605,"UdJiOs0MYpO4":131922539927,"L3ewPmWdGySH":387991542526,"lHM9AtQWXfFE":217541071674,"0m8EgbhcXh1o":374761137181,"fV8yO3IyiegD":623277680926,"ZSlH2oZbw4aR":587531730014,"0CZDii9azTrp":111381946385,"wqTigsjskADO":776003825136,"ZOOakJBoDNtc":2126252599,"X5JiSS0Od4GB":26850101131,"O4PcMWCxot5Z":206266563021,"fo6tbgGk6jxI":266388268924,"4yNpasoT6O67":659027265299,"ArY2hd8lczzN":473592209243,"0sb5lQx8qKlO":538489901857,"xX4N0HmQXuuP":638547573841,"Z04IK2V1XO3b":5140040612,"knB6ExBznFno":848652111482,"uSGOkfqgHQS6":798135470411,"fjA1pCEtDQvI":10034691151,"bqkAfPPmUWEy":480532926936,"oKjdj8FNEM5a":965266347833,"HoXKaJSBvFp3":135312760208,"kqnbVKPAFyAG":773191309390,"jGFhwTF2WoAJ":32252990194,"wbJR7TDJsBVE":549321569106,"3kCBK37YkkbW":143928158549,"eG12LKYvF1rC":72361955685,"lkCV3c614lqb":589759252286,"mSF3SYU17B4j":635285809221,"87n6dsYNUtXl":543331514442,"2neK0NTf9Mps":330602796622,"zwUOpN2u6rIb":931203364075,"GyRSZ0yJ9yyf":898544561998,"rarpMFATP4cl":293301806705,"L47nWV3R2EMn":286079030876,"Kk4Kwkt9ZdUc":20431525928,"SM9cukXXL54Q":190466273421,"0ocliViQy1QD":972935929835,"Mm3BK8woTaJy":346238724260,"QFRtFNKWti7q":929344436441,"XkYU8Y1etcMj":237667044708,"Wtn5JS8RdaDm":567172236022,"BK8ofsfZwac8":954605245849,"59GMHTjvm2aP":808194504561,"yiQ9IhV4Y2uk":521990700593,"JHjUHtCDjOAP":907643994687,"0Nr19Mi8sYlK":911787686518,"A0VH2uSPP3M6":141468137752,"nzjog1r0GQEn":752942263197,"pToERZdg5Giy":708538440518,"q4b2Wf6Ymp08":761348286452,"W4L1BRiuaPnP":384001368870,"mZAH6oZU25ry":981176303401,"gUzgCqVS2BQi":555393504921,"Sn9ATOVxcTTu":759274889151,"riESGONqMuXt":225024075148,"Ym4cJbsJEb8v":541863794387,"fL4aiKcxVUWX":146480502637,"3s31fMz25HzM":848318706604,"fhwXlXgPfSre":825612016814,"Sjh5lKOf5odY":869972074453,"BxP5dx57C5vy":598818178001,"03GSiuA0TEO8":355687659663,"UgKtn7kayxSs":528485058213,"kyNJOmeINYIL":212295226771,"RxVVM8VSpeYO":768170068701,"kWr1Zj30s6fd":245063558926,"mkiP2wLNHgl6":527492718374,"jwSm94Wv2Yzb":983334547502,"QRJmCDavxMi1":204380989146,"jnpETqVXYK7E":266791832515,"jkGoNShZPeYO":143230153560,"Iis8qZeFhGen":804725942302,"UjpNvZTFqEA1":400898646679,"DXIYalrCCj1m":7115586783,"kJ22mAU5ZpTl":315974078094,"14rw3b2ExomA":5347028027,"qZp5ln5LZ3Ql":699134108773,"dk94bMpSEi5w":588258667702,"FEb8GZNLCftx":293282500033,"J71YS4Q969fj":960403984737,"UxWHxko3HbUu":346329001800,"Z4Xic5gqibI6":236738636180,"TWSa8aKfboqG":628163470829,"4Jkze9LpFwD0":941293351037,"812cLNRg56xf":68580932651,"8ApCLwfsnzHP":776584146934,"Tgv7n44oyQhl":21202230888,"eaSRBDsbkYl0":589479076390,"uHKTblz1VA4n":507651658821,"FWLll0leELvK":379471554122,"RYnUb6rwrKuh":380946063932,"h08rBKXiSr8I":74541055237,"SwRDhC5zqlq3":948461307917,"xW7GXmSbEb4a":669518543889,"PhyxuSe3ZtdH":832956132770,"J7McDeq9CALi":724700679181,"pZIPYnldYkFk":191104479810,"GLnnKYyleIxy":570444999616,"2LuDER9e8IJa":100489776771,"mv7mZrHKsLyp":501035029461,"A2SUdgX9ocj3":873191417435,"TqKcsbrVXp6E":390017188877,"0M0hYnpKbdjY":394961856483,"AvIX9uTiDfSD":384101512093,"1Jd9LfsmzZNH":146090554291,"VCU7EWlYE5P1":936357872547,"SqkeBRm9cu9V":700261070906,"gcgH3V8Jhco4":214603592795,"i6iAQpm0SJWY":559566504507,"lFO0HJzI1heK":876313193783,"jjyAJ12Ym68u":827763329671,"DjzhpmBoSRV3":67045257819,"TQ01AJazAiy8":863944536181,"6eXpBep2s6pD":389058213251,"vnGHUp9fgKzP":226723654523,"o3hPieHYnqw9":341151041373,"h7diWOATrgE4":989675566349,"bgXLl9icxiZv":645504307031,"G5wj9DwGOgko":341934449052,"VlCE8DqBrB4s":104439352213,"6GY3pvYy8dir":718962955029,"FWf9PuclIqod":530966440448,"CDauF1pbg64h":167869355981,"ZGgU85ewwper":835587976581,"NlweaPTvjpgk":469163233394,"1EB8UwN1fUyY":64051507011,"dJeSkmpMQp25":821188180360,"if1z6YeiOtZ1":731319524295,"I0vqvh1aYk2t":900951663604,"Hbe3pu6hSXQj":204012466665,"D4II7YiHwkPi":524470987059,"bVHs2JDiEBHo":70667848499,"rORPQR3VL9Bh":144207025840,"H7HusbOdVZfz":792139344385,"Nkd4GIgDXKp9":2146926785,"nfkDbSYGewVo":736768570515,"RxrzPUDGS5Zp":18820786604,"t3trjege7uak":809068316833,"lSDcqfHO9BMG":34749411665,"eEZMVU9Uqxoj":445513717153,"CzgYULs6RqSO":253248502800,"GvB4NFyMWw2C":751626730525,"qEok1WKP9DUk":979435381690,"JQFOIrkZOK5f":525458949851,"uTC2hB9g7QwO":342973837593,"Lh1oLG59XSq1":374260184709,"HYg4QEcaymxR":720842472274,"1ICLLMWjLwnA":259300061814,"bTOEys6gKO9I":622863246277,"aF9SeAFalrYF":92982660424,"0F3so5L2RQ42":826148802992,"yDWMMmVprGAG":307673580096,"gmbs4t3gUeOz":843178909351,"JvoOl6oMTGLK":100158206545,"RxBRIFNr1JEh":220176372300,"e978sdjgEB86":122966521057,"gJgsvusMaeAU":54506885247,"pLZW9cFqjM2i":8407852632,"suDK6ZPJQDEo":123270832271,"aALXv1oJM2et":2189628193,"0qWaf5EzI6bV":623031499343,"FOUHaPm8ZBDZ":781589233207,"vbzlSB5cXV8T":822084908439,"TaE7h98hXATj":815884228792,"qVkXjCV1BbgD":290742459011,"dMzrB8ZydhEX":66950025722,"8onXv9fFzYYh":408572162149,"StTtmpe1qJXJ":224604730552,"mL9hQLsMpd5v":969926828114,"JdFJ0RJHv3uX":937656519228,"lh6GXq5vpNJ7":610539253988,"KBec8UtBqNnP":278152336313,"7vZv9yRW76T5":548523569316,"RyRnKgQM276a":530664234219,"aUHyDjcm8aHq":891921283705,"CFj2QBy1OwyH":289626554384,"G8KooUGWAl2W":250918186058,"uS6akwFsqdti":482353912406,"QaR9e7stYFaz":572412273491,"eTY1ZRW0ckhI":742071544403,"o1XvzUdoDtOG":550911993501,"2mH6s654I8v6":544955696616,"7I2b0GYFN0LS":425053374278,"DzTsV5TXBPGS":132111928552,"d6zt1e1sOacp":488210264491,"iCKOCTiAzxpC":239919007843,"0w1BeYQm3Ihc":451968226317,"s52hbQlTOdCW":562955975894,"typ59Ng70qJ4":80236721335,"aAuGEwxsrTJW":856839471062,"eQUwpN0Q2Cz9":97868187494,"imDRiE6NaiN1":255560478475,"ceRFFXRjeSGn":228755289722,"5s29HK5YivWW":577988848250,"EdHM9EhpCUjc":377605045313,"6pJTfhhnm78y":142846785617,"TZmDZL3s5tBh":207500143396,"zQgwAzQFgPrk":970677752403,"kEBZVMO9pPrr":611791803618,"qI8eddhYTG3R":989354425538,"SneWTULnmlv1":428434790100,"tAbdPnQZmSNn":420794724612,"wpuWp4Ce73NE":512237198542,"OW3VH4HGcBzl":601146051312,"lw7zf69CHzWZ":99136482218,"KGeYIyewp92U":138267228919,"srasZFGPLNnv":954466909816,"jLnvQMlMZsF2":50757049700,"nkcH86JyIBgU":214964087111,"DYz1TuiSZy1T":902718933872,"s5xok3JPZyBJ":922096145734,"MjeoymQlnOPs":362174502723,"c8rDlwYq5GZo":115704437502,"McSEIbsSbSzr":982595512861,"WXetxsy9iLpE":984219705649,"Juu410UZXme7":446024074260,"PTtW7PGJKO4b":901921935944,"TWdWHtErnIXK":333261484622,"yff66FKZwYcG":229714812159,"33DUe6ILOT82":153927260664,"7Dz6eFNrBF7i":559740050875,"IO4nWK107iTr":365526797618,"XJtUt7ZiqI8f":270683371187,"RJFuPYnZMw9F":282709827637,"avp063sswCLP":143779877560,"6MnbQvYyw8aE":235463333509,"Wt5fJ68oiZpG":853701699811,"IqHNbYCLPPQn":275058908613,"7XrfbeC7p0v7":264625895712,"sKJmWLW1iAr2":968257820149,"H2EkxLWihy0I":493814224433,"M2Ns98i4e20k":932846897581,"cIy63d4fgfKs":856665493536,"CFwuEFYOLQF9":500155661297,"32Nb1a7oGXlt":354384863663,"4nKjgue4RSgd":962331470960,"Y8Px07ff49XG":837331766270,"0wenuMpzfNsm":818083671650,"FzDgV2oTvPzl":664722816818,"q3rlt53VKs0J":839954702883,"9RuMQAzZqOYl":141682062113,"YMtvcSjyvYv6":710733306831,"3zxxCSvCbHTi":335154353754,"qGheeKY8FiKV":378312487188,"q25kHpGvfbyJ":453989069698,"NHyv0QkJWuvN":681509593676,"xOi2ANaZ7rKX":620764454191,"vDYhY1MPVLAU":21745478067,"UHVO3wphkMY7":215244473847,"tuP6QJu4VQDM":349812262942,"BwQheMEWazJY":299772572547,"KiQs6yKzRGk2":821499416908,"VVHArGsh5Ph1":354540684647,"vm2KzR3yv0q1":496832264510,"28yDjXhDOecq":79963900111,"uHCECiBJYaYF":26134992323,"o2Hio0YcBFtz":963186924186,"OWvhNcSpT3GH":591238247413,"db8NFukTXwH1":840946063223,"863jGmSdM90R":356625105189,"oN4Ty3eziLeb":970234243203,"4R3XATxx8kYC":562748187492,"NMObdUsdUNCC":803309962198,"0dul7fxbCNdJ":762656612838,"Eikabg4zAUbn":691175784377,"QsIJ5TsYyhcv":817167813065,"FT6AkhDbZRO0":849494519039,"KX2BJxbXpkLC":656051370671,"ZsijCrII40tf":304395730085,"rLpeKdMYquG0":750758695149,"ECdbhlx6eanM":777327597171,"CfKtF9PjaBEc":527528792264,"RCXdL6cGSkm3":559378907349,"YYOpuG2XRjS4":678443989952,"YnULMbBkkDyE":469919608528,"rllfgJIs0WQr":552234742957,"EHE8n18dT3oY":349043149063,"Vg66mImUYcWu":678893959994,"JmczL4yIJxZB":156320758114,"QACtQ7HQsrbI":242493729354,"QNNCSbNFS6hO":163904799747,"UPf5VKS8ZPZb":454575385195,"2GjxdsNwp57h":100581206692,"blP09CjHALOR":263324814675,"365eBhSAdyrQ":862095742030,"Yfz88EG4l65O":646854165363,"XTwPcwBtLVq0":448074184334,"pS5dX0vjHoyi":597012522396,"aJkp93rWudpq":567358698076,"Arjm0iOgRCrM":195270793468,"NecRZz2FxyxO":356387775237,"oqRDFGOltQlC":28097338424,"52E3HmVhwe1I":239426652926,"LvGieWv3xCe8":443137896240,"YXkCSaXwMVcN":759888622495,"zEjdDzMg4QxS":378405058806,"5CUeoMheldJe":226271831866,"86smRs44lb8U":632370903781,"lYJLVnaM4pCO":122184104613,"we4biFmEKzkz":802646410210,"kEUi9Pp6wpir":534341373875,"sgVSe01RxZmW":189987304553,"8k3rcFPdpMAG":118657556584,"LbCdkoO6B9Vz":999886643421,"D0SO6wzbEyow":419545446700,"mMCPEvzPeYqd":169778958798,"kWU8yFJWkRMw":336736715053,"xIEFxM0Evu96":290324964595,"kgfhtwDjXlt5":995288462083,"9YwYQsGo5toK":606275989050,"rfTzRCrtiB2b":504530940562,"r51tW35Y7APd":86819272793,"AaWMxxXOIydn":885682559583,"rjb4ciuJgSdA":460782934945,"zFxIDk3cV2m5":895890542907,"02QkGMCclq60":337542239608,"0GEOpfu8JmgT":349150163747,"qbusgXV6bdUU":101916157203,"eNQLwuwnAyUU":600450164679,"pyiCZq7op0ye":602390570016,"KL7izXVMbZNq":696799121578,"xyRLrVHCaDxX":91691218244,"sErMLH37QmZ8":146722877909,"A2vrmBS6AdYT":546401038052,"WdBjcf0FNUdw":488077059385,"l5CS6DwmxpJS":516183772781,"1rKIR4B0Gheu":410973975755,"I8vUFG8Z0VqN":544069123121,"vSusfWe5IEdv":820539439469,"WjOrJ4QKB4Bd":702081255458,"ngwMcqDZaXKw":428681634374,"74XuVZYXkVGV":244829872211,"WrdXQW1LB4vD":161069785452,"FtCRyR7ap5q3":9799461073,"qL2daUs0ryOV":270447947638,"2JPYkPSrOLx5":770902950469,"b9qsLLrmDe3o":566877820526,"8b7V5VHc3oOU":619094354710,"gu8AFTBhwHRN":737743914740,"72vQJ0iXreVp":206252179814,"hdM4PumFvNFa":254594904749,"4KOYkx1QRwt7":45212096762,"y9OAbPni7sGd":20867814973,"TDtbDVXZnmB7":958590018543,"sYQ2nci8P7mT":343307098059,"ttE5AY44hyMG":128968745728,"NP4Sjwia2d9c":646666855533,"63gExKOBqEKD":672107801118,"Fe0H3wyqhMDE":127871364995,"CnK7cB7jCWr7":724190348754,"VM7MJ1LYwkuV":649306228410,"K7lLzLUGEGKV":297051535715,"oVGJ4smslUfu":573252801550,"om1YsHnfvPAY":132308088113,"58a20lAci0ZX":651994437492,"buLLak2qAIDX":338488060255,"DuUIuCfrNsQx":881408763088,"LkY9v6w8sAF4":167630470089,"SOKyvCkHydi5":454734549612,"clnA0hRBjJLL":924924614934,"2BbhvMHJQLVS":351996531207,"cvLeiahyIYxd":955378154635,"Pw34hoyLcMuT":402284123401,"wWhbSCsjWPLC":925844375880,"7DDC26oTQLd1":874199657563,"tb79vo7w5cvf":298911575120,"Uj8mESTPbO26":507427400685,"yOMJNp5seqxK":454028208502,"ziSLUSpVZfUR":659884324693,"g2YOwtNf2KZJ":728224229153,"t2AmpEZXfmKR":640523638246,"zFpng9Ix0e9H":200892611339,"RAltHhu2MyZi":85913858726,"0bce5D7n2xYw":941889715376,"Eni2JClacltC":785203401357,"WU9Cu0dUVOYt":935084972528,"k3aNfhn05sPt":594201472081,"ZgegnCvrzcqp":93411599171,"DUNZyBsdxpKp":928617806048,"ItVMD4hMRNmS":339827545028,"yJ2KToOjNaJL":296592996654,"L08RFw3Br218":646899293533,"IhxUeLp3eR2v":341790880802,"QZoQmHV4VfKQ":683090265956,"9LYnlP0HnGXb":135351261632,"esQlqDaDuhjd":916663628551,"5WudzT5T8ebH":431952953702,"nUCDJdrTv49Y":146771797331,"VQ773shl21KZ":444927107245,"v31ojthIyZhA":93256018087,"JiwA2WayCyZk":388196892773,"a4d7j9f3vGla":322771402687,"knPuvi5vIYFc":21889362995,"gTDFeqB3U8so":670564384046,"6yWKvbb3JWFS":370266694354,"eBFz4rnM7AjT":705555466471,"uyDgGFoMfgCB":946084387835,"bC9dCbTuT4i0":315756755064,"aDLIdOMdnIL9":504798367669,"IVuoUf1Oz0C9":21204475719,"CAwntlJpJmzk":273697551676,"JKjC3KOLtoTQ":118732425633,"u7yEAuN1ZYKc":500961937931,"uksBdR7fF4wk":740197360199,"MRtF2dB2CDQE":587436253323,"q0pbNnNESOM1":743383310559,"W4kS81ZJmojK":817590754806,"SqwTN53HRFSV":750427222273,"JPU52Cnnw4qK":496222749035,"al2tLxoLmBbY":974115046113,"XBNFdXFdKjWS":10547682949,"YnY5JBA47ObX":115977735510,"vEsCetgPAlG7":917854045804,"LK97LiPJTDBm":562040922252,"MT8AM1PKWvLS":476354155155,"thLoL8H1IDjU":572680208003,"8iziUtlGupkM":911897845599,"zBXV5WH0DBin":292771284999,"7gY7DtAISk47":512589096578,"Y8VE15H1anWg":917614859646,"iPsYAuDRVjNO":646756284395,"tMOnSVwhv7hg":162991219580,"5T4O6FnCfb3n":235007384957,"NJJKnBdZ3RWt":247441868991,"Hr35XJyRSQNg":742528819129,"fcFIpw595ki2":256742218712,"hvPteSWIqKJ4":870438964091,"cqBBXTwY32ZU":974901792957,"hxndMRMkTwGM":832907450338,"RWFiNjggTLVJ":519180641749,"bmjb8GflM4BB":689800669256,"OWI3gD2m92S5":182555954779,"6LbsYxFSd5Km":783386950299,"bzpM6ogE50gE":831167679108,"cOu3bQy7UFAZ":72244795722,"xXb3zcCSflG0":675784017835,"gIbaq41gFaBW":327257802319,"ltQ2Fk0YHnrC":369770940230,"aDOBftuvnqUU":866921494996,"SDUPRlyyWObr":596459654164,"YDSvTdX0qHAF":229804755281,"iF4Dsc2rEw3f":520772346369,"3bv2ANKjuSK6":693966078782,"ui4wNhSGWCda":224047690732,"DaRcC2vHaF7C":708815234914,"yfNjoXKMVjFK":884531827130,"XwOwPvdWqv5J":238991453256,"wFPVKjVnpht0":427319246356,"vRpSIhHCQenf":284387945862,"th3CP6uGGrsu":967751327268,"LO0vt0sxNlc9":473708024297,"g6z4wyrz9btj":427670935090,"DKw1pNenAHZU":62480984380,"UHT4CqskBoFD":19251267000,"DvyrgxyKIvr7":94905804125,"zHwHT5MPs0WS":152079094714,"yJjQp5W5ZNqz":957595477297,"klNLZDTpEszk":288530987955,"y2WYhyu1VaLG":6560001303,"dMZENWZAsS5B":289509606461,"Fs7dCyx7e71O":607831538352,"azyRU03xHzef":53460241827,"ZiPbSSM3iNOf":927018012523,"CR7l9PKxoVLr":972688954127,"VujIiQMIOpCx":635735701124,"PJrL2yanWZjT":615920107815,"17F11l7qLfNb":578742756932,"wDVHL9aRQ5Qm":285055437162,"lS2rawVs2w8A":844876448989,"RQESK6P4LLuz":569545860899,"IIgWrOR27Llw":528352202287,"0Ll8B6if837x":13057073729,"apc5ZeqowFwl":695917204595,"3FQOAooZVgbw":149730846311,"WX43cxqomHLL":19678731189,"fCNTh4Ug6Aiz":365249460367,"i5EyTHaxAKvP":357324010726,"yMCGBSWbADHD":598638717415,"0Lg9Oxir9dIt":133174751530,"wB5GjaTh4ThT":30461776143,"gmeSHhe8GZSn":5806816683,"CWVdpoWjuZR0":953602981330,"yjbbESQkahRM":983378277285,"pdr2ZGtqd5Gf":143213687622,"kcDng2j1CMkz":300395500737,"kyF5flas9x4G":226634117233,"oe6Y6I9qnPC1":766675124006,"g0Yvf0vIxGX6":481438080187,"ig3kN473d5AU":422587251318,"DyDAZc9j2rg1":733572150481,"7ojAY3L5aXF2":133953727103,"ynQAnvDd2V3i":857542697161,"BBRx2Syu2vhm":326612517208,"1Lt5UpzziU0y":920148066763,"NqoOz0fIgF2r":702370297357,"j2JClQIvgbTy":544007826691,"AxEief40R2ZQ":839211254678,"p7bhEW8aNwko":533159816874,"XEusrFfzoLbO":592841948565,"uS80zrU5HbbU":208396512767,"wF0GbtxnDH5i":804310280271,"AffWnlz9G298":397463026658,"ZkgYl91JKhzT":249908455870,"YmXssFM0fQiA":939853685310,"s22H90lTCZd8":518284276794,"a7dRnNXR6RXH":793522755743,"Og5Dvde9NWjX":333583156825,"k3EFkXnk8hPG":674622346389,"iF6oUoOPmzte":622516320165,"AED4CXOhI8W3":151326010306,"KOU18wW5gc4F":1656874909,"XTNARnHwSgSe":748970788615,"zkUrSaC216Wx":940366863214,"izrsmtm9HU7Q":872650924675,"6wcYTSDiXqug":417851123491,"sthvpgPijW1b":156227058113,"KcoZqQlJDYyI":923953210493,"ygIMFgWOSNih":195339861185,"Y4ilZNPR6qZ1":457315858276,"pI9ae1DxkusP":303838061560,"WRtZR29jk6oG":785203082582,"kkZQ7ehltmlZ":817003058936,"wIQgkh8z85Qo":448893433619,"8jnC4WDY3NV8":421280513562,"gyTSgvG7TAoc":179765872532,"ef2ymO25aDZR":956037053461,"PJErgS5gp6HL":928945075596,"eoab83ZTuwz8":584953130857,"V0goA40DxGZw":943175459603,"PJnJsS87FR0F":723909014650,"MLTHz6SHY6KT":752007464482,"YdhzgkxuCr1Q":773520669876,"hfECrphb7q0T":544544947425,"7eaBujEXj1DD":194162775486,"C96C9W2IzFjR":914132428507,"yIHUWOSXt9PT":10794946906,"r9ctgvt8MxjL":458323188696,"ifyZl2GLlrQl":574253131354,"5wYqnS7C9rBQ":479663667783,"eLTS0rMQbOQ3":187238118884,"hqUlnkHP46dR":500134624359,"JqkdfRv0b6r6":337443891158,"tGcYlO14r05Y":186203015364,"RvkEsSk8qjXz":787420559069,"E2j5va1bIR0S":945499642145,"TF7RzTbYjRvy":577830489324,"xVveJ7x5xXcU":275490038238,"AFCckvKNBHDp":780784745217,"0mDaoAZIIkIu":442105635001,"QnzOqbvpzYij":502612958992,"OoW8KOi83cbZ":351540745308,"9pdKSspdCZg6":115785777095,"BAYR1rIWOFJh":68683953629,"R2H8HVmyMB98":474204939076,"SwMpIgW8qCEt":372664839305,"VX3Paxn4096A":484031143848,"cBfJYgfxApSc":287786899951,"yhWOUYKB0POh":861547192526,"krZssVzeI92T":94887166954,"iiILwOSoFV4H":59812825118,"3EuoYU0klY0b":693909238190,"yQjQjkU8S9FT":302239867350,"DAOdAS4tgEwx":365798863586,"K8M1svwjYmB7":329590807829,"JZTzftY86egY":932887394441,"IZdnE3ceVrZQ":145489471809,"4nZuq18bpdJF":258389254319,"SRZvzEIGDHCg":807525335255,"f8PeTjAdLF7w":300790712903,"rOT7WzJ6Knke":994967373541,"MDmMl9onCRjL":857535891003,"Wf9hljxiTBfc":507419969414,"okwx5rVGJvkI":90036387582,"QbsRZOnoynsO":873431675581,"kjylFVJ3Pvko":299935072174,"21eF4CfaTz6E":63518503609,"9Kv5HGNEiOOI":212917424455,"TdjUZSpCF90d":554372163718,"RfW71SihMFFo":975841230266,"tWwUCDpn38vE":396447681356,"TyAQMVwYEfkk":632012852026,"CMAnZLMhyHaG":76885326115,"Hw6x807kbTA7":862918885890,"mG54un9bY9na":55595761579,"ZiCfn6LYm5TP":7829301549,"dA1nXtqHlXwP":404518737103,"WsXv5yHgnRmv":68067124764,"d6OexpBvhwJP":161964719436,"p4XRdICeZu8S":321000007175,"WnU8oplHBMy2":713159505463,"M1OP27USb2Hl":209107318489,"ou9dqSSLWYYR":503117560929,"q8mOPgpwq1MK":850942494444,"EnBXMAIZpRcV":618915736140,"X5rJHdmE7uEK":598104576259,"WpWrryzAfgFt":554109472983,"5k6QA96fxs0R":828974750792,"Z3PhbebxBAq8":544176267318,"4RVhttvuj9DM":38866388897,"2AACnt1rXPIq":455615708527,"eMKbk1OtfKGr":753664426867,"teq9bIQNTv7t":862592005356,"Kagrp6O02Prt":588014529344,"CKd0nRtSavaw":353473612699,"LLzSArivTOez":515039159216,"SOqHlSqobj6O":545063216200,"j8iQsKjlhLYY":863759993965,"A8jQbm39wDER":695676383078,"LOjm4HSEM2Ag":242614506937,"TfMHaQLQRkFH":967708783514,"K8ISZo77Dah8":840516203186,"gl1I5QskQnua":491721065411,"aFjIqemI50Ly":512898367817,"O5H6fPReU5IG":205625483787,"KPaOSgD9RqrW":213516291520,"GS9JL2bRgozW":748019142115,"NXAJqsuqXvnV":969929825963,"iU6v6qmdJBm2":757036976577,"MGGgP2R8jfIh":826037050771,"wru8Hb7cMYpN":792437836596,"3G1k1jvTJCHE":957011894278,"Rt1UDauJTBDt":229434229779,"O9X3x4fZ0WYM":978191298442,"pgnNwgsNQK4l":345245377588,"kB6phe6iAZa7":275261870455,"1G584dXFS2nP":766789695066,"BIBKkehkGU15":607703576534,"B676NMk3rXzR":467996662693,"Mf8gHmQVmdoi":755615819306,"nza24Gudm3fR":987479016275,"enAtCOI558lI":705135471738,"tA2EQGhD4Anw":700507057163,"LDMusc424F54":27248155627,"6VCE4T8satXa":640791618485,"f7hYLXK92r71":27285934100,"9DdeWa4AA6q6":436948440678,"eQfqYOCNn7Ly":986116507040,"wfezXx525cEN":963444715448,"GdhoYhAa5rj4":732792887435,"icRxsEL9LIER":682591301373,"yqWbzfu0zHxa":101482519262,"1e5EM5qigsKV":226531963937,"bjNQihQzDsEB":969344777579,"ycXmdF843p49":219087392035,"RIsU9r1EEU47":891356850792,"KpLV9bvoSPtJ":631571966416,"7e8pmGc0oLit":867708866488,"LwztG64dX0ry":994688140817,"7BP5vgGWcN7k":444302748795,"VcYhIFPTFgqs":612352915025,"FsNt4YmZC5xa":378990397186,"cNkqotjofAC5":521839368530,"TwteywDsozl7":737305970796,"RTYz1g1b54PG":177375830156,"wp6pFP2arzgP":564759235618,"neAFF90hbv5G":399162049225,"4zMvYV10Xje5":576149324192,"7YJxlKHeKTxq":236598397393,"QSzv5dqq8lGT":262047420853,"TAWXybwnCARh":528657612286,"mOlDeGAbf2IT":896586845870,"ka5UQBATwoCa":210638640517,"lUfVIU9tKeRB":276648804336,"288zbCiXcjQY":710506600765,"Sjvh2BuKBz3e":949941760700,"Zy0ipo92sOlw":692043931352,"avTCGCWbs9JD":335925390315,"o6gKE3aeE5Cs":768795790601,"qlhun3wdbS5O":656203668595,"GCnthfnmARwn":919730302828,"TNn8uoMzE3ow":270608549505,"rdyZ5EiURtv8":868788374259,"n2Vf322EVZRZ":83648468858,"oXmeGcsPrr95":630117824274,"yBrUZIlf4kyi":836080825094,"2HxblyRPvQMu":792275760074,"kepbNHYWwR2c":742693892993,"O2CfMD0m8fWa":30390140964,"bJLaDih0UN2x":136156632662,"Zc60NnJAS0uE":898211840262,"kf41vRg8Vyxj":528579366159,"udMputN0Etza":114671749875,"Xq1I3xSYYaQW":145542042322,"H18ugKCUkFtJ":425206688753,"yS0qiU7CXLRs":861264414732,"lS6QU3dZ06yN":665059554761,"csrlACNPRbAq":276261012461,"vFYHqkJrdP7X":391340484886,"qvr9tcByMhYx":668908335492,"OVJyGhaAawoc":777428138605,"XGqy5hXCA0DC":731373674468,"eWqDRrdrbKdU":882034530695,"NGtMKK0k8h2K":225397085015,"TobgFj64x4cZ":738830583730,"9DFIpJveSxA3":119645106031,"GbmCMW6NBe3Y":869034065121,"pUjopmMEnjgn":766655019767,"WDS7vTB2J17Z":747348413362,"VntOztXFl4J6":417255072319,"Qn5l2c2LJCkA":771094904150,"aZ6xgbplGdVn":420727831349,"lZk6FV7hsmMG":250605593216,"aKx8IKBzCP03":149138825129,"KovQGP6XfHg1":381733874467,"Tp8FA90cfeik":598768372151,"zg3B2VYylxQH":38269170552,"ceep9IZvn8w0":704353714897,"SZAHOlgIQptV":484725954326,"0TnndgthqOJT":178236688406,"bxl9GD8I9moL":640291158243,"BnmVsb3fxy8c":713681242172,"XngZmQCLPZVI":227021196956,"wv2jPBegU7ym":463808127079,"EFC6FADl9gfl":182753548452,"JueFYjfWHMDg":699713805850,"FGzfupfrNOQF":471144921513,"fzXa4UnZTxUQ":184096260119,"pQdRqjl6Gzu4":949767198937,"c5WjxtTyJkRF":170154840132,"8tDKlUGBXfpR":454308000105,"Pad5jQOoGPtU":848782804811,"jEvzQXOlJnx1":981533068448,"rveTulu4tz8r":585685715318,"f1Pcn0LkI2Do":379567332087,"edx4X95aEWft":72956127587,"DZQvtFOXr8Ho":605248501221,"rIS9sJKykta6":332076245643,"wIHuXp17K5Af":593917233814,"JmemsRnKDrkN":437937217472,"jYkFds4xPplP":260067080189,"t5uvNwQxXndW":598513865191,"cddoruhRrTz6":328632167751,"Bey87e2w5flE":423308287383,"0lq2kGfWTZKx":437104959529,"zFxZmC3AMIpu":683408851305,"PKUhjofBojii":222647336902,"ZSsePxqHdmjB":910470467090,"ytqG4XmRTbJB":635208008451,"gfoZ3qMhzYgs":475880465522,"OSDseixGEHPk":999090982357,"CI86GxUw81uE":892996672735,"y1rRQMaJXeGK":790805921163,"79H9GfjzLXMO":161371491702,"F7RrcjxOIGKp":727792913465,"VNKOHTcLOtYt":50267533421,"jlk62Vn2tC5N":136752877645,"3TQoRCRe8UXX":921716165872,"3Eka5akYTj57":753223262721,"7Cn7qqppqr9l":148907160211,"6z2vfMy8p9lN":374793289975,"hskrh85Askdd":272352404797,"SVSlcGneVOH8":364825353431,"5Jvf6hyQY8kG":61806272109,"suSWzdgVz8XQ":151644687042,"e3TLLn248Y5A":287450637446,"v0t3mtNyagHV":423414486047,"M155DXN25UTg":436358797779,"XftJbw6NenDr":776518958600,"CqcssqHKKAUf":74579309606,"Yo82b9Fn2KHI":212882336300,"yVK9M9yNdfpF":449340998586,"dPMCWOjL002O":95961425360,"HsszwffbbWvt":891362866916,"Zo9aEGtoygIO":390547356534,"swhmmQZk2lky":690904025998,"skcaTwMijSQG":326490147470,"fXkTE5MNBLEf":28269715747,"MP2qxi4krswZ":722914513345,"g4EW9LXzDYVy":147511598635,"Nksa7ZitP0Ir":854763011724,"XSeC8tR0QNDw":660482675727,"8oQ5agMr7UAo":967622163082,"HkQswZ0EGTeT":215245879968,"xCGSodUeXI00":54704811601,"vN0RepFH5UA8":307374108791,"e1ShowuMdQjj":658513850943,"tpHr0hJcJFu6":475763484804,"OgqE11v7hPfI":327178979191,"AEh5WAu49eGm":9826060669,"gZ3Mtx4EYcfr":64159800827,"d21H5UL1DCRF":187429100284,"oytsUshrJitX":619088781504,"LSe0Gabh3h6B":665832673852,"iliIc179JJhE":42609771754,"qItTWabYKpu1":222069225039,"cZeHSrnv7nIQ":727572609951,"SbVrbSoYDgYp":341838625903,"e4F9TtvpHzuf":205959646977,"7PRqyTgKSUHb":645968983225,"yV9XRMfYNg4j":234677257978,"rrLl7tYXN5et":137549744162,"Gf90JdOt3UD4":143048057117,"5uVNByQ2CfCQ":236519617694,"GDpWHfgZRJvl":115065390972,"aPuERFvkfnLZ":689025527476,"v2vAoy0mNZUG":304871966984,"3kcGZVoR7ygV":390252585688,"xEWnk9pDKiKs":944886597950,"YD573uDqWVfU":859397608514,"MOTt8ulEGCHn":491817776173,"kORNLEMOgo6b":526072144059,"dpGzQs0YWIAS":766793384636,"ESQQdA7a3rrO":236294905726,"3S3xawhYpEGL":719655352640,"rRCMCQQsDFby":614913388787,"bWlSfPxzX1iV":866915979977,"gqlqyt8B0zxS":851757770894,"8Do8WgxxjcgX":900570420826,"7FgGxL5Sht7y":341394253559,"OJJoc0lgz5xB":974023046187,"PL2D4sjmiFZr":340818281876,"416c2V9NcwDI":965973558866,"zCiDh3hoL7eB":636100721309,"EP14kQVfEVT1":709398087453,"OhmbUUXBZmlR":715120638554,"6SrHMPSbzjGS":265049359690,"ugIZtkxTrHAB":944518774064,"l2rBkRW1H1kU":91245263706,"xvnJfZurRVUQ":834023092185,"sU2Uwgz1bNFM":292482956477,"Cd4lpxD1tf5w":733826295689,"tiVcGTkeE0VM":401129984537,"caG5ldHzN7Qj":619318889684,"XOAEitUQb52L":847402658269,"6BlWY9XIe39W":104643337707,"4RvOkirsrHSv":944056958647,"Q8glraKCNn1T":627681507987,"CpLX4UIZYzO0":688248043756,"Q2p7h5pTVyW5":715133247456,"JjgolK2glzqh":41176075389,"bv3WFLZeJ7Er":185727865416,"G5iqVahTwPma":414330041010,"bw4RO6aESbdo":741631268546,"zDQRGZvVQcFc":575865801701,"OSFO3pDnyK12":520835998060,"p9uD9GGNLlK7":754206209066,"PpgwRx5OVgns":493064333109,"kvfG3uY7f3zv":692028212559,"JszvbW1Mtcw7":415742144812,"tsaNHvlYJ45G":977407746080,"3mS2hg9OhAbG":392736654069,"eKixAt4SbBbh":291450109142,"E43JyMDpBKzd":683158589113,"lFQR7Jz5rXyK":113239720636,"4wlA5RMleoZ5":469321569496,"k3fx0Lqu5aSs":6789763350,"5uc0aJqiyY9F":285658315357,"m4Z74DrPlUon":963339903119,"AykWyUuEHmQ4":730202683906,"SuOcEMYR0NoP":264043751707,"5T6fIsze49Qp":525673924028,"ZuynyhlKERvI":688882489151,"vJVqdLFc3Bfy":757744083351,"deqkU9Hy5ng0":917318330536,"YisUHdM0j4PV":139007659629,"YAb6kACSCNMp":991600306949,"RR9Vt7TgIkhW":517185116891,"GOsmH4rQ062y":710697850903,"gqrXKMWMje5K":76570295065,"xvuHVjJuDl58":740429990217,"eyEpOSxWg78R":940537801592,"yA04OvxXNac7":914345375139,"bbE7uvrL7GYV":585199886025,"ejSBuqfFiJOS":295456798548,"UsfA0IzIAszA":696556285270,"WV9P8v7SsACA":588069085310,"pimRyYlIbr4F":115003489952,"2ZT5O7qU3gR1":456768091810,"Xz7L67ut0P3u":771217736170,"1yudlIW3pdV9":153616026177,"IFIHFORlpGfC":973862817493,"8DtSJwgmpbFO":635597596398,"6Lijv7D8aZbN":30730115525,"4d4tdVwbHriT":134714679002,"XiSgzVuwp5hr":160467483834,"6oHJt6cyNAfN":499568573740,"uJWqy4Tsf2Vk":92343961667,"ghrZQADJEWuC":208481576497,"56jVPTI5liu1":456663657903,"Eid8aU5asVa9":39857279643,"uW7iMLthk2WY":353657614698,"1RmWlqtMhfbe":501226418222,"qfdBl5ZL1jIf":47971510257,"dpjNKRzcgTEC":637438620777,"Um0ox7AL9NrL":514583408531,"cQ4MuoMFq3xo":15015697753,"PAvz9WUKNNuT":488998195906,"rtNilX1wVUl3":761388712171,"fn35i6Ehy5o3":629580367720,"UfyCbBXN78Qy":958821992736,"bjTxSqKyrqeo":308013289566,"xbLr06MAqbG5":296417223224,"pMPhw2jYJO5k":462542132246,"U9G7TjmSvOaX":486951008587,"gEEljeuujbiB":419264520647,"cLkXVWPLOMqy":669978709551,"gjg2eP1Iy2dF":975659729362,"1QkhLSVWYktX":66753568491,"fqwHQqsqJWTg":556583554936,"uAfHtg9vA3aa":926938044483,"qEC3QCw7Dcyj":367134097706,"KupwwEtcrDJ9":201197397809,"kgRk4cdL0LM5":213469490144,"tbIrPk29IYjp":608502100539,"2PtsI3skECNE":261546304980,"QwG3sRFTfMoW":912780588194,"rgK88yh4xA3I":442575999039,"qRG7i1ZQ4zQz":44201169389,"jbBjE5Fe4zZR":793743994418,"fGKTsYQHtXxr":631185221640,"CIlpbq9zeuTT":892537792993,"xIDbPySID49p":142771564604,"kbPhq320PzhO":36166106050,"wAVwrOuxPvcx":503394957616,"XFI6QBgGU4f2":873909005185,"zJkP6Gvd9khF":173173623147,"Q2YFhsxOM2yr":780873161616,"tgTgW8qTdrKx":637859618822,"i6Zz8ry58D89":568071708850,"BFuUNpRuqVLP":607361283456,"W3l1Hkmxc1J9":669813497508,"a7HGevqsLvjt":287987345272,"fl1BrZKIGuY5":708047886501,"OTNUO42SVMpc":317843708843,"ja9C8BTA5X2p":197900493794,"prTPTiQggsVf":356189866060,"WHrUm7PyaCsv":890051077933,"P6BglCepmoKc":833160525286,"0AizjqRXNbAc":689472460051,"Koxn5DfuOoc6":381203213099,"RxKhtMssDE62":834296668676,"wV0lceDSOY2j":541762443757,"WWjGDMbGrTTO":438599633489,"DBdjIzMW6Xwl":971412316174,"vpn0uBKE4XLE":712988713638,"wM4LQNkLQvwT":675784909174,"cMebHj1j8OeQ":769623359673,"HH9JqXzkCovp":617896750235,"JqZAOTLqrJKm":432927134147,"5T0fTHL7YaW9":747445532466,"ixXuee7Xsnbq":176591135021,"1Hu9BOjbbNOY":483645036009,"uC4raxQ3m5fI":308474837342,"chLtAo0g5S00":179222334158,"gqlZdesfKIYz":516520349052,"A7Rvmxp5Sf7D":400281283907,"rpaRUhO68Ozq":899850401573,"8zIzQRTxckJT":830031734663,"rJx13EGdN0d0":598353915743,"B9O8rwEr4m7e":832245928604,"E07Mrkg7szMg":159920754838,"MVvu8dbjIPSg":706408539385,"UkDVylqbi2Ul":168249891758,"ta3tP1UhmlFK":838457225485,"vnnCl2mew3yt":35808584874,"DW4Ca1oEyTnJ":85675897625,"EyR8clVmBtz0":471371474555,"X80Bxsh4rd6r":329759364638,"J0a0hyMEKtOb":767244876538,"WzkU8ZLWnPLC":595956984960,"xb6zdIrCcRPh":953862929042,"fh8TRX4OKP3q":282802150,"xExypTNRWEtK":62155542725,"u4zvOPHhGiss":930096747666,"4z4DFKpnOV3X":756071948659,"46RBRRrKNWNI":563338137889,"Tc48IgzYbkRf":230476201518,"SC39TkVOhZ99":734001339682,"nCtkU9bNfVd4":849953967583,"WtjXv91fToi0":733989523495,"F7MymaDQNW55":823147613004,"CZOY38YiBk8b":53687246141,"Aj0FQcheQVLJ":538361942992,"Z4w7l3Hpi2Qx":595202723620,"b6STnF64yLzI":401386583643,"NVLYVpJ3fvI4":731764863555,"2JAcBO09Nl2t":409289862694,"EnnBsSpPRCEU":15281562298,"4uCKC7EodAPq":25710745445,"PTmmrw0Ll7yx":341413413078,"cclRepfDIaHA":100777475159,"p5NONbpiXSKL":852019535563,"0dFn266CcqC6":110042847948,"kjPnjreu3f2P":796786406151,"GLQpNLAvbLXP":769438704515,"ihb1iyX4zfEa":608056324327,"3sAE108dqBTs":10299843949,"0DSpE67yK9qF":535607287001,"qOlVLUgATIzs":200622649148,"vqtuTfDg2a8h":446368665697,"BZGrvTlVAEhX":482325409888,"CQ124NgtGdHG":795047941153,"Fqd0FWpW6JeW":102804367012,"czY3vGYhds2I":83355195710,"01nzE5BtN7f0":100769855684,"JuZlEnJk19at":705736285039,"4TLk3SH41QXe":862763536095,"vQ6Hz4sHldfO":421867635528,"5oJaiM668hzz":20589043470,"u6iPmjV2FwlC":701503217886,"MqS2Or735Z3U":772281224726,"ze0exVky0Rp8":93339700278,"Mm5NRztFl2ie":534710232180,"JqKT1XaOgcnQ":229071142070,"tKD9ixTsrjpT":609898332144,"PUMrYWs7h8US":419724125989,"VkLKuxF4uXui":206501825526,"0D8rstkgAVoo":728301330326,"Zh8HS5MelqrO":473653985488,"sHAzAbFikFdx":62145187453,"tNbM2fQGyY9V":26721116398,"VSdYaOKYIefL":122262224530,"6Hp0zerjJ62h":178322666637,"hhS6HP2OkK6a":480443202288,"DhZz5pdUvOOw":356453946779,"iMM2kucM9CpI":216563614096,"xQPSXxoGxwfd":721622115786,"ckYEffpIRj2R":92687478124,"DLiyJiw6Nufq":936785925050,"73YBR2dqtV8G":997626116869,"bxxCGkWR0hWw":895357630721,"GbkCsahoWUk3":71911787582,"wJbq03HZFMWB":424713670576,"VoNbGvWGz3tm":368011754925,"4Ot4t4StUDtY":924038594205,"mLNdaSJJ1QLG":897769363614,"f4O9L7ESC5BE":17525698175,"zQ1XIQs8qxlu":24397953646,"d93I8tCszh9R":52404373543,"2tMRRZChAjdS":553279478705,"TylJAgpE9aqk":135781002973,"c608v7vvlppK":537940855105,"weRP8NUJJNQX":124028133267,"FeSkWdpCWx8Y":747543565535,"AqpcWGqNCpld":164878632322,"qoBbe9q0vARH":331683727365,"3NyIgaOIU8Sq":179884512048,"xNmrvmTyMulv":568737711203,"2GubUq97oJjD":476854286961,"qvoA3VjxULTm":402482902630,"XfawZDgCvZR5":719635169126,"2ui1v8trCPYd":639178775580,"Tyi6RKVQs3Tu":829971471318,"VTzu1lOCJMIo":436704865963,"6QFCD7jb2FiN":243977005499,"Tt2oosQdD649":815447136758,"Oijpep9AL1C8":187509258777,"C2YUA25mWacJ":789451474886,"ktw928zHMT8k":43830816511,"ezxmx6ZXW5X5":614580149292,"JaHtsv58ePh9":377078620947,"2ZlkvUT1zAFV":715263336744,"EyZrXV2tYv6s":453102897452,"5vZbI0qAfsHJ":343211780743,"OEWKJ8ZzGoVO":840636108730,"I7tZPLwbnw3n":460671799801,"i5IzBRop1aeX":783284530178,"aD6gsszErCfo":952377955917,"VnmILKVvKmqs":178767068953,"hq0g4gzZwumG":340273876682,"o1UIYR4L1tAF":399771379868,"QPWyUfpeE7c2":987086200596,"fokuOulZ3aLA":269411965244,"pZgh6oTAfUlC":839007694739,"RwBWhqYqGiTL":751266292749,"OA8F1IcE5fP4":518749487770,"hPduQWuKeEmP":990782054070,"yKC8HCzTE3Cp":438110185480,"X7iTBi0rSI7M":174177884781,"CpA4Um39tXF2":839334958869,"54XiWVnJWKjc":81668648657,"zYnuGy0GYaWJ":571939156439,"XfbDIun4ELtU":502747169377,"hr07u8ZiNTOW":450341024166,"jha9m9c7Zqd7":527667885116,"GISL0H5LNqm6":330730484220,"dRLASzwmZiD5":874463979807,"XlOtdx5NsIUe":597305649440,"cSC0oRBiJi1j":313902722729,"d4DZLYOKbOWc":469990330341,"5K2Fimf7SCAR":549231620129,"n4dQyx5ovfhf":82885732877,"wpVxGe81spl8":647556821794,"q7eC0Mp9NY06":863571731949,"mFw0rpiIloRn":577367270033,"N1ZC8Ou4B8J5":429683726642,"hI5NJ0ewgI1t":74610168268,"sTfchFTPbcz5":691436451572,"fr1rAhnVK3oS":188815233512,"uBnKSS7sd8LN":654363332178,"FwW8Ng6BaYnJ":818043558407,"LPmwvoO2DFHq":642517982976,"LJ7VlZk9YTum":956769536790,"kMuezMFDA4u1":448928385602,"AAZAbEWWY1tQ":519515091261,"xXttypc4umRr":229462094923,"KKdlMvjoWZp5":543477636890,"KjcinxDk0orE":468260890172,"Er0RQY4Uz6zJ":275820627078,"rakQltnotiDU":983904584998,"NGgkF6lk3DbR":14806104250,"eMmp1xsSJ0Cp":103083245707,"U4QDD1wWfGRw":512364006816,"Wgnkj62MSAFN":572707968197,"DrqrMRXj81Up":72654661667,"OxzCYkeCfhUO":896727021196,"3ZcdSQoLc4Wz":896516926128,"Bs5KydgRS1mq":307459856989,"vg7d2zZC63Db":426225007213,"RAVj07k4NCuL":432554945264,"2gmMopDrwA8W":51537853940,"LcuBkSQgsReS":55283913691,"3JeFBhlbKWFL":291843917755,"yV9KX18KY6Gm":846174785310,"ZOFmESYyriUw":681159480826,"atG1UiJB93cK":66415866134,"QdumQS7vhodJ":756104118440,"ANTCQxuCGOZF":795367701055,"MRrfqIGlVXoj":603226152133,"L0mQwGrRlsH2":681258787186,"TVIu2U2U838Y":441376043960,"3F3OLtSLwQtB":858920547113,"e95qYzFmsHf9":524786439244,"xNIBvIobnxR7":127654440198,"c7QGMcy3KLv0":981728728588,"SxPd28xFrPRl":385678832575,"jlbW3oKYQSd6":576038171136,"YiNSZWqxcOaA":714001231231,"z9e0hN5agzeF":296498002157,"RnEXuyQFjooN":529914555308,"e0hzSVBI4Fks":839604095965,"JeNaqEMZGa3j":937975865755,"YA8LStiTb6Jx":915547250994,"RNX4VN4GhvkN":396850106217,"04rsY5nDJQyg":983851580784,"AxSYShgtEmfl":649807788599,"UmuuqhcOqMUI":14023657417,"P839Tbfe1QtN":633528574214,"sVYbdgHWh6p0":122132533960,"mz1n6enhjmXC":457576163943,"x8mBI2bZGviS":166000409781,"dMqpkuTSTgjb":648553565779,"Dan0mK9UJVdG":93414314992,"NMmDdBzTHkb8":633818339784,"WuT4mGd32tNh":509817914543,"jZhyjnpcrA39":736074125002,"7jeO0v47AdFH":325397265353,"v7zzs8852Tt3":1548987552,"j3j22ZvWBAd3":976013388010,"6mxGYp0ZD4qV":957790256169,"eKu3GwHybeja":409270213413,"W6G6SXRJ23r2":694116196358,"terhDI5EiGe2":958827741585,"6md8eqQoTxEH":615577791524,"wDGkZli8U1zp":234825317755,"xgRdp6C3SxXL":569793684184,"dW1bNvS5QXKo":465627724078,"A0gpB7Ejj3Bj":524484503597,"e6cptCOzgxbC":76809955549,"TuIPdaqx7sPN":635531715794,"xJIn6AbVMEWa":375025613353,"BqmbdAW53URm":853077425027,"tLMZhwaMjmby":742846918488,"J3Z896voOUfj":831352346396,"p0F7g0TLHiCz":672620278145,"bi8DJOiRtyot":283623090306,"SUoTQwtRL9RC":620859312880,"cPffZLVJu4z1":106666668573,"yMJuiCJ2HV41":144310668542,"T4dtQB8gfEPd":155920138516,"ruLiLWFiNj2L":185730689958,"AR3ZiMJX3mzt":517139252362,"JjDV0N59f71d":910948770371,"R2bgKkLnA43M":328892545153,"1e1PvZJ2CpFi":677779802570,"kEMIMCjOTxZG":515304421002,"XwHmbEPr9H58":390133161649,"I4rrbGNxdSXa":806325163278,"8nvgIDresnZU":740945686555,"37Ssf2JqneJi":549666410666,"hKmfajd0Lx07":976001689108,"1Y8rngz6PCbc":755484479307,"PJQC3GfevLVV":858232405315,"fgqNvEodM92X":335633408282,"FAzISik7U51u":755879399224,"R57HW1PyWc6b":45200129120,"Giyt7TLB6daT":524674763017,"VPwohFRETrlG":780210423435,"KkEXIGtssMeF":656255553965,"CYN7YLRuuO8Y":15037626946,"z9vqtA7gxP2p":930980869898,"vGFUHr9DPRx0":308582474571,"VnWo3jQroQlC":527555216591,"t85F4slA4Slq":962380262861,"Ku9f07PIyBaF":275312708732,"sPAblzl2NZMQ":309403728496,"0RytCD4PS1F2":110128609638,"zWK9Uj6Xifib":385170204321,"Risr8kDhXrfo":570149056696,"Dx5Gv3TzFyor":336518239627,"ASTALLeZXW7R":826105147383,"VX8Ssqjj95Cg":739572145121,"T1DxbDYBvgzl":202751725261,"ywSXEw2YMfUu":65515094013,"xfXmGq5LPecT":534798033817,"agiHJmld7JZT":991888727304,"MZPXvI5CQjqP":295346844691,"eWQEPBW1InCN":665227582834,"512T9NIdUAnK":931513523572,"YuxS7pgsT40Z":36644396269,"cNK7DtFadLOm":625518729047,"Vfn4EiZ5N8H9":202275759603,"NxWVgYub0Aa9":776793185915,"GUcIo3tRpXjQ":586830788729,"IJS1FI7uk6w3":332522646826,"60JZNbsLHHeW":839819814691,"apNtSa00sVm4":884478490377,"gNGch1n6Q9nH":715229125704,"dTE9R26hkXAF":236502494520,"lX1SwIDoSbmy":43690559220,"frhGDwIYBFks":38060477778,"rHR7pRxlxau7":709549854296,"s0wVAUPYWmLj":850447764568,"3IAIzNfMjyNV":914687685377,"digKWfimSDEc":507793526294,"UllYcDAP6eE8":936046225249,"zuwkzwmnYhXn":667783398333,"3KJ3pYd8icSD":802871152788,"7mSgRaT3nerl":756956228935,"QlkBsMVNLbZU":65724150075,"zi3xViM3gaFK":681632118003,"RJMzIOcbZjmf":841413338734,"wYeeBSZ5t1Pj":578963523902,"adtj3xD21nuh":115268588625,"SVTVqdI2hHrv":550728635388,"8ILbkSIeaYIt":711533110408,"5hRZDnKBFbUy":3183518911,"jmMoSZzEZMCA":822427824764,"97G5OwYvpkUQ":960714339296,"qESHSZRwg7N5":578689866574,"5hJ9MvhQBswg":133465024791,"JgFPofmUY4Zo":674024971865,"K3BP7eria7yH":368324788782,"9yMcKyalwL0J":681704741263,"VguZz4eKsIJL":692205014524,"mmS9gqM5Fdfz":28030296718,"iN6kEvHMLXMk":321313817103,"mYSE5rS71YQC":343567565073,"aHlYhqUfOrc7":138362416288,"8l1WdkvldkES":925254840525,"5XuuN9n0mp8w":956506669411,"OeVjXNjam6dW":637532009931,"Q2MAAwFQOnPm":23981026767,"5L7Z9EBxZNwX":503231811991,"02iSk9oySfWy":276122482464,"TScbXFEyC9M2":666485200174,"3WpRb0w43h34":624321956527,"sP1sgP9pb5uM":664276710980,"tWB4xZ3iudk5":166185697527,"QF4YKaGLr3Oe":588350986089,"2N31zGdE1pNH":955481636901,"VLsrHyH9c1ML":167194087127,"5zpjN39Hsl22":343008994099,"3Nc1zaPdQhLy":461612797398,"q7T1V65K4ENt":915830184077,"zY99mJWZYPXR":140579762263,"6QoLqaNjhNnQ":67602022646,"DLBRZ2jChiX5":836174896438,"7TqZwL62fRYt":460220872656,"tRbLemB89y6R":192342962262,"JCDrcUk3DQvJ":780493129856,"0rtW5jgnz57U":533677628996,"IpQKGdSPBKKt":663483092889,"nPILvFwyl0Zj":386611513988,"ssD1hzxEVkf6":245078424094,"aW6K1R5Y1ijU":862121086764,"NH0eolijhONF":800799016749,"seZBlaEAgT9h":246106500064,"gkXuNfek5kpP":987249229421,"oHMz0eauVFZX":813887146670,"CyjsKzJSaqq1":801773187055,"0VxgIifRCwSW":76533996879,"fAQ7I3TSoDFi":642109170688,"KhEsGCURY29W":237980275241,"Ky6l29knOJ8L":95989605814,"KsrZtXDcv9wi":972189344520,"KocYQcPcwi2K":401203680434,"2trnsX5T0DmP":633166672371,"hF67MEjowhB5":414404516399,"2FpSnoRElGop":364456119977,"c9gqzlCnkMFc":348607841637,"HE61Byk4HO2K":684479903308,"LVcPLeiMGFog":895561514235,"x7O2KeF8YRwQ":244483163250,"IHOwOCE0v9z6":629164909561,"FAtTerhWm31O":669470216689,"9N8yZpiso42t":254516511704,"BUuYsTYyWxwV":575144559324,"umeg6SXpbWn7":55675788367,"dsUNGA5HD9c2":658398237428,"3k1SNrSq97IY":791004107489,"WwYL4JrVWb5w":747508543612,"N1Di0n8XwNfe":538301557415,"3QUTTTaQd1s0":994315431295,"lVpJE1G4MXLp":235185076087,"9wwMr4utCC0S":508686936880,"hhODuGjEI5ZT":88804957612,"2E7kbyNVepgB":579584942496,"pZnYOpMoEwob":865065052955,"pVCteootRZVL":807345078263,"gwM7zid7JAGC":797122768211,"9K4dn6ESw05R":978672235926,"b25zm6zUOT4T":348999422152,"RnXUS8SZsRiV":873732453162,"r20C08wxWXM4":979607048222,"D163QZa0wUDI":555888566998,"IwEAC0FssaND":395694930330,"6vycNe31BbHx":646735845296,"MpfoZ2jo3JEK":853000750715,"6aUkhPUeB3nh":426765083446,"2xgxDdf0t6fC":931376927979,"HdhBWnLKgh4y":872020138650,"5XdBIt6D5YDb":440599491075,"DUNkxDVIGWfH":675998126036,"Vz4RIPhLPITJ":930191698489,"GqczVA0aayBo":642609864859,"Ls7ZjXeGa6oo":308303938349,"2vZSXxZKdfQK":614695313701,"JoVwCMayL6Fn":407746894733,"zWBuQncNDs5M":178700495294,"Ee3BrYZzTTCJ":495387573445,"8ESZNSAMK10R":987463987091,"iqFGbhACOniK":929033167033,"fQKwfChuJr8Y":800761983974,"BKMOo28fw8h8":106870940134,"mFIETd1QpBxC":855544827935,"ZxEbuxp9DEnJ":519885348519,"loqZoVfxmZhd":470005968691,"dUqFtIozfHI5":527076866499,"Dj8nSFXJv6z2":579604148538,"QMWvOwiopTc8":829175533423,"Oza65Nhr0FH8":13489870163,"EIACc40na9yF":168503840323,"wO1uvJaZDsGO":652397776374,"b78OwAkePbZ7":4711049082,"A7T7BHxvjRKV":890608985201,"hQvaa5jnHFDH":753694180918,"otnZq5PFWvks":386006388993,"1QXr0pKOIiX1":772479987442,"QfEawUM6b64K":945768293838,"wzYJNmva7ljq":347415623026,"8awF1drMqbLp":759766287760,"wprgATb09JYi":153892571260,"0fHv6zdY8DPt":498762060330,"uwJy5DmThKQ0":41159806846,"78wREyyHXHo8":865350117396,"J9XkKaXxkOGv":503717331637,"FgqVXQxnKno8":12467992284,"CstabMQ4gIH1":12581119577,"Dl1yEV4oD0bT":86651141077,"TPiuvXSZWOgj":443566636030,"IrNUUEa0Pawi":677816502360,"ZoTZwkyVL8bK":777494344413,"S9vGenBmJSjg":545629910459,"6z2dYsxn7tR8":561478443629,"4AejE921cOKL":936760912981,"jnXLprsqkMSx":764181210291,"olG5I6NfHsyy":530424159020,"KvXuPX6xPCe4":781788363599,"jmEbmg76jh0k":784236403154,"oIvZfsTDCLFJ":421798390889,"hzMOVuxgY7FI":8323933847,"MCSfARRs7L14":867283843609,"sMmo1h87JNEo":417815558275,"VJVJiaDF3zF6":455411198846,"UVxDTCtEGRmW":220089722440,"b2agkf56T3ok":368308236825,"lCRXJd6xM9O5":339246659406,"dv9SM4VaN3nY":862675184775,"BLWOQVtwxwEr":817183542391,"a4tbTJEnmFGK":524249404188,"FAIQ3w7sfZAO":830131726978,"Ou9gz3vnTcoJ":703008870729,"gqLpRzAqS4U6":990363389473,"Ra6MK4n9Y1aq":733644421857,"cnX8mBwzDlyb":346836673187,"rleG5kOxuslb":117073662683,"eHiGzZj3gnkP":819464365069,"S95obFuCo6P0":218296720582,"bEroThZr09eC":961548575822,"ZCQw0fvPFlGP":510871658743,"eEcSQVr7rhQ7":820732124612,"BSaT7yKIrec8":319575554336,"tBHK2YV1VmxF":467672591634,"Ak0bPpSGln33":595708001233,"CMfFJMkWOWXg":768046260413,"Ml9dkMx8a5sy":238158837298,"BNeMcZmtfZGn":140984788587,"YXRaBaMAV4Pj":281884862617,"wbIfY0S9WeWh":334576223383,"iqylR8qaHHEH":647107468900,"Wsw2jViQyLRK":747294707729,"BitaDuGdp2Nh":911822673531,"cPkdp8gnm41G":849208610645,"jbO50f2fJzZd":769628140110,"RWvSXFUBkUdv":381636054832,"9OcxDiiLXaKI":526632979518,"oyLGt1fkjffD":76303854955,"4ufufWgA8ZbI":617835770786,"nBf3y8hXDELk":973504128950,"t24Qo24XCoFm":299230584131,"mroeCuE8z8K7":443959643957,"YF1X4i0eA2Xo":861150855950,"KTDSNmes2NLk":276985856698,"isWp9vM8SmS5":667217170959,"wd4uMZoWoK1P":476998527957,"VA5GigAn2W0P":729018291969,"DDdlEUZrDLaC":183058145987,"IvIPB9rrYDRo":277784702396,"vbbYu0w1wTc0":62936980413,"PbIMJNM9uvIb":382946654793,"8kTwTmwHspS1":945314997680,"Nap9518euzZJ":46281021656,"4Fpj3SqQM5lI":733453746223,"a5zwzkFfYQij":50523920282,"3KyabEHrs3Mu":133011276440,"bpqA1hp6FgKo":838816177293,"wsar4kGBqvDz":575247097438,"8GUea2f7pJPB":469458818970,"FmItcfN7n8Vj":60169268192,"yGt2bFrh88Vu":911816923628,"xBmD3kQIzo6x":682064565322,"hwMgp8vYHu1M":145792414424,"gk51uxjdrHqQ":512672846368,"0U2Ik2PFvdUd":339899550164,"HMajnwTA2iKv":579656502419,"IQ4a4GSlRIhv":930130838270,"g30puLRF0gMK":193935148982,"SD0Hz4TTKDxA":210034053757,"5UQINcNQ8ifO":620530464209,"sz44uHOpC1sD":442785178025,"ySsYNDWsVEUd":504823309243,"3zkrYrpy5FDB":778383710243,"6dJA8aHRlllA":902076287406,"4XUmlXxBdteh":741035543362,"TcyugXX0lL2h":555585126570,"iTPWmaQqDKEz":27619071569,"j9el6pEabsQe":897025884859,"5UPphbNScjVa":52638098366,"YSFBEVweak0f":366969301160,"bGikrxAmjOeu":598921476354,"4vX8MYAgm6PO":983086857440,"R7HLLsDBenCa":678303664127,"5VlCSaeF7Bsb":579526378329,"dc9ALrozl08Y":223799830893,"T0ZUXY0mTEOa":946413891671,"e8neQ8xFQqYv":649318798088,"ortUP6pxnMhh":758274898660,"I3bkK6272u67":281915911498,"wjZvhkwJNCPo":507362148980,"ZnZmlICPijII":242231770650,"wpHPjCMhUYJ9":498412263543,"TLgZkfsygQYv":857569109180,"j2UcWFXCO364":62576328804,"LN31VkpOX7SL":700609605384,"K5CaYe5e2WYU":894768969392,"cyH9truJe7PL":487701845355,"YsbLzgFUR0t7":321232771954,"prl0aeX9VixK":4362234865,"t3zeMIiuE0Ag":255369419100,"DbAKGFEhishg":183939111811,"G55RXAVMdX0Z":161956292036,"brxMimlTdsoE":903467878083,"w0NH7674Gjpq":469249540607,"W4H29zryt61V":333746861747,"tt06bk4h8oT5":263384632815,"NoquKV7WR5BJ":406707876377,"lEmNFbSSU0ux":93205589590,"0x2QlkV4AUg8":342321489005,"nPOv6RelsYru":398938260720,"K6De6sdiWLAt":490411948411,"qupaiU64IU2V":413816874857,"rruW8E8diVIQ":250553728875,"Ho1LWNoJfZrW":887571032215,"BbchorKcJ2cv":429612384199,"TAUWACg4ust8":928196315665,"OjLHuOIMn9d4":1988286383,"8StIwqDPnHhG":55660802238,"Xe3Op30lScef":442073514015,"hP3QS8Ff2IvA":802277667302,"P2OScveiXrJC":410847172952,"buqkr4aPoAjv":924111051276,"5MWpUF2IfXOT":292215999125,"IrfFcdQ02hds":945824832952,"eflXku9ZAEhi":984311898372,"27G2XH1QIRA4":665496834328,"hsXHv52NlguW":87314954624,"JxZjyTH8JdKy":581060966961,"K0U9QfBVabwn":54697974836,"eh5NrGBZ1QOs":139246892361,"TYd64eqtTFnX":680114163533,"M8da7uwv3mBL":258753554042,"v1fYpEELAIHE":161352166769,"LxFsqeKzGfe6":581993998761,"w1GiMldrxLUp":374205158911,"RByLOc0sxybR":462234700647,"ppYSgWwzjUiC":38037448715,"zLQ5ADy80q6p":509458579236,"YCERTqO3IZbA":140000679741,"PBefVP6aAPyC":811557001736,"iCKPykZCpAKa":902618643358,"y2kZHOlvrV24":992011711001,"4mTCoiwhAKeU":658634721630,"UuyEcpSyMbma":849866272050,"XDtXuxABSXVj":81595072186,"Ul1pl6gYjWNJ":696451384624,"qqfzoJ4MBvfa":570192517070,"vu8Fal1YEBRf":593521772942,"5TDc4ewJWH6J":880346996302,"WEi5jXGCEJZH":266662829001,"5HawZKX5nu8P":239868093063,"eP22zc4jcaxU":18065231439,"63fi1xc6ngck":867241013914,"SkBFSEK7qXko":555523751729,"tLjNOZZgxs7q":324212777991,"JyyVqWFTdMYp":108342574107,"45n2nVrrKT06":427586927222,"Y9CufQGEAYEA":321402800912,"tkedyLUvri2R":702585268381,"T8jwN5Rjd2pE":373974011073,"x7m5301TFkdl":526085415194,"0bp4FrZclCek":661518461328,"mpwqCuFMd1TV":442241588834,"svpezE8JvGXz":208623406404,"kQzHINOESiv4":891582583518,"U1A8EZ0sr5u2":159699639942,"rjLKJK48PxFx":435419978700,"MulaTrmDqTCh":885695365489,"7guQeeSoJnz1":758063177146,"HzQOoaXHtHq7":40371354708,"SZXapVlXODBT":326159396908,"cadHAQduA5pF":405997435042,"MNigDGQRf6Is":210573338680,"g88naEwLXEkY":853929412060,"EgbIqPAvJiP5":489057380818,"y1weJWDFpliR":500752302700,"5CH875TmqUlU":221528251260,"tLX4xJ0DM1Xh":8015472213,"PW7wXhFrD62N":861650992310,"5PHEfWTjhFop":265046462898,"avRx4pXujrlo":637244824073,"zTQcEEWM6EGR":239165193621,"D57HC1K35lCT":785419900008,"aVpvK2MOtJB0":474780827618,"R9tIdC4CmAIw":424358668079,"GPkazW9PkAzt":679622439447,"qHe4YWWON3Mf":495093997065,"1b2bo7AAVaaE":651986749461,"pDpmR83GHhUG":514599488889,"Zmnj0AM0jsfT":211054331695,"qXNhhT9bZltb":532247952746,"KzkT5CqNBtAB":134330253115,"RMkbSipqoJJN":529850667717,"zCVuAzr8KcTI":869534079977,"msMTg1xJ3rsD":496276446991,"PUUNzmUn74rU":358624048459,"LreGnEi2D1Zq":320717649688,"Ucer7JZqwGsN":338591888692,"6PQYnWiFPhsB":602052371033,"BqTegKxJhwMi":971035689196,"gKQNCajZjz3B":922373753866,"FgDhjVrLAfei":107694047328,"EzXHKLMT570v":184850624752,"WGq1OT4biPxw":623116567167,"gU7e7103NGHV":445506512805,"EIBXZR9LRtul":809900574647,"GVrw6JAkbHVS":948226448264,"1RE9YCPhoI89":442600184539,"VZabWnAS7bta":826735173190,"rFvaTWY2QVAf":356101674175,"IMi5XsBnoV2m":809658789127,"s9kDZGilhhZK":132047215356,"4Y6X3rLBD42F":894697964698,"Vcpxr9tr3jFe":927627473966,"AVHrFykgRPJP":554834757889,"nl1zGesf4QAP":495601535423,"Dn8RVNonO13L":157840481200,"2ctNpHtjkapA":200613350413,"YwzwkiiVemyO":427915746334,"jqM3591XeGpO":151040936286,"xBOfYlDhyYN0":95392782526,"jiLWPKvzD0Ga":103677552931,"MjXJtbTzS6B9":324056912230,"Dp8hXCz0zEV5":47988325715,"8hbs6CFcxB9q":453246806307,"s1W8U2UUMhv4":692433320685,"DdDxCpZ0fC5i":349100046518,"FggWI1NeIR3I":515601874848,"zQikCFlSLpkB":766824584875,"68YkxrJ2849V":346448384600,"msLrffnsp9Rh":394808907242,"rvEZNpPbDRqo":305168859003,"GuTVm6g6PoiM":374890095666,"JDe5KqhByIhJ":824777802272,"uw9NnIVmvJQO":884956976555,"bwuTirwfvrwy":750464049027,"1DJUaIIKcl33":322478349401,"hnPPg5DWyKCh":30724289204,"fbRSzypTVAFR":808925955283,"Os2oWsXe1W0x":364789365440,"jbvHaUt3nTAA":811573298597,"OgVgGjCRKdlJ":925175578155,"ph9odDg9I4Ym":446858053809,"kJPZ18Ma8Ebm":311530720470,"RlLAi0sySWHu":764485690515,"N0jJGz6hCiiP":957191341456,"vCp5loeHBNlU":495889818965,"PE3xWyv51tYS":84390209243,"3vOt1rlHbW61":359169980624,"cIUuY5UfIU2a":253487406166,"jb0EkFYwjBP3":53748748683,"5vmm1NkEUfE4":240786058764,"W5cbeJykXssk":298864567617,"tBRDRWWjrqg0":43761642385,"RfdE1e56M3Yy":288505710389,"gxfMUYy0Zii2":589841072721,"zydebW7y4z0V":320309131797,"jdbUzma8q6ez":549583865150,"U6Mjb72bmPjE":640509466996,"HzhN4oxEPcz8":791106785915,"D4w6W4UFthBe":921636988662,"byICocYTk94M":278282333785,"Sku9N2EegLYX":430555522065,"3Surz6jQguHX":830179095885,"UlKBNy0KGnsg":60972538778,"yduBicZ3raE5":310311435069,"Q1lS0RNoxBC9":788076874640,"Rm4Zo2nsRFs1":88039062770,"Xbprvrl8xqbB":453372159281,"1aaYFUnO3jQV":454235051842,"sHUicxEftqWx":789121504396,"A8G6rsJKAJ7K":175238830327,"m2kf45Oj88Rv":378963117791,"PFpGx2niuKqu":263496272282,"2cqOXllv72xy":168099886356,"XBRejmeICxfc":236798230428,"krwCaDJ80qH4":451587230891,"fYhW0LJPbPZV":261561687650,"eruEpwL3WaUG":805185716366,"jOa7rfiiQsQq":210515833832,"sQd5VDnlp9sJ":629664767348,"O67zA1gFK6n7":510766373226,"q7OJFWnXfvAY":759514505169,"SlKe9uUcBSL5":401289886829,"vMgR4qVB81Eq":441852844012,"B3a500NODjgT":505967163660,"O1hbBr2EBYLC":872244199863,"j4vHeYwa0bIN":504002668723,"53gRFtA4XXdx":803539350897,"Ldq6n7mCZ57x":226847713383,"o21aPXDtAeqj":934430336206,"PyuvSqoaU3gv":413567595264,"Y1Y0MZxrCLE6":752803636444,"PZ928A8LFSox":741884895870,"ZrCxZtSX8WmD":391439485579,"hjCmUvouks1m":817855598932,"vFhM8WXBuX9d":909049688315,"WZo8lKsBXWOo":232918613033,"Twc7YCxSVmXx":846492581137,"8xGuxLCYfgPa":931836806353,"WP7rXNXo2Djv":113037157549,"Y0kIn1msdbkJ":655652823817,"jJFaHYXot50r":733465247010,"TfUHSD7Nb3bU":825125858890,"yJ9QBBsgLdlA":404717856089,"6Jgin5dQYcs7":508194017739,"PgTDS45zKatQ":798936784562,"mSMpCjtGzRIP":250690327356,"XIjKzKgsD7Qh":916653384182,"Qqq6auSMiAp5":602979091435,"otXK0TmlvQRU":583274613395,"uG7oKZkakvWF":418986451413,"oPVPoqzEjouM":314946826188,"lMKsPpXjqOdR":337216088711,"T0L8FF0mkQOt":776811582536,"QOMK9N1l0Rae":233944619876,"WKNQQ3ZWrE8E":744493861189,"uDDl745aeyUu":239030577797,"Gkr6p8Osjx4A":157718377808,"MF9YZHb74l9i":346040346210,"8kLnGNdpm0qW":262346884975,"yOWNO8ymNIWy":491683897386,"bfj3zUGAujXG":229174228649,"KNvr1sJbeL3O":395153005073,"72eYXMiPAQTe":744983768710,"CbDsfpvaoJSz":719214578331,"mkK7iIvkyaJH":231976317599,"mmDwSNioc5Yl":417429468108,"hntbleUWOdEs":338291844097,"odGFBnlAoMne":479726709687,"0dyHfgIvcVUf":824541392486,"QIyecODDMxsR":671981776385,"xRIbsaEhGkip":113221574540,"CtQ0ndKfuK0p":861608427449,"GUYDAp8CjB18":745064674696,"dw25tTZkTLje":544715669263,"boH9Zyo9k7wD":525331980923,"UwnzyLOVjHUP":210938675276,"QcRfIshWidFg":262494476120,"z9h3PL5exxRV":945983294952,"qzdGM5PqF9q3":455053149440,"yE4Jtm4ihPHB":919693340537,"QDysrlVQVTqg":342834454903,"bZiSWnod8rnu":572680468828,"woOQyZhweNZ9":469766648309,"mhGQkcpaiXI3":772862341526,"Gw6et7TWONuV":992324179109,"tAYRUoaP8unh":708692919285,"ZXyKO4k84UkZ":115932003403,"gLWge55v7Jdv":500002051279,"C9FMbFSMJGYg":491011288559,"cEJatU3WXNff":449865267294,"NyZtUyIHtQJZ":227752875792,"ub2vs0gDww7h":975587550783,"IMgPrVU8Fnqd":373118590580,"caE4LWHdmwFF":915721254653,"w7x0Fb7JiniF":453839496671,"okt2MRyzdCJC":554541864179,"6DG13bkx5Y0e":323350348731,"iLqdoRe2dxaa":438894001863,"NFOLdCxsIZ6M":120315880854,"ymhwHUhN1B6D":900196635166,"9Y5WrUicN6an":349482117026,"zp1Xi0kSappK":350714301672,"M9B4p96y1plU":261139835016,"eCPczazIey9m":444075186205,"8RayfDaxkUr7":712592429135,"SZS05CTCL3VC":615470022403,"rOtDVooHj98M":228885382430,"qN1QUzIkNwpF":257596515966,"O9Q0DuOaqFUn":683911190934,"JDsar86pp3Q1":328088141534,"ACccTDS7pSyG":737864462466,"TxXxzaPTqdkn":807262321503,"lqlot779QnTA":608965863366,"V9pXVIGZWkye":21137430690,"AlUmjn7ari14":934769496489,"udeWzruVlvDi":626501742473,"9nFS6yxKaaBc":989678923986,"oMLyUuBO2wk4":850800797988,"wRpSI0A1miF0":777107553316,"AKl274z9yCKm":80188967228,"Dlqu6zhlKCfq":184341829214,"29KSOyLrT9d2":298356957355,"Wz9Vl8IeuoxV":901769919375,"QkfbMEmuYBQe":252932559930,"yHTnvzuN2p7z":666671761157,"DVAfmCzcm71k":169221714460,"eK8O7oF0Jx2P":640470882900,"a3rKmFSqjun9":663331518664,"lbJNoxpxyl5v":741794672073,"zApX878vW6gl":605660396818,"gDQItA8U1N7v":724194261858,"EEBuEO3RiEDq":338744604170,"wYalA8E759eA":515053810555,"hrVGuzNcWtFp":532033133799,"vgtp69YSpLIX":681359614853,"iPwSblZnIKtQ":820964291864,"jAixawBcUvto":769769726868,"ANShueFp5vKQ":756386875862,"0FVFz2wl5y7R":587572810695,"0m91BSVq5a48":277333713752,"TOofIn668RlH":678390954570,"zY56APGXSUbG":947871383481,"pZpL9sFQF7oy":351675048922,"Ggz1555ZtYz7":395336937463,"IvYPF1HQOTM3":967502967385,"2uoneW0hob5d":107606677790,"R9DGV0hBPEI1":84829110001,"u5HfRPEQPn1i":448270787720,"JdFqoq2cGrcG":763242012207,"MXKUF0QLno3I":124078516193,"D4OwS0Eb3kFB":374226278201,"xm2oZvXTIZbZ":563468617330,"gK034fRW3vwd":800951260409,"ihPRRUB9jbKH":907580687112,"k63JEBlsival":350635660695,"PetDFFgwOzMF":517921815632,"KEuHKr4Jsc17":634787949225,"sN73GRxAxTCJ":628943157248,"zlEx4RySYoCo":710143097880,"Igkf02JdrLJP":118709390223,"jNlaUtfL85oG":971676758199,"4QKkujT8ICZD":100572800864,"9Hv6ipx1EIjX":137110543851,"kDyyrDqZUpGf":524208509095,"KM5eJy4uLIMi":684614567631,"YTo5TjqUwSAf":939526128528,"87vzoSR37eDM":639283231562,"gyVUesl9KtTk":154664848257,"Vr6g2QfPromQ":329270892935,"xRnKGavFhgVk":750060300989,"YELcysPCopXw":16684089149,"8YuVctcR9Nis":350012200846,"bGepQAlJtsYy":659124630656,"S9FpRADm8URY":541816349890,"OovB18b6Vntp":145951319765,"q5bT68lvcnvG":840279474830,"y6ks6uLsHs3T":906593186842,"Q5MeSdvElOwy":456893128608,"PT4WPjQrLFsP":722903451916,"vYAZzjGpZK9b":193756200378,"RCpQ2mMCmtJs":433167965364,"Fh7v14YX07Ve":100991952797,"QKApf6eJwqwP":272325298759,"WklxzYEeTHGx":770791800620,"TOrjEdjrhFg0":278821550535,"TWpGGJKEP1n6":833722443268,"5mRDmoeUlxvb":727964603286,"5zuMURGhBM2H":395827883139,"C83M9P9dFYpC":483994333484,"RZYubaSh1Dyh":645491212346,"2pQ0MmRYWKDA":98140229863,"E5wYMeLqlCmj":443640248814,"uitpsCwerYR0":457000510287,"3Bkg0BwR4Kpl":624738362659,"ZMtouTx4Ry6Y":681997747141,"OtD7AHep5L7t":573209727082,"KH0dXKhMGAar":430753640803,"iLSLAlhlYrHx":354796887659,"eb5VYNwpZMpw":683456452271,"xGgRP1TYWKgM":180353275393,"Qlr6qPX9dD0E":789456689977,"XIdvnXjqVEJZ":67539455383,"4ynZHSH7y99j":74771266773,"ouH6qumSaASU":81572195406,"iAN7xLo4fa4W":455268169645,"3JRRV3Z5Ygl0":577676177940,"dHC5f6i6uD7j":715163509064,"cgaWvL1bDRDS":942625162772,"j0vOiIJboP05":866701867186,"IqimD7PdGj7g":993451683323,"Z8CP8PqJ41qg":313440718245,"5dfoKigDuvSL":426233832342,"KL36edSBh4pC":874964741316,"UU2ZK6NMH9GE":857620830641,"nszMNqyTO43Q":201826877294,"wJ4spe9OdltM":129469635996,"MLT0TPIUJOYw":633856139435,"7CaVNNMuYbM6":453839799714,"riLUTfEf5bMK":660184415038,"c0zTAaSlR02b":532453424302,"hFLu40IP8JD8":247691011079,"azCzGT0fRMPx":685131520452,"jUVD6TSdXhBP":277784532345,"uHhUtl8D5fO3":257258487529,"6MhiVlvjdh6t":305708625383,"LW0fPYnLAg8x":748298523873,"buR8IKkoshit":305652195392,"yKx9f9AkVTdP":729677375633,"dtgN9CgY9Sf4":407645195172,"xPjMIUa3ZeRB":759428704010,"IjnEuyvWv6Tp":660724794133,"j90JAogFdaLK":973702224653,"5DGxRpTmZVur":327156728205,"D1GOMS1ZdfJm":632158512070,"4JfLFZqY3PGP":896758798664,"AYxFA9apxt0K":317266402761,"JDwfO7RG5rNj":378559798953,"WhRBx8uz8RqE":911474312981,"f5HOiXxKExPe":819589815108,"ke0BOdSuu65y":702666287808,"mWkZk7JlZ3RC":353404170026,"tAI5poh04xgu":388320534426,"zTNI9NWHwkDe":801493362664,"m90VtFLPTgT6":378685181715,"lOjWlxiK2MQv":885306195062,"0LdiBYFvVWfl":257985806995,"mrJptBObQHaU":934934789875,"bPPiNnhghndM":802803837957,"48zYsWptdSnN":894479724630,"rrlJekz2MoIf":15787582128,"PcBmxWo0VxCi":377716029139,"kigYJILjlzzW":605683862342,"TiGCs42VTYAm":653601907131,"dZYYBFRLjY5U":221543667248,"VQN257SmhAKk":416714721888,"yUv0vMVJRbsd":180232351939,"I5KnixtcrqsH":644126695287,"5NAuAlsjhW2p":833424184759,"Z79zKgWTXElv":520012017606,"NVuI8b3HLXxT":622311674262,"PbROxOg1WiMX":457979747263,"ATSVfumQ5R4v":155603503431,"2tfVcgzJn6CF":372623485416,"m7exLcqk31UL":269154864025,"VWEw1qyJccLd":873525945358,"NpydnMCYVFDH":58826738977,"TD4cHe3v7Nyz":158188505334,"q3Fvm4LmRvj5":953947092434,"ubT6pKo0Ol1V":849342447986,"yxRY7XRh9zsl":771422283735,"Oa3U1uAGaTOt":219731152508,"XBzDWjoIneWy":694053409774,"RiYT0mbL80f8":609505794052,"LnP9AiaJ5GxV":72094315475,"XLPMv9oNIo8j":49137346715,"DK8SwEfIedO1":519291524323,"cl3LmJ6ylL2K":194223909712,"vAH5rqiOBUCh":339761271273,"XJldxrVrINF6":504887486270,"ewpk8aDAyv5I":442180123119,"96PTrv4D8ydx":161439631866,"xvQGAID3V25V":332933024252,"5NY3vOmCQUw2":743632600472,"HQi1cHLTCRHa":727949064878,"Em62kl0zPHUD":885745631950,"4v6JW8QH5x0q":375533102078,"NgfOnz2hJ3s4":978424393742,"9nyDVqa8J3XG":853458055601,"YxN5I4Oi5Uro":529142838729,"oOb6pd8woHWA":55393220862,"IIc7uI4WIw1i":406121820236,"ADQUvQNwE2Tv":467416658572,"1aKRcXKGiChH":777219488738,"Dpu2TCRz8rmG":93461436277,"mo20nNHBo3Bv":778040923262,"iEsP8x7sfRYs":975383804441,"HNeru8DDN65E":987008640229,"sw0pAQWpALYG":107105444844,"sw6KB3Hfrwsa":554605732158,"kHDk8SWC0iq7":222356878818,"2b47SZ6QasTk":897966309977,"Z3w5rfDfLpaw":283304865484,"ygnScdZOKLAN":714580798851,"mIRgMGW8hHKT":362793696650,"lzVziY66Hlhw":714633142189,"AisX3XC2tlvX":890489485070,"ZcPplUUZvICq":227788140809,"9JCA934JZwUJ":612272689058,"dTUvIB09HJJl":98297865386,"tG5PlsyaQlRt":956235449568,"HH5Tps6fKCGr":351873302288,"rgWJG23RsPZo":475864305896,"Ih5LxMFgwFTb":108429198162,"7TPjImpXZ0PC":139903130902,"0CkPLh33e6m6":76044118179,"cC7eKvwAWszG":659639827628,"SXEH6V3sjw6r":314797681590,"7jvGVBB7ONNl":543652189193,"0fCWlDkfsB63":220826416721,"usOqCFEITtFs":151942450828,"iHKsevPrQYK8":37198694610,"5nHo3Kp7oKtX":297764871988,"wx5Fraz8k3j5":519681654840,"rq0FVb8eTmak":419886863150,"b6gvmKhfobEp":472605220851,"tVVinhDIxkxp":178479364388,"WZJxJFZZivfg":870636742879,"rjgKbXh0ut7E":704672661783,"p1WryBAPPx32":423326555204,"BQVKJKNsEHgJ":423884069264,"QWMc7GkSopuW":554732685779,"rI56INt9X4Qb":978941834372,"9eBQ4TaNsEfJ":218998398521,"WoIZ13wNQCMR":108307427370,"FGvwRkErab9t":902169434494,"fQ0JxtW8coZD":20223389321,"jEdCS0vo2qKa":743776459836,"0jJrfyHxtd3G":793120561567,"q4CR797IpdR4":564435082590,"RJElL6aZYZF7":574057918941,"YC2NjlzccjTh":723424781933,"upk5qBzIOJxM":980739829394,"NCefeeQJM5hK":231910773212,"nxabapwoMTGP":502451080144,"ILJylGfjmUmh":88852312964,"mIrhqEgzpIoR":448620303321,"Lnlt6dth3b0h":118827198054,"fDJoRiAIrDp5":186666291822,"R1ui3MGKJ4Xi":248294065121,"He1WTpxddRKy":613563424471,"xYknvKLokrk7":53962718516,"ijRsFBOzmNod":973708932197,"ukmSe7d1l5Us":585761138504,"5CA5dYTqqtqZ":616703967655,"rZaDM4u6VYfE":372234750955,"w6rnk2FMG6jt":441102824650,"dsk26zJOM8Of":721433914678,"JpvKDDGDmuSY":95862581889,"rh0wHzWN0PXf":257387277763,"VjObPal1xDvY":387080990288,"nOjqbkK8MJVa":309903925124,"ZEwks0du3njw":453253691058,"u1ZLv9uMgIMD":949616895750,"eYZRsQVfvbQX":82305864704,"R8KP9x8wBwxn":802550010361,"0XQyJbR1Igfe":154056955590,"iJkxvYdaODzz":46736141485,"0bs6ieL7SNOu":283242810742,"hVlVNbFizmKc":926522710526,"pgGqN2EzJL2s":48331557323,"UppaMAAE0AV3":556944943709,"SHDiX7VDX54S":52669701556,"FBxt3OqgcD07":949805443587,"PtWcbZipbNlq":586028507095,"TCsytjl2CKAj":148840307337,"P4OQhx1YMC0k":853355904572,"dDV8MANwropk":833110194688,"gWMp8tHltklU":206102746132,"55i0Tf7J5pjz":290521943822,"4eu4DAMeURfi":699478036424,"m6Lxrc9stXlY":648644549857,"GtuLN5dljlYv":668389409874,"FgVjjRdSulCP":275676461245,"AJKhcOg0Rjdc":209627458480,"zp2XUsa43aHs":867626666849,"lwekx4MloeLW":649321951662,"3waSuzhNIvMv":578788921436,"mTOr0a5up8eU":652405340140,"IFa9CwzgDfH8":512837772298,"TVhpTWvu4g2R":664948887106,"2OHmPEj2omn8":517415847331,"Q9Fbj2fmYN2O":407161043916,"hKURhyXNw4xh":615916492462,"cQ54mjfpZBhE":46635375940,"gomK9BoLJish":777616637273,"C8THyvLz2iql":437194323514,"3jl2J6KGEf8d":283432582763,"rGpExvhcrg4G":292491866448,"RTu2UgkOjONY":436053157156,"uZRgV1xt5Kq8":914273502886,"UbplyhS5YhgA":495341252892,"oHEsaMlDnO9x":503095494520,"EAqP7d2oVUri":772785530890,"C0AxCFczJD3M":884392122016,"TgjFQV5LN5fp":116934969094,"ARzx7DdFaCAi":5068760813,"egDO9LWTYiMx":837246528484,"qOPYogbKhoGP":490861034550,"ljD4iCnxXslO":794607856381,"Uc71rNG70o80":626178485904,"sX3oSqm2EQmv":92235242198,"wRp9Vax2Rzk7":712132868160,"0tab4xSPsrAm":236868573341,"rXYbPgh1Ju2b":69426386464,"NWS7w1peKSyu":315107121187,"lIuUikKfmEim":126882628356,"ixcOm02SRQ6T":538696483026,"gIEAkUuEgLbn":390270304230,"1faCDH4mMiGo":559627906150,"qylVqCoOQJX2":997793654728,"ZVs4hyuc82r9":101053863557,"ypFmJiIXum95":137863122752,"7G88AOu441hC":142826306718,"I1TJQiU9zFMN":283700385276,"IK2Mx04GSlza":338036953541,"Q24lZP1DwXkr":754499367091,"Ns4yrzZ3bGpT":193206712405,"V23scxcgSJyI":651771906455,"lBDYBFhnsqT8":83938003112,"ff6ExpDwmRT5":465273223915,"hCbRF5gN5ESH":765445640383,"cnrrnkmNNFFB":900827536565,"cvwqliOrFCGV":615966567661,"wNHTD7kFR8cY":688990118043,"VcwQOnmz0JCw":619268788475,"bUUZOFHXoyws":748868845060,"NCBpkGUVURKC":637046650121,"G15u9CD1SgEb":354566993474,"UZ4T4bohugjb":203926152159,"09jp6n7i5NvY":529062211517,"RwoYT7nLgLjr":887361601504,"AgnsCojIgctk":752723511024,"BlaiVT0XsDkr":596362452898,"pp9jfYYxJE4r":165941211281,"WGfl6EbmqFNs":95124279346,"dfXy8AvLMPDF":944120818714,"IxJW9IeUJMhF":528548579399,"JYh7W7rLg7tz":789977470551,"rW9Gpj3WAzzK":170118718190,"rewHfcydDAnu":523790856536,"WXiKanJ1PBEL":826954435834,"6DqXRKyc5Lrv":323213664755,"M1E5qLzEs6k7":221765509825,"sF1pGWkn3jmg":842606227830,"zbsQl4v1y0cQ":751869761674,"YAlEpa2RvB0j":108602170037,"pW2tGxU8GLuI":527404864449,"tyDjZ8ksd68y":782107437706,"Gd8JqkznaYpX":617066536115,"M9hr6bKYvly1":75577739799,"t5d48F2JLywd":775232127571,"VRCFL8KM3b0V":368113579159,"HGBBK0UjY6oB":546367528702,"9OXWcm0rjyrZ":454121898657,"k0oGcDKjPrEo":946604352927,"N6P6C9qsCldT":573601944424,"naDhtChgfxJ8":835344820715,"rLylsccUyW7a":950861920297,"K9RQNuhXnal6":736099749326,"wcJpUpLgdc9k":507088943565,"s5X9QTXtteBX":755670257671,"61sUe9XUED3o":869984221817,"A8GYiMi5CxwF":188413464250,"WkxV7iXMvcMO":559931745692,"AhevhX6ncAvM":954217863233,"ezQ9bgF24z8k":228321633110,"pf0lviuVDBbf":43462824177,"x22Cy7zyNaOr":587712728167,"3yKE9gL5CYDx":151512309048,"eGe856Khju6B":38993138433,"E3uUEUorlQNv":231809945738,"XLDY9KcGCCTk":485001385199,"bU8e840D0cr0":709830028597,"e5K8XQVJ1VcP":105044832285,"mx4UCREi0YD5":671657980150,"SvGtlBxvUAeb":414814072500,"RS3SUtVnGVXp":786873646245,"pIfwpVRh7W6v":337994349640,"ZJIB85KW1Tip":342079618318,"NCzfIA88C9HP":701423725233,"4ahGK8I4F4TC":142843383437,"Vb68e81ECwr1":309488906010,"M5vAEBL2WDaY":578259544899,"mLzkkbxkfYTa":133906687148,"QRv2qEup5XRu":134199670935,"7DQhgwa9QQQg":137901140634,"1PwGoUs59KFS":164093196513,"8DQkkxJJsazl":47070622246,"zQLcyg8mqXz1":799717561193,"NDEV8LbWRmAl":138118192816,"F8DHQshkpxsL":119452717691,"yAiX1avLl5xd":380795578749,"mvbmCu37cjfP":601612611979,"QIIQzbdwdeJp":552614835186,"FRghhyyIAqNi":749232545847,"smHuY8kqu10q":886831164740,"IeSBD96wxnPP":222792966516,"vEl5ib5iCrxb":603880046753,"pFnkqSMgxQVR":113738175461,"RCmlOPPwGGgd":590462369411,"4oVEjK4vZJ3k":93901236211,"Ilg7Vaam8T5y":239550048779,"ZnQp3s4DB3gm":754470297375,"pi7D2hbtdtzY":980835389170,"ijbZgYa6uELC":65117463714,"2nvA3PfvYULj":262140579374,"Y5CNUyuye0nM":851920973229,"9oPjycjOtOYm":42496242543,"5ZtYyof2wsf8":131555347837,"fcpMiqh40KtW":470583546433,"a48LfdiYfNjP":176898532878,"6GRDdsYI9U5i":541596366011,"9Rkx6w8x9X8Q":967866212970,"goeIRi5IXf4B":763361285341,"UouwDEr7W6Ul":369393076309,"LL4C7sdWWWHf":271582727394,"yjV44DulSO2i":503806158738,"4amc1dOjPyBl":986494963908,"L8KdAK8CS9SP":224118595939,"6I3ncdG5yO4S":836949647510,"biFlKu1YdwTK":172627644665,"qZzvI1SoKtXL":685188819231,"gd1YU2XtLsTE":797132076002,"RPXMDOVRguay":531327765585,"wtL40VtIL5Yj":121799346627,"mIxscab6z9Se":388471079849,"QDhMrYSy8Bgi":25497996285,"UvOm6Sn9pS1r":5228969782,"d6Bx22stCJZ1":370850337134,"xSPGEJOngg8i":857736495290,"1rbRIy0sXIq3":371788667898,"rVQ66FOmstw0":326431678439,"RuZHQwCH2MRf":386147021476,"vIMutSWXvNbv":846186983014,"8Gl2JQk0n8U6":490859951485,"3fBSFSS7cTc0":594474362196,"WXuXSpgSB9X5":710610619509,"OnOmbS9tf9mC":40133783595,"nsk7zsrfwvOO":760138038114,"cqyip2hVMpGn":910789737933,"8sYCrVG3igzJ":625746917541,"gAEzgF9YtYRS":810780729406,"FMG329yJ4eJi":37949741986,"55iBWtVgrj1j":698256844095,"bCtr0wBaOARj":788159786561,"fIWLCSc6Fkfo":702790297058,"4WLGP9XxWs11":518283870982,"46cAjH1s3BF7":516497547252,"1MrntZm06tJs":558626029998,"S9rjH2grLvUF":506347437156,"PD0wrDQd9xhN":664666002376,"cjhR20K2W0qe":570952966912,"o8mO52fPxIAl":145782164679,"4fpqICIPKEo3":287115298998,"GfZrAVcFiozS":638767047156,"Aks94zPHnz1O":19240963638,"Q99WFZTlDHCB":676355912895,"tZW93Md826T9":127555617360,"W3cH1OMfjv1w":968720224496,"s2asp9M7sc50":784069844202,"sRazC9bzD7ji":474835136384,"07xloT38BnJx":234940733483,"Pl6x7N8g9K61":165792723674,"WWbFBJtfpFIY":468298043939,"9Q0eDmFaY2fb":149902363766,"g9P9aaGAVie3":733858264735,"2OwKqAPFNJkZ":44994277588,"dn9B9gI5fJ8b":942510003175,"QdqqRaO76Aj2":826831592338,"7COZzLsHzwT5":422221408134,"PbYedxNywHkd":981230972101,"EKQaoYHHzW3I":441033140324,"P1Z5YWRkMPls":908125217800,"u9duxj1nG1qF":243435709108,"r5zCheVpweZl":854989345500,"tr3Tnp5f4eH8":611847644103,"FAByt2cvExu3":830133278111,"nxoWDzBkRsOm":982947228911,"TqlM3Nc8tgyw":846598665209,"OPipgg391usT":417536745599,"JJWhm9vxZpZQ":247175206377,"k0Xn7VVhpS8l":911235755863,"oHuMm0KVsqbJ":306907456329,"VXB1j9aPjf50":869686502725,"jLt7F2DIQx8k":982777585405,"gPfZ7B0eOKd8":688378282248,"d8U4DfCWWkoF":130550638545,"IX70ATmfJQvx":523807166168,"EQIjFsnktlqJ":903891064034,"ou4lDwOBEL8M":458620944911,"cq2V1NkURTOU":988080810972,"AjkmsGDCJh2q":764585192206,"sX0aMMGsWkPg":561783629685,"l6CG8ljGWMFV":515555367410,"gWZ1nz65Lthn":691998247683,"O1P4khIZZA7E":665422022887,"dedYXVy4InBb":489883770408,"Gjw62dQPzG55":857522708807,"i8PIs6HTsusy":711971063201,"0rPmWTlD70J4":364707612001,"jk7Fth92YRyT":805905491918,"dLEKaVDFeOql":713551384764,"ouRoJkexigAM":904814622846,"slIbhFYZHwWl":98880048111,"5llJ2N0VAcNo":922216355382,"uiJznRF5AliH":865940336184,"zN227h5jHmYK":296417820151,"ITPJ2GfiWQ4F":832801195403,"VFNik5E6rJHf":803423767890,"E0x1oWEsVPWJ":393826614286,"mlzQXpfm676h":699097725145,"uBa7jyWzoWeL":770760836323,"4FLOPOAK0mxA":133766362432,"AVXzQKZRFAFU":872030002791,"1Elhhr2Mouvx":620222189707,"FaU5PMv2I5yu":483782373640,"bgcXMxfzxY3P":249766300116,"vksC5bUEuE1K":653771704650,"C9gVOybpGADo":861360170500,"bJRlZ9szHukS":630454820058,"2qkbT72OTdrf":212646156502,"IVbz3nPgtps8":559846741629,"Y7EsNdPyhxke":152056452340,"aIU1ILvJiiEC":29472273307,"TLvqeZKpprMb":486132286030,"W1ZSIZ2LHjNy":75720873798,"aWz5E93yxiWP":886216184692,"KxB8XPeCS06c":652175887060,"qfHWO4sxqtjI":159236781621,"Wq2fFh1kz8lS":819188563707,"6APnSr9tkMYX":416885798785,"dmynRlpZZdfV":962380898421,"VSKi5Hi4RpIv":235914350449,"PZwz5bJFAGk2":929325384774,"FItKYwahWXAU":453911037756,"bvQE7d3i1Ont":420817640209,"WnswXmYtNCrm":305483383263,"5g7KPhP8RxZ9":969857656439,"sFkKVjMw5c7g":337236371154,"YlSYDHFoVSzr":169753816362,"MoHsyeelMtxa":888586541619,"tgNceG2MC0SV":814392721381,"iX7FTP653HHV":747969277525,"U2mhyVfUIBPz":671888278689,"Lt0NFteYd7Dm":15938054185,"yOp7nJCSWOBS":31604758411,"dEFqBxEfviuo":756780990315,"iZ9p5Awoq3wS":444629977609,"6DPmkMAmDI75":292727525912,"3HghKwOPDRCY":223733429116,"obJSpE1Qi8Lm":993454959102,"nFc4fNWTI1Jf":928626144037,"n63UzDMquO2m":765273026887,"IKZz0wdwCnxJ":549471351714,"6JZTVYT7ER57":344223905287,"eete9JyUYoQi":292837256884,"cZTMRkKt9AZx":53706847355,"mqPIanFHtlII":490844505507,"0g6TdWI5fEUe":100136363805,"xUBEs9tyoqGy":68804881557,"A4PhenDU41sU":768177609570,"ypliXW1kAKue":320888126796,"ZNKMOSTYfSyQ":109065974872,"hf4N3XUbcD7s":384519590293,"xXc6H1G2amCA":714043476517,"eNooBhKNxwkt":766196907440,"RJXxXeNZE9ot":441248811484,"wJPb9nwAcYcs":687187841038,"E2eN1kVRRJcQ":711921139289,"iD4Nr6Igxcnt":942351783550,"5SRZQARejEFs":696510109088,"KiRBFKYLqPJv":672945176962,"rrmbyxk5tk0l":950624644099,"hrmdev7CSZh6":810674141254,"nMn3qCKAet72":538089614216,"6sjDVayuHvJO":453496096548,"ZnxJ1OGYHGOw":128377101498,"9G9uSruLWKaJ":280500316567,"0zoA910HpbFq":321594334929,"4dQl4LmvRGau":326098243440,"DDE4byuwPc5q":988030085061,"SyPSeHRGq6W1":277227849786,"RpHLF0GUjPgT":70883170728,"qaygSdKi7Z7j":789396528999,"MPWVvqWWFNfH":914285059938,"rcU2heVTjE5S":572323286382,"crt5mPvk8j0T":798347800779,"d15BIwYRacxR":45768951014,"ONdQPxtklEMf":811993934591,"q4jc11wZQ650":695621914358,"kfa8eCwFmp86":674884648565,"wPwd883vkwXh":474034843428,"6DmY92jcY2Pv":456124957623,"QLqWtm2UorUD":301027051596,"vOcNaUyRLR1w":591553751831,"Of7PIFv30D4K":193459749097,"QxniwkCrXaN2":211134381290,"LNHtZXmtkobA":68085168503,"7gY1iBjoGik1":348920766647,"6ZFLo5SwTp7o":583981710557,"p6gicCXzTcPY":268240695014,"fCd2DuiAqcEt":436859054181,"rVsQEunYxOFl":309932722607,"Jz8KfjoTeChR":865518225914,"iCseuraCK36v":507674203118,"eDyuql0Z2olX":918126814686,"8ZpQXrpBloAo":952003134227,"0M7ludr8y8Qk":177515054477,"IF1fDUeobkSc":428687095503,"QZjBvLloa3o9":939284890031,"12qGa8S6kDD5":983393089565,"US6r2OXyKsdn":751378842498,"efNmnLOqTBdQ":489353030562,"GDo1RWBUSDEO":894047591204,"A7ihueOGzDyz":87200143432,"MEp9evQItveO":964521615084,"1VIAzuIjN0HZ":981984080190,"XXFB2SC8kiEx":114400860669,"21SC2YgO43bB":634523018414,"F7Ktxi6PEiX8":30639144271,"aJvAUzJj9fHj":133165409194,"p78blpdAWVQM":236577839934,"A25hKjKeZ7qy":800414250326,"njkexs62uIgr":397354286720,"fvX0BJD8sj5u":92707495368,"19moA6QcKOwh":251720714123,"UmEolctDARRc":375042260652,"suE6OCa4KMd0":805778372890,"HbKwqMUmv7WV":658378772754,"Zl1sfcrdx6qm":453752778155,"pXNdbSpg4bmW":289091777526,"jPwuY47IOLo9":703895460418,"eeXRBan6kvpz":114296376313,"hnGtAaQ6X78C":450868221620,"uQM48Ej4ZdUI":66669747594,"tuxkBpXZ5bjx":140538218518,"PfJcijRWqKrR":49800583663,"LXBPpPEcwYFl":99582095913,"tvOD990eD8FD":873447604337,"986CpsSxvT5p":625459000217,"BXAkk0jwduZB":133126618219,"UTskp7qOoDiP":212339563482,"QadpQGVT9pC6":915382164435,"essYexEkVoRN":545241353799,"OotfLN6NcyE2":865893870901,"mWIo5ZSbnVTZ":925056986920,"LTKwLD6eHOrY":741319203415,"tHoGmCHRsxtJ":863864655935,"epuXwIZar0TM":369777018437,"aowLVxMTa4Wj":263479877538,"cz2onnbwArf6":5385317527,"VsfWgUHdPBT5":506019808874,"xmehTjlr27GA":616022738460,"hkSboCK969KX":131573277643,"1joCg5zguGqU":118851085899,"X9RzuBifunVJ":4547844363,"cQ7NnMC5bzMb":194911412224,"TQ01sNLbOLW7":363771821929,"9z9olS7BPT08":903320829389,"vcrfU5Ze14Mj":661931231943,"qv7VVnPBXe2L":591423046405,"XHihAAS6sRIz":365558645405,"cxxyISWy0CGl":602425309251,"DjckXie9wRhD":6805751773,"v7kH7mXULHvK":491784956630,"UBgmEgjaf7GW":603170529990,"fktvMSeu0emV":730133168695,"WYTHHkiL8BnI":924718117362,"ytbnEy5Db0TL":679062660076,"2uTU6ktdZ3YL":69540129865,"dljtm7ZZf7XV":144404430854,"ZguWiTRiyq9f":323562336855,"IY1hqVqdCSLL":450397354881,"nTtjdQaZhUXg":10821909016,"E5fvcDMgMVdt":822265238438,"PCDi1ySQjBMa":480546746480,"FRHXJ2lx6yFp":880144885339,"04OmfyjUOl8C":178579769278,"4XrjRhqCTzyS":590885424953,"9hJ7Zz173omu":708072516408,"tLmRVpofk2aD":196414822374,"IXbbyaQLeLaK":389311491243,"DX8Iq4N10szi":170736238669,"7dccKlKXTKEy":686131760151,"RJKNONGwarjQ":565623726680,"luVTQqLCXL83":259097752958,"ZAUB5rYavFI6":685635787131,"tLWV5Icaqy0P":512840480133,"sgDFnebXtSnz":290328597576,"mvX8gZ30ce7Y":196574695419,"LhDcFq45UxHf":246834614515,"QdTC9WPRoeMX":410043732982,"XMKbjGuWIRkL":47614852248,"rs75KEoXisAu":599877425546,"FkHZwGzTlTa4":160829003407,"txdGJho7QeRQ":857589302034,"xbSzYqqV7C6O":185088573772,"B9UFqoacbfjS":382748901687,"5LmjQm3OX2LL":409161674605,"OiYbYc7kfKRm":121538574639,"AMs0ONgY3dzm":632659986098,"a3lMp4nVGJQ5":938948374834,"i4aY8HbkX6Oi":212991791871,"ODxQkSwsCq5b":274573682923,"bqniwiNwXjXE":127424219552,"aFmfRnTWDMj0":553738834233,"ZW0HACoDKeoJ":660010900082,"pRuF9Sunyfbp":964872190181,"jDZAFhoVRk0l":765620807848,"AdWVwhUu6nVA":124871543284,"aOHEZxCrjDQu":110328720476,"rfi4Lxdphxaw":725334120192,"riApVQmWTers":203822947188,"z0As1l1PJry4":179203405117,"cjyZe8bcxOOH":810363952566,"GogqbpJn1Nt0":897225349294,"9UTfCKikhbxP":285819185328,"7llkv0mXY4LF":570062774977,"MErRAiLsFVRr":152152640977,"WcZjmkjisgbv":754138132617,"HMcbAe7hMeqK":39058953016,"x2ZnWG3Eo5z4":386978758232,"rDw9zB1vmjs6":514320764134,"T2W9f3GmaNyB":261241803876,"OtHkydSbQWmu":617213498814,"4eUpgEn7osmW":640138498007,"gnUjxH3qR30g":219105813475,"NPpEeasMKx5j":61072488794,"GTpHpMJ4IoTv":408126050813,"PfZl5Qh39YJp":981734235274,"OZB3AR22qrfo":757832164434,"e2S5V8phTkIN":855357747905,"huEE2Doj1c7H":187642418162,"Lp9y4z1kAF9i":338335646444,"278q7AEX26kC":827287565150,"xftW2dWRDFIo":90660323000,"HWJTuWcx2QGl":644808138812,"g2b1Jy5uJgtT":649185194571,"xKYab5KNGq5J":232840948566,"5A6oA3rSUmHs":786558995313,"9e6UAUcQizGO":1502528605,"rSoTVrMszs8I":358450782131,"D3fkWdw5lfdo":747222185782,"VnwKiChF0J70":646400403098,"rSk42Ef8LJTu":421843616594,"mwywSQyle2lz":36632495828,"tmROcqfoPyZc":218707615404,"OjwTQHfThQNF":543964667629,"14yYtIFMySNs":224916809126,"sc3YwrkEmDmv":104020761323,"OyPFxtXgDSKd":608024648115,"XtBVeKrz92uL":496011169540,"TudhSYKNcat0":643096718574,"9yHoZybTG6Tl":983670445847,"1sbI90CNKBpu":134076923406,"4FDcv3KLGf0Y":73488589200,"RWQo5iZH9jNJ":11022928696,"wsplaHVqIaqC":57898261073,"TjKdWo9Tq4RW":334023278612,"AP5TGkWkGPUD":207500477268,"6D6HvS4HvP1i":911159539458,"gRgLCL8A0WCU":774420083819,"0LxF9oEO3gQK":328639587413,"RbDJF94TCRHw":515221831492,"oBaROm10lpH7":255575857756,"e3UOLwxMLrwr":375318900545,"yyMUgG76d80y":959891669416,"DUzi3Sn6lx1a":684783317026,"AyoptdGdxcr9":870202664979,"h2mM8GhETaeV":35780205234,"JDRO6VYp1WQS":276101088819,"T9r8vHmSHy0l":138335107364,"TYic54DySWm7":980795553336,"dwLcHv8PQKbm":92808835443,"dHB3BGT3PdHJ":147022085185,"Lnh5cqbyLeDJ":4676574464,"0PTqEhYhpIOY":930571063028,"zkWppqrftzZr":827441182513,"GjvnBttBEBMO":734572214077,"IYbpJxM73DsR":349048246577,"sF7v7TtGzUPE":825994089873,"BuNC2d5VhvIH":496926238432,"8uW0yQTlgiLF":988670759310,"axpJrK3VrQ8l":938854210127,"4zisITyhcrMo":919589311916,"GLaUcCkCtcW1":934743139746,"tv4Ftsguyran":962003480154,"pkvjguicSJfq":482395311544,"nmBKUQvoPTwq":552430091184,"ed7G35Li1xs5":767496756966,"aT5kCGUnDJ6w":900411886699,"Fci1BQa2hF8R":586803246691,"JfsjKNNGT9I6":369959174213,"RMoSrwrRQXuB":940378598003,"ntC4sTpw9DnL":435664013973,"59PFQgpyDV0S":209765763263,"VhYbctXUMzsy":56951686187,"o60QSGddw4M0":389584759821,"eLRAJOZan1fV":568831383731,"lSw7d9G40mhQ":145399682079,"6lNTQt0A34qh":849084243750,"b4jGJ5JzlYdH":125756697524,"Zx8RrHRaAbMt":701021911832,"8G8TDYVQDrrm":450468911781,"6PHDcpSPe1JT":977317928474,"Oeu3JjJpR64P":836126173290,"KLB26rE9xszg":404117023449,"AuuLYskg8nms":519730366663,"VdVaZUuMYr4m":78023876973,"Z0V2AaqwCNj9":805401411864,"CUtmzDsVSoQ7":537056480384,"uanxrg7GgDzE":179719633716,"x1wL5ldZE9hk":851725080597,"nXoEKfw8GvZj":997869949217,"BPVAqAyS1IDs":949978524185,"428Bk3UCZZVE":250322838628,"yZiC8EKyrb3q":80881624456,"nxF5u5eUPyig":965555602390,"vK68qdKgYcBh":399037174026,"xZ8w2q96MkNv":704402374045,"zjMBHS9HC6KV":553691618558,"QSpjQJc09NPz":489007798111,"V9mj4SfxVq38":628726762500,"El1tEb4VawEa":860771922637,"jCdUQRZj3VwS":87082895272,"xLPcm0ggKAbh":837628053746,"jR9sBbVjU6bW":208640814288,"HofIBMr2SfsO":496549424584,"Z9T3kPkwU97h":768848692877,"ThfRyiS5CFWt":973220131333,"Lod0da6BVGxd":358400785898,"NljAYWHBqPRa":188019984343,"mfSQDjWJXdD5":606109920588,"NegUUQZQfy9D":580996733408,"3YyOZi4KnLyb":747350442606,"R4SyeDQPCBEl":197941770409,"64m4grB9rYuj":202803108706,"S63WBsAgBSkq":390365676758,"waoc6Hv3zi4j":110469773403,"qPirUM5iFEZX":346654042395,"IFTOixCOfVLw":313552254049,"qfyFo8bygSTu":589663770490,"PmXxPLJE0bi3":536873109932,"oskvM6Eg2u8P":404115974601,"ljFHLd4qkZ3E":780766152880,"vpY42triHSKR":756363477353,"l5TsFQ6Fv7jZ":705797948300,"UBMnAT769Vfc":536231762476,"D2Q5zUqPXADs":128868821776,"VVsjTEBRkFfm":300366403898,"ykjZwq81jlAz":282486624818,"YmfuF31HfZ2c":829405779781,"glevWTC4c8wO":258794868232,"gVjafr8gBw4K":17598799622,"R0z4fGD8dhvq":524596080970,"zq5AqLHzG0ia":139491706286,"gehGGvZkBUma":812673430500,"qdd0vX6hR0Ai":732319462229,"tLXzU4Ju5q99":26977761806,"n5Fs05DI3JGD":480576092866,"lkZl1KgJr8IR":187314732350,"Fs72vdY0Byoj":102185199612,"AYR5XewtsoMa":293037990923,"Y1S1gcblFcr1":242292985642,"io47NtBwy5Ek":952817907945,"VcoQRlP3Ummw":55392121529,"4j92tGCKnYxa":533238946582,"qiShXl1NWGPM":207233238116,"PYTKoHhh7WqG":928888907295,"4lmGgF72qjMJ":11445413820,"vx5d8z0DrXyo":590153402816,"w78nz2ibKhl0":926555602589,"iqmKYPMwcPHk":56523120954,"lsC4RnjT49O7":340857075223,"yWeBTyqPlt0U":977026923080,"lwwACwXgl2ya":303215837265,"ZBhTYFPmnjKF":207613522455,"KXCbQkQ8tlMM":622891159102,"tMMSfsyqeNn1":410422058702,"j5QqUtlIuRCg":373771388862,"V2yr8UhUvfcZ":86067789165,"OB1qqNjFXsAc":206463035215,"fRpPEoWMukMZ":495052218466,"HVzIi1KWd9xg":462504534703,"G2CNFb4OuhZU":656270094422,"nJrSs70Abehz":999594385264,"C04UR2nObAiR":121126623616,"44Xa0qnEG7Md":936249089868,"ftt1l3AdwfpP":853412294643,"KlFos0NonRNb":915086307782,"3687ImNg9T8M":552529860674,"iJ3cQ6nHzwJk":295868404825,"Ex1b3jzGZZ7b":906404533874,"5I306h5PaNA9":438585416897,"8WOwg6WfQs6I":796745364032,"CKsXzpDDeStd":496775992370,"809dDz5qx34d":311972252948,"vXT5tf6CqwGH":258306543119,"O8eyaQgVNh4w":354439302652,"kbiI7d1YSazQ":454669158652,"eQXW5BMhz5Y1":577724934579,"DJufUHvXAhr8":793706148562,"qRt927ARtTb0":863505672927,"QJWsNpp1UKa7":337682266462,"brUu4T1zIbFT":630866249046,"2ORrpr4W3Ygf":28776527782,"J6CIeISyuGIG":375174163097,"jQTTuDanfHvo":361346833180,"GuCUBvtTjQCI":43195664650,"nwzYL2oeZZwV":604521273310,"R7hp9zCxiXNq":258488481163,"ngQZ2KSHyukR":717235894052,"wLvEOAdBUX7u":197625393103,"d5Zm9A5vOBRX":812064848920,"3We5tyndrxkR":552716001006,"HNncKFKKnfZ7":760095332253,"SnkJ4lh7gqPq":807693262435,"VJ6wSJVm6G4J":94573399036,"D3LKX90Jo8b9":858719795520,"4R4vAORG6pll":883816438522,"WbhW3yFhFbp0":970556697125,"oQWp88DA2jbL":981171351228,"DLOvJL7MoUAf":918844852563,"YlSQkIuUWCfO":453058616664,"A2e7TSWCcDmz":699277636672,"co7kUZ7mK2Pi":325226807573,"rYfoX1wL60oa":574336341576,"kHNh9963iRPn":638539402714,"XNS0AmIoeEGZ":528779253723,"NzyxCYWVUZjA":240043294424,"jZ4XmSnSJaL1":180792568077,"47GFuewVp5R0":601632544587,"0Kbpl68PdRym":718786728637,"9kHZAEgtBu7Q":38145227427,"MVTIY2IiEOhS":182193019788,"58JtjSuOsNaC":571506777467,"giZEFUAhmDhf":800355996591,"5Cr7ky54OzQH":782089728181,"lZu2b3rbui8x":495027652291,"gRi74MxdwFau":453513381483,"JMiq12J7tL9A":59784849564,"2gGv2e11GZOB":576139094129,"o8FvWHiAxavN":199284342715,"0kWHazRyb894":858699660424,"7ihCMrbdeiG8":878129997599,"wL6GOZuTOOSy":157030166316,"t1CEyEOCz0pa":765892140100,"f2FO4oDXml3G":49660591825,"dFDRSWvx6U4G":56784127944,"igNxrVgWFBmN":66592112930,"D0rqr4xwAklT":305418678073,"ZhI4gji4E26k":757991949058,"5pafJEUhszS1":4189151818,"rP5q9q8XC4e6":928150318484,"c4Ubg5GwIYUU":989129892276,"i759aRQvvZCo":516931410880,"942aJM1GHSIL":180489662656,"kH6jXP2FjTGt":767776300389,"d5qSPf1FN63D":78310309792,"6NaZlhHtorf8":736671138197,"VOrc16PLVqf2":820421151791,"dstadQJtTNFA":717322015016,"iFowjVyOCvW0":975837395529,"5nfSO7sguPfQ":389050650232,"sFNoZ0S3HT91":761353608578,"pYQvcueAULZh":666310568486,"hUE29poUmnSq":328562151352,"GKoNzJ5tvULb":885721303333,"SIr1MNP0ayJG":822154720027,"2da1Ka5KrTvp":306397371978,"ENlMJ9KDi1aV":355530951724,"3w4HC8XGZkPM":624518469509,"WTUj5O9JKokk":717327547658,"In4qGYLc8vMV":326872349258,"wV1TmWsKZ8eT":803499999516,"mAZEJerTPOlB":355911480743,"t9r12FFS8jR3":552217039274,"nWyrCqh2tr8v":493052788899,"hat6o6NFmJGO":616759618772,"P0Xzre2EI5Ga":551966647798,"ntSHgmzsKXOU":381477818519,"CGCpPhlZLGPl":809881913989,"LvvG07hkcNAl":362843448123,"ByAYtYx32iv4":260877127990,"nrts8Bx2ZtBi":155870746298,"TeWt8wOxlAba":291096004054,"OTPVztC2wndc":149569914824,"gFBjflql4tr5":324782570694,"ABNeTrhJ3coW":353562815958,"ydlV6GS4MuWi":440314127301,"8dGzwbxX9Ksa":248984833106,"vAfHfTGpMs24":766079882375,"HHlxqjYPVoF1":566861909722,"AjalFdLuS5SP":94781379560,"0cBntAAtRN1l":510825626271,"VEq5Cmye4dOJ":755159501548,"EIfpOjKqIwrT":638607556163,"C00gDdetMRPK":741089188656,"co615VrToeGb":734951960378,"brgNXvPyWUNO":165701669670,"EjMibuc8yOsu":235851898011,"jUv3emg4rChu":549018896491,"37SvkHx0RWfD":984541571375,"s1MiCt58xDXN":878187749906,"AKyl5cJv2BrQ":907026195727,"vDLjUpTe9632":467562532616,"84ntwmm7KMLE":820007685920,"0yeji2XzaN05":386861394735,"ajIo2QPrvuvb":245463763272,"CAXfDuVv7BT9":174322866516,"XNvGOt0EQHKh":569471621724,"TVeBMQX7OwVa":312456450581,"IfMDeoOMMIRz":603897283396,"QHdTlujQ8HwC":146389873142,"qDmRfTntOhKH":890671295783,"JfkYJcgyhncf":256115592925,"gLXDd13uWI88":314671696836,"51dT1WVj4R7E":558976266746,"p3y6hsJBRhxw":941248404244,"pGGlmsnPx0Cg":361868409123,"4Utu9sfv3jhD":570212774233,"m7v9gfqKoMsr":629218387447,"Dio0sGpTlDQf":678500309953,"vHKJZdQRq7Cq":417461700797,"I6GexHV0huLo":80417495829,"HaSCGJs0ss6L":58659172835,"ODfb9ajYALjP":651021438899,"wOuKgAq2k0WO":363672926636,"mW6X4XJ8bJCj":4319140557,"YSIK4giW52SQ":672288003286,"02tI5drFspeQ":597618688439,"7egPyQcSwiaf":708570782086,"qYpC1CS5LZey":237560227240,"KvjSgqOscW5x":634979157285,"qOqduBlGfu9I":908626274333,"Ec0msanvvY1O":637670920391,"UR74I4Kuoyz5":659823903769,"80WIYWIrQSW3":885993120304,"phLLGKj2bCt3":368970167370,"iD6G8HtRlMQj":841627243757,"YoitDqwlteNE":858808502848,"CNU7qW2W2V6Q":574045033112,"rlwoRw2fm6Oe":647853763770,"5erknKB41Teu":870297963717,"yr27seyX4sZR":304315416687,"aMibz2U83WaO":147361032302,"02AuAz7ZbmzO":697508513728,"IqbbP8qGB8q6":964555897987,"CSCohUrjt9tY":754528843570,"okBKm9HJ5KiS":891023070344,"FDYKGDvlw3oQ":398389541829,"AeZuB4IdbVFt":333329963810,"7iLbY0TrrYNQ":681302600379,"zSEbxJZjS2Rk":649112001496,"cYGSk5GPrC0v":485124415413,"g4DBZkIHGy75":687942322113,"v2x56DEejzXn":316733745848,"ZpKtTSzimlCL":474493184485,"756g62E8kD7u":920420521087,"hOOBC0tAuTQN":409047119074,"R6IH2FaDK8S6":998389410008,"1r3iVHpPeCZE":699735623653,"uJSjI47IYs7k":226753300844,"f3U32MCBdUyG":753632407669,"b2uJIgdHycVu":483348200013,"CfhTbwbqHSjE":667843175295,"r9y4JhulICTR":860167594620,"2sEkFwrko8Lf":214233469882,"aappmpuZR7uo":393371146003,"cj8RboAQS6rW":116416296688,"tigegqyDvkfg":784019952589,"IWfExKzYUodJ":958190594746,"MSlaxeHwvycr":462918222929,"LTMd8BhebWwV":205250176955,"pA8uLOD3o3Zs":852509540260,"iG4iJ8gX9IlA":221214372842,"c1j3WwfVPgiE":455189613353,"cYtsfR1YZDPr":673274108950,"NgDFbAPnrDeH":442553605520,"jTwubkwKSX5R":210496880626,"M87fIA2zGTUn":542877127631,"w7dbRF6vCP67":575456694560,"K7IcNnxjLkfu":72356704270,"u6vRdeK0Bg39":579474431084,"QNv79D0yrxfe":693251861197,"hYcLvmAxsShu":330049970801,"MFb7x64QRO2P":417466915131,"4FcfTLN3TV8S":52590250923,"pV44WLnD9aqN":955748674192,"8HKQvB8FRxNR":928107748203,"uRQd4ImccWbi":263144418981,"q2hS0GjRb1Yz":355197331259,"4YYGeS9RntK5":518609242582,"0ZiAVGcubYma":329962858239,"0l0kZBoCqc7u":154410567411,"rf6oDjGMh5g7":923160171403,"8lw5jw5Qs76R":668947853791,"TL4enFPQEXlK":656504841106,"LsrMDf7TOn2w":992193023329,"7OFCTvajvemh":492729674569,"szA533QCQ4rx":813464085704,"KdAVCv3McnTJ":144876500770,"1De9LW0pPAS1":639403766481,"zTgd4DoM7q5b":106895215611,"PEpbrJsDkaIW":429783231106,"qMDUXwZlrVa3":791864491146,"pjXxj9PLOpVm":380382322472,"Nb60PO5QjHwI":322481176711,"cfSpxe9ZtunM":593700285106,"vkAkdkpAP96u":689234712646,"tqhCOUhzrhAy":95233376687,"t0QgXYncRdLI":297478787679,"W4aC1euwOsTj":151217802370,"YIdgU0WrtbrU":134407154214,"nwvRpHnCLMxf":623541180861,"AdKnE95g0hcA":220817317394,"8JbyyAYVBU2g":681259764770,"05sdbNrnhDzr":508634305774,"1VNkkiShetbE":995520687101,"EsNVy9lUPz3H":820619986630,"xsyMXp60yH3j":806868466987,"d7NtSCtSCfNZ":380727930228,"n3P0hSNLsWsp":686131161011,"9imw7SqrwQ6G":850040976855,"JV0R7apU7wIB":870081939811,"4ZO9btAxKtAb":715293522546,"pKLzzcWPzqGU":987925294941,"dTkrFWTH8rmU":396753223427,"Hmmk63nwYNif":211133621551,"VexPg99TFysE":582832559847,"7z3sQ2cx5hW6":513030514634,"a989RCfKemXb":464641012979,"H9wluujLcuUU":321277030685,"jJFMdM89MfGC":194380260325,"vrlYQtOynKoL":839232541740,"X0j3gaEkWxcr":2403185076,"8gIZ2tzTPFkY":603678354387,"hEkoXvxXAVA8":85115520549,"imE0zUmTHazz":620299122478,"hSrv6OuqlGbN":149721565071,"NJmEYAs0mYpc":976682500821,"VeIS5ZBcpQAp":287496926653,"N9k1yvZYkvV0":923867899202,"OqEanqshMvcJ":681080076826,"ihtszQWsZmDc":607777260603,"XyDP3jhUMXxP":479988234691,"V9XRAMrOwBK6":134556996194,"Foqx5DmLVaBu":2864288175,"BZLMU3l3PovK":388489354426,"LNn0UUI6JYmn":670012050261,"tF69tVWxInFT":568083749970,"w7LrFgyJNb9R":404227235222,"saxnPLHL5lYr":910126958366,"hjhAnbrBxhV2":789894366758,"b7dnRHBoTd0D":397001505351,"WklMoAWODiyY":229013616228,"kcqJrd36Jgiu":929265038282,"qYWSrxciHPs3":696313621373,"c50R50azF0p9":128504170246,"hYKQq9Mq3wJl":869763700251,"KpIdBeFDQx2I":944260423595,"4N5jVAoIfUXL":41229412300,"xG8wy2OY4Ilg":616300497707,"UH3ahA6ZcvBf":53806796811,"Ied3snvJBJff":105417858292,"TmUqB5iYL5dE":747476422577,"peVehwEUdfIF":281337495218,"667KDB2m12EU":929270617074,"SzH10WXJa8MK":718747966125,"pJuhPMlNhJX9":744968515300,"3Hr3kXmtT8iy":337154270431,"V1XVvCg7uDeO":360402696901,"0tnIkl0pACEP":536768141067,"TkKvrEmEwTcz":419641394330,"kEX9Gy4IHKfP":381184801729,"n6AlfNeBXMTU":502058899357,"3mPUpBxsjOGc":760808180314,"QuV3PYucEE5u":732518886283,"xn0NB2unxwqj":291589043081,"xV2RvV4Rm28W":67178899699,"Ef0WiS0yH2UA":532049496083,"bVjACAUNMxc0":770459658833,"a45ZVR6HnhHO":826331415186,"nmdoTihxhn4G":193941389288,"HbqBWbPMYxlh":605709382393,"QCndGPtW5S5U":661780201051,"mm8lnZLj80pZ":575775877914,"A6KIlttjgbAk":458062025839,"2lclCfcDjcuy":223244087272,"afGu2bShqhl6":44070218436,"Z0Y0ye5yTSwi":17986647526,"7YusbvdRuY6L":121728227938,"OSey1uS3ymd5":289045960265,"OvWrtr3emg9O":774791432329,"W7xVFZcro1vk":861721571679,"yFXH8aGTpQCi":937380199035,"5qI2NcWzdfIy":339042537754,"B4c0xp5cRM2C":880767639098,"LTFfSbMbv7UZ":699199785569,"tF5u0prP1Isg":926774868771,"93a5DstVTSQJ":479764115968,"P0w0mdyeEqNm":529334280128,"qKF2h9xJrtGF":739326735670,"CfPgdslXUEi0":129689045047,"UkapsXrop4jv":471086825031,"wYRBs7nKNY0S":811899748741,"RKSwm3RnX9NO":182783262731,"Pm4oL9YeFNNm":882037499278,"ckm377FVXSOk":529324526122,"FTqLF4lfHRRG":382655580087,"lLOm7X5NmErF":262200758833,"tu3KPh9qcdKW":796638553073,"xOMS0U5qoI8p":79060805933,"oFWiOeNLCof2":52101561464,"bcBEj7rLuuJF":968505222282,"ozG6IqvkydSp":182128541324,"QfWXyOMvw8Zd":818187668070,"HFsP9XvYF8R0":583968182749,"Qb7oKuoEvXPS":599327028536,"p7QtJHFECeUu":955494935802,"ba47RKg8SmWh":225256883064,"wZeH5RKI5p7h":777062906784,"H10GSgU1RcTD":705121957890,"U4wWYyMnoyqv":370253593572,"tUmVfCYp6cze":614735162389,"eNPKjuVTEk5j":495839770075,"kIeI51RIFW3b":463525432015,"Q7XmQRJtGjAi":288205038393,"aHLhL0VTtEDk":550862602997,"vE2SoaJ1AvYM":139103098762,"gKOEOGnMHngU":902451379636,"gCpxJewfS1n7":841626964428,"JeM6tzjM30Dc":875072220852,"p7684WLwovpB":25859217984,"81cetyQRyG7c":822496689756,"eXfLlkDEqhIK":954049686656,"NXX68xXV83RP":884330432300,"SNw46M2XumaS":7550954645,"EQz88sZvV10V":832587114978,"KLdhU7iYByjc":799921883043,"4I9KDlG6xsPj":485684777121,"brNQ853UTvVa":615973517828,"Cpe7cygwazsn":998150201392,"IOgErQ7QHxoM":894868597306,"DLQ5eDWkvHZZ":652532966769,"FlQgupU9DBZT":906895075742,"mFQ1riG2oAkY":62538692533,"e5Td5qsP2Xhw":373285955462,"hLH42gg7P8RN":342626341174,"dP8DudsZA3dk":296785509546,"lCUFyuDClrvs":658653762479,"bHlnK2OI9bop":636891543712,"oPWUE0C4Xu26":634385806968,"awYj1SrvXSAW":428882058122,"9ut3wqnhiTVJ":93293977276,"6353A5Hp8N7h":190708933915,"yVF98taHd7ap":905487049969,"J9kTIkOLPU77":953840844664,"mhOphMH4xF1R":909410461546,"qnmFMhLebCPs":448727657201,"XvRV4kmZZHms":120229159833,"hqHORRs5N0fy":798478316339,"FkQFQrGa5OOJ":702166184430,"yKBDh1tFZDZS":586928328752,"8MfcLZtHeV2h":908858889714,"voDSvWmXFMYH":288606408652,"OiyaEFCF0Let":123472960732,"7ydmfhomImwV":628504609963,"yKCOKSOMLflo":220196037153,"HYzSbpcHkZOX":153461284413,"I3nt82Ie0FqA":307716070938,"GwJg290vuLBW":605458010836,"ny6O78vIFoW7":836323879448,"PpDNN0nYDW43":905674735061,"bRMB5BOAUC7b":606829265998,"1zuDuc1UgixD":273138531130,"nW5Ob17tfrVG":939986867106,"sKHMDXb5K9IK":945793822736,"VI5HVg5dSdLa":876033087435,"cGoKBAGI3vU3":622230615936,"Od7WYrRJOXDy":919292644713,"qq2zbL4zz6Ow":739658061687,"XVvX5cVegmHy":195899254892,"VqEJRA8mElWl":216585381161,"1vIB2tU2PanE":858539299891,"OO7L4XIPe4zy":641846113238,"zzyPkUgAxqyL":355455470283,"f8QXYAKhRRKw":48163614291,"FSTlNffVkalg":976697609949,"lrbL3hWtuZ04":752087710317,"6JEwR3RWgaeH":567089267653,"7hiSdP8qbeGw":748681749674,"49VR9sIpIgfG":477948800415,"h0kIJuIvefqG":72213096310,"UnGc9meXtYEZ":602698225352,"Nx9B9E7PxGz5":89017044654,"3GdDr4X5Q51J":259414061532,"0zDsAMZjze1G":300186305565,"WfuIsa8119be":783215936687,"WGi36fen0RXq":973691139404,"p407WoOLfhsx":921544067721,"0jF5ehsxwhE2":74863032148,"0BfPkHdslvno":342304893608,"ulT5amCjPtAc":280276682144,"Fz7izkqQMNFr":897371726793,"NpKmp2eZzGwD":727501033964,"70lJbPDcmv57":90438894662,"TMMFvLkQjyPF":327922569566,"UaSEySMROzlo":904682944606,"ftQFFd7uVzI1":262432003236,"xqAF2s63meH5":122957359302,"Y22z7F0KerRZ":280617405681,"GVJdpUvxOSZQ":793473382767,"ZhTjEbw8Qxf3":847434486391,"NqrozwTwGdWI":203931768270,"n8DGzQjfQJkn":323653949020,"hmz2TU6qnHZL":201416139589,"mfF7UxtfCts9":349857240742,"dVg9dZZ1VbOl":363307334681,"cvrJMd6hHmpg":128285270124,"QZ2AkpfCQsU0":343764708862,"3PUr3dSVzM09":616543292052,"Wh1PTl5j8HBH":26328529675,"ETDaumXoA47B":186936858678,"Z8gNlYnkCVUC":746684337200,"nUh4VtOiiaDM":959657486109,"zjF80HzmSNfN":176953222168,"CFSOTD6605iU":690170980316,"onpjzBMu2Bju":208513416734,"XNATS6hFkpeN":636195144236,"NIaEcF6t9EsC":301147894454,"iRLOjUbMx0JJ":193751673242,"7PDH9DKvn0u2":160134518213,"6eouMVFkgmGb":182671387258,"dsUZkCwmZ8OY":539761270276,"w3XNYboEOw3D":99727307273,"0uy0IakjhzLT":947405072048,"ReNVijaM2tLk":385721104848,"umLDkEYJygMZ":718482831271,"LrWAtItcNWYz":507430480417,"CTvEgvmfHYiD":808704410088,"eyFyL3WLGzC9":396082541904,"G9BcFkpIFbhO":888624664410,"X38UPDBGW6hj":496268305376,"OjXS8KQxmrBi":394304891697,"bmyYHTFIbaZX":898097396500,"bejwKMVVCrOs":170424845317,"hgyflqaF4YFM":252985666080,"z8aO1tE5MfJ8":187810554393,"ZPscJ47tWKGi":691550088976,"ZFFdQQBa2F4V":706752143568,"51mSoXboj97V":592903254359,"lmNVHsEzEsPk":78234289716,"xq1WcyiwrdIb":984920317384,"7iVv9BqeKFQz":642615310678,"iv2sg6AidzuF":879544595145,"1IOYJPyLyhCk":515547960390,"7n3yahZeeaWk":888680155615,"6zKuL9LJJbFN":64770437603,"MuHLfRJZ5dgy":107582196411,"gVtvZ8qHTEjG":78747977254,"ooBr6qmA1u8N":406381928179,"0vpeenDtHpFR":989725825543,"p32nBujSCWRQ":883069929093,"iYktptnVEa0x":891128259481,"ZxOyfcaenvDD":870651774011,"FVfH0WPqfdkj":910070888190,"4QgWpqZk7MZr":264789438857,"1Y8N2MPuGJ6Z":122704747400,"acBPhDmJ08ws":848257096325,"xiVcsRlp88hN":951326224684,"E1lZVdfZMhHR":56202627820,"9joqvxn1mQi4":599551986470,"qWeaexzBi28j":965593857397,"igS52PzBLfOd":191863042745,"TEHr5zO9nMsX":107059690579,"UNcnRFqKRGPo":622009818928,"UF5Coh4O85ev":926549685804,"UJYertsdkIRF":268358241906,"fumWNYhdGW40":517850949950,"OGrGiF16xF03":655618212647,"Hibph20Jitvt":34389285293,"3NWW0nXEo3Cx":216150741853,"8foSPLmm8Btc":341675254009,"9xDIkOetI5MP":218755134202,"pIh1VnBG96le":666277348041,"oX7dr95tMZfL":159460087845,"GXKQ5wEOBBhM":111703798939,"J0PzwXErLnlM":924224520961,"nlXeKbmymfap":169918224833,"acl3Lh3Rarnl":393278295231,"qstxTn2iALh3":390903474785,"h1siSxYMiH3o":910828012804,"76WNIvPBMUoK":221140824486,"9f47wQ9QkSPD":210677808620,"BuL4qQNM14YM":339245937066,"ajgbcNJNzfYY":968048803756,"FxwxmgWD4uFa":971332488372,"IB5d94Jrsb4k":980308597358,"uf5IC95V0Gir":72302014193,"MZeff1hKJe5i":952678834162,"Hbd1vptABE39":402771049170,"xCbuZPptlAUX":56782711778,"gusYkOHkUNUT":940102462476,"Rnp6w8QkLspu":509479188110,"9hZSOVINpQdh":240854215934,"qCleNBm8TTw1":484939026029,"YsWnfP74g5v5":112444463575,"Y0jJRJfdrL5y":829369980438,"ppBMQU7S3EQE":138547115789,"JDvOAvyaIxXr":381430177755,"rJ4zYpOE8P9L":168008786034,"FPhlrp0dC1nc":877906056616,"tisuwhtjCqyL":67790002552,"RMUvADGdfgVu":934607180689,"Satd9AE2ZC4N":467860705114,"W9XwDOR2Wemo":838202070230,"HUghnHbGEpGI":647340043506,"nsxFQYedgjGg":974596850973,"VyzX5PQkNI1w":506158694871,"sIimedgbLOfr":738841377068,"JJiKXsy0fpI9":975325941570,"OosytqLQisjq":271235129169,"BPriFgFuIaMY":382946778282,"pGkSd5L0ttag":735765346953,"ifjujr4PwvAr":922132511799,"ae7333adCkL1":186695516825,"H17RHEd3RWn9":460635714841,"FcHbaBMcuGdH":634502511246,"4CZ3HWTa46fz":79781370666,"Y2VDc9FIU9sB":884723157068,"DpdV05orP4Tx":717526587696,"zRY4Yt6nj8KA":364345865693,"g9DQuqUdzsVc":866132160411,"IXUcycYatUBb":592250024014,"7kmXKl0MwnU8":483895859847,"tUb7u9HUn2l3":636647899062,"po9HSVDHaiTh":84700537940,"QbfgyYf1Ck7K":76223874621,"Dh2JRdfZmeg6":535520115583,"xBZEMbwBHqSA":789542042070,"OaZ9KmVhlhRi":291102456021,"rJPfHfkZM4T9":849852629192,"cXtDgxbE4WaO":536694992756,"W9mXXzFx58uM":56469801391,"7P5MT2bMz52W":695175202474,"kFcm1lQm4O7Y":822324234907,"uaVPJPj1byTg":193830501398,"8c5RRiaQL8Om":444747364966,"MvLQZOqD2w1Z":708358029531,"PnNqBkMpTN0w":653228217616,"6buMmxuP2TOU":913893922118,"nkd3lyhuoLfi":10662387385,"vx3i5kXl7ZWj":265860814533,"6FqJT4nZ5ric":197601434241,"yccCLhHBAkDT":482152961552,"sbq97gpQgt01":177462236747,"B58IrF9OQA2T":646475133776,"1vnuGC3YlpQg":957355884204,"tIAuFsObH2EZ":594289034419,"Wa7ZOKPRCVh2":662594862167,"dJuXrCXGJZ0E":74991678150,"SsQ8n7wbkUxC":530354520661,"0BN64nFffKPk":890607386081,"3KAjS269cokI":460496610736,"HhLMSO5jvsCO":861431063167,"u21UvdaPzL2g":134267997472,"ywUg8RtFhOxd":854164686496,"ApXTdit3QPth":8279991,"KhwNjFNkIi7Y":52514295765,"dZZiNVYMmPlW":402480666441,"FbegaM04jdTf":256146750711,"TgpamBtRXav2":725290632636,"xJeACQGBRBQF":490755327089,"0k4eNsfp3iAD":808429423495,"XKBicYIpNRIu":600732349583,"RJhePP47fh45":787063782986,"145k0d1LRbuH":743788775650,"AhpyYIZ9RTu7":15774954277,"KpuLocgBQ2Xs":913315846546,"xQUDlY8GeGXA":357745409398,"xw5YK9WkUX9M":658514491634,"bGT2akmblA6R":310065430213,"QdmQ4QsJ0fqr":379475863085,"IpbBrmxwYDIE":494377145313,"Ar0v1buhEyJX":554113475379,"rLItKHtsQcF4":321167357661,"nCs8qqGdm4oN":489925995205,"jweWPfBak9Jf":891089827122,"1gEEzn4C4FEG":724890152767,"PRqx74q4Nztq":504851823813,"px0FcO15uMKK":327158241796,"APYTlj9ucxvn":455651420639,"IiLi60CjvqR7":212531350461,"D5wLGz7SpouL":703525328002,"lxvlqhePOvcs":574468243085,"jnMznhzFTG8b":370981914094,"3tARUrmuMIbp":48131522592,"QiIK7qsBIQbX":398474137446,"PLUGoIBZQ94g":516839360700,"6jyY5yjV3B74":996496344466,"WCErmDINtUKb":8910265071,"qgF0f2JysYV1":316755874833,"D9MF2DqkvzdU":172437459592,"quGpsGzna13t":752846787174,"kt6LL3YWc3u6":899973651879,"DLGViN6ndOT4":984578309496,"JdQ5Y4ZO52gF":407681027768,"ren6NL4JBE6E":213142221063,"lWC6o9ZYE65J":697394520380,"bZn5tBPvyohh":658809413162,"H0Rq4v1kwbyb":154245630688,"CYT8k8FlAfAz":266864472672,"gVhMuZtQodGq":962139794611,"RxzplDXNheKG":941540915988,"5U4MWvbkaCyS":547437111605,"RlPz29l28I16":679829390105,"jRWNZ3k1h84H":50793810306,"s4oLmnJpW61m":882881692937,"6S6T3A8hDg7W":602701544762,"PtUcvlS8VFSS":980716204457,"C7WKMDaLWOLO":202307765691,"KOuhITfxwgBC":379824041054,"BMVQXy2a2VBf":271894583844,"dYurX1yLsfqa":842229739134,"AC691roj1t0J":948546719675,"KPFEkvfCyoUN":894797423707,"l3N9dprdTjCs":701940775974,"vmgv8bn5s8Jm":787938583276,"cfsLBvbYeGOQ":354940633232,"Ki2VQznR4GhM":772307255014,"Q0Jr01JOFoXC":815836105940,"tM7fooZAEJkf":238233190727,"orHXBVvAKTgo":493641854781,"TqUj2GsuldiG":941419149774,"WHjPAm6Tyel3":9790870920,"Kse8zvOgCewo":238768904774,"DVXBIDSDQfSA":872855064696,"hiuzLinojD8A":95114715995,"FMNS8ZIlgbGL":375427505045,"2TFL2n9lSAbv":946277862539,"NJH4TEqwiehJ":351289436421,"HVK6pfC1Fm31":973730094727,"xWZ320dwPd4k":861548851176,"Dv7g3neWYQwg":593609596623,"BpcrDb8KqOmE":709628780738,"mmMMtRoMLxXl":80490033976,"Ag0ol53DzQXP":289119631132,"ORgIDTfVtJxL":700526611951,"R6AsDIgnLeip":66417488381,"4h2rxI9xxzA4":913136820935,"6OfNJirvisTj":842543278018,"FilroXuj2mDl":50900373326,"elVtjEQhJDT7":598618439154,"uQyRV3c52TfY":640049101833,"YPmpEmNeIAmA":288562245624,"RfftCXOPs1nK":364523304619,"D2TNKgvgZdMf":856676928648,"tmuMV9xZmYZc":141473964862,"AKhHXvK6HQZU":708941795672,"zvNrML7Uwx0E":103815465749,"AGMoJWk9SnqF":48450930913,"iEEhYEqyDAMH":3805927683,"OKjU3z77TVbh":689519020353,"Cf1A1ZWv1epq":333445456154,"bFNmjVPP7rJM":121136859812,"rRuJbS1KJX3u":337791455760,"O8QQxtCpTuvJ":308148079427,"uYsXzlZZJDFt":97325000343,"XwRc2vFWprnA":148412323462,"ZEtES5bPEDfR":290860163639,"N8N0CG3tDBjJ":521542013130,"Hw2vQRWJbbLN":635991931455,"FUq7VfOuEcaB":249276635331,"IhyvjtSUYNlm":693947935812,"9TuPyAtFUHfE":525315651123,"avDkyxAeuLCL":906181648525,"bnFmRgMKT1gg":557745993262,"qttpeMmzqNnA":89328016154,"55IlKEeSUDFY":801498233875,"oj84xJ15Wh8s":780783110128,"9YyLUhPXEgkD":835322760009,"Mwmxv49AFeRj":6349486911,"S3p2S0XCnUcv":165782444481,"QDeSr59CmTmj":97547625986,"DnyrdLWQySpy":111678909525,"IUwc1tX0Ckeq":486631823935,"CBhz4gsRGTxX":733019824957,"m5O6umCsOWF6":252321424885,"xOebP8SDQDBL":452904409548,"HVS2ZEQhFp9Z":143526044059,"bFGmXNcprkbV":88655728526,"ujfHjg3hnnZg":220557125701,"eko0Q6dqL5JE":218489266099,"DK4lAsdFVwPk":236390127110,"LSWXsd4yZweR":248317736183,"3qDUcCdnD4Gm":50822675525,"H3wyjZhAHztI":243929555084,"DthafGabjCrq":711122118585,"9zKqZKzy8fdf":999609682247,"rmfQIptKFEBb":568302983090,"sRmk3cYDaPiP":45525990093,"TV2XH32RXmYC":973186709629,"mup2gXd8K5wU":504244673243,"mZgrSPZ0qiTE":716195192341,"dBV9UVYviRgR":13439430554,"MyYC3d32hnIS":391563459180,"RNcbDnXlsRne":475182457052,"W0bdJDRV3xLu":344030405889,"RdO1kzenVAwr":109590800376,"JOqzeg94msBH":308815996165,"iK2d9tMB9Jng":444509373131,"hIf7TfRqfwHM":51832372234,"IOIxEUxNBULR":558838791612,"VxeEFgpMP0IT":76800257408,"YbPlCvOl3C8s":647298585920,"xWuGgcTrWLT8":905750859150,"YLQ4qXmr6h4X":699966221401,"VY3FZZJIij7m":960927629866,"Bw9trvdBR3L5":284041106976,"CLlKx6EcOvVF":659966943550,"816Pld1lWmyR":198365151846,"SHpAjFjgscNI":796386612136,"8K433k7ADgFf":778785068045,"Cen5jchg3OJv":1002278121,"hiH11mZZ6e6g":308836673711,"eE9cvyrucMRg":103415250321,"aR2faKDWz2BA":256390737830,"dUOdFJiT9TlO":226520234104,"GLwUbFmDZRXU":935008114468,"Sye9quHkYpMT":582316605309,"2wGcy9B0NvTa":631342730550,"HLO9toTR9bC6":919979292340,"WHA9Bblg6zOb":101357947802,"Qp5n7QYlpAEC":738338155356,"6clqQwCOJOFl":995027371664,"65WfbpoGko0u":953070110503,"MltB36gAtAjF":597629603449,"neI2RtlPduY1":15433890277,"D8KSnnd1mNcW":833656818344,"Ml3X3u7gjDhT":277083562004,"TjlqYOXxvEdG":990264008448,"iadf25pnxJsJ":359134480890,"j2UdXT8PP6vq":349296740434,"nPAkIFw7yzKQ":410435791198,"sF0ISrJRM0JE":148546760705,"D6xB56ohNoqc":548874371281,"cV7pqp7GoVSH":568538243663,"ZNLSBLz4IoCo":882662159780,"ms9x9kTWC7VB":124656644473,"IPngOxBuowTF":374637905558,"D9Wn5E0Dqbj7":604400483334,"uu6lmtMfSbq8":63732042272,"QbH7WQk7wilk":573244055826,"hZWVOdrRWnIC":183168359270,"lhQNPgUp4Pot":326237153053,"CQwFpaEx3viC":375225776247,"S6z7urX4lQxK":494287702822,"rxEx1DkP1lCW":894159251413,"50R8f0zolKYU":830358416132,"bJp7TcNrdZhD":799136195049,"fBL5KYcbBrHP":75698098175,"2yrcxn1N5aA1":379354372087,"t9JbJB2b8RKg":539541186712,"9Unlt7Su6Xvc":849555537410,"YbmofTyINA0T":471794807738,"KfbBA9uaZI1N":320008380314,"GuJr9TXd9tak":210211128226,"YXlmXtjrRrnO":50829526634,"9Knhys9rRkdF":568674416159,"tJ4ddpf69ftc":623989346386,"YTnaRZG7mcY8":519194893805,"IjVDqxDoLUSX":371092633812,"ZuhoFVmSBcnE":965693880716,"0sMkY8JjOVCv":605884346221,"0GPqfbi9TSj5":30574085734,"OFyYCa4QwSec":493289991184,"NYaMh00wFauZ":468669699976,"l3sWXMhvfnVv":557060310084,"Gna3XLsi6hyo":985837878920,"qOG9875SFYg8":39089648580,"A1fjBIldcIOI":512808109372,"iJiS9jhbr4P8":567988316351,"Nf7HgeguONNd":98158068713,"nH7I027v61Mr":636017455201,"3hb88RPxaLky":534215088414,"tJqT5JpgNuYJ":234589519284,"8594oozuX2TX":406465225580,"aHrOvyOQkKiq":144890765042,"r1TWgS4gx6HC":900866697918,"pb1lPEKQbibg":743308105124,"17LNw2dKavPf":180519033675,"7G7GjspMgIb5":46503374395,"bCyaZcddKx5o":695336716076,"BeYrh24nMY6o":271221799525,"4ThbS9PSjOSU":645767648801,"okTFIg7EQX93":292261409256,"bdslTESFjLqC":48019676080,"Nnl447H2Y9Vf":260828849847,"eJFpW4rGdQlM":760450695292,"2NUwSymfa49T":941595413573,"6Yr5ndqAU6Dx":13115889755,"OsGeuv3vs6Id":990609862336,"pJ3NrNY3zxLO":78193398896,"DOCEWDaGQf7k":295382167766,"GAdhM9k98Uow":888000653677,"BCUbHv6vzX7c":800040342574,"6bM0bxOM0xNm":509054685480,"R4x1Q0XwFuDi":627252292514,"Lzp9mvKcfEna":707964317537,"hR6qsUBLxkUo":987397192095,"aOoWDDdaevga":145434029640,"QGX9UrRfvC6h":663189404280,"FP62sCxEFrSD":624396809132,"dpv3OjsMNvHr":801523373665,"AQpcUTOMoTL0":313654376786,"TOH3tVnYuKRp":197244258933,"IsfvTPFQbUff":684066635514,"Vi1oGIKv4Hya":12824288727,"9vd3775VE0PB":674995255821,"cRqSEBbzJ7YD":832817783253,"ufflMp6N8MTQ":468406413991,"h7OPIxB22uuj":848685780437,"Kf1vbiAgY7kb":590257628175,"1Mab0Cfc1YCC":200430292605,"PROoHfs4nP80":234647206638,"bYLw6lweUgE8":328084162904,"RNRx7pm8b7k0":823428943065,"ZIxbx2bk6jfr":454001132660,"sR7NeW79WEBy":111794567323,"kbw6QkSnehES":484506716617,"7Kx0XcFsicZP":333663015059,"OltUKfc8iahU":817221295991,"ek3Y7eJBYPX6":348340278949,"e2MXJxlXaMrR":950732069888,"wXiXZKCW90K4":473918849384,"xzTs9clzqGTY":441299229912,"qPpC9U7SHvt7":251477324919,"eCJ30qEPJnUJ":937417992469,"1lVhCL5JYVG1":312005448507,"jFc23IOmQdI4":83658590810,"VIu1vDHxdnou":363806148850,"nvrnG1DjctRk":898100999878,"NYRnBcVok2UW":605850833354,"WQ3jTbNIEZ4n":836818761135,"cQJ9kuOnSFCn":128858999373,"AAiq3lVroa1O":592574442465,"kcRd0EA2fQ66":788204032415,"iMRurAgqCiJl":888999491490,"iCUFsaILlWfr":913614039468,"RQnZSFqaqc4T":671392991102,"WG2YZ4MMpmlX":676867293890,"w3vMYBsgERau":676513861271,"uZrUQjFAcSzV":450144924713,"xDRBFpOnQWCr":234344087728,"zfvu3s2IAA2i":412114606497,"1F5kGUbjIPJ5":744325253780,"RJAR8v8BkwZF":595776916784,"oaH9mp3UHaZR":713836083545,"Wu8KHRs4m6lS":466183086474,"dsyooqmCRyVd":1274855391,"g7EMn6RN2Dv9":507618652018,"aPnSXOX7VmXx":251319403870,"3J54WnhStLB9":508832131640,"txhGHR6q7ToP":741069138722,"tRCX4TgrAfVX":431306400316,"GZgXm5l1RrLZ":906759219961,"L3bNtoeKoiow":215987528203,"r8zGQA26aztt":599865316713,"RC9ttu3VE4jC":512109398413,"jzgjk96hUOM2":173731708345,"HwbKTakn4si0":646429800947,"sthkml0vjUV5":792872921120,"6hUnotURoDgD":248330483125,"OnsnJNdkbpkh":704724603778,"dnQLbJkHcy98":176334182089,"VwIulDdFMJbk":603020400224,"a0uGwaOiviFO":228735943110,"kuSwsNygWB10":833576296329,"euJEpGpxqOHp":485017488703,"VDhfAUsFNO3V":660476212262,"ReakkhB81wau":293943003960,"iPdhAPRZ8H6R":735635904427,"GjVQqR6v6rna":21247996240,"Oyha75tygahM":496506382837,"A1v7eEs1KvPW":749881200246,"V2pp8n0mucHJ":776257411549,"tkOPmwYODTS2":926311104328,"42HB03YJWD4G":443644499302,"gf5m7FPrtG1G":403072065032,"V04hvSvoOdYS":422984115048,"A80jtfhRnIHX":661770231412,"30sxNUiB133z":39143138702,"TqDVZLDsOoGS":295185030451,"Nzt4ImMkjXLa":608181623506,"Ioy5bsISgJa9":242900830557,"a4K045ZpdOj0":24318364557,"M8kaRQS2ddKf":941905457244,"H3OiBGf05JAp":280891203166,"4Yqn9FWbY376":389082859739,"LPMkM7rF1z1c":495100481222,"1UFaRNrXIxQI":48012699453,"EvzVRQD9ug31":746370843632,"GpIP15qlpmKS":140317977613,"7PvWIFxTGwKm":412970516074,"zxJJzDz7EUA5":189087370059,"RbHCZBywAKXx":291651005180,"tlmOs5iWr7CN":133907883211,"JuZGApPrThQm":169474479203,"gJlY1aig8aH3":692030227305,"FTyaDNxMr9fk":969947454004,"iVX6Doh7uI3T":240529556266,"2jUUXFQO3ZWJ":809971239605,"mV9Fif8qxFvB":280680215867,"lVb5TKNF50pl":149167515713,"Saknr8LyoW6o":575190133026,"vRqx8IDD3Txw":931982590190,"6zmoniSSJySf":869287291151,"GerfmGShogn0":668550793167,"DQp87qymGXZd":21263537714,"epZhdKQZblTI":194671357263,"xz6BS695kVYL":995461804749,"rSUqzFXAqy19":231508943491,"47POslSn3HXO":249217055313,"pKfuc2aAb8rS":677292149277,"oMqM9azqAski":926966745538,"Qvx9UENeENRA":349287944915,"lNrIvyfV2ndV":807222243427,"kmx28SYIfNh1":806098854444,"mJEwkWZ6KimW":175409442808,"MakhTxHyRKYD":142488165593,"eo2XGJthLgi9":756346108361,"hksdNGxzCnl1":90591959946,"eZ8eNAPZKFeJ":548239602820,"MaYN5aqbd5iK":210575919508,"Ykhc5ImKG5VB":403020645239,"0sGN0X1XFSYV":566721690340,"fWcC7oyGvfnf":535124304732,"9ne1fzkR60bs":900339229876,"1KyIMW8XP1nC":127220967068,"lOyBogjywgLD":534465041421,"AH9UTfwCzdfi":455909084694,"a7qAgrMICHVF":854517738554,"yV06el3FGGKA":549884223657,"NXGdiFdfIRbi":885095794934,"JCwSkHIeaY0O":235613887436,"CXeCBUPczElj":317634377101,"3vNmlQiNhjfD":26545071524,"YACi4PJtUpcP":1259203395,"lBybdRzVO4kp":956225031185,"aYcfztWFQdQh":665292937219,"UzhKyVqy836Q":109888089897,"eqFjBkQjmWjF":615030664457,"xwYZ6PnywSiN":928155757939,"AD0KC1JiI9lh":799144683867,"UnRfjRNkBVME":282490608606,"FOPRR5278nda":787014068344,"24OUgq0lV6nD":354661741850,"VDlHiFPoRmw4":498275155454,"rtbeAnS4f8Jp":911903644088,"SdP1o3m7iodP":254515312320,"GWbfK49WV6Nn":625336687650,"zWSqEmm0PZX2":170141935833,"a6dNUAsyo1WI":357879816781,"oN1nEKi1Er1X":161584260303,"n3z28bz2gogE":437716691962,"1fGmJfr81zus":205749250527,"JEYKr1qIlVRO":755330928682,"Q52Y9YUwP5zG":10673746377,"MxiyGPSiCpo2":40785518240,"tGXFOnHEiMQc":259280360542,"x2sVwld8jAk3":54859144036,"Wz9Qd56NBclI":316655504698,"ET6httkLHU8d":735945475254,"gragKCZOVkXu":431444096376,"B7MLNjwxGiWq":119326620174,"eucNcoSJ1g9Q":695237218949,"us4MLSxRr2pq":859334762510,"vPudksVS6YUk":774824084962,"fpGwlB0R9f9p":678178141856,"BvVbb6Q2lihM":552993108539,"HVDDx8l3UNVw":908176215877,"ym5t6uGPx8lb":14684256333,"ZnosDNJvmVFS":695313032730,"8SEGK5Shlind":347744987146,"hl50ewhbSeog":933099230758,"AvU33u35IOpr":407926017303,"HN7RM4BBN8FQ":640146661935,"xV1ZuC74QDB3":996470611860,"pUS2KrTcGMng":641300313623,"nx620cOBFIdb":482273954840,"ZlF7v0rs6V8l":213961147541,"zDdpYICLOd08":299492124077,"WYGI1XERh8qy":443904487234,"Sf4IUguq5FHk":659909561006,"e1LljBx5Z2xj":519815700363,"VjPFXFiu59Uc":510716395902,"cQsuxuns2685":377797102894,"UVNEwg9KqX1k":668753890234,"Q9nrTMILTGn7":80333459650,"NogV1nNsdIB9":212865964821,"5r85ncxVyylY":159589806248,"E3knMcr0ZxVu":31500121575,"0AFTHnJg13hL":158279907311,"FDLRSvHN30PW":107149171042,"IOYukBvKvWTs":412861677304,"lS0uTVQxqqpg":59118531906,"4XmnuBiL8CTY":720435683043,"yjFgyezfNFUc":720895833048,"0OPKOuZjkXWf":449855109037,"OjgUd3BwEotY":594380194463,"v7eooIA3BMUZ":684127280899,"gjVblzlTaR7j":725769441266,"CJJgbBF5eB0S":822231054689,"onjouc2AvlbU":996990012596,"A1cQHknje22g":110514426958,"QSFyyv6egI2L":888114585738,"BsfDJ6wqt48C":342046935899,"arnWAaEJyj8v":124402886905,"DWsBiPRAe9AR":621970618965,"GRUnVaoRKS7s":750046317804,"dEdVodvfhQkI":706388412148,"v4Z3HdX2OYK0":230849360291,"nmq50h9twylJ":742255682280,"Irv5GXG9o44A":853954245622,"HYSIbliEkUKM":658568911877,"bEW5ll5DiLgX":491268602875,"F2idMKC7uhR5":141994056200,"5TdTAi2ZiMnP":203119151478,"QAmcdPzHldVe":405805505855,"rn2VWvjR8ltN":649815751641,"RWmB4YjzdRJ1":172059597290,"9ix8rrmvedBw":172021604208,"nlyqpGzSsiof":351360568682,"Znaz6o7XEgxz":18406674842,"ABqbVs2UARTD":667515346292,"VkIsF7sQzmr7":378213277153,"n6l6FLdeZb3F":113787274445,"iKUD8RU7hkQr":627584899487,"i5q65iQJJ0Pm":607015414296,"qRYPWt87tBGp":513030641130,"N7VFpyhu3LvQ":475824772073,"DO38XerAZvUz":770548829836,"0vjdgywhaTqC":151763668453,"ESsrjPPXUzDN":300703508469,"erJkYvSEe0XB":483846236979,"SiXprsJFu8yQ":968871772086,"gTheKkDB3un8":445699990474,"ln0Dx3mwQ1Gg":716454666784,"sGPFGtbtsPgl":299024064438,"rx4J8jHjgHvO":986147247377,"HwFDuZiajOVR":827668294619,"bg3zNX9xX6JU":817427786041,"t90nF1RqmNi6":385019117066,"NszVojoWcjAq":599272580316,"K4VKb6GIrB2i":329146549487,"b0quskNrD8gG":429729502986,"8yVvrPOmgZH3":308685952081,"oECCCIDliC8J":362062870159,"Kv936PuJmS4j":681196009651,"T29T2BJMmtEV":147297244636,"OIgIu7HH8X1h":970002642623,"X040S2znx89n":173664632544,"prkbh9DykZtj":937971478788,"M4R2WDBEUlXY":107955373268,"cvlpWnfivYVg":150155791437,"NoykDZKJ7sLs":680347757300,"LyuDhHhvopaD":618703781999,"DttZlSIc7swm":661588295296,"ymZuQSyHeY7g":402777750307,"ZveIw9LxBdxY":974535475714,"mb3UBpSrzH5E":528684199153,"uOZEwiGVkYSg":182730507702,"7z0lSa6Vtv59":421347792531,"1t3CZnotoHKO":310498151865,"DYYt3QD3yKR5":685780543202,"zeNnEYTdr8K1":320585268206,"Iofaa6ecDJLT":687464896741,"GCMJw2oZu0f7":348488188968,"e8ZW9F6x4Pmv":623436395421,"5NzCUCfsuP8k":605279731003,"FapygILNYRpV":734062299543,"Xh12G4B10PO4":600753839737,"oqAK1L8camiE":615522355237,"S4xVMfvqNVUs":500484912561,"07oaXYDyLgJ1":291152631335,"yTVB4Ny3zuyD":961174695202,"a720tF5QSmpo":526289457042,"jg8DZDRsc3zA":819100768566,"haPhYFUUQOl0":517208061605,"jTC87gsjqcQg":595358673701,"1959Q9YM7QKO":161478106048,"5Y1NkOdx1FM1":858898034623,"ozB3mhBmA1AU":574615256466,"BUCeIvz8eq9k":539648348392,"cK0ZP8uPtrIv":138503374713,"op8viHFlq03V":794772696120,"eqH7MnpKSE42":601617597568,"0XB48lemLl0t":208995528488,"eHH6miZHp4Ey":786348985933,"PThdnL8t7cr0":977525170057,"frJnaAIA7a00":596554815655,"qOu6Q5QlZyFX":438044524712,"1z9fuNnia4mH":373559584210,"yniwJUl1vk9g":201479853297,"ef11t3Qiqv98":172613858055,"0IR0uTghGP8t":872887549113,"F0Zoa28LRGEB":320575884915,"LMbdCsZTq2Jz":722505395700,"UOXvmQQ2h3qP":706128649485,"IfNTc98e3tqh":268057908001,"AbAdINzPhgkg":147443564176,"h0Z2NVpddZvZ":635278180499,"8blYjtlJkjVY":216634637220,"CrFgHnXlDmzR":484123686261,"mQx6Pg4ZdI17":43854735858,"j4ABaK22Nj9Y":876295902759,"X6tljfOZ0icu":516383201996,"uQHuuBYkGD78":476358575170,"tApdUnbQePDs":624057618860,"hRaqiSs1gpem":119514845347,"LaEOMJgNHEtq":371802616093,"hsx9LnaHOwMG":69778742416,"F9Kc7qkvn2jD":708909264089,"WEebhXDuA0T5":345438406402,"8r4pZGUZUW9C":370326748874,"ExkPidQKAZJn":412112267948,"oCDUY2MhDWWP":317038846625,"fsHJmLN8eX9R":9925833421,"vaMZG5MUb2Jm":23921119836,"V9c9ly0TWbP6":602696752566,"VfnnpzVd08Jf":394488048157,"OpKmukeqf7Tj":908554200948,"tI77WVRHhtcV":964834166775,"YuoxtlY2ceTU":117935624585,"cH3lzkxpkVK3":676225462746,"dqIRczMHJW1z":173468354456,"15ojMSwmbBrr":582758816519,"jVlQQwVJDE2h":671213690160,"UHIXkKsui7n2":672452718709,"UvYjceUbeJJW":187302609117,"wkBJAy6jjbps":917527328561,"3Ba772ELlBZT":268040024197,"uZMB7295cCbP":433776384149,"8Pbm5RaVNHCd":231121786576,"Yej3QilcEl2k":774044498659,"7I15z6RgqtDQ":865785279650,"bZJ2eo9RI8xZ":563213028222,"ZEx8DuDF7lp3":896915986795,"w2pYF5h1BoKe":407659159029,"U7ktj3yGLtup":856403352701,"Ht5u0w1GkYbp":406349794621,"MZbywFukKLsS":954957160141,"InNr3Gp0z7mG":796759094670,"CLYS4UsiVNKB":185180189081,"2rLzVkB9xEpl":440910136306,"L5QdqO76LG5O":638190073680,"enWqkqVvxjHA":104328676904,"qOSi4oZn988N":128598660681,"jwJtnYvjWFZE":184739763899,"BKRsdIUyaqa1":563178792653,"dw08Rw3tAbU3":965374554743,"D08JJ76KuVSR":698012164319,"YWyEbe1uAPx8":605608830694,"Li1LS1gdIr6s":937789496351,"exmqH3wn4tXq":396450036498,"rz7W40grqiF0":497166967624,"7HGb5F3fXyW0":316932238919,"BPSqLF1f59mm":464753733081,"28RbjgCZJdhd":692298575230,"6b0ys28v2g1y":146000483873,"YdzBVMDNEK4C":486816273431,"49utEYtTzgG7":23967007277,"u8CHNVmtUPdv":373614508408,"gjfloDOl5wHS":537135517030,"GaRIqms4tV2r":298765520824,"jJdTtAKFzZkE":176631421705,"nxPdodPuq9pe":150791208778,"aIobc0YGEXuC":358147397702,"AQKCwCJPQmTX":837719009415,"MYONKUou3qpZ":730123639838,"ZFwjdEohR1kN":648358746751,"adQiUm2Idt4T":554038519469,"uuC3uqYZtuNt":228028214330,"EdDTl0fXoF9J":868973575297,"LXDHrtmyBKdv":473873843442,"z34Wvqv7gnO9":854414350554,"8lp9yJtwyk2u":300472231059,"oCoVuvGOsgG5":669270638503,"uVs61i8rSUUg":844164482677,"36c04KGJqvfu":355651814070,"boDaSjejjcrA":972315360912,"degM7y8Pflvi":569384130683,"EFWWETYaeLaC":658615117037,"o6rhwzps5Hxf":504436268700,"TrTOOfagO1pw":286310343517,"S0E6n4dHkX39":495790340688,"xQwv5IqwV9tl":231500877888,"0PApcmsUCI23":977945865877,"G6DgNhxN5n0b":442198399978,"Ze51pNCjXMrl":89612181736,"Hob9uq6Zjz6o":2968261738,"IbtWj4BPWTKd":453949196061,"Aar0Fr9XCSOX":960971816202,"xuFa7nVgEsZC":490623135149,"rAcnSPBZlmbe":116811239073,"ErVJdMxCVeBP":527276194250,"ZuGy9H8HKGOu":473433279116,"4izRIZ78akKf":849927021132,"1FqC1eSD8vk4":255790958890,"6jT1tOpg3rnO":670751933066,"BkiAQlgdqU39":203338844089,"HNKjI24lXxMV":728129471996,"skDdOZI51wjY":212560878242,"wxx7K2rH5vgp":714549891194,"rgDA9d0oJDyK":649077088585,"LMcpez02v0hT":107981336295,"mDYT6UFIVgEd":830728658833,"S7fbJTwebUeM":255288701091,"uhHBMnpqsZjm":234820962498,"LWmkCeasZ2MU":402211778132,"E8EfXqw122at":981650422965,"NNP0s7SwyXsE":938987885984,"30IG0cyMULbW":711628763325,"quk20S5zkbnB":75109591894,"LB2B64vMCOwR":524231647972,"rs3hdxSYoyV9":128114746606,"n1OiioP0ITbk":146983076970,"Ud64QlvbR3cP":529508524766,"D4eIFC9vL9Yg":355376139929,"wGI2i06HVazb":514649620766,"Z85Mcyyvbeo6":697034795216,"HzrKyuoyEuA2":820214224073,"QKU5kR2ObDza":299900359869,"egMBn08KNim7":87486929453,"bQmsfalS9or8":147713896699,"ti3PZhYeL8Yb":655344378329,"iLPMT8ieWzhc":700206555590,"inkL98uSnCmd":905929645207,"pMhPKAEOMxeE":156633615259,"p3LLfsxuGx4q":987316682150,"a2sC6lIxGfaq":122163398105,"jdvp9eJCNtzs":853609733065,"GfhuKWmFKc6a":788945208943,"9aH0xgjExnkO":930050873992,"QEuXL0PGcJGG":195253287255,"oycGJpUnqiEg":873035633607,"yl5zVNSzfNRU":705879598884,"jKzO9WA8TZbB":598645404441,"svOHkvYdJxGl":862673180639,"HuFnKEvoB3rU":175532986153,"xGdObOfnytb0":875249290680,"IEuNj5IAslKL":290149808049,"stW8GfjZYh6G":177056253032,"gZYSjN6ikaiq":608581292705,"tvCuQnfUNDjH":945552565381,"iprwk8ONMXzn":10595016556,"hrKRnWyxY56e":777056702930,"emSCQYHZIfNB":483788860842,"hz0NoDym1Tkd":149250195396,"055lg7cYTHd5":745078893326,"PAldKnnTjQMt":76185388587,"Ldwcd203vbLi":101306055249,"mWX16kABLgOv":145109601325,"LmOjmOK045W9":833503499218,"6x0LdDFb2mZD":868841241848,"hGVLABDSscgS":227231125385,"QnvxZsN8M2lf":37528367295,"9npwB7RebdAZ":504621291587,"0L32AYLdhEOO":672924333557,"NObmaG2rIxAS":297679297228,"3gDUE1aB27qt":881028508238,"7mY6TOsW9eRO":584048639691,"SthOWA73XCfz":119850297843,"cxSGPHEFlF8y":341031126848,"79CGmKl1Li1i":378316067583,"Pa2xPiMDV2rA":528460730916,"Npbeqt2ozBVg":921917617813,"F2rjRzKEUZxr":198873049823,"LHTm1LG0Si9H":137571402895,"YSzKUUa7Z7cN":812056788733,"hD8mj195sLm7":846851401469,"YVnWMrqRAMeG":138598412122,"wK9fUUMr7lHA":95735095082,"DtiAp3kS7GtQ":145750629142,"bnk7Hi272P10":90083454598,"DH0ptJK8VgIK":395796077854,"PWfylChLQYLW":77792051857,"Brd3auVuzQI2":449269025892,"0nxz2GHjBkYf":910487241613,"Uj6a1r7DEVyy":429422297525,"Z9wgUsvOw8SS":345612525127,"llldD2nm47H4":518649697601,"7BpYpqydTPU5":196687589541,"yZ64rauRIpPO":100172757290,"sllHBlAYKjHk":335517298473,"GBpsLN0r8DZl":940023157983,"7n5xEx9zbEUz":703346456390,"Wt4cdK5xqRsa":821209241982,"DTfBTfi9POXq":229622840339,"a69XiO2A3YJ5":21348037667,"m222fkCvL7o7":798184963306,"6FQjODhR8dqU":591706300471,"jBXZpVV6P0Oi":988486478496,"TCcq44zD8DLG":479867059826,"TJZZJH22f4uj":954202442341,"0rFmPk25lcXu":287675395016,"mfPyd5vYwHuo":582603029868,"4nCHienSPSli":32953099991,"e3cZrl6IuaS8":496566125709,"ub40jqSnLqUY":50280987413,"ojeooGmvNx8U":516430041897,"sjqQXDMr81L7":265557087106,"CLWxRrYFAAAx":205409728211,"NCwbncYtUNsv":319580618469,"gXiP6MpChjHI":891271245079,"UAcHtXguV7pd":360043646618,"3rcveDk1JP3H":275885015491,"Uny8zi0AykGM":832884343925,"IkTnVDVujUFY":485135471336,"Pm5u8emtizLU":615370273554,"xl0Y8SRrn00S":920241560628,"cbbEyDx5s6IK":872740099776,"FVKgKFbqSNkr":599324465700,"4cPXiWPoD6xw":834768163835,"hfUIGzRZIP9B":295286748146,"sidDEth4vS2e":256621434594,"wqKK2V2vGARt":136875071969,"IT15FUAXc8oh":177465286554,"KP8qPYImpuNc":973908198465,"jS3Emc0qcKF8":72434327114,"SgbXhV8d7TmD":434891230582,"83J5R164yJkN":956994500698,"o8r09yergNFe":550456516446,"M2oyiobMvOhR":214253754610,"tOc1krKn1I8g":459155754284,"gJsxnXzj8Jtl":130349655070,"3mVXgfDQUQJB":639059423404,"GiI65H3LV0He":698410279455,"ZgUf1Wxf17TS":301760658952,"RbeUcjWFNSBg":260648797220,"P9OKjFCyfUIX":833954738527,"76DtZLvxWZoO":845411314353,"fqFZlFBnLQNl":395629925174,"KFUJlS1aBC8r":674237487418,"yGtXR4V10nrk":896527655214,"cdsAwUdpIw0S":749392367666,"VF2SqGjwx4iU":829027818338,"yxdupDV3LyKp":143303216738,"OwU8idpwePwB":516359451202,"TRRswXqyGpk0":682501256432,"x5ltdTtM6V3N":128171321440,"cluqgfEMVsCR":97659844703,"o6Si8pC4iDdp":162636584726,"YIBlFwhJSLmE":901893557919,"7pjf0ZnG4G78":491794650950,"YP9WlahvSUuj":597410951570,"jr3LQ2jlwF9q":985204318183,"mHXRvV3oDaC1":134527096797,"39ozXnhTADFy":610545367254,"BdD7iJPAvvx9":804147718824,"IKy3vD216JYN":183198223487,"oH8fg9oM9TZN":522172818788,"oSTAfwC2pfEm":240876190475,"EGMZBimrlkSG":369782401397,"F9CPBqDZ0PVO":563968940001,"bkOfzcmfiZ07":330825213870,"c9Jsvd5bzmbB":921518192638,"fLqmvq7vCXyn":959503703839,"ecwtLZgS1DME":890855581687,"iZdkV7201O65":507970243039,"bUXTMKoPDFgE":361140617710,"hbHS7gpLfWD0":843341043076,"mD60hfSBQ5JD":210009216203,"tk6wrctb1ob6":582289392780,"Hpa6WOutlobz":784540017765,"4kvTtHN3woXU":892998330330,"j6vSFbLPobkH":208115371771,"KSeYzJ5AHbm2":813622301924,"kDYSAnqJMvjL":159451885829,"0XGufBLYpRk0":124820572621,"cSPCNYkGEyfj":606822723177,"zxWbbgLMBX7M":389306556328,"6NkcT56iRdTC":228125362205,"9IbQtXilewKt":325568706840,"xd6X5eIDfkts":754794355901,"spUk1CPaVz5N":848349749129,"PDcgbGJ32663":452871591729,"AHVnwWzRosk8":465342140468,"Kk8DxrF59WQR":243756269761,"VuoZl3mAFG1S":977386374135,"eLfsFn8XOXue":317498134956,"ep88mEn0rrU0":547061193679,"Ulp56C1GGVHK":454748565129,"uQYAswfoe1fB":592497436793,"WBwTaFIPWNvO":645729287129,"o0nEUxfUDN3d":394771319228,"QTvU2SEa83PI":979529838342,"ilBV2FzjybGj":437299613466,"3B55tAMBVkHl":52948162224,"YSIeJjnvbyig":516823507849,"Y9u1yQhPLxNx":483512027593,"A33DozSPz9rT":443403365088,"yKXakVJxNM13":400780555456,"vD9zZzVprz1W":936310601571,"Q1H5BfTIt3Xm":499369970244,"69zgSlEekeIO":824340431657,"nzSnsya9lNbK":108316987450,"K2nQh3ypbqQT":806543544763,"VRWtSiCE9gjd":412808556303,"ApU0oHB1YA38":481356999015,"jo2SQmPSLkcz":658516080441,"z23aHfY2avja":628164583381,"ef29LoRzSeL5":410510407584,"SIhiqenWhJNF":323287111083,"Gl6yShKuUeMl":131031850513,"WjFU7DXt1q1v":323704320098,"XSDs6zIGulyf":421896588136,"IJktXvzbvWlv":792984669777,"ozYhKrO4Hvra":608375414421,"TwzluR5MZR3c":417739092649,"f8KjrkZt5vub":604599823028,"y8WNcgwFrUed":710139982304,"5hb0SXjsAMBf":148314668598,"8Mq50HlVd2nq":268860887948,"xW3IvSEXPNOF":407234440567,"0gFL0QiXipEV":257041827413,"yrmox2R4ec8r":643187185417,"PHHYPxa1JI9U":419444500753,"9HmVfkDRYHHj":843984365482,"QIbYdFCw1HtT":538504651475,"ytcKmG0UcX3d":323334117576,"h1aNUas8qsD1":592020670062,"HFqMcy82sX8g":834026561146,"l1RYCUrsGhbv":654559797319,"qIomKiKBiNqy":148596820988,"lLTcxH2uTcLW":609839565906,"e2VXvKJ0doeJ":864165514511,"nqKYJEYJV6ga":756344107330,"xA9dx87cQMO3":852361374314,"mTTSR2iTRUuc":85760502550,"YjNYC39ujhXi":434433794861,"29y8eQAnTH55":628257118245,"QQCUzqDRZkvj":835344818671,"WSOxMyfHW9wV":832096957748,"b6BRU8xfFmfh":44600469722,"IgLRzmiNWgnl":99485274917,"U7WdkOCUvyP1":64613469713,"M6deHYSMy1M4":502407387787,"rVojzP3pw3yS":53362928888,"noDzKmzthYgo":225574925357,"LqJgxxgNhvXo":451688622192,"wlxmCrkhsCOS":885643488326,"exKx5TfgSrSE":837063021707,"R4VO2YdGDdkA":456142652109,"1aHvCJf4tbUg":392034985300,"DLkuOe19cBxk":875666691422,"SPG97YyrhOda":705972596868,"fzrDGml0beOG":700826632287,"zlmPFHl0N0we":434753667058,"peTXm3r6e4pV":432060545245,"8F3sGkt5TGAd":612022102110,"vFmGlisoFWmZ":578327953123,"6AypVM2mPxwb":946812130146,"N6Tx7yPkDhav":172215672276,"yq4NtwQGuYEH":722915208964,"Oj6w2vBTqS7W":695274105039,"gTUfKCGWRCMB":863213477537,"KPS60KmKt4ZO":212840661540,"X3ulkdy94UD9":65699569764,"6sIJbktnigX4":239927223365,"RTrvwtOoDQvY":402829630624,"wxRS23YBDAM4":265324710452,"63mx18fMlVZA":404291180320,"4MVExWZuU3Qh":21401044521,"52aFjyFtomZX":87186278255,"vtXSXGc6mA0X":708613051160,"u8YXRZZXckKO":335148535530,"qNQahRX3JYu8":530881692632,"wOZSoPKmUW5a":820560396448,"ePm57OFn8WCC":296751956368,"JCJuYcmIiBFi":601073389277,"E7AQMXcs61TM":980909989460,"o6FOuIQCmNpj":511534066141,"U4TR5k760HnE":25788571425,"6KmjE3hCR71z":646209438322,"PZZgZHR6A8dO":510614412971,"vFnPSaGnCjtX":920917281692,"yDCtGzu4uYfd":128239932692,"rqckBUWqiEcT":950396956332,"mynEg7XTGpTW":125805904769,"154vMSa3MrtI":447257365943,"hGWnWSPc5IlT":858383634996,"58MKALgmKqAE":983882313553,"Xgowykqhb7jP":227479786421,"90mIQ32ZYayW":867313893386,"vLA0mdFhjguf":633514757038,"uXIICuuAA59s":83212442369,"VAIj7Xv4Xh3z":671870005595,"rsxUKZA9pxpF":137641894496,"ovB0HQN9kRt5":485192222782,"55CX4qLdFFr8":950258115239,"SCamqSyJpGoB":942935355949,"S1vYlngbGMqY":748034691244,"PBSKKfNc3Adw":283176930688,"0Gb6CMEdaQgf":916192673490,"BnOZZHQA0PpC":244522542721,"pwahnvhDEE6m":885102737333,"M9Y9uRWrcLbZ":515783255884,"HbBUtQ7TsJOx":396195924179,"R6xN9uBV1XCH":456737765023,"AHX8oXMZnCXo":314069325627,"h9r1FPX37MFD":311819471472,"3wGsUZqv03LF":171454479549,"GU9jq943oT01":532533167733,"eP0ox0RzDE7R":503190414645,"PwCvFydqn5jB":114748447549,"Li1dD7Fv2Mc3":458627923614,"HCgwv4Ec8wty":368642426171,"WrizK1GV3spx":117325412514,"Pu9xCiFID0FZ":696796659935,"ggBmQas1QY8E":714922364195,"mJfRLRqH0gPl":923550822712,"Xp4wxGGuenHn":336005566178,"Dkemkfl7YEX4":368060104645,"aAu4QeGamhgK":547991350706,"KOwsGgDRugbc":787505154397,"cPYTiCZWqFB1":982706731639,"S7nm2xWVfJnC":436505974342,"NBC5Fva8aaQz":215061374666,"276fmauMOFeQ":883110270429,"IpjTmCXNFTf3":766210434332,"5n7EbGUGTG9h":148673948897,"csUX2fvPZdGd":791039304528,"d31KShMemopA":816863322122,"5uSZbNjwkYSo":413794542737,"s2qH3BQhVqLk":384230342284,"94H6ZCCcF5KX":76852113532,"UgkvLsumTFtT":342159715783,"ofdss8O5huVp":934152673278,"8nQ9xKlE8qhb":600552603044,"9O5unD3IKCW1":670107667868,"eElvNYJ0EKfp":819480139976,"iHeWKS3tIM4F":676835120996,"fwbmDL9DQTaL":699748283728,"of0dYjIIqp40":836740316711,"rxRoC1NGCpq4":605760982345,"shNXD39ZFWuS":873578391452,"w2DQKshsrh3S":157687073474,"PYUwBwvXBfEq":720027344213,"Ekr5DLlNVijZ":780152564153,"gK53ddxqhiX7":611836734894,"HVcByPgpwwhr":417092373224,"xBcbTu51CkMW":398787225069,"3AUhirw7WVqK":321520317463,"DWtDmgICgIIU":787816199750,"keoF7QovQnJf":761723675591,"9SdnpXNQJzYP":41653863938,"EfeZNxqZsiNa":435891434089,"QTOXeMJlLnpJ":321502117159,"46n8tBadZXyS":501004796278,"nxKQn98BlBQu":477785720201,"WnnW3t6tuegz":714695695302,"orXbvenckRqw":897744211018,"O23z5pWSSLvl":809164556422,"Mpk13hEPyHkL":94540541271,"aZJgjdcZpl0i":815728260683,"I8qFFjphpikQ":795087769574,"fqlni0G0FnFa":253721667870,"3GV6HPXPBHS3":830555412175,"84w5yMaq2zxj":296189829637,"Zmv6cZw7HnBd":379587527527,"5EO7IQ4lnFoH":314625076020,"fh3ZntfSu79A":139875628455,"8Ckz6ouXDwUj":926820391391,"qLElzUqKloJU":292424477297,"plMz3r2OlkKH":177514456305,"xcpLk3VJmGYx":155141284402,"x8QkdfZa0crP":157526130815,"tTGGYmIr9Im8":777762834332,"xEFFxvsZjDTh":912666338243,"qIDAQRYOPCeW":730407865253,"dxxJoDgN9quN":20458960997,"3dNPSBWBw4vu":693269306450,"vZZD8ITPIeX3":675051163027,"1PyOAbpGEVfJ":765647485959,"ZC4zSb1TpyNX":152681912876,"dy0zIbB0e30Z":609508327732,"8nezLUGEim0Q":501416009779,"bG6Zrl1phTaq":593638445469,"prFiZ9DtaYlE":362040732160,"Z0xXYMW2TK70":765361215349,"xXAYcabWVrBD":124674477008,"Rp07D9dlXOAv":56805629425,"IaC6atChjXKP":199313817573,"9qmzlsqkHZLM":955566465180,"ZcyWYPhNcKBE":680495797982,"eAgT2GJQ2ac3":196672062340,"Q7zikkrtOp5g":502717127137,"hwl9nHMtKEBg":549948543791,"1y2GpkmK2zGK":599675316688,"pwQ00L3EZHZG":710581200797,"9am4cXEqzh3n":919507202171,"Qvz761ATGSVv":435205244342,"W8gQnOsHMyDo":525251835539,"TVIXT0zNiRZP":896996224283,"kMiKsUCNmSrU":721494457608,"7z7FwI7Vg3Yc":993631202384,"KAykuAfmFkyR":621090615463,"AylcBZlmhA9Y":322231182794,"iM0V6OzylkvA":933353301449,"Oxs2xfxnsK0B":842309134292,"17OXPjb4Z3Mi":799594141658,"jMLaIaPVrFVw":440257092304,"7jnn4bqYdSa3":7207745888,"BtjUhL1JburH":338688828427,"t0IburDODlEL":592687433511,"B72Se82TY6ga":76252096367,"p66d2rxpDR6H":568900095882,"5thlWa9adOxi":401860159909,"jYG3sVRiuo1c":50876732542,"QE3WIqnXCHHG":768569945728,"jGwa4WSbbL6C":613065534155,"aiafA7EbCKDF":299163324363,"lXtCX0s7zy86":487449350339,"IhywUsGzPviK":74202837856,"an6kcoQMNAd6":313722572294,"lD3uzFul81eA":90989034126,"2zNbwuh0DkDt":200031479650,"5p598Gxzc6ru":864285712571,"gS4mFjrO39Ln":687063588414,"qNiH9AUBQPuy":816251676018,"j9FVwsQgA9sk":863420610637,"X1TgMnAkIMcL":939868779512,"Zn5LXIHMXF21":348617344891,"K680V1KLY1Hq":806794803525,"ySjkCEgvnYpt":183179897067,"4Fx6SFugx0OW":710232201270,"EXMN6KTVDSuv":758221981854,"GuDFdXnqHfXi":687701992522,"mUI40dO97iRh":186242597987,"SYZq4b68AW7r":572981433950,"8F2gg6Bp8RvS":683567215890,"rzM6fKTAWMBH":692279904379,"qWPIOJKa9RiC":713435377646,"dKVncUfyPY1y":102854443125,"lmLfsOAFYQgT":393013253921,"eINgZCbzjjxL":325999145448,"hBj1TwTVRJIt":453601636328,"SJ5PrxljfcQo":472360782792,"VgK8bcrvqXix":515771582471,"97W5EithfBiA":218145455424,"wWIofSXQG0gS":333132094415,"n5O9OYlVCZ12":897482240446,"QP7AD6S5Ipc0":768945099121,"PO9tTYkL7CfG":574030306431,"XP3teK7YNC4Y":504378741878,"pOgkJMrC4doD":220895666748,"6eFbI8Cig2NY":39367407781,"Ydy8L4erUhzT":677215253898,"qEw2VU0nfcqp":503924786637,"GrgVdQ2SAOHq":24339046135,"KEHTmYbvXk8Q":780008832403,"ZLLs27Bo4UpA":73970500955,"lWRJzzUeHqXa":406030065435,"Jj4jgAwp8p8E":510779454130,"5eadskxDFIwk":146734926838,"BcrbAQBb0MRn":644451783048,"XosJ7cGWpRWG":160977134206,"rUaRle3RzhlC":291555220220,"xSdCJTsVOxIi":80811393246,"oT2Z7rDqi7b8":889664122102,"Kyx0GUMQIYXd":642292138146,"5Bp8oComarvB":803398788552,"rgn5vlQk1qUD":722778009613,"skoLn1xDOJPe":670191678122,"MuFfngBKHbYd":726485348444,"JWNDeWLaol7p":119874826905,"3tDDlJPtzepk":269725749291,"uFhUMB7Cqj03":542319766541,"0FNucwN5F3a2":309560600125,"lks2iFbtkylP":808881975351,"ucItnhkJTh97":813481039935,"gm4pNKBX215m":277311156799,"IbPQBvbbczpf":785495003599,"CTqHQdrjz2Lh":74163395443,"4o1ebbkaUPLY":271740534753,"4tBQYHaWPn76":498808663309,"yA0qK9gNR8e9":62102139171,"o8Fv8OaB8GWv":420475044218,"livVOkdWV3Ra":888377654072,"LTtvk7NkqYJx":196871883309,"w5hbCrAPyQ7H":379238903191,"UVrHWl7hjkts":86662218231,"eG4BBopCL5RW":71496444065,"PSeGd10cxCbL":944214022148,"yD1voF0jtKlh":711066139884,"WTyZOEAjyEY1":325402496132,"Vhmg1wyexJWI":887998695107,"hIKS6k64mAIK":724460436602,"hbb2MC01TwAX":859453674387,"ytvXbUxVHm5S":362957231880,"vlZr28f5hfzu":467588511751,"D73zW93NMZ5Z":482347000668,"8f5vUncaj50N":442868181563,"w1tiC54WxMLU":423476478357,"wjp3Cifo3D6q":273346103446,"rwRq1YPXpL4F":692423211272,"XaVQ6QmngYWS":621080269049,"AqXhfOI1KMwm":509111842852,"1pQPQxrMeaHv":390644339653,"rfF7F2WPXcl9":47945868876,"TtMABp51MExI":914294288365,"mxqR5uHzISjq":852772513625,"kMH8HLg4WFTl":415660073145,"5UVL0WRFaKbQ":434530732185,"nmkI1uCaLrkx":313805979141,"W0vnQN1XdPRZ":564638715911,"KIRzJ4nxxn7J":231106650668,"9JCPcQ0UHTDp":183692566889,"sdEkCAA1IMXh":753185511712,"i8Z1IysoJBAi":55637549916,"q7ulifGzQhgO":259036399226,"0Nl6tx4pPGy9":782191695378,"K4OLvzmk1oNw":107344736234,"jkvBBZvS1qhU":525469655116,"bCKYvCvgV3qi":223364083021,"hyEj3iVWzmyI":462621459299,"BmQxwqbgZnFL":7282992049,"L6D9QfsQSQOd":253765157180,"qAO6bTdy7koR":824692541619,"fGqvKOnEdWNw":19191824836,"Rty4D49UbkBb":178118231411,"6wcbzdChLDAl":482367155341,"eITxtU8YuKve":459155198200,"NuR5vc6kyGAI":491861042339,"NZ2NGakSfvO0":653226783816,"4YUQLdyKy52b":536615642500,"tP2aq7inySOU":792020273642,"NAnpaTvWzdOW":209202457987,"Xb6PyCh9KHoO":145563723301,"tfZJvos4dHzT":810669841612,"lRmsfPWpjv9F":474079975593,"weFLHcmzb8IT":706107769385,"LYbBSLCC5Pvs":285466263535,"cTg1OAhUll0u":298567514638,"nJmEwD8tPMd3":118058346452,"DWeqc9amExOo":563293024094,"e67StpE5AHvh":614402362448,"u6UrHyCf614u":291649332420,"afF9zMubmXgX":239362107827,"Ghlntz0MtGuZ":990580536090,"J5ueendDk6dy":584980495107,"3Tm6Ypc5UEQe":5658459841,"k8aLFujDLWzt":467952413097,"SCo9hKp3aFfl":17198491148,"3LYooo1gkfup":819270349561,"NDj0eGN0Pt1i":587199625024,"I9jaxQx17w9g":774531202792,"kzMXv80vKK7e":486686729692,"2deTDtlKSAdF":403350592761,"QDU09uZkIETe":554683520621,"IAZ0w1bteuJw":514446009435,"6ke1b6QiFgNU":91103997401,"dwL1miXXyoTd":119671141149,"HvduzNXVgcRr":302550253411,"H4HaQLq6LKTF":392114772907,"brm922A8Bw8Z":648333216056,"QSfB5Mfu1y9k":217202312110,"oBLTPX8nz9zZ":340661603467,"d0EqiGFKp1mW":721477439786,"1BuStY9ZdQs4":271122823174,"yjFpWcMuCpUW":553537530367,"cSQanXats42E":352873048030,"mvPP3asOr4so":725788170001,"FGseLrc5TVI5":97965477429,"hAi8kbbvfzye":463807798094,"SPr48oZm5KBA":762537303356,"N5jFMCxNV61h":963576142339,"9HQCdLbC6jp2":42679400590,"hnrXzl4po9YQ":766508460604,"L1n37QbmOJu2":792879570824,"1hdrUkQ5YzHl":485471461359,"G6XuTYxxp5ey":96652062376,"xcBup5XPIJWR":725091428380,"96w3GDy34FIr":411428830092,"3fW4hxOlgnbT":232305153086,"OKNVHQz9gneC":838283597800,"k6er4Z4tBE8X":856577960145,"hyOPDtZeLoFj":771469593757,"20rC44qKhism":354081491884,"D6jNxWxFeIF0":270603962171,"omKsJLHAWYsN":704166352303,"DN9j1YatYZIU":278084303516,"rko6DR25coqg":826167277826,"7AuFSqK6IpSH":993139315423,"xrO0VYruGge5":933445672996,"paSyoScSs1Qz":127610094405,"gd6Fj9nrSs07":734716797201,"JziADx9HnFRQ":314648872301,"LOAUqxOj4Lmt":925129639919,"Lu3Mfddvm4ed":827399965095,"6wOzVWM2pXzO":491514203476,"yNfYtkStaqaC":773214425933,"VTK6nCCxDxO3":527499925912,"APxAdsOLRZh4":200822455857,"qTcBZXqNuHrt":297203378990,"joKs02XK43Ze":256730467622,"2984ax6xMD5s":981315034936,"EQdPpWaU63NT":669366634490,"TB4AecwQNu7m":934844151383,"DTVWthKTgMdN":127869823987,"HC5aFvClfxyE":852194617023,"YSNTdGbIOmES":364430010814,"xIZtI44GwaV8":764438305502,"ndL9ySU9H6NS":628782094108,"ws6OzDnrMHfR":457398954221,"K1vQlYVa31Tj":213492524905,"AWY90e9ZLWYp":519805374746,"QKif6SoNlGY3":225015705445,"xsLJDGii4rXZ":997714295306,"YXd4mQfvnFDq":724118563047,"kjo2V9CVh3ol":794918541540,"7geEKmUolPDT":284291580692,"wjUDG6BNgdu7":703010034164,"XdMEgqtRu0mp":293320370237,"uH2DoOezXmWA":284306171453,"7Jie2OhLiFoX":261670839121,"XaddE5sCWwDW":365829623158,"iAb0Gclymr7X":590155823794,"n3IxSZdiMPo2":293682104536,"fHc3rxYJY3YZ":698692297720,"73QWc05A5CDJ":984258190964,"07UHN5Rceb65":69222206163,"Xk6Su9rjpTLM":54757428309,"hO4Q3Wvhvzo6":66506428237,"YB4GTiFzdS9X":998823545319,"0kbitzPL5T7X":322594502500,"Icm40bFGEQJV":124557615039,"MUAX8SfzNCMr":660724576462,"pW2fo7g3JIMS":268484410061,"o5Ft9QcBaAd2":947208870306,"XZaJomsgL5ow":173177660042,"QNcwpk08JKs8":754988494219,"OXOJNTESySp7":300112045309,"10xAfHtp2hZh":677561553765,"ZnGDMGTQx09J":244648945004,"DxHZs00thuoT":471081572734,"8xCWn8MRZxSr":821856193757,"Wn5BETvZqS5D":480572402261,"xXRirjbPOn2X":650154987532,"lqGt2sAY1DPU":275363957112,"r54lyFJUtxKV":653047898853,"MRDhOVZLm0fK":66550500017,"ibDn6D5OKp0h":512856574158,"BN6sfDctaBzZ":250358860072,"FmesmnVV0hVJ":69931484909,"bBJFxGhh2PZn":417872463544,"9KlFWzBfwkMO":391273562731,"8oMi0LiJIbYx":394422742085,"m33D6v8o9TiB":31419921573,"fASkcPH9BiRl":335480124795,"ozw814wq98rE":613856981060,"ooa92a5CBqy8":568207349868,"0kZoJaCpUmlG":626498955158,"aWagf6i21Afr":467246736698,"OY3w0xhuIjZm":541983269436,"zMf7d1wnCTZY":503790857308,"Fhck9otqIisk":265005903605,"8quKykkaTDFh":788611785741,"qyHlN670LjU7":433427621004,"ywMuNiDKN13b":575218004390,"ADiTsX0bOyKz":124638249572,"UQm0S56Wj1DV":34180421511,"BEVTqH697sou":137801907707,"AqAf3h8R5j8M":34457797324,"7fZvBn4eQfL2":348358626737,"IlX17L15g4cg":451829649268,"rUSFNGAg46KZ":810775397830,"rvpLvBEsUh05":485161292258,"xx5wVTH41D03":427211305267,"cY5z4PJSDMEA":716822654540,"YVuLbEVTuv29":305616451886,"Lbmz4yJOFXKA":538573545541,"Eya8WcSTtPjm":679664742635,"8ZN1BcjHL8zw":838233737552,"BOpE8GZxHcLF":442179554996,"r9H10xEACff7":97844122679,"e47ABRfVN9TB":539912864614,"plJH1JFi43Z0":635511227215,"Rwal0OSQ8NbU":369129497882,"F7yfenw5GTNX":997154785715,"qe8LUMoETMrG":753218532391,"hfckf1yxUfMq":450646868322,"znEowajltCPW":543424386661,"0B4IU5Ug7WGy":509541030845,"Nde8LgZOdZqB":34676505940,"AWOzL46xtj6H":942418747043,"J7chENMLhcSu":801754050027,"9Yej4oiPcWXm":535835497422,"1x6FiJrkzSYv":318110688044,"NuQskNhrF39P":370424858924,"SRc6GqlQ3UJw":926820583939,"bwJTcLkRJsjV":940061946678,"Z1qv1Azwipng":936572869277,"apGWMy01zPSr":558132043580,"LMawUpvE1zzA":8917692163,"kADs8k51khrl":277111076582,"knD1GMP7nACm":111821528566,"tJuv0n7FsKoo":773633370756,"dS6c4GWeg8Wc":441722479436,"7sewHuFsDmbS":835453035614,"zFA00t3WfLs2":633262025216,"xmBHyVhh5y6j":140507458209,"YVWOErXPYeBd":512807906410,"st666xLTKcD4":145129516222,"vWbDp3b8ER2u":452816673798,"NjWFts9toRHm":216549870790,"q3JFqUndQjpI":336813678127,"edxyvmY6BRy2":850354595910,"8QO26L8Ywyb2":662057185403,"4fQ1sLYtYXcD":11116259358,"zsHmLZNSdqLr":13882403928,"qcnzF1ZRlyDq":490894413888,"oTM3BLHFxhTl":696707402136,"P5jhNadulNlF":621445571313,"blBTFRqHEem1":327140349235,"Pz74n6g05eHQ":861953603265,"WbbJhh6bZZKM":572618791441,"xofeDweQBLV2":35870049565,"LLDyrZiPejHS":867792779149,"c6wDhgaL6mle":972770647474,"IAsPsyVQUmEG":307708684214,"FHyKTW7A8wv4":701620168238,"HjZAfNsup4Sr":227855426434,"Z4sAa87JeKTe":830389105108,"U1NTlk70OsrC":623955093471,"den59wsxyZQs":879555721544,"3eguZArCkPq9":505183683186,"JBNtjg1lZLFa":151040881276,"fpHSJpD0d80J":576954889906,"0uzbciUtGgMh":638612425548,"jlqaNxLCxt7s":21043443236,"8TlQSw333s2e":163836854991,"HUxnBtjSE79C":589877526740,"H70wXracEONw":36551639169,"MDT70fh4vI2C":883226639316,"xrPtfvsvjgyI":622787829135,"vVvs2DkrBWzN":548316090768,"M9VBGWCq3i4D":393650109258,"WOqii5RE2dzx":534851039905,"kDRKWvStkXM5":144268868816,"MhTumRIBLQlB":440808537695,"CplszakHxzot":293305425835,"L4hYi14XA6p2":300902913639,"Xr5fDAV1MfIq":648960686566,"n4SJkPm6KTn8":515382236595,"TiEjVFruCNIU":171081876114,"Sk063TnfwRGq":646891175224,"EhDD8iv8tLYg":575347550619,"U0SWR8fMlbXj":203806333608,"jnTSHcfDJYb0":767041237339,"BgAjgoEvAQGm":338549087358,"ZwVdGIsz7Ezp":965214470386,"6M5eoo650E57":639689323274,"lffqFBchPTzT":827692625423,"3ibRLJTmaz7d":135131160851,"AtVS5MYEYFVo":930930816542,"u2j0WX8jN318":379432882879,"vwOwjfa7PMOR":127608011502,"j4krDdCrr8XR":3450494067,"74AcZBmUlUqK":775224387493,"vdMG41BnOWbt":166941073130,"orzm2Vz5YQow":437967862732,"LVqHlTU6vsDi":822092948586,"52AK75x1aUe6":775194956597,"BiPNSrwQfUvT":999525707982,"bNBDU3CSe1s3":625307254644,"EOtMKF9MJcG3":894334960413,"wbWtPtI8MU6N":876761073593,"LTucqe5G4ebc":213641033196,"5vnTqPXAV9ZH":793685298189,"KzH6MoeLhKqK":800566900040,"n4KaKA2ZUGA7":468293372535,"u4ANCBDf9aJd":877711773379,"jLgswP5ebBnO":424426472693,"qgvJW6X2sYJP":445782998867,"webpD9NGBP6v":772197649102,"9L9tLibgU7aV":266666638446,"X2KtFEBqvgeX":48382160138,"kunMZej18UYN":29884345671,"bxyBCcVXpd2h":573751416233,"ZdcWtA8cu0Cz":353090438439,"y6flhe37xZnX":858736213361,"7ogTTZrhzX7Y":152581695571,"fqW454jtxDTv":844724800940,"7afhlrTSos5B":670212690850,"1CIfKlYB6b63":982777295139,"DKjiFLNaWIch":169086063071,"RNMVIUAvdsMK":888145410369,"RHLqN3RXdz1B":701268253109,"OpaTf4yDs8Dz":554770505258,"qUyt7vkN7ptj":928380417897,"qV16VaNhBlQZ":729864152529,"ocyqu57bTT4T":588210167230,"cDugxCkPzZK3":187518458929,"7sWrjfgdqH2y":793355391308,"Vr7ud62LU9Jy":643968952137,"5mm0ZE6MMVJU":707166173393,"fPNCqNIwNwTn":371152592236,"2Tp1QXT84Y5y":610594111775,"ZOLlC1qNscq1":273709643752,"eU5lGRfGpjgF":79386718273,"DyezC0fbcx4y":95105750847,"U5UseBmS4pn9":94455702082,"uOmEAPxX2N7L":697183565169,"JVfWpyPShEdC":91130269482,"nkXxxbKQ7gM1":49157378496,"wE2BdVPNgjHD":239269614333,"V62Y9Z8gXFuT":307341208806,"LgZFXaZxVJcI":726643634195,"BHJluyLJvjr3":873672561131,"Mp2f346Hifdg":626893496411,"4tVdaNbmsg7U":47769764155,"N6DKd9TZa6X9":161584768027,"5hXzIrtJpUUR":922678259671,"lUlhTRISGpVo":290765515317,"lMLPTG1oDkHS":798289104825,"ANVhp34uzFft":700760236625,"PqosakLKwOAl":902126807437,"Ocjnj8dlIcYW":469920590225,"qOdxbgFPY9VC":392619030592,"eRCiOtxaayJ1":38727450518,"lmdOfDhSX0pe":26407634907,"4rYtO4wdUkhb":509986051937,"BRIeWPCumuED":638434606749,"1ytiAwY4sbl5":627086448366,"tbDjSDdLAYCd":890083062770,"5QPb87rwolfR":937177423593,"vqo2uX1bkHsZ":11074118377,"DZF78wEuNnP9":615992520593,"bdAVYKrSEnFf":183405877078,"VuTEWoaDTjux":855251164952,"HJEwobcbo8Yp":109204616644,"pjwE6jS9MRO3":37953403844,"NhwrgGizIYQw":592922387537,"lCDiy4wLNbdT":414867355681,"ROguSXyYREXP":713081933999,"pZZYMY60Nw5k":152950372237,"vVkrb7OmfNQZ":867713717664,"gAjlyACeUBTC":964124077979,"oYiseNHkRNOx":916452634501,"86wcqO2VuVaX":100132174015,"YCxVmibaLOGh":725909243819,"kFsnHCvPiN6D":980565618722,"2syvG2lhBKjI":356361118857,"3NgmQkuGVI3i":8109798444,"Xspye6v6Cu4v":397423029196,"aknRCwLKVlxs":552982815691,"uNEV5xcjPjea":700790808692,"hLDK6qqjRgVi":391058592953,"sSkbF8Fqa13G":202362881606,"rHqgWcVbZXjw":142944177167,"eO5qjzO8O76U":393635894842,"SzEL9DVlkRqJ":800051594285,"xB7YRVkN9pSL":356110972398,"zt9pPFBMXCPK":849275651718,"3m2STHHkwCiI":794361842195,"y5hI2KWVmSqV":918200216017,"Cdu82dPlIt1x":681579627039,"LEu3ophgjlWG":156169842150,"e6tKlNhB1Jwn":676474596683,"719aHFXtKSgb":545879617327,"8YMw3fnSAS3i":884415684965,"BdAnBlPPoSJB":43661905756,"4Y3lWDT6YKJh":70254414868,"uedRdNewTjzW":459219412443,"JGSZwK97edpB":213757663790,"bcnawlUOStPp":670892373351,"LjgocCyQnfdX":622719013463,"S0HAXv9JELXr":37674479298,"g9w4TGQH92ay":389256020309,"jbmmHCmo9DXb":477500959680,"s0ZLt9o3qPHO":225685335355,"ZUVUcwD55Fd0":183591958392,"8BA1ef9Xx6lj":583865346953,"N5vPvI6FE92R":694087633623,"6Jwzv7HmNfyM":110407119965,"qLoBZFph6RrQ":951839882334,"31jryGl9bVhd":194890606462,"ldGe5BHAnQai":131532850041,"DtjpXbyxzZce":158703688345,"WwqGIrHwFpEN":491059733359,"QbSWg1ZP6UYO":338919555302,"TfF19pni7dFV":791209629396,"w4yT8lgGXsNV":101929615508,"nvNZL1Hybo7o":573424307701,"A7uDwc6CoVOB":620416850391,"cqSKBLOiPjSR":827076668159,"5S1Bg5QOIaDK":599474009191,"MQex3U6iuanH":754698826316,"gauQ6H7xfHdD":638523475125,"Tvuak3vFgSd8":741922728942,"nKVvdEzyOPsj":876967888591,"UKBdp0C4QaNn":703961600661,"9iuOYU21UY0S":171733232890,"kqFCoKrkRABJ":272265174996,"NEMGEgdvMqNy":676711508185,"uISSJWnUCysR":622693122787,"vYt7pSu58Piv":662380217777,"Blmiko7RK7G4":87635992801,"cV7Ly9q13yrb":763239455131,"uKocEMupK7FU":808434189377,"hgxZIOl99Kct":729365764548,"pmSkybEwvZgY":571925170768,"dEyK3zjhzjui":398164663554,"t6fH3sKExcEi":23546161068,"vZhR8BubporL":561153817688,"QvfjpfCs7Bhv":304987882175,"WITJylPfPz2G":301093177,"9gcTGv4KfZiy":707181743180,"hiqdx3s51IGB":808460401689,"ApdLTC9Mqv1H":542634184436,"K9AM1toYop94":798415696454,"69qLT3wbi3Jj":381460383820,"7nPfeaNw8TUU":978091754790,"FMxAHkDwf80W":713370484813,"e6t9gVkVKl1w":464648103569,"vpzKDYGr3Oo9":833053183204,"IYl9HAwPrQRk":636796965314,"lGmHadQlaBUa":451437044466,"6Gan9umDKw28":840279255715,"krpIbD8IrCGN":286775661410,"f99BK3YBFqYN":259594735272,"APG59wM7tudB":385912906026,"EGwIrQCn1QM2":688899573338,"vPhNuplc5xX0":265518261466,"k8dbrlrJSn1n":474344848691,"YRjmfWsfuPmr":595817431075,"3vRxwrFu8bN1":494689025605,"BPue2EjBSYdv":23663541539,"Dsg7D2hogOty":271997168446,"PNjT3MssQZ44":321166333387,"a5O2jrlyCdWS":807254849908,"z1usG4QeCmwf":791800259041,"efATMi65BU1L":290956310595,"0NyDJf77T2jL":654995196245,"nnXRRm2ARpup":789945481568,"9OEsCTrYEFkr":43775110079,"c7UAQ7kzUf9l":45316229579,"0e07dID3vWgV":812942195695,"x1iGuhhNyo5k":154851634640,"Bh108ayANqg3":299206645580,"wVoKGg0LkeFs":741731866515,"m5lFFPbblqJf":929910521577,"3YyjQfZWFW63":221396367731,"HUAljVYwsnfq":711830692924,"fISgdkeRm8Cn":585651057233,"deRDVLrWTHtm":802019823825,"k2pN28NxUDud":649990111509,"uPFeIgAc0hXg":918802325400,"aExqIbGWqZfy":307652399389,"j6LeEDuf9Nit":263872483265,"awMgyBfbLQT7":364575659248,"80qV1f4ksmjZ":544479489282,"FJzQV4uJmS66":793577991978,"IoUtVKaHBuVE":788179734125,"dS6MqR4OFfA0":923730198209,"TJxBuBrfcSue":692550261078,"4bAmHyfaSOnF":144211280977,"SiQfzEMwikla":130591116445,"tcuisAJUpvPd":802172155824,"uCl2rvWlhY99":252045825109,"Ku7cvyG8roYo":186708584734,"Uv5j2jsNBAAG":363501040789,"5gBqGFalx7hN":500867350257,"853hy46fDQmz":845291602503,"GmQ1CLfFQzBL":596218775493,"pFtKC1ltncUo":896816364140,"tiffvuCi1fyS":409172227388,"mtsY1hKGFh6N":452462613922,"81ofHd5ZnPWO":607262140344,"yo4M1Zw9xb1Z":916835817138,"Sm9XPCPz1DIC":987894175444,"HMFcxSeKl84D":946032479050,"ty2mDEi6OxH6":727870574547,"2u8DCJAzhUh1":745091219419,"j94lA7HcZYap":77665671789,"L6kXnwrg3OfE":493387796480,"0PPLI4ydwQeh":734143959873,"iAFTKZ8sQtXb":549833348444,"1ZTC8GoMXWb1":935930762153,"PK6MxKayJFlL":371497622738,"qtzex1qXafn5":638592401030,"oVpb7NHQQH4k":655616195874,"DhRYCZWssI2v":326401117777,"kfkVqPYxgD3l":185717923282,"FGKHA6s8qYwx":220549438041,"CLC46XxckrCK":663511826783,"SkCXxZmFDjkf":327785315343,"tgVPdVaITvEP":654859665922,"0OwFz4mKuDfn":146764624918,"A1Pmdsm51tWj":619013463209,"jb2EezEq46nW":23044431497,"QBZ7AP1hTqHm":397098665726,"iFGNQAhuVX3i":618265458086,"9ERrPkPj680F":799554961011,"H1MGbsXiXLw1":101392166434,"cDsRyhjvME3K":430892154226,"ytYja7MQ0ZaE":34343597578,"UN6yA4JOBEuZ":7419907349,"IJUGcA3v5jCw":643122023134,"YBCB2suyI8xd":856662087140,"h8Rua6izNTHT":263797096784,"LefMIv4qR8T8":646187023597,"LeEr1EEYBXqW":527431632878,"xMOnv3i49ZLW":577562885072,"zSByACzu4fGR":28269910843,"3Mt5wtzm0GOC":671434493528,"2RNjPvRebzxF":579272090717,"eF8PTHdoxvGy":246277275171,"MYWXDe3kHHIq":960315134463,"6jG4DsrgzHzR":339157342801,"bwZgnMqS8Sf4":148518175488,"PAv7uPK22NRp":391673854709,"tSzWo6Idai3X":368758058367,"0VBstvxj4UPs":795184295214,"GFbhyKxTWftE":361509578839,"hDzF5hCuW7Gh":737798426271,"sjE1Gzn9qbLx":143516850931,"VBS178wboMyy":29420712853,"QeZA7ryHkjcs":87783405431,"1OROC0voFAX5":350332856823,"ZL0RW9EHFjoO":681226728258,"EfpBzH1SCSOZ":584279037741,"26mFrWdLlCUY":102413900702,"zh6lkjYJzhtf":483877411022,"xrKszRJ22iUB":852097971860,"Hptp2InXPS0u":15706767155,"qiY9FTyCHt2L":652420164545,"vDArl8jK0KZF":411436447261,"to3dQzK99YKg":491926553704,"nFmNVD9D7woL":746058232149,"85EnD9CeSjxX":554528486450,"2q5JafqsOVP1":967069551890,"NMXCMcyOdIma":92629306037,"zMZXeTx5zy7R":764550187788,"Xn8jKQL01prT":954921545159,"dY7rWeDhKPZC":802007106277,"1mAEiy9QF1C4":829937990834,"p8c2RSkJBysc":352078780123,"NlOWR9WaK4bN":154328966874,"GVpKgFq1xGPa":338959619854,"WAAsp5KOoXsK":517744876701,"nC7XzIKkXnQi":493032205219,"xHYqFLCohdsK":492309591955,"DAjljsJCBYdu":315602027570,"wPEHqhkzreBX":134623723391,"ogyME4phu7dD":369662748576,"55nMtwmMS0UC":997450991170,"sydcfQwhLzrZ":786967126577,"R8Jum3ULVqi8":916045135094,"zULi9lN6A4Rq":813516152782,"8qwD6CX72lc8":169500201412,"3tCvfnuEMxeq":628815315711,"7YEZrN8PvkDl":74722749077,"kfY0nQAXm8UR":444319578409,"QABvhJhbQ7MX":675334276086,"Kc70fofN2XrJ":603110848779,"OKVjLTCoM7lo":112385144927,"XG4BsThVXeEr":605050731246,"JeU55JV3uNUm":645650443797,"sEHZ4aDwlcqq":354016274726,"z1lPwfOaDBI9":356547546091,"tFsjD4S2t1ju":448439823694,"In2NGWcIP5ZC":706013314216,"tQM1WTsetMXe":260485202205,"BgS0nhbwYeWS":312123474498,"VGHNHcfrko4Z":141106187419,"SZexRCRvoD7C":441297414630,"bUvuVU29eaiG":836968308908,"UUGrRwZwDx1i":513368247368,"h3mYwOShpo2h":232900255082,"EIMxFlwNRuzH":17253423521,"pc72G0Fg6ckc":434158280603,"YlVCsFSupR3Y":606461317625,"cGHxgDjBVQdJ":749030261801,"N9PKkISYdOqm":392209836602,"BZgpMg1mt0OA":713720173588,"r0vST5niW0Nf":997557508721,"i5BnaoOMpQpg":957759807968,"0tI9lMP3EY10":954853931597,"I7zhI4Ti6mUj":3299214060,"46uTWCjunOXn":762136577674,"FpZIMY8KxYAJ":128408286039,"nV19w6RhgU5G":374717613397,"L5LTCKv32Xk5":284961094252,"AAcjJbGgUtTH":220920764963,"cxLwATZXr0N2":319545397525,"hjfT4V9KQA2q":261182329519,"o546UVKrjsYj":909000041520,"MEx34LUzkJ3U":813625878506,"waykg4PBnpxq":362726940230,"2MniOcLBoUJz":905279112492,"vjvVbZvba624":954102984632,"UfF9cSRtjfXX":662603862783,"IkY32X1TYN0G":620628271952,"SC4zJttjBEPy":261949759953,"wXivBhvobA4Q":60126856911,"llzYxCEgemQ9":414434826812,"g34mwynozOJP":949660595658,"7V6t9dp30SQn":41453229461,"OrvZVw5byibv":478018241664,"xEJhbHQStxK0":687585238075,"g0EMweVVG705":179967266922,"NiIVzucEEnmi":585319848782,"x7XdrWh8kFFg":988256136702,"ZcKO0xUfDpLq":280727020595,"FXQez7NO1hvi":678734343479,"WZOgeniyZm2n":645657581103,"JmMEpbMiQFnu":56977199569,"e0I1jUSy3OwY":330678029150,"jgsvAAExjUlA":948274673519,"rtUxgTt2rHqX":29851056263,"wsPLlBkNFDTY":447433751290,"ZMuk3axgz5vM":722393580439,"BTpQqo5qVqZV":956019847068,"7xe96XF2xGwh":491360742006,"LiXOruKt7L6o":770389127670,"V1FAOyGBObND":303386174586,"foRWKNtzCI44":404986439685,"BO7UOO5Zf4WV":125051662332,"ZuSqP2xnHjjY":606704150678,"cgaveYwJaaOE":87750022808,"bJIAldjSdpEo":977544641638,"Ma2od6W303y5":496894715726,"BTIooOQMqf4M":549397474509,"3NiBve5uqNvZ":294497054365,"hn6pBFJ2LUB8":792824915430,"t3peXOZCTgNZ":536508959385,"aS6e18U4rW50":938379499134,"btZevmTqBNQu":334225919854,"WksGv7dyd5zJ":637446036765,"l6jZnPW0u9gt":20823891563,"0PCxv6sOwsIE":594952924119,"teY3Lwptq5h4":547695153563,"txVSKw755Fj3":592828803964,"n6T0nHmLzceq":440007937316,"XcXhTaIh1kKk":486978022654,"0MCrP3dfZWkN":271260189401,"edsOESxyftyZ":165188099109,"uYGcHfrCpyN4":270818800529,"8mHnq8FgBXiq":478505677399,"JqLBUI4YhOrb":822964699349,"qgBf0HxnA9C7":424198465686,"IlY1WduOe45r":552859089322,"myyaCSX1nXX7":790668765454,"SH9h1qpG8hke":475256623847,"xiYUeSxqmrDW":404559975201,"c6LvgyUuIh1R":632919819438,"48gfjI3a0mqh":109795674425,"xUWw3Dt7KZTL":185079900214,"nuk5vwGTJSxv":903511210674,"n4NwvFyjiOpT":461251188262,"plABvd2J3nN0":688742204067,"b8b0cwG09Zby":905055688329,"xMirmnGBSKTw":903162528317,"geeDqR516XgL":123336894896,"EPNiF9UOd17u":251875879314,"tTZ9ECR7ReLR":591895988770,"P4ur2nKB5Uvi":410016553123,"qpIb3pd9s7lE":267008192822,"3i9x1y8kStvh":624751808814,"dXsD923uzTs2":326527658600,"ETMMkr0tVRDA":980503511768,"JyM8vCu4tqHA":228882087346,"NG3s3voLCxeo":65704250981,"7nOhwYBmvnCX":525658535136,"ntsYPgkmOtnn":3388170640,"eDuxEVP2UpY3":248808318997,"SuyQU0YVtRff":292614231739,"l2sK2miA1g0P":126896565048,"BS1bTBbSEBw9":669704272766,"PcvKZQmFOIfj":223671716953,"wueVK1oCh16Q":765630640611,"xlQHMvoSstxe":66691197612,"afQVD2UGMXPB":259269314481,"fNmkfyBCsses":880514370233,"PNrt1mZNAMwV":336386290484,"SfTYKzVWe3k2":74177769735,"qspULkJqN6Qc":60287991186,"kwX1LAEXYgIz":985745638100,"dBYU8eNQypbG":773216401346,"sPjYGt9UptpL":795260641437,"ZVcpJHHosaU2":238175075707,"mr5V0eKQsnNx":243164083503,"6grHU5accc5t":634775494598,"CqVJaATH6IcH":764619739201,"FtK3gX0oz1i2":155375988178,"qd9L4OPowGuY":911147012462,"hikrNqX817PM":337065440998,"MRSMM8oMrXVI":624177798633,"ZHSzP2OovU6w":851309668053,"1b9ImyXMsbfl":846724031977,"aCVEl2E7NnIN":268172985622,"cGfWN15OrgGx":68980134861,"PT4BB3XkkKeh":755266281244,"1hv22HLTIFKj":346564529689,"8ApeJAgdfpyg":972891340309,"TZ66wQCOGvLp":924835336835,"ASmdrIOElgVE":338133714182,"fKhbhAar8i5v":42452326160,"bQPH4OqGSHbE":326528936944,"hcmwjCLVYv5T":423919634811,"hGs34W2O968Q":818095262616,"oTCq26gYi2FR":592024036106,"nJsFiiX3rmdK":990509571894,"3wNfCBe1BX7N":630432445722,"i3Arf2vIriZV":642342032984,"XaLzhOH3zSEH":986911622888,"Oz5EIpeWR7sx":657744106298,"xWmO8n5sFN57":159018857747,"IsrI7e2u76Oi":971227881100,"FVL1oZ9Z38wV":106734323078,"eigKZHkmD1jj":758452266397,"19OYyLjgVJtX":477917740202,"zDnyMKIx5zEh":861006260656,"ONcZDepvasBN":936698970680,"LwMFsxjoxS9l":373919867936,"RCk6lZqElor0":830061526749,"Wp0ntCHK9Qpz":754570236768,"jaiqZIJQU3XF":989362702399,"QIcuxMGaywq7":929901752484,"NFMK8l7L5d9f":279130040760,"F0GGaYDyyOns":542861587387,"blz7WinF2FS9":32451620989,"wFM3buyO8OdS":267667190555,"DJa7PYXzVlGV":449402900723,"gYRrERwuAAp6":803406131977,"GqGvyIi87Gw1":3859629867,"BTSriCJV1St5":186906976092,"ZLl7c2kkx1sI":999404434226,"ejQz2HTW3X7M":710988642091,"vW5CkLZZ7bCU":708835531807,"lGN7Lynd8b2Z":486057393800,"WaVd8s14yqgl":972159180684,"xRhvdR221F7h":792490635374,"FiwRgdGgHRs2":937837654144,"vYv8PvpRjz2g":597091333627,"PKxQ4aEXgQOb":548295590158,"hGBJkmZPDBR7":602854691641,"J2Qo8lk6wNBR":324838780005,"8tl77UvOAJIO":743652254009,"b221qOyTYmmO":405772736738,"s76efeclxKdq":335134689927,"Ia8Gtjruhk9J":66719153149,"WsaTuYrrx11y":843067115456,"FjWPvwvSYNWr":682893475981,"U7PN0d7wJYQM":604326136680,"xLrKTPYqb8GA":916543995649,"bYdSKlZ2XopI":824935456655,"SIo67Mn2rP22":142464179913,"NuKv6lsXs9sA":120025134644,"3eZUUA6bSdqO":870816759066,"MR3nfXoLL3J5":688775733563,"abKbU2cHYo91":861822411962,"vqPun4pzTItm":272414406478,"CwANCEk3VWrU":976113861940,"GxFDm1ce7PXz":690159771866,"YkWXRItONMxU":383725059551,"9orO9bUU4YlN":601360031916,"qdLNz6gOHWiZ":70741933642,"ZECYTQU5QwfF":61099251994,"i6VeZkkkWHo3":534632920337,"ffVMSeSlkU4A":107404172979,"zMzgiTMObArr":200917869125,"2Ez2mNp81Hfa":864813351764,"6NN9JPrIATyd":651542492911,"F0E2e75VlGoy":859332426306,"dmzU7ugsgHKd":219500232298,"XYUGSbvPC3Ge":534454448606,"hX4QEVR9RY4m":985367049352,"FLg1whEIDCu8":553776390841,"ILAsNkJnIwbz":440323839942,"AFSyZtVQqREB":75665380572,"6VBWrbQ6cgWE":823640972990,"Y6zRxkHB4cHX":50531083827,"6ZIk1FrTTIog":907566825669,"JmgjEFTUAgFa":102432426647,"uOAQJBSnJ6xT":512127766821,"GMVgC3SpiLmg":570425038052,"YhDoTzqo1Uuf":894257443003,"CF8OBnIAL4jZ":100498313188,"yclsrC9alFwN":102177055310,"w1SRO4TANKl4":624878067415,"2TyPFxOhD6J9":337209076805,"zuYkdNFxKLtv":127787577084,"X4erCmE3l5v9":606210659798,"23hcLIYtyZH3":219630793498,"BAXsojemmAgs":762743056899,"BrAYCRgHQW4R":793209785444,"OkEL4TJEuHiE":70536307913,"ie6TvJisxzTT":949847187338,"SfsWZTXUqoY1":663852081384,"ZhLwIYBa4wci":572152470499,"9d6NHEqPwj9k":466047841767,"2eHkknznCB0O":255346185374,"z2dmO44fZIhb":261650884271,"AqBYRStqionR":809896744054,"8OETfI53uJ9I":867880667374,"AgiqBFz8XoaZ":268897049782,"9KtXji44BTwg":845434031974,"c2Y3kWtBzcEI":268877775866,"jT9ul0ynbTVh":902294992257,"km49yCcuMsgE":308651018863,"rIIr31E587J7":543518637645,"tZkPYo2aOtrX":3038645717,"Ya2NmtGqIe0L":486360449640,"nNy5Q4OgNEdP":275873596947,"pzLlzrfoWpsp":821567575808,"IHCDEIkoceRQ":663770164737,"pMPGgm2RY8Us":15292577490,"DjYxkFvjieF4":798231168958,"cStGdBy1nroj":945538664680,"CaXl0Hnf1FAO":990903392647,"qCkrsttBNQGY":22397741402,"RNN4p1NukWI5":383720475212,"2JS1RWvapPCg":738794224284,"fO9COiS5JBeF":356941742745,"SwwA6jTL8sed":797493254421,"zZcxxwi5bZVf":544846723207,"ge5DwOLaRs9n":754109359317,"f0JSWCEfLytb":463457333650,"P5wXGwTvlY7E":519679306776,"Vzui9eNS4pjS":114811174850,"pMzBgSIbjOOC":939239744950,"pB8MFY5QbutO":464919157055,"kEu8EukeNUg6":846459692524,"Mn7k20zIzoX5":662995083243,"ALe4KsbxsfKk":812327109222,"FpASZTN03EiU":511103303598,"NpybE1bDREzS":492275660268,"qsF0ErHbGYit":975607423986,"epgpJYnx6B7k":855090011711,"rVq4e1q7aDGn":659082757421,"ORUSpUyM9MMk":411067512631,"4Irop2wwPnBl":739342701182,"DkqCE0ABTwMX":24032244626,"Iw3FBlQwasue":804190787229,"AQbxAjiWoWce":644100088404,"EvvSIs2GSgmt":817549037559,"8ergmv8q1Lzu":402118391354,"KMREQQT3rE37":999998230420,"me97U23tCHef":688663801733,"UAicN6tla82t":333797060329,"hFj6Fzw3mfws":920988524152,"Gcr0f6bwdDvo":994530938914,"PuPrTJq8nshM":547809030587,"uCL0OLkLAI5P":176786001227,"r8CNau2SNnMl":888248922575,"ldSnQNv2b3pC":53322162755,"v8DAMgfPxpSa":869024781797,"3HjbOZtKKPK2":377894443438,"g1usJdHoFtUo":703517580212,"JtK4YIhqzazl":257688928656,"IiyHsKRhBSTJ":434664951131,"uk9eiM6xBe3W":816396928231,"aL0htdf8f3pI":212114423828,"qWlPbmshRRLj":724915020645,"uam1LgQEJL1j":845870785340,"MHZunxMxMEKb":178818287279,"1S87B7lKl8Ks":710126459652,"w8aKfuH6QduQ":821699456999,"rzfOx3SUN513":906100543516,"mqloJtlZXrf7":491460880411,"yzXJUbAsFLk5":388989963555,"zUBJNQh05Wvq":474388642100,"jg5Nk4JOrE2v":886778184969,"E45pViXUAt0A":831994449019,"6alfxRix2F4m":244790738352,"HWjZmTdrQLCp":696357249316,"Q0ZlnORZdD6B":546062320973,"u5j12bShO125":546761542627,"j86c9Y1Dj97o":303080410017,"1KMMqb1WSPLJ":732557391664,"W7XYq1qnAEVS":755013614954,"a6ZYEnEdsiW8":694055261692,"BECjxYaCFNJR":669969442367,"mdcekGIFZyH0":408675112141,"sQ4k0r7NeX4c":180028127326,"gwlxi9EMrZj3":23473594017,"081Zu7ZVWUNF":874205881737,"UF9R6gQpu7mn":254135323567,"2ZaoIIzs4yvd":299754108009,"ZcGWIuGDx7GH":907604511549,"cAPNjvkYonuL":606384737273,"INNaMsBJzXQx":36581594113,"UAzj9WPjTEFO":863382933284,"xGARaJrCEPjs":47798750404,"0nXGR7VsHlNv":803818723610,"wsiJCl16IAnM":50393517614,"53VsET8h5cfF":349216978001,"Io5tCzUX33fj":910845243830,"VpJZozvraGWa":431227054581,"f2FfwpB9Revy":285385991673,"rtb83mNteJhl":578265716653,"vjTKbtl37AIA":149127275529,"7meod93g82jM":514457733324,"JN2zx8AjY7cS":41102742236,"zDdVCFcQvyz7":700934784817,"wIlVQH6arKzA":613075867899,"kRZpbYUvDz0r":467487196504,"ouFA9zSpjlNO":565156611875,"z8Yra0IjnBnH":696683379335,"exKe2NA1M0jU":958552941299,"voqoJHES5oE9":955904856308,"tcFaFn1uETuf":433136503273,"taC0MSzAmKX2":258608825472,"bUxXAjW6rzud":665437422373,"El26ZSutpNez":519176757327,"IMie4GModqow":18565718011,"fjmbmHRppO8G":499951635931,"eBQT1Jg2haGZ":315720703605,"gaAMW71vvOFw":345165336809,"eR7xTnjnAUVx":206042625112,"ftlzpYKV38iW":314305024200,"bOWP1UpXQwSC":385032316128,"QZBP3BnxAhcG":783969388789,"ov5I0TRhsToo":736442937957,"hjM3blyPVHHg":727362851340,"R6Zr74niNgqF":123748047179,"OnvUKtjB5Pi6":360127235494,"PVpQduc82ztn":989665804768,"nnFpvRqW7zBF":20994891581,"8WUhpSSTAmel":525166273565,"b05kqXXsISRX":144075383224,"uqLujIsE1rpG":143485659780,"XMDKVeTS5r9k":367934891913,"itjETOj4Pzj0":453620912351,"ia9qdYqkms2o":287049198905,"LjKHgyeWvUdh":878665361838,"gZuPUw450dYg":371804316738,"sgW4MAq8tcQf":64265295323,"wztn7sSQzvxL":299339985605,"3qVVqGrmhFVZ":8083310630,"ujhuVSvovc7C":713703614318,"vG4IXu4Xv7em":821469276743,"lcNRfe2LaO5J":448356461917,"zLLZ3Y3ESqGh":967081447661,"1bBAlrQtGaXL":854984791281,"DdDpgAjrLdj8":554937588446,"zduMMmpdWcbx":340504900037,"TwRT5jAsDV9t":46822015069,"hdojA9MuciqN":879680884020,"ATPL8WDnC6eo":371291980353,"thsyi3QDU8Z8":3032715411,"2d5NG2EmQdGO":187337631135,"v8H8udFYhbdY":206477438429,"ql06DJFBrfVi":368137358044,"weX5A1alcoTT":123954605122,"UO6qgQu3ofJr":478077708127,"LLoLrufIpAOR":520525980586,"96jInA8VbkdQ":795797408495,"6Ta7PxzSW0sg":157493436735,"7IvkhnuIz8jh":559336112931,"DUb5I9DhQ3pk":383560132147,"UFQgRceVxxu1":339495040121,"f4CK7R9CUiaZ":868128807752,"4a9PjWGdtu0T":109148523220,"WREUvMmnynPX":105010873025,"rBtaQBEPEksd":965216325599,"Nz4L2jK3QjJV":329859943219,"ldK1ERQkBd2g":808507484509,"fFWPIuPlVfdp":635310749842,"OSWtCdGM5A05":230874701722,"o1XlP3h4DaL7":365031839621,"STrVBoxl3SQF":676770720529,"11ubvOb7bCwR":865010293769,"UxRFYI6CIRMy":130661447125,"W7NRkzRAvMKo":505546047001,"JpiGoGFIuPtx":7657982069,"bILbf7Vjjr69":242473089292,"PMBzQjVP4iBe":200536663054,"VWWSud03CVEy":504935332925,"6dLOYQNTlHVh":84619715222,"sSD0ZSoxbpOr":94645688637,"tEUITAajNojj":475420271336,"mELy3zFWa7Mr":424762912171,"6AYBUocJjLIR":400161212617,"YRBbmnIIAUgJ":763037049829,"g68TuMHefC9g":781726826146,"MZegujmwpLGG":106903806798,"KCzQjgju5L5W":947899899128,"6ILWTw3z203C":844348960588,"skhPFQTZDFG9":508191377012,"WOzGk9z4jnrK":114074170555,"kfEWmlrryMfO":900894549856,"ADH4dEBKzWc3":251325600665,"kmutiKuQEti3":328704331110,"wkNerfokWUon":355619578234,"GeE8kqCO77RA":894966577109,"Uyw6AiAOxSEP":225957041401,"7TsvCBfkcEvL":567966521227,"iZG3fE9LQSte":186493462221,"B2x0il5JKvI1":819447356520,"9E1bKL6KFTY1":638543729274,"xzgXKpSqavLD":109206621678,"ziXJea2RpEjl":302355529663,"wg132uxJ5Gzm":79278978920,"kznKZ5i91vur":97358756707,"7zaaUCO05rfp":635702645176,"JaSE2skXsOqo":692876359923,"qgivFHlU9EpD":237696541217,"mvpyAAHikUzg":212420335368,"FSf3l46n3ieV":972384522600,"tssP6liMpM5G":84174836306,"gnQF0ivCgjJy":277846076355,"zRsJnt6WrzKF":405508929956,"20s1xf1soMkG":29379323378,"nVuci3ccmUQP":315902748084,"rP921gV8toOQ":82000952525,"lwMOAgfNSXwO":143919588174,"uT9fZz7Ac6nM":268335851126,"bQhn3HbUtFNK":197722417048,"5dqUzFK1LyDx":441753843937,"3m338m84xylA":123562249422,"w4ATg860SolZ":89280228260,"8g5OKminvLIx":16970877301,"sH4HQhskPlaF":858270395427,"lr8LCApQOqp9":694110724922,"QsuRky8B3kLH":957827097872,"xxy3VN6ey69z":673581868948,"8v2bZLzclxPv":145791004974,"V6zzGp5vME9c":882741906808,"7rAZSCM3id4h":126378597735,"PLfZtPtbDBUb":590920480925,"CwH0LK6TQkZm":742978175583,"CiOzWEv4vB3i":490842853961,"VtbArDwZXoVw":555067442283,"si4jhBCf16wq":215314145572,"pz6DZIG3PLvn":304988100405,"zc0pUkuPgxNL":22173343443,"zm5pCByi65kP":728210120367,"PvPKIxFPR0mz":195972880595,"9RcbrdMXXC9F":600915592965,"qeA0NtSyIfyj":896865911250,"fFoHY7Dr3jiU":876313066975,"KIRlwgiWr03p":443565877871,"spxY0LTwK88B":297431837960,"Au36438kZPAX":920624080441,"xWtR1IyaYliX":660075328147,"ksscWzIkLWfc":515113855512,"QtBgR5271fGO":583186279369,"PxW7qdBw6VCG":168774801592,"zWJJqUZrsL6l":197397395337,"lDixsMTZpLmb":905296470194,"wNo694eyUw5q":882800630235,"kIqJpwTrUGgo":827189387813,"ZQ4JdmQNSDvx":160470560572,"l3Uglm4Tf7rc":18631846327,"nvcqdTGP1SL8":699346427652,"obi1enTThB7O":787934429664,"R7cHFySrCz4A":938312529318,"2gLic3xTNd93":930249179312,"jwSqwoRIk95D":377228480158,"j4mjKSqHtLKS":173363839631,"QiIxi6wBdCYr":396777349195,"IT9Hf0Y2aoyQ":723458856321,"4reIVyRUntUp":820628003718,"EvonozaqrtX4":466275056254,"GI61AZ3qGnAi":521103223014,"m7qaX9GQ1C0r":262410472323,"itC2C16sGEVA":623514847206,"aTydfiPKziHn":745041632726,"mfRrCfxeZoNv":435552542741,"tUaU92cRDs6D":828617892717,"3N6YZewXJEw9":758812330055,"yXyeDpcLdIPZ":99195470454,"OfZit9Qvkp3K":220802388827,"vwmadPvqF63C":586098041604,"Kz7F5480Ci5D":34587052756,"9h8rGVtoIYAm":643260014290,"JJMonZpdKjLc":436910843594,"eCbdSCuv4yK5":176120311901,"V25KAKsL0ujq":5472291670,"R6gHPUPWEHoB":641722460544,"fITjbx0nfiBG":687365146308,"xZRf4VxFDEcI":257152695166,"vsILB2em9YnW":677591464225,"UtCGaokE8JGN":779366776723,"3yjuJMEJIbSG":68443836773,"41SQHJGSFk9y":257755562792,"H14ysaQWuUuU":864065871650,"y1FWXdn7jsHZ":304668873030,"u1hDF5yqUySL":934895788752,"BmThMI9SM7sz":12749070032,"NjrJ43j3SZ1t":629693834749,"G2BSMJTHgvxt":186325338717,"PWFShaeTvnCH":901947960911,"Schrnn63gRIz":949962282126,"Ssr5A6WNXELI":702653719161,"3nlXIGeu30MP":163533923546,"RPcY04rP7g4s":937292484569,"mmqj4wa7Ha2r":334243072363,"G162vecFVKgr":285562049901,"qUmbK59ckkhZ":434232507330,"RIU4oXeLrLpT":674429287271,"UrqDrek4GA6d":740567500709,"C5OpqhbVQbnN":309554471645,"uqXYn977kw7l":569960331314,"PqSrAYppXNMm":288068596876,"36JBJ2Gvv7mc":945534163066,"uvyf92NqnRFC":793624921677,"P7Uyq9vJopGL":510649926998,"T4Z70GBTEU16":989877080852,"R3b9b0gj2KyG":611073614906,"vy0PmqoOfBlE":371373099637,"RrnstQMon9cE":560614683009,"hm1Ab8hy8t86":577250382097,"CFb26o2bH7RJ":917003254216,"y2gDQUM6nRso":663059045393,"vhXx4TvlkkYl":714946468129,"wQeEFl3tUjnN":246420470111,"YeFb46WvLim3":462645903887,"MRfkjv3DyB9x":607362554603,"4XeO3JvO4R3N":921437110543,"avqNXhYZGWFm":26354022211,"YQpRp5tD3CAC":166461553437,"V0DjA1aaIUoh":510381877233,"0Jx1XOejjOIl":836178665176,"i2uto6mxxvKe":380574353265,"eWxhOp4TNkuE":144920127297,"s2HY9E2og6sB":279113714329,"lEvoWzmot2ZT":120441177008,"16NplCVnoBpT":40769436209,"kzZtdDUo3f5P":104055619910,"twWCNB5DH0Fn":301593917536,"7YUugr0LBXnO":216091703931,"edhx4Ywbmztn":493737230857,"sgBMGFykHBiV":942406380211,"gYd9w9PRFSw6":570772532269,"YtM9LFiviPDB":965333433788,"AFACEpBy3bqw":836708824182,"1YSHjgf3t2Iu":646981824737,"GA8Pt5eam4PS":113929225479,"BzQhUGkCTnWY":612186385211,"jHBRqgNRHhrG":909488679731,"C1G3bUMyOY03":517616538929,"co6P5UKr8hyY":72220284722,"0vJ3Ed2dH0Tm":511192396651,"6aY7rzLuzpEU":550706392979,"s9bGVguixBMH":328837334748,"L6dXmumJH41a":962938628758,"pxkZiqVeCle5":114879395208,"Ncz6bBQ4IQVY":376018373716,"STt0iyieUpso":905810174137,"OKzNvVGauMTa":947930611662,"Y6yrVmrdA334":189118856191,"UO7aNSOOm277":132667409229,"rhExh6l5KYqm":232566000654,"q2P6fJ31IilO":765249854331,"exBvRjU4XhM1":250691743518,"fM4AjU7j9irR":722277295182,"48sHq8UeI0fU":171135116997,"HNb8f2JIANz7":821970761013,"OXGrnJqR58zM":351541397338,"k9h1TmEDxfOO":976376946642,"ws1DyQtO9aFc":46287796109,"LTmShGfLr4Vc":518216776580,"YfCPyUI1nhco":138861230910,"lmjs8Sb9gief":671190532731,"Qg2Gi6GnxXLZ":513262530166,"f5VCYg9gliRr":907731032681,"YRxCQa5HEB7c":195292426723,"hvqq2vpEXMs1":315075495870,"kP16nFZC4UVM":732702151742,"eYVzF3B9Ivxd":289455993982,"81NYqAvMZmwr":968304740158,"hxPj8UxnMzRF":658492144481,"mz8kvNHzFmwH":678646238551,"asYGkuDMqK4x":601678297609,"LNolaOQast5L":463787048451,"RBmnN7FQiX3d":740629373152,"jFpwTB9ckgCl":496526576087,"dGyqyhn2uusA":262582613497,"RlFG2nNZvjog":378359497891,"NXPpawPUxnPM":910965136955,"OCi7KXsVJbQn":268370896975,"i6yX8aduWVmI":451854626942,"QHtPq88xAEVa":399777350141,"7rTGxEOeB9DX":365195445329,"MUpHcZHu6K8u":5877775248,"FJHMnFX3l74n":822599165785,"a5AOSjDs8Dyi":557398324328,"yqaNqfoGifq1":499651222076,"4VVzxY8EVDok":381692917793,"cnudi1NAXNhE":867203927817,"cAMCaFceNULY":958495129525,"15eqch3oOhZb":712996949916,"fhnQGPg4TEZ8":974861221284,"auvHUYUIvxlu":340137595258,"ee2x4oFH62Vo":557843185861,"3o1NWktGv8jq":773435142667,"TVk5vV0SlaQO":827131035433,"uFEIBYZXWniR":932344313084,"dEXE4Mcj1LSa":709210554985,"X8f9raUsOznD":885888539988,"bgYVIuzNtx5D":826565501307,"M6HcjCjkIK08":877089097030,"9a1gwTzbd5LS":826656755306,"CQHYQWkNDR7J":85543389782,"iAkctUVnyCdI":371929707960,"ceNCdfGZKANf":226014043858,"t7C9rrK9l5qN":947828562382,"4cpViPNAJ2EF":547529221118,"rZumd8cOyWPL":369651982874,"mQ6gI1Lzq06l":354856715083,"ei0vHYyeKOp1":388059210183,"pbOPkOo3s2jq":753746848033,"FcmiKBRG1FhQ":854269771051,"B3W5W9AG1i38":716228405777,"y0fhFT1Y2JII":304155173993,"cI86B9DZH7nm":424359731100,"a3Wg0XaOAwIh":874182687230,"hcD1HniXXhCD":641574021458,"HawUTsvX7SgM":616155142551,"jajOqQoVauWW":341678942259,"SHyqygfMvzUw":414029864033,"5tI6ZdL2uMeC":324369635813,"mTJVoLnUjScW":974320329098,"Kmzvs4URjJcd":452736962453,"zieIzp8MLu73":893337455530,"Bpw7zRiJWlL5":865258656446,"WOZkmK3uKWkg":497776788082,"zmwB2hedaqq9":906059604091,"17nN0DtPk9Xt":269883833482,"hWVURbvKdKKb":694581899222,"emh67aiVFnfx":442749383311,"AXUrmkDq8SaA":571180970787,"hJH5yuMl5FeW":584345847750,"qLkbOYZ5Hldv":161140766023,"lHLtV5KQdnpV":613029988368,"6nCeqGVmmSDP":156967553290,"xqbNPEO8DXtM":260828992054,"5TjX0aKd5NzT":46177980586,"oqlehNWkWivM":713083418152,"dPtKcMi02Z28":37645924431,"9F11jQPlFyge":700660087282,"oNmkHy5kgYBS":284910752263,"yDXdo6lSJBpx":200374999265,"BYnPGFNnarZC":906332398657,"OhHyuhv6GFw6":947591598981,"cOUVJh8EMFcP":215871825672,"ps2mDPa2jvju":128661736541,"H3UBcAiOpy8F":377044697497,"fQxzDLRkxKaw":166095429268,"YbSBuzEBflKl":634009602146,"uzmkf5LFznkM":593962378623,"quUlIpOUxHAT":885330947342,"Ux5jJeVwRIxJ":727057424125,"7a5mBuvnIemB":95140228407,"VIro5eUGpaWP":298074489490,"YkVmNi07qGUk":781151632130,"vyTCJE9rhBEF":154759816560,"7aSi0O1kjrnQ":421549101545,"EdRiyKcvAE7F":134262967122,"mPHb0bgq9HyH":496179854380,"KdQKg9k7t67t":700308636432,"tOvTbUPfnRAz":905668290865,"DdnUAhbtIkNx":506377748778,"yXL7fPkIkm0Z":601524390033,"SC3jksYodVQb":446418937012,"6PYPJ3BoUBOC":354332906199,"Wu8naLO4MHQt":915374999277,"lYL9g8lgizE5":987138935828,"4y91YJzz7bzN":935393482103,"r07EOZtLP5Hw":37340886635,"6o4vwdhd6SOI":20877760321,"l86TGvMAysHV":454809971249,"qLMtnvGKRruz":782429835049,"Jahd6rr2xWW0":420774731201,"f1V2UHMgC5hx":631274324483,"w96WGWtZyNsw":204596445739,"l8TEifsISSXA":115900188661,"j1WbGeJ9De6y":241131871841,"it1AvuaaAKOj":788832265081,"0EEt52iDvVFk":409481176846,"xLKZSvGwvEz6":555738556999,"xErMnsmbtzoC":372865701363,"uHz8FxeatYL6":997787763888,"cNbQicBOEkr2":459019560855,"eClXY9dGxyWf":739470002391,"uYfqMcrgB4kt":95010190010,"JfEXeP5Yb589":839493297757,"wwKSzNzwbvsQ":627318631211,"KupDogrOo84p":261042865727,"iYdPtfE0B6rq":352647521183,"VzU84x7RK9JY":676561531699,"I931ydfvfYYL":447663339507,"cAgptUJIUDHI":944475039500,"omAoQrN0HqrP":224971083417,"46wZy7CoLFyf":35314641778,"DY4P5AhJ5PCu":260322101408,"8JPGKj1NUxps":243224417052,"FgaZrhWN4Q6d":769206035174,"i53vaaK7VoGo":132027511332,"YJC1rIX42DMs":421998835355,"XHaq1ayPaZpK":746548609570,"gk8JBTKMiYyd":413644959451,"iWWGo3mzFqyk":399132874551,"E6ciC7C4iFN8":377262892220,"Ff5xWhv4JYMv":445316414262,"RWSQ05OKHlyK":877596784475,"FpHo7HYB2qKm":56006313454,"P5KqPikW0KU5":667575243726,"NSW9lC2HCnLY":56268677445,"ujTIaJ7pQDJF":5631086013,"wJfKM9FemHVF":219919828358,"PuYoJFuXq97u":202806436746,"Pi5vzaTAIpz0":639266066080,"NlllIjR2Zi6M":664412617174,"5TNTIb9f1j0S":597281410565,"H9XgQ026AUFO":514575324828,"vWv5juk0LsHJ":864035782719,"MZI1jmQlEBPA":28955946589,"rOHi6nFtgE4e":603508069953,"UA62KgH10JPq":341462357385,"EJV4Fm8wkmGn":784008017968,"7x8k053D7TKb":875952110550,"ODs7RJucej32":149726316312,"2z1X1HRpUDR9":339993238547,"W5c3SAl4qz5x":81191998372,"kC8kHZ4lDFPP":783236159322,"OMtJkBb2aVN2":364767769614,"6atnPhvXDF8p":807615695062,"zWpyJPXEXwIe":450341699187,"8c0uur3ZeIkT":282208892539,"2DrSM7Us05mJ":610111676066,"GcudkaneWZib":318844318524,"8ERD31euxt1L":681336647710,"Bj2fKOKVYvm7":130238490692,"WPtAmuSgk393":768084024334,"o1fkcTItMqRq":628042064865,"tNUIWUN6urWP":871565428296,"GP6OZV3Xk0yE":264317907306,"GbOIbhla8NIW":275060415451,"kRoYUhyU2cvM":672290472531,"zM0wo7CwGieH":672112933347,"pphrGZoaPMSn":862876115943,"drYBskuzFP1f":369002538202,"bHW02PwabCYO":306489407334,"mJmWnVbqYS0z":578126355952,"r6rdLd0RjNFH":472315472012,"5hIBXaGX1doT":348867990776,"1QmONTBtGmDE":878391968312,"kg8Mqm3wajWl":800948869243,"d3hNMg2B3Rhu":690495972685,"yGOPOlqVWr1q":524868091317,"cYI4sBTRp87T":921099522771,"QMsPTmyQCvKa":984716608120,"P0VfsCpUrpJ0":264837012846,"KPAPRTbnffRR":708123269676,"VAIAPgrG8RIT":900516374046,"VPHv2HEs6eik":980199391453,"eWxI7xC3KuuW":471102724423,"IvjGLP495GSy":628273296380,"nvt2t7mkZ7o6":795578552152,"Snt7lleXnLrx":401551998849,"clZF58vkWIt4":60545269342,"zf0KhAKCWEJl":31870420063,"z03C1op00LLa":950287541266,"plJEXTkHF8bR":2616725318,"NDbEFdBdo8iz":3870536677,"9vnCqAUOeEsT":368275470274,"nuW916YsmEYQ":932960623011,"z92vZ19fG1ih":428431253408,"pJewQQtUbFkq":341145116427,"y0K5ldNyCEms":257814976507,"n0j1jGR1uF4g":2430830057,"NCfE5A9Bpowr":703752647020,"b5cHYzbU5SYE":264219457241,"0qTCCdYvZw2q":669955379899,"4L6IzikxT5EK":737514541681,"3LnNCjz0NanI":915266779330,"9PqdnyvyLq1Z":138396883801,"77WoJ7EmXBTy":496622962512,"YsuYoLK5eVkV":445117561963,"n810Wdx5pRzh":557628188522,"q25GmGaYZD7Q":279390443365,"dLhFQKIHI2Ex":26345163730,"hnG8kEvV7dhk":61588235672,"T6yVwZEa2s8g":350841202033,"OAWT0eZZu2aR":931447601428,"kNIhwst0TmMy":982786071635,"Fm526lzD85DQ":564150815378,"R9DjUx2lT9Mx":166747565063,"35AxnbWzWs42":892523460288,"NDGqFsrwmz84":970665147012,"VUCEEX3uUqne":449091773004,"QkCmLWVS8gcC":117166099509,"zHpsj0BNaP2P":323559965053,"mVuMDeEO8k6T":136878535780,"nf1CEkWStl9r":957167518893,"FL3bhPznVfi6":306648967116,"MalCnpzGpV3z":727548368709,"mLEUby8LiUSm":717726574612,"NXIfXwtoyVla":136475244804,"GkEhzdVIwbJ1":529938119308,"gzgQAYqKF6dp":208187065118,"EcooJHDxk44L":903213159202,"owvVQAovQXuq":702697819918,"duSzGbG5yZey":942314967542,"75tfTB05fiz9":23733850874,"z7UP6wr5nqOc":637211243342,"d1rkcTIpZNxt":767432756283,"v5dxdK2eZqI3":502655005076,"mC3GF10rfY3E":567385286474,"YDKjyvXDrT4x":800873638023,"Hi8P1FGE537a":100570321356,"cxcSZm2CATZg":496638406416,"eMMcgE7BM7D4":413607770455,"uRQZtdBFQnyZ":475569359200,"tKADDMVbcJEg":92188772217,"ljLzG5gOCBBR":410148775356,"YStacplStnXn":568892715123,"CTCYavVtayi0":312035733788,"i3hX6ZFRuAGx":295519299317,"HoJEPZ2ZRZLH":960996793815,"XrXkl2EbKwha":651630443528,"zkY6e4QWZaJb":909979258335,"RLAgmFZo5pqb":314446266079,"rvGzyogzLkwn":255614345723,"M0nebWgXrt69":3852498219,"A5yNsETzFPvq":433368890707,"VUIfQoY7Hxih":889676468944,"l3RPMHIdJMXF":149907193445,"KVdmVknuDbDI":809801738644,"uGHMkSlsAJzH":14159025237,"F8xHeKR5hef7":159110609148,"3UoyT3pOUUg4":729073156524,"em1rmuGYzNHN":636336499926,"5XMO4F5k4mNa":597292966884,"PMIhXYWYl1u5":364303112148,"0S51L7VsvV7l":472371413825,"4WJIpgR8QfCC":284467892457,"Vh9joEtQWR87":161352262120,"khBpcXpUD0W6":304262398375,"qxjaH6OAiRfM":268029157269,"8ziusqh7mR73":877222451921,"eLlwyzH7Dp3v":390630662404,"H0hoNSLCtHgZ":734022513435,"mhPsrOpuubiJ":256112132592,"V0tzWTgLI89Q":988375735279,"ZwJ9TIWjiMMs":490024765101,"STRDiwQvgFW4":907445990366,"5my3QFRJzI9t":384828828706,"CuUCW9kePbym":912218887109,"McjTI9zuiN3g":667367925861,"EwjmAjHcRUpD":393206185238,"p4yXez7Gco5b":318729828639,"zEFn6EgaxccE":524872684073,"n2flO1yxLV7h":330333340336,"QR9rFjDe33M6":265980254667,"b0EUemJiLgDQ":382569642271,"ng4tavzdbqed":17970086650,"zEJulHGYhags":15438488710,"jbRvlMuKVRO9":953514530596,"0D52Uw0ErTDQ":885063043244,"LVT7f8GcktNq":727982803019,"1cWs9Mwegav3":349475065875,"Cm5E387UxsoC":669399320284,"uPCoyjWd3MVO":895756783909,"dJ69kZDMPXDg":1546146101,"XpFgYtO1zGNN":227029631147,"K6xATl6DG2oS":407058336376,"kfKj3PWXwUgw":507205962080,"fdxsbSrGdE9o":566799960176,"oePo5Bni0VEN":445318342521,"xcuQa96qkfwe":979728289281,"RHRFZscxTX8B":95694550633,"5WoNVQVzLIiQ":420600824708,"sv0r7qCW6KvD":216662640030,"M779k16gRJHe":125277961876,"QOyfcaVyLr3W":665759273555,"8B0YZdS0cgU4":324338283868,"rRNkveu3iZZ6":180063479319,"w7aIhZbzsJxB":877536741679,"llWMja08sFTB":703638204525,"l6ZPPeYItxwK":204409671991,"HhbQpVVOHPBF":819020518661,"Br49EGrZtLwD":426601487262,"abWW5a3juIOR":72998443273,"yi6ZLR6Inavm":985438582941,"Ln9tdr4PXtEi":145127597798,"vqvSfqKfw0LU":862607641704,"bF5qyULSkU9R":598704805923,"5rgFjQERYpPh":659681058146,"Klvz4utrUTlW":866386821937,"npiTd8xsqyX4":153304179810,"h5o52OeGBQxG":299254486480,"0g1fy3yreF41":984724020923,"aH8yLiB2QR9D":498344845733,"D5tJFV2mB5LC":609023415845,"04VV596QDVw6":493081793120,"amobuloI9JcF":556166310986,"bCA1YBrYtFB1":906149288943,"U091Lk1nLi7v":137890891946,"2wGWG61tafel":161496176069,"Nwz2Aghs6tWo":768278442223,"0ZvyLgkH4VKD":296570514747,"UHeUQY62VP7m":761468839547,"R90FWthfmtDc":967534093848,"n5xb1ZbZon42":359799568775,"B5ozwg3CsmMO":860676062448,"w7TX3y1noEBl":334778374655,"MLJFJdQ60rHE":191218439708,"sR6lxoDEtqKl":177439897795,"0gmXDAEsh8TZ":843441196570,"e968w0if8zjq":662524993290,"zRiovRRPXadT":689844266340,"AmvZdmaIrkVv":824639878011,"J9wC5DW3VIvP":377751644707,"wicYc6MUg1k2":46162283362,"0aEfBZ0rscyG":637183669716,"x9NE0JWzz922":49383146325,"Etdn8vFlFatx":897209473086,"9RCp0hQiHwKb":84885944348,"s4AiqjzmvGQi":220109884639,"Un1VKYEY0gcJ":533243173631,"852mNVIplkZD":472305345637,"eN2AsyOcIDVJ":906085137261,"lmxfQ8MIkRMy":723982630973,"dnfLRszdXVAY":249989597971,"wsUIzMhlCm5z":360581305814,"AvaGLRdnMUnm":5317243010,"EfgNExwRkxgp":757927722706,"ohRedhet727C":305612648706,"4aZUV3ZBVqUz":26063520537,"uoIvmiHWJ5LA":302675595884,"6O9CLdBKSWuV":491382608862,"cGIv4WK2pLmQ":991653775937,"vNRBfAJoI3Ce":9344032697,"bxsVf3v5vQUY":519128007544,"ORKJWOis64Oh":275195701723,"DP377YgxQpbZ":132967428717,"ZlBwzbafFasR":419095818334,"tiQ1EdX0oaxW":656136701484,"UIU58gtLNkJo":203002786890,"VxP534XmrqyQ":421270430240,"gkGjUxcHoQU1":823918198449,"fTFbVgur7USO":549388525638,"594YUZ3nlIWX":961448194653,"5Vi4CTw78p19":919018669337,"OV3lJc7BDM7Z":612269911668,"sulimCfeC8nk":90309914480,"UVUmzU7FsxFK":838001485899,"xOsbqGGZTXfi":453380862030,"3nWEbSYJlZvT":217880709205,"eisOhswZ5dpj":571398425143,"sp4hpe5KupBn":200568891258,"ZCV4mE9SYWHy":879496027180,"RT8Us7ptFDBb":876787659145,"9rTZzheP2T2M":808314140573,"yv9dRbvoubnE":370111743267,"qbh63gtVKQwO":685738917470,"GE6VWoIebxfA":801891189734,"v3Gdk4wyh9x3":230966844383,"ipk2FtrIkGPE":769592798858,"aOOELG3qUmMf":882586573328,"Y6uxFfbMUJjf":551371725497,"uHVSNyVNcWsu":896411479561,"7KuYbgSrMgtw":637601030965,"pbdKessgvb0H":129166621960,"gormCHHIG74y":259981747141,"YhmGIypD996r":342095569532,"M0Lr2p7XixCG":341857995395,"0c8Hb5YV9N0G":423606437630,"dVZZXXjO2Cco":915808388412,"qyya4Xgx7NPP":594198942992,"ezsWDNQAiYGd":191900496306,"OpdNiRhMkson":897408375029,"xJmzsy4SFxTm":998410656448,"OWOVChl6p45A":859421770330,"oXDoKLxNJskc":25720344362,"lcrzIkGoqzJz":644180845591,"9lK1Bn8j7fXa":642891718858,"HGRRcZDv7FvG":610023430267,"fXxZNpni2Axx":715794553243,"9iM0i69TkHli":764467297063,"fpCdDTE0XZwr":724122455665,"FK48MaDaOMX2":296147491532,"a57zrvIqUlRk":293958638765,"hppM9bJjeGsC":937029934317,"ERan8nqecMLH":712670306654,"jUMfPotwMzjy":669825004455,"jVCQeoWAvfFg":774804668112,"Rj9lHVye9zZs":546685149259,"C50Z8BspVe5L":351762997157,"bq6bGUnysyLN":315852151801,"QeXqKmQSNqeY":405934530697,"2HLD1oRi67Zo":982696676784,"LPeThnKh72R0":35474720599,"r8pZG9lF28TU":847530009775,"YkOI4m6zq5XD":950906050872,"lI7UsPLtoltL":468812605208,"fSLzwV9xeAG2":776241029701,"4FXvlukIRCFm":445860838305,"SQUXKmUIpSAU":523707680743,"UlV1O0Yz0f1S":963813396056,"yfskMkT9XdL9":132556466602,"4GwTJRv7eooe":647453271879,"GaxKMvGeFpWD":458601913045,"zkAh0i3OZp0X":109161754306,"SuAfMmBfHgUT":815113743240,"IDMHzv15nafq":580825515867,"UZKJj4pkxIQ8":601140068606,"0iG69Rft0bg0":185867023174,"iG29icFAZBRK":442664615242,"nWzLru3DW2de":590025369283,"H66MMAkoNHM8":257270952282,"Hz7hoy5CO5Jy":809844480521,"T5ZOvRbGyflL":980999356105,"KyGuKj6jowVY":938106839318,"FjGa8T1sa7bx":174554693867,"6dgeWKAzkbl6":899991924489,"RAVki5H9Ao8y":631318611073,"sGjLVOJrPIsV":671781934642,"wPs10JIQqqCu":89203044136,"0WqqVAReTIDU":1903224175,"Fld1FTaTpgpz":950752987941,"98vvgMzMIQMs":404412990395,"uLNNdNzFk7Xx":942296440709,"94CK9xOf3auS":59396939562,"q18dqI9ohUkG":8446575275,"0HfrbWNXIcN6":384332141569,"ByO6WfPYZ4TY":855306318845,"twq4LdyIjK21":777318112013,"XBi0f8S8BTSe":538220916395,"cq1VxkU3gdXy":922968217337,"SOzMwUEy6Z6i":449307613553,"tDQZ6M7uFJxF":659166136849,"VJfRD5XHO40p":593393348534,"wlRwn5dKJYlP":492611406433,"i1Y6UTgg1SPO":31613797443,"kPeQlvAZLhgs":807909143610,"MJKAuhtY0stp":56407514015,"4QWhkzlI3xCg":342833079776,"ZYKe5w8CM6XP":254911383173,"pMvQpjVWLut2":626650139712,"iSCY8yslx98q":262844830504,"B9BQH9UhT4Rg":404940957736,"eCjXijOl2U5L":894135927360,"W3aZAfe2iSUt":51069352566,"6g9Miy8v4HjO":591098570338,"wVdooNaLVh4z":581239888168,"HTiNGrxzhITS":908003730874,"ZNPmt6qx4Roq":375503771517,"mU9CUw3pDrCX":393165326088,"7pafqJ17DL3s":486538211561,"nSvdQP1pe3uN":458451077684,"KYYgBoh6LGLJ":800770206266,"YJeyPCgnJrgf":358746042101,"fYjgDEUzBqeI":215111817694,"y7hHlqM4Jlfj":368412932873,"lhxQjqLmKBGT":818335675188,"SW2xhlBunAEi":604782146632,"V9Izpj757agQ":143028401516,"LdRFg3iy6D9t":166751170561,"VqinGGQFrHVD":455294951913,"kbBDd3hRNaY6":227432416032,"DA9rPqpqzkAm":336282798445,"FTLeuM0moZfE":49100443466,"zMSTK9Es9rEc":475814888483,"8tzgT8jzmoN2":488671962098,"jUNafVJWLXjr":345880375674,"upl972TstaGf":942367999565,"VukmvDP4s5sr":836229369923,"SQXYNO0fIpgb":313310479444,"ddMBwruXOFlY":332709543969,"oChhDjkAuJJf":690861981431,"geegFMIIw4XH":187758816651,"eXBZob0Cs7Pr":83806355132,"DhUaiyXYK4sP":722894876249,"iH1rziDOTxSQ":945312138333,"oFHCXSVC5U7F":200871477338,"dIWUYNZfDJ0z":732723826633,"EXNEGvWgBu9m":953704625261,"0VyncLE3u5V5":728125582287,"L5Gmj3JVfIMm":332857215318,"wisYJWrJWHd6":504088076126,"e2YYg9HlCVJU":721797283614,"kq25UPMkldiV":868256727842,"kQVSuCGgOvDv":862971713916,"c9RBGmzoeiVp":33349803975,"Hz97iqtXsM82":628686991906,"6nLxHhNazMUy":334281512913,"BUo8a0r08i2Q":914281476627,"ut1T0GCFGRBo":666066771183,"RewZxeCEIlLi":400443635134,"keSXJtAhluet":973534547959,"bOHJ1016vrI5":291676090896,"puo3hyojEdcp":441784109138,"1WLvehuFvkfe":973468794948,"bJkZrcZClk4v":872142827279,"CwnS6Fp0WrFT":638407904056,"DsdfkizB2qLa":732583478093,"nZXn7HhorHmE":511265613658,"8q9dRys7tStr":764319233918,"kNt7UOLv7F11":121765017521,"vsOlyOlckWeT":271590053606,"42TXFhrxjZHx":379329052868,"rXxgyCqEp1rY":495536336761,"voGj9W3kxQ73":125105332016,"XC8lMe5V113l":692614245024,"tmd4iBiBckjZ":873244326220,"F1QyZ6hGicHd":515310839181,"qxH3Oe0NqVEE":280229018777,"QHypROHIRcnz":645442629198,"9jlPOe2tHKph":166637281413,"INmpQRS4eCzM":527387404724,"Wc4cX6PceXG9":269638344320,"tElW7hMwmYh2":310674001233,"DhjGhB94RbYV":961989879613,"Ypu8CH9DNhRh":391436872584,"0JBK0Ejp6mxN":920945055265,"UBZYC9k77Bjd":731979859204,"FLwpCh5NzkoL":848048958801,"OfOt7LoNhBXG":317636585791,"MD8XZaHKyEE3":398038878609,"NrGaE5BZrJ6y":199184406579,"HKME3SJii6zK":738459248850,"02ME41puQzA1":187294987181,"ttxrhz1q3loQ":447489582297,"jiViBKqDwV4r":444585192406,"z2OzrEKLprGF":951199779292,"LrfG2F9pqbPE":739275697114,"PVUBLaksYMi8":358871875795,"cfRgn4Ezd0uB":469454500078,"Mfm1O19IF5gg":236065041463,"UHUWJGJAVrmR":823238203136,"JfurXVseQmpc":792493732957,"LocqzknFD0VW":839606921237,"hfO22WdLzVGN":210440862161,"aO3wLXxT7Myh":476782646601,"m0GQ8rdTgfa1":564351004817,"WOMKIE2tmp2K":221098639093,"PGqcQwqXfjzO":118517847945,"391VvRRYXNgt":418135669912,"eEB0i1YrznH9":457144836735,"CJzaoTYVWyMT":959894739709,"Xheyn1n5lZUY":501965352424,"3H4DefmFzeHY":202944312096,"STtlyNTNVO7p":984798649959,"YyfF43pOtzlT":646540380195,"9lIvtqLgXFba":842557643227,"qXjeKZ8QNVlk":501859120473,"BFQiUHYaxARv":5783146716,"Ip3zneusDLHt":164491313379,"u98gVQDuu2Zb":867179896194,"bPMVL6ML97WD":111203842609,"M9yc1xidBMgd":205486343788,"udxmqx5iSiza":428511787966,"pNnZqStXv91z":248636303142,"dPRQoCrFbXe5":550673420022,"BLk5rtqcpzzG":864737291782,"NO1UvVb7eXD9":270923969230,"3L6WL08Sb3m7":15445425181,"8ZE0a3Z6ZxcX":496722174101,"LMUaf82MHW36":935521654056,"0LqrnFvWBSnB":905956156137,"3mfOEZIflAlh":251432717944,"jNxe7PtvuuOW":189424948521,"dGFYBdHIbhhP":537035712145,"8r5Ezd60hO1g":754253036357,"b5LSjsGsic1I":24943300579,"XFkBL265OSKl":922643764827,"z9qko0bonVmE":198225029590,"jbLsO5dnHDbI":603201900330,"JsZa7sESWwaP":206289913617,"9z0ThvPsLpBF":299556329734,"ydUvOccGmAmb":709239125416,"u7DPNrJ4KDaX":6293971787,"DlB58QFVY23z":729727521690,"oPEXByHmfGfI":288778626948,"P2cjJSy4ipiV":44818730441,"p876v00g0JYu":88732283229,"SVODfiJGtZYb":686249382794,"wXhjMp3cvfV2":124006249770,"jaFXNTeFpEPD":690418921912,"mnB1w4t8nGVG":802472471295,"Qyu6EXF7O2S3":577791347306,"0orIPhJb8VR4":312310482088,"GJYsr7HdXXce":680210455245,"sRwt218Isr8h":915425032358,"MLyoy0BzfJLF":927563900961,"mgJ8GgH4n0Vp":428061188028,"GeRb3hoHKsWY":695285570414,"TLfxVfiL7r1a":375602730104,"MEnYs5scVISL":420027105141,"PBkqq8CFBq8F":596327327205,"S6SL1ujC9vJZ":456896747967,"CQCP3Mg7RNsP":480328716857,"VvS4wYPd0fVd":728199376359,"TAQMbCMy2YL0":21383366097,"3oDg2bZyI2tH":580783795373,"s5A7QzvEm5Js":784473169952,"hUbgx6ShfvMC":445257683574,"NaOFobnB7aXC":610695409123,"vYTF13u2Jq3z":232116258074,"BSgGCEBLRprU":898627929885,"XD82d1GmP0C7":330669495318,"URZJ27bqPlCS":945154104721,"H7tU83HJeUCR":591455720492,"tbUl89J0jCcJ":326878338327,"HBm5EaKdadqC":531224138661,"lpQyubnAPgju":238380649683,"9ETgDxZsWnBm":981219737455,"lo7jUE6yOiPd":466665673162,"dXkgKI1UkZw8":952226961734,"xdDfLqsA1LUE":888657731378,"r6tDtRsYk78f":480774606374,"jMS4tvMuETXw":402308328661,"LCY36aiyI59L":863552639169,"qhmgBWGtyne3":561518513001,"pZMYiMLyP4XP":126868384275,"W1d7sfGk68UT":681089246480,"GrtSM6StORxw":650900705657,"PSdF1xb0U5iJ":426527442402,"7DmOFWCInRcC":835183843391,"eF7XpeQllf6E":339565673328,"2Z64aBrCXM6G":766782432422,"ahUFBdhbOmsy":348710810618,"ZGJntANQ86dX":169272402228,"SCnPo3ASmT95":450525339395,"E4lvqO5Dstap":891792123774,"oGGw459paMUU":532719653220,"op7zXSx3KCFh":475948441815,"yoNci1ZwFkUn":623623564169,"KdRdKRQD0SP4":712192456240,"jvCK2txjreD9":123334971614,"BwKf6fjQ0KrY":617362123676,"hEnElOzjpUiE":500089991091,"Kbvwah34mGtl":209465742076,"lEKkI8rmS560":330542110584,"zxEvcvXBh6al":516503359523,"FnBC3UUJZhAG":119002750093,"xNj0kg6jZVNq":46223345719,"JLVsy1rFIan3":712425249487,"vzuyEqnlbJOW":937112168313,"t7EZnQofqZXL":399408635089,"GyYNW8deVsx5":161794698583,"blZzDZFY04p6":537162593776,"VPn56DRnAfRO":546323762963,"6Ah7DSw0hp8k":465204077939,"rjcSy4kkpjsa":281808339616,"pGlDgaf9OeaP":737878706422,"QYMMgKFkzluG":25929838246,"qF8bxCW1pkoM":528606070642,"Uzta7XD9N51x":834547918686,"vZbgmTvD0YkX":654267271243,"7luauLnzDKVO":844232743918,"iTpbKdpmvj3R":659519098252,"mydP6KSImW3u":188900604573,"G58gCMsOhiNz":973658001555,"HkcToqUXTqAy":392225206754,"E0Fi8M0QdKr7":348342925558,"LdEeQHboFyRi":502528483114,"p48HAR34PFpi":837921523581,"nytDaXf5vMqs":297453287505,"ogZysjq79Gtx":491048303623,"RN4FGJ38DnS6":756404741777,"dg0GBDsSeaAb":705233591752,"6Xel1Rpjf5Cm":249848054758,"frOFc0jBLchX":887897504170,"cpMc78RikfQE":178656647629,"dlBTTk8whjpJ":842205308601,"t83C7tW1x5mo":671560754760,"FyIUNwIx21Sq":992774865182,"kAcy0BVKL2Vt":390713969092,"dEcBCnsOMiG5":178261148802,"iUCvQHNWSfwj":153610156711,"asrPz12MJqGV":312610227550,"Pt8BfrHxhsOe":227328855925,"Gik6CyZRPA7u":96728623475,"6XGgfUG221zR":51807646248,"b6RqcrS246Bk":932478861412,"OXLGqFBqU3XW":90656933841,"xBwhjopjr1Hj":498586092438,"yziVUXdOaBIx":126874145237,"L4BG3hI8oFsV":821084068351,"dGFwcccFLWSV":699260794953,"GGZCmVnq7FmN":457796892468,"DILEZFCNyz6k":862004247216,"WqgGXgx2nkUN":556312434186,"O7W54xayMSOo":527596075962,"Aiakk3EMjp8G":501961205440,"xfwYc6Eh2BkQ":718344431023,"G85UNUaGLSxo":685011712457,"QU56p02jWBJ9":840858624144,"5EsDgLE5QpT0":342157490205,"uYFSVTrgYhyh":444124150639,"IxM0UOl9VrNH":366683912777,"cDPRghqm2AzO":857884899640,"IoBEvlGUyLPk":874403538982,"fd8TEkwjPEKx":256637124577,"JjF65CbR2Xap":734794712605,"2CALohCpB2jR":577188244003,"AUp85NCjJpqI":861837769311,"F2FOv9lNxTXg":103638306495,"BYbTiHaNUdp0":63726018168,"2XroREkVSLU1":614378700750,"L7q6KNoaNl65":665946448158,"rY0NfwUUUgrg":357276973330,"jRUYTlNBDFuu":36065740069,"k4ORuX384x1V":754518383139,"wMqM1NKAX1Pc":626251629423,"8KV0ZNfHoojd":297618852567,"5tgfPAiHSVvH":198078682037,"wzqAmdn3FgpJ":657064577821,"bkFkPlO8vO9w":619184470882,"emoWN7o2eikZ":789979451017,"5oCJ2xdxAq6v":647053467711,"Rakd82DZncUI":956674810015,"wQR8Bzpj7buW":686283703183,"KxhhzoSaxvq0":599760956513,"eEMgHw75KcpJ":764000688718,"Eild9bBUr8EJ":444861975034,"H1eSGR8pbUQB":732232687576,"wSTCTc8ln3zG":671222455572,"IwOOQL2uDXqP":353537808872,"NfLjs3j2t2Lx":623023866399,"akA6bJzLCWd3":613469554183,"HZDX06pgoj6F":466345700197,"bFVtSMvEdkag":246312511419,"sZa39YXSgtkp":415976237310,"VMGgVrMEVGEq":224120499638,"yDoOyBlSw9JZ":856900831666,"UTAGS9wwli3U":236624015023,"6ReejPCu6yid":632038936735,"n0bHXUIedymJ":485990998234,"O5W14MNKF2LN":370942839548,"EBjc3g7TxJvZ":900194159365,"CoAJPcam8GzR":621138338391,"EgnODha1SQY9":398695887055,"VZft16tf3El5":221785635406,"NpD7tahe2oOj":540660173329,"2Dn4MerpllG1":123305298527,"1PtAqgUBrunT":404486815460,"xVY9gM40Yz1b":102855365545,"H029AtzocIQE":338584114137,"8inbo8Nh0PVb":503042940390,"JtSwq2jvdDM6":220451583550,"G23ukBoObw8S":995080232018,"HbEelRFTd7Kj":918536073114,"Z61ugVWrjyZq":729295143969,"vFMkhICwHhy5":542510497717,"tLeCISxUWxsJ":831077103055,"xKqAPsbRxAqY":660053025113,"SxFlAZFLP2eZ":694986396410,"iyW7c2x2g8JJ":966053110958,"zyTJ7IxUXlzj":516321477557,"g0Mc6HN1HmAh":698429583855,"55IqdGUtd6Df":558455512655,"rK5fBNGtzqQ6":978826503365,"fFkQ9v2U6VdS":888323450579,"QVpsojStFTiZ":398290395853,"rLl8voESDhGB":654122119278,"U2I5BnI9HMtZ":772054572670,"FSgOkj8zSJmo":721507029775,"qjZDbmsd6ooI":503944325986,"ttPABpi8afAf":331592432760,"A9aY7MB3oZWW":978164250194,"9YUHarRTwHQi":240304656174,"yCkNaZmw10bH":147513241890,"F6XxaQiFgC2J":864931120557,"7fBAdiu81p4O":24692059190,"E6jyVCL9rWgP":387002452559,"8rIBKJ8NBRQb":129412504964,"c27WHUzIK5YO":874047165941,"ghFHoQb8CUpO":480237538388,"V4DuraMDjIRH":192174565674,"AZgzftPM1tGF":870461605469,"r5itp0Rh1w6G":679433754054,"s34oO0lzJxXN":747903035360,"VV7FFMw8c5wA":818089639219,"6F2xu2LbLHCE":133504322096,"JQfmM740r3rO":552895301109,"t0L4ZOzTvZwB":214895486038,"rB2bZxidj1x2":989633261003,"XOvfyIePh4e8":654083844917,"3HIDvRgQprsD":640523773887,"QyDJy1o5iclu":703074303299,"ZwaYa7lGvAgh":139224740005,"3wsevfYHgEUF":128296170568,"8ge3PwPhONIZ":902039467114,"JNZLQYlsMH6t":853170686732,"6I1ag65lxIZu":195243505802,"dsnxCqiIBZ1i":639634205987,"g0RsNF09EF6J":241350880566,"96LZ50EOpe3f":24462639891,"AOBC5su1Q1rU":221484185362,"nezifEzPp2oN":18854326466,"RaqIrqyYelAH":995481688624,"eXdjoMuWzjEH":776876565996,"R01cUh7VHOXz":61472572906,"Xbt14b9bJlDp":237844511812,"zO1XwdUiYRps":536049979076,"HuV3m7JfEcrb":919469336300,"Jnb0pENg8d1f":243143823830,"fxxoIhE0vRbo":893597305205,"JXuvIW1shA6p":347370151577,"16z9VI3XnEjG":665288441537,"ZP1gAuA6DZiP":1855953617,"19EndwAJD2mT":474463565723,"W88xHdQdAMp1":369750905809,"bvyiLMti6aRU":194651475959,"caAFE67Go4Bh":560141340165,"GNt77kpeMpz4":437510155638,"OWgocepZoHth":801690159002,"sQIAwpgnYhcN":53575411631,"pweH2ZtqxqOq":538158650573,"hqohXAqIAvTO":217897165128,"CHLnoHFxaLvS":830626777750,"44Umknh2hC4Q":413760330651,"INzN07j9y3tj":710838355989,"Y14IXJ61OfuY":505641009362,"CzEim1YTWYDw":778186185047,"wju985XwOnK1":411566916273,"x6hzWp8BsKTG":971101593848,"uvMbvUWD04Ug":928575765306,"mFR1agTkGxis":462954960923,"qLhN3ZABkMHz":673676603890,"nZH0JvTTqvIt":891956705577,"ZKSGWYSBePbg":22987341916,"g4SkX0PXzL9R":166055218503,"hdQ1Y0miMcEa":112181314251,"jdq4UzSNbxJa":857836304035,"mofGsuC8eIHS":43127631135,"fXWuqBvfOYbP":772948565346,"erm1Pia6jxN3":995854087878,"nwNSxJ43BXqN":3298964732,"DrKVbQGiOUAG":151665654115,"BXxNYaJUBzmg":564703717253,"AVDCx0kRYMaS":441975012218,"MtnIsfgw1KMN":525651382493,"MFDSrG4u2GUv":398302331202,"EGA01xyuXOk5":9996099921,"mXCUHAHeIO8Q":489545349905,"wNI8NhHwWKhd":762603189881,"PMszLW6WZI5H":696837355794,"ZymlGkPDlkqL":477626806836,"YGZaSnlismCc":817357858688,"lAttLfWlg5UZ":578871423801,"4BzxBxXsE29A":525356493718,"YeSH7YsAKO7E":788757270736,"9CNB39seo6uu":529159361203,"TfkpAMdqMgZj":672742349565,"s35pGBFo3xD0":546851774091,"b7unOimNNgVx":583371541342,"UzwRvHxvBcBw":500951313463,"P0YK7CIILjn9":428694237379,"p7db7wGbRScM":720704710475,"V5cpLbZlrxwr":752385499347,"Kl1fkouETras":430104780320,"RuEy4LfE9hdm":982118517312,"b9dYcIjbkzHb":270162570432,"UDulvD4bykUz":465233774665,"4cfXhaPsIuwy":588405432849,"cxwY4mKnuaiQ":688273669343,"yADKb1XC99k4":129266609395,"ME7u0KNhtxWS":558249840091,"gKFQ1UyttCVR":150761226147,"cLcOoeAg5a7M":453832294063,"N6jDHRicr4BJ":409866853842,"17h5bimuk3gv":838845699151,"qSXN0wcxdVdS":496971639474,"GbWAxq7vgyAP":70126215995,"3sSZAv9fePeV":759988850180,"hU4bPeO0vLCe":501700825354,"h9dYigvfaRAi":655846034319,"HMKlR8St499e":645925118636,"DQ5dS6fqVvzk":442648803057,"jDrwSYiBGHPL":633585861010,"vucuoNrhFFPm":186642569599,"EZ21Pf3WsI7p":982201359321,"b2DQultd2CZd":810041279003,"Pu5ESrA8R44B":367933950680,"iCSZBq0CN296":906652524064,"6DYNdsBYeRFT":17024551421,"JVyZcCA4nod8":294894466580,"Vhzhzy44DQDy":47553167928,"2sdipXJhLIpR":161836220949,"PP1d86GGLe8S":299219038468,"7Oy4CxYK8DSy":992910628544,"zezi5qfyBpGO":75693606308,"NtA3S1H0EF1x":609997880154,"xK5DqKUdNWNb":69878963836,"CnM8NEINQcbE":895539606467,"cLtDtuy72cIi":292936466178,"7cw2bWvImVjo":489807017578,"iwtq4uijo6nx":965119543149,"9nmVPVaNLzx4":916143318711,"3pECvzTW46L4":586981920013,"7n2vHxkZyqds":9918033628,"ywg6Xxrr6uOE":740269052508,"FmEvVUftQYMP":164556279058,"TT7Q3rAnUyxT":342024477790,"SA1r1ILoKbVx":657709084998,"7t1PhcHMxn3z":458242767948,"FWcMwztxYq8V":413718751197,"2qlLNx4APKA1":97232094391,"uC5S04QPrCxg":46635369509,"umn4GdobTQZV":414136414876,"tIWC3lZInr2N":237777933859,"BAMJyQDHhYCG":651084410409,"QikuGm7IHzaA":923057915664,"Rla5Q5QZoxUZ":271576022228,"PsFr15hzebcC":927020867827,"EeMW2HVY3MSW":83361611860,"BG265Ju8NSjM":723642042846,"4QQELSJ0o7j7":806948636868,"VnjtRq4E49LG":943603472094,"EUW6UE13xFHA":726027319250,"MOM5v9k3Gn5P":507666619778,"2GqCjIUhQetX":582902929916,"I2F0n4tCQVPf":829464057223,"LSQHZ4i7JmW4":899098737753,"dY4gKn0ZH4pK":141580143679,"E5Ze88NAOE3g":953259421810,"5zQ5mKufGO7a":487556138653,"UpSAohwBEiug":973424000738,"FTRWRB1srBNH":458767462436,"QSDqvOTw8yPz":667808950039,"bBgFHPfr3eXn":269833684108,"Q2ZnhlJ3qZKW":144807134317,"6S9Y0Jn5rB9n":438757891045,"N9qJ3P6vYaNZ":514305735880,"CxdhTogfdWDZ":193874442020,"ZvGMVgcZJ9yc":208941925023,"n45YD2cI747Y":746600620377,"E4aplKF8gnrE":576116560445,"DGbkiQYQyuPU":348597536388,"y65HtwmjyTXP":116539612976,"Pc4Vh0nsv33W":997731731808,"Uy0FZ1lkUjsU":856004979652,"VQV9VKAzt2sc":720670699190,"3ODDcTMnDKfg":272777637857,"edrVkyTm0rOC":845509972845,"dAlCkH079AAH":113878274772,"KGYZ5I2UoLDo":137227741852,"5CRKhoepnG50":460036599678,"M7nUmPdpCRE7":987751408670,"ehu6jdEwmUrP":748046114918,"YYYQoM39qi2x":764863330220,"HHEQ090huOZf":716154146545,"A4kUqRDvKjoW":986315888064,"Qt9z09Juntxu":316797199273,"QIS76kxtWR5P":211363083696,"XaOYiRYlfOfJ":636365837392,"l48PIjqUoPcU":829817503375,"q7ixHBs76um4":477563386829,"dma3FFplr0Oe":922480495794,"F934dEObIQrK":408420807127,"XVYTRIUV1Zaa":423448078423,"7kfKTSXMTNRv":391497974741,"I5wQMbhN9urd":464075394723,"ORKdGIr3b8l6":766208090769,"GywJ0rVrxkIF":7438130980,"Ib11lLYW0sFx":825254622519,"pmV2pn4Ob1Rl":982636906298,"0yTMWB19ap4m":366469438978,"6P1wvIIdyqRP":366720147946,"KwMd8ILnB4Ni":752415552207,"c5VGwOHFNgJL":144803863042,"rMCRRcivLpgE":100382106295,"hT04Jxj597nM":925263161779,"bDXuYZHsSIkT":210772623065,"Bj8vSQeLJLvB":502326274196,"aVc4BoYaniRX":277401066337,"h3w2BJ84lYXg":491640438378,"bdGM0KPySpvU":720187409694,"e3GbiOyc4Y48":745265524745,"T0cfZzLuSl5C":59379683369,"69yk9DixYHmo":270776246083,"hW2C1fHhXWeK":736603488800,"ucL7ap1MyQF1":261784623013,"sIBzEP5bGS8R":670720407636,"qU1GlxA0O7xT":713011633017,"CxSGoGGzXZuL":959832484177,"pOK9MPUnGygf":621987481,"nkjmdbVBq6Ux":63280535346,"dH1mH03cEInn":400567154433,"geKklvkGOqfd":180626191107,"rL5ZoeTbO0Et":339136144602,"wAV9lBfNYYMb":983443915455,"JJ2jGw17KaKP":94058032642,"MkHpqsSeS2WH":899819528793,"5BiZky9vWpEk":314658544913,"U5x9W7exKRdn":838029337530,"S4qwAzluElKC":457856107968,"LjADNiojCxb7":992048004348,"hjF9zu9gUfUe":179311655992,"sm458zmELTiB":629696831332,"hiJaOxGw2Gpa":810005254847,"Xsf68AzBcZZM":85486288198,"xWTK5EFhnLeY":866304205590,"Qitbxq6Aa7aH":450055998271,"RRNsHBfuLXo4":799051418114,"eNUY1WBurYrB":377012746034,"BzqHzDMnqjA5":192631197935,"ntcWKuLyEMZY":826610383764,"8jBkGMGdUnPe":552943813139,"rPKULUSzpYwm":78186020691,"Ct8uSGgXmdZi":992909725759,"f2ArkMowjKay":284918496132,"Bnb1tFo57nyO":498639577465,"NV0r1MVwP3T4":345750078846,"uNEObg4nVBV2":860955997954,"IF1o8bGd1PXf":259625009447,"flhHbnTVpgSn":178945929409,"KIzsC1JQhKeW":670538478022,"76e5X88lLw3g":278997944227,"sK2Xz4EhAMcD":97858290992,"5xmQL7rnNE1O":79180468985,"BBqoIIErmsli":280455928463,"JlmlXgnbeDCD":944392494315,"i9u09lm30EZF":895326448269,"BwNYF0XQXQMF":459429324662,"TckOLWOxxuu6":986639038034,"W54KDz6hzA1a":522599622014,"mScK1eN3C8f3":426555874034,"IZmSzQquzbxn":478745042717,"VG4bbh3clQLU":264721858822,"1dHh8wWf8GSg":287685928749,"SPVMiJ7I1HEY":632520676478,"Hf7xCwlSggBC":898925854016,"0acN1q13fE0k":995180623627,"98mNF3CZwZFM":405098314500,"lsX33yKM8cjg":539987784788,"BrK3AxofTxkj":316869484510,"pMeOCRoPdYPJ":783694239667,"bnAPkUULr7Pz":896541733561,"SuxAfBbAxNif":101747661575,"DrCtfVtQ5mBH":622966227640,"KlMwDwNTLwqq":756045784635,"UyhoupfwRqKV":559939861176,"roWOhhyetYY1":516116512542,"RqX1GVuhZFC1":541196598578,"sej88F3yXBOl":547775600887,"G2O8Vf4A5AcB":859153923237,"78Zl5aInQJJN":656603063383,"9HrUD2wfOqFb":658077033759,"Rh5kqcyXTIKG":206005588755,"gXU3Uk8cQ7ku":104813787143,"LYIrLxgbiCWe":241704680786,"sJ3MvHTe2G9g":130315176817,"6n7GdMs6HqCm":831501490918,"8Au7aCJKVe7Q":363576114302,"k3946cf8drYW":149448233096,"ho3QcN9TCMiv":424009585043,"zwQnlRHWIPN9":43591151885,"vew9nthO2j40":203792051217,"pz5C6bdV6goJ":236395054473,"2HaL4EAEOvaD":800414740616,"YUdZSk7nL4ou":70198628540,"csMR46yCJRzL":407917544765,"LTxG9YKYZdCC":713184681528,"ubD1IC3pnnsx":525761504477,"8hAxgiTfawDg":920745198310,"8a0WtX3y0V9h":877828290997,"Bb4kAGezWDH0":663629315703,"Uz840Wb8EpJZ":773407238421,"sKmxV6f5fE7g":23105748614,"eHuALmVfx8Qr":686121954868,"0PvOi9WepRv1":941094139188,"0NPr9kHnof0V":664199933603,"4tsuAawHpWla":246895156858,"hRRSObzTy1Di":221081716029,"6P6MkVhF56xC":8582773995,"sIKMUewrqVRS":68201895173,"g4XImi650ttx":334853163952,"D7ECZ0BVCaZh":667769717386,"T48WrkdWV58m":679833352563,"rvBiSo435WVG":428469410064,"tGMKAcy7l8pv":33350039598,"qIpf4PncT4Js":797299856999,"vQmTP8TFqoiF":547897548984,"mpzlbL1l5Zrm":419671943010,"HewU8P9kzbLe":240549350020,"Tp7XRtX3Avru":999624600963,"glMtfOl7Ucpe":883997572949,"BTq8B5oiUWhb":478996620841,"22hBjfQ3PW9b":758278027193,"2LyIgBajfy4f":217869563849,"ToGtmxVIGBXd":443697990355,"E2G1ZVeqbZ6z":539459142601,"lYKNJTIYA3YH":821212125085,"YRsoRhbVDfaA":732082619699,"jtL8OCBtRADa":662255586232,"B4YBtFjlmAzf":989199341351,"Q6xKhObQMapl":799115101252,"rgPvHKuZzlj8":542282345216,"vtjGUrlX8uxP":638135411700,"DlWf4kvhdv8u":902012438689,"ALSeRsoqgpbQ":749782620270,"MAn8txxe8ZIs":719748490690,"tH8Q5qfqxOJF":922563197062,"50NhzgnQQaiG":173715984779,"BTgP4gHntCIa":813118281479,"H5rA7azQBq89":182913474813,"ERC4TVqkc5CH":291840079301,"5izHLb1qB6nv":860446027892,"atWLB1GQZTC4":845087071464,"IZxMcafTf8q9":622993365653,"7Phe1JVVPWrw":759903526120,"W8FbryBmUFiw":510906705937,"3FNUZDWlSgFe":127006305406,"uYS4W1dOGi6u":378063811576,"900yYIjfmZGC":257514923584,"JrgJ4wZQJiXG":754073877665,"pEj16S5MRILV":466092682082,"yFQ64sKV2Ywf":240966142522,"D0RcO1i1qepl":662249631290,"12Etd763XNQp":641167826888,"9O53Wiu9xbHD":746621915549,"Ca32xNsIk7T2":455039552498,"hN6K685diSAf":436810620151,"DM4rHedwvXLV":4701988743,"3vcREtNTIN5Z":70187067817,"eIr26CPxANLg":131665373860,"tOMDHB7UVlHO":704222866484,"ufKOBlk5cLh7":9910794219,"7JVXYtg39u2W":190665960665,"gExJNRCuxevk":28854525913,"S7BWRHBgh9Vm":396868363299,"AbB3gpCYtZZ9":757923776689,"qW2sQgu5hUWz":456521222693,"z2VQrcpLsRkt":209926236754,"BTiuJoj30pVw":534110025386,"7x5Hr0q6RBIv":775253065947,"RpYe9ZSJZekr":745285340307,"RZknfbS4lXbG":104734418729,"KR6IxqQUpCOO":735175633695,"OLqIPGhMLWcB":919372698453,"B4AGNEQEM9ZM":138162540085,"AGWmO9xnVCuH":846327736743,"GkUv5bkDdZB3":373009490321,"wPBH8tAO8Bvi":330977983072,"GwkjgvDessV8":559269252241,"I1S2CK6AzF7w":213908194816,"jJUbN766niPJ":459108500377,"b9NnQCgULeEM":36549390877,"w8MnxZOJ6ngS":691599904960,"yFSBdfeAAhty":75346826034,"wcgJYGz0UlAe":126424742338,"JOfEdifU0F8S":756499773067,"3g2Q6RbNC1bO":143226250061,"MkNovnDOJtY1":166545388543,"yzqr9FFEfxgy":976982664927,"HOz7lixuFUDe":521615794797,"BGKqIyalLlsY":382860487204,"G2DF51cul646":639410608042,"EXso1DXY8VgL":597729014133,"aslKkTgCS8cD":694672209225,"FrR7BpXlFh3u":6856889330,"GfIGzfWVvgMK":613890063774,"vZLzkl5ONnlX":533638278359,"itW0RhWWKQ63":966364031453,"yM3eGRN5OEHI":221226446326,"NxqJor0cF4mi":232245853337,"epwj7HzyIlmE":25660031902,"n3XtFum9lqJ0":46855551417,"rYaBqrszu2IX":467725493763,"9IkTNrHlLfKk":565554590104,"h3nqdY4T2T2V":632715750520,"u5Dl2l4smQ1v":433831146584,"HGf3OyisNaMD":381927059912,"Oh3BS7TisPHS":310167432818,"TZoF350rahfu":48168459193,"RsAeNbKYazkY":234446732546,"yVOhhqPsI46N":990798968034,"XLumPxKEaetK":476548885088,"HmK1PoUTqHnO":742671499115,"Hf1SBBgcFn8R":206475816414,"w3EGMylZMsAY":901414488614,"4SYmlaGX44fq":693803476720,"uEe5nVd1oOrT":57886086346,"MooCMXG3F8MM":627590063691,"THBkfM7e0Lg3":622425320749,"r8SCdV82gs8i":759477556028,"VAOJPyt4rNhQ":766898462030,"W9DddYrVByh1":511989874149,"zv6vr1fIgjk7":419864911889,"zjz8TdVAgP2T":836015754890,"x1ZC6juQMGmI":359008064063,"2h2DiIz9B2rs":347070844671,"UxZAkB9K64Ii":994604598182,"c5ufo56Wu4Mn":969338774584,"7GyXgQAJQOsI":925975274035,"CZ9GXaWsTnLY":693781580419,"UPbTe1BSIaJi":340892009087,"gRNwVsvxa48r":474854569831,"upyu1dMpUwLv":985811203122,"uIxA614TDhKE":868912295784,"9vlExQ9Bv6gC":884889850147,"HnV7w0zXka4V":15568344143,"CSXWAfuBlUOr":443776600033,"MYRHr3Mp7HYB":10034578063,"sN0zXz54703p":148087571865,"Jvu8HJtaM0sH":285307927564,"k604WU8uZZ6Q":547079179449,"6UlYQJx7Rsdz":657970905762,"KH5FhMjgadqS":951016016973,"0uwbtxnO46Zq":283595468886,"RQvfGeKYamY3":251491017820,"RrdQF7A8zNGJ":208166325988,"HC3lEELFLJRx":265664895593,"ZXMUhyMaks00":963773910315,"RlFBGGpazVSk":450476733902,"X8oAfCM3jwZD":253147748747,"T0gVBQlC2Ham":254300706081,"gfRmKfcADMkB":58749437768,"iuHRSEKmOma3":417549983591,"xBKa1y89BTXG":564437003465,"VmPAXCA7P8I2":997969606257,"FHBqOmV4Jtoa":897977199980,"AcQWbDsxTnyG":210891057224,"J7Il460xyhlU":58336195088,"jd9YmfkdYqnj":348366813187,"RKMOjP9APgee":247616412595,"NFPBgmjWPnxX":185293900779,"cEPLQMsHJxJb":79981703980,"SW0u0gnrvPws":618142218517,"mXEo0BMDtFfV":311879448467,"L9nO25W1JpdV":678760466186,"IJPe8hc7NMil":504652351155,"OyPN5vqKH6ZG":225964109017,"SGDHEMrFURkq":652568091016,"4qlBYFAtnOAb":514264972297,"YAn0sZdLRI36":44015423215,"9B9o4NGNDU2Z":853096166660,"r0STCrU4lvzY":680554190236,"tipYWj65ENa4":306810718679,"uoB4lOGRCZqm":664204278094,"z1u5v13LlzXv":928638456575,"iTp88aIiKSNG":510586514924,"CTchpazmNiou":540574898193,"gfcy9snJ7LfC":982923584953,"YqzLWGtlw8Fu":355607980771,"Fmip1SuDaesn":216424432327,"LloaaVjd1YTY":141023043985,"Vf0W77gdDwyJ":633503594464,"G0mOeA6KuZR6":863809442899,"QeJAindR4HtI":966399156517,"PduzAq1t8AKc":316580698799,"hhCvGMdL01lN":769906063339,"7hzWcIj9i6SE":424969360656,"WL7JbQuO50Xa":890425182239,"DDa8uvasrNHc":993579328196,"cnuwa7Afrzz4":436776155793,"uLs9FlMbyIIU":168787578086,"0MmIU6UPm9EI":132603506206,"wq4JEU6hFbRB":822398749545,"rgJdYsgwjJFU":759842488113,"wMFtxXeWC3XU":298879560730,"fY94QAJAEgy4":3054635374,"ugw0tvjrqXtw":427545950070,"v88H3u5OEgNM":22077378966,"jI1yNt1oMEJd":898990078755,"o9PTkD7Hf8bl":24903642395,"VFXYQvzz7Ahh":746779947468,"loTYlD7JdHjB":302567455174,"QrIOnNCEuRET":781049905204,"zcG2YF3u56HG":467040844280,"II1YCMHtPbxr":664809367017,"VdF591buFOhc":33722355362,"0nKDsAUE4V9D":658673661477,"g7hXtGIdLUkC":328943733904,"XsODe7PqjZpc":22600717979,"jSNHgCS1dzxa":955811633129,"KGn4xyBsoNUK":223224453626,"opHzreVZzzap":545356453464,"pZQTDsXS7RUY":781159800354,"yeC2JkkYIO2C":33175001770,"V3A1MMpnRPzX":771562475025,"yvr1Jsh1bw5d":299780158159,"raSE3XSYhiRM":66420760971,"BNE76qMxGG3l":209270910544,"fBm5O82WA6aF":391726396095,"f8NNoCAmNJYN":757298026333,"3rzKP2cXenkd":981961225833,"BGfU1EOTab7F":674909083342,"fLZA7pnmNrgg":380427080772,"NiHKla5EWOf7":19326834398,"f9VLnKP8jxmt":545616577306,"HHHcReUvjy43":194090054501,"CWWfE7NZKUuw":874864651182,"CnJHxz7NxCEo":14167622964,"UyRLpS25pU18":700323646830,"7eRqrnCcYlyN":115927414834,"MSGjr26YpAy8":307353622494,"jC1SBHquyPIB":857657433875,"ocPO8y5tuEzZ":361274816304,"C97vWyWptRgW":48697863783,"xyTGEWqTkwMH":115518516632,"ZzqyoEIAOl2g":987856770587,"hVCBFYOHahJd":70036307105,"9U4EqXC8vGGE":64598362402,"5o5mZjOdHb06":13948968154,"iAPOXWMxy3iK":858641189207,"eFXBjrYrvksK":674384001693,"m70FeatgenYh":386799060378,"8XQKHN5JqE0U":219774201337,"EXOnEDvQOJkl":425228997042,"kCw2dhBq1vaw":879463376396,"p3nc4zgr2SgU":246610430984,"nxMDJGo6dXIe":899755739106,"evmARplxTDxG":141220956629,"zzxZWkH5Puxk":365956089559,"A0gZO61BcI1j":954413596457,"5S1E4oDNOdhB":691301391476,"pHR245emDM1a":539457683572,"6N5wtEpbxvES":247098323342,"tiYXyyTarQKj":195490626389,"iZflPSSjMwU9":128368725362,"xZHVHdYOhM5m":880604957634,"owagOQP3gbxv":261700509414,"z7erAS6ppy8v":298659850303,"WrEb2Us4EmVy":901031409400,"V9YCrlE1C80x":155334008130,"hrA57wotS4vA":718431040412,"Sj9UQygmAACJ":44776302287,"afFexL2TLYrh":692349850664,"HIN5MmflIDB0":47595529643,"Db5axVvHMHjZ":401498818235,"hJafLUlu4lPp":134358155766,"WEytaf7XVUBi":664906250139,"tU2O3nNjlCEq":515879874482,"omAtZ0HtCLof":96369323861,"1DQjOBKCnvVQ":448601743786,"l5gC2kO8lX04":706415794107,"zFu8EKfriHub":21249464890,"IyjPefubVkdt":791350991740,"UKVgbZmuRuGA":541024710768,"oeJ2vuwjIPuv":63863911705,"DIv6VJY7C6po":497199079776,"VPjHvmrG7f0W":642430438112,"jYmdbaIVEjA0":611742073750,"n5yYnttcm9vT":981305125657,"kWIhqKUHr1Jd":590717117821,"narSdTjaJEtZ":643240508016,"QjOLveIs805F":627347473577,"23MJ1WMUSVp4":868603295434,"9yK8ek4TpcHd":33211729480,"iJ9i7SrQh9tP":939217659440,"IBwb3ATUxPEh":155446822961,"xO3FyE7O8Q9z":307280518703,"IychEMRPdMmO":608367854731,"ehVDW2jQQHYP":983820050615,"c4BmVzlb8Xsq":49334774125,"wKmhQi6inskR":513424281904,"BUFi7GX2gXg6":391598755555,"vaPTyhWbDgiL":568164477606,"x9zAFPHArNNS":600051153517,"eATXHqf3Iobb":278832164644,"uzZtGyQSTXgT":65171288629,"71QgXyHjyFbS":358401654065,"t2gSyohWP4EP":958615537520,"gkcm9UwoFu8v":654886217033,"zVuHPn4CJm7N":697836760540,"azXeyFyiKi9f":438236828733,"x6qT3epSzoAU":961095115724,"xWoe2BolAiTD":164608320277,"Zkvg139XxFrO":81549259504,"qwMRUoD2qF4e":330507019501,"m8qLTLDYXv9G":988878826714,"OeYHd5Gi0HkE":352624619942,"mQdTZZQ477Ul":114470719124,"CRsies3OhD6x":128409621660,"K7mWox2T8BMk":432241075215,"ohvZFnTD8UEC":848377833782,"hBeykcU8Qd2z":275736240366,"0uxIGk1BM7tA":671458618120,"C8s5COQCC7KT":486552577716,"lENyRWb6uIHy":966465985576,"PkJlfSkZ4GjL":417282280773,"mmG8ffRqEvUe":707876231403,"WsztW1E6OX3M":80475376400,"A3C88HQLaHjY":999666234188,"apJvGUAYesbx":693187947385,"NhWmAFQz4zzE":686663568772,"f5YuTIivL0wd":611757971768,"FlTV9UC89QI5":838904987712,"hHQfGuEQLWFs":589532983719,"fOzOZ4SYxy3o":659999384515,"hM0ZhXYgLqse":878650444065,"fg6knOag1BIp":103092293053,"3crr2yS1C3Ic":600931578590,"CE5r2waQmT1E":475133094538,"2WawjfICZ5Gs":378887965293,"cWXqsZxgfzkY":937044792639,"8NB4Rle76GX3":225898384047,"abbVBa7mSPZm":492409159343,"jrzgcqcF6orC":983870943705,"E2j8HLbR4NLp":70171167501,"1y8fEueoSNYD":891885355544,"WcPNCNIsYRmU":100099953711,"bfpMb9tprTkY":527741606711,"8Oz5NbJVt6vS":280847001074,"pY0XQWiKdEfj":502692706738,"Ow37KlnV5HJh":815420839229,"o9ep6ippcQtl":27684772257,"wXXh1Nq5NlwT":871387314386,"MWUpXKKcQMRp":304632193064,"7aELXXWw3Qkt":905658197882,"tbH8zYdlKymq":302732412685,"GwNTGQ4lDQX4":703079246736,"Iayx3zDt4kVd":913510443353,"yPT6AJfuhTcW":45964796571,"iIdwZRMQh6OE":373942364540,"ayvBPBnLxOUB":206558617550,"69qBWwbxERQB":815692853160,"g58iGCI8BWBi":759463888286,"yoXmTxsPaqRz":664137834229,"74eOKvjUzHFQ":677306568765,"ctfPNWTDMW31":327368775140,"jqCOQOA1puu2":617282377517,"EMLdRk3zMcSl":558172266097,"OgfRSRuLBHum":919177437646,"OkF85mtd57wp":271914276904,"yIiN7KtCoF22":207731072986,"YPqp7ualR9sM":433623689268,"qecAgpzkh82f":503697031023,"MrMX1aRX8Bpy":438612312304,"EmPAiHz7parC":406377048218,"dOvMQeBbIqU7":341905646702,"dWhsgeKetTIi":859908219280,"Etm8mRkPzdhj":606336897092,"ASXlHgGohtBF":863173963157,"gvs9zkBCMJUd":815668293505,"wWdzmL0hYWyD":627462778580,"pyCYvcM3oIj6":115874901192,"b14IFcEMPlOH":380232283707,"egbCWauRL4KA":471303089162,"Vb0C2eRPCjoF":186288392235,"cHI5r5DNlyK2":80293880275,"9hCZ8fdHk3K2":931861578075,"smNZv8PGb8ud":196335991731,"mlJdx3fPMSrt":816280791891,"6TKvPaCpaJB5":323008428149,"EcnKpYW9lZ3s":860325467044,"Se1SITAx1loJ":888822079419,"sCYS3TVoyzdf":479006259293,"SkwrHOrJEN7D":734429580344,"oGKtvZhdH4GZ":895051452291,"EWHJyyOsdlXw":29803740849,"RpzKNgGobZbB":414945151069,"wNR6f3WJtLmQ":202956759959,"6JxBexViarHZ":577318950408,"oPsOskZs8pD2":997258835131,"7xgTayaR6IKX":466471668452,"7Gq7sjBtv5lH":13407239697,"zKGzWmr9de0M":699267767523,"oECYrnq4Qlxo":181712095954,"CPmG7CKLZkSX":624349524724,"ui7kL064U8Wu":952962537315,"5Bxn16Eho0X6":643108200217,"91G9yHOBXsJs":294845641246,"lYFU2BNKcEdD":221432459537,"dVVSzmS8lU9W":712310077454,"F7V4ZP72R3Fk":989730002323,"ZeNMOgdCYRYf":502789255692,"P2DLJPL8zb1D":423169795086,"tFeLAB1C0jgX":968458294294,"hl51ww1arg5k":435851160821,"0xwRFeVkOwpZ":749582601152,"LTjRuHeT2YSo":264985815212,"8DOwICyhYp73":251513148578,"igtIDxrquQTZ":138392188606,"IxUJR3vZwlau":485838584772,"hFB15hHZarUu":35183810385,"pcnGJ93FzrnH":991328151553,"p5R5kK7BjX0z":238668979566,"LPMLTu7YHBM4":700623949993,"5ROai9eFDLHJ":458834902720,"IeyvmKqd7Uph":326531945213,"jkPXdAlc4QBD":617190925691,"PqByYMYnusG5":115798948537,"w2roAtr8pxON":364908438861,"h9BNnbOZiqQu":445522137363,"deoWRZKBM8GE":285940150264,"3YHE7Z83aRlO":610006138576,"zO94prpykKhO":5963399580,"r4A2vuosRZsq":168388904569,"Dh5WyP3qhDLY":708354304836,"7PuuzoAuzD6q":365920735543,"ncI5nDnDvmgC":981598671724,"HH1WsIOiFpFQ":76434176914,"cNN7W5Un5sey":738913147673,"l2hfOX2NSnHL":202867827007,"OibmWnVWp3pA":168684463351,"S7B4XrWRP9vM":847592140114,"YvBIbvk2gwjP":813060887466,"EsfXQfs6ubgv":976090063455,"exHmvJHTIUvf":190140013127,"GUaB6A8M7p2D":671390976098,"7GU7RgKh1Dl5":176074018346,"jxykRxCZ3o2L":836730245521,"xth9gkfBMO90":439073252454,"ypamWassaHxa":357417009091,"AH4PSowqBjjj":47844552355,"riRfClFrLCnE":165623919439,"CFdcTuyWSALk":407578831429,"z4nxoNfEMJvE":254118785235,"DKQnLTG79R3W":628875165021,"RdhOktSgxyT7":378580504645,"wUt72XCbyNe8":895242749388,"BSiE7dfOngLe":775892893644,"G3qAILgM3GQp":467973883233,"OUlpAhhqQz1m":943162867217,"BdhgzSPeAEsB":578229084198,"ZyQ3swcVcnwR":224793819606,"gjORYwN4pc2Q":62708295099,"d8F2hhe39G44":731520008859,"cC2HXnl70G2A":717768175852,"8qi18oGucYLC":162841417014,"4rHsqf9U8ZqA":386394337786,"gNMCPDP68QUp":432121004505,"g2rdhKGjVmJl":918632239756,"3BtUuq6sR89T":630646427732,"OubocTEawjiQ":185470783193,"7WvF8gfZsObb":875468245193,"OlZJqdwvQm3F":644062749568,"WV2PJsWKkbag":186542081693,"pW8HVAFHp9BV":71654537175,"FqLg5owuzH4w":909173045086,"cPRwT9if4fvG":165721892685,"JxouknyNFEAE":715486750439,"D1CkmdhguUDg":729916108186,"xsmai1VqdCYP":666775682421,"dfCyPrFvj4UN":609924410966,"WSb4KTPFWkwn":134883604278,"O0f1tBbODgp1":730787602576,"Dfx7xg1VELjU":69478702,"fDaAOzKdbaX6":670169530512,"Ax1kBB3S2Txu":264231252176,"06HwTbJBLasn":303056171848,"JvtaIwajeb6r":719464979701,"zkAny5dYXbMX":333637178751,"5F48EThsJzQu":565687268891,"171ZEfGQx5ja":277965010185,"Ul2akcvBeNUt":305326129174,"qMlfjc1vbPyH":32440343098,"FAaOWgJdvXmU":285228959766,"rZ8y5M9KQjmy":871444432885,"Sdy94ESsnNGx":494106321555,"uTAnHzStI4B3":525968348415,"TTXHaFgJH965":101237658437,"ooRtAosv3Rye":131847960586,"AqSV4Kl00Ebm":666483558875,"Z1rPcYuJ4kut":577411962644,"tepEQBIv6kHA":850040921338,"4yWltqXCs50h":668952381886,"gT6iWV57ZnpP":919404676252,"xLJJB3uH71F0":496323796748,"7lhLaMxJOyF6":385605790057,"mA3x4OniK2cl":552004533472,"YdaHbUgAJ3Za":291760096123,"XV0Syrd7QS3I":576925646496,"SOEZqsuUhXTQ":659688656215,"4NSTlM5suzCx":861077001668,"FlPYG03DLxj6":617036992160,"n6liDP9m6lTA":41477234405,"DNJGuwUWF9S8":621880098910,"OLSpoQBoyGnz":818419677012,"vTlwapzkin3Z":463123970186,"2Jg4rO7X3QXX":279484321745,"jr8BZx0D5CAh":229775248637,"64LMX4h1pVkf":768256538936,"SVZM9vDIVFh4":650250500214,"jRgzbEb7Xyt9":130004580067,"mENmtgkb6h9S":137605044437,"b6KPjYBbnq50":43442779327,"U0J8gCc99nec":417379801391,"7t2wZ2vwRYMv":668475150166,"StREqNM1cC1M":297379023198,"TSD0CHJLcfWy":815716352484,"po7I6pCwAzKx":549698624699,"rdvX5unAm7P5":786292524562,"TwsDIYxU4BQ5":875913838593,"zeZIK5Q35GnT":474412307764,"1e52U1AkZnUN":960255135517,"4B7Vc4wMVHAm":519902389716,"fVkwskGqigFH":389144538001,"lA4b0ZOb822d":86648117207,"SMijCf4DUWJ5":88770754863,"JpcebNJ8xPi4":40368889766,"r7dfqxtjgTWH":533892459044,"kNP0jBBQIB8k":52622766189,"1li7YbOodiNK":252367239286,"qwulwJLBcaxv":427929863835,"qfJsvhlS8sAc":501034979593,"7PCL4A302Qgq":880054111090,"yUIzaawT6eOi":588951655694,"gwA4gInd0jYh":783681505089,"5yPQ5X09NFYl":278976631815,"naFGUBHF0PZH":624758533960,"bjKbuFvWtuQM":224947053965,"WhoVXzbYcwDm":111634972571,"xZpYmsaU9T2S":612257797894,"fWRup1xaM6fM":252007930545,"lr4lOIWisBHU":494443192713,"fjFs6NkV4Yr4":369865848917,"O1BTjWZmoX9b":196262019020,"AS0z0VX5MVOv":711779757940,"TTzqfFadH4ng":478810623991,"NP9jTJc1TPt6":621460207099,"JGJJz5zTbKlx":782360717653,"OPnCLjTEdfVL":542149760382,"8cCdqxdlVySz":200716131485,"PejyXT4cWhPf":895862560489,"hQWo4Fz4vYHh":129461517060,"F5JdKzS5y5Zo":326773983525,"mB3q05BkEFg7":705070481007,"R3Oyryjn6vEF":191356533821,"AzT09Vkvy3UO":203300489952,"GVUQ7Q8fuIcg":707457085254,"YNFBHr7fUlEY":388914748654,"B9QjIcptRr4q":72096728841,"iN3D18Id3PNN":749347320754,"sKisKq7T530k":123921932813,"FQkQ8FNLXUtb":958828819044,"YOvRfHmIBSBf":140344165633,"nmydG2JAHCZd":26423540868,"3qV2W0L1y00q":904389099726,"GRCCeVz7FZ2V":839986929244,"TsiRqqvHZQTR":301470375147,"iE20Du7q0HrY":862052860318,"xG7FTVeKFTtj":311545838983,"tdjxvIs3tcM3":478521912934,"1CMpv9yc3bba":695592825762,"0H1vBPrjpcR7":291280864640,"dTjvEXQ00Nq0":429669636498,"UmoY56SWwXHl":443260359797,"gvD6uQ7qBkBG":602658980459,"q5hYQvdK7Z3W":546319429172,"9GRdZSaiwJoE":611483849069,"QAZNHCvgQE42":210364820070,"81NUuNssAhTE":266319700437,"xZElAHtNpCom":954251833958,"Es4bYGpdaHZH":867019404772,"X6N5is5KTHxC":355110784161,"n1jJOQD3HTD4":858188957026,"CYb34IT7ARmO":494083719392,"cBGB4FClJwwx":233173038159,"VPYwKvMtW6Be":537781697744,"hG1iIg4m1UpM":412957097165,"vOr4Et3MD5yO":715288121808,"qE4ervseW530":610805167566,"unAuJOtm2Sv6":447613555696,"j4FpdzpG2otv":41907310095,"n7e3OuCAn8iw":483176367563,"VSa1fapyrnTY":286675242707,"N3QIWFRCQ9fd":622681325749,"s7g06v9EGzMn":216858777022,"U2xRMAtXAqax":208913406539,"QGjgTWVnYg52":860918960119,"hNdleLQYitVs":890119365772,"lxUMJKNXDZZO":74564076928,"RNqIIsTKYenH":464167437956,"3KB6PGy8noAi":901657128033,"m06m0X8sfsRq":927288204804,"mVNLiQNjpTXz":719733303401,"PhHy6yWCeQUd":539823173658,"6a2Njnj6m4g1":494964050642,"CVB1mH2EZHcY":8058241249,"hK8vvQmSY9nO":587844912244,"P9OZ9r0mcjsT":559534470546,"mFBDPSdwyrTu":84789074577,"2FWGRouyCcdm":41718651858,"gaVBHDbmmkzk":608803238644,"3zGwupfrLymo":780584178119,"OKcBArGq7ziL":893613940374,"ZUpYTmWMXl6B":216563729639,"kKV97k0ObC6T":766020808303,"wlpYRJ4UF8yR":29016577020,"DwdTeHM3Uo8h":944795715120,"gdneEivDSNM1":721494722443,"Ao1LbQ4XOJrs":962964777949,"Ot7uGZwc03W5":802401495165,"3bj8qhdvStzP":397018850677,"F2RnWwSVBs0J":701468450481,"7CTc1A7iejZz":34631310735,"Hv7TbBMQzFbD":781626849283,"c79NL4chuaPT":96712775695,"uoeSWpT7I4yc":878090810164,"TTFWjKBlN83Y":405070986789,"S2o02CZEaTV9":967908356944,"d7nnjKNcaDdJ":370625866610,"PvWpJzL9p6vV":979435589169,"L4rKALk7thfA":462604587312,"Mljxj4aiAqWb":487712991631,"FKOkbJ3sezmU":510936620906,"seIvv9OXHAXP":448109245313,"Ze5OajpBpuZp":422307524763,"s9MUYjU5DtqL":796813829924,"AA7ostoa2kqi":475828082708,"RKMRbP3WoU4b":319263027000,"UUCCHjoqnRnU":998870736151,"9P7eGeNgI8J5":843955668019,"yoDK6VySUy1M":192879491232,"dijCRctmlbai":667038829253,"j2O3xOH1kvlE":146444825719,"wcb5j3qIH7vq":14255449264,"MVeQQzPlydbM":445800721330,"TH8lTPJav15J":358121522247,"xdA9WhLl45uk":732311028252,"U509mZ2eKAXt":752019748036,"rlEBtCkpBMkm":324889255693,"VX0iMgeoDqef":871229329488,"lCqJNvQopwGl":801122060548,"fPYEOTAhbA6V":115752254297,"2twSf2aR0GoP":472112669444,"vtGg9wE6CVpQ":318096187519,"In86KxdstRkW":609832452828,"y2kiTR1Ebvn9":610789595614,"ULTYlvH4M53N":55063573044,"GYsMfpVQdPm8":493460947708,"mW0HOytjEHKU":650753140290,"i623xVJy7OKf":340534330236,"qVF8UyGtdjkn":605881375973,"G24LexzXGMGb":337762051112,"h5LRQNGqlF1G":480434450786,"WatyML5IonC4":856050614970,"lYlOkgWn0tn9":400853870785,"Mh9ach7dCt7F":743412760866,"UNHz0TyuAqV8":389847723448,"891I7zt9JdQq":541788847751,"W3Yk62106bA5":906826044225,"8cxo5wrcSEyb":92182126040,"MHuGEP0CVguW":395261877913,"C6g2mbc1pqyK":558235402408,"V2bW4f6xT9qj":101203342550,"TyqN7LJ1oYUN":52193385573,"9PKdljjEVlPS":208143217616,"0rwdoLOZ3mKi":745142000543,"06O44fu5vXOW":19808792924,"GtRGnBmhDvS5":250487994641,"DBvKDwPO8jVt":401291398127,"cAowfmPy7812":415324671770,"VnbIChL3eiNI":963914093471,"jVmbFaAAjPPR":489689577842,"vgX3QLkOCGjI":136274190857,"vESi4geYxm1c":877825358361,"Br6gluSMXLXN":815909069782,"i2nfuI0Pokaw":152720505793,"YCkOtrwc8Je4":744207128335,"LD8TNkN0pLtG":862101103851,"Z3P7RKdIrBaY":989399966536,"QOsAyOrIjzHv":810565088759,"eAt8tXM5v3Sw":358606987715,"5OUVSO30psHb":388447139040,"zWD6TUwWc3nP":136039355512,"ehRY2IiFGEPa":913396816802,"J0QWCDU5dGuQ":105996312256,"DQubfn6PzRie":27426076259,"K6WNFeHs21CJ":641654286650,"GM98gH71toEY":53377217595,"eeoe6O6LfqYL":1755123834,"KV24b6dIkXWq":695873596199,"515EP87gpG1k":465323958091,"Difw6vlpl43Y":939262912832,"1o6es4OyRdkf":460616505129,"ygQH44V2NxBD":104423699605,"jOY3D2mnBxCu":448823834195,"4buRNFgr4IJ0":224013867907,"DlxTu3frAbrc":721371679466,"1f1F6YpBiSC2":774179539448,"xHZMEuqB2Ezq":881746780346,"5TehT9jno3Pu":480720724137,"gTUkW1uPOHfb":612691250594,"VVRxo1apav3u":758882635311,"akAGeEqatywj":640603177338,"Sq02d5OGY1mU":446876355735,"iqBtQj0KslQw":280213248437,"fCjICaDJnUPa":431732565410,"NMkbMCBFL915":708907835764,"gv3DYLb1mmsL":117887160537,"VS2wTRc4DScE":771718131675,"q58nKeVJmKRM":189100210858,"QGobwrJTZFnV":519604076962,"EhXNGwCFglrC":573359997596,"TmUJ0z3bSKtI":453950737437,"OKDA5ALiGXdx":928548910444,"Qb8mP17Wb917":431434020330,"YjwwSamsuSEN":487909647312,"9oN0EHKpC6pP":931168751640,"c2nut6RBJLeH":793495221316,"uKCEWOUYXq1G":62800555555,"k2w6kNb3qD54":462948171429,"01XL19hlxjGX":331375785266,"5m9lWAuyPhQ1":545458558605,"gqTpHDHghFYz":991519572850,"GMLVQb7xU5Dm":204873606798,"mhVXUWhBinpe":202783536853,"K5SShUd86fO3":458485433077,"u93fXmciEeEr":560481859156,"L9T2jOiiiTsc":33920762793,"K5R5T54gEYEL":608870693774,"jgw4EUX74CUN":248180425337,"7wgwPzwXBJH8":608884484980,"QSccXEiYcXwX":490418420313,"xzGkgjTjuaqk":974595995142,"cpYvHOtPEo0D":973360613099,"uz0i5P9uPJDn":49114616312,"gG3BfT2IvFjV":16497622511,"8KVVUocw4cn3":411199008418,"4zKCxeaq3xYi":40008529140,"am35j791PHJb":565014815009,"G76B2hP4eSTi":998853508099,"L3YPaKDlLRdk":404108925365,"AOnprvvNzEsG":319496991408,"f9e4N9kjEAsn":908467894172,"37igNd3RMXZs":591975612534,"cc1uHonOfiMN":635264195663,"y7PMqNLtww22":270576413157,"9qPsWVSHCiTP":426084036110,"CYgIBqx4BKa9":674511783407,"1tmVr1G11Iqa":342549216746,"WGvZIXw7tHTj":608415098943,"X9bOtGNcimUd":731730961600,"WsfgP7p5asvh":654497259176,"tf90ygpOwWXa":944666846608,"B8wxPcRiu75E":522856275260,"0yiOG2O7qfnl":189410178025,"1V4Y0rzs1SyA":775547511753,"3DUzm1a4WMxY":906036053162,"EjUe3QwuP79X":751991142418,"IKyzxmADqERK":18921606415,"gTZIxP3f2S7D":358482203481,"WwyvCTgKYune":587533468057,"39FxsyQZgTXA":831588660289,"lpsDZ388JAMq":957017624554,"nyJZX9h0wKfD":579029586606,"StCKHKA5CsSk":763765012553,"sj18d6ZuLrmV":753308645687,"2gkk56Xn5K4K":768445404089,"27kswQhXiEez":261121784642,"sQGL8LT6QasN":59799898790,"VutqACZlsVbA":512176150550,"ipbSNXHqLbF4":159495867651,"VK6RNhX8Ca2t":645991349291,"LValWY0uk8VA":706990560453,"y1HhXqDZAJKs":638763620513,"QYfMcXAZ7ldn":782496334795,"egpm8ihpz9iU":460053500025,"YQ2IKZs3CTvC":100402828577,"R7szkH5tGYhL":745600975637,"d73M8nMsMqLv":540936782159,"e8L8OI0FnHM3":214691314223,"jNd11GhNRcVZ":315519068309,"p5oux9CDbi4e":423510119597,"cuqhuA8xlTLW":203286366284,"6HaH9K5vgTQh":939461376310,"MSAEKjCM1u1b":663074957966,"CIhV2RmKoyRK":674933159333,"5YWck6jGq3Sx":806347031196,"cmVyClHRSd10":688049167338,"dQ6Mg9eh5QlG":22350398027,"oAKPxAvCaaOd":71027169530,"yClFua8Gw6vP":8126914655,"qXvQfpvhQoZ2":569232512073,"2bt06MtpwTDE":35467721299,"XMHMxMIyLWPo":738259188151,"Wyda5q6Lz8Qe":679204088890,"wkkbHTE6oMNK":178401814896,"OLZ6qv3hDWf1":168920317663,"UWruAd0jEVMB":782062955722,"Cy1kYZtNdR7E":313895270477,"1Ycqj2Bifidf":827510204043,"EzdxfYDVPp6r":686122690749,"LEIM0PAhG0VR":967645867039,"3rEPhB4oY78G":466424981529,"e9nhdg85m7Ev":382686021572,"TRaiFz8RFz9I":6947825821,"TupoRWfQmTI3":409875990059,"jw6bMe4UcPjr":907665406576,"VCii1wYkvquE":924777183385,"GOqWasP8a3py":323903309624,"hd2854w1ayL8":259188552415,"nhKI6oOyuwqQ":283030113154,"4nLVnZxhxzPK":330536817559,"DaaxfbEYJT3B":29189887750,"eWhOROR3sNI8":608462811402,"OFkiiFuGZStH":49626547809,"xnrByLOr6FVJ":901701442855,"sItG9KyJjEF9":547874568566,"CyF6WRB053MV":88175473152,"BDuffd0kHUpm":281999831696,"LS7zehC0uFqG":305054696078,"sQy2l1H9NhlB":539529889429,"XalJCzzmEUWs":8198600449,"lep4JhxioXwM":489206167197,"wXQSITbz5jUx":858223064514,"up6Xd2jFzmJe":401159282209,"wts5OHa98IBd":932323360369,"UasdUlFuLum5":445598278316,"us42t8mo6MQc":501939481889,"cTl9wnEL97Ny":198573114498,"SG7fOvNOF0kM":160254222233,"h2GzQsj7YH69":763349018223,"aFqQmgUGN6uo":744136334532,"Ezir5UDeCc6v":859038302675,"b9ltp7wvoVbb":657774958595,"xElZDvGxQwzf":836381377913,"aylccCtotxcB":919927948822,"WwYIoNyiYdHv":72550708923,"VMkAOO6KUlud":980601464855,"1c7xHIGqkKTq":759644789955,"xWxZg7818HcG":436097467991,"tgmluuq5uKgt":276810236248,"hxil4Gjr45SK":915400275676,"g3QekPFFvLbo":289696317436,"eRxLijjzBYEv":122918456878,"OlMcVP802paM":252214328887,"u9sFSgsN2vNH":952966344733,"vp5NCqHkcUnN":430568481850,"SEZh5mIwVEO3":831880431637,"ewGB720iBO6I":78048829509,"RU2mOScnhIRt":738982146819,"86DR628YpW0V":959309103600,"COcwcavdDqNk":465315515169,"yKhW9VRD9Cen":806404121456,"8dURYD8SUrlj":486261470648,"kXkF7IwHdACk":225704871725,"jJQ9nueB4kQs":883609488302,"b2Zigf6DVdZW":595470945229,"Q5fIRfrLq46U":654242040866,"XmFAGhuWnzJD":518279457692,"CRHup7xtcNQI":511929749404,"mZTK7yruXf6G":872991918398,"xv9Vm26YizTZ":372712848507,"59g7anorUF17":11424118847,"aMaG0KkZf7vA":812152478896,"SzJ1n5Hzyqle":18778548461,"aII4mNSiI8UH":695839294679,"tJjr4738ZR0Q":798645616074,"GSOvA4Jscm5h":811298161353,"ZXiF8jho5yzi":695430140972,"3nHWJEPkkxPL":580429519148,"6Vq5pKKvtZgo":550097772250,"SYnj4pOo759c":375291401502,"60jeXADtm1I4":228217394901,"4bseFMukBXGX":308723643367,"lVMAqHpVYObY":590498697712,"4HkIKdlowHUw":839889853840,"65j1rhsNVsyV":753978168957,"LqOhu83n9fY5":879787745818,"rA76Ns7jBosz":71482478216,"ftAu7ax71ggH":293983865090,"NnTLJM3K5w4o":633324968095,"UyEJ8fEoiVCe":418172399068,"S7q7pSihPJ2p":508388124296,"5d9SzNMeJNfD":688058221466,"5dnk83oMaWwC":629115445122,"ACBgG52x4RGo":877649634923,"BREAdWQQgl6x":500421134408,"zci2mz0m3rHD":563719004569,"yD1tFqnH9cap":11706946535,"nm5KGwTi2x0z":875093883837,"VmJUApfrbZBm":207370904108,"y2wdhA4qhkqj":301863664136,"aHg5MmtJS7nl":965753613154,"W7aqPUtzr8Gw":287579703977,"2p7bTzRjHfoD":264689194035,"i0PzApii4B3O":544031005813,"rpJ5EroUsXZR":546140469912,"McmleX3MFg6q":616405135995,"3qiOQog19LUh":136019036639,"VNfgGAVFAkdM":499537069864,"mwuQ9mKX6bWn":390374318410,"P5WkKbsgUbf1":779637872189,"q3uyWSX4Pbhl":455411886411,"owmF3OMxPL2Q":64278894921,"hWt3HcwEl9gn":501689195369,"AgrAhahs7fuK":770912430215,"UJvR9ib0MDp9":797805016276,"UplDxvg7LxCE":836755351513,"LO95OIg5Vhtm":402991935678,"aUmlaOFfG2m8":327461973615,"eBFcfq54Ox6N":930326392279,"jT2nWXcTWbZM":820609599823,"OEw87HSdET84":605324411759,"ErN2YL0UALlX":609064534155,"SU8LddKmgRyp":629278530761,"e6ZJfZPdlQNf":49530876348,"Q150RjzlOeeh":928690837353,"neXdwxMqsRPv":521975251927,"fBf0E3CsJ5uV":635269450059,"OU3cabVmmE3L":74837656031,"MRdomGkoF9b7":450890971083,"t8F6NZ2G63y2":351462758202,"xMEflrQbIlD6":826454142690,"1STdxvkHu64Z":734152230697,"1qaia4NwiEB7":848093156388,"f29Rn5OVbtt2":310097047795,"a6pvm5HI5Z50":251763365731,"tDqvomo8J2Nx":806717724333,"TajmFodZuGS4":221022865861,"NSyZVcCooznj":442710196966,"PgTj2aPGq3Ax":966634379034,"dIeyPQ9fwp7P":652598613000,"MSOcUxjgJRMg":292644623089,"1ahulb0Mi8D6":268083403756,"e19TI3J0yJH2":535869041286,"0dRRBnM8SJse":946738965696,"ByUIrUCfL9rt":723042517362,"K8pp8i39S3OQ":297197629347,"pFgWCIeGuHb1":110564464017,"QAZsJ8Tdw1YX":612770309913,"RW0Q5asNIocl":581233952519,"jxlPby6VZtyg":613820065674,"mjRGQHk0qYkn":744380043990,"c0DdKrnFum0C":653550979936,"8MVVZ5lSeAKu":609238283665,"8Dee1wjema9X":394925398939,"SwsJD0fb5SBH":983557335183,"uKnxL8nFxdJ8":722062995368,"qcwouJEOh4XV":570429988373,"7GHfQjsBcrdb":459042265657,"cbYZbvhd2tIs":410656273932,"0emLMEAgGWqL":921919163181,"yWNtmh1arocH":967866056440,"9PHOVuyxBPOD":496997343198,"DD0QQh5ZH1D2":139564365875,"wxY487Nraqw3":164704062057,"txjtKXDOvDla":845472060145,"KofsIoF1ERkq":883709725994,"jo4kODCP8s9W":17692680889,"va1pvUaezBEl":385657667186,"yJZynDjBVjYA":951416954986,"0IofV8WI4b1C":35619537430,"uyhn8UOv8SjQ":518017026430,"PpM1j8EAhb8L":414473791322,"PXL0RIjvWDYg":725265731898,"hEwvoCK8LAn5":969798004433,"8IDXBceWkMll":300610735555,"IgIIxWkQPLhi":262738515077,"qAuFciMJMnam":942359196038,"asrRorL89Zc6":112292692425,"9cxMLRR3r5pb":627830301015,"FzVNv4HRi4Va":171526581672,"YPoYqryFWnlc":975859670917,"ZFogmGeBq826":360230425249,"j0rP8oNZoZyf":777333870294,"719XXEBrYeIb":879719106901,"ZBO962eop71v":65308159711,"FcfL2lLl0YOL":460579520945,"bIZLDO7LbNxv":378794093121,"2xnTDjXvJrP0":13820752108,"STq1VDkpoWd8":430648652349,"FKvPJ8KULbhF":886532826009,"v5CGe7Lg3OX8":628604423380,"Fai2Zy8mJik8":514929412800,"xfGkHx8659H2":378312980828,"SY5y9y3UG6uZ":748790835744,"k2pnNAbvpGEu":828893179718,"O2YmogsHfG43":732233787376,"ELxsj8B8Ro8B":149700601401,"DG0GFF5O3NJB":400745957631,"VR2afayEFIZv":915953791261,"b3L0nqH5mF5W":847842334607,"0ae92C988t2Q":377128232494,"q2uHSx3fxDxm":996141936208,"vgmAe5yRu29u":557636219335,"5VldcSKHXOyr":560339386847,"7TjkftgXl1ck":703641247085,"Dr6l3lmEl54o":599774689521,"pkhH7IL38arq":787514667013,"kke6laL0MeQf":954701936791,"GAV3dFMAWZAQ":336384660016,"5xusX5iIxgTH":704676603723,"8EZmTwI7VFOR":467346646377,"m6b70qRLf6dz":656588864916,"E1hAI6X7VbT4":398755131438,"SXWQjSxHG9Kq":524052699915,"mgyH0GdcaNiJ":148563256052,"y5GlG63KRZRs":704645703801,"i8ao7gL3A21Q":817547804450,"oeOLgJWfZu05":353720047699,"5n57obEZl1tC":736150435196,"Fh9SY5srWAwu":641884788814,"MLSmgDu8nNi2":132932462088,"mrZaGa5SWctA":148404718345,"HL6TAgquhNpX":583202445516,"dWkplWlGnN8g":964683748321,"4lSUMfXzjehC":925365186052,"nsp3HTLoj9w9":158206133199,"ctJur39MDdJm":995181497277,"9XE6OBMUyzUk":37959397445,"Lc5IDICn05Uu":383979626580,"vL1NPOEsW8oC":378380861366,"AZQGwVV8bLyr":804091833717,"yl6mgosrl2lW":121258143543,"vpsd9jYrZDMh":609282156684,"jRvuJgcUxkbg":860258369740,"eYwFmPr2hLye":899846252458,"hbwBKZ8rnT14":777619665542,"c3jrwxSRkGDg":356062253379,"g7XMXsyM8MuP":524016036013,"VQjLCXDcFFAR":418183040066,"Tr65k1sLz8iW":978054779471,"Pt35IhYQjWLA":540252087778,"qej70hwdLeg0":618589596680,"SVlfy6YhSIPi":77734494911,"RhKSxieH3SGT":200527317230,"yif2Zmfced0u":125318302069,"4Jbw9hcNJ5SF":180280228105,"4JppyzsiHfoW":777782996751,"7v47C4Qo9Edl":596347387990,"GOKUzQhJir1I":54125458842,"bLceqDvNjFPL":676518615475,"yZMxTRZnlXEm":31050163300,"MUzOTiNpNVLy":844328965963,"mK3fIG5OOPyB":97502830462,"5oscixbS2Yjk":444163883526,"cSdVqbTGi2kU":354353512433,"d6oYDvrvyRQR":413955286407,"T9SzpJP6J15u":521967139221,"b9cgVeA7V7yW":701120331102,"4w5O8AYL9kxU":798721440067,"zIakPqpHwozA":356468512142,"4Hu4lxgcnsHL":958584084636,"EunXodWqsx0U":257140662871,"Hu2yQuHHx26s":284923859399,"OB2Hdi7VxmbL":460350541469,"TLoAEAj1zvM0":503791870919,"SGrFALAY3TcR":706433426817,"253f5crS67UG":459476960545,"WroXd0aEI5pl":621444009533,"Xi2fta82bls9":599707304032,"SqEPnEDk02SI":940711163717,"hGODd58IRNsq":291930184885,"Lh9YSoDf8Ox7":742378271355,"q5FUWJh0RA3X":952432223324,"ygoTjp6DSMAq":488854362220,"Xr12wNukUKjw":355295629014,"hCMIWiZGDQbE":103470693409,"NHiW7kbxbHBU":541000225137,"iHKJEsjrnLIX":164020726003,"oIfLJHKs0UvV":988934679677,"h4y15v5FZ4lj":810738683771,"4tDAHkk7w3vI":332344591043,"KQ3fHeKoegNZ":74589579332,"ZwODBk4vrgcL":213702826749,"z69w0a5UsFI5":399636950093,"EjBsLMEDwzoH":230319840732,"GVKQykekCUdL":230006700291,"aGGwfaEt2lOy":329610618477,"qfKTo12aGzHK":744837622083,"Nm3u3sYsWptB":490242160529,"TsFhY7DbRSoQ":874993320461,"5GY0L3QrhSDH":910310793263,"YmMxZhp5YfaT":374611300907,"6w1pWxe0tktl":123028669940,"5wdA2d95W4TK":53935891266,"6EmDzk1vEIXc":120386895688,"6TZ4RtN6mmyg":65460415987,"LBV9bAEa9o0h":996738752469,"aN10V1dnUjxi":94897600520,"ApRJuvhbmcbm":241668212459,"VUlOiTsfucgL":907675058702,"QdQCwDmSeR6L":794020734486,"5cHH5FEBdXmT":237271879108,"FsbaasOOVguY":455299820340,"RYrrwRvSEeoU":362662177017,"urdIWDTmGeMA":631654170290,"R31zwMOhVa5F":371501650008,"69Yy41gbD4pF":37274765493,"lcOEAKQAqp0h":685546274760,"JnPWNZRlErqn":611471293063,"pLB2kSBrXdfb":946371416580,"YuLE4VtU9fE3":307502561233,"ow3FcYHdLl7r":372559411891,"0TDZDbbQZvfX":991482933109,"e8VF8ejG3BJ2":977789757188,"RzquD7kM7aFx":194420472340,"brsv31DAFrL7":279809034487,"vY5d6X1Tqnbk":919042377574,"P8cIuDHvVGFp":138984033927,"KFPsWRYV3D95":944542686677,"blVT6zwe5sIk":452255758136,"Yhfwkc7q2tUf":231872510765,"LKZJxsaAXZR4":867980279373,"118fmXAWf3hr":568258515011,"tbCNOHlEyNGO":101448847440,"3XjobD3u823z":871749873277,"Uv8EGnlv5EpA":419944910284,"lvWXJhiQNqSa":136646880033,"6HzFVOSSS3e6":428009903224,"VQ2qdEJT4qmj":853285763610,"RzNKhouqVZ6J":291822850984,"WhaYqdzzyZt9":907232767402,"yeyDPZh1pd4r":729734306166,"8rQ0lcNAvdGp":214896766858,"c7ThrXu07veh":278386699421,"1v8w5LrLFW0z":186540127984,"SGUYW9EC2LfY":407354262467,"stkfu9MuUYa7":72067847246,"rZF6nQJkT4XN":662069215532,"bqc8cNjxcT4Y":582945550378,"rN6VE6lyfqE0":155501392002,"xc4s6CkB90s2":421496834605,"dDJZ9tDxXHuM":367210216926,"0YZeJ6XANfju":821235911596,"CQ8uitIqvqPb":975158181811,"0FJgLRkcq1BP":105740710348,"GoBTKMtETsOz":645859532847,"HXoDbyVdyszV":798951783891,"0iCBLtCHg139":114025766450,"JRyQLErk0aSz":134769463960,"enGlLYF8vo5o":170266962993,"SjnFpAWJfSQX":779775366077,"tiHmFjeowkHW":566787210481,"24a0KSPPQcvh":613662129509,"G5KYNro3x3CZ":167165695995,"EczS3UZ8Ubwo":634856743819,"9qr0u4PScaUn":445639048559,"EbkKQmsCAAjo":25489343638,"zJIwwBSqU4Y6":108547241595,"WX9DNOSqZm3R":343095875638,"tEDtm9Qg00pS":491936097402,"4oXwKRCbUWci":304208048266,"RvSbuK5HjEJe":562571484955,"fQyBKsiLuRcp":324851122392,"A5yZj6lfSbEw":875482386019,"dCKPq5tglQM5":241654983639,"4vgvtG7VZXhV":66522210907,"1606pYfhd6kZ":751261934786,"iAz8vrzPpTzH":649204604077,"5aSozuIAXmM9":967588873538,"Rk8luMsMMXn7":830677714838,"JMh6HSF9MpIf":599508638581,"oNuK6Gi6RvDZ":647976717722,"kfiCydrHjvrz":583912636382,"52xDMP4jx4Mw":292747212388,"5j526QyOV8Ha":257714120819,"D8RdHujNfIci":544586541745,"mYWVQY13emWK":593833932148,"9Daz6OyiYMEK":134206131639,"0Hq0GFeHMvug":31788383554,"xC7Pdn9UgkIP":738368893895,"pwgNQvnpX9UA":369624681107,"czcBjlSIJjpy":362274243444,"Yx9moXoDLim0":653624436741,"4NC7ggnvc4nS":33471539298,"1k67WyFdKF9i":811401494009,"y9bedSL56U7n":510022468560,"bxJEHoZkPbfg":135376305748,"wHIGDOeeMLwL":418385155578,"lpaG6h7OOf0p":610109448712,"o3vqJHnXnFfD":892289107304,"NXAHlVJ7YyZz":743898712361,"JekrpMWxVHNV":542040775039,"r3kB1j0JKmtH":138636418778,"WoXU8Q1ECo9S":593436492265,"3UACtlOTx6iv":505209060641,"WadkxmxHOfVn":866834810110,"XA7mLfcOXPAW":650909198435,"DSB5hJqgsigD":389035481299,"CSdaiHaCYifH":309014935609,"EMloZZ50KeT8":182933822117,"cmLXHMksLLwR":641479471600,"Hy9AiVKcnOxc":514630366989,"vfNcTlexfB5k":303709486794,"YGyztI6FQvC8":992166509009,"B80lCNkmM6oJ":17697006728,"6mWhCkdTSXiF":922910043644,"8fL1YhcIssiw":504088973340,"qirV8q00hYZj":236619019414,"KNcFTasW950d":704256305289,"WhnMY48DNRrF":424675986193,"b3R5D095ihxB":515694425414,"yABiNtXdDvLA":705827406700,"J1Ey7Uk6nXA6":113997602768,"35ZT9etdrbpr":383177387219,"nTG2NV9NgUxp":856581483130,"wHLj80bajpVC":201221459263,"tGVwnAygZMIk":564767822879,"AwvTiHp5IESt":126097512141,"3KEoCILVdHyy":774848303190,"1nTJMruR7Jxn":448290561429,"wWttbpIYcRud":233587171448,"xJJ2aAHRd5nC":643335881206,"Hw3bbiV0w4oJ":691379479839,"9BEoCqQil1Jw":846533307928,"T7dcmZB4ca31":456940615325,"Z5dV78ZbAhhb":422404462946,"yeb94EpZdp6l":628776657343,"dRrjNNjRjus2":295652279832,"ImydI1bVeal8":309016716689,"f9OVHQwjGY0G":528570072151,"laXOasa7p91d":954978193529,"OaK9CK59rE1g":357587369490,"KfrEZxlNTj3M":567359416811,"WjcnVQcQdYSE":67558417197,"qPjHvfbERr9F":728864820828,"l8WbtNrLnx8k":944566490439,"jCBYKuiHK9RR":263137158610,"RzgAn4q40EA9":721484979440,"vocWzWvkkMLq":885683425733,"3zhFTQ1KLdU7":296190601314,"3dginl1hHwZV":453732824414,"4NOPLYaBXMXo":394324220399,"DWXkZ8lqdVfj":37546292875,"1OY4DC2nNetr":609865103695,"Ch8dmskVVkHT":317130715751,"5qTHuoCNeVph":482821002016,"1YgvHVF7o8qF":567836381106,"PZRF50DLNfDM":630505107324,"S9i1DidxhLr7":68556872285,"MV8N9cHpTM4O":343586472800,"aTvW373givon":236591722452,"5z7T2A51paK1":330014990062,"Sd2E4ZM4VmeU":275075565091,"LoXJFexhM4yx":264926398295,"4QXPoDCKmGlW":709522345363,"xpK2d2sbNW8J":544708353817,"SVMHSwnYtrvl":903376794763,"CFjoJrb6j9mT":539494570848,"p6rwXyRYsqdk":730141415456,"rQSSlOreklNI":103391605088,"RhCqhVOItNA2":727655570049,"UfwkAFPu741q":18529033778,"bQaedughKcs4":702357251701,"thZYs3PEtrTi":91191121794,"rAjlpLpTcgEs":169209742804,"alX7C9DBDddH":522473573564,"d9kQS9JT6Vub":133832410319,"pniLWMqj5qFl":455199222752,"HBvHphMMKdcH":116890111329,"QnIQrMlJCLzk":718284693886,"kUvdjgYs7Huv":820577004227,"vztZ8FeMCatX":62943117758,"kaZaJEAhsdMS":261710925205,"wPO6ohf5UqTY":272720290664,"1vxfFWbq7EhJ":729427870457,"75DhWH73feoM":791978201621,"KjihNTkpstP0":191067497838,"kx1aqg5nzsc0":288230569853,"gui8T89EIV1H":408733852133,"VlK6MipPiTYX":304782147170,"KdweFvgEYyYW":461120717203,"363mqkP2LXVM":233650273641,"T8Es1aIalsF2":791560971320,"MWx3Z9rXVadg":184588344315,"JBNkY01L7v5G":384807211653,"4LnaTNUtitcs":384878420595,"vBNLvicYQLfY":228931177963,"LdXD8Xlb1KzC":183898321540,"L25UbtQKPDtI":389789168451,"LuNVDlXemCkv":752483859260,"VBFmneXjE3UV":29874356960,"iANT5jag37gp":940243007431,"c4IpauKGjSwv":858531535563,"dTxIMP2zKpsx":780801840227,"N9EyBw1pDxT4":462641994096,"WV2Zu9KURkT3":309401839743,"UkgBxmEoSVEh":415082095187,"fDFkH0qHB8sK":151316684471,"EbIBK0Y0GeHy":982898585812,"k80EvkmUzJVk":331314706819,"he8BtUDOgfEU":481390201521,"dmcO0vaQjZF6":712649041049,"CaNkal00s5dc":52688058907,"fchCrRZpzAHC":392074019973,"dJ0YFnIHdOkv":243715867480,"TUUdXi4js5my":147218275646,"9RZjkZQPj1Dh":30737333008,"wCrdsTlt8m9l":304658667505,"TsN8rDvecIEW":461009518271,"TVfiqcBnoumv":764463228605,"3vtm0V7E3NnT":846368301730,"zLJqN48Bz1FE":905412828731,"nU2cyPswaZhw":874319588070,"riPufjzDcbLl":807244249489,"bIi7LflnElNP":56601565868,"U0194lEvz6we":746612666450,"qIY63JuPAYWU":421992351412,"zVHJ76BwasKc":946510785944,"VH4YCOkrKrrT":771177167224,"GEJ6EJviMpW2":352707538811,"obvk50XEv6zJ":388994801106,"EEe4AtaVJOth":411028263096,"9GgA9dxF54GZ":43451088695,"0veTPuhiHFHM":59174427270,"f386p0VsDo8e":941715970832,"eQBYlbWOtntv":961139091428,"z943YK4TBj9f":862087277089,"wDcsZMlXI7W5":573927280591,"lc2vLM8GvMBw":624285278537,"dFQxwJvCbY2W":973807716883,"2SNsAyYuERI6":479973426813,"zS0rRb1011Ee":366719283001,"s48o9OaG9fmi":924053078290,"ZzBiK7BNhRpN":296128473649,"a6NPssTcSryo":161978630653,"o08FZWVAAv5O":830348000160,"5y6QZADx8yjR":123326460818,"xxHcTA46bhuT":327441513689,"ARcpMO3oTPEu":992531052668,"2wZi1gTSZlRS":625248483577,"EaGYcQjKTs83":445520132712,"fezZG0O3fEk4":51399374096,"vHiw6e2YVbPq":503289835689,"wZZlC0FvOPMI":103651289721,"74MhupD2HtB5":808640369967,"UPDgbYK8dwXI":616701171327,"RuLOyAz0hh8P":69297354854,"HRJXZKvmAfMn":672141366986,"4a1SVVPmDYDD":71473164402,"PYNWCEVDwVnX":43026501736,"Co5tplcCbwBI":725471110331,"KC1md1XNpLBe":353223008139,"bWqRf9eB8XXL":24913319510,"Z118gkbuiSyw":462909676534,"LpJ1F3mbLflO":62968516984,"41UBHYWlw4Rj":889449803389,"eGjr18fjSThp":493979807169,"ioGq7sIMerF2":345409653599,"10eTzvNRNKw9":278910424110,"4QwDzOGFh98E":636356482269,"lo7kCPIEG6c8":230605727336,"2WyudGT6UfLd":980293352576,"z3sOqxyCTFZu":831615817709,"dK8ZZcmZBVf5":373250423626,"iwXJIQbkt47i":505676098928,"KU0XQE92KW1s":241054893827,"UoSY9vRmf2Gk":500030525999,"2vPE7AwdXyQp":190957516643,"2X3Cd7Z34qOP":783387518243,"WXil9Tzh0WmN":215382094859,"KroUguubQ4dM":115542538679,"TdK2a7ajdWfJ":669615094799,"HlRp4MlOWsXb":845316904297,"Mu0p4gp8KbDm":523257720553,"F0m1EaLj8wBW":895760290388,"ONrgdymgkOqP":926118142350,"uqxYB5FWjpIp":638605838044,"LTlLmuYhIAOQ":362572713556,"iF04CYYnGK4w":951630758742,"X7Tj8rj8J7CZ":848319631899,"HhKyNkKqL6pm":749500509830,"OpIYtxfMIi77":749758624920,"HQ5nve0cPLFi":318205010717,"TOl26Qx2ii1K":292081288524,"kodYEF6SqqOO":408524799628,"gH3aNyKBpMCZ":315773371983,"b5jq7zkNRPqy":372699373893,"gBGyfMdqcD2l":173190831529,"mVqbxwIxP7fb":980993510711,"eYl0kpyhbN3k":424765140212,"3n4fnVg059Rk":192333518242,"2tGc2qMcq5De":569750597493,"ANLD3LoJnYPV":717820118403,"mNzMh9J88K1y":593068005447,"1ooWhRX8rFvn":886947006357,"oYkw18knEQKS":829253441906,"IhktkbT3UXfb":83717108314,"zMJ96T0TjXhz":600058019549,"hWvADRENPqn8":100392334814,"SKNzFEeUwAtB":232438260327,"CrTg85jZQgiU":935616294847,"SEz1tw0yh1vr":204731255724,"TlmMHim6OIrW":493807639162,"WtextLF9Qdvf":849675823458,"vl6hE1ereeZQ":92799649375,"cjGR7x4Yo9ho":313048022778,"EjV4Tp8VPXrQ":8826364498,"3MJhB6GEPImo":74211849419,"fQ6QzgCexdfU":434766266219,"ikDG2I08C5UC":38385253616,"tBMcE96axoYv":484783160185,"UH6k3XSyptMl":452774869191,"QeKQDUF7VkIa":40539987978,"IE8K3vARBey5":953216783907,"ZsBJwfCtWgWG":484485127739,"l9g3C5DYQ4Kt":367150076526,"RZAW16VTMzIw":902629641352,"jLAG16wTvNAQ":701513837491,"HVHnYFyNNrjU":415509266722,"zywDvXidfhyJ":928720345836,"cYDRa3kh1j7y":955423293827,"DUNMKJY62418":964048182873,"KLK0BGy9oKqS":71158595439,"A9TUwQQByoPq":717275153786,"786jqg62Fv32":70173412098,"dRo8Iq80Gf26":536618050328,"HJBLvAPH5OYl":951128864725,"AG8iWdrZqIs2":305079413704,"objjQSp7whnl":650852382394,"s5DtCYTMFLKz":982383967088,"ToaQ3aQ22reH":496615684409,"edYNBbPc8LSK":746099437196,"HAyUTF3onXtA":434010223247,"9P37zIt0VPVZ":580622176879,"DPB2ceJyYmbX":882432999127,"PFoH3PBM5jJw":293092677271,"MrM7MwdzwRKd":689523177109,"9svWEgYy9VVA":232524181695,"lbxO4P8ePLM7":454623222877,"Z3axjhyu57Hl":239721881207,"6lqtr23nMUto":515346510355,"VMzT6UDW52Ok":757286899415,"KLAlaxOXBqas":4668215334,"km7RE2B1tJ8H":544038146770,"A33y7QdDeqIb":982330079377,"POlYQ83DoYgG":68469000168,"bvU4mNzHnI6t":579296933794,"UjuBP07rT185":754329708024,"HhOzEX7w4z5K":103700346805,"Wda04HfN4GIL":143163894395,"fjbk9gtemdnk":582803984086,"O3Fmn3eKM2GN":989186629623,"m9ssKnF8uJYh":570481237015,"1CTsXY3fesvq":305678572139,"AQ15v1V67KIr":188655107448,"lHXhC2yFIg47":372408583176,"622dmJvH49b0":860564680072,"GxBCX4aElyHP":61648538712,"cA5nHa6TS7a7":875623717192,"lLvx3uTDqynr":171972997480,"LnyVDPrDLcJZ":727442690329,"BQLo0XTwEw7n":991516711205,"gCMLOuqhN9tt":107804040126,"1DCO8iHPbdM2":422942872799,"NXCU6MExbo0g":827606095508,"Rx0XYXjlGeQ9":804089993441,"dOF187ku7ATY":773895935425,"sDpfjbtJYRp3":373874345975,"yyDHAUXS5CTb":329011041774,"9UHS0LcyfTwS":621807674156,"tx2wm9xsfIp7":853451966742,"zUe0kcbHBCBN":227926718006,"1IYPi3bCSCNK":223387669951,"D79dIGIspjKR":508643690541,"bTwXSsPPq5Ka":982270839832,"IwYnGjqlrEvE":159449582616,"hm7sePgYtkgW":389389126040,"GihxIpvYcboG":50168851811,"SZ44F0fL7eEI":284354504858,"SWiXptBa13E6":310585406075,"JD1YWPqlzLLD":861071277573,"gmdeM1qj8hFR":689456629085,"q4S4pfxQs6CS":515493906321,"WUWHK77Xp74H":568990697545,"V6bHRfDchXP7":623695817729,"L9mmoHrfOjMd":881615424247,"4awnUrI1mlne":39552987885,"n0BqhLDQkWWe":427237857875,"EjBR40Fn58FJ":575621319304,"h8mBb2RfIjJR":531456951692,"0vnOu6uONZox":639056473339,"cDZXQBZ09Nnk":976248796086,"AMkV8pIyQtbQ":24651480415,"wNrkXodpYxmO":484653848265,"ySdON7SSKq6k":778871859099,"ZidWvsq6G3fA":191494180947,"gMFuNDyMjuKy":411071155119,"sABn0FaPmgnM":808558008854,"JktooW77w8Rp":450576081854,"NkROuwBWNnN9":693263045955,"BofO904ABlJS":604191978492,"P1FFzc8pgqMO":557620638596,"wpGyimxlpfZ0":87454037918,"Ay1kSNTqJ6VI":694211417489,"6NBp7auv7hj9":522970976719,"bbs6NOElJ8tn":67930341021,"yyurQBahD5MA":90222221594,"vSK3QGEyhuK8":899492875321,"UXr6BnreoRl2":250696271945,"QBkBuljbQTv4":733473934310,"AM8oekrzYZfH":782826648615,"HvOpRPuvF3V8":98786334278,"GYd1Sc6xAv6j":14161632355,"wRZie2uS4mzh":285742709440,"ZTZgZ83219Dm":186457471742,"wRjUKpoYgoRJ":388143463427,"bxjTPFkwaffx":354035383733,"7cXl0Qhm7o7Z":923967221342,"CYhkrC8bCjtu":528049051800,"7wJ2Y23NFW4p":272995527275,"8D9QVMNoQZ3S":20990583408,"Kmnbw8uMnGZB":159602204523,"X75GGXPEpGPL":324118131129,"QUye9DPss9sI":240479176210,"2n103ERLmYJv":641790107760,"iPtyApcfOlep":398569597942,"SYnjg4nxuaZP":362772217012,"BjTTlzi0aBJA":214366846260,"5dldi7RnCneD":862267715999,"euU01AKaqaYp":225410283796,"xXKTCX00rULs":89634096278,"ZtbEIuCsYih8":273761836525,"rPkCLELDjINp":303710972552,"0Zt4pUSOErBt":569754171320,"4QMvbMwAtSB7":629822508967,"hQdhvbaphZ18":763611997493,"UvtvBcjAP5ZO":231299473295,"AgvRQSmKEhXA":856294905735,"bwgy1jX3mgRI":300030633970,"dnBhBLo7o45c":607329599865,"16GPDIgQ77qX":757820980499,"a8TrzE9u4pPI":89775367348,"g6NRTrl31a49":818186379453,"DumnN4WNOKbm":430500338722,"u8Blm4BQgCMD":972556803324,"FHR24cjEkwT4":796946521888,"rgxHRG4KsacN":30754309306,"dZjhqh1hdTMk":981807589384,"IgZNcujP4tKI":19676260134,"nnQOKfcywwBy":615777978384,"kP3bKQnZdCSI":706742346628,"HYsD6nAuHP4N":280411982184,"3qAeMFHROh9z":548904516751,"7BjsFrwB1lry":345368650043,"EZ4w7QA9kbw7":391698647488,"bZ0F8cxJAu7I":437871694600,"uKVFschVQw7r":133694962364,"vSD2TQneVNDb":170979343373,"Aq49dr0qN0wF":574161615017,"2PH27HlitM0Y":508556104863,"EHBStcv0on96":845992134104,"6uu86SA6vFIK":163659458003,"NbTIr5y5Six9":37437253918,"ErEFTpX1PYmb":970454755816,"cP5kLOaJg0zG":971268060002,"eQgF7XMm8g8c":319878902821,"H1h0gb0lfE51":661511989487,"MCnr8qpGDTGp":872308351470,"wzjmE8MsDbUF":611627699680,"obRF2MBqBXVo":193626863982,"Q8FmnFcSMznN":4825863992,"Yd1Nf8kduqlc":967719365451,"KH86JS1yADHD":763323880186,"khDmMYzSueho":90755900493,"d9X0TWd6uRtG":507872673139,"kpkcYNg6YpBv":110309216766,"csnW1r4xmt4Q":836359873295,"NJUNMSAS3ZyO":619170256800,"Keok89n196Bv":966387858097,"OmQ4EeAqNE0h":804737431141,"3TW1PukhAbUy":804641868228,"Z33vAV1eF7di":673912087578,"NsAcHEGovcnw":750364050451,"2RxHhziquyD0":487321290599,"BX5KP2LW8KLQ":278591608757,"2OK9N12ioHeI":208685596388,"WX3S96ITlRgG":990204212518,"OQVBeuCMnr2s":609512133503,"0xJY4HhxMNUW":604179679715,"6zwUCqgDxWES":866802973143,"5aoY0x8B9ES6":705509302544,"j3ZLrhQK745k":56863325186,"TMXcUd7z6gWC":74905655186,"VNA7WUXbaXRd":173853847080,"fDWlsJmXNipr":417182008430,"DuYgEuWCBxjO":234205207839,"BB2fcW69p6sx":788143339480,"LTyqgr1Ef26i":111821811073,"4ZWgVAhkXPg9":790872344504,"KabYpB7Pg0G8":98918190560,"zI02cKKUyxh9":741030767515,"U2pgpf5r73sF":613111164780,"M4nozGznoSg0":742863131902,"oApC5ff1f3Yw":748132254201,"BQgrFNsDsl4l":197615184580,"jr9HcaiHYYGV":208522246504,"cvq4N7X8VxzO":325810335862,"3hCU38LE7t82":325054770170,"YTfYiVNAx4yJ":660562927583,"ex4HPjRufemt":312997067245,"JCQ3wrOzn4zJ":518222734284,"Z58USKZzSlBV":127022253099,"F1jNz7EZuo98":953864595250,"m69EKlSyjnDn":577641967615,"319dJqQ1StaZ":512059135869,"4691Re0r1SKZ":591205626363,"Dr81djee1W6m":639401240651,"STN8P0VxlFMa":297669817122,"wjGGp3Y69FkR":846229665569,"mvLmMmP5O1mp":395242436173,"cYFjx61M8kt9":859403242590,"UwoTPOCdZeYQ":295488409312,"NhIfShu5nRtt":535859468993,"0kuQl47kyrbQ":730101884079,"n3DWUwzwurFp":385905168436,"7cb8dEEURnzg":750709006786,"tiDBN6TmedbH":345565783820,"bbsIz0ExR5xX":74470914078,"aUYS7kRCEjfb":362832494143,"uJ4zWBAPZwcA":445100726752,"QGKazAqJ2R9h":481024532086,"DB3Czs9TfsFA":434166884341,"gzymEE2rKnOT":446986356159,"WhY4xRl4cWVV":190860040909,"AV3ajBFwNItG":672034915539,"xCDywQpztqy3":819489849823,"sBGGzQly7kaJ":654492428083,"Pmb67hpJXyTZ":600087288031,"vNpd8Jw6ugxS":534830187698,"35Ywn19Zhlys":219779741008,"Qlw33pv0xQNh":980826882623,"cFx1zPmxL0pu":611383230414,"9Uzi1Y0MdwLe":831789754077,"CsYlKGCeEAzm":2423025461,"Fsy6fvhb7qPu":595639728046,"LDlYgFJqmWEG":202965709567,"23o2pbDjcnIT":822162776154,"gsbbyyuwfQQg":388929482333,"FQbISm4psIFX":956327333256,"IwyF7wxrjzTp":987620254092,"TD2nc4BKFRA3":651919305034,"FTdUBy4x7MLI":145740994268,"9A4QLMw65O5v":950518563072,"k3CdeBVIUwqh":615041890070,"9BIZgI66nUJ3":299209035343,"TXEqd3eYAw51":658020126338,"G9jKn0gmzOhS":348773792076,"0lJnpK8yyM9Z":503470074352,"d2ayhh9gcfTw":108003315721,"4ajBXdpEI1sX":931301918181,"QaSwE61vQblS":574803759214,"h0DKEO3ZMPFV":926889179898,"C5OyMnwNgg1l":160652731598,"ELulQoCndhrK":297941334904,"57oOsoGRVbz7":319501462940,"liJvfkVYj957":623193425449,"qiDa1nMOBtjY":156810486230,"N785qwvhUIUd":260179162503,"9dbpQbBZrf1O":453064811633,"00w2wIonLIUu":899232579546,"eZz6ZLVCp5VN":779572596367,"aW7mQ4Ew34pb":362407445433,"xF5qCmRy6FRN":886760264955,"VkKelQW7JoId":300886580386,"x5KmqC6LdcVT":823806711682,"7tgUwu0Lc542":658759444379,"5qRJCbnNjJSa":182130988062,"njt9BmmFR1zQ":418575693706,"bsjHT412Cs3R":910774047155,"pkQ5ArfFptBI":559783279753,"1S5cLDrenymf":829978105351,"8Iybgx2ioc5u":345843224932,"vQgI31omJQsr":401745402180,"WqNVf1EKCjm2":615839539834,"GmyzjtMNUhDm":85277394427,"edzAe8WIOr6d":643995683652,"EDpxVedAIskf":267067842798,"PukRgzjyqBBb":742883251884,"iwf0lRpz67LN":361271819562,"IEhEvlJKAQi0":375180305080,"mq63MmY90Qgt":202282716594,"IUCBukvsKf9k":117582312787,"Axbsv4nfY7fW":74719116534,"TwRdIJBxonT2":324425560240,"06eNsCspNRgb":96677269177,"4N3RxtkIzUK9":980817522522,"Hk07OekG4aSw":449826537831,"DldsF2WRuUOF":48346709958,"WUJZdzOlxwaG":505110107940,"eggD6rrHUm8P":817858073320,"6LfebtlbxkUv":125487261325,"HTtPQtTbHYwA":979102799112,"T5TWuSLb6UnA":359815436049,"VZfZkMxfJNmy":491625050574,"YV2GJmEbXVGk":198929355293,"1vtMI5x5b6YC":344177510462,"l3LPovKpV0tK":508888205997,"I0eKJmsWOUfY":416262532134,"e6eMw5mJQXBt":256344778902,"VmU24axMxFxZ":394772499523,"gKEvL9SGKfwX":852879951994,"qk3tac5Mfp5D":621333451348,"qbguo3IU0lD4":829321688351,"tJ7nzK4eSG9X":605455316130,"w2UL8NtZ3w4B":738797711542,"ARORh6VRj5Mo":627763973516,"9vQPP01u0IGE":624631609651,"lYauojWfyo2t":198239314813,"LEQrTHJi5YRR":769532417918,"MXqfDB1TBSzb":485123102792,"9BXrMfwVx5rV":390559030725,"TxhAGsW9L4dv":56525925814,"wlQ9lVfqUTnT":708062963707,"afS1NxbxXSXj":817286879896,"hsMNglTr3ZZm":465402504698,"8GbAArfxoAkj":591131194682,"R4m33R0qGJPA":414528274905,"f8hgnZ2q4MKC":317053392175,"3HkftKBqr3np":338614093901,"SekoseDO5TgJ":774544234988,"QdY0WZin62ZF":594986556331,"Yb5poKpLTSE9":527493650938,"RRBUhwmOrjU3":690814714085,"CrZG0YvJAA14":438226664209,"nDJ8eCWXxra8":836433665660,"l89qCPfnD29e":183311715168,"3kolo58SByRt":279269918668,"KvSDeaznRJzl":140966596922,"nKgDOUDTbj4O":229781995446,"mvW4iRP4gioH":508671489287,"iluND6iDt5JE":824791076196,"XPRRB9DmZgVr":198532157610,"YiWzzUve6rNC":664285392859,"NjWTur6DWqyB":296880939357,"Np5KSyG2jPtE":579699163461,"Z2rVtyyYuyCQ":739359770798,"W5MoOwiNDw6r":808233598056,"IsfgDWbwraBG":800452200618,"KOuhVRiMFlDZ":851665640127,"Rx61Lgov0vel":734370506433,"GwCXnUg9YxTd":762507172128,"bhmYPw7EWjRr":799704098392,"TcMpuVVICTLV":945075587754,"nphHlGrh12rs":98210919037,"SJoR1aNbRVqs":291721023376,"kZS1J5SIpkZX":423417843812,"PAYe0FEHyVFI":947809382521,"yUeFAdmhyu7b":712797194777,"Iyu6R1yyenqe":6567532063,"f9jLCQGPuNiI":549685407714,"LNZjjUYLleWZ":891255517233,"GcLL95n4x1sO":849867439419,"spEFGBsrx3FU":102834698559,"KjIefLMUkuND":276774447447,"lrgyONIYDqiC":724394060430,"AeIi6DHNWvlv":492282381479,"q8tYuQSVfUUr":250542382570,"uvutrDo8PXpb":12716918919,"g99JWdI81Yzg":898706726436,"Ztmab5JrHpii":462684853338,"bw5zp5GrkGsl":589443176500,"neQcSQhO80NX":323289354151,"gNDo8qk4vRLA":510727358114,"b8GVt8KMPx1C":598874545803,"7mtMvgiQQV1L":988258136780,"oFoBMecN1JTH":322645441981,"IR2pyTRZGlGH":805813553504,"etpcs2GtEbMg":269595004685,"RDVbY4CFJs8r":946215731479,"0QUVbs5PaE5A":256582920750,"rxsbWjFCORDr":392562006021,"QAGj7DpxPUJY":936269538768,"hOCYePZ2Gz7W":807617426459,"D7fduchaZnZs":927194143309,"EbWhiuLjrrRj":430740442898,"8dXdYhZzDosI":428399830410,"R5K8tQXlDfci":772915228753,"DvVbnhen2qcM":88204584308,"cf4dA2re6mRh":564160220600,"5NQ5f10pILT6":191595351255,"9koHHcUSY1OQ":587111418712,"z4FvYKEMXD4K":196843078614,"pr7rSW5lGLYP":142603292225,"riJf44T8Ius9":762741335948,"rn5MvZK6hoNT":803049776566,"GMyyG4HO7fD9":402226521407,"cLxCbePgbHz2":779967062990,"9COBrNHX4tnB":518447528289,"R7TjsoueAuaN":931964897527,"Jj4xa7NY9QGX":863422554096,"mwn2HYdShald":648150409428,"XVLaZMQmvVeF":147230324107,"MRdEzg2L4CJh":156398147741,"tzVcBs9BMAdX":751590089013,"EAxJWSmQKPq3":251110388355,"kf2u85wd1n3L":807274997336,"B7JZEayIpFeh":109142564388,"DDKFG1GfRVXq":753941956023,"c4eZVzT5oPoa":568295095697,"7eoSlw0Y0NY4":646538163741,"8AXkQDpvQAGE":225631299491,"l5wWZ8Bgoru6":467433907094,"dn04p3zcpJkd":771452282579,"XEfrvm9Qxrwq":167819757156,"PUizIG4J7jQC":114767525677,"IBITuKBo2fhk":21020788730,"DtNR2C3TXjyz":520076522079,"ggC1wBDginFy":946922459138,"nZRHlUUxsoES":546851957544,"PnZAg2sncl2B":268951919753,"hTT2IGWgBiMM":604247883056,"yZR3l0mcfshL":355169481119,"3TtQsGgiNctJ":320520034767,"lexPekjZw8zD":679001340752,"FEV1RKWRIaZr":11190105448,"Kbm70DxR18E4":576517382583,"etXaodzAXnZS":796188412645,"fdRhUd6Odxbx":312503398800,"PneIpw7cBet2":263852395994,"zfAyg83kSPWc":306527040419,"CO2pGORIzqWl":209431360125,"lANUOjJxScXU":26259284430,"DyZoLLRrRCxV":384967380972,"ryXp4s9Q1KCN":543036962543,"DbHISQq21Ta5":11497795600,"PIoxt4wrBq6j":947432137306,"On16CrDo7oEu":736974934439,"Zcdw0g7mj2ie":394488011503,"f6vDrBGB86pu":845251190882,"RBH7ZyDrPCjy":996349814824,"PpWltaB6eAlI":69764297823,"Nj5XCmeOnVnd":576001739675,"waXHTiAn64bo":540154775774,"NRpKgvKNf4t1":186727073423,"HfaY7fBIVa4K":886872655305,"oipOtTMtPBEF":10214584956,"flfPKrGNIRdz":496839805733,"OqWV8xS5tXM4":642583696770,"tipeNpQqrDLr":947176778272,"I5KH5DSuoMqz":341532056676,"L1rVxdkaiA93":220414580897,"F8txZBQ0wrZe":557757322022,"wa8wENW4FUR3":72009249927,"vtfVCpca5hod":594025330408,"3xI9SaAljR8t":177733530150,"t3MAwSZDCbQ0":778896152593,"Yu9Y3QJxc8go":620993527596,"JUAULbB9Kdyc":761941217815,"9MTxbMJcIJVE":523235894909,"HgRz2PCetYLT":816793846599,"p2NitUpdbkdj":152878342192,"Y16qcucH3nwE":106557330409,"5E7vVGnQ3c9Y":125182650207,"uJz7zTOv6aDv":968878006041,"fWUJRiub4H7R":964381289883,"w2vZXwdow6m3":15510701367,"g2aduSOvK7pR":119689702851,"polsiWqFD4kQ":782881715750,"mMrBNGEt9nWs":984829673983,"V1DVd8pa36pm":677432179694,"E7Q4k9LmQgIn":301731398726,"MbVo8Mp7rV9N":395349417079,"VTBUxCX57z65":938132275817,"fn6XM035braP":737159360544,"JHAvf036bB1f":192213916016,"uUGIWSFItYWH":784076034662,"nBGC9ICnFw8E":493343325506,"DfY1l2VxQAKY":591994732400,"lFZ2eRX0fiSu":605859570910,"EPMUiYPfvJE9":353354007478,"VcZJHb1CDrdh":818519477403,"uehTWMUtRfZe":308350930574,"3o7z7A1GuVFP":620609806940,"2goW6j5qpZfm":649656222396,"PPLwEIx9aL3b":127890712454,"QrB45iCJDoWP":828552114259,"8I21p6C1ON5Z":557436307324,"LEal2kloZhKL":370934059606,"HosOztZ68YZf":964859749629,"Bk3QdNHPTUXX":880230253665,"sC76dwW4vtFc":544257021888,"2EgD2xY7FJ4y":165468783921,"CaE0zaImEM9p":167325948385,"awK84KrriC1R":890043551543,"pIfZca9nYbqz":647081341629,"50ho9i7W6esH":497982119206,"P3h9zZS6SIR1":296444041305,"ngnKsfjFZ3m2":154311108776,"8AZV9RXbdqdo":853481185948,"62f8LShSazXu":533512248068,"4VBv0LuBURnI":376616114109,"oBIOX5INHuq8":147739366070,"rgvgO4TIUeWf":842856992065,"IQPJQ87BNn5I":110046988560,"7810Yr74m3A4":510165506614,"ybyop0cjaWwn":779944679745,"Yt6CRF5EtzWl":299825032144,"DaIo2bPvcbRR":155996121396,"PCJxfqGAuNwf":432063950456,"gKYHHGjF8kML":620309381843,"UT2zsCdBZcWZ":66266868964,"96r4upmou7bx":233206908449,"IFy6s2C2yuII":304021599439,"1djHrrkYkoI2":943388023024,"YCTF0pR2FcWG":587130599443,"XLd1Nr2AoN9K":601349922804,"T1zpKHvBvTwN":509999239571,"4dZJ6ncn0gSJ":829978749578,"y2d07UEDpAis":38139112316,"ldzT2leh9A3x":485442483704,"ysWnLyBFGgwU":304212963948,"dODeTmbMWCFQ":321082002213,"37qCCeycSKUU":592477803711,"93GoLiyr10OW":755063714648,"DlMmQK2abFf5":54952507804,"4vYD9RjopelU":200097360263,"xu4ICnF5zxSm":969726161972,"E6GFWOURyYdE":785954081384,"0HfkHdK5moRH":666752004641,"jHn60dQCNTgN":984279937412,"0565xFWWBBhs":66549030815,"kiiNmQZjmlzN":730926147019,"A5eThToYq0mX":889667176012,"reP1xi3iA1IH":322529803242,"G5hZpdimOFRs":627148291728,"jXMhAOrcwbHu":273151593961,"7ubAjZwME00k":279411436244,"10C7DkTrmyoY":147298720921,"Aiv12DxTyTZo":64448696027,"d4SqzbFT3WEu":166276328880,"GH21Sdum30e2":911610956475,"oDij0jE2Uh6p":995453991378,"8S4a8cGyhpqe":527030047808,"ws7CaHOnL7Mh":717805995319,"ZvEvYkP6Cp8F":16200236436,"GDpsw9lRvKlI":447985663451,"OEFs7AAmE1e4":415420824348,"LZXsJzAogNzv":376806170249,"wsjZGrIosxt8":179005812912,"oBq7MVbXMx5R":624513819127,"h2NIHoiRddyA":741049680295,"a2a6zqB2ehVX":132039310633,"m0h7fm2Oaogp":520749754443,"weMCjwETfgSV":144632259960,"z6Y173yByn2B":668335029822,"yJanbXvIsd78":280849146905,"yGTg8Wnl2pq1":680782575923,"1au4Vub0MaAW":951523374300,"sdSs4K1cg2gi":602936318706,"HND8YuuwDkN3":653186417342,"77HL7J6Wt2vj":320008802514,"qFcWEoionZAR":904759860704,"R8hPWhVhwmQE":791457183258,"uPvprpUF2wKf":674074501864,"coP0lcdQ2fTs":687992173454,"QJYd9lf9bIiF":796244352241,"M84YZDrvQDzo":153249887370,"347o7qvJMmdU":376800319998,"c4tX0KVt9Ee3":540065738319,"wfjyZaxJjJa1":910063422414,"rPPJbEJSNyFz":168690608781,"mjTmaagQDZ5A":517342554734,"i3UJjFfLmIJU":961365103338,"6o55yMYQIsBv":477631925496,"tFouh6bayI8d":428075339394,"3JKEcUrn450G":387684056525,"goZmCv8gIa57":449918795255,"3vUDCtPFzOh0":456832696263,"VmhKs2zYquBZ":806636613828,"BvvFP53CUHz0":390856891381,"AGEDfJAVZQFA":598324970897,"i8wjJSkrKKzt":632397219475,"UzxKNnRK6Pzx":426237096271,"VEcvTMxpFFuG":410004725535,"ehYDdMVzl0z8":763507534937,"su70p1fuMJ0O":660373746990,"f56fpOjfrrdk":524167220612,"H2tdb67DtYf6":12287492008,"18mQ2AW3ZsVb":442498663565,"hYnxE9mVB3HM":497380732947,"8eai1ZKWgKZm":140958276950,"h3TYE7LuaBrc":952093441213,"Jiqap7IHGlPt":791966436835,"ZsWCCIupkFm4":772559326562,"YqGBwk8wS3ht":998470912474,"MV98w3US0pt5":502592408487,"I3MbuxUrWWvd":960471040949,"8JX1wy0Uw1az":307573442809,"jnyo3lNq7mW2":920703300238,"Sqc4qBwc93On":630590384633,"EQOWSDTl9xEw":299362548813,"hVbR6AjajuAP":985842584296,"LDd1nv6n3xdG":209717230790,"8UykUZAF2iJi":33464156218,"Jtlr4Qrmwvwe":320404241840,"FN1NXyV9AmJ7":937763962144,"QZ42iinL4wgq":270527060708,"CQZoEc3H5f9A":318093578360,"FSNlsQh2M7yy":706962900361,"Jd4oX17xWRG7":498698368749,"GDb7JnHVGslL":67577654583,"Z55ovzj1OiuF":310925113976,"x6wIOQwoOt8i":275782116466,"25zrirHgbKCJ":953381745074,"iQwQsot6TwZm":40258906312,"3q8po1Pytie2":788736071805,"vTcfrpycx4bf":995530469557,"aE1IIdMYBq58":955303843511,"1PBo37zrtCsI":510435901701,"mjyfmjc6uFiy":405335369646,"gvD9mVaXjtGb":715223171065,"X80uxwf9T5Zj":235593179643,"mj3CcD6FyHHZ":869626096256,"J1zO8PvCn4In":818626490055,"7oRuDM5UOED2":4016825834,"0nv2q72VoqPS":434279742853,"yc7G3DYEBBed":369049040651,"Fm69OFxrTMhx":172684380269,"rjxFbYdTLWyt":375628333486,"wykKLOgrbqgY":39891366530,"6I8qgL5N2z6w":335480133561,"mJWfEYHfnucJ":429156860223,"ua6g7ryU4uYe":236981210872,"zxfKj9VWr2Kc":425461636434,"sSunsGnxMENk":169375151335,"XeV2JzwGCot9":531022333848,"7Bv4gVXdDWrw":796688718897,"y9ja9jguiXKk":484950154704,"LVK2os5PaBaz":21580402450,"55JH5zUv1myr":677365486878,"Y4mHZpWD3KwD":584035979606,"8diH9BOpDd1b":228029042378,"PEAekw6WDUQQ":398285201282,"5LrCMYDQl54E":863201891991,"4De3579LBDPX":270579596474,"4wG22c0gae2j":494460624041,"V2XOyKd8c65v":850448013064,"5YYnbQnkSKDe":34991192811,"Qt5oZpvDHgyH":250617609714,"zoqPYeOk0FSo":836612796775,"BLKvyqaM8rDB":323807714994,"lb7BzAiaLv4N":654111808026,"9pxw8YsSR46g":35391551090,"7lvkjOwhaBAW":308073988549,"romv8JHw4LRS":396704303455,"MyWFDu8Ty1TS":471083838153,"HjI0TJ7Q2BLc":593548888192,"0g8usflNcUcb":859781752819,"YH2naCckngHe":324086148084,"GF84Ova2uF2q":67632755094,"wjP0iqvZatbW":990050578252,"OhOj2WbLBeUM":638051769344,"VfRJyTyrnrgr":658414158985,"QA29uErTgKiV":913178356463,"7kyYQ3FrimG4":320684742715,"cDjerNEjrsb2":49904595952,"3dB59rWuJ6jZ":685382141241,"K4vYi7wmtTLm":974942526082,"c18stXw60g5t":686211557048,"Pi56Kw8BwLiZ":627886031296,"JfmjaSVONn7t":545507730070,"RGnohVjsoat7":309858864742,"fXq6q8NnFlHD":502981727054,"6xvycqsTvviY":52335956838,"nqIliI7pJ1Nt":627418175954,"QmnLj0EqbNch":404616889515,"EEmWNe8X82cq":759204269056,"PHbaVvpfzy9l":738298075081,"NhFRXCXZpUFD":997883851017,"ANJFEOGjvpUz":609515086047,"ryQ4BstDW9RA":11591244961,"3J5aZYUQ7atM":635356087616,"sD37pn2WR2O5":645624310847,"jRsVAn1LmFKV":625339992244,"sw7P2GFitzbV":886540409663,"GuKJJR4FXq5I":500682942171,"b8gyAdVdZLK3":365282918246,"z420W6bLBpmf":617670209696,"vpA4eGp1x9h3":2813987843,"wpuAb9jND0gE":2844770799,"l7DJc1z8l1ZE":251722053007,"ks3xlgmqm8uT":844020906874,"qu40SoSQUSzi":19636082739,"G6Tn83hvWHFO":278285539800,"FH9Keq7j7Llo":91587037523,"eCHWVBxNfLNp":758122198945,"GE6LipOWGTnY":194175496907,"2ZLHTIsx9sSV":615546051596,"y89YTIScBM42":612480788576,"77obO6IgQjr6":859284678113,"DX7gcuQ0T07y":249169209964,"W01dLURLrHsW":765140503557,"IMhbPx50QGq9":636292593305,"mLAK6RH4E5OI":14040716940,"EbqJQLpJ7Lqd":158412811396,"AJP2Ue81Gf2v":931267319972,"Lsx2NPMbLMXR":605620029961,"RmE39MQEo9m1":398546722605,"aKUUxplpLkuy":693054799841,"nWMNnvM975bP":264721374837,"OVY0utdSr7tC":986665925056,"9vaWj6Wyrp70":405942224942,"mSwZJQTBc8HM":468365646274,"AsarMmDqR5AD":559165222483,"NCBK1x3wJ4J6":19775243363,"lCoHNkgsJQj8":890770101452,"8CnYpQjBLWdp":23767249311,"DBFfggGeKBHb":279321201369,"bVmtun2RxWEr":984004234686,"y0LDjIX0ydZY":148911946070,"ngdS7GH7S5dZ":993910616907,"rwpiKrmZXNlh":322909721306,"cjy7EIGo1G4n":678912107776,"SpCXjg6seZ9V":320044298801,"4Qc2O5C1Keuz":62596467306,"6hBUwt7TWKgI":243661871491,"imJ58Eg5800i":671532514985,"S3z7eBUoazaw":291664384640,"0d8aaHmhn93l":3162172322,"nMqUWp7QhVOo":73963643625,"C9iHZhbDNGq5":512476004678,"WbMiG425vmQg":378935465566,"0PLRZWZ6y9qU":35794979844,"mS3AVfObEBhU":891043884913,"Rg5PQBwJPtlh":431603785886,"wp8HDvPUmxHs":738845703831,"95mC8vGYLaIZ":368067980044,"E7tvIaKgAB3M":397571923787,"SNtm5VVmK4NB":610204183197,"xBFSfwVbaz75":704930647013,"eHQGlcvqmbNC":67493605913,"DkcI7UY0XudI":58451245083,"82GqcRdZUSRw":301657956422,"R73G5iUCiOgg":879564217324,"bB252MtZx2lD":815979472575,"pkc9kOxCr5ZX":284098887780,"WO83vZhl3URY":433733808361,"NmboVMEQDBG7":969945002322,"vapP3CVCHX0o":43508240387,"wgbGOFRo5X1T":667693075369,"IKufWIeDMv51":636482451541,"JKUNyCCU4awV":921775236647,"Cz9ZQZjtEyMm":300446028601,"eh7JPRmUx1KK":595310134849,"qbkZWwsxClXk":496533188482,"j5jdSa3mXcL9":745606375979,"UdPEbV1tyY9h":953465940220,"mzxPQ4pPFzZ9":677056285775,"VL0DoDR1Z52V":61521317828,"2r3VhTwnkCYx":587130045234,"UBdaZgQNsIs3":229443592145,"0cPNKPXrnJvL":725291564771,"fnKzVYTZNFpK":605362258,"sydqWbKQ32vW":749777294283,"En36GGupoyLD":915776078050,"zt0QB74i9KTY":533063880302,"EQnf2jH0Sncr":759558581352,"Kx6kHYTyzZdw":778038683342,"vAWnznhDWpfR":342161604296,"zQuzHlZBjwLP":785427559858,"8M5qOvWPjYte":23084944059,"yAKAD23nIokb":559674532708,"51MOw5fkDaVt":342402937609,"QPut5sgIS6Us":795943556640,"qq7cSQwGjsJu":419689672611,"m6K0kT8fX9JM":132369192967,"LEKkaao7Nsjw":786180974183,"pYKptEHc8hd3":657096843361,"wrPTWCVgxZxM":454050682049,"2UNYspfotHyJ":856358355411,"YStxBnGnufbM":28512338885,"Wd4sAm4NMVGo":437130988559,"Is8WKNAct4Hw":549774067189,"r6B4gBOm66Et":720537398582,"kH8Jkoi1RHYl":932099976220,"2xBoVtfdWojQ":796750160529,"zFLnCgmLy1rc":964153275318,"PThh8tiwV7ou":656050826952,"jT6kPzeSeZfl":839065494573,"sMVyEvGoKizK":475989593645,"68s4VaA5F7Zl":839041169969,"e7eCwHCOJpVJ":671213507318,"hjuL71yuX2dR":859489013553,"5m7tM4GnBMYl":263641519757,"OBvbjhE8cx8h":867531540856,"dp1HzlkbKoop":758766122744,"eG09SCnwkntD":86833321486,"w7iCsKKln4TL":330872092920,"V8bIRGe8S4OI":325322443341,"G6ChinvRbwJI":123424448958,"rzM0XlOdU3eJ":753085139075,"wkbnQOwtPElM":431083736768,"XpYceSnxE6yp":881932228475,"9sApYyffrxEf":326171975648,"JBMo3OmD0JzF":635912203305,"RAwRLwHiXqWi":834763043386,"E32s0xgPe8RM":452051098972,"LGDtXn7OTy1U":234240178416,"CjQ66n8pXNAs":479991717017,"Ev57lqm2F6aF":282564840101,"aCrtcY07OyLm":381658362316,"tbzDSIcRRdyn":574340053538,"HKcnPhNt15CU":322988499933,"SXYRncGmGB8j":160212190823,"fphsELs3mZex":22983466871,"HXwcO7XmANvJ":308436933957,"Mzxewbi3Wdyf":805698482845,"REpqx7z99NIZ":617545845355,"7TJ0tAHqXOND":561429067116,"rgEok5ARSZR6":465046068083,"UZSDKGg9i1j4":542209494943,"sd4UKMHwzdzh":765195704764,"sfAfYaqgJrJf":447576112770,"D2X7fAfEk3oE":750883102502,"n3Q96eCRj8vR":892444917894,"II2vvBYrQ0w8":525848112060,"hy59eAHXWpbb":565095210719,"9McyYTu6Dlrx":433578114247,"MM2IggksOOz1":986021021305,"9KaIXLiJT9rv":790185091008,"2TKuPeXHMJE2":799074108769,"USez779DGdnA":176205952921,"fOlFvtY2CDCn":317260183685,"JfGik22LRrrl":816889950950,"m97gHCZ7CpzN":478559120107,"0VimrVoKECW0":874801102526,"iVs9V9C0TKc2":818914043026,"wuBtWVrbxXKn":336160116173,"fDy726ZZ0QKE":728033089028,"agK29bi2Bwju":38979666442,"1QTLxMf4Ziwl":613442903489,"QD7bRw30aYqC":719857752667,"AaM01iN4fdw8":926373478178,"kuEtJ07d33Ir":521138118987,"odey55sPK4jb":95767886785,"KLzYmkxVokvo":372774110411,"2X2zH6SJZh9C":437923806098,"vm80lvf1DwRE":315057158042,"gIFY3dk7LcGj":290382122188,"8CcYsZ5A8UZ8":992724099340,"JPYJyh8k3AXx":191042600586,"zeg8T5OKrI6t":479428856047,"8rEU5kwAJgLZ":934751186059,"TuxFzKTSCPLy":357199864923,"K3Lr8GaP9j6l":604325052036,"id102m3FbkoY":469252519717,"vILi8FDz4XQR":176923553716,"k17PH6TCEJnF":124855056127,"hTg79XVQ34zX":291193241103,"6XMXbInHPvBv":809616217899,"wJGfJaMygAzP":574500588471,"k039C2nMPIEW":364877633492,"wdxrrucLDEYr":815011279613,"WlYY4LW8V9yU":443867312544,"IPeqmGMcjhv4":157529893147,"8whzpY4TL2wf":998656606812,"sfjaAtWrzm3E":496276567365,"vSiGHxNRvl47":955444892617,"ZuEScNCgBT9b":368983650255,"T9FlxTgpHfp7":131898872407,"O3c684m7T76w":29210357364,"NxxzeHzbCoP1":464548344353,"Exzsn1eM3bAr":352084504814,"6FYHMjIX9B1U":24372256274,"Vqs2Wd7nngQE":326448800188,"fDsPgqt1SggR":627180851989,"KAB7QSAZJHp9":140461400327,"BHedPyUPqBAf":609947210313,"JUvy05uZN8sp":970828043498,"LrcTG0hpn7Ks":167903740666,"wVpwnGJ8FbWn":156146537189,"WAK4UVIRSlnH":487769325040,"E39WrWri6ER0":825260694448,"gz4igBavOUYT":375853571820,"4J6dw7iEEmhR":362286156431,"vEWgHk6brSrv":437858805264,"W7DyMsGTZAFG":60569717948,"mF59vH2tTncz":980159828641,"olayWsy6gGGN":624212387559,"n8MZ4bkpJJOT":223510208394,"iYAFBGH7adGT":428760732675,"h13vwnQiPfcX":181585942417,"RkP9PAyqu5yM":895150897188,"rAUjJmG5qpSj":510824602687,"4GUTDLwNdDlW":723666442704,"YjzscB1qbsmm":562979241262,"rGxgK9cYAFs8":541785937122,"H827X8BbkwU5":956020414452,"ppTaQrkPmmti":698100187246,"F21NOzXTDZWZ":911033638303,"H2OgPiOlBMaY":369024573797,"fNG7AllMyMSz":983347076535,"42DQQH7kk395":328552504036,"FgUHPaXC5h2I":49075653048,"GhoodZIeS1HD":439780991815,"P3eQu6OgiqVa":880909346512,"zqWz6GZ7ojak":866051249883,"OfNS4SEew6aj":268669786363,"ht8533bUOT0i":801137487016,"Vz5aLfEKSP5z":967190288663,"FOxdFtB5fJIZ":411502530207,"ELyIlJj4d4NH":50854181274,"E836vBOZUVal":61797305065,"5lzjIf3udjm2":172001264924,"VDtAW5MbBFEl":745774460674,"MMlwSfLa43VW":535725547313,"9LxSdQDfqweL":948745512520,"cPTttbQpezjt":536783992567,"dBMJlnVPs6Gg":770312095676,"igzxCFkiAWbu":636938418147,"aKdYaJQOhkq0":331445021447,"DiWaFNCJibgI":298456882190,"isZqKM3618rc":722256261731,"GUtY73EF7oZG":879862327882,"8eK7lT4NumiE":473898327954,"FTKWcJaV1nsC":142308828871,"kSl8TP3MtaFX":816107690316,"SGslEiWtRB4N":76365172763,"IYvh2gIzIZ8W":857863508048,"b83zTqUz3v8x":806626373362,"TtU9EpCGh3yL":790611886080,"q9drH5SiLUtw":206737905174,"XQtxDZ1Fs8Im":824647868960,"DNn4nTsZq7oK":934336125276,"qTw55tg7KhVD":573309544655,"IMJft2tHpUne":273331902355,"yqMFlCDZnDyD":775335304931,"nujad2qtBQ8D":615316162568,"5QyZCgw1K4wv":754923575533,"PSqJiBkv8Y6y":558937624029,"5utmd3eM5zb9":899742870287,"nVQ7gSg67zMJ":707302321683,"mw39NDGkio8y":329531468174,"UTbvaDy9NiTS":626352190774,"EsuuhFQGGeKe":651477951792,"m1G9dSwnbZy5":296959171127,"yexOS2eKvqwX":510896722700,"XYToHZF7gPk2":370295673378,"W0pkfIVCDAuX":536103532536,"OqSCRgNSrpVV":398844581295,"rUkZpxgbvJ08":326135260127,"M0YedFe83GnT":244147901087,"XIqYu7MpweCR":726608283361,"qFp5Qvf5XxYg":994894759104,"diPiamj1CadL":632815004498,"p9mn4vUjvsRT":604274170494,"jHW0vR5zG2g3":153878133044,"rxea4BfRf4Fx":8142441884,"W3gSBjNxV9ux":373063843700,"8ovLLJrMwSk6":990371860500,"YWItftwHSl7A":295260575056,"A0lgHr9sZDy8":464092533559,"k4VxXrJlHKy7":271205929986,"R5ML8GAZI7uw":609055518591,"VXdof1URtTKS":40432085983,"S1WMPUalCIdu":817111385324,"DoyaPjJmnlyy":695340721893,"YXTrjaj9sbc0":37556028342,"gk6CCjpQ9wjE":237886411128,"cRW6jH8suxsA":686055799650,"dMTZD3zPGxjV":460436613234,"prm8drGYBWh0":629082838136,"2QqiiXgAvtRy":875776227953,"csKe7jCE1lt7":990021772606,"zhI7ULaRGiSG":847056039874,"MonKCXYCeGIO":225265461999,"CXmAn9uNhdCy":1621396843,"q6WzNbAwoxhJ":48012103365,"CiOr3UAP4TLg":62992439199,"g2tSPuUFIisu":998002231343,"HBNlG2yBqJLq":915098639713,"VnO1T4A4LtPb":551650882048,"a2HkkJpYgaBf":534861295166,"8SBPVrc5xVBa":388018138477,"BqEdjEVg45HD":208454531637,"Zj21dfcrbYqO":565393449244,"gf0Yx20VrSHT":455101432953,"7f24QOQkCfaQ":239752263146,"t6sOw31hbU73":38140070560,"8RkjUfEdCeDA":22099610777,"2VcOgAcxgexw":428882906517,"47ZIeIsi8w6P":824591952074,"iP1SZ6ULU83H":621033355842,"EXcsquFGOH0m":971570595981,"9mMnz3L5l4vb":938576437209,"gH2Hb6a8cMrI":5702037864,"CVIvN0kJw6hJ":150169400978,"IyHEX4sLpv4u":924984299094,"6HCOloG3oKo0":334922939129,"iazV8znIuM0c":526669772806,"muWDoI8qerg8":335449835730,"aIr9nhzmuDX2":151106187492,"USBZED7CHH4R":240922819905,"CFlsqZ5TGkCI":742551226947,"hb91gHmmZrZS":286759887269,"5o5mlYt9kQji":955912051499,"YOaMVbHj7b8o":189414764890,"7yfzmcE8Nfyf":680866411738,"pcQfHoutxZUE":531165881804,"P6K6tW4yYgQw":456568429841,"NltFM1dUTuXY":466170197386,"IeaoIHnbNv3I":362970898663,"YCsgVwdePpvZ":432633642894,"9PtZp3TRiZNE":377246998661,"pvTs3H0iTWJ6":375587900476,"tO8829UoCEFi":657489681726,"lYBeZ8rmFkok":645687814529,"Bl289Q5Awcsk":483601826767,"MboWHO8F3Cll":769929255420,"rZLRrw8j1wYi":750181905269,"REfhaI5ODRUW":221280654114,"85hqCl14AoQn":783857270259,"7aecW0TZmPZ1":197920808804,"OEA5GtZtejBx":640596453426,"SV6n6KnQsca2":327801268267,"UCKRQ7hgiiw9":874127796102,"aQJVLOGg4L1N":500231535046,"oHYrCge03oRr":865166936471,"qD1SbNWUPY3n":481926479596,"BRxvGU6YozeO":52807616632,"B8NPzH4EXatd":221625500692,"nyDqGK3L3gcd":15157398092,"6zs4Qz0KNoe0":892795612572,"94WQBbYIqyrs":323675736028,"XOR8nR7dfIcC":728874917546,"NQfBVeVikqa1":36079901652,"FS8i6eRXeA6H":505561035187,"9b1XDcShizB3":726077675920,"ojafHSjNtznh":809913071093,"BhD6BfgcVNor":448907861142,"j2Zt7YOtdznH":671564540968,"GL8B5CmC0z18":388258343776,"X4LaJ63QsAy9":35937416237,"clvCim3BiW9x":466845674385,"6vrySvGVZyt0":369535199818,"tSbeTklHSQsL":363810321366,"r9goYPGG5iHh":754025986,"wa6BsZZXQEiA":758495742221,"xI98aGBnIO4X":629814117897,"gA3JgyYbURhP":395350463341,"aH805kMhvkY8":442793691855,"cY3H7pZOrt4n":339637702082,"L3c7moeNdaT0":733870688381,"zccTg1bvaCew":55923930213,"v7xhd1dl01an":233441674517,"NI5Xx0ElOsmG":931290494879,"V8uCTJakiLVC":378496315254,"grvowBlco1vK":472999999545,"JAeMXU68Rlna":499730795164,"uMXFLeB0vSOd":809850231368,"zEwPFCUaxVO6":918244873040,"WOAoW3N5PKyH":352540057776,"2Uh1eRyER8Ye":145928743656,"WqnKIwDyyLsm":292187810982,"lxzq3ffePyWG":426888617535,"M80R6z6JwaXr":499890222080,"8fiTzqQD9XJl":232683243813,"NkusDKSnh4Ed":657291896808,"K9a7bm2ED3by":469878200953,"2y0ATQbGQhPi":523306997369,"b8bsSH8nWJtY":373112199332,"aENpxCvrsBqk":749815910666,"T2SCRo2eLiwn":29788286141,"gmA5uZYBtmzN":731623977229,"7aUpiUNZkd9x":410885861946,"GbUvJt09Pc8U":201573958813,"6F2yNHQFBTKC":246560554214,"ITryUg4Yufqp":198360969232,"bzSq85bqBF9l":194769121390,"IdsTQKVJpdmf":570006234294,"hZkLB923QySO":744275665976,"hkCwZ8XAAxGn":215022920778,"98Mx6Yk3o6i9":585286476464,"lI8Sy1cdfiAm":177487994688,"5OMwbS3AjIRf":131879536823,"2UdAWPHkWnLw":208395825498,"wEDdapbEkdVl":234794265971,"Wz2EHIjhmrOO":187385651936,"WnbLT4xwZRcq":481840862225,"ZlkhcyWEhMm1":634039339518,"vTUplAUeKobW":272884994479,"nozAuK8ciTTG":633265835200,"7uoB81WtZIXD":741595589702,"RjwIysasU2Mk":441241491149,"KAxSCv0BFhq8":224636068188,"Lsn5LWzGUWRF":808483098312,"9673Iy12o5q5":312527925224,"4sB8npPQaEYF":89464075720,"r0pjiKYi11PW":506737793112,"ZIYBa5h6WgFj":757353036947,"sQUCgrJs3Zg5":241925949165,"kVzzO3pfOHmT":17890192212,"5QFf2RPfMOhT":761139258291,"aAbIAXmbvmkg":27749585087,"hf9i6N9v6L1O":300481193855,"KpxSNkbmvWUE":241669180172,"jOPSgJx7uPGL":20904063438,"4A4zVKyRDKyt":37002813502,"bpHMMryE8r5w":48360838301,"astBVccKNN7j":660535342004,"w3HXrEx0L4BP":460999968251,"4ItbKnPSBspD":898070516957,"0Jzj5e1yMjhL":715594245563,"fXyfAkqzr0tn":511060409035,"jIVAoWSSE9Rh":842996731962,"wnS7xnqH1Zx7":753801758383,"5JNdra8nDfkV":462135998417,"m0v9qPinDNpx":471953502875,"YKrtuD0si6Pc":811345453783,"PAOV7gkRCNQx":66020176042,"fSOSAo4CjtsM":322232648740,"NfrZgVfCWORF":230217657820,"J7bCxuQoNk7b":746896556560,"dh3jElEbiWrG":449932964634,"ZifPYWTvlR7M":206788279344,"M2UZwVrr2gwY":614588708355,"MtBCR5VeXrgI":403361269618,"t0mUGzNnREvv":513330986236,"W3IU3iDvhplL":275953445790,"cv8oopHFGtZq":205950854477,"efxz0qlWaSj5":535930608776,"RXDk8uNcBk8L":502019576669,"0Qdb6NVSX92R":431908299344,"wHkURdR669W1":199613501506,"orMaatvPucej":604428927047,"199HkpsKAc70":677624046305,"XgRIAgovJvvI":327244645655,"rjgsZzXDMeLu":582326978736,"YHniP8Ruxl1i":131862300396,"GnUFrjZ1WuoM":309890203855,"hPXHMchlIM46":443961389896,"fGANDSthBSMH":523465856141,"eH9XdUjBgoQA":562420822375,"6LcacDL7NZG9":656164870373,"HvSLwnLRx8TQ":989392380897,"aUMGC5RboUMB":356638656668,"ZNB5iVG7yAz9":309650105256,"uqw4DjGfue0h":259006841935,"SnSdiTdTUD7B":729162757663,"eJgkY9yjuJ59":659632640551,"EKcYPMk2hdTp":605539045718,"WPBfz0i4Askj":498683111607,"60jPxHyqSevU":884913267425,"ZW2WiunK7UZ3":320231029988,"lpk68AkL0bI2":585961721621,"oWuTMrwuhnHS":399194431152,"lIKrOa2yy9wT":128931908207,"7YCmDkCemwIl":422025457085,"VLWBx59bDZsN":381484806273,"MUFyNbYgyaEr":998058063396,"Q8l78dBbZSJ3":261363737920,"knLLVRHPnXDc":126125265292,"zQ4hmauIcb0s":140507912850,"e9F5pPqKA4u8":571950326317,"khaHwbwrWyi3":967120971582,"gmW9oAK8wbxh":826661680691,"mcv0N5IKQbrT":282464805260,"pCSpdDw8yJMY":835142870350,"H6KOh7ZRgLPO":124275690428,"bQ6abpEdWGRW":247899090948,"98vo8biAlndl":256517817570,"AYhUzPQ6wYMl":943088960099,"DmgY5SD62JZy":729872416800,"MuYRqRtb2pUR":301352204723,"MZFN4TVlreTF":912204823017,"IkunRZHTu4Ly":614349094555,"CEtGDWlsr12B":797985825492,"D8bDAv8sNbPI":573031038328,"4XyNW4NXpLWI":117879039684,"215GEVRZCsEd":578284683005,"MPGIYQK7GzjX":38435943835,"ZaeB3082n2M7":388318031417,"kFeZ4efYTjCG":627864214989,"sDTNvngYLwpr":898159566463,"QXal8SlMZO4a":330570188748,"4AKVYrV08Ysm":949237317461,"8FiuSbXHrjoh":673008347607,"49jlZQIyxO9b":286470937273,"z6mcMyAE6MTO":726818698347,"Gny5OGj0sCkC":803299326851,"Tm5sy8hyin97":379884455135,"PPVPi8Z4Gk9C":881581434014,"BcfH6hVzyiIi":727981871226,"oD2jXIeWAjjQ":745057540229,"vyDGOw5KGD8B":749365760017,"1hEgUlGuXm0E":548196618131,"TONDqe0o0pjo":467610911687,"ZPT4VhUkB3MG":867295153114,"xot1XwmzhX9R":652248387715,"qcWMoXzLYHtX":883656126678,"gHoApEvexVL1":961905096492,"UYOMkXchtOcm":657672218801,"0yooyWsFNtku":698905511588,"NgJIyh7fg04n":855846073527,"U0KFJ3np0RHY":40653211443,"MUNpNeTD0K0V":67119608385,"J2s9h6JAemil":537325402720,"MvzsJGDmnYjG":731438847833,"x27gZx5nJlPL":203522491986,"V2m53rJ6TVRh":930647538403,"CbjnRSeYC3YY":713917419439,"qBTthTR61svj":856266665959,"1h9Nzw6ARpa1":385135917532,"Ohll2wjc3Id8":261993510288,"iByuh9QIbTTf":590536664810,"5HxZhxS33Cb2":914874840928,"n8eOOIeaVC1P":410835170345,"S16WYKMkP4Kc":704675662055,"jZo9Da5H0BSQ":404990837269,"HOw8v6K6fp53":776670341559,"kkt3D1TQzZjz":659684407190,"oPTgHPJxkJjn":219413787783,"CVYQo17pkMe6":556775887584,"tEeHrJV8R5Qh":208206363809,"dRYznWnnn6F8":290098400046,"EY7onIuPmB8v":389600202142,"9I395bFULkq7":850240889681,"3q4Rs9L9BK6E":401201717533,"UJck1df9z1yN":966150662850,"m2F462R5xfzW":281464840499,"jTxCcVGwMg8H":160569195088,"LY7SzIcNm85B":163157729511,"nhUPLLYxhD80":578262613377,"la72mrf3bVYg":922795610619,"eqXjEQeGpZGj":673187712236,"k91RXsS1DHVs":181935746066,"RhmGyOMkgSmM":159957130731,"7X1jrUxmDqfE":753544156522,"q8yORHvrPBAq":893099857849,"GBUUXyWKmEEU":327106034713,"BNN8m1b18nBR":702985913517,"plXSgle7U9Mf":566447285525,"VLd3ZYkA4kW6":794650057803,"OQsWJNxDEON9":975030242285,"f6fil7QiZo6U":157960457251,"ycmiXfK9TvJt":370426171103,"nKEtK8ZyBWVH":487536323968,"wdHTZioofRFv":19856984428,"vyqFVKaBFrIy":247751560993,"jovQ9zK9kNcq":853688117240,"8wzALS7KBZaE":485767422125,"ngUKPS6rqc9U":943992261741,"9zOgcQrdGL1z":892744611533,"uG0xq1ju1yTw":830205637971,"rb0Y62vv0Ztn":228766923548,"dT3k31oKeYby":595465703090,"2wghLg3gb84n":252250368272,"wHDhPBzRWd7f":55579950105,"wKPrGrq3WRuz":939931311104,"zA6NQwy79Syy":803094927267,"8OdXAa06Z6xF":981025410152,"4iR20VdJDQ1V":402382006124,"heKkYmzhnsXQ":893766586261,"GiwqQctqBqhS":579272125325,"WRFa7TIbVAPX":729210588168,"DLcAYyzHWPlo":841374690895,"8R9jQRrP8Cx6":180846479554,"BjJPrq2m6ooJ":965096923670,"AdwmR8Y7Czh0":59417688199,"ZliSDBTySHNh":433733192040,"1PzPRPQDae98":827433344669,"d07HAox9cuxn":911279584083,"bPXDZh3otpjR":664052882892,"sseyRasScOwM":773092652179,"NENwrJjNNUR8":672120582407,"dQcBAjHXKYsF":874391942470,"YDKPVo13vDrg":352637277179,"NpuvOSqWmJIu":845906107432,"FfwgoHjegqvH":341964181768,"kjjKFhmgcBx2":639126003269,"gg2w5cVCp3z1":577905318070,"j3GmZTyOSv7w":486348235686,"Z5m8vNfqcDqk":844168329107,"HUv6eDWU0kNJ":548265310039,"L3uqfHYGpHWj":632465427250,"f8Uh9Q3UwPCY":274795768429,"jCgmS2kyVAja":31819698423,"gym0ivfE4DHM":219827644426,"4hCXLNOjycdC":995134020198,"HX2ERbwxJzP1":875833914089,"bgyPJ8df4EEN":730834861778,"TfuFrfqotlEW":18698372358,"I6UvcRoSSzKN":158672893903,"uDIgmCMM3S1U":50025626815,"TxnwwaGC59YT":279746202948,"ROSbCB0EZJLZ":654191325132,"uNYqm27yuBJh":309680607950,"VAtQ3eL07Tdo":603126907541,"5N5xnZd6POXt":412373845768,"Ela7u6D3V5xZ":277117116595,"Nlqpqx5kSjYr":265743437514,"wPEsCzCgDw7T":102180835307,"BWfijRQPtGtZ":699576179193,"ExUzbo7SwBGU":229832792417,"ee3aaYrOv7LI":942002052626,"sNSqBh0hgjhA":450170376345,"iuqRixu0PjzJ":924053391218,"zttUYVg8HJIq":307133658760,"0rTUH89qZG04":36772522479,"WbU2s2trBzUS":521292960215,"WTgj7Q3pfpu2":972629007640,"5oU741BfjonW":125158037639,"TRDbDupTrIDB":62266791677,"kjWkV1mOFR6k":312187062647,"ml5ou9Putpu7":463414210319,"jsNKyTyrcCpH":600875353844,"RA0WtnrduXlg":905404996538,"FqeZLJAgXAMt":664922757874,"2MvnMnowHS9X":6652918675,"sYauejPyugED":962648921256,"WK2E5r9xmtZx":276274542625,"2944s8Kibn3S":73674889053,"LdUs1Dz7BGy5":679225247678,"qs5Jctni4oEh":26775454787,"OlHPy2orZB2S":931713619508,"YOzBgXMfFUvP":562358755986,"yMJMFXDBw6o2":636294586807,"2R0w1U20kMa0":287487128616,"pPXklbdmhtPn":527668745815,"EIVSOSebY1qo":730570041190,"gOjQQu8ngHVN":421773388027,"wSH9QwVtn13R":648485037861,"cr2L1Gw4r4Y5":474675267116,"PHe4YX9xy6A1":414317765696,"8w36NZRS3qwQ":435758395639,"zurp5PDpaWWB":99551087923,"mz2AySHfAMCL":772123081463,"7rNVeUSrp9xz":374061722450,"ypI2Gfq47MXe":825357210010,"lVZO5ECew7eQ":672329811146,"yjFIuroLwIEI":964735819442,"2PclWDpXIVub":204052823175,"vANnAy4synAt":31498446732,"853SuHLqLZIZ":689907068020,"tRtNqvNipBnt":370344375555,"zUp9NT8M6Ur0":842062025587,"b21pSFsszzmc":760019825033,"yuafMFOT0O4o":955800348689,"iTSh5ylAq2pA":776947434256,"QN2gELgwj1iK":206874423724,"zinHwpJa3Y9R":70628351498,"o4KLUGOMIw7N":596273889665,"sQwWWATGkVVW":70211784086,"vi5kyOUMY3rc":169647327444,"WqVunswgnKaI":124666683999,"6tpyk7BZC8PH":730464700012,"QmPyRpRuEZN5":680719390416,"VBKyVYIcTy7b":238676854695,"6DktlxPxygLJ":176171666807,"WilewpZZNTfJ":694793090134,"sAdSrAoNbhgV":117205493555,"bFxT0wTiJaFy":832549240906,"mEZ3qZ5PnomW":808637718816,"Qi0M5XNDukCx":821885295448,"RdWMhDZF0M7W":219168987694,"AdmZ5BVigL7u":340211978385,"wP26IgvvnB8y":813677288663,"hG0Pdj5fCXUy":350397377689,"weoSNLaZlmSE":385844615380,"XDba0BuXAjRN":484458724069,"82hxzjXo40cM":637522774992,"DvrmWMCQTI8i":618578384018,"FcohO5YsLAlz":291598164960,"g5kQnapXrZsx":585882926091,"sOS5HdCYCPj2":400351313253,"jr5kCxtxI8R0":495107864795,"jLla054EKNeB":366923546838,"r7lZuxydBwsr":223968304929,"vcqjBYTZUpV2":694379666752,"gNseK8ML7flw":626285531203,"s16rjD2I7UUw":459656471612,"Sdg0x378RFxb":408854080089,"LpqA0SYFJ6HG":635082607415,"9gJU9slPPbXi":915778483438,"4UNXlxVJd38o":701077286487,"SovQaUSyzZq5":111330844297,"jLz1RvkMQyRs":604088714474,"WwT3WyJk4WFs":21422193666,"bdlJEQFa0M53":249026278364,"3ShEC7Tu71TE":43380467708,"qn9PrWSRBjm5":575446113720,"XK9mnORB0VSD":380160875808,"ZQgmN7LEYZmc":484307987338,"SP4rWy0joJLf":50334096060,"ELp2X5FNJp7o":762147458241,"gm67lHek3S9A":204674662376,"W1S4oVuqVYdf":182037914076,"GaVCgH3K9Cl9":527225756550,"A7LThcXs1tqx":689524904150,"IyFqmlZXlAZR":787898172108,"BGufijkyLFBt":394203736313,"gCAwr9iDPBqx":913092655191,"owfmfpaGeP5i":897236409953,"yPbLDtOUZZ7Z":410334860491,"UYPrSYTaSM08":988652055634,"Dfa3UH9U4eqi":137171367535,"9ngQqZH7xuLw":53858127873,"FRiV5ZQA8HL0":125440314716,"esdzXvhdANkx":133771679226,"y5euWp8wxzAC":165927860384,"H0n7MSLP5eqo":496176103647,"jvHTQ2k2SUMp":695689187747,"w6MED22Pb1DO":736242624517,"Eww9G5rp5T2d":68055748187,"sF3GKBjcJFSZ":232169904865,"JJWxlbZxdjlr":4253886572,"3PPIZL0oULiO":580858847831,"TflqWeitArO8":433307860536,"qfAgsiV83IGv":630553091686,"jQrt2S3EACBI":181527994433,"0ZF8PsmzHFm6":73044142118,"Oc0Ee6XnXjRP":523374110531,"P6kWCEugXCL0":287559440524,"7Pxw75SYl0fH":352465380691,"pqdCSxRJgaGG":210724661659,"VgNbaOg0i5Ck":358198588768,"naTO1skQfYqf":676024285783,"Jug7d5Zae4Me":90932387616,"63ARhhmhBwrY":679260867116,"vNaG351RHHH2":778474343555,"feI7iJFpRZJ7":753345025561,"7qdMOSz1aw77":880313369326,"5D8iHdLNJGFL":904367737097,"UlFvvBE2wGAN":205463101645,"4LNAI9tKlysY":829171506604,"cHhdsEKFJSZR":425596940026,"DSodIqF83RPP":672521046592,"hUYZFkNgmwvQ":713223507518,"dsO7aW9i4TYO":485403603322,"zDyG7phlUcfb":349875597759,"C2l6Q7jR6M55":358869641238,"FMglG7XS0wi4":312608539390,"xN6fxfu7ICBf":462603194197,"AMef4ilv7vMb":883469733978,"lvmeHeCLmzdn":926631921569,"QfBlNzGsrz2k":688912968155,"0c2r0ZfUoGPj":635513254180,"5U9rDL4tJAmP":625203769344,"sRyKHC1ajSCn":914716436398,"L5pGEWEAqFTZ":812000604341,"ALNPoFYXXI9O":520819133954,"tBbjiem8lD1K":276090194472,"onQnCplZsoPG":772585457791,"RIDTtOJRYsYz":345962870102,"oVqdkj4U3ZFM":676418427675,"FUxgTDahgaLT":979116489945,"lr9oCYx6KQ9O":358833666304,"z9lSBUHQnNmD":189594439894,"aH6h1hnCE1ki":997531616043,"yFbR6cC0z33r":470156816831,"3pz8nb09XXi9":833162459164,"wCEQ9uU5SerC":523431162354,"62YdUIPB7T0V":988312876035,"V6oJhqUBipkU":607342682619,"bJFUyIxEheL7":5646296684,"NneXGuSji6Dh":885012309958,"wkZM4sHZjazL":223841373278,"zjwuKdwSlNwC":630999275164,"W0poPU8eLwuA":350048106059,"HgOOAfZ4iLCF":751524862887,"CgAN86p1v2yC":40536158148,"6lmQrpIItm9O":634883915876,"ZMlHBmYuQsLW":841159042291,"Ol0xPkyxX5ua":115528155778,"a4A5fYntzua7":825050461421,"nlWwZKrXUgEd":386168728470,"PXy7aKmsJJQB":777662079715,"XyMpfDwMjPue":44501430166,"n85evcxvrlR5":915802575646,"tD7wWJxgdhdO":636082970142,"DlMiSeX7E4Dd":201031465259,"23IdEbmYp3UJ":448256193474,"nxcguK4uYtM9":391467148371,"KmYuXXbNGhTX":99850437672,"VW3K7WZbqjlv":754104578666,"8C7AkRj1g2j5":35156176605,"88M6CeFv0BPN":517875370448,"5tMU2Sni8Wzp":385074875376,"V3VVbw7FM4yh":603747919733,"WAdbtQcy6NzU":363299226959,"RWRYI2c56HWe":956201450138,"BvzXmtpzTKW5":45173254765,"xAuPyX04TiA4":377154519392,"XPPx5W4XC4QE":378387436997,"Tn02lwjgRrpo":129205169287,"9IRihwZZecMM":580474706222,"gSuEd7zGYHe9":366643099884,"X4TFNYwIqpCz":314523968049,"Q0thfpNVj1nx":803798369336,"ay28eRdbpGQg":199665608943,"4tAiZeswvDeQ":737046560542,"K4pVoO7QFDYh":763743059076,"HEzJiZtAas2W":270392349316,"BSGQ4xSj1YHa":982654066464,"5req81gpAWhk":502027538358,"y2SbaSAitd7E":259641564833,"3eiiLO7s3T11":910296546225,"hZuhsd0EBC8B":369125077252,"2LLTgMMlUTvV":731824404677,"ORuLFfgZKTXb":538367112977,"vBcDu74OGanP":839194233709,"WnSOAIJnPWL4":370306936398,"zNCmjlvEKIoB":289964337115,"6XwSo23WHKRo":555097093089,"G9OCchQsQoZb":637183021571,"8NBvpJ6s8sWs":829889933388,"0pV1duQpgxao":831748229891,"9sMaEKtLE6gX":208000998706,"Xq1MNjs7AeGK":673838600044,"v8djU0lUCuox":546682286607,"TkrUMUgM4zuO":51053257794,"5y0O01NZ4f72":710049465258,"v3wOlzutTQ9e":407210368473,"iS9BFs24ITUB":221449569631,"r2gTt3mqeiic":144762397382,"5AhRV1mgCuJc":603729459182,"PrIpoVvhr3O7":367786883974,"jy2qbNrmqUDI":699562088550,"1NOYqBiTO2j3":159944745936,"0tZLeIgwxBJn":114182288387,"llSejIR0M0j3":531332650342,"3syxNWW6NJh6":389832493508,"XwF3B0cp9rN3":84008419105,"BRCEQDHZoC1E":853843974881,"nUMulxdq7M6W":892191769112,"OfKjesnAkXBE":213040067138,"z716oO7bgTnd":524963758236,"FysUk9D6VTGd":735271569137,"f8VvZql1Ex2U":364259999889,"UfeBri3enT70":613165479041,"89sFfoRDJ0He":667420931853,"DVhctrgYZWpv":820560913168,"toCCUfECo1ZY":428459369751,"2sOrgvTTnmIb":647496290431,"gAMFwfJvhLpz":281451635829,"5b3gmpjbBfBr":591693546364,"KYXTIeHt35Ah":816958044884,"qvTvft2thjp6":771913565424,"lM9kHO85vXe7":667806482428,"tKbnFPss3iqz":929821362726,"J6Ea6HxVvtqn":177245683491,"swXSrO1j0emS":560534503163,"aSSr9VzGX4gW":34030061438,"ZG2ck2Wv5MGL":485310690956,"QOj5wXqwx7nb":642024491826,"8OU5aHZZIvlM":509094473192,"NFCvsn53lXxe":414363077072,"ZurPw3zJ5fj0":188532602901,"Z7XMkaqzLkGX":599755800140,"EeNVMhmBXvh6":72235159111,"Iq60G5hrOLJv":369426417049,"0trvF7xCaCp4":403458356011,"CkPvvnz9E3sS":37046037631,"92aagNPOyvfK":244241267326,"vmfmZtY1TeiU":452305245964,"dQ6IvNXp3Tbc":959671337379,"QSWUWMf2EkGm":907838137674,"VAbzPMnsecqh":683321035178,"zJZHF7W5MT7D":731482287087,"mReMYwnn9PSe":226207028998,"OPE3jYrd539F":337927783575,"VAYXy1S3X60j":448396417036,"TRLoeQ7eSbZS":46634335999,"qnHdgOSQE4vr":797151919510,"tf7FwGBBYwDw":8585787467,"J1GfAOysd1RX":799711019870,"DcbnlnKEP8lq":52430361779,"XZXzbDaXoyyv":387961850836,"u3TTRzAJkv7D":645810576302,"8SwPaD2pQwJ9":153950766040,"wJMskIv7A2qo":469110329831,"WcbkwPqZW6EU":623032526464,"NRuJgITNAYHQ":506310373505,"aPlfCeBkKNf8":216164187060,"7ZIqFrL2L52r":837341128490,"I6KqUD9xm6M3":525291620784,"QWHRVUDDn6HX":286291332853,"Hju3Mr5mUSQl":368504610871,"MvaLtCp6KtT9":718018196261,"gso93e7VxDAs":256371774633,"1mQsHPSfOLkt":468519517562,"90AIWM9tMraY":761268465240,"5VmMf2wtcTXh":656048635310,"Mh2ol37Ozkb0":230611799322,"WNGe0XuVlasV":850724931413,"dCQwlhTRL9tl":401635028752,"hpUHcsEVRw5B":748370603518,"mvFYvdmzHXyY":693588712778,"2FHGOHuwK1C9":973012628074,"8c58FN671dL9":605738413123,"CDU3MhnjsN0N":504929444326,"JSlq3LwBhUM7":405046148487,"es8sHZOgbE0P":853548688194,"S1Tfpo2Z7jWx":461975286911,"prZDIsYNk67q":598931289329,"kxrxCiFIOZsc":903282788777,"Co4GB8AHLdsx":525457941538,"lCk5UHLtaEyq":645844258280,"XOdvScI14sm9":673171245433,"vEj2bQZ0MSfw":929076571146,"DPMJ3ot9qrok":338269006690,"N182iunsGuR0":183741305276,"1Zme3nZkIWP3":844860624061,"ueLxPap8HSFh":103577620397,"3CgZ6AjmdKEQ":689641736017,"pRszRCgsfcov":81992373376,"zmWhRgOZupjz":399719422251,"oVt06ZI5ankS":751139923337,"DYAOm5Nl9PQR":238224226711,"DNEfxwKlYJmy":230501360090,"dYSMWrtbhuDz":978458955390,"z9MXPuOmGx3E":190629738648,"1cFi5zzILMy6":521453943250,"ruPf3Yn4lYCH":594111686254,"TQzWhO2azhkn":961902842183,"mkzmp0Kjk7h8":180544252309,"ckWG5MGuMKFo":257876797414,"w8ZgRiGED2je":959597001474,"vF3gVbHSC70n":889006658938,"28fZMJCYt40k":465164234230,"HUU61rinOKsP":163812689305,"GuWWA0fUPFBO":437273000061,"ZMaxWVHEnJxS":772987096326,"0vh6Cb3gbgUQ":810031277765,"nSb32U557LbI":674948968992,"3KgWauJAS9fl":276350585639,"ctBFSHaVmA0e":407410650839,"dmZcZWWOE5G9":949941459412,"7MYrJ1mlK9qR":838396349377,"735DU04ZrPBD":714948299791,"Rw2mdUG6BkRl":496005283536,"9SMwADf6esnU":793197804404,"wvRWyT4qclkb":288295331315,"IfkLtjJ2sMsG":549527118480,"dTAKwrPGdIgx":342014597363,"gyjreeoAmzgL":133661233877,"zBd5luHkHe8r":157482033299,"Pwx9Dcdy2g3O":304008395161,"C9n9veC3B4OX":974322665411,"1eudBozT15KP":603921247679,"rC6qxuKlNuQp":123286546611,"TWJzwMCg3MAg":814943455715,"OztDChT6FgW8":54703442163,"ANyBKu9MGdtm":695439370525,"s1GRP2THHxRt":552369734799,"Wpj8Nuyv3hpc":463661530139,"NtE61LcCzRKd":179883718865,"Wnbx8JPfqyps":450894158632,"1pbicT3a9hxT":21674406167,"TGGK1Vu8aM1Q":755367075889,"2erQ2WVDCdff":154042776480,"z9SRL9dXmlqs":386449141949,"NZP8uDXWDvVM":620315010317,"pHk7JdUHCvDC":940159529605,"IX4YYHH5omSt":360702434949,"JcYMUQQE5BPm":120599677946,"XU4CZvnwIFEB":554315906865,"NfNw9CheaJGm":961493638872,"cxlzAFfrizGQ":604538022515,"5WUblahEEU4x":263694388143,"00P3Xrvfpwii":213995392648,"akNlnTury8Lp":353400892567,"jLfyzMN93Kng":887716600540,"eggRDXmo1mY3":513026432163,"qoxfBT7TOH1P":48598343048,"M1l2fOZbbzNA":765585386554,"54aSgoBw6lFn":136142727212,"5WL5E3LJYndf":14313822858,"SuayHJwKwKTB":732535613212,"YRco2uwypaGg":104632818674,"8sRWn8G2njCF":544856375594,"lJO9lfO51o00":517023034731,"1XjpwQIR8mOO":972086557020,"8XNQnkD07FQt":562641809784,"rLzsX45pKgGP":286089382071,"BhaPQtjVOrkK":513978674625,"QB9lOx0HjfQI":788435571267,"ypCMfLytWUzI":680443487667,"3IzdzKpWnZ1T":526254878297,"T5A5UlFrDx4f":39620681188,"QFAndI5BXa1u":282314673352,"v7BWvW7gCjzA":634692787746,"H2ZnatwqPnjZ":283263799541,"3iL0lv0L4Amp":260216109759,"4zW7PTU9FM17":933803695068,"QpvKx9PAdkSh":378753794012,"4WBBSjniuGBZ":748690258604,"clofOpOADZdI":332769360360,"xtIGrjGRifRj":296648051973,"7NNPYj89eO3R":443900735335,"v09OV4ucSjr6":59061173163,"xoabPOcpqOnF":256673859010,"ljDT3AS3oGCZ":557832677699,"3reHze31IMfb":172062291319,"yv8N3JzNSmmT":952501037491,"HT1XTdAdT9yO":786741433329,"F1G7CdnFomTh":236409079897,"f0scGa7cFbes":36165163671,"0fsLYQCfxiPI":911508330479,"BnzqK1xH2GPq":264979899089,"Thj9zBSu2yev":784667795510,"ITbJcRUln1Z3":994234241237,"5ueW0ho1Ws1u":324988350265,"JSyr0MWbZRPb":516844721585,"OpG8rt0b5GhS":131237527358,"kRPhG6EWye0g":47215066595,"v0pOFAHj7fV8":432116253993,"cVdSpfvjjefW":656742168935,"t3aBJcoMo7Fu":184976749150,"Ojvk5DP02AEJ":553346646373,"QiYFjDx5vGXu":701554689202,"oVATHGt44sCw":944085035103,"ltBUZFsMYzEK":312651424987,"TLV4VVYLpQ2R":22913981627,"1ghyx0E1SYPl":360327010915,"BROlObN43ZuO":104686586336,"wy7FKhuWYVMP":739744634094,"hBpy3kcZrsiK":624455298032,"sxCnHGYv11pb":352526013106,"0lW1C6NGhSSV":751297504092,"qwOR6piYJtWr":819507212172,"3Eq2fEZsciDW":932725206970,"R841eozezPic":357876409066,"P8DFtE2LwnAr":980544142192,"Bgk6ejvdfUph":267279093325,"aUral3rNdlbJ":165493730432,"4e4LSFtgoOtj":121474543091,"byRe5G1pIPHn":419191756488,"OpnRSZCilhh8":588957423614,"jX6rO3TSBJaE":809868026239,"mEoaiiKYmaWP":458447457219,"js8BiTY9n0KA":3874741738,"WJYGvAHdFq1u":393678159204,"JdnjldneEtc6":948429935580,"vEPKc6v0wKOz":766323828760,"9isZZNIffSJr":637205706095,"hPadt2k2dsa8":636407400379,"GWwXrSpbomW6":457919121886,"GKoOPyo0w1mV":971823358364,"5PEi3le1OV2C":480103923344,"fTT4RyXxRCIn":411305138292,"0N0KTRW6J57c":465871559175,"sTWRQ9wUPbcE":482261838388,"Fqj6hvjFCmzA":587646940692,"2LWweihbf01r":744893081031,"G1gblhgnswWM":548844322200,"ZXSAObKKXZ3M":66066161934,"nDfRB6rS3XsB":374598842374,"gYk7QSPOlrFw":37997378061,"ugGBooSGaH3H":987838803488,"McneYW9gtLPa":115172930224,"tDj47UEwWANN":671158254860,"Zu7js7yvCvnU":622666084553,"bZeYPcbCJxHc":67758286287,"nSlLNfxqFzMh":793850880636,"rkrx6eSejg0h":73705858646,"FK68Z12tN0Rz":548616944913,"qoLAXYOqNnwt":518799378429,"S4sEUB5Do3R8":948071603640,"zAQVHhBVitPn":671236287315,"wruAieVUhF2N":959585611616,"3CZOKxkCBYZO":104089416015,"3Eq3DL7SU9dC":55943392148,"wM0CXWLO6aYO":52300552247,"3YEWhlUZilvd":499835602538,"rPUxGDEVvSCD":105002636392,"2epuOoyrJKjY":374320897987,"EQcdo9qL0XzB":321123818035,"Eep9JqBYc7Jw":946246797486,"OjehUZUc82Bu":91885215462,"p7tyW6GEOFYP":166874941060,"Xnmmbf7hKD8V":702771087470,"quISNVJ7pTot":302351027031,"23rD9LT7aagf":516762209053,"6kMLWuIy3NFf":398374361247,"DysaiGXPNk4u":930493775377,"axiZh3jwRYHL":747155249400,"XNjsaCrMIoAp":443395473555,"bo443uihWs41":393557143835,"rkkStNLip0u0":557187967061,"Us1NlqUNperH":8803907777,"Tiku3LhaTve8":808197670441,"q5A4mtz4rvkZ":646967860435,"Zgd1YNtAbHAU":863734352926,"V8VgemaIenYo":261424818955,"m4N3cN40jGKB":264988506173,"D8WFmk65iqES":696907900087,"aUnztQzrdBGC":25648847447,"4sNW9OBNTDG8":74448729591,"tbJR8Xeqdmqe":364114700934,"loqHPshUCrXQ":744601454485,"o8sC8EuYXokt":557695094046,"mA2lkSHNvZ6P":285760950968,"bzdjf0H329N6":673794677086,"x8vGLLQgUrQr":620692745921,"S3mpYf51w7Oz":825425926223,"qge6pUbprP5X":902245349945,"QSzULrqUD0Z5":578774077598,"RH4GG45LRzSr":112312764339,"QVwZJ7FnL6jD":939672677605,"ysUlN9L7cqFC":105680423694,"YCIZb3IxCl3j":69280572534,"1v5FYg9haGZN":345588765344,"DyXrJnKSndwy":425260816934,"dUdQWWCgzEIz":637097302110,"R8S6muQwPLVJ":742854321777,"RhULfmW2K11V":394578794891,"skYlyVIdrgw6":627708688195,"43bIJ4SfGy2b":640627319560,"wogjwmtTkBR1":305787889727,"3qZaipD7hhMt":584527836356,"YHRpQcEjJJS1":594549672520,"D1S3OchsZfqz":822084819749,"AzTJJhjKyFwz":270632828492,"2dC79sybf4Sd":186497464670,"yF9ADWwyomcr":304780245084,"VKlVcKYOvb4O":731804038205,"6PTihgKaIwEs":108863230714,"erNmEv2zPfN1":691664076140,"RcJcU7hDp4w7":764563115214,"BMZUjzcuIDaj":766633241241,"PYhVit6QhWOI":184125601958,"73vYRXnaQebx":67541227753,"TgJsXTOrVy80":992269700404,"4N3VFw0ucjlb":258922198714,"DBiZmHkahqVf":265366815039,"6zmVXLVJbBKw":877376529750,"7up9fqh7FP03":517752241585,"qTTaBiDd3opc":441601028162,"dvJPGGzjqvQL":552318015209,"ukQUSNuf2zdn":587227902729,"1XbfgZuORmhF":979986303584,"XZwfpRpqsuS5":157507123971,"1RKYYtOTiVZM":818775452948,"L1JY5JV6KM7U":960506983222,"oRoOM3NlF5z2":289588144426,"KLKpK39RhAFn":246259151511,"lEclzWh23UOF":84282028293,"Xw0HO76MaZMO":902160071634,"b8lbeg0HtusX":515553005310,"QsoHqePrrdZm":633086998063,"wu6ExlatbUGq":643712592621,"tl470XS4eqOJ":28326470576,"GOaj7xnQsuno":534368951914,"N4lLvVUJnaAw":912297238104,"Il6wNmb4jeGO":706010741557,"BSGv3aNc7fwg":929709616284,"jHla2aqI5hDa":412152117798,"awBOrWQGdFax":851866578878,"e5VusHVTjKJD":28547367185,"k7X9iFOuK1XI":358321069340,"9eMsTKeEBZhO":972553515153,"jpeMiAC7IPgc":199046074832,"LNajxjoERDED":412722912418,"hd6bmJ6GjJJR":826469157102,"qNPGaf1CJruS":748332802531,"nrL4Q1T06X9L":223958768563,"CPheIdCEF0eT":260607398334,"yyKp7D4B0bDk":687181371850,"TZ2h5ano7M7y":697864887389,"wcBTeaAIqx3G":529247567508,"RLJD3Fw0sMqY":334661848046,"smpkTeSw1YKJ":36077646592,"UZsHLIhMEyBn":160020468132,"AQx0yp98B7O0":633070389722,"xI4UCjadLOfS":197371250009,"C2j4qkJqQiuh":184605775237,"bJANOs0SXQ9F":364440531334,"8SPTMb3PuXEV":393474202462,"6uH9pchZjUIb":991477342406,"utz4IV3Daycu":506784577576,"qZeAIa4rUoFj":766191598064,"tdn2neznhkGK":315825696154,"RLi1ymyNOFq6":821011254294,"pCZKO08AQ9f2":657681698757,"396PyNyEnR8U":680520331071,"W0zUTyNIrtvm":688176489171,"WVQV4W10bRpo":433188650598,"b87bWMWI2mYF":149287996044,"iSd0zX8mz97y":113275341897,"t4wqBMvZU6kL":488410741774,"GHfWKtNQ4qhR":619784375655,"04bTk7ZEfoEi":806714697432,"Sg5J5SGVwu7x":12481052591,"FVcBdeeisTCy":513596483120,"fAef6pHZcDZT":339057756136,"HowDXTUYRTIu":396309541161,"eItfzFOf49E6":991415471553,"XtWCmqKoQPkj":980871139974,"a5Qv7F7o30OW":551720640511,"a8uUjft447Kk":867984046620,"9YNkgc5NWTts":9713957208,"BZReFNMX8ywR":218853983497,"mHnGU25FjwXl":625159054369,"eDhvn5Y5fTLk":183554430097,"mQSU74tJYI4J":838723547226,"WfsruAlbmq9A":687056569304,"xU2F8SWAPtdU":979260097922,"ULgvMoxcyCAC":644253182296,"EWBSq0Fp0xYK":103991894509,"iUL3xXtZVFQz":173088912128,"2Vp8JUVftHp2":746390311281,"pQTGHUSKIEMz":757852420625,"7Xflr6GNPN6N":35076330810,"hv02wwNVLddb":167856109332,"xH3tX7j75sIN":323516579604,"MpHAX7FUe2kR":560024309328,"Icwsompky1X1":569086976,"u7hCtPk5pBZD":783105603372,"BVtgbixMsLLs":47466275085,"QHntHLGLVooF":779963091535,"Uo5DOokbxdcO":182138091667,"QBNPsYOdvDOM":481689782528,"iqprrmGOJ8pL":313314649164,"Y7owvYpR3XDE":397527158349,"GrNtXYbYa1yU":861103451606,"rs81FIoKH2VE":780798182286,"QQxP0EjGq5h4":680818286631,"mM2X7JR20cvf":129205482066,"8aqoR1ioFGWj":501610391859,"96N0FySMQ6a1":262748252109,"8eQ2aiZntx5O":433295249021,"tg5HtuKOh7ER":317426296180,"JwjwnaF5gcUm":638747109156,"HDvtJXrCpQnI":610855009209,"KR3ga7QVMwZp":154453651406,"HIvoiXKpBB4O":986147717966,"Q6BE1FOa2t0o":598420491026,"DGWiyCEIIJhP":720399286899,"PuCnJlZB0Awn":775938745975,"kwsFqCSJrbKK":332445153280,"bd3af8ZbeUka":728137097401,"1TbwYLpO9XGC":432250682152,"cg308b79rhpI":5045044376,"jWKQgnduARw5":418228078990,"NUPURLueEihO":214567158017,"fKfkzHTeFaJQ":617414399805,"eG8Eb7vqzk7L":721705905856,"xf1CFczCNHCE":43239095765,"C582ht0D597K":673761241419,"hUFgXXifxXnE":136199851182,"iTbQ5gqY3d02":948052938646,"yOSA2Jxu9QQx":449872557989,"EbOtAOxqDWFB":805276557242,"mGANpfk6e0Li":945623670921,"q74FpWFGN82W":228948573613,"dZW5qMpD0EMj":574950360127,"BPy6khIIApMo":591235741427,"psjAXLfNhXGp":4182992621,"q9l8Ie8vuBVl":980470895526,"sEvrS5TuzpIC":925973164634,"ggiUplA5yzh1":563962058278,"brxX3c2FMlPR":41258875508,"UnuuZbi8JTVH":861994463736,"upRyUHVpZuo1":590445797078,"o2hDHmCW02Xp":130343098971,"f7NQYMJVmMCJ":983529448821,"rbWdY1aalBIZ":153427971397,"iMiDm1eT7URY":565573021937,"ZQ2ReDxcWjqL":757256769421,"709rm38OBfPb":972821584106,"OPyIm17ZI3SN":607529602347,"UX0spPOFpDbf":892363248399,"yijg6Xwwfo3l":926369016061,"a3U4SISvZEtG":567564482361,"gev0IRHi6LjP":840566144026,"9FQm9tJtVZ8z":566222523491,"WOEOqKqlwZ8X":564429825253,"aHFeuclKfTDm":547147347483,"TlRRVebGXZ5c":343692319917,"NuIC7vBxJXSa":751251172527,"8A4uGBNd9OXJ":41848462082,"iOFH2qmm6fRm":662788846372,"uW84LBWcJD2T":343764120031,"pWdnhyoZtSS6":585146386910,"uwP0DxfPK3yV":890417743467,"1VXInf5uSfVZ":127838531116,"ZClBu2m2NEdf":488263241062,"MKY9XpML1qCk":340608333282,"8ngCYV3nHWfV":332859345655,"nTzQKodNJx8i":962287296890,"RUi59kRC7blj":131774601281,"uykDlc3HkViT":503592554170,"Zh0TEsm3PJ1i":662441142273,"XrNfaOveqzbS":247600516716,"I4bCk0At6R9j":3389500955,"fgXLzWl7UKF0":411475641074,"YgsZvyJU71Gt":982690416001,"UQFcxhfH3nVn":812838071418,"4jEmBq0zVLbJ":477610676534,"v3TFd8wenCrZ":683662873246,"jETS8ZlHXvfA":428978400862,"YRppb8mgheaB":941120735175,"59dtliwyZMOs":816590417818,"tb8kmcvDjZia":717575540712,"6Iig0pxmHxoV":440866746855,"DznWsVrbKvHT":71538328146,"T7t5guRayp2d":621002482975,"jSTSz1mFtUFr":107307130672,"7tLgspGl8mFn":568531954611,"Kx91h2B8Ad2o":50910690987,"RkueXGZfIcLw":428387701422,"6i33MHBXR2wc":189146354098,"bAzcyPlr5mlg":779433568334,"Hmn1Usb21AyB":13072339976,"EkM5k7eGVLhY":272958890563,"EsdEcJkaI1Dc":162180861581,"s1zuXmO6qFxh":208207594741,"DcYfFCTLbory":916068152475,"BamkapBBxIzT":928405818448,"ksT74LhNnQJd":871970449500,"DHyOnbitIV0P":37612393968,"Cfx6j92FfXb3":282841189165,"jywxGsuFnWc6":185847710935,"tBxpym3MX8LC":553759839979,"C50U2uWRHtS0":991742339510,"EEk4887NneFY":700908975987,"eQZqy403hxi0":468512020218,"TWBmVcRuIm0n":952749223466,"W7sP66VhAxTu":800144224487,"T4m7P0pLmvPf":507850139433,"HletHGQkjKzM":593639897070,"7Pq80umqRTaU":312486156935,"tAAARyUEZUXW":567532859852,"3m8Z3AfMbgxt":813527600530,"HBpjneseYCx2":164528669736,"1a5M7AWv6flI":12126230154,"ykyaJwivB3Uq":303831941666,"2264dLna9Kzm":903345167081,"Uo2avV1Yv25U":313857316083,"KAnsn4gjiRa3":937879150884,"FYsDLDcOKsGa":44634465729,"N1Ctun3icin4":81015924789,"qNtYVM107o1U":376924823119,"Xb0bKXbYr0JD":688731018124,"uCgSRTn4vf2v":464280616045,"tELW8TJFhm38":894974410265,"jfRJH0HSRlJz":344082854441,"L4apGQKf9MWa":26233101066,"RV50d3UFEEek":864659411712,"hp98VW3m4k6w":500749921076,"GCzaC0ASQtVE":705240903476,"QBRa4HtjoWLd":117696481177,"fLqvdRvOKyDe":521289896291,"3KtrUVIkttkw":236122317216,"kXBnCQSyJLIb":950478550573,"jc0oBA0PY66A":739833891132,"8pLdfcbv7Xfc":303085864060,"vkXHbdlBHP2n":691553748206,"mwIchGNGp47h":577875785525,"D7aTItHUj3Uk":644242073517,"HKqmstmpEdzB":513402008902,"k7RzOqsVmes8":472983781305,"8QZBSBHQXLTh":899228538898,"dVlnOiE1oKDr":328400855690,"agzJS6ppeB2T":750409175974,"5mlMiOAzCCSR":230663407602,"7lAjLiuATJ0d":646551060208,"Bs6YZUhWMMYk":240877321084,"ofFPwx4QhZbK":836694064535,"e5DDKLS70U5o":152464416350,"AbnxwzBq9tIj":412671474791,"s5iBsBBvoqwB":371569680015,"uC47h07ULNcj":533283074537,"DZgmNuNCRJLr":547232769024,"sntjU4BKaGe4":36577109025,"cA3GyyOn2FaH":632201914562,"NXRZLJ4EkfSW":980909601375,"a4oersDghR4q":425188304374,"zONzJim6e7sQ":935547529555,"m2Pem2T8SmXp":871327291317,"0riERGfUOwiW":585336101371,"XB1qMC8bfS1p":748394702173,"xRQe3qnvM0sZ":987200011707,"cdM6ZcPuAlmX":331718722599,"zJwUkyGdwXhV":956929044491,"LG2BSZL8f6r2":914625470993,"y3UDW9lfnPw0":265542616400,"K7q6FpS6DA2h":406245192447,"mPk1Lnxp30Fq":527673180895,"XHsCKFdqDstE":43159303576,"bOXUDv9oYwcA":185722634084,"aPNI4nmOe5uN":321394243604,"vNYF7ox8GPHK":783105234847,"ig22ZABw9kXH":514842607734,"vfG8uS9edf4W":886745427816,"YODwmXe7lLL1":861427054190,"jADw58IhiEPD":229628213545,"8k4od1UbqG0r":96170712942,"BfE4MrNGvBqy":313321803635,"mhtz8eThlSfo":10494289737,"CoPYKX8tEYLg":820175102945,"zw1Q2Pj471sI":428339194444,"QKo1ffWisWmv":510624661179,"jTXmnp1c3I41":194692600714,"UfRVxbuvQGNV":918237487861,"OMbqgVfArxdk":430492946222,"7zuS6Rcj8SA3":464381832301,"0d8zsxqCtYmS":724074116674,"f55cbMR9kuis":38829250271,"FMAWb5lEdCoU":333827728704,"Hj4VgeWxqYqu":868040284349,"OoF1td27osEu":470147891898,"Ml79AKTmTNXH":445195954215,"LvVLfOVOlh1F":228713757050,"YVvgDgQ7SmYY":480092647915,"K8KSVbidVn0N":838706803423,"JDfbfeIAMzBM":855519185376,"W5ACfXxOYSt6":230983200816,"7PAgXn9wUDrB":561326989126,"4AAWqjTYD1oK":620845876144,"Z77ZxxNKEZcz":264892945455,"pftfg5D9qyXY":980336332639,"wkRcgDi2H3pU":343991403112,"V28POQtgcehv":669145012563,"G4sd8PmWpwxO":640725122664,"jBzVtPz3VLAD":677969004496,"eD5ICQgWpNRd":704513363558,"DKk9J22R9uZA":316629533330,"SqwcSTCs1mqb":346355760605,"xUT8wS4yejO8":530932702565,"ps4bnIXsJptY":700730883761,"dkxBt1ZlUIEY":946835795,"be5PUI3i0A0k":377436690911,"KipjjwGZPDJi":567078586639,"TkNw0WLzeDyH":933444787293,"ExoOrRehocuO":515201282697,"powmqKnHeTyK":842513687258,"y1O6dMyCUR6M":947024146777,"dpbmpHHO6xHP":777242048638,"QkjICDLKq0nE":629331547194,"ulUpWiskYgyY":996968217698,"518hKuu0nFh7":875846001350,"nwy5ddYt5s9A":253253058844,"dsf0k7K9ZdAL":159535371971,"dwfYgspXZbVW":340480339633,"gaD9rpnSjHRx":252651659774,"GP43RSDKGMZa":858909275298,"7SZ36KIYLzbJ":432901185268,"UkVWz2l0UFK2":501811559945,"tZ9Ycx5fyEsq":189862237582,"sBEjuCRXbUAG":675412958369,"w9oGvWvjuAUV":309727237900,"dV1dPDq12KcM":917609951826,"pqPMXUGIjT6Y":886015654224,"UEeb4KU3KJCy":554371549076,"ranTDj7BgphT":441769969241,"9bwp7nUj4CTj":442220506139,"gSgfzEx1llM9":541728727225,"TCFVhFzdNbrm":316041390398,"gumi30ie9XIU":520276293315,"ygyDziJMnViE":675106694134,"sCfXXhSjV4nv":115673639064,"EuN96PxPIT9n":284236450640,"dXTS5RpntHEZ":173185340392,"MUIa5NpBeVhC":961878359137,"Rnsvc63abXkh":171667041860,"M8fEp4VavLyg":25592228762,"pyv2sp0cyqVg":921141784031,"wWHN3yEvBam1":711138524725,"tFESG7EFUbNS":535980757715,"WD272FjVht6h":996302334435,"YLXUcjtwGLfB":675141898790,"4wNbrlqZGznw":184942628358,"fm2eN2xQe2ut":552813892006,"pmNis1I7Ceds":589195112334,"AdTZpAOtSZXX":214518830774,"vckobFxQcY41":194534301558,"IzOKCqPCB2wO":399909730398,"TJBfLfBIzZpP":430225515199,"PrXq1Ydu3Llx":389760063806,"E3vTpS2vyoGt":473724120242,"i1SK4dLoULAz":16661795659,"hrr73B8ZwR5X":847867716428,"eo4EvQjYzFGu":505638912927,"XzJYXWwltWbk":493996863804,"ip7ghhOD4pcY":745920826170,"kQrD1kuCikeo":727099287600,"7ev11MwG4Fra":603922308883,"sSlCtc5TkbUV":747790099074,"a5ClCV2HM3iW":748934268325,"K96kxUlBoXGR":141621607243,"UR36wUhpTkRh":282158790793,"nkFXwn0ZDJTL":600494775853,"gpDd4xdLDDkm":667011962092,"zlI8UnXV1l1r":477965436652,"mGBpoXot7kEt":147589433382,"YWZQxmC0CVgw":516993857224,"24K1sxQgZfNY":282418405389,"27orljwuM1Fd":649478745849,"KdvvR94rmRau":819706359119,"PdSprNV7Dkch":91144526303,"woCjwr4hFo9u":18542143976,"cCqt1qC4pEz4":399276209651,"2FvLcec4Zbio":324953763834,"2bYG8HqINgiD":107876678109,"hk1XauYdBIzJ":724083417671,"Vli0nTVgU0wF":82973383699,"BdjSvkma76S7":572397159922,"ZLIZHPdNl2g9":966646342024,"ZjcEWDz7Vnu4":42167848581,"HUE92xspW1u0":356218073240,"M73GH8rrbMq8":828778305040,"0EQ8wLmBeSjO":522523317118,"ldlxQfja9HP6":498854565149,"xnd8h7QXObDK":451095211775,"a8zrq6Kh73qj":906110487775,"J0yCrtDUbuXI":958810796782,"AgaaaWtrJAY5":153183397348,"D5SVcJKNy9Om":312999274214,"BhSZQe0nuU8R":7153989299,"dw3wx3xJMn7e":248196842260,"8vF06E6Yx7c8":879598195030,"4HMgcEpScq79":352424080455,"Z13pgk4wxhye":945372798675,"ClTYZ1dISnJI":608077961126,"5iuRDVQOMmJj":94033102549,"MSW9FomAXviK":103469226147,"hnAu94Y1INIt":235704896378,"82maGoKfaANo":942506081974,"vSOGltc6Q7WQ":2467353611,"05qsvtMF9VOt":963654152876,"qii0bv3cEA2z":806931904255,"W6vFNcBIQveU":615061191556,"ojETry1MRHwj":19273431177,"VgvZuJSUdz35":940723912561,"fGFO0csuiis9":2143318777,"fops6UqEnVA3":203714644526,"s4PdzNafkEbc":32951983798,"7QDkIhRRGjFW":750039439700,"b7oVzj5uyTKm":388856013764,"GFYz2KYl3ybE":60484403700,"oz1aQDAQfDJh":412594265644,"4BSGhWZiG6yJ":331870881068,"CERIYIT4RyW3":10721221863,"86ZqVaJakDK2":223391277654,"3s0qo0b10s6Z":478985941806,"Vdk5KfbeXEy6":328523625189,"JtsQJ4tw7a58":841064221604,"U5FiEJWZB0hu":707042026377,"ebRnS5QbFc0Q":539762325317,"kadYQZwwApmw":984474310534,"IknRVQBWwDok":888514661475,"J96MaimUlsDx":600908419471,"c3YgAV09O45S":870018002264,"BFEil20xZhYM":206028705139,"YBT2lxV5K2I9":490932686312,"tvc1f4cSHgE4":295108600873,"JoYbWSoZROpR":280512543153,"kv60pJ8IW1VG":598133246404,"DWS8N2IiMXIG":185764994827,"pPwMk2oCXuyY":2852504251,"9rKxyP87yLnv":886116675846,"ZbmPRO8K2MXs":10951567772,"WKtV7BeDDdf5":308939160752,"GWZt32YbVtDk":788123127211,"KIcT8XajyWzH":36340612243,"ZYpQsZydHIpV":528270369802,"ndOzyt40RKoD":902844318495,"qV1ZmImBs3a5":154923440925,"becStwGtw8zi":296069806392,"maf4JnPHxJJO":644322162819,"d8BhPygJS6Jc":590020602577,"beS6xYxVVdAi":212668818273,"5Kh0J3jivrRi":722310729072,"KPMAeIhi6UTc":915069982478,"ZRXog9CiVs4t":41764344688,"iQBgJTyqtjKr":488593531474,"95ZgY0LGBPTV":509261687637,"qz2iiPbxFfVl":351335442231,"AkG1GMb0k1qm":628973076243,"wcihvzgkAhdF":195839772677,"3HQtk26rqKlB":477400603971,"i5ojsYVhtspp":146269088625,"sNga9ia0yuo0":985019216507,"N6rqugSurxcF":384401389739,"lHAHMJgXJBKP":493502602627,"VnLsGcnHsYMo":160221630888,"kU1H9nosjDfZ":937377067637,"jdPdldPE5zIs":161591464564,"QdfWwZpSUb6K":507866957862,"OBHlYAyC2d6c":985062520693,"IkXL0iZEtEbA":948678234544,"23rpN7W8bbYQ":628898039312,"oN0KhufVLxqc":53726462352,"lKq3eihqoJSW":890709774878,"UbOxISYhAhld":336628545184,"8ZHfyNYCLykn":356686993894,"PBkszrKjkn5b":844476023008,"C69M9jMRqh1a":490098666672,"pdpqPblhXqDx":437464297277,"THfx2wKghiDD":526897182409,"Ec0VkRcbyNfL":747062537794,"7EpIkVaGnUgY":689933531611,"n9xjQ2o2HIVK":593918257679,"9KZdIdZSrzuK":679043761279,"SKUqxZtFG3im":798962769582,"o4jN2OReR5TQ":144150855004,"Jeh1Ra86co4a":105566185556,"IjWWrRc0Tg66":803163040945,"vXX15JpJs6Bc":201044551195,"c6JSwPLi9z5h":716853460240,"dWT1SO6XugDA":479948230821,"etpTTMw3cbVc":330338904239,"BpSPjbTl3YeZ":292022457456,"JcITv9vddBIg":501190082034,"fEc1YwQxptIU":996716786375,"2Ayf6Xw2h45q":818034760578,"K3VIj9HGUy0r":631463321541,"OSCaRmTGAFzm":991520396631,"VETDCnbLhGX2":898313965374,"20OiP7zwg2OH":3069201315,"MSYRguiqiYMT":91649176335,"KZZK86nbrQNN":404532835846,"FbdVwn5IwLRW":181487886699,"1Z5ugWI0huSO":466664696202,"vgxvV3OlL9lL":947476684053,"FIb4cEuVBXiO":139795666838,"vqQ4gYoD9O9j":852607059038,"PHdZKTrquImU":727981824722,"S1BZCaJff9Ia":641242173123,"QDpjkEVTbVwC":373726958208,"Tjytmq7TQzh2":397813929794,"xwgVWqt9onlH":772656356559,"pUBIrJ4wknV2":200363504262,"ANRtcpnTc6i3":779710931540,"8PhBHZRS6DOp":18192321227,"No7URQmUjNpA":91832453642,"QgkFyH2hRcMk":916567213933,"UOoFrjGQAFPw":442521327312,"vOHgO0rDiHnv":307066728,"6kvIvEVCby20":495468586367,"tqyQxUEYoceK":635459342554,"fiRILVTGYBmh":136715152361,"urelrp5GXn1e":647506197213,"T0DgTq6Vbyna":796607677333,"islEk3T4Rm92":684963342733,"PcvbaotrGwCI":98983941204,"ujKQruEMX6Hp":786696784406,"KvwY81wr18Pp":655235010662,"F9dbNGvDBp78":383078279808,"oaYFWLcIUxVD":796664821799,"gVr7e1fgzCyJ":392094486521,"3lR9V3MT5qg9":361943767929,"5X9JloFTQTWk":410054368427,"ZqgvC4nrOcme":234467408943,"xVODdHSsl9qA":860919115713,"zTti0kDxYonM":582400960832,"hkuJauWD4tOP":872304307720,"msFvSqUXMcTs":261106247330,"MyAoSgv6P5PE":982729349314,"EjCZ02GxJuVP":285599109741,"q8cNTvz0wJLw":255669689549,"UKsijchHAb0u":965811189320,"mSdJbgfytkDG":811160855146,"fOK9ePOgkRdL":924219019746,"gx6cwTgZCEwZ":315427343274,"CI4lF0qtp3d6":579304236887,"yDbpCpxl36Hu":851174517550,"OoHKkemVauZx":667140706296,"8kc1HrpAgA7h":996888301023,"WrMx0J3iSnQx":29166907292,"i2euz0f6vpIk":325979103162,"dLcqlJIQKvGH":548699183430,"DPHfRrHZFEuv":932119563871,"zzWSR4utC10P":606242980842,"0sBMcDLBeR2Y":166962776232,"vwPHef1rKxRR":338724442113,"TL73L4iqmWl5":45928684889,"XW3xCI29c6Bk":85721971450,"4N9wgb7Bm9Rj":715445824356,"bSKiqhc2LaGu":761335310188,"kDdii1Xj2uI7":633620352983,"taaMGTO7KS0H":244921548486,"jYvmH6WF0NdE":428194040575,"N3IWIuRJH7jo":624226256774,"lbu4JAu7eiBy":759603667479,"xwcxi7P4VKqw":84885457903,"l8wdGzNkbdQM":122968843023,"KkTzQXlYVX5W":101461082658,"HsNTXKMrd5Bq":934527511001,"CaeHDbOqMBoC":891324399462,"dtisgasFFrMs":296118985586,"IV9DT0qigRwv":513269842949,"Cn7RBqLbLEtM":195650357291,"3s1Cyjuw0tYm":157305314093,"zzZNVlbnpP27":790919846968,"KPsisIiBMeo5":710444418300,"IBZq0Nq2bIDM":209792230862,"wR6zj8VrDBdP":357562617472,"AfpALMghKr2r":717101720662,"9brrVSN5YN1Z":479534046463,"4dQyb6sVBHbo":552243882255,"79gG8QYTdOWs":901325498848,"TPvyec6pQBJx":960387270620,"rPejsAWplkqw":587488944685,"8kB74YQ5BRmV":967205451058,"TFFWFs6lxOhc":950925716080,"E3ypuvn0CBo9":371910703864,"E9YmMR4d3ThQ":664114767700,"fBFu762l8SUE":123962170490,"6EA69UswSF0s":51368513390,"tmZaF8wf195t":404359157555,"c2FVIOVblbB5":958489062123,"3xc4QE3KzgHY":839360989141,"BKF4PaAKJq2M":691550159796,"yjSudnObGnRe":225189627764,"9tRRK67UsJQX":237260621703,"4mY0FxCkNnb5":971370221717,"EtYL0XM2zgyD":972609570114,"pCtAct3iVMUx":754051098505,"9kCqvqL4THk8":17505690195,"9rocO5wZzpIE":626603481993,"Bn6IF0MtY8KQ":75837879672,"j6bjnry1NImP":294785336164,"gSG6WOvOdIaH":5888828717,"UYdbmWVhIDMS":706574390412,"diqy7GCdWzp7":301248943772,"cWOktO2W3q7F":358568855051,"LKAwPgUkhIms":294689391273,"MH6U5BcHS4VE":783000517166,"NoUckk2FBoGK":512351578462,"o1rUcGs33awx":77491972767,"6kpdhW4JwHvF":314370251032,"x0o9GGPbzQHr":676542575184,"A5o8o24LESm6":697189115467,"i4waDOIgFwt9":313544642655,"SVGStmKIYy66":610723006062,"9oe1bdLKZYbC":691594564646,"x7jUc4vItJKi":217982602087,"xABJScmdDxzX":150958601041,"MIidJH5v8aeu":329474872919,"VRNkXYKxWmhM":219153547585,"tnsYle9EU6bg":176903093746,"FLSIfAMOGgw4":33048788505,"y166GdIGPa9E":218835178490,"evhD9xlkhxuV":491061792236,"JCDsFTlIDZHG":472990342933,"YFINARVY81fO":632548512104,"dW11aMBsvsG2":187905213697,"8r3BJBxTB0Tu":752280563855,"H3dZeqW9Vz4s":265043210538,"QzMHGOMv5ZuT":702374640927,"sLkckscm4oTo":515636963953,"iKWsYYC5TIkF":845334418872,"JQoOM8y9zOcQ":597338463991,"bAqr520td5RI":669456936820,"EZR4YdhhjPU3":766216137068,"BL0t84cSjtTU":28697845387,"baej8GVGagVW":368895715081,"wnu9YhKAmbQd":416413417550,"VwN2swbQ31qK":225041173558,"QouSg9iNtYiC":15785710493,"StEAmW5h6YC2":398660195878,"HAfoc7FZfxBh":743076977745,"aBCSQGsDjaQR":166898430121,"bO8szOfUc9CA":541392931655,"ynFWYKKrctTK":690209435358,"AHUCdmjaU60h":797401368490,"CKen8fr0j09u":871294450432,"zyaqTVBaWDmy":835956184821,"bBlOIk6pqsgX":97515613942,"RjitzGgFIjaU":291780689995,"ddKdFbsF0zam":301723998405,"LWplH3knhcRw":131099849453,"QmtuyvNtJha4":427644278733,"Lk6AqOe2Gs6J":923929033337,"AStpqdH7Ohjz":427223189728,"44Wu7BLeJZge":680185031890,"6270E3U2E8Xd":972215822999,"FF2TrbgcM97x":439242501367,"GB3D0uVHlSad":457613798063,"zxNTXmhVsNV8":241829550813,"UfrfVIKLVsvq":329239120497,"UEiZlKMEwybc":783247635043,"MPSUF5DEhHjd":135438363606,"BRODiXHpv1z5":791192936505,"0w2BEsxl8KqS":744272026988,"ThrK8TYZ5NSl":888120409369,"glk3qzxr2KA1":156706073250,"5mtOCLJMk5V6":970538470083,"1eAHomj1YyPE":855689146567,"op8N3E1VDmV8":676925372119,"htUMuv9YBhfp":266797210477,"0zKScZPuURrx":533584569533,"0rBFlIlPTJNO":422643905644,"NUi3E0lajwfn":132654078695,"Y4J4GLnOJSe8":988832541527,"95tIIQtnF7zf":447121010709,"xdoHZ8VWAWSA":431810720746,"IHYyAWEwcwUj":467737459396,"1qum25mcHEJf":717362846442,"unxCT0uDPRDA":939436429499,"sz3bh4S8rFyB":241308002445,"Glufu4ngBmC9":462143930295,"Kph5gu0sN5lB":88096221244,"dvfyfjcVhLVH":406865966736,"pJeRbKb6KQvv":615984860148,"fLXwgw9oKUvR":99406264259,"BChJSfdyQgAE":916233808985,"FrC4Y6lCcJkk":127733077128,"pcpwjcauI1BZ":381719724112,"JvSquuYRRxO9":966245928956,"S5WeptGnbhaj":858455153625,"sjv1tBf9GkBs":202030152728,"KcTnhg2JLTBW":267549403261,"naLFNXlT7My1":837850877999,"zolDtN6oXpbz":464152718247,"NEpf95xx77e9":11924702293,"52J9OXf64Xev":345133550042,"5hD2JxWsTHTw":212017954491,"6sh4UEMw7prm":23817848118,"He6KxOC7r2ku":444037657759,"6YDHF3mdlrHk":763330612304,"9toW104ENpNj":940216917995,"03FpDbCur1DL":654017943210,"a5lCywUHxs9v":793559000018,"ggLTzbHGAXob":89753364274,"hWA1TrWREYeN":889127899231,"NTvbdOudrI8a":868964008589,"2fOnAKKE20LK":661396967212,"Vo5mJD3JfKsX":14632026827,"JXV21bLheuAQ":886388546585,"VtecKU6QZcbR":22354215865,"hLVreDIFXv1e":959301682303,"Af5K955I6bQd":469861619974,"ISdlR3fDbdqh":985722972960,"8aY99Z980XF4":66014405463,"mqs3abt6rPcT":729015880499,"B7akju8EmxlW":297924498244,"01eq6wBRDKAu":780966378482,"SQfThXYrGeJS":263633025448,"nbrlQDiphqAN":586110979221,"BkrpLhQyvFns":726587405738,"21UtS1hmSiQE":887102602915,"PYgMQyKuDUFC":438758651411,"JQy4cyoaBXpf":595534801248,"FCYKhlhGECGQ":399198813045,"8gB5KEJ3q25C":288222401785,"yj1PakIiZjqt":208421931825,"aBovsbsbDXvc":984752924461,"uk9UmSala88n":453767686805,"yVosGTditvFx":145314508555,"yCVBcY1QnXS8":742900695160,"6KSJm6UOcKsP":93519092599,"03JWU8Rt6dWY":756155989099,"YAkrwFOfpXwV":35275157386,"IOqOWbUnEzz0":980827453564,"U4xnAPCwRExP":776747289416,"FKLUCG0JvO8M":342202899080,"W0qfy2QGE1D2":848606865834,"5oFmyeUj3YLm":870349848532,"jFOSGVbQUf29":379916484410,"SiUvK8EgfEAV":977305532192,"OMXIOE9mtY5V":300863972968,"s8wbQMMMi0kG":577626113834,"wlQPWgIMKKE5":157397997874,"4VkEf4PmcywY":196185432066,"MvGwYFdZBT9t":336959626197,"Xu56QTruILzZ":16946389224,"iLgUFAO4Vjk4":545867426956,"WE4tqyyLyEpK":710757069305,"giUR7y4qn2LN":635917673501,"9TGM2DQDfPER":902131480482,"jDYocyiwEx18":487520857673,"7Tt9g7rXnSou":100871049765,"GiS8WPZKwMmC":355797585907,"5ZdxtCvhqNYF":533305728989,"ksqjMe50fjPk":778270826196,"tETGZiXR7tUZ":11662391371,"3J0Yhkk6RIvj":31369791766,"LjHfeCS3Z4Od":240736309390,"G5UpbDkn7RbU":624341158503,"OvfbANhQXoyB":452217611017,"KfeW7xROlzCx":377552761979,"gdTybWYSSNzy":7449160869,"oyr62aoD33zz":771867887231,"i7eb5cdknwBi":431059857815,"JJmrhtv4V1rq":211599570709,"c9WuGQ0i9HrY":374808787822,"PWhKfGtJUWrU":282346310939,"0UwdOGkooNyv":157657316819,"D5c6wk7Fc5CV":493605386957,"gtdTJBPFhEwr":971109947138,"wVIWv7o0mt1k":852895841333,"octRR50R3Vqa":925329032021,"XSxMndCTayQB":468795475117,"4MY1Uy7DvtAG":487290502715,"iacQL47ViLug":99789460788,"ypRek4hL6pCK":189659258201,"JHqbIeDDRZFh":971226616871,"BYm6VBqeZqcf":258687983168,"DnoxaikvaRfU":852265957920,"yl26oonSQ3Wt":554093548843,"TaQ4NZ1kGNqu":18014555503,"sIASMDPOEIEU":511871492336,"Cos6iisnHZvy":849725728860,"NVUb6BJXmyg9":148740426810,"SjNQ3M8IWSOz":949439921317,"whGKn1Vg7qrQ":650082445272,"QH7g3OSPUXbz":499645072611,"w7zyi9pZ51DX":140159187838,"2XNrSL8PTcbe":376168875839,"p2wZkRsJLfXy":604244269062,"4XWS1poG9XWG":328779563167,"aCCVP29VeEy9":766530735058,"z8nBS94GZM8j":457503874910,"vitnxNpH00qg":960206035541,"WFxHNE8Z7cOh":963965708982,"TacFR43MD9Om":814954253274,"yCUI0RYCeWxy":837550824895,"MgKCv6K2hdBu":927448723419,"bh60WczgDOxx":777030677323,"Mw6UBtmY2mLU":450569326110,"RaO3Uu93JIKR":855017914629,"Psdjk1J6p6xi":89640457211,"fLnlTlEcUiHY":653367850923,"Qf0DLmpNoHyA":237681090170,"FxHqvwO5tsuE":183292537477,"1guHVn5x0jtT":707584329869,"HWszvkDz9ZDW":789906690058,"T8zPxkckHmtb":951609598206,"5NK7rPzIUne9":898889214481,"2SMTHzd18dKu":261716873077,"odX5WK2XRZlp":461321864869,"YtdkTb9cPPl7":43309115024,"7f8mH4trXPiO":17826791112,"pcObam5C8Z4K":75154236515,"B80bDanhrnzu":158636345096,"NzfszOdJc5Qq":425832900283,"3EU1f8rDrGeJ":826343306849,"krkb69L5YDdy":566946669589,"TzW8k3MRwDGw":51571227232,"llSaD2zPNsTk":45179773412,"AJFtTH08bxot":458925984234,"qi6DlDnufs5w":108478026685,"z3A1JUFDqCWb":146825912779,"tVsGiXDqLduu":524565727178,"jG0odF0UpLrv":926720138216,"fLFutnAZSLnY":11344348663,"K49mXv5GiBm6":108242179712,"JtN1wNrVSDiB":966555669746,"JUqkTNLAaqES":86389163216,"ocmFLiHUGiof":332629873963,"eQZLKYZBM3os":66161284375,"OM5djqHhI1Q1":331268395355,"v29W109zkryF":26589041906,"2zVyyqoLzIkz":965093627972,"eI9k0VqQBKeP":289650644835,"akEHZiL7vHxg":374524926463,"HbXTdUYNligL":506605356475,"4K4vYIVMK2ym":491040547865,"OAvYfhADZMIh":915656760293,"CjSMEyNLVQHx":781207720406,"pTM0BLG4o7IS":407801148301,"YTEErDLF89Ww":591799710147,"xJFjfnZOyO5A":937117059830,"fk1Ti4vaqmZM":393574774194,"HYoP32zTwm7y":842449498607,"kZPDQRUvJRW1":825739757616,"1tO6kFQkrM0Y":968721252836,"1IsxHuuI5mkt":9808198655,"9TIrctSpd9Yy":260393116085,"6UBO64PMAODT":737715449252,"AY2ftynnG1QA":693822322426,"XwpPalHqMXvS":681806254947,"0tXbliuumvsk":669918712643,"TnEu1EyjxOpC":963570795073,"Ky0UeKjPfooJ":862549411498,"qhJudbACcnJG":489105735190,"GcopZPfYBALY":796145024755,"HartIgXPYc8t":725628076948,"NTGLQD89r789":456519720378,"zwiOQKyUHJ7Z":697376593916,"ZXU7WhY5Dskz":961957438024,"jLcRgefQvhEZ":163179311388,"NkKIg93MrXwj":640798817934,"KWOZIM6UJqty":461531021932,"GBKDJtQNfYrv":161805047174,"9MfRaKBLwPj4":935065154895,"BRmj3LpBxUNu":187158465699,"SogGaOzP0UTn":560980202377,"yejnrnuODZwp":670910884825,"gk21LoMgMR7Y":249065518250,"OsJW1vRZUDeT":760082682105,"R3BueWJFyBEy":134668977108,"3dn4tKnuODlo":512160022505,"NqNAydEeTST7":857662221213,"ZOUdhCIpmnDC":821033269627,"dlSUHoM2JeB8":922821230532,"urJq9VF9UMNp":716201549260,"LfyZgknCDC2S":926332390180,"Mw1VznySwKhD":414299451445,"I2zJsGQlI2eO":302614388592,"yLhtcMdBSVC2":188180237571,"qRYYhFAvtjvj":568854088467,"5rVH8Rhld4hL":963997885826,"iuVkaxmLNSY8":341222728556,"fbt2ETXQMthf":25652926257,"T9nmUjrVvEIe":508649080518,"85uLJABesKeO":102932859439,"jwuMvGbU0aKG":117269050898,"savfoiEtOswq":969944463476,"Z80tvq5hCOcv":182016051592,"Lwax7tMuoJJP":653490335135,"e1BdmM2zZSYX":759985708328,"X9S52gf3gfez":282669298126,"GT41rYUGpbHO":993152007863,"p586T9rpTnab":925086965756,"fwNil7GtGYCV":348728357502,"0FlUG1d8TcNF":993561363551,"ivRs2ntomeEY":89288204313,"YkALQl9wDIkB":220299085231,"mY8WKFOpQW9V":199007433507,"o9ZnW6ffCP7D":831860186681,"DcBtbZA1Wij8":911909711121,"aRDFELQIgJ7e":282912107442,"C6D2kUqKDysE":160159164588,"E1qjlawQIK2D":228207672065,"zi4smoYkQ6TJ":529986165952,"oZqOSJYrTasv":732124741081,"rz34RVhxiKPJ":990266482045,"BlzaRB6FvJoC":450789075683,"RA7flQvYBEEe":969029463858,"zqRaXp7aiKm9":502145432893,"zfgA8Ne7Ebwv":848502233316,"aHuLwKa91kcq":970082441438,"YcQsBu7TE7rQ":972256594948,"16rb1QMhBEGM":539508077351,"9BChEkZdum3K":306039307113,"BPbzOzHLkG25":223115148538,"kMMjxvNU7CII":196239708534,"xQFKsCrz71yB":546582076856,"hmxOpt12H1GZ":823755411471,"mC0PF6Ed6Lq3":329453771775,"koz0dj6Ez1q1":188040049063,"MMzvD5denG9V":654574058706,"pjo98AdtroqX":680907620308,"UxoCnQAX5hmL":312111708880,"oZlWrDkO3NGu":883904077926,"qgfHrYyPHRun":693991189624,"BGoNQGbCkacD":897854743116,"4Yj2aTJzzcz3":141968202821,"3xNfOJKmnCJc":851300655616,"kxL9ZvcIk1D1":59101709164,"bioxMwcEQbOI":834207717320,"GBJhhBgjunCW":974087068494,"fD69snlzCuV5":957411217382,"SYlMdeORLBjm":845712808107,"XgRuKgH0Phpj":921505761831,"eEkhvsy48laD":869001476541,"SCA9SE2wvFF4":19564024455,"fJP0VbmJoJjC":632464987944,"N9RPTHnl63T0":483705880387,"A9Bv8bjJxjTi":975594434225,"mRmhYLCkXjP1":119879758685,"PB8TWSczZbha":55264216083,"mY4GPqlYvm0O":802995109867,"NowT8jmdLOiJ":743514330210,"K2hX1J0asRRo":267724012143,"3oyeE1ekvF2c":982966396879,"CM5BuLD3yKaA":67604622819,"ZvLk6ZQzTBUF":770826747151,"T6b8kR4Y3V3g":459839746419,"HCzGF9k7RsnJ":330950975331,"vAe67rZ60u5P":84044941571,"6SAxL7aqQAx4":922517944944,"6zDXlNlNOoCb":64010292762,"HO31V9R7qtLm":775219066997,"4OZuMFivcTzL":402655311254,"xt1KhEk4AaFT":470127503801,"NWPTCgn3Uw1B":115526306248,"7sq39ssBV6bY":887894048698,"vdm1CHV9BIL6":153733764680,"tO0e5soSO7Bl":244307266008,"ZSNFksHRTsQr":239158371455,"EAO0twbsu4Q3":667657122597,"yItm2hVN4b43":997078296291,"1NFGB9ZlWhuA":614295247328,"kF0xvI7r0geb":58918357288,"EnL981JZ8ZZB":676146931606,"EH431E12Fj5r":369636847004,"WZBkQtG6842H":899146400691,"zREcHQXOPxDY":146418814181,"iLM72APkh0ha":945312761010,"6kiMgHsjcAiT":46841620348,"3xDINvFGdJ3L":715552520199,"qrD4FwjWoimE":217731347122,"0nBEEJ7pnQDG":239864959901,"FS63szacoFwa":759139257536,"CLK0wl1bilrm":93730422548,"mGnkWzVZxEhS":614372520350,"8Zdwp2NbFJXc":113838661315,"JZR5ltC4jlR2":907084646316,"u3YiYm8Hjze0":765096492854,"DjqF58JKob8x":404219635963,"WWTh7utFgRkY":194829019878,"7DLfMvAUCkfM":335530143197,"TEbjrTVL7Sgv":813731523231,"K18Ro52dHuGP":584589196982,"WWrh0N5fp4Y7":182950634314,"7fLtFyW1qKT0":662496330647,"W9H5efTrfTeU":335418799981,"sAGG45xpg5OM":5155793209,"ssNIbloGbfxj":523713843343,"pKpB7djR5vlu":688571975718,"s7p6ri62FgfI":257090060107,"SGq7q0d8J8Xx":719441079493,"xREARBPtTDcD":319472486149,"3N7Dc9XSuboM":794880097743,"lpGjidUzzfkz":350905027966,"9P0sRWReAPdG":709329005739,"bMmzjlH6U4x5":947225764354,"u6awBUeQ8iat":888351454321,"7R4cYtJgrfZa":764385485245,"TyB4LJiNpfqW":778856980106,"xipzgOI5zdwj":842841360638,"Tmipv9ga73HJ":775421878324,"S4r26S0ZetAl":273266647232,"Y3rV0M6eJSSb":912994233935,"nhbBRoBEFdjv":485778908931,"pCdZGWWkx2ik":451450788845,"TsMjj2jonJKk":724478149634,"AVl8rxOeNpQr":40429696013,"6BDL9gXTdid0":918988651610,"O4kqW0e3LM3q":936258728164,"MsdeRZanh8kG":202512265486,"ayDgY4w0rvsJ":190903542135,"LgT0rPMahSBN":100627334850,"uGcjblOq7zXF":583654269814,"LYfi4PcAJLRu":932648026247,"PcXcI74epQbz":515542637862,"6isVeuMyB7RY":657313024182,"nun7243lB9OL":115067895812,"8gqRBcwrhWk6":925159165528,"iY9cF4OMrOxF":43729422523,"0uWIR5JkoKf0":286000237337,"eKrbJ4VbCnVq":502791839425,"uBpK8fE7ccs6":928923182533,"9pyPtYGzG16I":617788669995,"b1fIQbi5Nofx":843416772091,"8glpc8wT57yZ":40164821450,"WThuYarwGzPl":950790618160,"LoXIjYLXP4PN":259891892957,"dPRmXutd2fW7":395981200259,"36Par18pnxr7":715987409869,"VQC9eDM9IA19":579401582854,"85ClnrQksmRD":630100131181,"ZOK1g6939UzL":393540377539,"vqAp4HxAgtTj":893919184477,"ND2dbiBSa5XV":210545194116,"uzcDAukTy2xv":60099265182,"r3ZSewwo2Tol":192375260034,"RjUbodGL9w7w":866580256288,"c41CahFgeWz2":891212689066,"n2D48739dE31":662484655811,"G1PyOWStsfLB":616597325211,"03zNFjV9RWJn":753600676222,"aE3hcDgAtwRo":519657721846,"scjaL8vdsWoe":796485604212,"cv41WvfT7Ica":571894559870,"kI6omrNPxOp9":125823860108,"blcH8mTci5BK":982066678024,"t1bBy5SUEplB":516367543179,"8fdTflFt4fjJ":376181662126,"LuVdhf4NXOHT":39501683221,"zrHgL7iWcvl7":675579659667,"T4R319uzcGXY":512327131851,"6Bi1GW2VnTre":129821113733,"bw5AW2TLiuyv":221758004668,"DaRZfSRtXf2R":674937699454,"oBvPdW4hgIRC":593983751365,"pqVAAoZ9wY3y":914555813704,"4VqQ8Aaknt5I":561661043706,"mUtN940y4VZk":778316580278,"lREIJyGWmuDD":557672938190,"b6OagJiRNBfT":162817834594,"Sp257l39dVax":232130378689,"aXYg319hWcU6":884536717430,"ZVCJcgrNQkET":504918202368,"HHakXLBKwjlh":825266427328,"U2GyHoSLlhRI":905605967868,"5QIFMCvwypMA":103837207965,"TWGewEOplG8A":751284844696,"Pl7mUa0y41Qm":28677001804,"ToqY9Dc5lTIQ":164393272486,"2wEQvrlemmDR":224500206201,"CDmtfUziFV1G":491687068843,"aBIMoOeYYQ4J":794647260426,"BoSVvBNbbIu3":95545536403,"ZHxjKlLkEiBv":851283841471,"jmEmbdW5lq6d":888732697959,"lY7yP2kCeLZ0":522569868120,"iwEkG8qRw5OO":61960893339,"orwrXybzafBQ":438076596378,"YrPv55OYxSo5":561198267153,"DF0IJXXdwdeF":533675365533,"xNqtWq7DJ8WQ":603181938271,"ImMVsrqU8jLY":731320484828,"pKDoBVqCgRZN":954595832859,"zBBUQjTTYFdf":729048505747,"fjkP8mciylvC":961515094768,"nJppeaSArbID":224547745638,"Tcs84Md6qqKJ":404818853722,"Z4CGTKwMG6oz":756435175592,"Z4JS6d0xPEpp":346002022218,"GHllJAgWR6IC":361661030942,"SyTtkr7gvg4r":112213774960,"VDGcBHufn7VB":560825711590,"urz7SMc57CLa":636596198710,"14YopO1wG9n7":475407009662,"vytuZ6izNZOA":408326896051,"PxaLx0eHEilS":480661441767,"ZZnFJBTi5yXa":493988097748,"03gcRlYMjVhf":747461096274,"iAxokpWhPsRw":772876675278,"rPhOnxDCT1Mn":841012949768,"3eBJCHhysI7C":216628349447,"rQlNRkimj2kJ":848709807435,"2pHUivmzU2Bl":293382538488,"e5wnoC6YCjiQ":794788164643,"VgAnhMEklMTk":332463342126,"sheVwESy14dR":727090483990,"KEHUIoScOO6N":463979634314,"S5PqLTD7A23x":675551456121,"dLousnLi0d1m":791170385769,"OAetyuAm69G9":699356050260,"5cJRZArw9bqN":289035862969,"Tny9YHzRgFmG":639325522862,"NCnzC9F3lpRL":319455765293,"k6njTxVepI2W":416379185411,"mzzdHC0GKfmU":490083823548,"o6BObuIkUrVr":60391877969,"XVloNy7JgTgf":453750155744,"mBhst2eoez5W":117680087257,"XlKD24Vy0Wrl":584412552775,"0NxwzW3AsKew":488071685244,"VFBir0ywULww":896595396688,"yRif5Hlt6x7a":997548040477,"RJi2yPuCf6kq":580896402729,"q60A1i166RDy":736951603011,"0DLqSbtR2uEi":438490823176,"tlMN7k15N2ak":653330411631,"ZxJmjCTEsJ3r":425187116810,"OazhxjeXFq4u":751598980740,"rcri4VaT5aZI":904082361748,"Pdxz8ZtzlbyZ":411878640177,"4qKAFbiX2oER":160953384424,"tuDAGbkDl8QX":959471957839,"SK5oeUwE4SD5":714820069242,"4vqDuxA2qC9a":947401515273,"GfC8uNjEwSJf":474923816777,"U4NHPpOC0RzA":891426114895,"L7hKMRMA3CEk":57450716268,"NKFsByFchjGb":956921099150,"oo4izYec1xfO":867894536113,"LaQG0vIpfieA":416505085860,"uYez84j1Giuj":685765859692,"Hb5M0w8Bf7eo":959612117807,"MPWNDdno5lq5":755717440902,"wFkiITlliGVO":204295046572,"zJUMXKFzETO2":207954799935,"QmXeX1uGH9uf":25973272578,"sjnGNLnWtE6W":784271880952,"mTGfXGIF2uvm":116242338850,"kCDryIIEoa7U":888354110179,"fSWRJMpcBI9Z":344932963635,"3Pq7NsM5kE73":605826578230,"gNn7Ncrrir7A":201046926016,"Xw7t6pjKYJYK":918002438101,"NtTof9H0IsLY":751087727772,"xn9IhsZ1fHeb":527011327990,"yKBhtcEZIJ0v":519515356047,"AGSwcB3CeGRX":954918539124,"wANaxpaMWpNC":254547845071,"X6DrxfxjjiqD":508541212926,"iS86f2Fufcf7":831926984306,"hioMoc42fAPb":675163809518,"tDFtT9fuUHJ0":211944980799,"lnbv2tquQqiN":571062120372,"lYaGAdS80obr":905358957600,"5SNk68JgDUy8":809575826503,"N0Q2932mPQCu":539900453453,"Uv8LGb4wvPk6":947240941066,"swYeKKHqEaBG":394009366319,"CqMaGhP1Wvrq":920975567866,"YLCq0c7v4HTj":594343801357,"jMVvQq5KlsyI":315661419287,"EcPGkD45Bv6L":64230931426,"DEvygawuTTVh":27703714816,"vKRCKH4efXhD":255640647972,"roWfiMaOn21I":209263616410,"ZIOqwCr3MXWA":546391279456,"TQHunUKZ9v5N":13525397190,"dRxLLLNYapSm":556981635257,"yukDda8bEbf2":41957741831,"9C2T3FEsaolL":328570239429,"OaLzMlwMqlXK":253045258340,"qeP7fhyzhCw0":853847112881,"ftGikL5cqcN9":783694490398,"gejqS9iRBd0O":369249963586,"VwppChfzaO8R":50561155027,"g5sl43KGdsS6":825025890730,"zba7cpI29K2r":880456390425,"NsfGcUrPs7WG":594392328000,"MGJ7ZDWCnn9c":629179137676,"vZOIArLbOu49":93920517770,"BvsA2fp8rf9n":156081701091,"x1wEJo9nAvkB":629688959688,"7G6bPdrHVJ7w":754206746495,"iAOA564NsO8T":973334681180,"Sbr4juMIlO1T":438852578884,"jJwYtzFOsDue":523427495485,"3VDfVG9Oz8Va":10003950120,"rXIb7X5NrYsf":893508052442,"zZQOAdfOZycO":701639106408,"o1x2CVNyn6SN":111232355604,"ExMP69ywe2jB":368959161938,"GpWqCIftHfpI":311618347157,"YB5EWPBGK88H":965037592933,"AP3l1UtatbI4":748093356796,"hkrcir9vysKA":603963189086,"JcJsyh7BgxXC":412026565970,"1c1Cx9Nmt9Vu":882599511429,"LtMPZxtsQDx0":332623714091,"95HX5aOETcUv":29429874343,"dOY5RaagjTNO":247577799268,"GJvP6nJ9n8Ch":413585663855,"2CRxO5pKCxD0":301285110179,"qGjrwCTVOoMr":830740987813,"CzbutpNCasFy":805653606105,"D9pXV1PZfgs8":587651073067,"q4ZB2iAcSFlw":837159335705,"g5bMyXb2t2AZ":954412864013,"okXRXMZi8AzS":192402388113,"nFdFVuusjysp":661121894006,"st3357tBnTw3":247376276526,"dKCaZLy1QVrI":363502771148,"qEWx7ROQ3Ls8":123296009990,"ZyC5anyEklO1":758981339110,"V2DkQnQSCwOu":283159050669,"ndsc97tFM5MU":984205051353,"zp2FNFgmloZJ":521260763566,"She5F1YjalYC":128216894580,"fK6ntCnomTCG":195149722096,"BTdcz9hXjIsw":686545567716,"tQUwfdppS0Ey":300656459080,"Dl86dWMFthfp":674544984403,"W4v1eBbgVxRf":263461503443,"bwQG2sCS8Hmw":611662999972,"BCYgy50aA5U7":920705871039,"luOGW50abZ4o":319130759791,"HzbjRE7ogP33":714618769223,"NFSV1euxWAJc":356918910130,"KxH8aBVh39kg":1640558501,"za0gwmAMF4Xw":123152783618,"ksbjgC8KFohw":929790474942,"Gvao15bdk4TN":900188347487,"m8b40VIWa14T":270257963075,"D3FCoR9AFsx4":954582353954,"0Ph5wwWtzZOx":275216639281,"FAEdgsv1yf7w":141566626280,"qER5oBNDSK4c":636727381459,"ddKgmQXyYWDV":477559987147,"r1lRWRhBbZsD":359144413797,"tFv26PcEdhVj":399918903734,"vJeqz2ujU0O2":789059390357,"Y2ziDTd1NnMf":949501276076,"sppD2xQYdIsT":688758394035,"APajkVgGF3SF":218300426510,"8aDGhEGeMSXE":291204849424,"J65Z821JXsqp":683945986549,"TnTnWgaBbnl6":833022856224,"19nUeknr2g9e":492268394325,"APLG5OLwLPao":572402690085,"zaCzqDHx6ave":1140083565,"sUHxkKAGXDHv":917589208992,"UgoKkvzLBusn":894964328372,"xVn6B7eWPkGe":847380037358,"yUGMYfnWhtUl":373494311441,"ZEzXa3z7hKGN":287772317945,"CwzZ3rIVnrPZ":292014931399,"xUHPZc5Tx12Y":20598093710,"9xnuibdVhvZ0":582342275735,"TwMZX7KyAw3m":70505554603,"Iqw5cmoeEkQr":613248259042,"z9x6mDsjKata":281975988779,"t0chRbcvtYx7":188580882598,"yEuYN7pAMUmU":465305602977,"6DHXnu3z164E":466696308090,"NUlrZMqk4opO":611398385986,"2Ug9k6YEE6Nc":396630176041,"zzsxFbzFv21n":10766467221,"DFclsNofNxdS":726610507659,"G9AiPIcB0oDo":870827262560,"6PwAvZYdHB9A":726454841364,"JzhwI2atBcv2":138131265371,"LuhIb2X50klC":386928761258,"zBWV8PkQLv8v":548181045092,"7GXdql4wAJMJ":371053714676,"lZmwTSiMThmj":177987570132,"Bo5a2gW4X28y":3934991805,"vVc2MMFia3rd":97198599553,"ugx5MHSLntP4":244625574489,"WWPaxEb6zpcI":113311373030,"qUAM9fRq8cx7":544212973893,"4xiBaITD8PNN":600933329378,"pEam2AaIQyxG":600790671937,"eXqBZGWJn3lR":253066051822,"DvOd0AmB0HyM":105876578751,"CwQjtgKJCe5T":479154143427,"s8BNa6qnA5MX":269382964195,"3SmFPIAQWcCC":516252630080,"kzq4Mu40NBS3":456404881234,"oIM4hlRfladC":117136534763,"SN4hfp8yy1qh":369422088813,"pyPap2hJ7AkO":936009576987,"XwFhurIvLAqm":630311034351,"WeFkaWQV45Vp":959339938958,"ZHk8AkZu2QlA":127332754354,"84Z3CQvSd95o":650637479877,"7j1UgKipTXHl":554150188649,"9CMCtp4kHcoc":28754036749,"YuCMOwIvybTO":70305641771,"yLGk2uLilgtM":452855941902,"JoKXpsUBZRA6":2754875718,"TjdXlmchzVtJ":483992877098,"H8KTbtJBRjxE":772367604539,"dm2V4e8yYjha":124386702489,"yjo0S3Hnwmks":902327542401,"cQlpx8cvunfE":253105920707,"YdjOiDT3bioe":56034556847,"H1BqIv5oN2p5":581174181322,"NnNxbnEvW2OP":835906484876,"nA1ZfUrNUQI6":348088403827,"W4fkc24gwSnu":654970490159,"rnG8nuaL6QKO":189898537829,"MsR4jikDeEhd":256414224631,"jklMntQ3WrbU":83968895926,"mvkpYsi6JIN9":160495241027,"HE8lVjS0BaQL":894034170554,"KfEJHjkTpPZ0":159260586838,"OZIyO7bvM5B6":689111701756,"dmLvkCtXFIVZ":300573194161,"W4ZVsEqpyKUl":523233276975,"UHvMFtZaW0fE":404322379748,"fhw92OWCPOc7":985494505366,"YQXxPtBNwnhh":701264759896,"1LnHgEs1l6Ov":922036990383,"v4wzdQ239B8d":398345271833,"AU9fwUZnhIyh":803537621307,"IO5TnT7JT0EQ":757604517767,"sAHXwo1mw81J":142718058212,"igUiCLx5K8gy":505945536670,"vJSIUawX0ZWX":199742501839,"uSGQhzRoNhBY":444309483254,"IpiyVc2oU7FG":827299494617,"ppbe3WcMbz1X":657306478134,"RbFw5Cnuw4hm":130998300886,"39WnEdv0n16T":623523435651,"4lecsNGtJTEt":807321894855,"kgf4DrgwcsoK":697450904138,"m7hx8MozrpLw":164702941256,"eZey6dbbA5kz":857389680711,"mojiUlTyFkPV":184530748527,"KP8GCK8T2eiR":686190550185,"VxwoYg693pL7":357619487427,"OUHj3EPIeIDf":161890717314,"nDoZhxiragp3":615065533428,"cSjAAm9lRCYJ":321007041209,"3MGsXc3mkEgg":838054157149,"OpHQlxNUgRnb":341398809043,"vvIwH1c3V6RL":787492181626,"vuvlx1w1efH7":104649247533,"rmy8blJOKvED":475765989070,"TB7Cbyc1H7vl":42018870009,"Ehk1JRtG6Kp0":295890262682,"xtdf9WHVOFcJ":403476053865,"CO7aPODxfN32":585258171903,"bO7uezr8g13g":788785190860,"pYKttm9FFnCN":177448795804,"Sc1Y3UMPMhhN":98008848274,"MAuargDtcWVY":151466904274,"Z2czRpRpaejp":86155245800,"KM7oOBsv5Ocj":470117748656,"EqRCRYyChVrp":269082171503,"2Gltd464QMCe":37281939313,"OzEULUumBdxY":976573519480,"mrEiH24X6NPT":486392053174,"D8EMdKhc43uc":850249317914,"Ai9z7jt6GQpJ":887249166696,"3mMiXoVQUAhq":277128102575,"sOtDgRi3QIyL":360198023399,"YGwOEDi4iY5y":582676505565,"Joai1HUzKlOr":122955468570,"EqkKQNPj0cJn":725124846226,"1dcCLlRd9C7O":167207445678,"qIdB23w85BTP":581474832076,"HS5zDirshTAH":331137403387,"berz8gCzdtW6":205819964855,"HIgb2Xx1BarA":986413957979,"eG5bnAsOJHXs":799621514091,"gfoaQ5hHGaak":229534073148,"elpaz2hxEQ67":259831236755,"kAkYzBBv2t5r":721833674980,"gKuV7ZaBm03P":434757878648,"X48dILkeCWQE":813259400197,"pouxV8HVZx0s":626539461339,"uKPy5bIu8kTx":795627164965,"KxNc3sA7Iul0":722163886703,"HbWD3N7m6AvY":94338421776,"7SwnXiGaeFWb":136122052808,"9VtpkRhT7MNj":896121602091,"oca7hZrp3cfC":429720528012,"DV9j9CpTClxV":107837604385,"sERPWNCzWIZU":699666387195,"vCaKf8R1gOzV":693125957979,"DR9h9KiOD8Pk":776698430465,"Tx3xybJYJXiM":554680991710,"lHjT47HYFFIT":436703850334,"V50nTDrQtWu0":641946641897,"wIzpOCh4QerB":138424650868,"0Ucv06NXmIUS":710190418920,"LlnkXus9Mm09":6074656518,"9kOj7onzOzho":385075271614,"9t3MEuBf6VQ8":622050878216,"oNn5Phr0IKYu":840307447045,"NFMulXFDVBrN":100222377218,"JcL9dbZyQYXn":989988049793,"cGpL3D0ig79h":998165722362,"oBgOCkqXblSI":988416302118,"XpQnoNGUDZ8t":260374846120,"VUKHKpRtqDbB":100918216152,"P0sgiDkgfiJi":240359839823,"T2BjMuWM8sWp":92575907941,"X0QE1uOmTLQL":529549036048,"EnLWy569KEzL":659208005686,"RdnJb4PhrNke":467429109784,"3iMmJGLyg4Fq":990372698453,"ojXscsmn9x7F":406601584346,"OsE5ugOO6edR":207281366868,"IDsJh6f3DHYL":193865872365,"ju0kAr334WCK":529038434302,"NUdMn9vSdPyY":779719112529,"NRvSanK2lmTD":57976850892,"lC62UTVDbiHi":941289377472,"4pYnzg1BfcAX":413581589417,"xinYKIOWq64e":666902970163,"5kcxy16E3QOB":631816589754,"Sh91vOcOnblP":642383603449,"U45PGxpAeyao":897321937029,"lxhPm1hWWE27":275023848187,"dNiHaWIe9g2t":732697503065,"lFCJyi3028NO":534327417104,"FuravVXYjLrw":560756264665,"CRiclhWL7DvJ":415554437646,"soD2NwVavO86":771990208071,"S8MKMw9mvOsK":542452144922,"UaHZeCSFAod2":667305638831,"YsWQrKu7RuSr":614433467828,"EZCBDWAyJg2u":574262525072,"hnPvWPBOQn32":223006347543,"moGsRNIZVeJ0":199135767741,"pFHDtCKYB3jV":33684903736,"pRSvpXu0b1TZ":703741942581,"Xlou6qGC1dp1":898087576399,"adaGwsmbYP4b":207325262780,"PDu0e1jk0eIZ":410385806744,"d9C2ye05nky9":424875287384,"3x1pNhS6OfdY":502950224438,"EvtGSoScHNyB":980145693894,"EV5L8LBmYAjE":485734384015,"qVvH2IA0palo":277249575162,"zSSIfva02Di7":868375175138,"9TCBB07cnR4E":797671260065,"uV2AUvzZNmLD":63540977787,"SJyODZX8BULD":207202666834,"6SxUngRFvZPQ":135238283334,"Bj6duTlQmlvW":861118478095,"gZiWGjDwUmfF":313466010247,"VyGBzWxqvHYh":930872470384,"hbP1Ow6Wgohc":578381014455,"qClDzHQ6KhKd":550090124885,"7iyDCT9Oappd":526894323749,"TuaAXyvAIdre":942469760506,"XaiEF3XyUcZS":546610542141,"u2CFjxAQj3JM":238695755795,"mWwyPCXdKKml":458543280457,"jNM7T3Rn3CJK":577776444195,"mFKrEIlji2GP":691845856317,"5rADu80lXLJv":185712229725,"p0VrjqXnVBn3":758101709391,"0MU8Hg7DxJL1":283689007357,"xegj2sBukuei":258746568301,"C8Sm3QyUM7yY":778301228606,"H47IjCXSi1G6":193668772928,"VD7AupD5pf1h":413460496368,"ZbLZxsBK4pxH":82241186854,"gZBNvLJow3mI":295633004406,"TDl7SbqN6LH9":576173055801,"pGD9BCL5WC8P":878280723222,"MphzZ8u9dKGr":501212336127,"llvOpTRwxj1U":352430259189,"EcmTfEhQFnKM":408417189908,"NRnHNlMBaTfE":99700298797,"MDKybSpsNvAj":702169324013,"E11cAKy4Lf7s":135460834843,"rsJcI1sqpX3G":162021322647,"DUEVDsy9h0dM":686870683810,"u5k9GGJILt0P":700755714216,"VVUrQoNiAkXd":671421754082,"ZAUYgBhChxJR":729623712179,"DPMJnoPuuqqH":852100257839,"P5aSrgKsdr57":4555884092,"AOPkcfLSRiXV":265216396089,"ibbdFUXBlA1n":885380898285,"0cHOPsryJEQV":280803238283,"kodOeNvF0MoX":513510675241,"huskxPEORHLF":515755741362,"XpkDAu2Ko0ix":859795525272,"9pHklkYHFGBx":215491694140,"b1jSay4j991v":452342604976,"rGiGUnDdNkZP":484769399354,"Q6DhYm7kcW02":504355088770,"5K3mbSNfTRMg":793837155251,"neRGj0VD59lX":368284426342,"YKghXX8AF7nv":60376456093,"NrWyG6jw01Dg":570930220606,"ERjLkiHndyEd":3596311166,"PUSCLTzpcRAz":717486936919,"HRgqvqpK9UTN":204795173284,"tLRoZ1UDpj6C":950027928816,"TzOTy57LQoZA":431243632172,"nfCzLpsH35Em":696067474135,"Ps1ADMVd7WOm":779352405697,"utGtc2Dj1czL":490647537470,"GZg1hdryRih7":308456396629,"f6p0CTCBHivt":697391776449,"Xb0WIabpuN8Q":695499194033,"lrcFy05CmHnH":985223442991,"druix9bHkE8E":169722622406,"PWutVBOm7icr":285357970685,"35DApQYddbUQ":718340631990,"cbXMU1eOv0Vl":542505977621,"N3HHuV3O7MEA":833619375260,"agoLgAVbqOeO":74888250162,"idlVADyNjRtg":662280471686,"LgbuLkgesZKS":255083773335,"y40v5YLj8i7I":659118022664,"vH740T1QtFJL":943288062312,"MiCRGlnpddx3":792584704012,"hXgXxSfrNt43":874273537350,"eTpKF34cs7us":626501001883,"lTeBoB6fXuwy":608009831223,"1cszCGczxwMG":955744156538,"RFdzkd4arrok":115125798065,"haUfOTizxuwj":306651728238,"3W2b6xps4Hud":567710367348,"YkTU1Bm6mQII":704282005167,"ILAhmO9XpNpL":506748558518,"QI6kpZEvnzcm":796814343878,"F8LZ78IoYP6H":712154545793,"AHsZ6or5h7Oq":36709713863,"GisbzYmD2ig3":302963092372,"1tTdBIQUV5cs":269178991729,"u6FxulP1JztF":355622277443,"GKcja0Yuio46":72443073527,"HK77hrXwQe57":479952847855,"lfzStEy75rpg":413284880959,"g5L9dWXJYIDw":392971438310,"gIbbtD9c0QCT":793274197276,"LtouZPs4JIhC":625266066782,"aEf1sSRVhcqC":561371267164,"yfK9dhghBXmY":623074134943,"OzOL4aehPgGt":217895814608,"xW5bUVqYfkdE":305456836190,"1B43HeRVbOhY":709199934323,"2nKs0gesuKcH":902286096312,"ivAmmh0hXtko":12882366053,"RIGNdH8PgU8Y":366457474252,"fOrv2t58dw42":194411693666,"ew2xTby167Wh":312042334210,"liDy2VE6hk5G":384654150548,"UkcntZAH8WrH":719944560427,"ChLAavBNfEmA":776499189948,"0EScylSMa41h":286814908092,"LD6qWHCRjujf":71416209759,"gQ7cbTxoBkqD":275780916925,"5ezq2KumaDTC":488809437916,"yfEAbxq22DLm":345705016215,"w0tER89H9x7r":878307347457,"g1veiqpb8UCn":197407579345,"B6XSCbNgQX0q":14685257603,"bFlteW5hxu0N":461150359685,"CLESC1J1tlXB":378360377542,"PdINb9CZr0lV":277183079329,"eiEpbN3E856e":310070275336,"XrMSIsLzsZDu":997437414654,"012w8EPquTqZ":326641278795,"NBXfA73cmtsf":393981964760,"C5XoMj236qk6":115470020515,"wsjXofxp7zZB":437990062160,"vUp6RmCRAJr4":620013751347,"yLlqdhpIURMC":101378818879,"trqHLSmLCy1D":671311284377,"6k0uGnW5cWzv":404009174738,"bvoldtJEMDtb":242327717288,"Ua86pv5FYXSe":123346735603,"4kb6YcOyEDGE":231453516932,"ucpoLEilptlt":866362033148,"00z5CePgnQuG":331728962006,"YZMswJKvQJ7B":919307236869,"CtAGd2YWVkCF":22289149934,"XfNiR858L8zp":165101914079,"JPTPG6KUCMoR":994110490715,"LcET1EZC37Yt":514239368646,"ycQsZmHSe3dv":819710379088,"aMGGH1ysGfRa":759140657018,"wkf8szzb7IVr":436959630198,"5aLa23Mur9Xb":364965065989,"F7JrJxPDqAG4":162593001505,"vntkHttCbSj3":94245986242,"Gqud1r47sLtt":565814337668,"xS52nNhZC66F":106466977590,"fPvu3caqlrN8":867256042337,"y8CPJX5asxPp":260468778778,"LAkqlK3W4xSM":946773460365,"HJtZUOKHbOeI":321903761235,"X6XRuxueVpG8":432179536610,"12DEhISIXvDZ":435852420720,"teq2OOiV3uII":45015739222,"EGm18k55beyc":486377932341,"rkgNUKl2gD3Z":713537691718,"9ixLROAebYSz":442350433504,"q5RrBaATLUlQ":661297768108,"7KXF9tI1s3Uc":924949110693,"nkSN0FSTFJZN":558324036664,"c9ggEGDdt9Vw":259908579659,"ajlbOmI9baHw":589849803466,"3EcamNINR37g":707600649281,"VQKrkn933fxT":473448965541,"uOKMyJcmkiUn":280842633605,"Ye77bS1R6Z6c":721474336219,"fAir18Bgz3wP":491146956650,"GtzqJlv7dTdF":262762601708,"5D0oP0CoFYup":34480476708,"TARa8s3XfVRv":849390772158,"s2WWNZFF6mrA":300402584558,"s3Lu1O1g5HdT":718597091804,"7pR3baEKHKFf":710741056570,"t4y413MOPguv":625726602203,"lTssdkZH5oPu":282764156915,"9ewCgaP81H19":966985187278,"8tvP9xRHbck0":41241894922,"Nv36LR3dnDhq":261414236503,"UU5eIeqUiYgP":399354424448,"2nQza7JVkpFm":323543478402,"7P64w5bGB6tt":561667720216,"3COEovvYsMJ8":129029470245,"AC0iz429xGZc":672992036269,"bnfqnu8kCm0K":62642971026,"J1dthzuxAzuZ":653391740588,"29yWrnPL64Fu":783100931202,"uvTKddIcIqW4":366162944104,"Zs0afAQiVbRS":169133128771,"X1YySNKZAPHB":178862333219,"FOAcLExUFdkZ":164885241341,"SuK18B38XQLi":154619459650,"KZuri38Qt9wK":715248305933,"g3OlFwzX3w4f":422648342171,"Sr1AYV6Ca6i7":164434438163,"YNp2tR9oDnLo":983833057764,"2z8tLCXIfuc3":453637371037,"VsC9BIm538Ri":239184367247,"A5iwoLX0bbgk":629994486438,"qzBBLc4ww91b":7173844234,"z2n0T6JT9jdB":957542308673,"STMSvUasERr4":181703869572,"0ocp0vm9YxVI":291738037433,"lPpdowM0mKpH":238966822292,"JsJ8V3c68Wsr":119251062587,"3zx55QwMKo7J":747927670586,"YGWB2QCLUjAR":45745305701,"qmU2qcxknUCd":508152303509,"AfxWHAE6XLFf":82718556951,"zQYOsQBgSPZN":49284675679,"n4qkrhg41EMm":517942138255,"Xql2UPbqTQJ7":290845256755,"85UdXog0zeVJ":134255147484,"25cFx6xonnWb":60574932705,"G07lELdng98I":996204319615,"mJmHhqXqEYun":306162854065,"4wB1YeB4a5iC":594922072036,"skaUZrVGmLud":517456781658,"GplxLBxaNHFs":262322041318,"WIupUWL41Cdv":612181129729,"NvtVfG2no7mQ":735825582509,"NIjMB3nZQ1X6":758296718551,"aWgJMMz4RkEg":397504599141,"AadRiYANOn49":779698092652,"jEzHAZunhmcG":845389908262,"AKJoPtV5ZIrM":964096189072,"TKL5DxbGlao3":633631614529,"mYAclycC6kJO":617300839898,"ZsfY6Ys5iA9I":337841014902,"L3wbswRtDUkW":898687046173,"1IEdmNYRbn0h":406271870757,"sHAItvUQN3PV":367700700355,"z3IqTAkgmJa5":868028219571,"IQdsSYnXC9GY":843166092838,"qfEoksoRL6X9":434934134348,"1j2uVagA06kM":938316816865,"4mMXxChhMSGm":972131941481,"bkz2YVJNeM4Q":688304436402,"7Q9kcTAntxSj":907605947293,"tqmqq8YfSXdc":762097473553,"ZVPDnwWHxcwn":419791818210,"rC4aNpGOp3za":165068576801,"IUdVt6SWYBlc":670283247926,"F50WV1sBwed8":374980658966,"aaW8ID3ImxSq":285556129574,"5c2K2whRRRGB":232665847952,"Lghu6GRPiiZm":553102866524,"9AX6bgJ3DOAe":438868565115,"9oBAzxLTgvfZ":33139671081,"XNYzZrQnX3qN":259901840934,"fw7kPmh6hsBX":167404934421,"5IogdInlhsEc":339609339044,"I6HOp6gotlV0":601178294948,"WDMF6YlFN08M":965470238161,"IyMkpmZh7XeY":283940919242,"vhqQkm0Qu8BH":777711495500,"Qf02nbfHfkug":729279267824,"9CaYlfYTOzM2":922700862166,"8B2AdlfyRbDp":902900013503,"ihdDDnvxplF7":654810857859,"O6r3kWTJEdLH":219738714392,"C37GZIbOLow6":808473225904,"DrZZUiijKPCI":857747119431,"cLBE4EXThpVC":844091088916,"cl8l6Up9Zv3G":304382532834,"UmIxOpEGp04Z":970115121275,"czu6AYFlKnZU":13446651660,"PDN0MdtUNLZV":910345832661,"VkMGyUcrCd6X":233901422892,"ulQCnUX6D4Dp":69991880197,"9eyAe8wTfWL9":996350039645,"U3yAVakQQOSO":541745040652,"LTUQjgrVLcbb":786881723729,"Yc4dTMXvMf2x":587731768811,"LIbLr7naEWiT":314314895133,"VTjfhPUag4n5":433501008197,"62ZusFDLxGyq":501173660180,"k4J13Qhku1qV":470928620696,"DC3u4gOpHr5z":806879126198,"Q26aydJCiOez":313077686346,"uQUAwo2w709d":133149070693,"wv68lc3rvaIR":145575827615,"95EcKWYTdQfE":410556492071,"zH0QAQSjtvKS":15351244234,"8LRF5siYKnhV":381470128215,"4maRKFumfNj0":475303495270,"2KUNH7vs9WuQ":762260523861,"cAcXBVB0Gfh3":258292855574,"W4f5kxZIAq5S":229864847612,"WxwCpLCCiEVD":775955019692,"VYLcb82Zz28c":992636557049,"Zj4rsqhn1aZs":763451827241,"gSQkwyPQRGtS":9374806367,"ZxeCHhFGQqzT":64341458771,"pnzC7yOvP0oA":419145808032,"SqTJzzSf8d1t":450158274607,"P5xq9n1EpjkH":5701983300,"4S5QvnuoqpKD":342700688186,"5W7Wu4FUlCdg":45752633743,"5BNCH2eQjK0f":701463110759,"DJXNAVHebmzH":702073163875,"jqxVSIiOTTue":534219753547,"g5QZZu3uUeJF":23932098459,"Na47xqMrMBOP":924650526179,"JtRJCVQLiFbo":257618874705,"iVvlSJnFtrct":988897119258,"zEoKTKw4I9Bq":821533574151,"R97wgerlQELu":889186632525,"QLotKDXBJXso":158956519220,"7pUOj8kcd4I3":427275819718,"WKM3jHeKBfJV":922886946298,"oEXEdszxUCGU":479352111069,"oACcyC1dcAPI":483021448501,"hkIIt2jx87wj":742753397174,"FFGcawhh28cb":388473647589,"XOjSrLu8z1EN":951812928920,"A2r7F8VIKZ7j":715374516494,"fGAGP4GVqpm8":99469876943,"2JowHSyzytWQ":923055869870,"WQsJE7hSZoBu":336738897998,"ZEwuHmn0onod":587351890799,"PiFEAwAKxPGa":203313696454,"yoz5g2L57ZU5":494865228141,"8j1ZmBZahGjh":328734615817,"bRLpXXpMjypu":711972944154,"YqcRC8wgWjuD":710037121827,"XsjUCXaqLI6x":455749453635,"BW1PzFqAPpxx":201298436987,"Rrdw9KCKjSxF":76084809115,"kYhSixTKIaZf":361273167671,"Dat2vFP2wc0B":254432264847,"hqqffCx4Ajry":632464317293,"91r5mYUjWZ9j":53264698842,"2DZB82r2RBHN":155529190718,"ntpL5aOPFU98":199232643896,"xcebajqFTvda":72779899001,"e6ODIAJDJtTR":304943174879,"eKU6nKdRWZEE":513697351970,"R2ZGkBaSI5Oq":509734693635,"FxH8SOpRPL1s":85550565795,"KasOFzEw0ypc":837479529548,"jBJvr3TNfOYH":209430479924,"vvyRDEzGZ9Bv":379618847746,"MFv1fHcUVjBl":533672839330,"42sjsoa5xiXG":162056874645,"v7CkRXfaMX1z":736064126105,"WJYJIK2YRvNO":485683302335,"Kdioxf9LHLi4":295686373904,"xzLBSXlbIBmX":635251737718,"NDL1u55vSBQv":97875726051,"ODSdKVywExQT":531180065109,"K57Utqh1MaTw":83545768244,"cL7BX3kQPHZh":276428865512,"WVAotW564ZoV":472074785622,"DQZxQwAz5ron":634601440203,"GmBPlIjNtRMW":790482579881,"uY4EyxOaPiHs":670682142357,"6XCc5SBvCLgZ":156522122687,"FNplW0fDXT4u":53249777573,"ePo5FX64i0jh":900116474445,"WBBLgo4bI7BG":484478338611,"fZY0uEoeg5X2":989835506248,"Jk6ehpnSc9ib":638238567795,"4qs4VSZ6imGS":190882183407,"5fJGeYhv43R2":825476072735,"471sEbP8tA0A":225276300272,"ByYXvUJqHxDS":903587748719,"gZUyoAjaab5b":216409913724,"HKHTLQo1zPjr":16692601568,"2ieTEIG6E35X":196639262315,"xPTGCaPiEioj":559679219013,"YYTubvJaTJ56":194044995934,"jNeUj2ushDW7":85576447425,"Pa7jIH86rMjF":598773059829,"YpzorY6FsLgz":962579541333,"3FGfvpTL9Iw1":92715008437,"Do0dfxHJg3Pz":603652161593,"uGnlSUWcT12L":165830269584,"aHuTAUhBTQ1K":58383305183,"MAUqOI1LIZmq":805318867976,"BBL7eYlIw4xv":795201639486,"6dnrBrPLp6BE":684188810991,"KFE64fTUikIc":145841016340,"Fni6zD7q4osh":253943676418,"tm2pLwYcU2Ez":173316511655,"fqLUyB9XPGP6":457468564279,"3awE9Pne3VuU":847103388628,"b2l3Cnxi9dzy":528956155945,"JiJbUtVuvIxg":683654874053,"wpWo4JUcofKZ":788575448258,"787v9egjZt56":829212048523,"tJ5M23gYlg2G":266689546898,"vNh8UODP1rNd":307102199997,"FZLQYA2vTimG":870217855480,"ki87eylWtBKn":297055255383,"FbLIjMYtY0eG":564302048759,"cYEiOOESydjo":714219119497,"EUmJJB5NQ87a":919942465703,"lxIR9UIMONWl":203209465397,"GtxhWur27IGH":768710240914,"O7v0GcV2QtmF":368138805671,"1d7cIopNHW1y":943447369420,"2q6V50mN7WTq":23569188678,"h2NmvE3p1BE1":631436305840,"IXGEtwFCmDm7":400658213114,"QvhFJ3jfWXSY":448102332633,"36BoYZA1Pl1c":490592753425,"Y31sTNFKYWeY":994907664700,"aVXBCAvpDI1h":229849647107,"Z1kKKBcJytdo":361550709706,"QobvhxbOTc31":698152347913,"twvabT0qqbG4":200184075954,"VosyvaTQtjB2":407215910491,"cOeJns5Gdx0a":67555151879,"hmi54wY3NQ39":712156113195,"toN9GwSudk6w":497927553976,"eETmrzljjjKz":55058755666,"ThinH4ilXSDA":865131840383,"KsxGK6KbE06e":601297083885,"v3I8VqbPCWwm":811793947289,"GMbXeHzUWIAe":685347561969,"It3yo7zWFSqJ":236036837898,"IKlDIZ5TNI51":644927792028,"dnj16L92Ul7q":462087758143,"jD1SiHipYVhu":464395878448,"rMhJIDd7UIYn":256304749195,"gvn5Pma2DmAk":474040148654,"Hk5E9WGvnI7q":734948734039,"ABQz7XLNZ0Hs":496733217303,"JBgkHo2XW0Do":986920797392,"6drB8uJ3uPyU":302015588451,"hBPSjE5cqvwA":662257840325,"6QTn4zJfODvC":58982929347,"HIk7jGX5MmPA":475966101629,"tzkxTdzsLNVT":615671169187,"QOOkUbAPB8PP":802788292605,"MFqFO3vKzcSp":712367826986,"61ST1YkTw3zZ":575309086262,"8JoBxYsCjHME":7350038479,"qIFc2R9rE0Tm":631054360638,"KLgJvcYb1UxJ":150075843302,"MA3VJzAdJ9Nq":276558457157,"nfPxnOwGXZtC":315892873980,"KBZnAHANJI7R":60135108840,"Cn2H5huVxvsM":624307631562,"U28Td6roKW9C":332457409028,"TOvR0vz9Rc9d":661372644630,"SMgeWIIYxAr1":581055153561,"cp0FkqaXaZkj":674686893642,"koyD7Mytogfd":270703822429,"NOmwM1mPuNWf":507557659348,"m653Qt9Q0ZLP":988258944504,"HFJhVtcgmHCN":957310225919,"OTsdfIi8RqZU":635243912347,"C8k6cy3BL8TF":715499601599,"remDGNYacCiL":811303690027,"XwgkDZsUDtmn":947667382606,"WYqvp8QTHuIa":222092011946,"XVIfeDuirqHw":654874180271,"FTOZRhcq2um7":678575343384,"9tH0GIQEobjW":98118698568,"drCRDJNJK6F2":452444686381,"80IKBq340qoA":738298311985,"Vgv0kwIN5WRx":180199440302,"r0Te0jK8hRLK":102309501130,"aCqVSYGbtR4O":245169061809,"A8ODbwbUFVCO":424137159338,"s6o1wJuYkOPa":205387031808,"PsNnhQZtK6Gc":124781906399,"rqpAXOmin2dw":803871138731,"QNuWGl5Frytx":586705521307,"zdqVMzQuPSGa":534183016814,"L5uqxv6ayfT6":426771194815,"xLDCUM7Lnq9A":800423103682,"LhrraDwPzSfY":38486879386,"1vgwDuWCcpBk":647097547398,"mBacgGloVTdR":930126510068,"MfAFAaoNBEIj":320969864208,"RcBNI9ywOhRr":207228454002,"qCSLys3OIIY3":828951799824,"K3RCVC8klLe6":941008562076,"YwPrPVPvR8vR":130994126778,"3udbcQeibvzf":492611519038,"Oob4hKYHb8hf":830828937946,"C6jThSUtBzHb":209860490251,"YPwTGD81hJVN":354646323999,"UOEHlM4MqZiR":401374242857,"qq6WnsH2XxR7":133425780257,"bNvnPT1SgW96":609195958277,"hw49Li9ucTbq":941170741146,"2Ig7YL9yOXfN":870568291645,"D9VAJid2UdZ6":44387508004,"4N7Py4ig5sFq":861350849441,"V4abHWqJNGuZ":238014421929,"ettAwRDczc38":245566492399,"ltdLAEVITGx1":387109684516,"gxsOsrhS8KnR":676514305175,"2ivWWGF0GZox":35447769104,"w75RfMRlPzDl":715328032087,"mIF1w3FskA4Y":757115621069,"4isUTKn8liVe":75688098969,"rOnxKqWi2TT8":243220885596,"bVaohjLSqadh":263128178034,"MgtCobugYyRa":334414503794,"PkMOOsGtCThe":644365784600,"XKIUMKTBUgC9":586342727722,"oz1dPUh2JYtN":341252648526,"wSMHLovsHQs8":570878805555,"uQgOHv084GRZ":231582217476,"2i1ieXqIVjo9":605669477560,"rH5UAL9bOR5U":813080801820,"MlnemwXRmgwP":561480616934,"u0SZ03wlSyct":210462850840,"hEhSu6C0rHed":710637812026,"ew6stQkawrxo":759468120400,"NG59LgVGWJnr":939813464171,"ak2XNmxXTDum":341368318226,"nEgnu80TvZ47":136003046502,"UWRo6E59Urt1":230867080815,"SOkQQrPuJZIb":934133551303,"IbuLZeHBSdTq":271332346096,"MDIlWNxucfqM":606842356820,"GEArXTui7gFv":222707913180,"iWvVGBly0Qhd":144287731338,"HIkKNtINuUvF":793011248234,"gIrWjKWLapog":576802653637,"UplkrdOByFOG":319438136475,"UvbAeb0E9uj2":31076380677,"SbY705jl7DMG":47314523058,"kxO9ISInijrP":837651258521,"NXNJd7X4dc9o":214756068984,"2zNW3hxh53dQ":750238393909,"WVL7936pX1d4":717812274085,"rLfLN7J10e3L":691932493984,"eCdV7zjBcONZ":103417464626,"pcJVa1G5ecu8":495725579607,"5CZWI4QDFUfV":481389655786,"XG1AaTQvCDbK":999319249751,"8khcQN0OsIHO":9003089424,"ZOEp6RCGdtqV":254425958409,"GpImZPPAZskR":699403183655,"I3PYJrLaOatp":106491439923,"f4ieEkIEnTfN":799159496765,"SFm7ZCbm9ANo":875455384713,"eqDHShkebCHS":887024920534,"Ylr4WafJ7RFK":964579105309,"TMNnB9uwkG7G":466714676216,"WtbzmA9ttYbK":496441522183,"3r8Q0H5lApgL":999017486446,"zDVFlKZiob2e":685821819214,"MSi0lOjMkQsx":374058382319,"22kYAaJilbLW":2960129735,"OIZNLCMP7aOn":180318104180,"ittPgYzoAudU":47585311990,"oCF2Pvmpe23Y":172670212454,"P2BkuSLAa4ge":348127661222,"UIFVz60cOTiI":862176408539,"b5ys9alhMRoI":926950920855,"H3OAnOiyNanI":855689872276,"yWAeDDGQ7mLy":786002789070,"aA0krWx5JgS2":572879277144,"Po4y7k7hP4gd":307884327380,"0dBMA04aoJlp":63322012924,"JG4Y2vhSnNvS":905700070653,"xAYJr0TuyvPe":887324215986,"HI8DyKEFo5rf":739651654320,"8F3G0wRqxzSy":55929814759,"z7XPpczvECVd":613444904615,"Ctz9SRKZVBt5":531690294353,"VvEF4lTDiTvk":714694710417,"itpp9QIaJH4A":598960639765,"iToUojplxYDI":162420186427,"6zWkqWe1YLrp":950833305004,"BlRuBTOn2HKE":155393465907,"BSBI5N88xvwL":82908903009,"ocZkq54XqYqn":926390996721,"soQhoRqLVAII":404380508664,"075i2yafZ7jF":33757125627,"xvtFduzmaAML":733218142074,"x8uakOJs6RIf":838247711009,"60gQgIit35hX":212490838899,"GoWZ0qL6ZJLa":655280574327,"tmfASGPTO7xV":361106238866,"XMTdcP8VFClL":457783394982,"OF7404GBJNuj":459246816401,"dYsgUDQUYcdH":181520684667,"YwPe9iDaRIiP":816560624102,"4WvGZy65JIvd":879622635042,"e76QjMxnbc1s":328975076821,"Xx0A9Jd9srr5":257764181939,"IJbDoJanbOIA":12027624882,"1HtZhzUcNsfH":994971167836,"oNMV8eL2hgQJ":50207777995,"iVFcOdQPKT3x":567869344094,"IQ3bdfnRDztR":753469646947,"kFBa9UvODnq1":151073814206,"DwWegq5yaJuH":134161659858,"43ZZ9ytkxQ1X":956516951548,"VAPWelPOzSUw":320520220282,"HJUK9O17yGZv":971990605821,"VUwrdOU9H4eE":706818166271,"vguJIuHqF3Ud":903381806168,"NbGLpwssQTF3":411416392407,"aWthi1GMgLnK":708723038955,"VlLAfdybd0HE":703342785928,"TfbTatrboBst":109662784357,"XS93Sa9wglVX":286747336829,"MCKD6gtfr3XC":918007137121,"GI6Tk8NMGruu":942155506160,"AT9tA6O1Gi0F":160869656457,"5yKhdcnOXWrB":712020965381,"0mOQG4tVvUIw":620823649090,"PoZ9TNtzQA1Z":963882681966,"Pouzt9e8QANi":619884472023,"UxtC24xKDTfH":267181763361,"MDanardT57O0":618006746664,"Gw04gtTZAR0F":449249682201,"BhJeTCEUvbkc":619628922597,"QEjo1j9zWQ0J":904343513871,"9wXGrb0BZnVS":447821406400,"0QwAG1vzAEHB":764619633837,"mxwGFJTZeaoI":989285101014,"4ILmpTb4GI75":309292388616,"Ne6wTYexoo6y":602678139648,"sKdlN7fRgE8y":860863148626,"gkM0jpKtIJbi":327751263563,"ozDiQJE2N4Sc":155609711524,"bJY11KK7eKpr":684246097969,"FO20kwSESZ7t":424318650775,"r54Eba4NX63V":319675093659,"6bUVfu902ScX":83978111524,"LsMXXh7lJime":89017981910,"gcf8Zacr27Ka":357203478175,"haWQrGg9xrjP":519323886763,"rv3npmFhN1Ga":996132988441,"8kpo4h69cklN":619136664941,"naUziXSJSnOj":765538005553,"hwfazwmkLyqt":511800148559,"S1PcBFXNbYRN":702215266262,"cGnYileNufNi":416944248079,"F7jMfJhAmp25":942277476613,"3L9c589bj0wD":247226044965,"ewVZTt3KCVyp":409690590620,"iYe4y8cz8y5c":980397296538,"phViV981uYcT":514899918562,"WPkPkPvtAaHy":526318875093,"AX8LwXKj43Dh":894302609735,"gpPqFDL1DwcG":23092105855,"b27odA00RqTK":316847131588,"tqgtVf4ZBIxh":439899696308,"pBq54b1drBxe":265399896977,"zmMgYtNenVTN":535737048529,"u6ogwW99LyPf":585246039509,"Tpz3JsyZSAsA":287523939403,"Qn9CSLHSK8av":427991594620,"SVT0GUbBAlSx":280844031275,"l83JDV1AHWpa":730530099388,"DjCq8dUKSlWD":212670301693,"Og7pBInKy42t":430121236232,"ANyjmlsceMJ4":110246345432,"Hy9JUyxVL8N6":940607963499,"0gxJ3P1Md21y":219942860162,"zzlIARDyo9vF":394608427331,"18zsSRlTgXNI":912245584768,"tmLzWRKlTub6":192302300826,"uhUs7sd5IG0T":329526873600,"GWD6ZSFNwjfg":977836545615,"UQtXZnjuDGx2":158278365704,"F25ZspXi0hUw":837738901429,"HvcKKfPdhi8E":163199417313,"16vpmkxoJhwD":431619552854,"92n5H9acbGxp":416640791872,"DdLAlPKKtlTe":874542186888,"4RK78FS1r3Ul":996423286694,"rrpbLjSoiqCG":617060485602,"FlhPCBMCfMZf":729182437966,"RvMJboZfSpYp":39702811193,"Qz4hpWpEbMrP":624698494688,"X2WA0Bpxr9cO":468525295206,"3QhMhriNy9Rg":822805356138,"ukouxHFUkzXd":746911156817,"jZfaySFC1rE6":665638877370,"wcYyAbeC0SoN":259538589214,"KFGve7LOJK6R":710294154740,"e8TJhsXosalG":59679251598,"2P1XSEX9UIvT":547807073904,"Hyz60hM2DY8O":723482448869,"TdJvKWrDjhyF":981186503795,"OCxvNQ5v2UZ5":841277756620,"vC7byUP1NxUA":255199305363,"b5jhB5eNop1p":94904474425,"h8qhUBKKjTNe":23293412613,"aCydtVwhxgxh":984152528097,"YySXCHdis0nn":276749529987,"GSdOnxChZ2dG":15754323278,"0Fr8j934J0Xj":813299291944,"CH1mYtEMuoRA":570731615391,"YRXjoXyuq8Wr":673094924486,"69hg8bBWMvj7":615148066013,"ep9rFI2LI2uT":787390271472,"EkOWi4w9sAlW":152892467570,"qH2Hk2nnOKzK":311143237706,"fe6cI0gjUiDV":787918764457,"r9ywMR5kP7aK":688284490805,"zot8UGaz8FZr":757983785765,"DuLjELBKcsFd":655866450251,"LziD7xha1z8J":513924789515,"ZcgXP7IfwIy9":765808025239,"Tw26XPed2oVb":591129054533,"3u0Xrg5fWa4J":391011268139,"J3zgAXZYcqTZ":98200762633,"aSlvQqy8EELn":239722543171,"9OLlu2L87lhw":203796316731,"q4m9f0weronh":258147943792,"2itF1a7jya5H":536466360977,"HeIIUdQN5dnN":525164265395,"CC0NLTqYr2x3":23648906542,"53PjByh2VXbQ":166522668465,"Hj1W1hdDBsqc":169439907975,"KFAAf6UmqDJN":49724934122,"XAhYWWuaopC3":903392330241,"j3e0Ltw8ft5b":614234248000,"Uz1A0xHdcprn":891746521211,"bYUGkaCci8sS":298362533893,"O7r6fFbpZnTh":630645701367,"8GwW5RdM9m1m":470974729325,"XxoVc9azEhvK":767390465383,"nIRQmronXq9v":883075742230,"HxHPMRhdO3Jw":501641462108,"BRdD5jP4FZZ9":621937284460,"nks7UpjUcHMJ":459996144890,"5nyHUYnoae95":309005732549,"p3bstCesFGiQ":281166366246,"6n8K80euuxWE":555087876217,"yZznNvbRB1qG":493801352687,"R9ldA6eGbtWK":215534997130,"5cbAabsiFKaf":296895983973,"5BBNH92E6LU3":36339658438,"ypS0mcBo5MJG":319607312202,"XO2ak7S96JPN":922577166850,"vwXXli3Y3fgZ":890563582299,"Poq0TzcOMkq9":815123853076,"MCxAJNmVfHoV":510754743353,"0qDMImWuwnf6":494332973030,"hJXG4duRoH0U":480731452730,"CNrOAQLfJHHY":176222623688,"6N7gcOY85upu":591720018025,"F4CKEep6HRsh":227765574398,"agsaGQ2a9VsZ":705601448595,"awM0ddoQ231v":982103549623,"Lzh3M1BTTmBo":718178944235,"4YFgRSUpD4S6":284581386491,"UDd9HLieVFzf":185080875802,"UNEXz706xmFw":124071806266,"5yGPlicd7dSw":709089759087,"htk7jCbwjWUC":986525799019,"erOMA9pXvYJN":816394955097,"NU6yJeclfauv":708749480530,"VRGYDDUs6CWj":488694012529,"pJXmcAOVwgKX":471812045551,"rVB41MHAMxYw":28912163621,"j7PNTJKzd1mj":409220095302,"1RhtOY9Xvxxf":415491726932,"yr2xUgsJfLFs":727741405960,"XJUTkgDO0nU5":279268303770,"oCO6xF55geXv":578471143513,"Ji1Sdoyt3yAQ":40573915314,"JvDSuiV728T5":36029846920,"zqSV3gjiZsDU":661339884828,"aJmyW6OmuwWY":576698926077,"Se1to9DxHute":37671699128,"TE5gRgBD9syX":346909234395,"fzEPYHGkqwFn":49605092323,"uiqB5d0TcA76":490376651469,"iWNQT5Fdl5cn":642751472644,"S2SHQwIOtx8B":348839453997,"9EyjtJAWgZQB":627069878960,"vm0YmsLs0tCa":851364552536,"y9ruzpDrT0Kv":202550071936,"O1NISDHMUXG7":59110400495,"VvOgtUAUHFXu":598655025852,"3nXB1qfhmUrw":198769403868,"r1gRSMhTrcSu":273204043645,"a4dmaOMNfEOh":504052666429,"ZNovr05Z6DDA":695503877258,"nDPGm1KwUole":15561801838,"E4op83f2MMOx":226944551613,"J6dXfMfQA4iu":193946553527,"eHDsPu7pggUg":435276272326,"YnSPMC2sGRER":119417699662,"mnbp86MR5Ud0":33070809426,"r0iH3KsvrooL":840617114714,"DvYWUTVOv7xE":643942772582,"U69HohLUFX7b":932671192507,"VV8cztm6zh0W":312994761988,"L4kPEtlVqkjK":15635299398,"Yfna639TPWRW":783248973743,"NIdErZXIsnzs":999279761235,"QVloeb1VurnO":846491313635,"M9lxK7bPHW9F":394165994636,"3XC9ccGlVrrR":219979491027,"mP3FRJprQzKD":658211784150,"ybNAT0q1AzuF":8602028867,"S2sjDjutqhFz":551797624939,"sYVhWL4jsijl":419845289070,"FbCRhP16fTKX":776139002281,"HxFQq32Ju7eJ":824697492686,"HyEyoN8H3k1H":82781224119,"wun53ouvqkkl":539853214231,"ycmnoObhmne2":766403927409,"Z2jNNrjzlk2x":342712216082,"7jSenbHWRJHT":827338623613,"tSrxLbMk6Lay":330015015230,"vqJjYv3zVr16":165184693015,"bB5ZmDBnSfSU":230311245080,"jzsxeUeWVpwy":355312494570,"YhuJTTFlwFPr":165505430287,"4HhCWW9uAJ6Z":913925333514,"sZLSE33s38sj":986100747693,"5X2TxoTymqLd":524994667794,"KNCbgExoTM7u":559259473932,"gIP3h4NJ0YOx":293701523232,"prwXxYKUBAxd":541293130094,"TeP3BWxLsExA":512551196591,"WUgUNw33QxkW":331475950545,"vLrIyGe4KcbS":718757833009,"Tclj1uDYsSxG":164404478071,"WuSWBufzURHF":773917331149,"QrWNHyhO7yHz":900705392776,"y1HI6Pbp53AQ":580400034761,"OIONtgNt0nqw":731874748436,"JPGHdjP0seN8":865042712141,"CcZLmL7hhyDu":634550945134,"PDgzw4dBEWEv":453496940150,"svK1SkFVm8pg":603217727401,"NpK5yfZ97s7r":130158949808,"Tup0lvHkPqEx":563141211307,"Db7MWk7qPdXM":523500747361,"R3MK84BHhSjb":409181492538,"Sq7R3KQEru7s":766206106795,"iZJ84gRl2Cd0":533714520070,"YT2IDhbOXURu":418544770703,"WX8y4jG95YL5":383446609433,"vnljigAeRfr7":894542673301,"lpIkl8JETC0k":930615222683,"nNlcfluZERu6":992791159393,"0fhoizePyu4i":82175905804,"JLe4viB5ATtQ":687456195466,"USxut1ff6eVl":84618004014,"yuDKcyhPtOkX":617190162125,"z2dOWxMiZHKx":494995287324,"XdCUHTsoYcbT":168381091996,"geLOd7I6T03N":487570628029,"Y0Qd7abFv7T0":606268326404,"SI8EMl6b8Khj":553496032915,"STjOpqHeuIZ6":750307686966,"CHnT7qvRJPwB":79035228621,"j3VHI4DPhjo0":212266803419,"6QFjSTkBQjdK":668406433034,"LemQckmpNTOa":810647252025,"x3dA0GhuMLIQ":746149963774,"aAbUhHljFrE4":458803594656,"tVXqexAxZdKO":439880869101,"gPD66nCH1ka4":50942872604,"riEPfuYwas3c":937094727351,"QlRWcpj0gZ8G":913331443628,"v9Su7RT9AvFH":212870283087,"OUDyH7d6jCRH":264574283485,"fnzWdJzsFWXc":677717095947,"iNOW5prgteZ5":891693458564,"GXt1wEaE4z33":484771498998,"5D7Q9RbuKBAo":953485166397,"BH7363wUQKLd":11474931785,"t4WqxAXd2ZXI":679032252919,"0teFvYonWGRw":809247802128,"OFNi7SBhKpgC":937737161689,"CnDxVZ50Dx90":710712574204,"mBMNYp7E0VcR":444267435818,"Awfw949uITnL":701303651847,"LleCydIedYiX":141018299329,"gEMg8SCkR9qT":948511567765,"Msdi6Jn58cDo":331093612929,"cA3xGGUyFhGl":879357885030,"Cx3eOh8Ef4aB":838721695625,"COuayLnnlftc":661596819390,"WOhPmT7Qkl5r":994440431298,"t1WJj6KQJt8z":295900168403,"hygaHHVVoNhB":114485449925,"xGuKiKq7iTCS":59641425960,"nPpE07bDyvRn":831901944756,"83rz8yOhCEvy":884341001229,"18k0wKST8Lzt":61923294361,"0t4d5OG8C6vE":888490744620,"SPDCENDz7ejK":659401555191,"3BmYMrm2j2lg":37954415267,"uFE6JA2J4aVt":928676372872,"zVzK1Huf3TrG":234799281541,"6n1dM4Y5x3b8":410963920186,"7494dL10xaaL":631037233424,"2Grvke6MqPQe":766466294341,"vPqUnW3JtRm6":811164694715,"yAkwMANHqmR9":608864774031,"vRm8lBBJam0W":669439793983,"tdlE2oAiPfM1":770203328173,"xd8KveL8IWEv":349300620978,"u2p4HdMzGeat":727489160191,"agDtdXVcxUvo":627824140238,"bwNRprUThZ0r":200049700599,"rcxvhvRJ1oWZ":945407092906,"nqGaIaUZEtei":577110790443,"YsrYrsfcPo4V":60919631528,"OvLFH1upqcNo":134009564938,"oFx7TsP9JkG8":527374404663,"BUZHv7yR9NA5":260851303338,"gWNVlqhMPOkK":527650296116,"6mYvtEDJvUgm":723034906001,"TbPh6MFj8QnZ":408973208363,"igXOQ7aOQARf":143583499622,"6dgw52XfJ7Un":700731491180,"tDitkD6HzYeT":653272192271,"Au0U00jGzkzX":828660330408,"03dzzFOZ9YLu":649629177596,"koi0xNj9XLLD":434187045730,"YRh8aHCrT9NL":894463442231,"V7vjdTjll8mH":640995370865,"C7koZVscKGv2":962005976712,"rLfbzcCOnfb6":965790678186,"wbm8uvHkUj2N":653049769202,"bL18YGx953Ho":894318858919,"nSxRu6SqUed6":143783331791,"qVsqIENvUwDi":220194057886,"bL7XUgQCxqRf":47085027832,"1Q7uOlpJLd3y":739187619674,"d5Nwt3sdgOPL":729784955523,"jyP6V50ptiUG":333935954307,"tFQML6fzTVvp":204111997971,"P1pY2pqnWRzE":103817023972,"KnRYUOiDpBLT":18154187488,"DvwLLwcOGmXc":994791890913,"opc9cMwraQ9k":387148852566,"CTd0OZBVdS21":883514906752,"0CFfhC1NFZjy":748807013090,"wb2PAcJCZaDy":243594407282,"jemW2ewAkeoN":187473794606,"5kNgNTUWCvI2":988074443635,"3FRPz2enzoLS":979431634551,"e7I21erIeHga":103771961950,"226jGlf4JEfY":920312218141,"TutoGd0WIZmB":435735392953,"Jmdy7kulCav4":664950859110,"DFpxsDo736OS":394201589768,"Vtiuiu2W1GiZ":160969119077,"354RqDonVuUN":990922077520,"vyTRyZHZi55Q":332763938646,"dd0eGYl5Ct1L":276189993987,"wOqaTmAxiUSI":331114619263,"I6PjdetmEykL":295282603531,"QCmKNfEYZPjr":51040233702,"fsY1WnkZyDFM":102575203400,"IHwjqkT5bLJ7":834254011430,"K20U6tbe7T0G":695112074054,"ttG8SqYizAQ7":59718224602,"nS0i91LyvE5C":663764337928,"dgRlUZ1QeqeF":380322673145,"xFq8KUcxrxLV":240261190492,"DeYro5nUOmBT":995523862631,"fEYrZmdyyHZt":190925912783,"zfwf5mROiGr2":371827286851,"Vf7YZUXjOimz":924649322470,"5fYkk6It8YZM":419569567878,"a9Wz0YCOZklJ":869962771619,"Ync0ZS2yLU8E":459061631014,"0puVuxs6w9B5":13012548922,"XaALwL1QxPK0":735183937388,"tRy8Xk3sGFd0":595215882456,"UvGDd8sRymc5":197030117852,"pEmup0h7GN1k":263695527325,"feeFJb1O1oHX":53009094421,"SHdWjphZ6ss5":523438302475,"pVWbXJbwcHdD":559958881919,"6Y8hShlvSBJ9":265734216662,"5xGpixEYkQT4":390944442477,"m9S6j7MUdsX1":885582663816,"qitVOe0e2Lqw":301993199893,"XO72wSJBhF5M":597835628342,"xRT7OhnoNEmj":180669837925,"s61pbMbzBDLj":445959234114,"Rz1ELGCJinKn":368494935435,"2ASSQCmQrVkv":490382287314,"hTgfWEOhCzs4":90349523426,"D30DnNdtZsfR":600042767113,"XWj2c0h6SPoJ":55105200838,"ThCILT2NZsIV":705504170284,"cR9Kf584JpI6":533360459323,"jhdC99nM2Q6t":213412820851,"D6QyfclTU2xa":352874922586,"7JAVQLUNVtzM":192920455837,"eWdwQOgSNEwn":811500502098,"z29XGXdxJGvD":640926597054,"OXyAeMuR2LzE":264317898161,"5GacHE9ksV5r":379207068325,"ecBr7C7JR0gB":934252876763,"oiRkttT3ocqL":782794958022,"1ETPX4S0komH":993324452383,"TXDNk6lBZ4nZ":651177110729,"pB0bpvJBTGEk":361800664826,"jVZgPjzWe47G":915066610119,"VCmpXAkdDI1p":483734422900,"w2Gqerm9c7K6":280786473273,"kvHpvxbsFv0o":920393885004,"gYMGxGNHwI8q":253913060030,"91YGd82OPAbF":794493931004,"tAZ1hse2z6GI":348475706906,"6W5olt1TgKhJ":749583039935,"st2Zo3JrM8Uu":83866173378,"RquJNISmrtpM":67051698128,"KAGVzjr469NJ":697999635838,"UHx1bmRWoR8P":192403432731,"AEjxrNty5hfC":151169246257,"44lujBbkSM9x":497864940225,"Fb9apYLcXlNU":424769253360,"qHtBOxPBxUPS":965176421574,"AznauvCw3LNj":669492324964,"weDj4Rs8Wj3D":587660435807,"KqRa9Vs2XPB9":346889255406,"CcvgMsG547Ct":678200648448,"SqGGzAuWJS2w":563674359808,"JwEJfuJUJhEI":565902295325,"A1zOrlNZnx7F":648926251611,"Gp08DY9ZduLB":236855044184,"YS2pDlA7HESe":24739533488,"k9k6EE7aNkNj":501757554030,"mTdN3r7Rr3Gb":944614928890,"h9imsVuGyhvH":511977186892,"BqrYoFKIICq6":578526979030,"GQ6VcZ23aOr8":995947771191,"UkgDvlVAtybg":387162280456,"M8qKb5shN8Vb":540565259879,"FPDSWJUU0sci":952273180121,"90KGSKrqxOUp":964207903493,"b5KB8QrjTa2r":542067662318,"sEdOSz6z3iBU":648644350946,"s82pTs3y4ihS":608969663922,"IOsxGGAGIXKU":734575330754,"c9LPDnnF8xOI":370367827240,"wEepVPPeGBu7":619033489060,"NHHxw25puvzd":875639482211,"tw7FbJi8bwHL":829392966448,"HyA5ImkceYy8":156030434276,"h6cJ1FGj1TGc":105106186194,"fnZ5tVHZu6lh":628420617613,"AJDpOo08cq0x":823545976457,"d5jUqb0Ltujp":291151683207,"iMnPNMfmhQZh":338581692694,"grZFLHuI0Uch":85542377249,"sGG2F3JKwSeS":306192339224,"OZgBagGKBv7O":110998333871,"ILrpT0GuVnTl":430707680891,"ulD5LvG6urzT":930034938310,"pDpuszsCK2RX":987601229579,"zmLGZ2Ws6zoo":301576084137,"RgBUsHpaAlLm":152674739341,"BlswzME0mPgZ":653863110578,"7PGXlGeENR8Q":78793497382,"8N5DBZ0CBU1v":623437744053,"Gbi9AYRI6n9x":428454519263,"SJX6lrIv6XoT":188097003554,"RgNIWjm5e4Ka":106714345339,"YENA5u5tQHEF":104685856524,"R08kYdpXvvbR":234999372364,"eePlHQmXYzN8":275663793962,"FtrRdoLtnyH8":369481741322,"K4O9BTCRlUAl":149091470420,"TRVLZtekav9Y":437528577769,"We9ppcvPTw2x":46663093644,"knSoSojjLPCh":289811380572,"DO9jHOFI5oEN":495324582658,"b3EoB9HsvkhH":327328637401,"FbXmbeZz5vYU":957981893308,"wvpT2CWAOSHb":440919918159,"PKAm96RPmuZp":611620345039,"CQnoWiiHdB6b":567014048335,"1yBqZzCGVQEh":825498569391,"1YDfcRs7NQAm":292712566090,"R8p3FfV9e9ni":494621552677,"cNEA6TxDH8ag":326576436400,"NrKQjPbcUAuF":789050143875,"z0BtVukPZFhV":931287101882,"C8SO8wlX1zTc":198759187939,"tgFrpELMrPs3":562496257703,"V6OumzxTsePY":606756225079,"YuITN4x6THSW":539710181078,"bwz6MoQ5xsXm":998570598387,"s3Te9EwdGyUx":325666053087,"pFKXOf6Mizp7":827675777065,"qmJENv9ByeDz":546859874451,"2IxhEO99Ew1O":749082481645,"cUofKLXw6du7":859174983629,"PZG6jcIE8maV":942499690052,"bIduQOwaIYq5":969776026854,"JOVD8fQ0FfNP":957566435982,"iUco2A6ppwrc":699670563371,"EokH2dSsQLIq":622796227942,"V8aqQo0j2Rb6":48651605525,"PV12aDEYRSHx":728053138424,"I9rlPZYqzNRC":139331057411,"9ZvJVn0WeIj9":500041999280,"HzbZZ41LnigV":517695002288,"Ceq6BSMtsiYP":357460201243,"vqHePaL6DL4P":701970406123,"WVD3iSMgUjE9":187055353849,"ORS8z4hMCC9x":241596495073,"ei9ottHBU18N":654208285963,"lBCzpsNkSWF2":586213149988,"OcMxZ32S9bdP":301191793537,"lDYg1TsocJJy":814089430234,"z1ILg8oAklmi":158618901856,"waBO8HuDQNCE":664466436586,"qbTlMwp6qK20":126106669216,"chs5raJhzcGR":404946839439,"8hzZW8d8QVFa":339381329071,"bp4u7A1nliUR":247686132506,"IZJzIGFrmB4B":23103877072,"0D4KXtFVmaMt":509828548465,"euWAmAfeasT8":800065569669,"ismImEp5V2HV":439412034070,"PDTznQEEToYE":192136695308,"E9BvqX12H92e":427205635842,"H3tfWhlAUeU2":682838255424,"R46Ci5IWdGG8":614619093529,"s3mx5EUn6ptS":853842647455,"u87OkCAh6cI8":875776186175,"nTSjGYsIBQZ3":179383284060,"TbNvVh9eywOL":605618145275,"VCZ6nAuH41it":31194059509,"mO7tjgh01iRy":642207945328,"U96oLIKPkLxr":828232993574,"16xfgJq3z8sW":364994451696,"mq6cWpZrbsRW":656262399159,"Tu21m2ijnBrx":77441081129,"Sdy1oeTWByjt":485403413754,"AEGoebooFB1k":87094241060,"Ril20z49Cme8":804906872480,"j9xo4wZSmweU":456841132840,"x5B53oVHnSC3":849192275794,"hizaBBhs13mx":579304302539,"4JAShnpQ1NSP":508309959514,"SFim8oKugShG":809187640649,"LrJY16gcx3Mz":511705642203,"3xEr6a0ANIUf":711364954431,"rLUv2b6XpYyF":581031779436,"14iYNARVk64p":756210715578,"OymupA0pW6Ts":494799985502,"GXv138AJGPup":617427085742,"pzk1O4DMDKZJ":984442917534,"AsfotGsqByNz":633209329034,"wgKXctJv5hQN":247793280200,"zJzrs5o6eknN":467247740691,"VgvHqIMV7qv3":91723977145,"FOWzVgKUzRIT":682321625522,"jljTG7dQw9B7":711874939753,"1hxCAlODVpdS":245527687367,"MRQGMuC7M9O5":685736277437,"eTrpOUy7vyEp":544410135906,"o1YZiWoNvEG3":464681238610,"NqoLFUoTRK6J":231194231216,"Ovtfxj4VmL0n":658325857558,"DSe0pY5I4Swz":928448137083,"r0C41j04QzuN":629109134691,"bw76jHkY8vwh":322638218904,"xHBc7OgIJbu3":278987870609,"MQDk8TYavPnt":105101261591,"YFjZ9qDMKhQv":207930007886,"Y26dHgxvDQHS":692015502005,"fQudrQ3OpTJF":34311735424,"4C3QAYNBUJyE":190229023037,"WyrCzuIOemRX":309979343396,"cVUI2EEhmmNr":990565485608,"3cFYRshbzQwB":833289576657,"bqapy2N8I0wT":49507655880,"s4GICroRn1WM":604880208103,"OLZ58qVWsG5N":443221399996,"E8YURx7LjyNc":788425966670,"JWhVjLnq1OY3":214724065972,"qtdFbJEQjFHI":675129537099,"Iz2QzvWME7eT":532092686406,"3DfvdvOeEIFL":685152071319,"pxgn1ZZp7VXZ":440514835280,"pB6443a9ulr4":545632776606,"gOmBzf714WMj":477577240151,"7NGrsYe1Ev7F":115096328974,"6Nnrr07QY7Gr":57973559722,"lGpyyh1EOlqq":480382065547,"GVrh6fxCBE38":449332332116,"Ogaas6ZwWh1H":750774992745,"6jpZgDufuNlU":385541024701,"mmjaGUxh8daj":5873696495,"eUaneJiVm8Ue":715816469197,"wOKf1e0uHqnu":221228311257,"7PnTfNgivaCr":160930526013,"TZsIvdwghpCZ":464829601129,"av2g088U5GVB":715384964025,"oCYQHEJHkSdT":262052934572,"CDDDTjY6LsKg":944222951441,"iprN7GpCXv0u":701335237725,"iwTOrtFzXA88":360707280210,"0v7qJrk8pAX8":72965712160,"WFaTDNIuIWAk":147745765190,"4ro6l2a3Pm75":721670720152,"Y2kKbWGTb76H":4773652242,"x6AycUJIO99t":601422057477,"RFk4VzSY2NdR":213771461651,"eihHzuvKpmre":84725617269,"IoUmkSJG3B0z":43283265000,"Y3SESgTXBkwi":909183155369,"tNWQTnHF8V4Z":294402452166,"XW8lrRjEFV7P":740051729487,"03QHrrRcTtRH":867446087584,"SEsLR5RZ9T91":542558633980,"Ay3zZzT9Btji":395269183558,"TlkpoAVc8dEY":168320924536,"qES7T1wJ3Gx1":888445987757,"2r3tOtCtx5JK":77910725563,"djQDFWoXs9xE":399266590524,"GRHr2RiHTPb1":537914039739,"4Ljfw2xxGGPn":773675144515,"rq1i2TLJ8Gyw":773629240734,"o5MUDM8x8gi0":240797211588,"OfYXPWugJrQo":834426727278,"9H7YG3muzPOr":639164622226,"6SOJIAOi0Bzk":810468707956,"SGrnopvVIcuA":673952702963,"Is0kX6yYYy39":665620768670,"XejYFK02Z0Gu":707840902345,"GnWbiJcQb6RK":225435798837,"7TKJLLpDp6C7":446318127444,"s74g8ZkPcDIl":356101811610,"ax2FLQkLU2zr":165739040546,"mPBGcLEmiByK":793969354120,"BAwbMolKEDwc":859943164263,"HcqfXnihEzSV":918804357870,"GrCXlFBJB9HV":35118841447,"b3Bh840UMXHO":73999409671,"hbjRTuvFzq8O":900893939770,"x3I3SUC8fBHB":693794129860,"UqbRPRA5X80O":358313010393,"GN1o3PnhiynJ":736906194332,"n87l1tVkgskI":984543383603,"dfOOmHVCy3As":958936541916,"WQAmq3nApbCM":204674872393,"jJCZ8mu5Afl0":763915078706,"emVyVS5BCcgg":537746965793,"hpMQb8ZMox12":929692959481,"Tu70Cgllog5d":274816463346,"JlxlBiYIIn07":133842010100,"zw77ILVeohwk":971883749415,"0CioTeFc7vgq":286028149287,"UWVXK2wAtUKJ":491637499251,"nglSUjYqq3Ef":216817586097,"gZO1iTxmP54D":18336781236,"cvxeQMkrIClZ":60953586801,"nr17wmbUIh1I":655574290155,"FWzAy67LNFuZ":2152819999,"jAmyCSHMdOEX":956942132043,"qU5EcxL63YWI":378648077391,"7e6L3kUkT5UJ":509757158947,"mYsPYBcEQmEB":104142400988,"KvUz1dBUxhAg":97147044404,"2ImZdHNmyBZa":862004188867,"SUbpJDuAdL4x":548350010087,"YFonODAE6MSQ":218703507213,"xQM3Znpm9vwn":34879798480,"LFM6PLUZyFex":900716300391,"6mLurdePcQbE":750557845080,"OCAxSb9MtVsN":752113422350,"NTszgQ2tfZQF":280244904919,"eJB95Uniszp5":631395612401,"JsAmmOvoiSci":633166741964,"EB1RL6US64NB":456228171462,"z6AuH3wyS4Yw":449065381484,"vWCou6iwEcZ2":584990800913,"sxI2GCf6BArN":266601283285,"FgDh3OE469RG":10544696022,"bcwZojTl9A4U":170627587165,"y3NaDlMP8LS7":47879558999,"4MoQ8rvh36dd":859936946137,"wsT3Ej2Xa6hI":362835857672,"DoIixeZZnxSt":416540385341,"WChUxqIA166p":817603628784,"ISoSQlCWYjAx":869656914099,"kRlAmwpMDxjX":824764294684,"C4YHH8HXvOcV":104018820932,"zfZQDwOE7g1w":641416434726,"uDJ4RQ12Qr45":330427598520,"Un2wOE0hkOwM":642567512274,"f6Kw8yGJrJOh":148720555205,"h4o5OSk9QJVb":341058129999,"aygB3h2jF3ZW":73457387042,"LxB14M6nrMYL":863033037870,"2Xy2T7RyUUHE":63290033579,"MRxNmoEEY7wk":172546268403,"IEXGpk74n2On":298246007953,"wipRXiWDBf7x":377436227172,"FEJDQ4QWP8CJ":16579394048,"Mrfmjysn5LS6":439563605008,"FNONqJhvEDOf":150160869236,"orvaMRDx6IVF":674985165561,"WsAcPGVoGEwb":537200270016,"n9yX0DQYcwhx":562842191812,"d4cgTPLBr7PV":37080974115,"yfq0y7zQJeoi":194011111929,"tRH5242VpASO":696506105499,"TgoCtSP8fHWS":567196149613,"NeDbJp4zEy6Q":776606760580,"Jr92Irow487x":93193136627,"4aoJoIni78gP":836190635711,"Lx8qUETHOBwt":313054565527,"ydd40nY5PJbH":356755789150,"gLEERxocZdWI":257107952012,"5tNYQNTUDq4O":475968777861,"v900tsKEqYh8":856671758963,"3vzWB3W0K6BJ":262638939675,"yTIqNuUgJyTo":365028467985,"VBywK7tpI3wx":843672912918,"MIdOI1YB3zh5":45465543014,"OzuoQJosu6nx":730241955346,"lLHLC7kwaBeY":542751184002,"LDmbUfBuOrYb":652236536096,"aJBYucMcmOYZ":384102541113,"5ZzhnRo6ekpS":649065579573,"rWiwE0kXRPU5":482444863919,"QXcR9YpXQpYL":440885057102,"SCvWnGUUymqL":224853279850,"VvtMZJh45iga":739004519164,"gokXDSCCDHeT":530580812290,"919ULlNeBsRU":558143408643,"mnlTGfXJrqE7":287880381824,"D2VTWo3XiZTb":151703688674,"EnLkp2MikkYg":691054125096,"IEKYiCtH2PPA":519419195063,"icEg4fdE3NOt":35146980750,"b4jJDmV210ev":724768826980,"s12e4tWcCDWS":12497855635,"Fok3Y1lWAEM5":597950581134,"TR2BuZoHMoeo":651538014901,"eL8MvdqPvNP7":279880500961,"FZuJdWCEzT5s":290176714271,"3RPFKvuy4e0a":708479477817,"eGpKqUR0Mln8":435784725895,"ySbpNzRWRqHs":185149034436,"CJmFI6J7fOip":246748289474,"3zVBhQTObaRv":526158006326,"yfDLGAKFwocH":719777461688,"jPxn5bjQ2JDK":767781966761,"JU8C9zZSEMwq":841651378433,"CpFplczuGy6S":372643323291,"ZbvxUD5tLytO":237537547610,"j4B0culVcaro":779787925171,"sDOY0qE2H5jB":254225354424,"RJm4GImm0jC9":621165763135,"sCrUwMju1ASG":819316146304,"UbY6K65RvCy9":537521782185,"b27rNOyeVdRR":182823081473,"rhLr0LWIcmXh":31350722133,"WeE3YG3zJ9zB":858085916971,"NtCElrlXPvfV":863693976625,"GLbUUFq1vThO":694601917732,"W1X100ceW9Lb":493624032642,"ZH9OTjPi200c":424145444299,"jBQcrAHhUcve":417536530681,"Fr5TaxYd50qy":73345045235,"NPRvP3JayuIE":505761640267,"usfx1uRiwqKZ":140053664842,"y72RFoI0ldEW":602211816842,"vHbQo9BgMVOJ":504987806773,"yp8ZXqTnxFlW":725031969255,"zXTm5o6LBgvj":951193840554,"36cgwdlUKD7y":52211336203,"RGLHEAMs6611":224188738087,"aaDRErOxQFwy":221016739789,"y86YWyTTWjxJ":305025136475,"nZc5LcHq6kYs":393916755818,"KpWUgy3yfwN3":738446256642,"8af384PCUYNc":399193562803,"KzLawZCIdOAt":892888897196,"J0WyMhJM2Ya2":122815123393,"DFL9DRDUGhek":297775905252,"rRqbrzymSHAS":499089231000,"TxkELtE5ty6S":856118305336,"MnXnf2jt0eh9":988639525403,"apMNoIBG38Uu":719944017821,"XLpXoKaMKyR7":695711517322,"hMgfIxEVZCiO":55464760575,"yvQGHwXoA51v":493023183303,"mFvHooxpqeVu":585165088730,"jAMlFE2isus0":820685658141,"875pAzumeCnt":951559643688,"39HU5Tw3bW39":357807275458,"NaWBXKUzuINY":412932510265,"9jMEziSNZpEf":782536640548,"craCfZkg2M7M":251716267017,"l5qoRfhy4coX":223502532863,"nVKk81AgTr9j":238502244981,"YlVFY1yJfNLW":217980198417,"YEVKHflzB7AI":400805231803,"3VvbSb6BV6dz":86418199261,"R7LO7DEARmUt":343013040868,"GsgPk4tLIVZP":435758940392,"sbjlbPpZ4JE5":327391134662,"dj9HmsZvVHlw":38188249688,"8oYDJLSmJm4n":975596312729,"rfX2GvvrLO8L":119694390842,"TSe7qsCEE7mv":961829240305,"aJANMO3UbEEp":580968791435,"wtbIEZJYKc22":432845220725,"2w42DtCpAnDi":997944687244,"y4TTcBTdOdad":502181742826,"gDidtXrPQm6r":565118228204,"WaGLlbQoN7qB":46482189954,"ypIa8qAbET5s":560266994927,"N8m92jUIc4wH":447036254549,"IeUhWOYkJ0f4":404271721750,"VY05ZtBy6zPl":644201160586,"JjrOpiyPxCLF":325604634882,"v7RMyTHtLVDl":488746974329,"FyCru5x9kqVN":257278260927,"jWq0Mx6crxqM":34407721029,"uj0Lo5fnOFZ5":116527753981,"Hj3MrfL3ch8H":5575162753,"CKwIZJuOOto4":200362490783,"cygtgTHJunBq":152727033594,"zVTCSSSbU9BR":796830718201,"udaaaAv0D8yk":485794288246,"vb5Sz8gbRjW0":493042210946,"GkMtGwL0nMrI":838214994219,"gi3N48QV5NNI":237565227779,"Pjt98tWtmQBU":973685346174,"KIeyo7EGnMmC":100048377601,"QSKo03MjyQxu":532708942476,"urF7rmrfMSCT":47427493880,"kwuDsAn6FjTP":236856775262,"0HE01tvEnfW1":835758815755,"PSGy0hJlfS1D":407830769611,"35l0ZtmbQ3u0":315750904083,"vWBw3W3ZoaCv":440544344522,"73W6p0rVmJkm":399339365253,"eHYiHYAcSCgE":842079950297,"yh0hlbzoaFkb":873019416727,"xX6K44sDl08T":804512828675,"sYbK8y4MIcPX":122632388607,"xICPEP9mjPbI":126681632886,"coVf8HD6Ypik":681847840198,"1sYpbpwxVtq9":406867676596,"zWLPFIBn32qH":377224758847,"b4HahUw0OKHO":888450905253,"zt25lAM0rg5Q":399155944014,"YNGIEoK8lIHq":720421021476,"RA1EkfDD0QRr":475799991211,"502yb0mHlf2i":941912811664,"AEaD8fCyj9fP":858197508490,"fCHsYcr7gI8i":879694480206,"b6tKIFXOkLX4":71840551177,"YOUTtLTfFV80":16716109162,"vqqb1j54N4Bg":943632272349,"Km4WzXAygHhs":179811413588,"u779KWMjyk3Y":413003931340,"keq3XrA1W8SY":414148810187,"bv7fLUKrdwL1":481928443338,"kYwb268j0uLy":249196815053,"fuvFI2rLMKJd":331344866768,"a4frFj6f9Htb":285709487542,"g3Qa6HP1Oqq5":598233168120,"3a1slsnvXcym":844599559661,"7x0w6g3n8IlZ":921002388173,"pxGlJsxPlLps":662001686896,"j6zptZzwvtK8":183701007762,"qwqbniFB9ies":27340422384,"3Ug6BuSC1BBm":262588268609,"zinl8R3NYSld":957580739312,"fzxY3WLiUb5l":354789352225,"Vr2nnhxdw0pL":788358357434,"TQ1aXlD4xLH6":825054096942,"0tZ4syKxOAPC":375868819638,"7aMR56qarGS2":303852149877,"rUy49sXZfJ5M":642387196253,"czDNsbdGapJP":561771586815,"zID1Z6c7CoWj":110821434340,"QADe70LtOYoK":241242770357,"4peoBy7t4YI6":820786873048,"J1NysP9OUy0T":506725430256,"CMbE8wyMKqpc":101802955532,"ztVDN4qnbX64":118794871267,"nSpevtvEef9p":202050598901,"jfHfcC5eg3gg":959479091905,"T8ZZosvczynH":150142971043,"ZX6wYQ9KGu1p":869046926954,"FW98K50J93Uu":619579309446,"96iyWTKzfDcY":827874590660,"Qe9BesaM5QR7":177283131742,"GvGK1L0uGtLf":58132274100,"QbNIWOW46tG7":146174228656,"4bEfQw9rLSXS":192745544854,"aEEVmOgUkA5m":777818328301,"j3EjMwfWmFrq":152018967477,"DxlODpc9DweT":840454194565,"AnsTOPEggcPD":91858403396,"hSgyBuqjF1Et":524988944205,"3u4AvlIfqab2":662043997892,"csHFRSjTT0wp":782632108881,"LcwDUWVBidaf":387995730684,"26vRHNsRfw0Y":810672064241,"aVtBghG92FGg":435730316490,"MGknUGOBz66y":242887820343,"I1Yj13laqac0":294783667471,"iHi6PkLW0jjZ":935216897414,"w6PaSHIgDxMD":247406108033,"XWmg9rkrRw24":434847272104,"hTtQD6GevuUK":143015352236,"v6keJuO2RVXN":20986611176,"aN4CuQT349om":33663594706,"lheFvCSxgtHR":608745180641,"qHIa4POMPCb6":532302701135,"QyWL1DgM0le1":366981293957,"s97pcuLp8oBu":796090331494,"YQabRJMLCfAN":147045127987,"0IldxD7a61kD":894916754039,"ODKn2S5ZvRj7":874284414524,"PQJ2z1H2ionf":197828925124,"YPX61XdsrMag":223905663468,"nvnBbfE1dnC1":58273679506,"YnJYc7zDUTlK":927084847919,"7Wmw2LewUzIN":302752965537,"ba3LXQGEA3Os":467942984437,"x100e9DdzXPv":566195145860,"JPCT17kUw1ZH":422576762776,"CREWjtU1C5Te":654666951623,"yYaEMPt0UyCp":417088341323,"6YVW6b8Tmir2":222796925399,"7lGjaxelFVKK":382335002170,"KjMmvbUFZUmE":482580229095,"fSCzTPchyQnU":436442317527,"30ZwJi33fVW7":122901598307,"VGB5XDFClbpl":272009838200,"nDly2WeB5WWV":481976902530,"xZcKgu7CtONs":415104675650,"1ND9J2cF8iOg":635645424819,"N396rbFFKPLy":623143150346,"c99gPFAQat9G":894224579445,"c0x0IKHpTrrW":381517895506,"TNCkSGBsRWPg":95525549072,"FuqeptebKDvE":409169717521,"h5hfcXutDJtv":917137116019,"FGhekKwJ1v5a":585172866822,"d3KATc1csAFP":53296067174,"169aiqJXy4vJ":185952156525,"1vpiAR21iut6":407867964028,"mOwzTTzTvH2s":33258465622,"BRi4vFEoebni":37903174458,"AMPtOITWNP0v":169081445390,"iaYsj2NinKAC":761685644663,"LJhMs3MwqO36":757266541199,"VGkrlIGqPe7Z":755694572680,"I75z2SIXcvaT":379456150209,"MbB30BwdpvKk":144729473033,"HzhCr9Do6atE":168820730151,"kTRtthui6tPy":832173962344,"bUzWLX7lx8wk":797246297938,"qWDgD5BAPwRm":875690264933,"5FizLiRyu4hv":39559614263,"ojIjrAAG4TY1":432096865704,"I4C6HD0PWL4p":502574172274,"e52zirSZN8dG":226948695468,"zrR1aSFflQ56":658809228502,"zGdMQ6pzjQgP":285007274745,"OjIcObvgB7oz":492666656632,"ApDhsTjdghTp":726628663867,"IpedaXJd9oqa":437506176448,"Qj7HzUnYPu1F":280750811882,"glM3MXmxHd40":983158105991,"SBdETIqTmGhj":539169125630,"R4hNleFVTUbM":112886841189,"Ri4cCzDGWynf":935810130658,"WpQhPVAPNrCk":888358506418,"HbqFY28tqQPb":392316231400,"IchEzpkw54UD":324337599263,"HMeihLdCdF3x":533141580280,"jxbQ1UvtdKUn":157860447930,"JuVEM6vBUaB0":427701829241,"9Wh3vpswwIQM":76270507999,"4Ys5r0s8lVy4":448332660617,"b7bKMAXd5KXy":179722394068,"JOnD8Tn6Bbig":481821158698,"Ihg265Qa7xKe":834154658414,"xw6RNXu6y2Vk":115158095547,"j8vTxHztUTC0":727089831370,"bkQh3TPDx2dt":933115870876,"WhuzQ1OqkZ3O":718643267241,"qCat3zOea9yx":770020161032,"lzdB6p4OI00c":950493780729,"8XrcAlSxBF3J":623453829886,"qBcTh9zJvr20":36627826061,"1vIjYs8jEOf9":148655951421,"NRbp71G3Q1g3":3743625726,"2fKg70adwRoZ":722645371948,"SZcypocncrbx":252725572861,"vWqWG6janQlh":453533770417,"5JRP0oO2Gwpc":310872910450,"K6aFJu3kgeVk":149216020724,"Nnbbhnznutfx":314527278214,"XViBkvXMA3Fe":213047875489,"QneQftk8m0ng":677078860355,"sLu2C77niV9M":66799391759,"bYsCxeQUw9gJ":882163076781,"DXw6xivLJjSR":282805261483,"KCizdKWj9qZ1":249149941761,"akF53bOkPME9":940203672526,"kGJsEBudKa1h":172704983463,"1OlYNqz7wNJa":135236661211,"TNZNo05HpReL":250594630883,"TORGh6hTonZL":428139032268,"fDLbrLfamgdq":129978280081,"IpJGhxP4PI9J":136681658480,"KbIDgMxBgYEK":488805113797,"E2DGV2SVXNcZ":889891869640,"zDqmus2YP8wA":721563625658,"lK0VmpfdPc52":625398645131,"hIbKxXbBY5Ik":370853322182,"MH3rCxah1qdJ":855692037680,"NatRCyQScF3z":885261616073,"b941N1V90H2n":958909680233,"C6kVsBS4X7j4":299369647024,"Fpt0ZamLYKfL":649589410154,"G6uNBuV7fq5z":210876843568,"fe7LRb65XF7u":341037923569,"V97P2O6VTN4N":151057798367,"7TUwtudMxJVs":388815886512,"GTlp0C4qYcPM":229065656467,"ItYOi1pK3dFR":462063769433,"nUfde7AjjN3M":940128403242,"zypuG9nhfQIw":194852690870,"54pmw9hK5AEP":483202905826,"tdiwUQAh8UXy":646614909379,"G7DfWMLOTY8o":163449454749,"uSW8hq3Fa8et":922252164018,"kp599naMhDPH":48562713047,"ly0Jv3S2uTRe":866940571132,"RrKMdWYvD6ar":505724127981,"8qfexKTVzusI":988258471593,"R7j5o3JuJ9MS":704081517305,"m5Mf2ZaTEC9T":2312318957,"Zi30SbtQPVXI":213660490578,"PrnUnWPSRob6":109663235838,"OdNxjNCvfaVF":821290823566,"UEhrkxw76ltY":441032700930,"WAlBUCqkJTBB":309570833426,"VLNUnwSBfJLo":867039826827,"cnT3lGGCUJuN":315005291322,"E3V2bX3MRybU":438921233129,"rWsBoqpWIqfI":807371550206,"hrODe12t5fdF":525386544481,"cloOFLtpag7z":775806013174,"Qkx0qtKXPrXk":239898667109,"K6lB0s8G75Ws":342366594306,"fzELxSY2LVpE":287955031286,"OmiAWj8G3rFO":490712849406,"7FvIHrMH94O3":261618620237,"jHbJC5mqLMAv":944604647021,"ZLdA9GDLsYOr":318274987398,"36KoU9NKozvs":722098457534,"Z6TZ2YVFMrvL":185925716763,"aJPLIyeQuC5s":84136457372,"uU8g4UvZdZBp":133371078419,"zWqqbzPrcu7G":427254670752,"FQ39DgOgHikq":864974511917,"ZrhwwYhFFlYk":541638740242,"FjqEsTWwWhqm":625210269727,"VLFubf2fcVr9":305555248524,"PdInv6172Qmq":127847537501,"gUI02TvXnwTC":97503332019,"qqjXrKP4giIW":600424084327,"JKlru7IHJEEB":509734672472,"RFhZAbo4p4am":225537235730,"7cfG3d3Xj9mp":988953760488,"NgsFoxcCbtNe":301515213208,"BkMAvDMQ81I3":230913370870,"O3YS8wnNFbSw":856200340009,"dCZn7NtLxKtx":105612389725,"KeSdgIiD5uFB":719720834300,"bfEjtxNguV97":639659954679,"X3almj0NxrWr":947901124468,"TfnVbl7tgVus":399987147636,"Hq0yFZM543vm":228384514502,"ENqr0nTjukMU":853635285275,"TDEt8YgHleJ5":287701313887,"rsAJmDlWhJR2":803163835059,"DdnNzQO7Fxi5":6889165244,"PGNIweuzrsqw":765285875614,"ydsXlkKwSZpW":128236523710,"NBBJ6iW96V4n":517322330997,"32b3b0Tg3lFm":326063580784,"Y2jgjG1ADhIB":649269785740,"D2W1jqlL3Zor":436505130314,"wEkctTIFEqtz":576786261604,"34Q7qecqZhNo":457127778766,"LdiiqbOKhDTb":938529066803,"FczjwIkMpUDC":196369623444,"GSLWB9Rd5qt0":529734735815,"EK7A9c7X14c0":93648058186,"VDQmIsvZhWCX":33671663858,"4nZi7JjgEK5A":996939967776,"0MRvwE8VYr9i":44211708211,"3MnmXFuEXzNS":660995270354,"cHBeIeoNdc1V":816207342249,"vmqYnXKzK0PO":435642574880,"m7gwQoLWS1Ly":117156054823,"jLEMFt90Ajjv":496858090536,"CJXdx3SpxJTD":229284398553,"hsuFAiY6sz6e":174030849820,"ZTHqiYvWyRk6":560681325735,"w0oSiiV1GjA4":836679930355,"u44N5rp7NoLg":246229965354,"WMfxZ2Mrpi7A":85202879794,"qQPmnLDRrItA":705607716332,"sMNIJeoA7ZX9":918174176464,"SxJiSLOCGLpM":595739525579,"wQ5IlnVWk0JP":382015442643,"KAHYUdf9xhzn":664321828606,"WIu4RIVupQgQ":409291145994,"iLjpU4FufHkB":69136206191,"0DyO2ctOSAOx":215756950147,"B6E9QVWi420R":944248529276,"a5vGBvoESYtQ":161750467269,"PU98CchaPalb":375141945530,"AoL9TXv5Lyjf":41855822955,"ZyAJSoR1EBYc":385162446412,"xfrs8ZNU17gj":878043589622,"RM5QpiOKaRoN":44043955028,"97A97Vshwbzi":573599951789,"ecv6uL6FgPQm":464263804919,"lg8vwk3LNWgU":248106229544,"UtWUuzVy6Y8P":235294144616,"1wvQbhhUhbQz":818051773127,"pRh1Zxc3IPO9":912475826401,"zfF2z96t0EDq":977556526044,"6YjeobZ33wJe":284221637161,"fRdbaM02LgR6":802617125346,"QpWUlhPImJYN":95281451486,"CCN9IMBo06jY":428006110045,"Qzd97b2I9ZTW":470523275184,"yvfu7j21rzLa":205202195397,"il3VGo7JZCM4":56982846738,"QqNeyoEKonje":157643518693,"QROSyZ30kunM":585351500389,"G92w6gcgffOl":9635576333,"k68i1eEJYFQj":548806720374,"Y8IIJZWbFkCM":279893532807,"5qXCBGkHqDSG":458711454924,"pkzY8ih2V5qr":300262798264,"OpkSlMT5EX3k":280119692221,"86smTqV6SFwe":670130360819,"XOiCJvbTlZ2n":498959911121,"BsytUB6cJhsC":706085217037,"RF8vMqm1JX81":63031184063,"ZQCnd0Ghr0CN":816433676692,"IHg94orocENF":457502732613,"WdrVpx1SBwW0":26596856350,"8OwHVf6cAzIq":547530382374,"wXlmBMsnA63H":108542148780,"3p2ZTXwY0lKy":682864408173,"Qq7sEvFCs61C":111981742869,"aPgUH6NsNYd1":663361656973,"2ESOb5cjmcuF":287161325370,"db3FcWAyA7CJ":923323963662,"Irf6jdmG97WI":320260473978,"oSagZmil0Zrs":208760097355,"JkklyrYv6VJc":833782207758,"pEIbx0MLe0AG":896542290809,"aF8xesAm321M":956768049638,"1sa74DGF7eX2":471091955082,"zuPTYEAOm93P":203121969628,"IbbiwkP1vWQg":120078834413,"5fzKmrSAc6Oa":607811105062,"pIEXlz99nDp6":989995956179,"1xHbvGQxBjOh":151420980869,"L29tWn7T6o8F":18167300284,"1aw4rkETOpOU":537550947977,"UxyDYotCHUFI":823838220988,"uGfkr3ApAXVL":857028035069,"5Uj9KHnwXZcb":599889953882,"tCcECbLJfVw1":435258013606,"dgyTzWpfNZXb":896462870016,"hadZXj0BhKUs":760339923163,"y28N8jo0qHoH":123007507768,"3s7RVg2JCMP4":586309110154,"LU2fClFOykKK":835227487026,"exVu4ezLlHvo":211091354257,"t0NfJOZEgb5K":388135060097,"c6AiA7g8zHaQ":560560143404,"jFOPUNzNVojd":769721931216,"XI2bSSKOC4ap":497639371526,"QZrGV5fjHf0r":641299345413,"XiXfNtcOncL2":169119817201,"ApUeyeAUuA6B":250431017383,"7JfLvNcMb28i":747578944153,"HGHo4OrAWQNb":576319493482,"hCqevjevp7IL":719380070253,"dPlpHbbisoll":283810786015,"b4UjTxBla4pB":759322433021,"JR2MLYXzA8eN":818563797696,"wp6k73zBwOsY":718166487494,"hfZRT7Q9yIby":26001227123,"3n1iYLf7AJ0c":523456397787,"axf6J78cy6R9":484414789206,"ATl5VTDD18d2":7188789732,"WjN3SNnKzTwL":535175311381,"cuYxhW2IWjNi":654271345964,"hL4FZCNOVx9d":502095729057,"hfUxCIZhwJVo":210990883751,"F2dMHxBrAwl7":406011556311,"vIpXi4MNmn1B":462911606145,"fm0eNZPLCGm7":457146672572,"P5JC2h0vgOT9":574772388867,"UgWw1vIz8N3X":662469693175,"NkoOG1oQbl4K":122135861039,"2TfLT8Vn3OAb":415355264565,"eAFM6OlMpi2C":965424510828,"Ql64Iz7fI2C5":157984848669,"jleJ5fSINlaU":741727057260,"bdY4SFzBvOR1":799312425821,"AXbNJZRBLBHg":162418496797,"hpdSaShga4Ev":844785659017,"Of1ztqry8r97":4875297180,"eQGHCXABUuDk":645160635792,"t2IKx8W7jWpL":939225484896,"oWAYukDZqB7f":729071535920,"D2DLFpcfCTXH":182289630049,"gy6xNEjIvfeL":98926846114,"Brp7dikMzZpV":641276430154,"ra8iSIKCZJ7R":410964142398,"YBg4ZWet12qm":967224362360,"PJJVVb6Um8vB":504645669140,"4QQLoKjDQQV9":375154638109,"EQxcHEMAswNs":178681140093,"OMzZsov5UYB0":880061578123,"XzC41LrAVF7d":827307847008,"DqHQW4tgkv7y":372098334985,"33L0Sxk9liQh":570527775258,"iJ7vylxga0gf":54572100397,"kURagDwBGKgt":99898572540,"r4TP87yn2Mag":989145193138,"Z7BU5civTsOQ":642803307922,"qEOfXoN7f4PE":956216185068,"nonvNxsQiB7l":310858336315,"htVQ35ZJ3AS2":989461353817,"3mlfSB26ipby":545951656945,"FQyOfAShYjIo":18157741959,"W3LZn9SzQTQg":469423307166,"c1cv4U43Kr0B":914656015568,"QL6X2UJQ1RMX":106646763648,"QC5Ix4Vq93vw":396441033971,"z77oskf6p3rz":3952991277,"zwJDyqeBpNKm":524100351533,"lwjJ0k3KrQnu":763146244768,"Vm0oe45XqgUz":565350261780,"hqoydvd3lIKk":176239200560,"IH1iG3EfIWXZ":90498856430,"8oVbyAyBhxjs":208589711654,"8rmtpMBTAcdq":278542712091,"ySqhohrSg0IM":191345976429,"wtJhWBrfEec8":905005885215,"TKd3GqTi7xpa":645627040747,"aecclsLJAOQH":602226237991,"suAmx95r8GFq":330044705266,"ENNJXjSoMIS3":891316998831,"1hAWXejP4xWF":82694715497,"DbB9Whyg2cdb":63919316252,"w0yKd8705oyr":134132335369,"OAWAoJtRYCaa":220245863216,"pLGPMjbpVN4K":658284649457,"ONq59GEnmZQ9":385252364516,"FNDuQbRpui5w":297186841746,"ZMn4n4YmJpgD":441738617323,"K8TMoAMVlitx":42726680,"P3220hVd2zBe":177835621979,"hbhEAp5r8xMs":398525551344,"twjjJTeUljhM":385697113093,"kJQNOUcmQIa5":62943126600,"rNfzZBdlupQH":989446913725,"b4ERnsC2HVSQ":18292650919,"YnhwTQ6hqPyy":46562119848,"cj4joGGrtmqU":88252019808,"eL3mfuLdUMrW":849709358430,"7lvaRiHO9mYp":291363041668,"q77VOqe0BfCK":281083596003,"Wlx9dkI9zDxU":109879111769,"PvOFWSH5q9eh":394089175669,"mkQ1LSylZXsk":30312517052,"VxbEPHFKVo1Q":189377212619,"f293PyWmgokf":549483878598,"xTOmG4bEuJRs":973423017149,"pJvdIM6G4wcs":360337524496,"bDSbprj8XAbg":795981056501,"oLfMoqbG8yKe":403271373112,"0eYfjNSSClxY":993125963360,"iJKzXyPjQLce":191978186632,"sfxxCIbVqNRG":616356050936,"MUhIZME5yGEF":7163840692,"yqKm9JbkAq97":380297337325,"6LWH4PLM1PfK":138762994974,"Ky1w4gEM1N3B":429168347967,"C76yLCqfUGBH":556547561082,"9yRpeosPDI5c":137829117565,"HwJXpNSHbyhD":165105253783,"uSzIgFdg1QtF":857027762825,"dRUJc4oe595M":988431332219,"EDtcgXs8IN5d":905650672361,"4gNnmSl6ZyeU":254747169281,"EIUU1ztzRJAc":798847299294,"SJF8KMZuovtz":818818482035,"DAr00HTwr8Rz":530427309203,"M7IV6kXAtKxW":343395683997,"ekaRWcaXbxxN":419868937428,"USecPD2NIsqM":291811055234,"Uy5NyWfO9pSM":77090463106,"TmG9kIimWAPM":264147634121,"CdLQOvOIubVY":466006725832,"NfrBX4796UhY":346408296038,"lfmzPF2rP4eg":920073888746,"lKAg44i2Aope":963281906640,"TRXbduUBwZUu":253875308672,"RzMsaWRDTrBP":642038536591,"YCfKC6OERwor":359829573561,"8UyiXuMxNsre":696574254093,"ixC0IQvCoU09":117631293363,"Z6KD7NDNZpGp":877759190840,"xtIywiThGpSn":83866260950,"pwZlgADCK0tJ":302876444014,"kIdeezT3JCcD":70399144029,"AvVwjU6Lk52B":45105288974,"Sxhk16BP8Yzm":157963605769,"JhsI2QYK5dSi":69134236093,"ccW05gKnfaOt":126507890538,"tHX4Gx3FntSR":987895336422,"WSN5SVD7b0TT":755135318406,"qcbCAVCoSbqb":917017545291,"WHdMabnQdJKP":264802645633,"gFtu1l2xtSsu":1109358537,"Cz5VaAjqXkgq":823068393454,"4trtTQuMArbl":56595040491,"NVJ3nuFsHAQx":533599887854,"alKq6GEtg6DS":894990977558,"RcHYOkL17C9b":714872198621,"CjfOLefWkqN2":302062083978,"4yT4RzqydpOf":405344103612,"qRNPyVD9VMMC":400204896994,"stkpv0W9OmVh":518529596828,"rlaDl5Nnhnr7":761451180246,"hBRxUcJjd3vr":260587961616,"kj3THBRopYEe":934180244164,"tPGJGeJ2wOct":899372015826,"JQ5jC91Rz8XO":480820098585,"EUpQTpdtaJfV":237652008865,"pxTzRS1QlSju":468720126607,"CEcKGSOKK0r1":926209592870,"lvcFtidhbR79":532962180469,"X4R9RreQ5q1E":23966877934,"FZL98N4ZKZuY":291465452954,"YRhvEdQz4ibw":198757219626,"tzcI1RVj8YpL":53605147714,"CdZUvQh2xkJH":147232745941,"vbUx5a0KeYXs":827585456104,"aOt4uuavrEcu":318866991304,"qQ9uCTsJIKeY":578590057943,"8epFixoe3UVn":520261590547,"ssqXhA5aaMHR":711461804979,"VT2hajemupKa":774511883910,"HkjHpbDRQ0q1":937820219003,"259nzNOcLEAV":482320259276,"VeHAOwn4jGIi":823017280290,"66I0PyzaR6MY":539685135567,"dbF5oPLirZV5":845278305445,"2eVP779OJKH8":315468299121,"oX96NaUNUScW":628502166268,"QhhRt9ZYPHjG":806812046538,"si7Jev9Zu8yS":361339873365,"BFIglTtbFQPj":554910700113,"eer2f41caNRa":865951951347,"1iqFsag4T04k":512208762768,"fk5DtlB3qH5x":933644343313,"SDvxDcHyyIFN":85304436257,"bNqB88wFr6Mr":174333887127,"GFgwkZxS15Rm":341795043088,"Qn4L4mDc5lya":280405780181,"tENcIbVFm2Pk":400598279100,"GpYI6IpDGhuG":399767996839,"5bzJt99cWFmO":796531891584,"knWG1BHMqczN":274003717345,"CdTRg7blzHvp":996949837864,"8cTfPN8Ouh9n":165424956394,"nEd6fhyMAvNM":569042367635,"XFowVls7BiEm":736780051849,"T7OQo7RfhLTQ":434423675418,"2FJTHUaNZF02":541350215697,"v2qnGXjin1oo":842121553528,"3sIomaxbYmWV":525810627473,"x6sNEoRl10XA":391739002502,"lA6i3bYOaZdQ":472745442352,"Tq99BRs3oZ96":809950912238,"0FMawcf3N6Iv":959796644492,"ulIfDTmtNZuN":695366445263,"0F6VkkdBo4d4":951805785839,"7imFrUxfaBBo":190003519642,"HO6qxE84CHoo":950106889000,"uUNFOEGWEYwa":924746191418,"JyBRzMf3mZY4":179321406087,"v2PDzSavjLv4":5999397934,"cVIUNYLyRqf7":707741595253,"sjUtcuxHKnMS":69808286981,"BRmJQpBZRa2X":611970430738,"re5oVfD49tFf":88616120809,"DDfakzt2PbUs":966567302731,"GDIO1mBWDhHi":239201024674,"FQ85zj42VM2h":615647315972,"rhTvL2ZVeSyG":537166162346,"hnxS0CQ38AQn":581698855368,"VL8us3wKLABF":498639755466,"BqZ1OO1uwmDA":299528595965,"cjIbj51kEIN7":640910700796,"0FTeuI3kEUYI":69938969661,"A5nSuTLZKuRx":250466074096,"6zcW6FU10ePX":820956318295,"Tnte3XN0zcqK":233980524,"9EtOdX5RVq9W":202209734271,"nnQB4pgs84BG":70586343317,"X31vDcQftBVE":962100204883,"RjN8UwwaTugE":275245274081,"uPEIkCAoTpBx":240134785138,"trltojHj3k3v":826153528619,"vSVXTt67KTCR":702819127822,"zPEPCDsu900v":859156080668,"ukR03OLZkgiM":759524967896,"DVMmHV0F8vhn":856513736476,"9FOhPq1SJc1I":661520277222,"pIH7QBwLDS7L":543600879983,"gIpnxKlXtQDG":36824787882,"PM588B3AUxJ5":741021310573,"k7lGqK3H0XzP":385866828230,"OKhZWW7Reucg":287619134602,"c1FJjyzm035s":137131868712,"GBxrWWcwKlDm":177437075443,"gqD60vTlddTa":937718351484,"0dKk9M6pwdVI":219110943767,"ORjraU5jCB8A":501656651057,"GQQcZMbpwGqd":238280704231,"m4TX0HI9GGZP":254282990019,"Wu3Z1j7xyCBu":675016771146,"m9sFf2pSNUG9":297598595066,"JG5Ne7GvSHNH":968300228164,"bb7yK8Ll6ijZ":316063422247,"mGurstGZPjrF":429203419879,"JfbOiFg7DASZ":835354921271,"Fbb1rGsrrvoJ":310236209531,"Tldz8FJ4YC8X":361186805743,"BBAbYIm48SQ4":246345776747,"REY9EYqE3zXd":288678970630,"yeS00ZYk8j7L":708339363903,"IUcY3Zh9Ax5k":135728098369,"ZenLCB0wDM1J":801885814188,"7YutwWmNMUNq":683901583527,"hxdNhXmpZGuj":873130202282,"h9wEhSyxYRGj":448769087218,"Xq1S3j7SxZnh":905335765513,"UllAsmSCRfFq":578963817760,"gV7piLrMSLf8":241698668230,"qfrfVA1U4MRO":352864124504,"tinO6juV1WQN":147215356689,"QuOSOahnaMwC":806399056056,"rS3aij8imQXF":905520157566,"yXqg5PdOKeKI":557104650159,"imGFPHWuSvMo":660312844968,"RMWYcxVx2RFJ":556106395002,"cQmYShGaF7NT":563378731130,"F0miSqt8BiLV":45411428753,"TgaXj7opku6q":846459946183,"sMPIGBRtFJiZ":475716254276,"YazSQF7u25q0":969247809413,"24NxIiZCPeL7":65236485231,"VzddCSeMkBVK":177155561035,"EqxAoMciyhZI":564519807708,"MIcFtMjmPLHs":461859379163,"LJhsgZ2qXWF3":649982894015,"YG2jHB25KiKA":627652980801,"mgtl9SRzbXa4":44614421695,"JXSUxOEQoYmC":879676819188,"a1sRGEX4GJMd":640261262658,"CxrLtmxkWdM5":253216945584,"NtCZKuqlXTyd":209801408475,"j8bhrq0pbJFf":897850865211,"1Uha9ZbmesIJ":811527733960,"iMqURMYS7IOt":671082731398,"Qixijv94BDuS":597640062044,"87XklVyT94Jm":870753693527,"7mPrInP6vbD8":820456648091,"HXE3MBFqdLgp":635031716070,"Wc1998ZbbIfd":185664213693,"XG36fDPhewAw":375749801603,"GDb63DiQOEsg":843977850194,"15QN1Ag8eoeK":571198526466,"RMZ94O3dGpzf":487945292547,"neDB6nwkIY30":704778829887,"uj0PdYBgnauc":333205411905,"kykichJQzAuG":679942321456,"z96au0EbuFhZ":516773241453,"ugnnndxZsUwQ":8680952480,"0ktTwyDh8yi4":69861201545,"5FHKF7ly59aX":305359551951,"yV2rxrhhu4U7":921318644573,"xLNlV55VCquY":715890174571,"IzhpbFOnZXmD":256717799482,"g0WgPTmDAUtb":932655615907,"2OIqrHftvIGr":311144611819,"Vgv6CFeXtA8Q":505328098399,"UqyVJi76zLQl":403118823081,"RSXSjPNJWiOH":921647207545,"J1R0vt8b7xTU":792070963780,"TQfOVCgU0eZl":478583338965,"4nRlBxRRvv1h":532736297190,"CL7XHAwVw6Hh":536762223254,"nQDL2JFPvwDI":594321169479,"ruLX3H2yXq3u":37905280750,"HUIH8BsRR4Fw":433563786382,"ORQ169IOPML3":889591169658,"BrCekBIbNWaF":286969948883,"yd6jCFzDjGJk":53326444923,"o6jo7IrEKTIC":193895862817,"FSXjD5LcHE6X":194905820653,"XAzJqvnlFBZj":492937142339,"uQZbjNfhvoam":861619673021,"enCNlnN06xwF":23257377058,"zwnUvXRmYa1S":800917189548,"6SziRg8SptiP":953156197948,"bIpQuyu7Etx4":513119349643,"GA1XtujGSaVX":352918039995,"UhZC2m5KPEka":862854325563,"U7ahscQIb95d":388682329174,"uNiiAftCD71a":953990278381,"HFAZpAFpE5Rk":624460995521,"UBwvZ9V0SRxS":655935152501,"LO0so0ESwNZm":33687960316,"5siPH4UiRydI":848877943479,"U2jwm5vSlhzT":743651138500,"ZJz0YQfaKuEp":257095989992,"NUhkXf3MLsFu":597627731015,"K3oUyfg1pFBY":205681368859,"UpRRdX1MNoqU":180414889980,"uNmLlcbFtlrh":702089470795,"zRJ3XiK000Rv":728223954259,"Fu4djpwrPxha":194059796809,"WwkOA520bC8T":414198140593,"bdka3BubUc2A":542708622360,"kMQGJiCL4ekd":27696628496,"CEuMXoLWVX4M":551221035901,"2Dg0zLMdMabJ":559398173641,"M1d0oJYlhQ9M":337279020606,"yLGJpJEt0h7N":112160458058,"I1M741FcbxPI":549278903963,"qYhSFWjLAjRl":216248745090,"pF0vgbCpqlwG":851366396330,"br7INj0qxBAN":420575363732,"OS9gMzB9NOyZ":516126582599,"Sn8QEVHKDhnC":743516053437,"KwWFDwtGqHdy":372571748290,"MDaf9dBB4ULx":650640438492,"e4c9MuONelAO":968175702063,"Vxb4WR2m3rqN":158666956227,"eahZzuWjR3rN":614489102085,"UgblY6k3JI0a":790548426136,"umA2OkB10U7u":134074242712,"TGqrqFBnfQpL":876390013068,"z8T7RvCo2hJ4":289016115612,"WnH1iMklQdXj":374438462384,"gMf9nxJnXVlA":582503754654,"vlTReTjrJhFa":530175021759,"ZmLLHnzAkGSW":875203577020,"Ns2nFvNKwRSB":311265341175,"uX0IottrbK7k":942921394360,"qBTwA6fGLixn":565576615299,"8tDhV2PM2bc8":48045210517,"gpsWLpTIySt9":393645961876,"UI7RYLWC2PHg":867897656545,"AHYuzQd6wa9q":509796920884,"kIX71MJz7u6V":46552482678,"23uSSzPUfID6":824302640502,"ZfcEK083LGpi":14303571717,"RJBb6epyLm64":477717515107,"fBO3mMGFOVQi":48424644165,"ATXNFlfIHc2b":878229000713,"rTCq2MS7kfNd":633196021892,"JiTn9EGwwH3q":844737486854,"U5kmzxf5qUTd":314177158056,"jn3Y65BVEXKy":410465665200,"XFOnXv7R2qu4":474565779499,"gAIHYwDPytrT":952102243086,"kUrSdzCYU71f":450872872210,"2TbsGyLpbF2l":225928265324,"flCjhpo98pYI":639053820282,"DM79S9pnqv59":919501846207,"jLUWa2WNvbpb":367924209901,"z3E7xwg9oHxK":847858208743,"idfJv2R8vg1G":390159592328,"o1TdfU8W1sY2":625866915892,"V59bkcKc38AM":619338524600,"JUkw4UoRA2ri":579058277190,"5l26XkoaMc7f":440509255679,"gBcJp6fyw5D2":11948049901,"1X2oOn7Vokb2":130889299730,"wwJTThIBfnWj":245911623469,"UiBxbQQ1ZmtL":364894403529,"ZKMmIgg75IIF":400589870220,"HlGYFEKKl073":934904943873,"AKHgZZb2H8wy":613734352559,"BK6Ncyo5xdTE":757418623536,"Rw3aT15NHlrS":918794025922,"QgJ1ULWAJqD8":548238739477,"4cVe9X9DZTgq":310197770551,"2HNoBlM6Vx09":95310040836,"3N6GYWuwk9PX":938399098204,"MM8uhI7Tjric":369498796237,"yNhF3JsmCWaT":296033097419,"3LSYAtyJOykb":832982042851,"0YCrFKx3E54w":649878720014,"cZHxJBP2wFV6":596863695714,"Ur9U0vCyl2IL":837591400720,"UeIDfDamf7G6":283832474597,"rtAfwlFKqZUm":625827579376,"j8FLIFUDsQ1y":166429106942,"kyhLVYnqBdT7":594763216176,"al7aooHrQh68":804605348543,"NVv3j6bUEdtW":930663488013,"S3K6rZSS87iO":284891114201,"cFYqjEvIiF3f":637608684298,"KFqMS53ZRcKn":894147592259,"Do3GbNE400V7":817993791492,"1sIq31DMD7Hb":935848685896,"abMujUZYcYEz":752567504760,"f6C6gmIRaTR0":29608730299,"TQNFj5tJy2d3":705102280380,"lUE3KSKRcBiJ":350201929776,"GuA33PYsT4gP":785337582155,"VFhOWoyK1Flb":428036290833,"TR0qgCs8YWs3":330859441397,"cpLtJQWdsiO0":772701585039,"uLRhVilnb3e8":601683562157,"a02zSgYBYkPE":621049805541,"ewk0nP8gRM73":625696361492,"QQVl7CqajqqX":246439642831,"h3egcjKA18iL":824898750319,"gkHqNBqqTHDj":601738617309,"pn6rR15KoVB6":725397189361,"xVDzNWyqRWnZ":55838763696,"nklbRXJvBaBe":129141934911,"ubhfEtvWicg5":377324138138,"vkMSOdI9dIeY":97662965407,"XF1VJ8w2nbdN":162311593661,"eCDw9GyqLBks":568047558996,"pt5VcelUMnnV":189868937247,"r28YU8HhVZKn":510784881078,"uriPrM4DrSSF":769173088831,"d5UNhbbcX2PX":258579910522,"KDDWsfBLZ3vL":55442068485,"dnQSH5sRHRxv":242990715772,"k3ApFelsimMX":410556680134,"Glu1T3M6V9Ak":121358080406,"1NWVeZnzYb8l":129190260322,"ogkLGPUdP0BK":808870104197,"bSXa0F9tsqJN":576658059638,"2F2EcXkefkoJ":410023997315,"wrKgwzI5ewNK":235302269913,"BUQfZ4TXpplj":247097343465,"i4ENecR9EL6K":427419687797,"HgHXfls7f8SA":153014595,"mtY34oy0lPtl":915731033894,"L10ggC3dZ5X5":130130073422,"l1hN8yJmguao":359454600456,"4rFTsKzrTNwc":879112689512,"XA8maTTZ1DjX":466794820942,"0x6MSY7hK2oG":26338852315,"dhoDKU2c35n6":584942365120,"OjVmHzTQLQ7g":947037517010,"hwVS3mNjkyef":570426097199,"laLvLw9NZA9B":531085557173,"atbOhJKGI3tO":655157357452,"gm9Sf4uBWBHS":651741609445,"DPSDlheoPYYF":44367157497,"pujYsOvghRRY":692842027904,"yd4FKRAOOKc9":13688305674,"z13B80q8kEdK":689168677641,"j7Cja2hJW6Hr":187122822742,"nRWs3Y1BSoLx":685962532347,"7cOqbUV6WWmX":453887225466,"3xy7t2pCAU3k":593133814356,"I8zfdC9u3W5E":795782092910,"npP9aMbyIjWy":542016579299,"hNugvej1KpnC":463095635909,"yqHpIyLXsCnv":462785389116,"AUqHeqhBcgjA":959506516528,"HBTSYNjU82Ro":442181041010,"Rb8XQXIQ3Rgd":432758463447,"M8VxzwPzbFhI":671640740540,"fplg33394vZj":101399894913,"cVeJUiW9SAd5":129609206602,"LFNCLxK5CA7x":771838133904,"ciOonteYHoEp":488832076239,"cQZNw1Rv2UoH":975215577618,"63XzttYJjrOa":521064133771,"1zlQOEbY2a7V":586718790187,"bCmSa7na8Clk":653061765476,"G56PDEtR9eyD":570081642086,"9rPPjef48nQM":962528482408,"5XPxYdzeX6OD":335425648484,"TWhYEWXdSjnu":466185989424,"41Iqt10DL74n":798734685447,"N3u9Uyd5XNbR":694669114601,"Rk9zccORRWm0":879467191570,"cKjVe9wWBxrz":519017341107,"lqc2crrSqyBz":220565847982,"ejAeU8L0R6Vd":497950838233,"0eIl20jVXLyN":543989552868,"SnMGXStdJnNG":978876600221,"3Pb5XCS52TKQ":413498901604,"n29X8SZYVru8":962755517915,"1YcnUbexPX7c":428394850817,"QdVVH5tUxQUh":868406850171,"bacMtuBsCAmN":796992447088,"eCEckzdG2Ks1":907311368904,"4aZ3PNyYt4zl":712812493759,"7wDFThaNNfva":499972985686,"rdOrCyAi43UE":446539634845,"hDTsXEwqavsO":948075007927,"vwWBENj6gNuT":277902832070,"yQKbE9b6NjHj":124260588598,"izUQO3OzNC8f":88006771862,"0OewO7WwmFPo":539932755536,"A7Hhsu88vaTr":643046605481,"L1ONUKXAR4q3":181009586491,"7Du9FIstZwvG":147986647719,"M4rXowbu4V1s":347829176090,"Y5wToiMx16T8":525298685127,"MEQYVONvSwHC":269487930596,"B9SA2MmVuMmV":809337782127,"xQOaeu56aZfz":681459433605,"wO09sLq25h8S":968186761670,"pAcsr2TAk65w":25833123076,"EylesbClgl1J":415281691369,"z37lH3B3BEEN":139461515470,"YiIFLpziQaIv":525897318672,"6lPbZDO1SJkx":2758308238,"VJjtMDcclreu":775606658053,"C6LVwgWjW6u8":188208009604,"Y7hpXDEojCL1":929990757029,"zs9aM51uglEE":959230907776,"dONzhY6J2a6x":444222064241,"DCxPkiYh25Ve":670975736362,"k6T5k7ZDh4d5":954775919854,"bfjMsWHG6ztF":175531781250,"7ovDUD023PuP":760332935528,"TJNHyufvnP9V":287245754494,"goZ0pTaN4G6b":982585034103,"Nr9eRANrucPb":634601835125,"BLDTOQUygMx7":813370921362,"nkLwYUZk1ABb":570060650801,"cBSpt57WTdiE":342985998,"1dy3ppAOtvn5":185214006004,"kUBEoWIZqTsP":387594146286,"uQsnCQAYfjRq":618744138849,"3gH9brT0i0Cs":82216487679,"qaC35kd9IVoI":204044819027,"TB78PD9fDrng":23678550151,"LuWAFejjszX5":788370388860,"1eoEIoFoQPD5":280641481208,"iQhtum0BM16h":393051628156,"8zXxHkP80Ju4":904286642343,"zxZbORjSpngP":243937138071,"jN3mklk0ghHn":96870335402,"Aupw3eqJDpPQ":760175104430,"d5rhGc7AvIVp":51118179238,"CfJBfNovCYDA":580294876325,"Y5JEUsk4nFv8":329234631011,"XCBomWpR0Qwj":53356448701,"0vIwWLQdYuDj":103357462107,"0yM7TbtRGKPS":661986586818,"IWRyXBnAZ3MS":377096494198,"N559PoZyf1Sh":994463722455,"eeFt0h0OOfhg":432587987909,"iG30MiFwLkDl":44738883994,"gUXpdbrbjv2y":566446912663,"pvReuDqLChRm":726276035409,"WNRyPyTdZFc0":448461337715,"mFMVEqULpQUv":402568666929,"8oak5zvbMkar":710978485312,"RYNUXS42sKi7":591184806078,"Kir6zJfG8g6o":165479016591,"PW5xwEl8pkCk":432991778877,"bknw7hItIjE5":466830234604,"fx8gf92mkU2o":433843152197,"I6N2mQTjqlp6":980112084662,"q7KRebx3ruV9":43333719683,"gowMLHnwXDno":390127620605,"4rQrmkSWQuFp":358030600625,"rL7hYpKkXF7O":834069983525,"Bs8vRzXjBPJ4":627153772727,"cGtqi200H7Li":655986599827,"aG3UrswYUNIK":975614096223,"9NSRVTzF2Asb":443270909373,"WIS5Geh30cTm":168768500234,"faxQo4Dtpf0N":984396478518,"szMAOUi2BB0W":980158747847,"nGF7sHKJHC8h":11063185425,"KoQi8HAaVHQN":160759267315,"WETY5l9w0udw":943292371598,"qjr4FMZyFdxt":922655121489,"i6gvScEWqZvQ":109989455551,"JBvR4UBrGOws":897789878778,"JdsmAtxCFFMF":836805495618,"PnUxIiYrpUi9":131363199533,"vze1M5gOEWY6":756439475463,"feh9aqbNhRv9":798885550091,"dpqRKPKyJlzX":959468897552,"S5UrzB2789C1":40663544750,"AcfRyNipef9w":302196137164,"9saeAvxLgvDP":587969031910,"cqmZNk5wJZVe":253243772751,"V3wZJAjx4ixP":370536159831,"4k2yUnayu4c6":822229281411,"khFLCFxVgkKE":224325325594,"WuD4RyLOZ632":9152106543,"V8YVTYNASIdg":876969496578,"ZgpQg5TFJgpz":839446153485,"yMcttS53Tq9N":968293600338,"kIlg9oSJTtRe":773840322764,"I08vZ3DqVo8W":721402184544,"AayctZ5sVRzx":668071318065,"0vYb235uSVCP":647723937581,"WX3JxiyA1P5g":276691774362,"yi9oGXoTaQxu":281976904062,"cjq2ZXcYMy63":593746089630,"b4qwy4OaFovy":256789629822,"Hqlw7Hnnyfmx":872438151265,"2NN6RfD65hC0":373492946879,"YrvAAFEgmkqk":513848988314,"Rhw7427u8rY0":541044864663,"B7G815695FvE":432390490773,"9tSWrzfVYmkN":231865688730,"eCPx1FNNgy6X":137284797314,"pwsmcjZ3BJsR":926175461969,"PKpYTQbknYlQ":999497177159,"IlkbPOMz9esT":697620115250,"ScdJQkJd4Xww":84092126994,"6awiOwzzCnTp":123204262932,"NOejRydncc0o":839906728502,"IP138FQrnyrQ":73898570338,"RYvofUsi7wkD":569025939955,"IzS98Bbun45T":1424395832,"5rwxIiDrHAqo":693243128505,"k5CP1dtcVFSu":443095965912,"Nej7V3aF052Z":380692099276,"CtQS2GdLPO31":684498166602,"1gKsIaetPSCc":842546868110,"S4ozPrz5pMyK":435987915512,"fVUAZLldFx1m":409107605848,"NFpGXVKYa6jv":86208899965,"a23LgLN74r21":841723135791,"z6Bjb2xancNI":960094252662,"Tw6CyqUNl2L4":764295914087,"wbeGxVowuMp4":833476705109,"zK9wbSnOkCXH":130732854351,"hjHQW84lkGFA":159333876039,"xBQJuYl3mWyl":567116626942,"qUwrvyAZXf7c":529429253289,"lR2VcJdUT4OA":495537577535,"SxPnPntlxDVa":582005263672,"KmHSA8jxKGPN":912105331029,"zV6xpeySXLAZ":31648290382,"lKdtlg80mvr1":841604716318,"gR96rlvMRGKr":668126716956,"1aJCxHSu2xt4":741627367766,"1AwywHRZMm8s":340245023314,"QnuMP1OQRqRZ":957890170505,"y0f77Legmx97":490411494381,"83PVHfOGPpJW":26619825563,"xFbfrwqGbZFf":506375055246,"IUUVkRLeiSZl":247383290879,"GJHTbz38ug55":887197570231,"fQJL3NwkaVxB":801505500435,"0CuZgVwToNSl":250455460223,"pGoUX2IPDWIp":365324358860,"7H0oNdnVLyIy":88468881750,"e0h4pqj6tKD0":332724028535,"f6Z91EzmiL6i":980597932679,"Ln1x5RvMHJPG":931930116196,"DLYMcEXbPuwr":348986092112,"eup5GPMCwq67":711926756924,"FH3TNQS8CwGD":929182256288,"dnE8BSzYJRr4":880159132511,"O26PBubdWlnO":551679826096,"Lymhx9XNoqkI":157666278497,"wf06HcPESsJK":830071894161,"ulhtVB5OBGi7":243526907064,"4h0VU6QGLOHm":820230587764,"ivOxnGFKVLYG":783401381514,"qAJO4bxpL7zu":69903422592,"geVJvQ3LN4U3":468549322867,"Nj27bP3Q9axs":205700594436,"JwHVBNJcWxCv":968570040117,"h9z3hhiOPRir":56772320795,"4Qk4FxW0TlS4":734119288568,"0yTl3Z9pl0kI":384092926823,"k369pWh8B4Y4":80770852788,"gkAaPRklRy5v":479366915748,"9uq8i6s55dho":608060056527,"zOHaUCMzmuxM":20907789729,"CNv1qFzbs5jZ":73960267261,"pPQAcy4iUz7g":885914160672,"WM11iWFR2ifn":636569825430,"1l19EbhF8wGs":688101544873,"BrHaeoBnN09o":838605767766,"ZwAlmjTwRrra":738592809491,"5AGFphcy84rd":108520771269,"V3AcoboXoR99":713997124205,"CpK0TbbCd0vI":453780378884,"uNyrPRqxAogO":817172650152,"thSFggYrtm8j":725009199275,"b2tWoLuKWjVa":499893427463,"Twp3m0x2IbA6":622555059885,"2vJMfHecadx7":284223078227,"SWzSp2nc1b6q":60443293873,"Hpyw0ajWmGUN":729454449974,"T1ZmZ2RvVT4m":530520573883,"MGm3kTVzDavR":863219788065,"696BcCzKO6gT":688388346766,"kJWF7RSWENu6":161089114640,"g2SWX5RodMds":962230315415,"z3WY1O2eN4yl":598711870370,"IKEHrvQb0kR1":716422408982,"wSpMYjQjqrJp":983063005982,"XYRw4tgHxL00":14365713473,"p9I4KcEtBuJd":151152414187,"DIQi0xOQ2AaI":86654103999,"QV6Ng9NrOK1a":713222401286,"I3NJyftGrn6O":838770335914,"gbtXMSUt5RBv":772745415210,"ROrDdAHXCpEn":274184495996,"LcTTBm2lQqo1":649306138345,"v6JRObDx9M4V":401353445804,"I66vMHXXvqXa":367984253658,"KJ5v05rAgMWo":245748343225,"H8hZwVLIKAFR":94387960476,"YueyfVh7oLED":430988825425,"N2aHQSQP145L":985680471044,"O9kwxVuSjMgO":378826354478,"ut8HBepJ5e0n":298253362749,"ZtF20GTtokFt":17826779638,"0vwPaJa33HPB":657313513504,"tJzRKEquEfiH":446319259936,"Wf05epMOZguN":233283738733,"v5F8JI9GXoij":475998564011,"jPjqhGokE6mi":333639847239,"skLjeVAjeomA":56857466487,"RUAetBBafEO3":556317160659,"G4TysjQWfmdu":140905828729,"wgOBc7050A01":865731716113,"uW1oN0OeXNOd":847065053050,"znsszkaal5Je":786466579879,"6ZHtjfTiPQ0k":624256327253,"uL8RkcW0Gxrt":166806080137,"SVkl1fuo4JhK":411102833824,"pWbL9Kd5TUTp":996971935879,"tC0dz4UXxtAn":770171740535,"Q3AwMIzPzKgd":205479531068,"S8UVQlChpBJZ":199336762892,"sydIKFEMJmNw":3165447874,"vRcymR3VXzgs":380706522741,"MreNsvH4rWR7":994854537167,"dICrw66PzaDO":828512586242,"VFrsyyDrJQsY":666629800889,"7O4twuvDwXmN":438917071901,"bgdOjSvIjR8O":195730181069,"gE7nZlPlOcVO":316888948438,"OatdwhXgG5IR":443183256695,"9SHTptnqVHhQ":721416859566,"EcP9st1pFwAZ":45583125545,"1PG6nXrftdyU":980447637918,"LDNZcBTHY9EL":297023843189,"R95Q7kntqPSG":606254613211,"Bkgt1F44Zqqj":415536770172,"EEvuKLwbjWpE":831555891608,"3jMBxjaJwTyf":979542068078,"aejc3TcxqH63":425769679518,"dFMIiG4e6bGE":981510568461,"rkLgxBPAu9pm":604620474195,"9NteWx0OgfSu":486988383416,"feWrKnVmDwhm":304635696377,"DciiQWhLf7BE":836787452094,"kpP3ePPS5jZO":633351478983,"cSOKUHVD13mn":231288540369,"arkud9Jp049n":409406928427,"pcPBIrnbyyHP":530367732203,"GwnZGfxzpgFr":294807341378,"2ajwl94x4v95":69753417500,"f0GNgPeyAI7c":617100696791,"071xfmrbOVSo":318185544626,"L7oKbLI21VEn":493858233806,"aPXrn4N1f9MD":667926541478,"tEvHXe6s0xTA":827489745050,"Vj7CPyExC1If":731295540342,"rq3A1KAH79Fh":488814749592,"m5h0Kn8pCWlW":96960411973,"4MBc6VnBDeCg":431004157324,"UM6CbekvUSXi":111561275596,"zwtV6Wl6f2J2":600697698552,"2OADnnrB9XEY":739871245316,"VM8KStVqUj7W":597113255768,"TUfgvXW0dy8k":867043341969,"Yi8JBeEjNyI9":908092232040,"H0pdU6gOKtQS":474512760945,"BmsgvBdEL3C7":752330628770,"q9U5ymHUvjcT":262167356430,"KdXpFieRj8vr":464787325290,"4Lzofd51hJC5":378409606640,"z8w4EXGtlJge":57704972862,"87scfC4kPoD1":898916330396,"Jt9ZkGpJeOwd":853904239159,"nbEqX6AzqF9T":995926083362,"sWv1ccUPQmE7":874651476917,"NiMQtFoRCKAc":459410917297,"CDnpHRSV5VJ4":811357396607,"u6iIwz0VjGwd":500587995675,"K6w3zJbcPX1S":266375457408,"J35yUvh4vi1S":378320858588,"CXItMwb8PRdU":368339165338,"M5ReCPOWp7lr":244740172452,"ihrpMSErZKTo":520218879334,"uSzkvtigJLFk":675039717695,"rvr6VgUCFgyZ":680758330782,"hE6fw3kZkmmk":97843568810,"a7wvcDTdfTQj":396611792891,"yrPGUxqxugwj":245826520276,"xllBj65CPFw6":683724887622,"DvzvU4LIHrRb":952101116764,"zztMJTg1bxXc":734058629956,"su3BjjaukxLW":860617860132,"fkfk1HNdJObL":409826250766,"z1LCevVWW8En":906838085418,"zvyj45wsIG24":360612525607,"7Q1Mt7gN20m5":698013902653,"n1CATEZAGiyJ":976333282054,"1Um3lcrmmhYK":357629210965,"MF1e4U8v18SW":989527461485,"sr5EBWZxmHMo":753255211454,"NxUmZJ3B70oy":269734324057,"fEqfiadi5Wry":797154275528,"60D4F3AI8Qh1":728871750764,"iHq540CMeOw3":831236368012,"e1fsG6UKQUN8":182349858769,"IOcXxsXP84KX":665494781676,"XJDlTUHil0cW":624926663720,"w9xXzSY3XPem":239776823638,"Jq9IfULXXGh2":425855215631,"SOmk7kpijhV5":274151680204,"khqtyjTcT38p":559616617648,"zRwdXDeXOA2J":426172989282,"wK56kjx6tPIw":480095243675,"yVtqGwKER25g":972241233987,"1y1n3Uj6itob":494308598338,"UnkqSjKqXgex":497262383464,"JBZWZU8QaUs9":806311242489,"v0cDpvIxZRCz":302660349355,"tUSk27FT92pm":954341356793,"yKjSvZh2rq3P":277313338809,"am7PAJuL6hUV":344164170880,"45HRuk4brCa1":109268151836,"mQxohEECyBRx":539702973622,"xGXx1IcjGdiZ":895229982155,"fDnUQ8oddZUJ":942273281568,"ejyQ8mGm0NxR":923243503470,"rX8d0hjMv9U0":684808824033,"VpOkH6fjA1yG":781777601843,"70NL9eJ0NJWd":924563134256,"yfT1bovFQStq":530382067140,"ei8lsbxcU6l7":101983374671,"KKUBDGvhg8uE":167380728862,"T7PSVFpolKne":483048206257,"amB7Jc5GeXQA":788261946502,"IO0LDGYeXwiv":341977285857,"jB9emRqizzir":356939585018,"VMz1c442P0Nc":21806760185,"ecC0vpLAOXdu":707858178222,"f6mv86Rvlkfh":743473485817,"DHwk6FGp9XIV":754192610698,"F2VPHENVv5P0":711492083007,"ZacNcojS6nv0":149487757601,"Y5hNKyY2RvOk":865694797834,"mxYIOtzVrHaO":299736327032,"6cr4y3tE6xuh":582547808017,"8Xi6CgXghuDJ":224743558107,"NfPhkVqFel6p":849335045324,"moOnDN1pKZrO":781988722378,"mmK75ZLRqouz":782640564891,"nV11bJtpZMTJ":479657228207,"w8R97XlUFySv":362359334710,"DkHMqPcIXQrE":160706313379,"s6gkOnsfc83G":189033360879,"HIT0g3CcuYbL":132142429324,"Z9IW5BoX0of1":778828389731,"Q9v5cS2CU7gK":457907142117,"az6Ja8shzKvr":641184981329,"OK9GAGB3dIRP":41478878904,"Iv2UxAjJHVZQ":456075260473,"hX2JjRqyRmoB":103296440449,"IgMTaOMZvstj":674573906680,"KPUqElCwQhY2":77370327640,"2L58qSsEKb3i":487540900544,"a4gS616S1kR7":329982484247,"VJp7JOZIcbIN":95310616645,"U2RNjqV1UWJ0":68309110924,"Atl3CTW2DTag":248048840625,"sqfFlLLoQYk4":293077099847,"kIL6cwgN7oi6":957375863442,"uTLiHmBKNFpY":244077887323,"ljvnCYyzdYX1":995714246762,"b24xQfIQLKe1":226020307602,"RpN2WGCq8Yy7":154807332015,"YlBZpZu0XybK":161961763184,"dHi6iiUJlwi6":947061563439,"TXEOuv7iiqaK":602827229294,"0L8sYVLfVslb":494146271691,"tpr9opealkBN":811240962830,"kuyhFdO3BHYH":589149577345,"8JAtHvuJiTB2":903034824553,"mTT5vTQf9Goc":883269562955,"ICxCcNQFvjXd":944262090348,"KqsUZ0NU0jP3":359810096901,"aFK7UIq81qOX":462910156317,"E1DqYN8nzNxb":616675266906,"nar1BiAMsNIS":720915592667,"MvZzZTvPg5K8":948475547791,"uQjAyfIojr91":645370188099,"iUi9DaK64gg3":110683436444,"KHO9yH48W0sy":421767759870,"XfperqKlC5ma":791701937752,"1zwEPjMMyKj1":750975649326,"MFhm880hI4qg":3323703433,"GCZsHqCmI9Rg":834860098789,"r9G0jQVAF2vP":989367142693,"6FOBUQ1jALYD":736251091823,"dBA7t4ErXY1d":256094653844,"qcbKWpSEN5TL":810229454565,"Ggijf7a9fhsu":10175781752,"InSv3hB2uqVL":31485922489,"8Pq5NjCmWRhw":94081869863,"T1fEHtbOSHkn":586434267890,"curGtatLYVE8":467702935749,"naHJuoDH4EMQ":164872884248,"6k2BSRO97SUs":959023865482,"OxNUKhdrtcDP":627342459821,"IJgeegs6tZO9":849738058235,"vFjk6QD6EEmB":353976600294,"xSv1P05cjoU8":298494450945,"kDEQUGJI1Brq":322682138927,"FLbQ5m0Lk5D3":283204393754,"WsOvkQXlafeI":971675162257,"HJyokfltlE8P":769678454277,"DjuuCIbSg5DB":205874290822,"vaa5TXXJelHF":795485003725,"MUuiBq17Ha94":15992942055,"5v1aYmGoYrtg":7828395593,"6rHK4IAXcNiP":544460145201,"UkIAyKpB2kBM":116875016886,"BkGcGfwdH2IK":891261587054,"ycmLmsdId0PG":369293121074,"0i2trnpOuJYP":514229042174,"Y9d1wr1KiZzT":677759225267,"Rp52kVQNYIFI":41421771212,"3oprltrM2T0W":430271851128,"gLL0cAj4kiJE":269048533032,"Zql9BatifhUC":876930008457,"UvotfHrlF9lT":322429261681,"yI2TqeqUOgXT":641396143722,"t6LwjJ2imOd3":638039971236,"jgQ4bdJLf26o":411414804393,"M7tROnqxLSWF":397566410602,"fmVlmwpCK3yR":34108033313,"fneDdti2rqU2":945354366424,"rtYV8UpH9x5u":664957013303,"rdfIXu6Xbnf6":159884981655,"g0SOO9FtMzHX":725145998891,"CtOg4kGrFRCQ":233084615417,"ur9Yo3FZpabz":269508569467,"J1m5Se7G06SL":647101790311,"zvArlzzwXALA":319703745887,"FN4YehLs5kHM":826739682006,"Py4SSinQz5wn":994316343564,"5DZCCfeO5iVW":853197810686,"BsUfKCm5seXS":466875296096,"ZsrEjLJyv43M":809694524912,"1sD5JEi42SY5":868939911617,"rye5X4OpuIhz":867781155628,"is3WwkSMHVwm":682005315882,"OS8bRmmAndep":134939445335,"ja78kG76iRl3":336381698855,"xSqomxC5uyZj":302033358163,"RbAuStmtFD11":430223869487,"h74en4BUFqhM":729435193881,"affw16rbqI4S":574233893463,"aDpatyt67sLW":261172575110,"jTaSzumCgBZz":197195556093,"kNUmKFnThLIM":231650770061,"Ut67vXV6tsEm":407552324788,"Sc943toVnGto":992042416960,"wKydnOQ9umH8":174099618067,"KHqyVyAitDUb":191431648913,"C9KR2xAFPHJK":488973683837,"dAzlpXKP8por":834076533434,"BVzAoDURZOC5":261299524377,"0XnqkB6BQLhk":497170165307,"sEsggiTr9VQa":142956735405,"FqL75sBw8IEl":794162365281,"6dJSkae6jScb":615783135241,"Y5k5UIkLTkce":39939553286,"phFP3fcghzIs":159437840858,"D8GWbegnuWxc":763430496519,"fgf9uR1DAYTL":385109473238,"p7PMENbRfGBz":293638416781,"P6HMyUdFcQRc":850468315750,"V5anDtHW9T7t":494998788511,"6X7V9szkL3rS":593159574317,"6ew5YGcyrq1D":668760334098,"pAlVDuc8xIML":943833057800,"8V9jxEkTtdxe":356300391419,"8VHpmxrZHcpg":446768667621,"KunSaOPzHo0y":586615641679,"Jerl3ROnjfa2":547264934461,"4zmNlWA5j9Vc":839766917515,"21ypZDmG6qHs":260935438276,"ioFWp6RuQjjj":314397716438,"8upAOdPnSDsr":756251960294,"KVzxHBr3beyN":790075153293,"vjhAZ9tIEaBm":631013445082,"VcTeAE35Cu0Q":313962104342,"t4yZYTsStTOG":502967904971,"geCBCHbQQVcI":182518159100,"q0hR1dibh3Sk":649863440847,"oUv4PnSZbfgx":199896842483,"uVfWTY5V2DfA":321135048342,"auwo6ls0LS0V":262421132590,"P9Stwl01UZEG":143102691482,"VTOqUIVg9xuG":206268997263,"X27WCaXfARVV":783597886382,"SUPifDHKdjrT":608859861290,"sP6Yb4ZnjFDR":112460773154,"CQQQkpgwqbbV":806511533888,"KdawppOBNhvr":827865389140,"W7So7j90Tu7f":753895265932,"ob80NhrvkbPt":986242363509,"T13pjLGusp8i":87675060764,"Az4uRlLlhiz9":547381293536,"DNJRwbYjYY8g":592347407542,"IMndVNcNkm1o":538172921610,"i5jACW1miDHm":628242365651,"xXJvOimjA3Ob":510270957224,"vNPw0ntyr2FT":347563322785,"ejuELsLq0V7o":159934851562,"WVugf9ptBi2R":490218082906,"kQrfFsHSL06f":507760308809,"LMZzNVScs0Jp":69972089687,"EfngOh9958Pu":681189257366,"Sw80HcUWyHDK":911153285958,"Sn6k3MFnvjrR":648947693212,"kphUMRCP8yzy":167798182095,"UBwCZcS3MZhp":500248390282,"j2tXO44okG67":267457339140,"7l4iN5jkykSb":257119049908,"UDu1nQfa5nZa":199735439460,"4vkbfEmEwspk":97214104023,"pYRAlLUg5L42":778894696436,"xzpSkLrA1SBU":25691187203,"nA1MfS71EsM8":87187407122,"isVzgdo9mJmC":367941278052,"pYX9ElH7l4Va":147543596251,"FNKMcgihEpCa":817153164216,"2MxCjCiWusii":823769216382,"KSOieq6f7Hd4":657015291348,"64ksFaWB39jP":953115734341,"AihyhSlKTMNw":19133647359,"NE5O4d5Q24y7":590425441284,"TuZXIXIJmWdV":830757060068,"ZvS6xvVsWp9L":358013953344,"dqmF0wCoYWOO":55010998951,"SnktDqBTZv3g":845777843908,"2jKD3FgtjUGV":588344974515,"fd6jblHTAhJf":581540508098,"TwGzxjFqVYB7":704002113900,"4fnJHsMDx7oU":557977428677,"kCRZwqkWxL9G":990225902658,"y9jN9T3X7WAn":657418093486,"24TzOxZcSz8i":622605393954,"iey7oW9pHPMe":980269390189,"cZJMu5Gt5JXg":466266760614,"eSCqlEVSgrOk":6193235002,"DrNyacL4bwCd":109084159358,"UNm1kt5l2aa8":874536769056,"9ETmstVHfLL3":786617580648,"bFrYPJYWwnbU":985735158012,"pA5bjMoJxMCz":267013555248,"FLHzC0XgheTA":215118176359,"0hTiDfC8r7kp":125176224581,"5A547XFu9rK3":512174613079,"UaKof7SKTKR5":371162412641,"N9bPK326qiJq":577312419057,"RMLdGWluKctp":314745525593,"EU7ydPG3lhV9":25347404935,"Gd6wxYOSHob0":104735316601,"NyLE7cxIaG6s":442191831155,"m38ABxsXmBT4":984668650381,"PpLJ0mY3uGxV":334381962862,"QneK5yaHlvnM":333583206391,"xuRkjKDmOQFJ":850429467752,"AGtesTX2xaIC":707842735986,"jWREOtidfroL":618970225049,"aZDp5PWVCYLZ":533410572389,"cMXxcjUUMmQa":795493480193,"WgIXnFr0s3Uh":643654737274,"61xQOMNTrbdu":102119592881,"S1254MMgtEly":302724337518,"kvDoJnhhwxH0":676299350728,"Md4ZHpa8TukH":495003731793,"6zycSMgGdQ0m":492987422863,"5KE5PIGHcBMd":244151953722,"AdMZwRuswb5H":630339532886,"hroHfNflBq3n":204531264711,"nplZ6DF2PDPF":847911850068,"v6inNT5qHsMV":802776863042,"XiNslQnfMmHa":373866395017,"vH9ih3qeucsd":326519092260,"Ar4G3LuadUff":610197257539,"jHyJc8OdSFlG":359633184310,"6Q8tCD8VtvCN":97706112811,"ewrlongSoozl":32014692141,"N66TiKx3OXmS":997188227292,"aVh5kOSVDbDm":968174133281,"Nqq5imta3zdv":681429947442,"nliT2ABSaDzV":185947300792,"vom8UhKottvu":758853437690,"bhOZak5y4ID2":190052883308,"zp4aotR482kC":684275070792,"EH5UZN4deVrL":943015880982,"OCR8QK9MDg2s":274395608116,"dXOlDjWt0a1V":184203979755,"qLgK6ictIQc3":678592579232,"kpQnSr0xkyoc":521856005410,"2VKOmiONlmLq":123336092132,"cPtP2ml91rUK":981802316295,"Qu1BtYfA93IR":359510326166,"odPdPSeoQrVN":860600508754,"rEGq8Uw3mwfD":933829402203,"rAYwb16Rl5cU":826789668868,"qvM0gBb8zQ59":834485297638,"UGJXk8js0J85":443574536202,"V44WJsEaMmiw":252093019832,"sZC5bRWmIfiV":831710992711,"MTcdTbuLUUVr":980914311537,"xomCq3ZwCRMh":332336773340,"NWFpH9vqX4gq":631982354377,"1QTep7EfEUQf":44088223159,"yt93kYTguYv2":469584637496,"Ta8KeNoqUjys":939000949744,"y5oNt9h8WHlf":87904158166,"6inHvohOXfPt":528479286868,"3fQAc9q5Zt8L":102755288937,"L1ku0YTbOIvF":182880774410,"tUTGA4hPrq3F":157578535162,"IoF0O0Y6mS0f":905153200570,"dlqYO2NVDL7F":296572955138,"kgk07YaDPtfh":726907188791,"CcgG0F6izGBW":273183965082,"h0LlHCgGrJBX":728124611634,"CZo7vBK34mPz":450159105772,"UuDVssoLC7Bz":734580684899,"hLa3nIJhSDfi":465746546593,"56npXwT28Gif":75325521073,"IDCweIJQQynF":645063836504,"MTm7EtXNti9w":421620909762,"fySfjueT215j":667453414926,"f7DaMYRKpI2p":984601757723,"J8updGNSXJY3":453479250778,"Ya3X4apA7sdo":126072698393,"Fy8hePTounDb":590871886180,"BBwGTFcnzcyj":381192183219,"yTlx8XReEGhy":50317269240,"VdaTKEEXACsJ":431714627093,"H5tCZ7SgNOqy":280724612328,"MO926LvmlZR5":177461042490,"ViKcWPqigCBo":470397173036,"22EuDGcnVopK":638418177071,"UgJAAM2WSafO":588495981954,"ADw0dh8PLx3t":669143966571,"geZihi0m4MRm":93134366724,"H5x39TMOrgaA":976522202252,"k4fLkZieXgwE":552712566133,"shgc21jr4Dbn":682382836648,"yvcC0yFThDgF":718638204252,"zocUxpS2QkNP":809809023593,"sxzcFPqoRiKK":87891672896,"X9gSoMI8ebML":455327005205,"vYecadVr27ET":399060708118,"KGPtiJwaukMn":136089102683,"LtJIuRzN08ZZ":741489169552,"oa4hIcm3J1T2":292911088454,"mRsiPR6q2KgY":803042858695,"JUVhwl5TsvxS":536427266591,"nQPDM0OQfQOr":443600026542,"OPlOVTiBRLz5":651442958264,"udmWSHUvRHHw":234800482397,"2huezdnwJGPp":545289938083,"puBVFW2eYGFS":256094650857,"Z1ZwmQXFFvE9":326371881130,"jE9k74kGtD41":480916585443,"0R84HCD9zm8l":12529481955,"zmlNl9Qbkpxg":279870176308,"lyd39RKkPXvw":852539546309,"gDE3cmkubCGE":840542268381,"97RJBEItDEJ6":131184119769,"r1QRqbheoTZm":731197657176,"rIND47tlrP8G":153369501418,"UDOp4DrYebtf":897294253607,"JAfHOGjsAfGu":242653622773,"NUr4uXDQkF4A":278281135889,"Ef0ng9u9O8Aj":707324406286,"AZVVmAmMTBis":916426774988,"Qp0HQG6F73Te":801973160228,"vvvvFl2E3ouR":875601449282,"fznC5fruMeYJ":547949758210,"9DGkfOXJfFJa":221816537964,"dZeGIdKZVa9N":194090469742,"BAUI7JlJBQjP":997112065201,"6Qi08BljYuaZ":329749087522,"c7JH9iuKhLWT":521590797308,"zjRqtIWEiL7H":198955899012,"5FGTbRVId306":293838973158,"tL4SqpIw3PIO":307990778543,"JzCiSgOiBkZd":379181358053,"EHQKtvIpazmt":885053563455,"3mclV0ODDsxe":930279834682,"ff9P4WyafmMu":581494294388,"TG5EV1d7Nj3A":258928554315,"T1V3UIIzUDsK":286150699184,"kZN2IwmpEG48":35573193970,"fibx8THcMK38":711909984538,"UqXGpod4CkdF":964534815440,"ZJkGcRdP5hIU":91987175420,"TXlzuYRvEyg2":923769210094,"50wmQ8K4XkMH":367486007616,"5c2e3XceXji6":598949265172,"uI4mdIOlsVr7":618200478682,"dDF0k5iCT77F":389657788857,"AaKtdvFukSLz":460552360511,"utasYjO8njV4":774425837904,"Cq9Y0loIx7sL":247338460412,"VOi5TAHxXS2l":732201161575,"YLVLVN5g4Frx":703522809080,"oTsn8xooGN32":46917567468,"ndMTvU4erVjy":687081461782,"yqZR5D1xhGSO":6580970370,"79i3r2RgIPoe":751311237359,"K7u5ISFZfJjh":476831111749,"a3KrTL86FLXY":118850750653,"qOTKPbhcCmms":728222196868,"PBe1p0tR9q8N":399836920151,"ogMMYoIjuQrs":66368251791,"v5aCVKYVX37u":847073177091,"gj2rSI1763Db":331349693024,"kuLT10EQvL6D":630927134807,"rnJv156Y62Wo":635811550644,"PEHrVGptGOWn":605776568675,"PLv8Sr0CfOJa":215031822882,"cBFSGLUjSZdS":557567541252,"b3gw2UDrSMxO":961128497373,"p6V5RxhsHnIO":691155901410,"tMb6H5CJrKrw":854864597487,"ItX3MM1bqr61":599870990170,"58OiYVw6jl3n":65594579497,"EM0lMmgweAMe":413125175286,"XpfiIc3AEVlY":465073446816,"NEXaTzD70TPG":988598789490,"xI2gj5CXZCxY":673242074469,"w5PdLJPfxNES":363341486834,"ggHAvrZqqz1d":610270905115,"9WkowggFgpGT":933019848389,"tnL1huGL1Dbm":58846376728,"cYyoN6arhow0":509359565128,"VYhNBuaWAMoN":526122052659,"jHWTJ6n2rcT4":277029737359,"eUYfATl4g5pV":478689440128,"WIREXUSodlXL":396078216257,"k0P5RUfueDWl":76979703556,"kmM29dZBthen":372930230363,"NrooIOskI6Y5":345358789599,"sT6T0hx7PIJX":561294564497,"q1bx8C2MJeFW":507461891621,"Jb9cnqyHfDeB":193842759254,"UXhvLSgZcLAq":333176227157,"4UToLANaGRhI":654681893530,"A9BZKhgFE4p8":371200534035,"Rrx8zUucRd68":61009449167,"moiMSXl3u1A8":922703729348,"LCfAcOQcoOO6":978822443516,"7J4ZBy49u9tT":759365339069,"QMoX4SBCnfW9":682380674309,"CSbDVfjvUJrE":690625146478,"l6u5gHF3t8vg":528017553282,"DH6efZg7Lkm3":119507555838,"1rUtVUSGdcgP":601597274252,"Mh6hxS8o8r8U":381530103670,"u00DUEvn9f8Z":802201853985,"TmwQQ8DFTQXQ":455817081540,"ZMjMt3KQIpZ4":902428450778,"KKRIoO7tdlXL":288352080877,"jgAqdUDFovZ9":315611468307,"qKogEEbI3QkJ":805801687901,"uVlKwV4SHovf":564260775320,"9SWl2E7fLiY7":7544782802,"arcozLDsnfSj":256432675107,"QBdoVQj3lNb9":839423542786,"O5UlQbWwsHk1":136431413534,"EuomClwovn2p":160197428820,"EtzcIVScw5di":373792237265,"zJ5hG7RMP5Qh":899491659491,"oWy9UK5twdH8":7666435208,"3br6J2Y6LEZJ":758300880958,"4n0h55y7SL1x":257019663532,"zs4iKBonTDhA":83032501082,"XDUKQW10n7wQ":587742454048,"OIeDQrIK5lWE":887455655612,"Ek6o3hpddfTG":402872561745,"ZKB83z4VOEXj":538619089646,"lB0VkHd2Ss12":670705033614,"4Qi1smCySXF5":426369680597,"ggQSr0xjFk6c":493337172926,"wS97WmasJKrd":260475142607,"cCgOZs4ZHBpJ":199119773737,"xLP8dXY1742K":268604915661,"4Vzgka7F3qg0":575362012813,"ZOy5Xad34BrB":209519706666,"8RnxoNmDpi07":522937176909,"PLe1RE34l7HW":870173523455,"WcSwuux9wPox":748974811569,"pwu0qROlT3N3":301041546692,"sF1YHIYzCKwx":667747015004,"krLNBbcPt1nP":466978417759,"vZcbQhZz2f1X":690402331615,"ZutPN5zPFB5S":996656483217,"UanIjmKVmqO6":961481800413,"WyoWfdjV8QqX":98940844241,"9P40Ttt75e5c":774846107130,"W5OwdhL7uGP8":938572639387,"8D4rMqnDYfwp":689506868,"hGoBaiC4Apom":500625857905,"l0DQUdJsAl29":658231193551,"xPiHQhCj7cgq":873344043381,"pJkw9GizYeXh":144102477726,"dAVR35LemPuV":442667548501,"n5ESK1u4wEDB":775229423129,"ozUadlpsHea5":49788944186,"Y3lG3uNJYbvx":352467437774,"5ff91jGUsgxF":291791080723,"OeH2fNk7DokB":564608984179,"jMIf5kDGDETH":996316526403,"w0jfBeWmUqIi":912862847306,"q3igwn2x8nc1":545979579625,"Lo7xZT8uHe6I":377224896544,"EARJOWHlGxN3":815106444472,"pxSYufmSGJXi":427726475435,"CJVaz1cmnKQl":666405335040,"zt8NZ7C5pY63":980130016568,"mDveec6SEeVm":217220890320,"AtScgT9tYPfy":151244597807,"3baqKkEnnbOU":353091204211,"CL1DMoDbjyel":355683819843,"WtgyG38rp4w6":412451323073,"0yC1Fsypj2vo":813361182433,"vO9JboebzCF3":21134069378,"hrcfRDeHOE0s":147887607325,"HBHFsi310BIr":804689234430,"cfoHJnqBNvvY":418447750221,"rMgEg5jCKnaA":218579210597,"U0PpkiW7Ciwz":83042357629,"E2nTiJJzEyKr":337063900794,"wPrLIWcZ3JDI":933264552423,"RVUEI8gnHbjn":236415259455,"NlWgqxoO82dE":6971013912,"WBGUiufsgxkC":666591542956,"STBi80Wde5Oo":759216535612,"JSOxdkQvZ65j":368959317779,"4k7JHWOiZ9lh":556008351923,"Z6hMjjN66jEZ":353375630771,"VDEWrZqscxro":74223136607,"SNpuEONJEcw5":341360525903,"VgcvAf8GzneK":420802609840,"ITCHdR9wW3ut":52503381253,"i2Wcq6vaDqoU":612470866234,"HHigGnc4nrly":119142385044,"T1jSVHW90049":144421379067,"FJSWIvHMKsg4":313883505984,"DDSOlfTwLopP":945045536592,"Z4YfKiqfQbIf":966499472009,"23hhkE9vSTE4":531479260329,"ed9psG7ufoXc":36434110702,"VbgfnLchGik9":177234938975,"blVxb3Yn4PW9":763443686266,"nscshKajRMQK":860095294988,"cEvVdQQ8ECos":624459202927,"JkDjER0Q0Oes":53227653096,"x4SHSZfK1yvu":918508274997,"tAuATEgnBCWf":198279251288,"sKtLMuoJ4hFv":528670491111,"nhFl9xRsFURs":896647213212,"3OxmX5dmxQPS":40663130360,"ntIQEs4YtPa6":866512777356,"etD88foGeg60":767666011105,"zsoy1mxBsUo9":723708328110,"pAE5FbR9rArR":853987300380,"EeYN8GDFPd1m":641591802200,"0aVPwwcR3RVO":867157961157,"D9oAdOLI4tBK":295041567070,"kNXzjteBy38z":434809525930,"u7aHY52l6lwA":94013417777,"In1FgUI0miqb":100537578816,"bgyIqa4UwNQO":291270093794,"ymovKXXc2w9X":908168845404,"9OeoY1cxEB1O":406226048981,"9N0FDJ1bNLjR":22826838638,"HcH85jEwuWfT":690002079301,"aKII4anhTdds":563845021493,"8x2pJAuI4741":542258991507,"nr7V8RtWqlt8":55364474645,"nCCVHesDyc12":427373341608,"meUF4C56Udx0":451460203177,"5LjHQ1WQ1UpF":918857110552,"MJuprDf8qoBD":591849883937,"t1i1LjCPcBIf":513188928502,"EHYGfYWUw21C":269971962130,"qyUuM4X4Hj1K":155771975603,"reidwp72x34y":719761670710,"BBPWHxbUdQ49":355012922073,"oVZKnoLPASho":397079267427,"ZCn0TQgZthf9":110860736301,"AuKZTeqN1354":210700886685,"STKyMqaQ0IPQ":673838495998,"THFLIfxGZA1u":98202416146,"Tu7KHqA6oSUm":655481050778,"MnHUQX24yQWv":706563900074,"DU18ksWj2F88":838148436822,"yu4kxLLJjtNZ":474827397779,"ktpVphOsIWn3":999961325746,"6kdieHKjoOnd":974529125013,"eZohVGfqDr8V":637693383891,"iNqB0lTAA16a":657466807789,"FKH1VWJNnjQu":973659583118,"itzxVQjEi6RA":287521617731,"NCwpzP0qLPiA":120640179127,"LtBOhQUIgX1u":926359880443,"TYAQ3jqlcn4R":309699858864,"AiUZjDFKylUG":833742049200,"ufZrUVPBoRdh":42717811129,"cQOebKoENysk":181875927020,"SHCnoz4Lp0fZ":641332294731,"9Mr6bhBBkuks":474294042449,"Unbkq6MX1L2e":485679732707,"huoxCS6mJ8JG":690232362214,"XvmT59BY8PRU":886026590836,"8t721wXThVsw":811194474247,"BV2zUXIjlH25":483811848973,"uyNgXQ4MCRkt":255358216528,"Cecm0Z149pnh":757602597794,"dEWtCijqqs4T":367109780320,"8847APgn1BlD":519987972291,"6AHQVOQvDb7B":466204286006,"gfyi6bCz1vzf":683222574596,"7wCmVgQO7AFy":792472570588,"9mawVgJN1XK4":365240105062,"vH5Wfpj9VgDd":905428588469,"AEQt93wHx2hk":678170619882,"HC6Z7W6xzQ6z":866731717240,"Patjkk79HCiA":196983422309,"OCC3SFTaEOxe":327097124116,"fsx2MWBfgZ4d":947861774463,"RDjNJScdzPua":543024062038,"pYanu8t6nnk2":362453793773,"cEnUnPRpcl6y":198483644765,"Xcz9AROXnNQX":28228721090,"ctryp71vf1H8":686490511362,"5iHp1Hg8hfLJ":357135387061,"tTims5jiW6CT":209154828367,"h26Z6qQFE8Fq":144976152930,"6K72mB5cvaNK":212593005118,"EVYB7zMomptV":158662725110,"Axl4QUnAzc41":629101012460,"jYLt59KRIlTk":169711718457,"dB6qK6wYKALz":39444789046,"Vn5uagLpoBjT":174031426299,"0GbOml5sDVWT":35815724043,"TqukOVbK3Jhm":815856109477,"IiSa1yJd0RBY":912836078912,"p90mOd5njfF6":582025780982,"UoIesdmSQYlk":936210851232,"nYSCDZWZve7g":894663228812,"bvzXiNGZwiDc":583669198473,"zBsV5DkxRvMe":590969490001,"M0YhENszIMVY":598718975725,"quBlo0L3EIyv":931506458397,"2BevAcVRZX7r":185644182276,"2ZYDIh7A1Jb0":967456408143,"vyqnCFd6B90s":186440928290,"TobVf1U60iio":470328140351,"IWypBob5vaRZ":622078663788,"MBTeQo4reO73":185780184940,"81V3KGyObCz4":60097151651,"E7juc6zsVgk8":141952662666,"JY7zHkLopPMl":22122575137,"mZXJ1TAj7h8p":87325685491,"WdWaugexyrz3":20050180448,"R6972w9wNHyE":594012572658,"NCM0xSQ3pOpE":561043102392,"tYBeasERGEiN":832999929214,"76QjtByqFP3P":971239299976,"3cys9WoMNV55":107095435941,"PkGNr2sdgL2O":809725521774,"QWdg4MDBPYM7":170160777645,"XBkMwd02yEeg":575080315278,"ssn1pvDEIPim":462585689367,"vYIk4szCaQKq":587527515798,"wMrMkv74w2ku":194901395731,"RAUtwhpIhc3d":848756785305,"2pmccx4GK7Vi":355753835638,"AMAB1iP3CK7L":25380132466,"Rl0zwECt1dUH":55593350945,"yPTQyFO0T6CB":327344409307,"lTKH23KwxgDu":652131274149,"Cd2VGGhzqqum":818151841711,"JanNxPMdobEf":174973701901,"MdOZabcpAQQf":139502201463,"tq9W3cmwE8t6":11985766089,"2cabRJrel6vR":494774682278,"TGA2NCm7NBDB":381459367158,"8DeMh6AS6BEP":617452088856,"wLrzxz15R3u5":697962776486,"Ga3jR2HNha85":540752029129,"6h9nF90FmAS7":234229599614,"gDjr6OJCqJVJ":684858927221,"lbEbMwGlsA26":255446705620,"1M37bo2IXAzK":263405569742,"wB7FpGeGiTJm":300894578288,"muBJryqMnXD9":132632127995,"mows8H4lU2oV":825327481123,"LJo0KxkvqqlN":633362926864,"dU1ODDoExPvK":357221100020,"MoQiBvAAKt5R":510531851582,"iwsAxQCqj6jN":498559468545,"vGHXO2BQeocH":652630767466,"quFvbk98Ogba":790334669168,"TpB3UTHPhDL7":438590230582,"b2DIVDyT0n01":605211696304,"ly9YmKNGUp1K":324017666954,"MUJooW2ltKft":435649929172,"E0TQ0HYzNMfv":179831854587,"Ea1ab1GraCQ7":936243360078,"tQqMdbSQLGUX":897201453310,"yDjiV7obtdp8":800211335399,"2aa6eBMnYB9P":602060227168,"phhj0ouV4dST":113474264485,"hFqSWSBdGMFt":96581398576,"Gvj94WTECZG7":399819295738,"SClmRjQZ3faq":19401351038,"enIcyAP1Bdvn":508532342965,"K2SjoucJNyWv":272846534348,"Li75PB1OoLyw":543701688775,"pxdKdUHR9vFG":688580908668,"spcexwisO1k1":9815618830,"ZMUsFlQIxFFE":686475260034,"GsfWltTsNJHi":207186889972,"9FtN8fLmKqUW":711360521717,"xeTlwMAfPgCm":17585794732,"QOS9TwrfcNoC":820170868946,"IfkElVTLL0mo":87917234019,"vWtnfPXdGMYm":173606096566,"qyl5iH1rYtRF":375866225109,"eObLIqUWdr2T":502559880574,"YQIHtNiZAiPn":742383636924,"MxROH6YUhXtP":90449319705,"YJQ7WZ8y22jk":916606972325,"N2J7qxVhomBj":686617406394,"sZXtzrESmqAC":847763899412,"rPCHKUf87orL":245995724735,"620n1vnoRf53":110824858,"SDYCm8Ak61me":551201626638,"y5JbYndFLvbP":247459471019,"oD1RgX02Znor":739294140975,"Psd8wi9F8PbZ":197482785577,"imzsU0ck7Gly":883250425155,"RnzAHSEG5dQ7":268247303930,"Eh7gnKBSkow5":561005874337,"C4Q50KVruWWV":803957951837,"PgP76jN5xQV8":753186269508,"pG2rkzSjVPM6":794801686083,"BFFo8Y6R4pum":163160819006,"TdtNGqgNaVaN":432381484884,"1uotK0IpObPU":731327633462,"jiETw1biKQxR":44477285133,"k0i3IaqEvxGm":538422900138,"Dv7QjSoDMfDi":92506005492,"DLjeNczD1uQT":549451487705,"wEKiRrocVr5N":546014365859,"rL3HVTYC6p5T":968395949364,"cbNmBJUxVl22":820263966152,"AuuDHbPV3bob":56900457180,"3cTgK2ONpmHt":466431298966,"aNXkVPqadDQj":194181881999,"wa3qahLyKUbb":742644143804,"RA5YZG4vBKzg":525993756475,"PzTnm4NIAP20":682524326777,"m4lQEcOJ3TgK":244171126088,"Cq6X4ApGmNaV":13067970701,"W1tcYizVjPmZ":587655276333,"pAR1XvwHFL4A":296324992045,"SbQPAK4HVDD9":52945660788,"jh4gkZu1hM4f":82310770047,"607d9jrWFU4L":700146830407,"s66ZHsLIpoII":289152504723,"F84LB7QubhbQ":503721948929,"W0OhZ7aSVDxv":803755413768,"yDN5Y4JJQ5bc":914899576236,"BENLk55yCSO3":130656877986,"UJT4Vu3OMIUt":945128108412,"QJvO4eeBz187":780465633928,"oQsKt6sP6OCf":253752970877,"KBblwtj22EyN":764028921729,"CwpvrWaADTer":151501943633,"NR0AEFaCWcOb":385829322301,"l1zbtodpgu6d":475327499494,"f93xR5ubmJfJ":102308972456,"not8Ey64x3qe":386858890442,"75tDsactKotE":449194257898,"WlYrJimebnti":197861840787,"j1Sq36dIvpsh":368005619851,"8MsKz1Dwmxbo":830172171638,"C8Hlc8YgA6au":238965125872,"RJVynw0HGjkV":705159480449,"rsiVZtZ6jhmy":810609574789,"cmTSPpBLg991":447846128872,"zCqY7Ac0Wc6X":63722809013,"swRHaGgmCFJI":184570517742,"T7dh3Ywrf0lR":510575078693,"dpm0u8hMwqMB":656287448661,"ImLAX4GZ0g9e":977005123827,"mOqy69DKmyQa":399552938601,"opC4mcEQAo3O":52133751311,"PqiYvVMePZtO":223990022952,"qnUaHz7RUFqv":919244066541,"uLIMUBohJxpb":328269476644,"xYfs5Xz3cvx5":308716374609,"5VAFhf2Wb2mk":638000159523,"QE5TKFbRGR4T":667831289684,"Jr4YIThh9GhG":442601604189,"FI7nB7gAJigt":88452265208,"CcD75k2Uwfeq":46164881173,"E6VOPflpl9fq":533705410500,"MZPRCw3QGke5":785031309674,"QouqqNzytAFd":787214419721,"Lmq8sbWpKddh":302157317166,"9b2PH9dmWAL6":473329012100,"hbhW4FpMJSUi":395710278260,"l9OxkpBBLZLB":222787333644,"7LqpM3ikRhSR":311377059336,"bbij3P3iDO2x":294708526289,"7LcKOor1lddz":629728787555,"mPOplIGP2mm9":573630218434,"SW7NYwcqNBeA":63010156214,"DehqupUWy15S":159032734436,"sALLXALF9UCN":822473429428,"OecSX7grZMRw":429052411568,"bbOot9l7LZqO":365458463681,"9B9q8BwYS68W":678962346878,"QoM5rfd7xXJ2":990411463211,"X8RvxAlmN79v":430990761574,"YkjNfTWNiW71":658426339389,"5hs4XJCQt2az":207927388734,"1WD7DGbXfglR":856368834576,"pt2dCkpGXw8Y":414804016140,"YI8BQg5pwXBm":90836887770,"rBm5HDFkvzxX":753100218634,"CUCSc5n9fogM":62577608529,"SFzxw8X8d2R0":777354482720,"qGnPIjn9lSJ3":117872429525,"1lQe96IWyTXS":7420065193,"h5QJtxTwwA3N":85288789955,"VFMMd2IMyXsU":711125188805,"Brx2OF4MLm9W":587522999907,"85tTvScbeu7o":643585253470,"1hRHuRtGES5k":285931671527,"F1Cf3YHZdBg0":392252877142,"3kAuBmbsfQPY":818682501461,"xXaMHHp3xYq8":204289942822,"Ift8OMbZOZM5":640949416055,"1bYQYQSbtTtq":180773138942,"NxpYF0eJirII":615537214732,"vlIR7lruV1r0":335728521288,"YfTUcL3sOArf":952681378374,"IeyLsjtZKz4W":155509410512,"Vd4cviW1btnX":999645496395,"S1H2PYYGAkJu":978569858125,"jeYRPzZ6uQPQ":245621618303,"bSHUWe5iisNt":164089418027,"ysmhQnhoLYTc":811840793123,"GGqaLVfVTCcD":104821410103,"UAf5GlInemdV":263645538727,"yijzpanZhL5X":349585251284,"sp9E6qHThO0o":632680685343,"vL5lPegarpbg":178214287078,"Ityjz7J9kSHS":533645346029,"vCY8TJq7WPiS":364033751926,"9yGKjt7iU4gE":410414022720,"4gYr3ZK9xeju":484592246316,"uGv1g6pmrsJk":567649525845,"tAkVi03KeOLH":249954456618,"MNKF5G7692UP":533752915102,"0EUFqnsohQQx":65262948282,"nN81Uo1H4nBL":932083645593,"FyHBhsPoIYLB":984991926959,"RCE8pzJt1JuO":999456684088,"Uez0BxBeXw43":144727397352,"REX0q9BdxFoe":149760494789,"DRF3NTjq53z4":431168235805,"upQfGQ0SivkY":163895748153,"t8a52egZQmZD":660566977408,"oXq6KVyWtzA8":797155686868,"zxtPjCdG8RZR":864165564043,"9g55ipCfF5B0":862380801627,"wvj6CKn9truy":493592924806,"P3sMHrZEd3KZ":758372481506,"pE94dNYcBNLZ":484724954765,"hVPNpJ8z96Wy":493818102144,"yQC6Gng2Olmk":410035820181,"WdOwbp5OyRfj":766096820829,"Q5j73XwE3pXV":461463020447,"5r9Fzr85rtwG":529467099558,"oc1G7RcHW9Go":249399998409,"TTDNukgZLeIr":335384134192,"zPhMVTz9th1v":265784338240,"5NERBbOL8nbC":226289107064,"3mujNhhfl72G":445135423763,"vjGDPHUpIwWO":820246372166,"wUf1rGLGNiH0":924859912060,"wYFQscU9uZEn":190680072847,"8NZMw8CG9pxx":510455765883,"GDfc3A8m9Joy":661067122488,"jpu717Ii0PX6":930653938979,"zXphebdgMh72":203923441681,"5CUPBP7FVkZd":253415553146,"W5lpPvkznqw1":528229675191,"0wbUksIKE5Cm":756484895384,"MzU48W1OWBge":626461899397,"7wrkuJSsYGvG":215844336494,"0oM3ZlHuH1Me":384890169081,"xmS2aWAhOapA":599381616000,"0r1BHsMFaJEa":640362511442,"FCiJswWZCo4X":887285116412,"GKBEKjMeHg5o":126910026308,"xUHdhwmGptPp":855200255070,"VuVejidCwgHV":125155294244,"rCfpka4DMpGo":984366640655,"FnRnKDUz5ixP":506541602849,"XctwRyxRBWLi":593838922964,"Rb7SIvsesDNl":858728205514,"uL6Dp8vuhz2d":40543199277,"Qufo0gvSiiLc":472205085537,"dxcOKkDSTAdv":83138746339,"gHEYRk2zzLrU":3521778622,"fk4MZdUwxrUH":439923000651,"LbuQ99tu05rY":459329474772,"e4pCdnxCx9bj":584745278656,"ML2EgU2OaRV0":300737907249,"JO8hJDesvI1T":673677849248,"ejXpD9tFFxEM":673786314863,"WLdJVQMyAGQO":99408452254,"60CjPlG80UwT":59768978870,"wCWYEroudbRe":310169702100,"4dorZEZM8k6E":532996905356,"aNKDKG59ekHt":963656336632,"Gi8AK0HAa4q7":945706810246,"8itdHM0S2Bum":712926740445,"az0xMxNrWggC":504598669085,"dY40HuxHmhzG":384625653755,"VhykCHmywARo":712962132136,"gwOQcStNed9N":658076832776,"yyhpRlz4c1et":336552118424,"do5ftNqbMnri":863908609420,"dZrgJtnaVlUe":662363577331,"prrwG5fA2bnp":813566335046,"4iTEuuy8WQck":783168056748,"noHnlJZZFRuA":753734663897,"WBzYTiQS6OeH":725005372540,"ITeVdEdU8Zxw":345783480597,"sogNoXL9LfwU":313401583278,"2HaKCHZCpIjY":764574572988,"nDskM2EXEnqF":342333930976,"Wqb3mV35eR5a":179620484934,"ylhYmx5YXb7R":947615680808,"u8m9AmvhXzYi":521874976255,"1tqPdXZC6Pe6":973924701663,"qyGYM4ieJ0i7":577750279913,"t9OE2ARkDHVO":253294451790,"UMKfkCQri5F7":967379625718,"W2w1PDYZOHeU":649276997484,"hu95K9H1SAHE":239888747397,"s1ZgdbxYOCzG":72561244356,"zbUoWsV9q4iW":341467273912,"bBJyOZUpvbbB":782681307619,"w6Qxv1qlnwFJ":728843351141,"jPGtXcXFp1Av":843156195361,"l1skGi9vBPdL":146567897418,"oZWmN0wRPmVp":897310898517,"9oyklao4EMIE":767382718088,"7iYGtYPKFpWS":755501068020,"6vzpjGLEc1dz":641319261184,"J7goVDOmpBb5":863854357635,"FC2Qvj7ajxvK":933936671809,"QAOhoyam5SZ3":102077978856,"LRodKNdG8x9C":985618681533,"3yGh9pOa8Mhn":687159240338,"fMD7qcDzYh6X":471869404178,"MeCvm0JGJuBH":441831506288,"rlW7MfY2akjI":578477604340,"FqrWx1ui8rYA":922753523561,"r8Jy1Rb5CuCm":346866104301,"XaRR05KVh0mY":104165237403,"Unl8mOCnlCsZ":64232909150,"cBp9DcsNDFw2":306013014019,"0FxWTK8qtK2E":232231304642,"jh2Y9FYFyZ4I":590652475264,"iK0KeJnAU7WR":918125052800,"sc0lRObH4hO6":565115704851,"MIedGNq1YPvu":954629684343,"HE7rW6wCMzDe":48562117041,"HVqFVaAWwB2b":397854684167,"p6Y7rXgTjRnm":82299098953,"JX6jIHtGM7LF":730873609454,"uBiWVK1XuVAr":727468004483,"qocP6NJqPHpx":335277344626,"xenzZwYNFY1j":91298441318,"N7vdQBN8EPLj":186898729871,"dEUPHt82mqCP":491483912596,"OWbCq4aFjKYg":830723609704,"8Ts4Znj0O8TY":335032431296,"ku5tswOxK5RP":102297292764,"whYdhzWG48GW":868680720924,"V3LS4wFx8k5G":160981339988,"0akRHcI6phnu":110318239322,"mii4FPkTasgh":407373369155,"qu1F5DatWggj":733981342397,"Y3LZ66dLTvH4":758234815732,"ld1aQk5lwKoq":754585257500,"t622LdfJp2Ug":677947786234,"MbgKZZuaIO3M":454420424850,"tJsyrUuHHv0q":840357351444,"AtV7aRiQEwlk":694873724983,"DYQam3FJkqQV":974885190522,"o2ktkdeCHn7N":66511327872,"LDhPMxjeWgCh":267169634679,"lVXDQdh851C8":34206846623,"ep2GmbNKaXw9":207065895734,"VrVisa22ygdB":915090596776,"gGpA5rrXpz49":34008407373,"9R8oNV6XPU4z":749605822377,"FVlhtXC1J7ag":419598833761,"p5QMRFRyws3Q":605855772859,"t3fa6lrXYoo2":914503600088,"Yxz4fA0XQfkY":719892295041,"nx85NhAWHjIC":678376375322,"HNsDDOdiSJ2F":554660535275,"zWNtVNUbrj2Z":888827436980,"fm6eIJz50Vna":294620794720,"BScvfZYkNSFS":231812956082,"FnckcB2zRSNL":243420395806,"SYWQTAmzwzys":512636393101,"X2ptAsJb8Du6":187499467019,"Gn45an20AvPT":462398140066,"AI8joPQ0VdOM":70054257353,"KUlTCfnsO5eJ":231694087670,"V6fdrdg2QSpF":552182257582,"TmVnLF315Uty":596571796919,"Y3Dtm4xQfkJK":510740126562,"UnzqdL0zDoMi":120764449217,"JCkaPaBpTkR1":390649258150,"KxIAKBnwkEl1":864994133034,"uQhm5vwDQePO":993530325722,"prdbVwt62ztu":278986210056,"tTLWjihuh50C":595866960280,"rFn6xf68pgTR":722289393251,"5hfbHd8zy4ch":564331364937,"68J08ol8boXV":258906161007,"cuu6IGD0O5IT":427862048697,"1Sd3SrUjgeAH":383089482426,"TPfqnsahXFYY":117970642193,"qAa1di5d0PFH":55335232322,"gAsjT6OCGTqj":15972022477,"FgZoW7Dxk3Z5":128300472264,"z8h1y0HO7fMM":475476603241,"sQylsBql75B2":547664922043,"fVkWVQ1DlDOy":282910007467,"9JRdVHw6RqVj":646532387802,"Vx7LU3JQE5op":958634922764,"eQ7L2K9jggwc":647480661464,"xmqOQdiORkPq":841541261540,"3qxu5IvTJ0o3":772799019369,"uB5iijZpc6RM":404893470228,"3kh0guB4jK9h":873864236683,"70iwk0yNrzmr":763822914701,"zcyEWd2mfrr9":303635277533,"8eSdnTjXCn0A":29668657613,"jOSpiCaE4V58":640063979252,"1PKUy8bgUGmI":718614005152,"HMaVlCh1iHew":876280297860,"ntzdZrH3zBAc":851150509104,"GKEgCtUbsde6":818901584999,"nrEab36mFc8F":954465626461,"yPnLzz6Kcq50":831355162599,"cLKloUHIy0k2":724718668641,"fQCK2Y3Ad61v":533291042471,"wWToXzSSxv1W":548424715337,"7WtmM1b1x1PE":805814519762,"emq7rPXRG9Op":619392450791,"ESWyFnpc2r4v":602222411210,"035eI8XqiyPo":75571077341,"ZVNdDC0ef0bW":240387779185,"isb3rxJdz7KR":924775531413,"YKpck3xJ1ohJ":904227905036,"GALIEmfCKdHl":36677168143,"YMlqTa7ZAGDi":566830012973,"1pAwdh1P68KF":347169718940,"QN75iMD5mp0T":988535690466,"f5XIzDmyOpMY":256863470424,"r52bTmPfS3gT":766117949202,"9juj61ROygNN":732417641464,"bA15he48oMVb":930870667857,"vcnF8JoBh0fU":475851309486,"LWGTLwOMX3Ui":406013803433,"GmyW04XqQH14":763823615711,"ZNt7PccXqVfW":645370319478,"WVlQqTaCU58f":183395348098,"U0yqFp7hrZTF":2216083709,"GWDJxypQbhTe":162077346309,"EyxVufXArc1p":590505601061,"eBCfpYW5QOT3":511197645204,"FcOATONl1V0v":626356285106,"ALIjPhd5BOtH":915135271977,"VaK1jP2jyXsy":267313783293,"woEZtFDFPgQR":340996611741,"lcpQzFdfYtnN":515501348824,"DQMSV7om6V7C":672010763669,"xhzKpXAuxNj6":729746996741,"nfC19vF7Qt50":763812045643,"wxxRW3eKVsOR":723438953805,"BkMsxq6IQjyW":750581923069,"fmP13F7Sxnv0":472598772650,"0E87NIq22CaP":444321857154,"UMSi83BsZJXY":826809487645,"vs0hNU8RKiXw":166815734248,"Z9ucQx990YG8":540109057144,"iITBj1t5kMNc":426639207958,"1MuqOWTAXdX7":981819501461,"pQWcKqhDQ9ya":439404649544,"mSnUYo6AgfM9":751483754838,"mL2kzUkiNsYt":878881870007,"YTetlQszpLSA":669604047746,"nKBZFfK7KsFC":928199389629,"gPFw00ow9iMB":115782095539,"zhCjh21plM0g":384787322231,"Zx1HqlSUGS1N":644144545599,"OjMqcynMRfbc":368963372798,"efCqKIqhaE6G":238699887038,"aPByuuflzZVX":176384640022,"i0GZm5edh6wL":223636572601,"qQh2eq5N3H7y":668478497853,"doWwDE2wyrWK":98653813705,"TgPfe4aG9PKB":515465051712,"EiSBJ07n1HVl":489204294028,"4VbzENAzRP4Z":729885466141,"2VlGvf734dVn":42782163572,"920h1DTwK1W3":495998606612,"dsK9NeEqbZcQ":379894543045,"xZeM9vwudbkH":480989265050,"OHrGQjuMbAFp":678216875582,"WNgVE7uDBR4X":907860791893,"sZtLhdE3Lk2P":968290929616,"dAsIBciYaAHA":839374322762,"KJjpOAoUFH4L":628476156392,"wYp0R4qAxHfu":781473075227,"CIOsjOqgB6Lg":333695152050,"2Nk1XLzcv4km":867893250686,"ctz4FtABaKn7":461514323456,"uriNOK2v94Sd":320570069087,"xfXt06oOX0Ek":567221257702,"VE0USmx0vquB":310253030451,"KtCKrhYBJplN":849959704095,"bAGtPd7FT1sk":159002278267,"wHuFMIok4feQ":525898989026,"X2tArIgkqQZm":417112861558,"BqwHJBlLY6xf":610652944629,"RHadyJBm6jUh":450067694897,"7XRAfiLOQmKQ":912698047295,"SwArf9nXxpHc":975302032254,"1ohe1XF4yZLO":590659598540,"gwDSTpLK8uss":800360628969,"mre1B9O1Kp9H":868266522844,"kz1Eoooy8blu":700363037810,"9mWHrLQaxjOQ":785086036170,"zX65TvU6VB6B":828375987648,"GCtroT5caD1n":765052176957,"2phFiSZ1NU1f":584447606961,"7GqbwryFIe2K":348818345207,"kSwtDLw5FJnW":761803616313,"bEUQNAlTu4G1":144952271664,"rkq1arZw5cWH":214775069181,"r90li6XQps7P":798092094687,"1kQ81kJkLyj8":113389354128,"BEEHNgrN47Rk":902426756048,"aBWVu1ixLefk":347263877725,"v6ZGpwYdopUV":79414239281,"qDK5hSaoj43H":226933749949,"g0L2lnNFb0JU":613044371482,"sa2Z8noo9V2o":804102312336,"sh8Gc58cv2xK":461823843753,"V3vlgtTpuzYF":624945567146,"eCvLYa4oHTS2":314992780612,"AuFOzgheeAIJ":382724149170,"g5CQt35X0QPy":244496690765,"jHwsEZIaTAPH":158953919861,"UnNleUgMpGpv":193062413753,"czOcqAU1C5FE":887354852986,"5oHxD52ZkIML":756462428128,"F7mwraJenxIB":672785805476,"hLLST8X8c9Mt":347046106924,"GSuLXRvf8SY3":464369961472,"byt9qPMwnANH":761711480824,"tWaalnzjYWws":232024429811,"rLYVEPCuRyc8":634217100217,"yH4XNlqVCuS3":882886099752,"H3dytcr9WDN6":663202471321,"XafcgJP7X52k":261325064720,"UVfNgYoolO2o":425699739710,"mtA0bI07qkkT":438788734487,"2cJB7henmg8N":932177729117,"BEbj8JNr6I8O":824568558386,"wopZXxlSCvR2":834425958857,"2wzYvrPBqdbJ":897734797120,"WTPayd4qQBFa":888954204906,"cy1jSolugtA8":644844735456,"dI3YfPuUZaqW":450220460157,"2JEwMLYDZ33g":358706499742,"ZsPJ6tLetKqJ":27292081046,"OxdE8XRwiPGI":303213449145,"lLFby5bf8oyS":434673134672,"qxochffBHMT1":776858057081,"7VT8IbE55shM":991888459593,"S1Ys8ZDKvmD5":633966707806,"4bEKXV0nAC9V":9868059668,"TJ1zHrHswLP2":771894048512,"5vjR7gtZM3bW":675894257632,"2WoQgKghxroB":278265906615,"9LT3b1InKlkt":645927080107,"poH03caXXZor":402886617891,"Ita5HCGPLoxy":445715863246,"x99XCpExmnAT":273496333327,"7eDoZyiwO4O3":672036834677,"7qc5NNiYeYVc":407327216584,"7Q33KJj6pfGV":469805930999,"xiRgJygtypzw":312127409001,"WVWChhnLWCcJ":756129110518,"RzNaxpdycSmZ":645200173655,"C9vIEEZvsJkn":436819814335,"RFx8WzAcpZio":363651284757,"085yG1j51rcp":496363087921,"K4GglPvnNOZf":473641403569,"deynxpB8IbGm":594636630036,"HZO9nJYpWUaE":282718510916,"XuVU2EhyiqR4":141970468440,"xTZBw0zyYBLh":853909549723,"Glvlvzfi6HqW":16070624140,"oymP1no40aEW":443432158378,"VydIhQm0EbAr":933649533097,"qBfvGIM5dlil":673102493008,"cR782sfrZwiJ":417722503394,"ZEJ5qDUTvCcv":149815909952,"xumxpHotdh2u":526315868003,"WR5WYllx6nJc":701704982002,"xv8DOOtvhveV":303114305162,"54RwWcHlcli4":83498622300,"cqeZBGpnJMNi":423148799145,"2l65wqWIZ36S":60582556992,"nkQjgdAFK562":331422569949,"y6qoGrzjXQDL":602389557116,"fce5OMM0DaL3":302233501035,"JeOBKxnMWDVB":141779559056,"h4kZz0LrLu02":566202216005,"m8GC2F2ZKb14":3983149571,"BZpH3nev2Ldu":515453548719,"qXpvv7lpY5Z1":37822578265,"xBwmCdODsZwT":312672319891,"DMJNOyjcAIqj":104918341392,"Ak74egQ0dAlQ":463752863098,"Yh7ks8umTtT0":651576105172,"FU8yNPLoQps8":237683884051,"dxiXdcsVwRxZ":659895280796,"1SdCMoilcPbn":440576699986,"XPC9Oodior6M":498482016812,"lDV9Vw0hCRwB":528233007016,"Mk8uH3WjvxP5":15768016296,"C1sdOaiqFKRk":566684747270,"nPbWZYK1sM7w":174255223138,"mSwwssAXJkTO":175825024222,"gOEeapW85ENr":549659274787,"dJu1xvSM5scY":148577965454,"AnHTLgFITrge":386566600720,"hkePVD9BwZsu":477811972268,"yqS36cO50kdz":11705245278,"HPPcfQ1aDwWE":941522314979,"SGhUimqi01Yr":877830964763,"twsJbjFqEUJD":37259004376,"YD5wqG8QaAbT":464224558770,"VAHUM5RtG6SH":410299605961,"8cwryBmsx500":865434827989,"NAl8Du1vB1Oh":155657664346,"HuzJWtp2CTFj":927917045900,"dXR1pasI2cmH":576512875845,"PiwTueoNlCUZ":294425232483,"UUWptqXqGOB7":39323149859,"qsXgaMi1yeHo":146939659303,"ZIvmT8nguUMn":153595785584,"QCH8ndXrDz0G":798995569113,"lnnjXNwq2HnP":386473564199,"CD2gxtw9Hkcx":84315283822,"u65TXIOiS2po":51682324381,"np8AwBk8hKUL":720017169451,"jWOM8zGMkfmj":441254123821,"rBR6W6RA3Vj6":70399493088,"JSV01TTct7U8":370853740888,"BY7GLgDiZrGV":378728729457,"kMNhIe7hUZHu":55216886297,"w88WZnBcysht":427065510791,"tg1kHu72aas7":276303526307,"777FXLxVohRn":374653732421,"kUIUcQ3Q7t34":521740127362,"WwAjebvCOrvr":581609889312,"gxc2WxFhgcEW":68051994102,"ft9CJHyOB5ea":596312208197,"LwYX0oxhV7rn":375282793414,"l5crsPncHyVt":465575417800,"UBRz54TMjjPf":36623451792,"3jtjNE95MOTp":625322824872,"QgmRHvywh6ei":639252885463,"flGgLK9YDz41":762382234517,"JtYeFlmjNKra":172547355379,"9Hr5JHAu30QN":327901269650,"abnOSaIAr8i0":975587810487,"l69vJI2tKvrr":671854334683,"Mm4V7Z5agmGH":708147178202,"rHPv3qjj62ZF":345680582608,"6cLfAz1Bn8c4":138944333595,"z9Lmdj0hy5or":440722667113,"8yN2n2UkmiMO":293215877062,"L4DCN7SUUI3m":357063519722,"gxeNFlErxjKK":467464924576,"aeATS3bnCUm6":526623358361,"ujVDM5hFg893":841365463575,"MlFScyPYlubI":576899890376,"FdWv7L2V5Noh":310880140001,"E1OfD7Lnb4F6":570323925410,"gOTYEapBJtV3":82621655780,"Fi0COXIUv0i1":751347270023,"GKZV6gmIRAFw":864595728865,"RlfqlAmhqm57":295560079603,"6eflfZzdWDiK":304395390920,"GywkvciHM8eh":439317149068,"aZzBjtCXpcui":205978512554,"9W3B6ydh7BhW":624544572099,"Yn6WmSrXk4LZ":916883283093,"qvkZVhT1e4Ls":706686295020,"dSmgJXp3XXBe":444154257138,"j2jv1wlvV9Cv":43744097411,"q56ubGesCM7W":759382765935,"qZ75UpjcfMEX":981401883946,"zMCrKrEGnoFW":304180317381,"8nTboucIndpY":944087851893,"TZ07DLYNOFCE":211141632425,"UZQTsU6rFFgB":17155392142,"uGdKbuUR0PEh":180236068559,"JH8boyv4mNbm":784144153242,"BBovpByUUrsq":19872752211,"uHgODZ1Wf1cN":378800312743,"6F2mdvW1FEcS":866705830262,"wbMEZNxQ5Klx":659764994616,"jpccqSuZaSwP":384847609886,"wYaIsmizXszo":965558542897,"LjGZDYmU9RWn":148031481951,"SkYzZyf8IQZo":865054923031,"KcfkcOPpm0oa":812430736358,"6b33fDEEbb8X":308707217949,"XL37LcNWmetf":333393828899,"khST0oUhoJx0":432566650689,"JU9AFFIjzV5E":398029891248,"UBboRXjoN6Gz":166537550404,"182CpvpUCuzk":495014104561,"Gxp8CRDWc0XN":643698135782,"JN8OkeoKDWf0":48355251250,"6vplm8djQ3C4":961151081953,"9wEEsRs0nOog":241232145917,"0Dc2AWMVKAPR":207910044113,"oXdBT0QP8gfV":695693007607,"TdNqrPQ8yvbg":712573462696,"YfCCf1oFTXJi":702201965471,"OnstAYvaHexD":314257598701,"mdcxnNFGzo2A":371536229971,"3l5TIUv2o3VG":112843689879,"8JSPU0sN2PaU":344609159043,"4xbqBhq5FhDW":906059760341,"DkzehzrM5bNC":314269220090,"G5UpVN37COYI":227812795818,"6enkAcrnTuXF":567564048588,"SRZej5bMPqwQ":569218546528,"QYfGe073hRci":938494617017,"NtDdVPygteIk":757011813115,"WBhiyI0wDSNC":650629659913,"JpVpBi95ftoR":231121314930,"aH3vcpHmRZAX":998220089831,"t6b2KSNhojZ8":595689589162,"YdyjWg5ToSjB":751841751187,"IfjvHvtGwq7Y":48728429616,"f8btBHvSvX7o":178520439793,"RBeme0LUYnIm":917405792509,"nrlIKtIVEM81":535357360309,"uDZRRVJTpsgH":123868866606,"Bmp4aOHSklgu":375146201761,"RQ44brsxncls":78124045443,"ac90ewfHHK6K":563052043105,"aJMrwHBXQQ5U":776510692826,"xhFLs9hW0hkc":595416724609,"vKtdejwjkIYc":830192760938,"bnaUkCSBUV2k":500827613896,"ceeHw9k7U75D":478330687719,"DVWHy3ekhGQt":444643385016,"z3wNCozAQsIE":177792039350,"woQWYemg5KD0":281229638031,"galMIMRpV7QY":282992523611,"eZzkxom4gStr":392812867315,"YVi3hoUOpihH":157821482380,"eGjJgkN21SYn":773743141863,"wnZgLbFPtJv8":94519874693,"dKw2LFu6Efm4":224979345112,"lxttAKuPEJx0":857932604334,"788XShYtA97l":815795751559,"Fp9ZLYeUEuiE":496386297313,"AxHLjbwVRjA2":234547332984,"6qaKdTK1WxNR":983849144544,"8VBbPBw2pvEL":484427311439,"Vz6brQVGaHdx":82469548627,"3ACe257xLoyy":763061232514,"bo5gnqDOb82I":593344707749,"ApxXsKESKtvl":398184653253,"qp15QVK1KkTv":726831405940,"HW6RcMCzvDsB":632359988023,"LUtuNET9wjlC":882248655794,"wbhk4mRyxouV":196671296434,"m4Of5YsqQE1B":775000717615,"cpXndOrbXuQp":544565226040,"ehY50Iga0ANX":874131615378,"SivdspOYOuNg":385373276228,"iWm4T6IzY0WF":795479367176,"AU1iUzhIi1lD":853305095341,"c4HFAJx8prKD":285063922245,"dTGdvgWJmN38":987255174449,"DoeE8CTZ6861":402481310940,"70kdanGpVZCg":699309024534,"0DbCPm0i5nCw":991585429791,"Ne8HJcCVt1Bq":408059345098,"sEggrh0x4tpA":216391577150,"sU4hNU8dAkLQ":748865455223,"xDpgeHmd97rz":251751426167,"ycO4yrwI7aGC":678307613527,"ao0bzpEjpUJH":172962306989,"TYA68zdEQAiZ":159249230834,"6o7QNs6yjUgT":929349689750,"5bNo3pxDPfUA":878087590271,"lKlCfhR7hDhD":387714106750,"TNXcKwhG89Pp":642023028966,"fxvXjUNIYKOJ":542305288086,"I6oGUaOEy0Wj":136826236761,"88jZsV8TzA3b":215680576280,"aUdbM0du2Lhj":75405010644,"KaJ4y8s5YunA":94011245248,"osmwhcZ7D7YA":83812998669,"RsgkKLR6qssi":473416623005,"CoAh537YdKst":134539630422,"ulcrDHv8oOi0":574182498753,"mv8cec8lvTzp":803563346868,"z8ap97KGgSux":667077261792,"QqYebs8AOOP5":134943754246,"9P3rUK6hYhpi":226967793168,"Q5fyoaQxYKh7":156748873439,"gbtaEhP4iYvX":840797868512,"GMaolJAiDAzm":370768917655,"EGkmQMtgZimX":792751575897,"e7eV5VnAzo4M":831760079008,"MhE7KDp6ZlVp":883476987399,"jjclNZyQFzdd":253584563144,"h68m4uA3EDH1":53028472754,"rl1Uw5FVOnXW":754366484869,"v0xbuof2BoNM":304034256079,"76ejZuWii98u":547812301765,"bAfZXgBpxI9V":615164198419,"YULheCfOAMq7":668536742631,"n4UsrXIUe82n":310631798182,"i5xJg6oQ65hr":985876479410,"baBZXqiUo0oY":435157931532,"aa9ONIRvtvUK":376258297599,"w3IeSR1Tqs0p":162530566962,"MjlyNk1rCYT3":593617936923,"eEhWvKNsqrFb":161700568369,"XBNgadlXT1To":352032622928,"NUELdpjc4FLA":843762056458,"urLdP3YCzne2":135357250577,"qd9RXnt6seVZ":479985827476,"xjSlR5YHqGGI":89121265475,"1lIssc6u3mh4":700581720517,"GVf2qhkdWlgp":945522655286,"BqPHlQWgPI7w":985472103749,"LylCgJjlxJsT":601883246320,"VzqZEgU2NZzA":580082876595,"PmokhhRtwXoX":164522326683,"4751RqHmm3WW":507675444444,"Iozt750owm0H":896343166352,"k539wGe08zIy":232530178120,"54Ch22BgAJnv":145004479723,"3zQqMmGPPeRU":764676602674,"WS3jn6zuHA4z":580356438665,"cWjQwO3psOCz":243554509457,"btQr1oJzRvWk":860442611376,"Rv5y37JrmqH9":755050071451,"iuxbCuX52OG7":891023880518,"EpoGHXk1genu":213997551012,"EG87q2RxiOaw":5476083945,"DSl8ylA022bs":18700985851,"xOlxwtiuEasi":98595235821,"SZeo5zwLeVU7":690976828987,"rBAai5RtxJpx":496397213990,"unU3rh7iFdc3":240590374038,"T5pyxkquYxfI":565432869713,"mGsT6VBAOQeh":116492716001,"CEWkEPJRWnTm":598607793497,"6Stnvap7PX2W":857889820037,"xR1BxWtLbTMN":503871278019,"h3HjhwE1u3ip":808839717673,"U7DEtlV7FqfQ":989557398730,"iEY9nLlcMjD6":903736613195,"37zxwBb17Tii":668283356782,"QIG4BV8TUIXl":299695804810,"0ebTCyzBS8Wd":153930167810,"FenAdoeB6Qry":730863294100,"dXZwj8aKfClj":754140388922,"MMdqZgXRrVWL":235157483798,"MJYTgOYym6Tx":967576997899,"wgHzfTEpGR26":203602132989,"GwONAo8tmStk":899067626663,"iVK59zVe3ItH":983698046435,"JVAY4aNYCECO":201378890394,"LK2VXXv48eDT":859924528749,"gBpIVn625W6b":268845714175,"2zZRPLRWpC3s":901879415574,"EzCziwtNWpBC":711696157048,"j8xjSY2OzAWB":702124670513,"sgskpimnyIOq":543116627269,"EiG1ll9a6XHs":675559071021,"kPztzgK9sPiK":411660720834,"ZZMM5ptwn0RH":275062667401,"7fbYXaTnO9uo":606940692689,"392FABwWULGz":242182016974,"O6f2uDV7PDt8":754417500892,"8fBXE2JzOZ6a":387097316061,"CvHlmhFst3On":81340123976,"Bm2YKia69ZAv":992788975405,"YqZKS17mkIa2":990998776148,"dwQcuFc3e2AP":931235667536,"6dTBKVJaWxAC":945881288597,"89NddzzboOYt":951241953233,"cz4XXKySlqrw":531108017011,"8KWmloxrMTXl":329964008659,"d40Zf2mj9UsY":512848605031,"nXRsOExhIsma":26990672985,"PKQ1xIMPM363":368748036649,"ILMDYb3QhIAO":517095288539,"yKvc4E3nzwOn":612640769731,"mMV1qW2fdHQt":468900643997,"EH4m9kFJ4Xhp":489374188072,"8I80kSHDuFCG":549190329468,"o5MBMEuNqPD0":409907389211,"HXwO6ExpB6sb":681975799386,"gK0ToH6XoCRx":633581740592,"lGyTB1JArUXb":720952910499,"Tb3NfEnin2ZH":466380606312,"xpUQe19VYMhS":576500110045,"CmN4We6DKX4H":746825503264,"d8IetO46Cywi":582622616157,"JCM3yc0ajpdB":313616611053,"CYCQHopoSLKY":643818533260,"6H8pxfVk5wqa":568954792506,"XerTTKEgVYVZ":334857241437,"3ZeOUl6rGazU":832897838143,"FGxgtNoYKbjm":205783403726,"LT0YPk0xTozo":726188763349,"clGnWzmMabni":746204930702,"D6SG5kHG8FWH":126872565669,"te1E5JW8RTVM":146591973147,"9nkwGChjhTUK":287128841122,"zPVWsPJLc7Qa":69760700900,"gS1ezSnbSQLK":369432090043,"FmzDJ3husi7n":185416637610,"Bb1V6B9dHIjl":97632027844,"ViKQDJ0UzWGE":151821898528,"qvoE52zJzlCF":253484966431,"ApIKhnDXFmDf":238265206358,"H4kRZveVi84Z":327112436008,"v7BFj29ysjiO":491394394830,"xxpEe1fiDNG9":53952494311,"QL2JkBAZxzya":865175859517,"zWKCl2dhnCHU":187078236172,"PYFbKMipHtUR":563248646054,"VGAtkUHamCix":106830318037,"3LNYrpH2Xhgp":147322771015,"DFmgO6vfS6oR":262745432044,"8rRbhMqcICVC":225056133119,"sWeRNmLrzhAh":976431839521,"SJhakEA4pqm9":562676151155,"Jfc8gGdhATjR":522233740316,"KigPsrV14c2v":820582606603,"rp8eIlIbl1B8":723534959634,"FU6BAB69S95f":224826422653,"yNdv28xt4Lk0":511142810287,"hZ9KOnYvIWCB":99581595217,"57QHTRwVMZSy":186331144558,"g38XKk3qI9vl":484867931550,"dXkbch8noxWH":121583238589,"BDEKARvSmddK":177447401433,"anPWMOX4Wf58":803123514053,"AA4e6fqJSlWd":802925443373,"ZVFwY8k5RY1n":212010053399,"dzFz66BHgvrg":721724161151,"KQjTm8dUK7Kn":43498766517,"TC6G4fagSTmD":989820593808,"tS1Aw2Rk4Nn7":208578005895,"jIK3Mdz15mSQ":249699452375,"aYvyKtRnZ7GA":956266405254,"BABmq969xH3U":93941599867,"B2etsqWYKOU4":970803118910,"q5o48NF53m0S":963601861114,"xE2P06WGAMjG":621240690035,"iNJSG2P7Y85j":430475682796,"Ip0GyAJAxRKy":570954416469,"DJq3xgLBjFH6":38508091805,"0pcGaMn8IU9p":239211507054,"4VEBUzt5IIrk":292458396951,"a8r2YXZMup3n":237847646755,"Qnn0BsOIgBd9":381358389673,"WVi21p0dpBLp":883950961985,"9pxskLBrDpfz":841362938386,"0eGK3aE17bXC":719902178913,"fMZ8zmG4WWs9":555194830944,"8O1iqoEX52ls":470268861698,"R8DPiW5gLEnu":460626600330,"1OmtnAQSd58K":495980055838,"VwV944TTzaNE":133295128981,"PwxEMcV5knTj":705648842638,"dyaolBa7lIaW":54881481017,"wgR0taIHNXwd":880938565761,"x8ymy6MGdjce":263960716522,"Z38sfub67XxF":808721683112,"HzcjGRXmk88f":824178970595,"sYeDdLVULvPW":545202278580,"EaGfYyiv9ZVL":576843135255,"VZwQLEd7WyHs":650336639103,"p5ETFT4kzNKu":53454865667,"CpOBQOqUd9xM":988580981959,"MGY6pNJHcwm6":492210874029,"ygvykPuJcpYm":740867229156,"dHkrp2nucZMM":512497137324,"mCUCP7JAKgU0":423507709329,"ttz2B6fSzbc2":612339166129,"7ZXBlypMNUTG":519347011608,"min9IwFwl9oR":561224269212,"SMAcovvqgEkl":381782005604,"HmY4xmuJKeji":373929736040,"dC1uKXUOChzc":73727887242,"1elZZuuCaKle":151412997352,"jr0GNrKgwvVq":457236899566,"l4k1vglpaAQs":521976957629,"drsv5QMLHun6":627004069248,"mAfPvdeefJxI":437852602412,"VofUfcaCcTzv":906515323027,"sAwpQi06d0jr":626827185361,"IFCRVDt4RKcT":639872212764,"t3oVBx03HWyD":243889210170,"c20UXGzpIXLw":451181055858,"I4BjCLVjVtij":482528591256,"vrtDuDbRDTU2":280997273588,"kS6GwhDKinXm":110727609230,"sxtKqS9XEsDh":731213244702,"fWYq8soHcbFq":7274572419,"7zyFaCOOZL3c":741987659118,"xl6yIHNArOxq":711911757390,"SBEZeEs6SKaI":451551011801,"k3ZeA0mlbLJC":410324974080,"XAB07BUnYf4j":359467447678,"BG9Z1SE7xpFq":349189467651,"pAyjmPBOMy51":788156684218,"82rn2Cg680j6":738891016033,"CRPFEJuSwBxf":922307566195,"DtlcS3737niv":549475828834,"9c0TldEfLCKP":612278901650,"ecmgm5JnKVO7":6322265567,"8ohBaX766tKh":374074511691,"GRag0Jx8nXZp":672291378844,"O3WmXpdMHuIq":206443916513,"dg9TgrEc1yze":287702065382,"JTPQ7mVhYBod":284061832965,"lj3wB86nfwYu":452925969148,"bS0y4GeHXmvp":778673862460,"FM39WZSbT6Ap":722990180653,"pyw0HMy99fQl":711494155060,"vPmI0MFGJUk9":423206307494,"Hudt5Y4ecvC0":76610598383,"Hje9nX8laJBf":64939547123,"tHy2d6WloVy1":793501095252,"0Ejm7SQtqTnn":448993595666,"YbvpiBC57pst":303527527902,"NOrfqaHMjNvQ":222992650634,"KU9AEJckw579":840773144038,"pRWV2S9Pc2OL":284486021067,"DcpBMSizA1iT":830466791565,"YVI9s3rCaJ3c":906137791101,"8K5VEA0AkPxQ":826943810606,"2APS8gGNpp0D":260558712186,"jYyaJhqYKzHY":202037971052,"tzxqM1ReddtM":777061581488,"IgC8IEZa4f5t":663192503913,"giXtI9asjP4O":859677956380,"t0y1jvxx7t4Y":549710511679,"HFVrO9yoBdMq":628469797145,"ytmXjgXjGVQ1":394752447844,"MH2tapB0nlvM":308109062037,"HZ4UNFBgkbBM":131509289390,"Mm04cvtl2bKd":272688506433,"rxPS9j4yggb9":247752288342,"LrHbUsfVIMA3":108582871151,"JECDIwHrD6o3":920503594810,"iHNo9J4T1jhU":865187201255,"PJIQsp4hk3XV":717799802305,"I5fkxsU9QDnb":257893731286,"ZDq0dJVAlROC":84517454995,"0TU2jAG9Wvi7":534619499414,"f2ryz0H07WT0":420118074524,"wY5fsVA2Wksc":10592582754,"X6l6qEl4XliY":343177685340,"6NvXmJCYFgBY":659146724152,"HbXwiA7fwE1r":201257007962,"OmM7WrrZwZgJ":752143999925,"9iXhiyUKOSKY":658846039541,"hTZJAxawNjdD":993725850232,"TLHVTe44DQII":880860086451,"47SIGhJgmAOf":324037804042,"RmOWJaXAxUa5":639808159282,"Dq6bsZgMLwVm":947604633110,"HKdxkQJ9ZLL8":187362492213,"8NwE72kANjBk":516008276967,"5FYcwSY1k9GP":724684403506,"OLHCYuR9R2Ua":230237972667,"ILyY2UbcYJo1":801818327547,"LymJKeAQYWHS":435674045096,"7F9JInB7L9bm":112936164438,"OIB78rSz8VzJ":533496246046,"20jO3wGlWOwx":471649872464,"1VG2Y2UGIdVI":105793998130,"8e0wyvFrcKXi":919413371872,"tJFwLir8OiY0":132623233051,"D9x8GdqCZJAs":636223724357,"4KRgkgaMKqow":921597742518,"4xSXGegTEkgV":134572020135,"asQsLnN5Xu9M":699678575084,"xRPyqPKoCCCO":882112788352,"e0AZ19wBTXOm":333269107845,"AU8xrwqTHx0z":727586887647,"Lug0BT4j278V":315570576967,"LVFbMUAmgiQq":615358664998,"c2anVS1vUB4E":457111150415,"p4NUtASKifN5":986530730512,"BCzCSgCrJiQq":685640536341,"ktYqM5rSKpFI":741352433876,"U9Gq8gitsQn1":235564036720,"9lLpDWdnQK5f":847047857581,"l168zVkPBMTu":387993310479,"ja44zLcaZs60":344848591789,"u5CDScjS2dF0":337244908246,"zaX1SrHka6Au":787106578459,"SYHN8N3GW5kA":441249908572,"7pBuzve9G7Dj":257738147613,"KXGSwTEUvs2q":238613170157,"0CJemFNEuVHu":552139676779,"5BCI4DPNaE5G":736060796553,"7sDsXIzKm6fA":575387436021,"zMgyN0pNrxtd":114953856677,"i0jXhUJFvvkf":484533270716,"qP97hq7mvcDJ":148200243574,"IFPkgsbg3IuS":722187976928,"SLnjDiwf7OMY":721262902043,"i1DrxqbusWFU":12493005003,"lSF3GYaFF9XF":864452580038,"V0myaIbBkOJg":388212126470,"GeZTORsNMagZ":257027493675,"PL1PXjyCAESK":152822303130,"7n90PAxjSvFo":632698201439,"VaFUNVW2CN4j":552460082670,"nI0O1R73kOos":345560675484,"9BrhRPKhMk3t":13285949705,"AEkHwW6Z1uVJ":673711253970,"amZH2OYSEd03":27006407048,"OpNPSiMWF13f":374402817572,"J2hxhInsIPLp":874432888994,"tLxTFXB9iwiM":210255032677,"FP7R89gdU3ka":770190602615,"7xPw8TfMxXFk":376614269881,"Bau9FNMhbfp2":419941293766,"QCEUJDmJLt3Z":821642245116,"g69gw3RaQtOA":799450005253,"asI2t7Xnmm6A":415245183450,"CzFv6ZoJAF9i":987180586050,"g9IH6XQdR2UJ":846481168535,"S9LleJ53OofC":975660077655,"fIMqtoHHej8K":587065611486,"CkNOvNl1fxeB":698126798634,"A6t0KawgCG22":972895120792,"pGntDIPu5aFe":225763064830,"bzk3iSxROwGu":517442989816,"7rvpNfJEbd3j":740461507859,"6HgK3V9IW7ZE":641897548645,"CI9HSduVTlQ3":940078601923,"34JG73g8RqcL":421812410068,"TDO577sp5IeY":368057512532,"15q5AXP36m17":874436516020,"tWj3TMS173z3":490794751901,"DYxBbPj1cdct":994748296192,"tA3Urv2EEE5D":654884760742,"ZKkrdBij6WiK":541774400042,"BMHADEbtw24N":398221805345,"LXuZi3B8ZdiE":682040521597,"EyuvXtMm0Bu4":659528439476,"eurgDGnpliUh":867134146351,"DyGilovOtg5e":63831510452,"812Yz9iwDZ56":72478515915,"bpG2nICZyXXV":152427761180,"QNGGQW5PWHEJ":53374232318,"kbyfl4NJ4mAa":764296136014,"NOSD0laLe2Jj":201258425131,"wgh0QwPMC6aP":387980537539,"htADPruRPEQn":680644565353,"SZNVN1Dg1lGi":975665369841,"jjetaNH3HEKK":333598845407,"U8DWOlpYX6Cc":976480208961,"GGZV8BAaupmq":434889662023,"yejwLZHADNCG":227200270463,"fF6IAul2Ue21":118940272046,"HILxPAb8VUFF":2381065515,"l8G0IQogxvTs":961002844623,"EadC5lwUhMcX":755240709687,"dtLWJsbWcfVY":190562064157,"qP3KIERYhsvd":398298987656,"POMqM5w8u8Q3":547986386797,"OfT5M2NmkPlt":104680704290,"tfbAmfiro12s":256601415038,"b3Q0MaofrBje":33590527366,"M4v57DJkAkUR":416602438323,"ifI0LFWY0jJO":511458099723,"k99Q6hUCvcuL":789439869416,"JOSzYZDezejT":508060641466,"wTA4az2W0Agh":580771662339,"jOJHy86z9TbK":525667039334,"pi4BkoSnITH9":178184549003,"HNJV6JBV5fjt":54765570301,"yDVGk8Jr7U4R":953993554111,"fUKkf6Z4PM6w":729110989291,"gr4CVlywc8lC":301022586252,"I6DMEpbVBR7p":920429817947,"NOSx1EFK7knW":397659185894,"pnFkfGJzaYAB":441362303434,"EtTrNhtDmnT9":645538005543,"ikWaME7VBBeq":598547330405,"1k8NBOBWAr12":6898232507,"gMWgixvUuH8F":254128700709,"4ApPAG9G4n42":655524498358,"vNEJQUhmw4jw":436252717017,"RX0dNrXIFPFj":694053196016,"kmvDjHyeO5kX":746835392520,"6M3Uz82MmPv9":621895391352,"H1Cfwjwjlu57":224332973720,"boNEAZjaVTr7":122782851614,"24F1KxDe3ro9":172245367430,"F1K5TPIpanpg":996282138405,"R7jev2Y7n87R":942734430115,"JVdutS0MAwod":895523195859,"AdrlpsOc6XMy":54186691132,"WoIDTzAJAJft":733885618735,"LF5jT63kMB1h":388400218510,"H95dvvURYpqo":551729899723,"Pd46I29ybflR":209418431254,"yPFfIHqAD8CB":864010788727,"mh4f9GMJ31Ur":921505436211,"ViZsikjFuorg":666260564600,"TUx6x5dlkg9N":884131566124,"LAVSszsR4IVa":482725568732,"DdZlCyTTfXNf":496299839070,"lYDXESKOkn67":285681050123,"YkALpuYz0UJy":123822895084,"CvLyypLm71qJ":676293596389,"SPQqvzkqWlqi":272817759370,"AdS453vbaNQt":847628929172,"ps1sb5KIEhz3":300478816893,"Qwnieg2MRF9E":375362055183,"F0TfW52DfsIM":713649393572,"JxKVA3tGQy5R":653410309908,"c6WhjlRvaRcl":805498398,"P6v3abiwtM51":443821828951,"lNRUyvwcZ81R":598004603258,"9NuwL4nSA8GT":138815305449,"j6LcH92dngFj":249361878219,"1j6z9aesHRTa":706178466784,"jptcLePMzbop":419424736227,"VHTkrCJOndOv":12018605621,"unAgN6obWsEr":842720086451,"JNX1JlG9ucm5":518634825161,"qp2HHqQIbBkf":724610747695,"LVXN7KOUEjpQ":313409020153,"8MtJ1WZjBj2X":694029554343,"8Db29Sj5HN7M":831536525227,"iZmJio6pf7fp":466121637085,"fm4XeVqmkCTN":599223302766,"gwG5IZ97WJGs":363500563169,"n2iCG8e967Ea":158843218034,"qBMHoAm4yYkD":172276823531,"Gwhv9KwGEhct":475809506960,"PRm6z4rsVfKK":895056228705,"0AndVESrwSHM":365852344855,"gFyyObIlMN9O":264489939776,"5Wt19jr9ukgl":434740815348,"yuBxiAbpKdUd":61408168617,"bsqlvZX2oWzu":312191008910,"JtglSK6MXLuK":368189895265,"GaDyTfiCPWbN":499652337984,"EDINBqmQRaA3":527167336636,"ChZBn50I7dqK":410013571725,"D9vQ4lodxE3N":687716569349,"kGHTClVdf35R":780362955486,"mfDP4gjQ6hgf":247107393277,"vUSAWrfdLQ7z":277970963646,"DIRWncOlQx1B":61752234195,"Ve0gxwQtrdiY":576663330565,"rQANnvyJOiZU":936109123282,"nhluXYi4LAG4":196830437719,"LMRuX4BN8UDa":197373880009,"iS7CtF2kATUY":995822636649,"bSKIOMVO3A93":412377384881,"X43p50X6NwYH":654897394982,"33HcTxHS04rV":918907395058,"63D2z90EOPi9":893908487722,"6mAy7eTBMB3u":1796114246,"VNkdc0qMv9Mj":322311770414,"ImDYussbKM27":983719816225,"oaI2SitCbPO2":346620833114,"uc5hVlqPyy4b":331553415410,"uRUXGZhg42Qd":62445624571,"sdI8R0vRcbaC":848795479947,"C3k1luK00a6T":867770430527,"VSygDGFfh7Pm":219732517095,"tOOrGvAZ15O6":563848491473,"W63UFv6VBUKo":206169425364,"mxJvjfRqJHji":276007555901,"LjThwo2W5wAI":684826888317,"7ucr0ewAJwMo":952295373671,"x8QOIii9AThC":745696833857,"oheTzFQvogtp":819687168823,"7oMf6Pn2MWst":316239698545,"xYj8l507jlZV":381750800209,"yH7undNq7Blc":137985046761,"SrYMY8PnKLw1":383046744686,"OT1uLzYwEK0l":129988441762,"N8pf9RA0Qxm4":137557032535,"PDNfJKqbZbpV":718553344226,"HS4MYTvHNj4T":438389760881,"TbPoK6FlMdOg":648684871898,"6tNdSFWdNM3x":206394291966,"znjjmlWQz4oR":730449775460,"FBn2R7O6eLlg":469887778075,"6uiZqf0J6zHT":416540756290,"MEdbyMKBczRp":833868579457,"ANitHRZhuH73":276519758219,"UkUlY1DjBFXG":483691297380,"VwusuMTHSnps":14828681493,"jzKfT2yFLjP8":997869844819,"VXu1hM07Ylpt":644183022003,"uITsI7OKdgqS":93709531620,"85Rrbtdev15u":122585381712,"iEojrX21xHMh":365251946481,"aoWbzKoLqsb8":139378848498,"jJjyHKHt2dFr":696730412325,"afpljpMrh2xZ":426494975198,"4CifwHRfmXXJ":147839064427,"26KQ2Gb4Uwjk":12497084233,"wTgWF0lI1nO9":276322743390,"etN4l4mpMGgx":530635718526,"oOyxko0VOxK0":119682077019,"JZAqODocoyyN":457985111435,"9sLRTACYK112":316496421968,"CPFIkYzZuAq3":666782111856,"lK50N5ZAXhtL":145878791862,"ANJrfGkXkEqm":255318672334,"TDnYsNHFZsvH":818571722957,"NkIBhgPnRE2F":599130809970,"01zNgUl6TTTa":710193700463,"hglrhjsWu7KK":394748638493,"Mm66OtPkrhV1":951900749700,"LS4jC7lYR9en":759427429807,"9DMuT07OtIg7":63861142273,"ZAIsrohD3TBe":269729235100,"nESkiSQ4LXi1":933144925976,"TA8Wa3HehJ3X":663024487842,"oNumoQt42bm4":513517142912,"kxPjFzkVoRZa":897853612012,"GTMKGH60XPl6":160879873053,"x1hI3T0hHzm9":336866054557,"gy3c7eaXyX6d":808974955350,"Zaoo88v8pF5G":361757509019,"GegKxcGDjvnR":846932378953,"PE27LbZ7epqk":579022043670,"JWz9G6dtWqV8":2379544313,"DwXWx2DPkSYA":768127645653,"45qSqbAlVJfz":788259008479,"5vRxXN2L9zC3":901748777414,"RK5qX6zJYiWe":981783121701,"mJYw2Nb0rt3k":746279986168,"2mx7ozJdOwt2":562750777158,"TkjLcp3ooR2m":200454155050,"iBFh7t4J0Q22":704966689830,"7yeNbs5884Db":733016991371,"4i8R3QABNdof":791231066770,"m3CU86kvoZek":215010683490,"xKSpzqA33vex":503362565490,"eK7ZJ4CPbdwn":545639801535,"mH292NsbWPEg":480995698290,"JqpN3CkcAGOn":895856681290,"wF27vUKq9Wbr":510368383182,"CPy64PE7nE86":994481731445,"9d8jhcPp4rNy":493256732060,"a7L4tuWvYHg3":643370214321,"hCKIzmUfKgaP":663775962822,"ktA8oO8iuOfD":262091731090,"mmbxYrArp9T7":79663266463,"DPHUM8hWgZMX":286249864931,"aM63YzbUU7aB":884431708359,"EMs70f06ySvR":380746243632,"PCJSDzKOkxP5":37603083905,"155TT0YteYLM":303838995163,"neLFgvXuHhw7":852240519980,"78uMREXLqk4f":347847887619,"vvdvqw8ZoB0b":179579261104,"S1zXhAxBnXL9":99195252752,"05sIn7nUJT1X":449954104448,"eo9uV0KEB9o3":520914212001,"RUcueTpdy7J2":838831900459,"iYYKgyMwecsA":127526210681,"MQwBAAX253yb":616624770766,"TuDDz2RRYgVb":109582266019,"4FqqSXctCFm8":520256964851,"7jkLTAWktkbZ":937140524428,"Safo8m4oVok8":121275593507,"B6SamoNKSsET":488283243266,"omZkgtigy7DN":510732875380,"fPrr8xQrMZNF":114721467211,"ZAe0LDgwe6F0":895463151045,"J9InI9h0PJXZ":49259615681,"jHy93v8c55aV":708285802420,"9g3QuMDBmYkZ":79292702851,"GFKk3cfl2IKg":134562956175,"2omdoNXFKWXx":510509147518,"1SEkVS2J7NUh":31117076779,"Vj5PiGEN40Fj":97451088026,"Npib3nmrhawv":578138599484,"2gZJRQujyDqz":215902545219,"vPtnvoZmtk3g":148265374416,"XpDZ9Hjs8fnl":531817851228,"ax4M0MNgnXit":400156230427,"knPhITnvCaZM":886113475214,"vGsX8nR6JcPn":572099997082,"dfEN9lQDNpfO":75389632323,"WprxbDHj7qwx":691017438825,"su2utaDN82Pz":847488847038,"LEDPTXwXNFLe":451295555965,"DG7AMEPtaIHz":650643193011,"RiPUK0OhEttr":586435705237,"ehhPtJZ1Tf8c":558574101957,"BjhovuZzc26C":62000038132,"lN3D8ugY0vdK":881506983007,"obGMqReAgRh8":120251950069,"vmWxWpLMZ7CJ":502211197703,"X5SYW22fhjfr":571263076274,"eRWzSrPFWdN4":520744587644,"QWt0w2skVjPm":788540966847,"kAcgITzFisqt":718546383651,"GG3i9QeWY85A":935302027118,"ro9GkZQaI0Wq":467406934083,"2lqzKYBLQpPn":532953481591,"Hr5BtuQpCsII":824214187589,"WIex2jitr67v":731828489999,"8Xp2GEJfB8uS":273235345783,"KYY9r01c3zCp":409605055932,"Btce9AQuV1Sz":555415686747,"ZOBQubAzZqtn":712466784862,"LZZLtO17FXHm":878903787676,"mSRjk9VDmkUl":246396642624,"KfYePauGpsos":141199045256,"HduFGOkZ0Txa":419391013790,"3avDRdNGdp02":646423846217,"JB0bQEdpm2JT":253179721986,"jajOcgCB8FMZ":598493026483,"fHrtx0My4zhn":515967886474,"rpQ3aXNh6mtU":790143465902,"WPhoWW2DaguI":475485091280,"FnMe5fABwN4N":187703995572,"fVhaRrY3fvf3":124262511340,"GBIgvftbCtG2":474627373867,"Lnb1gqh5wEFB":798245178528,"3MlM2veJ1k6e":589679248648,"qKx75xBVDUfF":982359975034,"KxA3DRbTneEI":845750660069,"HcQYTB70MVhP":525741379757,"nrZVD3gftIrA":998321244903,"MEd5htosCFK3":145563945310,"ZDYVXajQ9KVP":119864794488,"Poi9OgKBZ79c":419330557548,"vEpJCzS82nIU":43091500561,"BCDeU9S4NKdS":777367432509,"PDPCYncZ9lRC":671628501106,"eMsX5zJ5En3m":581754864737,"mgsRLWScw6fd":600257990353,"2osSyZ1vCfFl":634199978346,"x6lVwxWFhhIF":831689267564,"Cnzkrjc0S8Tl":82276481106,"xYsj6dOa1Bsm":807843110174,"YIUi3GsfRFHu":830343117512,"zhT5kiuTaCqQ":381452109827,"BsgvBjt1UcO2":540467478112,"n8u9HbhN7yxX":289742346968,"eR3paYt9fFq8":11829473175,"weXdG58856j2":662778368901,"5oIlTPYSSPoW":180321694992,"F2iy9UUbmHfV":662424783563,"4fJAYnMbHmaz":844893132490,"3A9TUGzsJumX":477925172999,"ZkYnZw3bXWI5":956236488168,"QERJA89GDxTm":597835837882,"IA4jJAKIvoEb":394407733297,"0VIjQgiy1FZZ":932064637223,"YZoXflR5QmRA":563240756902,"wOEY9n0KWwhG":463491965154,"O0KD3f4zE7iq":402485342487,"iAqYC3SkAxV4":335573911480,"zFPuX7T0wudt":593265385688,"WTVHotfeHgJg":583345810994,"cAgTsNZkV4dy":366363174973,"xtHi6ISiYJys":638004475886,"pFky5vEsiUMo":804016951678,"FwBRmmTt1ndX":345675107439,"i3aD70lNCFy2":551764647778,"x1cSlNY8bgeu":80752882918,"u9zCdk4HI45O":972049457057,"ud2KKFzCrFAM":172615650657,"o8UmnkDjxmP3":318921432864,"DHpkyVSLR2Za":518929428420,"49U9uSkNUfd3":234781141309,"uwpDkWZJPu7j":159807857490,"x55EeKJEOqZ0":895724753736,"FAiL69Jaq84I":175074584157,"6HRPnm70ukQ5":799709895308,"0zX2vHRS9QwK":206323232157,"0nB6357zispO":224685475048,"ctSWBdAoy4iW":858173053235,"ywjvBx113xV0":55043456576,"jvtw63u8Shq2":913933833055,"oTtF7VXkMBix":673583027336,"jFrwSsFFoL6Q":170036120004,"aCG8Fw6yeTQc":360770200104,"DfMCXvodcwSz":667936978847,"fyYhJNSsjNcg":381640966872,"2EoKLwYxT9lY":830172257017,"3JjsZ6v8sDBy":884598597478,"iVdAy1PzI9Dr":244117051466,"TG5bN6wcdtgS":385137707380,"p08O1yiFiOyI":132344763744,"rAiv1VhPruU2":52336129589,"c1hjccsacnDK":479962191139,"Q34IpkUQVPdT":488380698344,"0jmomdXwim0f":770507803568,"lOlxEtmqLXLk":415783638282,"pSa9bNe6eAkj":219502300775,"tlIYEm1MHifn":626079919039,"2h3sGdPnnE1S":399058794516,"DEH2QBZQANc0":385205044535,"8h7s659jJbae":173718285702,"99TeWwaOEe2q":197948012668,"cJgqov3mZFPV":423199422448,"PmaakIqtii7D":919368800803,"wW4iLcnNoYH7":367716127570,"DPPdH9PP1qrc":246878310795,"lbXd2XQuFSEE":242595372048,"PVxZlBjqZyh7":89016736352,"kqL5JhSz0nuI":405911888184,"RQ324fHGDbc6":706348507523,"48d4eCVsIlts":888422484065,"OlsIVDRZQp1E":595671033509,"qmTakVcxM5NS":659847655411,"g8pH0Kvjnz4M":351761241591,"eBF7DCqjCIaA":34158986686,"a4B9vzvj632s":944594915517,"uZXFVAt6zvJ7":84855919130,"GCH7G7RJhhq5":269676818650,"ABCj5wVXF4Ai":3458709158,"Y3fwYwikyVGp":195029451408,"sOo2Uty9GlZo":567257069519,"PT1PluHgc2LK":244358545438,"znBxANMiKELe":141510846245,"6YzSb584nbXh":77530851464,"3qoj6u9nNLgh":426297589057,"9aTUpeQil94d":955665444056,"FeRfiZOU6zn6":626788094578,"13QHLuS2roUd":712199239738,"P4iN3LJEoLTM":666043927463,"NnCCNI1eFRjK":936005266042,"VryCmGfTKwxj":391433452610,"ZWrBftJqVEri":641844050132,"gtc0naq9Rhwu":672143462932,"t0niEPgoB9qC":159249837650,"oV1zSCiaKXO5":543601439936,"uC4J6tDDwaxC":467076244991,"NQWxl20pxg7d":357965226923,"lxX0hRqUTshw":259060631810,"wxpqQaQCDY9F":113936654832,"wh5COw0EZ7Ql":14667914685,"evgTMPX44ywg":339332619954,"vCpo9TuqWp9Y":309596953739,"ZgF4ONhp6Ezc":446891762656,"v2WmjyKLuIqN":163080782572,"q4cbmlDajFph":937205461091,"7ijoNjSl9HfV":636368691312,"GKnLmU5AS0wU":317170771324,"3MfVduooShsr":111860542753,"77oeqA1LFCIN":681628346053,"w80vDXaYrRWk":519995252911,"fuUdoiLh5DZ2":314156370668,"4F5H7i7P4tJc":67762565135,"2POCllyb9GMf":550840000434,"nVdQlDgHJngm":199754094933,"FektsmdbAJl2":867474065976,"Sk6HqqdWcBUU":680090278425,"gx1KlrA2ikez":988055918580,"KUf67nbw7eWs":764598763890,"UvVwfjSecFfN":749273750225,"GSGeRKmWN7av":785090326541,"bAzehqPNrRL3":567802715572,"JO7EyzSDU3CC":793429481397,"oBYY80DUCc3O":480991618777,"9O1tLRCbCy0M":878724945387,"u5gDB4BjW8NW":445741628579,"mjdXMexsxJL3":540474690376,"ouuOMlYS9FTj":546351556188,"Pe7B87xGzjtL":560362065792,"04E2l4jKsS8L":698579976505,"RTvMCV79tZpg":749308954305,"oBLbCgJUEwDi":175259157662,"UBEn41BGsa7k":271742883900,"7a3cEC9WYiid":754728956094,"Ta65u7JQ1sm1":689660926874,"kbyB9xAMflxz":527936947232,"f3GJmdey7sJD":848275063962,"5K04cWmFpK2Y":34929671374,"pIlgkpRAiBNY":193788042358,"ZrygmgdVleLI":689850399489,"nYUZfsGXumDL":110884822556,"MLnEiDjkPyiZ":151888498540,"onOVkfzrAas1":400224792434,"3lELp4Px9oXf":40125695365,"QoLTEJXEtcqp":417750784162,"s8KyQ6YWZ82F":572901680452,"Rq6vUvCbwH89":42205086245,"EaPrzg5Z1oxU":119942431384,"o54TfeKryjRA":78914587740,"UpQmgZwnbglE":182986493971,"VbF3KVGvU7vJ":235221137768,"zdPZdYtP88QX":73494756133,"rYgN9E24OVGW":909514393405,"eoxeIVRJ4zgH":727411498764,"6Zip69IijjFJ":481343349885,"NX0xl7pMl9VR":628613182631,"2KTaJ3lJ5QDj":539297284993,"LLlF1vEKVjDz":971477850245,"axbHrcJRpc21":293799493359,"lQhHsZkqVsgd":893315612044,"iaocsp59vX84":835608054703,"AnsK7woodxeT":63392228494,"bBY5Il5GX8Aq":280662127332,"noNPokBZEdc9":870454746769,"MPOIl760HOKp":158782576318,"otjysfPaZVP7":289984887639,"aAHe45k0Wnfk":307891378923,"p6FuAOMRHmJZ":235264203949,"q87ePJWbst6B":588644767734,"vj9muhZJ2JhJ":526440012004,"YlKxUaAUzbtY":763383144004,"bRObz8K2Gojx":346546911236,"r9lt98tWCSx7":790680674987,"kQxphNQYFJNG":960574588031,"UspeFUrdYHV2":501385171447,"2b5bIosssKXf":75292221822,"YRk7mNvcpWbL":229173497175,"x2RQXqqv5jii":539801857887,"ie3YrgL5rXaT":452771742460,"JdPIa1FqPWxp":137486821498,"2hCN2BkXb1lw":801386647196,"hyBwHRpLrM4H":708344763379,"RGzCZDfZnj9U":962025435063,"TDpBmqmxrThi":948610959002,"GYafvb0KJnet":413425342136,"XmQkyIy8TYrJ":317786147411,"ip1oFqwjlKus":585740877417,"ebqc20CllkPe":130262263799,"GJpmW7Sl8EKw":198483310046,"5nnjsblPDCZR":871187661881,"5IbGCdo5LbNl":930624772481,"4rH5JUFypFQu":328718848511,"Slrw7tIkro2I":628143195079,"RRONQI7ZLrIN":166812924201,"5JTtOPVixwfY":838026296151,"Fyfi90nWvW25":164079055303,"ZavMktHDXkZC":611606522099,"5uUg1NXQbiEy":123097903803,"uLw5MxDzcPMB":264856417676,"f6OzbMoB4tct":51882698184,"YGnLkSyOsnGa":113119172791,"hFiyK48q8YfA":727079433719,"QP0KEqfSLGNF":945786430258,"wOR1HljZoh1R":661706905206,"UDjviBxBZCuZ":842909604886,"cIeRsYsKPDUO":93661475748,"WqOJHLnlVCy5":743577927417,"I3o5p5djLW4C":78474448138,"VX4juXIsev6H":526907122221,"CSLq85VmRmrj":592842405803,"8isz7CKSVYH6":591325504395,"PTvQ3zjgyXGO":558660937586,"QFP2VEb1dNuT":569800194574,"R2WUqaCJc8i8":995510812855,"K9yNjNkg1TQ2":908416796577,"7pfisp5kuyur":34867135065,"eBHytARPnMax":981288406075,"bSzisEHAC9t6":635282801586,"E8QRX1KyrYTk":421757713531,"mTM4QFgX3Jje":406351888628,"6fwi5zSBNAvC":911689355251,"KQXJd4SdLdOe":8501747827,"XDuFptFKpU9w":317151652508,"GYNHqx1zFE34":587804984161,"1oHpziEe89cO":204927688198,"5SzTPxnfjiSL":790977502097,"2PKVtbAYLaFk":55386142199,"XFtITvmtx5B0":440018860292,"SCfVjJyFXHLq":643533651273,"hkFWIBiUTElP":675021183537,"s8glGNctr0xu":62213933635,"AIYWGAtogkpm":920018485449,"PdmabtBSiS9u":173883708075,"C3R3EqFLXM2M":652468014618,"P3MJ9G6Q8SLv":56369028914,"fzq19FZCC5gv":780284923828,"ipZuphdff4sT":565110430922,"XZAged4u5nmN":843540285500,"QadnmekToj5m":332369068505,"tpcehGGal0AR":219777191525,"tjDrOqaQQt32":833194580475,"KlhPECFJlMwH":459117879444,"yP2GOGNTt7Fj":69096473522,"kz3hsX1OqFyo":544226459802,"b1Scpc20lCML":266317875896,"jpG7f8f0pjdE":961290607504,"YRPjAGsIICNM":499877500240,"pDdKtgwNuj6R":776928500565,"dRivsxuhZBnr":380736822539,"5xq5QXfbT262":820868642397,"eKLNWbkD1PyV":418529457315,"5uaI0r03Juqq":700995358942,"Vg2uJCy1tx5S":59219729020,"UwUALr7UgcGo":473957790524,"0rI9pVzQesrZ":244589404309,"z5AgHlKrpf0w":502105025527,"4oeA8qOUYpXD":26540198800,"aYAgnovSHDC7":797703978462,"yYUs12aZmKo9":902119148699,"cxHuWTEF7Oea":712174656892,"ZzjSUCrWVRsg":186255347026,"miTyEDSwL1cQ":219447542616,"SjZq5mRbVpF7":559235447685,"wuulRFEK2ZQt":331084139169,"vEXxK6X9Ls56":993573610717,"SllrdRyXxbZR":912875478195,"SsXrdEruX4V7":632811824537,"tk0LV6yms9GJ":655219914964,"JB2RlFpY18Cd":920583828398,"g0MGukR32tkN":267758633290,"UiC6a6I81KER":244851609396,"LuSptPOHdcrq":918874341198,"EJKU90keRpuT":269081263956,"TW22yxnBD9Lu":626217053733,"uNXvX8ww7Gqd":152680787158,"5U1c7GdfNCpm":526338978727,"gmRaor7xPw7p":788030808544,"Ev0kwHHpqwBY":337052861306,"hXVOtglDRWvY":619825253527,"fIItfYIGQbBd":230460209126,"I5nFcrfmQV7S":476264285306,"EkOJjO372DuN":588100380952,"JGOO2nMAyPhH":251694240820,"cpfK2mVtfsJC":617586512981,"O2uVO0oTZ2tG":1750218460,"mXcPoxk6lldu":553921431629,"Rh1eDkgoMTRm":909155694008,"qwM5xD8jf3Ab":380095911076,"RQHT4jPlUtD5":249220067846,"C9fex1wG5F8l":344578656143,"SyRYNlIB3Cx8":946525056486,"0edeFUPtuvoJ":767834311024,"tI2MQ9ib6lbT":362173452079,"i9ZwLMDRfGhC":805017593245,"KCxXxOwQdzhy":677672206913,"yQ8ZrlxnsWtj":70637717809,"4UfJqeqRJjKT":955217457431,"tSOlGpIstVBO":515381992947,"gu7SSFXZxCz2":287871242011,"FRIJzoNfF0KN":968093730,"vPyjbGbyGfEV":896284870574,"VbcyIS2SzSBH":291499992641,"L32sV8lIZHgc":821406303738,"HsYb7ifdf0tc":567093959222,"h2IHkNiXeKaP":98134374928,"kzD4SjCI9XU4":283369545474,"XiCJqVFMvG8N":646642964369,"O2XJXkqIpnW4":642494983766,"b0z3T3LjNykL":850112976983,"VzwQbYN5VMcu":897626496879,"x5D8qPAGAjhU":966377864905,"vNr2ByZI1iUJ":453879072759,"aqoVpDlD5bPQ":158856549453,"H8Mi8TgVWEFm":648072854941,"zrpMl6QWyo6c":354890039968,"K5BqRfwfJ6Yy":686089492199,"dSKizoPl7rrZ":36441291483,"NoztKbexfkjB":379938735684,"C5hxIQKgYDto":689809552324,"c3hGTb4nzyFU":798703867930,"jyXzpfpBlk3T":453953021012,"xVC9nmid90sG":36759189366,"nFgCu2JdXHD4":25869298915,"Eazuz531menx":701068582077,"tyXPdvDSNwTD":784942077945,"b7Ei6uzsuq25":570313887803,"LRFVWVdiBTNz":246373461739,"TdD8eyW6OvLU":97176606021,"K83JXTCynBzS":208639520127,"05YrJdEmIdV5":248814915888,"6R6mx8G2lNpK":811565286161,"b0B9mUFWPoHn":115937895087,"essBJQacq5ju":256510450298,"F8rag0fhFMyN":897905032499,"2t2vLBNxXaDP":563112372025,"yo2f7hJ9rw3M":989269346675,"GpW7CII6IcdS":511993045938,"3Bya6aDNbBvO":558282786179,"6uGW9iorIpzg":806238972119,"tkbGKmk1nw2e":841108077791,"fCduYJKarQkb":341124949610,"ykyo4a8viFRC":642581482348,"npLi9Fxl0HrT":342409994892,"Y5PqwmCUqhHZ":266082397456,"1VANsPgnfPX9":214108531921,"WictEdWpoEmW":838110343232,"cYeKt5H3Er2o":720775142838,"QblvPA2wT4tB":948948742493,"V6Gg68rtd7Bf":536071802555,"lcML7AJx6XMR":874272907615,"DT9oGTZAeEep":487104999744,"UR3ZkrUiatOR":604117454677,"7y1cDZVYzKZX":296495978138,"beLgc9EKlj0X":521177753775,"yuYeKtuw7jtf":585916799474,"qmGra14D6N2k":667598779169,"rTdkDEh4Rb6c":116466636665,"W6dLLajTzcBL":382746235226,"8qjj9ZKpLevy":694226383253,"3ywvh5p4qdMg":744516740155,"uhmF9ufMfjUK":914699699690,"bJjXl2ZoJ5qw":290655890670,"IlLtzzpeayiY":373414945574,"ng5IFy8uKHqb":514784256628,"3ixahQURDjqu":659827993904,"V8LZKHaS4eF2":134781501438,"d8zjy6EvIvPO":869401684831,"c4udy6rYs0np":878428330067,"jKZhbiTIDhpt":618408923168,"FAipu6mMxLLD":299040465420,"1mkouISzx3xz":95949070445,"VElHsebelAyX":251039247306,"p2LK1cmE2onW":141124010443,"U10wregGgcnE":574783974708,"uTdE1k3uwwqB":361965977797,"ThxbuK3NL22K":750594719583,"JzJA8fMegwv1":808456524884,"QfkDFUXIH9vd":268273825143,"cSt1FIU5Glta":989187339433,"DS0rS90nUCxN":37206655362,"lZ6G4mns1lP4":522805053809,"EUERCcq1lziH":477376219702,"tPlG9D2ZRzSI":298402459995,"8c3kPrhjnXIK":405270533854,"Ke4wf8IoB0ee":454093059531,"vEH7aQsMjOQ5":943130281887,"ZBplmEv0KyQn":608231415301,"kjTpj7QpndaU":775578534812,"NkBXXvvtqsv4":835037992386,"Aa4H5CBq4vUy":781585620070,"Zqq2R4QBrhQj":447850139797,"40KPyW3LEfSn":929297881565,"q5K0BzhFLKRu":390259056932,"HskPcvfj3xMb":76449996430,"CpQiuSXmV5vl":35647120646,"yu3mnnaY2PJx":563668894898,"0PCMO3Hc3TU0":160911332550,"FELxGgBIYo9J":85423751012,"3rR3vCB4y7hk":375460499389,"oRjkySX8zfpv":588266588323,"gScvfbT5s3V3":351640656898,"dpj6F27IqjVx":750880489246,"BcTh17R2VQVw":195570671963,"KgISlKdBwGXz":973714835672,"D0qxetejodBX":133730620863,"aoPWEgPAfKun":353381913400,"OjKG44DY9UwI":143755619136,"72G6Bt31qobc":142136401333,"VrEKMJ8mpCTY":51898478704,"jFdPeG4ar1fQ":147810053473,"y2ZCPyOladfr":923666152760,"9FzaOEe2QMbb":477383733083,"F7aWoTps2WLh":559021627710,"bLLSfcrjMCof":891552615715,"gIZ6ULFZ7ZSl":264197653921,"hBSLNJs1Oho9":303849445191,"NyFJrmV9VpZD":144707701889,"vlgaBaqkSQIe":592676714029,"w89GIkqrNtvd":758881070026,"unRalxKeOc7b":385952814113,"cF2IKCbsyFWm":152269444979,"q0k5XvUNqSps":726746611978,"CxDvtsRcUp6d":197606454252,"LhZC2s4Z6ZR9":747978034822,"NnBQ1sXcxHwT":501764967585,"JybzSQ8kEzoG":443328533271,"RGoUXFIFrhpM":192809613132,"DNfYLdIArhgy":705995054982,"SWtAVhH5Rj2t":314705947105,"UteVZ42BtVX4":102581312210,"ofQwfR6ac1zj":324398686782,"qIE0At8sKC4p":409418278623,"x5xUVWJ1sIl8":876847045637,"oW5EQhCWm8GG":870141163240,"3xKoQR99NqPQ":203664034784,"R9gGbwVdaYej":394952060341,"AaHhsQr3VmtB":272039584935,"oOYkFJ9jnbzd":767476712428,"J6Wi75jFU8Kh":329430512158,"6QKsfvTGAjwt":928165666266,"8NlFnb5H4IV2":167242493739,"Ajp4vvv1FqPr":92606253784,"IjaBrCl6ZAfT":501646514588,"vXNMs0JwaBfV":142695384988,"U6LU38U832RV":233977623975,"iTebO2m3dbbI":464146944049,"YSQqhSWhgt4P":384754754627,"OwDUytg74yBY":19862095250,"jqJQzDOHwtUe":446279331274,"u5vGlTgZaoUc":903590047466,"pmdXIT09aKDd":845294220466,"1HHk7PviUvEK":836024209622,"kVrOqQ35jHRo":463366308523,"IP2Rj7uuhVyY":602036564959,"Ft8ib8qAjCDm":968323413897,"N2Trh7M0pNG8":899683305707,"fiGgZogbZrPG":389497410260,"bIOODN0GFsem":166748830794,"kmolwH84M1Wv":383616881121,"NISwAPmCelda":49048460641,"a2dQhdqfvWZh":162943681436,"10QQUMGdgKm7":14535555060,"e9ddO7CFCfvu":931997693774,"tjMIjLz1boOd":17043508800,"65q7IA0ZG7P5":826867542170,"jNRtsXJS8vyp":36608234729,"JcZLO9C1Wtyp":290327621194,"uoo4ykVm5krh":637765067991,"alM2oy6m28qM":458622425404,"40bLpTEB9slm":565507698331,"vewuEXRWrZ8i":327631421956,"Kp8XoGzGwBSa":437252970696,"bCxF0YzvP4jU":946785675991,"cCvKaVk2BMBG":954305369183,"OZLRCMpODCtc":212133355170,"ojpw0fYEe2c2":254597500295,"x0aUjnYJxNZJ":25080221515,"DawnrIMgjkIn":23392417596,"ViLGhhDsHiDD":555811280831,"rEYQMS9j3Bn3":298678618972,"i6wnSm8jPWHf":682715604679,"gU5yVCQ5xIaO":293673141625,"MEsnpmZVpQ9z":116085572480,"UkupcubMcuqV":134122505316,"MUNqV0lBuVak":772846205389,"n5wytADMvVgw":341387336896,"haQbCBYetHVy":322536525247,"oS4eWgsSHMnX":748843855229,"PHtQe3qwA9SC":493186190993,"Ob94lXyjBkrG":134948158411,"oPU9xCOHb8vG":132557999493,"XUgl49RwhlNN":60602932988,"Pmuh1aS0fSkB":337425612842,"s17wWQS67GPZ":580148967619,"wIKJxdzjUtCf":629470610733,"40DiU7DC5kG5":75195107594,"nIdYCbBysWuA":449877016742,"0gvtdDQzO99a":351329273227,"v8UOvrOj7Dlc":684016243621,"e9f5vbr5Lo9S":848087615087,"ZNO7iLHkhtnU":290851188829,"CmxcIbRtViR1":146927855028,"7iRhd07aoBLv":102142517988,"jxA4LNsQTqlo":413968991903,"qeMvxAWuAfXp":23506548039,"OsuDvG7MyAzn":869791002464,"rxNE9Y61XO0v":368150031729,"SxaAdiLZaU7c":973117842517,"lLTlzMgUIIWJ":867963946703,"C8z2UoT6g7GD":815135946861,"RBcgHR6doLln":739378162287,"R8Mv2zfPkw0A":658693567081,"fF8p65GbFy8T":483153220269,"2hbbcS8RO9QQ":589738766386,"9xUujcL7QtiI":951513371397,"114FF7lwuyKN":631894720000,"hoewOgC2538p":426106297879,"MB5ZLUePLDWR":293853270962,"VQH5JfHQnnJQ":63089771300,"SrekXsQxWk4c":152724998376,"7umsN4a2xGOS":769486737015,"Wy3HX1FPhKgO":661175611999,"KR7kHWU7kTum":572154734077,"pKhfLI51ccYs":623631947854,"J6MzjslkIjHe":709419938253,"eCGKVZihprFO":627196067157,"Sj8fFdTWW1zX":119958797370,"uIXgE5B0RHAv":425226537844,"r805trAM2jIF":565482637723,"gisj5Pcog9Gs":661607766356,"MhTbnlNSQftR":898731241123,"G1edCf0mztRk":529927753043,"jFQEWZGF6rwv":134786580057,"MgDbnGtLscOG":174644835213,"jpBjnTKFdQkt":590612787417,"tvGtSezv2jVY":275383489302,"01CwL4oRLPpa":490057940232,"TuIRD00FaaBc":591624108423,"gqSkL0rNbEJf":184578604005,"Z78wDDAGE8hk":998166537381,"553mVzLR8mxa":626314763182,"XzKm8nDWpnZb":353037703844,"D2pIr3HUlkxH":415944621297,"ZrzHsxen6Yzk":478722081995,"SJR2nedy50Vy":976449871583,"DYlOiCSkQAtM":445188546396,"m5sB9io9H4fL":82541345430,"Ct5M3HjGKNha":545871924164,"F92z2FCFr9lD":352896259037,"arC9kbiaEItI":972539445902,"bHQPbVRePnno":148544929313,"vtCapV1WLlG0":92253897990,"mJEesoAmLSNY":674117054233,"micC0vbwuxBo":457129925077,"T0m2JUGXXtVz":540973647213,"D9bZqXM75lRG":412388822323,"5byvbOxeoq7v":347908041507,"pNnQpHY3aueK":950939117676,"d1026Sp28twP":11330528862,"2Z3XNq4CkXY0":197662665212,"zlNlENuKCVtL":528281006266,"xs5hhDEpja3n":632716980846,"YA3fFhKY6K3s":739372575399,"g0Ctn0bm9cUx":997893326420,"6ZCucImjtPYR":173459680994,"n879A38OsQeV":305178902037,"qPiaaAc0DM1Z":723515424565,"y1pTF7IeYKEC":479640762167,"eTHlKyHwbKcQ":893830009190,"G3s9pSA0SKdY":681388451514,"Jr65i8vvasV6":933020228573,"m58KgKPX3wzo":567209840694,"ajO1kjwGNqC1":659467005962,"9ibHgttfpOFr":46915675400,"0IKZK4oXbu1q":613474689395,"XP9DumVSSB3N":442686532448,"7ogFNmEFxcOx":958836152942,"hfQfXEakm9N3":305977898003,"dWkuGHXggrHV":421157037388,"cie8zwX4Fg75":203707941557,"4hbEv2iltLMd":775119726088,"XOarrObakEAj":813457009010,"SAWrPV0IYz4s":351900277943,"I6KrcF078waE":119942255910,"RBMJHPa8JVbc":95345787040,"KiEiVYC0H6UJ":547982730603,"AQ8VRsyZbGmD":262093085453,"HYDOu2sbkxwF":439671867194,"PLTRxGlsBwna":638332773511,"qGiO7qNhJRJz":633769328601,"2Wv2x0XtH1ng":486902643204,"KNG7igvlg5ID":271519854555,"BLirUwXG6met":946861314385,"OWdUIMTPU1r7":850147602624,"RchqOssFILE4":445390558803,"HL8lUfmcwbAg":254982133137,"yUI5cSYPWLdj":836294825846,"k9dQHhsqKls2":744408887811,"xWvvKsKnawo7":771889691116,"isTGn2aaRYED":724819603639,"NFgh4gZ9WaxK":258547160914,"6FjLp0KEI4kE":561971483862,"VGnBHe2BBUp1":994288821815,"I8t1CkRcnd81":881200833587,"XVLSIP9SPUf2":509193022292,"MLolQRVO1mOd":156762904718,"qbwmbRnXOqr4":875416844196,"EFYdnWtjmpdm":568597733574,"aP0QzvaqB5gK":147685862653,"EubmiqrDEMiT":573294421772,"Fu6MSvYMeb6D":83999291728,"2DsBisTo0mZu":263816532258,"P9oevPkPcqvh":216289412803,"W1hOysSndDkF":653479478262,"Z5PYv8R8qh7a":520505551428,"xT2cM0Z1gbzF":345981644201,"lFbfiyWkXqNz":172245347272,"d2mqRnmdB1eG":846182314015,"hafPnjXk0tjR":375263646902,"RTgihYh2cJ0L":185046097594,"WhrVXV9IMXhw":94257440817,"hwHOKm3JvSrc":335870964326,"ZWSImVdbUemF":108031522628,"34YslIBiSSFy":461084634108,"66t9BxvUmHrm":490187225238,"NXodHfjrDTxf":792339710694,"QgHphMD3WTft":175033524751,"DpSiuUDyBjak":989194416703,"Ati6jAEmQeZK":855972501068,"0UqJz5iEbB25":622942112965,"pAZpFsKDTYaR":815483470344,"QpXzuhjuUQ59":376938373327,"HV8Jfzz289uI":238946889941,"UE96zZVOrmFo":24855215621,"Fa8Wc0NM4TVY":667070494007,"Vpkj5OuWrc6X":490097588430,"gxZ0V53U0zEY":839881797198,"1Nx3phDFjtCC":274017545769,"fmDgfedEKuN8":905018299924,"OsLgCvRYEYF2":111934308415,"afjorCpicNEc":177448188953,"mmkQEwixJ4GZ":887926016693,"u4KoBIkQ57fq":374206592922,"sYoPckEE5gq5":300207368633,"hjQlOpFl1D8t":193649454560,"sPkIvdsHHPKX":247575087707,"KXMXkpWGwF0R":931847490031,"HVPjCMLdFDhu":113511322115,"1eZudS8RMBoX":249132664814,"vzTEYp0NYcxy":604776966905,"FBz3b6yeFAar":871164335309,"5ea4Gp52X6Z6":740794377155,"6FhvLD5QiOXN":654737269899,"qro9WyVh3FDt":564466272739,"GhkOkhQ1k6hL":143873811351,"muZHa9T8Bam5":568515704291,"oO5mKBKaVSvT":1108951918,"zu8ijHj6uCbo":171077642794,"2jNRnTPl68Kt":911479157942,"lxybqPGASMpy":70360252016,"ZxveS02Nlj3G":731473262566,"ekoh9dGFAV00":213268248863,"KdY5eYdF8baU":896409809743,"3VsxuhMdB3uU":689939569554,"qvHUNhHHVD0L":273551937970,"tKLnwVSGyPl1":872662300793,"z34gvo0AqvLo":167665142065,"U8jNyIupJJr8":995837958252,"WGrW46U3lckB":595626443250,"zbvrKQAJzsb7":823362156379,"lAIb6EcOHb6e":682292650788,"RXg7XcVh7gF9":250315392428,"6TfqkZuFpUzj":331983784724,"RHXmYtcxhX6K":699881340071,"ST1P0ntLcCFF":763143790073,"JuXNTJfzjdfA":983578110677,"SDrfjXo5us6m":794885306300,"w7vph0YeyDfq":723959830369,"eERO0T63vzau":874377954159,"lpisPWFnD2Mt":424400091937,"Bx7J7SZVrPBA":320958950604,"AEWaZidbInXH":4755743909,"pZCuhITgj1Gs":717953966288,"MZEEcCkupk6s":120869296733,"eoF82pusV2N1":648734301708,"rPZxfoYrZUBg":453488749277,"zc66CZXIe0Zv":966541912265,"Uy9YXYti8SQh":99113808228,"4ZJICAoT6gge":377932587185,"NH276VsE8m3F":159226155724,"AD7kwf9F2gF2":666272677396,"b8m9uOrs5OXZ":863389136379,"V9iK3bWh4ZKO":201956577802,"NDNQu45SbEaA":880964803074,"qfPA9TYkT4Mt":151326999086,"TKXyLctjlXMn":829179529543,"lM81TRrowMmp":650979656605,"KgRM0AwATL8q":813409976378,"UrpEN8UJu5ZI":336695932340,"9PMXtyeL5tcF":284991135103,"VM3B52TyKdtn":495231185616,"8udSYGB6L4sr":862594513391,"CltErV6JjR7W":352350312172,"cN0NyZeYLyjF":623721940764,"oiAQo2bhLro7":466140883625,"3acRqwiBByVr":743642209813,"LdlEmcyK5frD":662761205983,"0rkseRUCV1Bn":501741896111,"7nZSAkhqF4Pl":389520841278,"HdgIWAfWEMXX":904771012462,"SQROGy8zwSvR":630952529026,"a5ElrdEaBTyE":943902321530,"HSuu9mTEUP93":139102621421,"su3JYn3bRVbz":898134647916,"kZLka40Ua0Nh":504588423960,"QecbKY7RS5x2":9184344085,"CuBRTeKztDNC":517863030259,"IJQT2og9JTkI":79116176957,"AwyRxwpEfVF6":288855564377,"lN7SQ8UQEfwr":72572029823,"dCrEHMo3XpTG":757708204705,"aCHhdxQLeRdU":410085968571,"4JywnbrMyB5t":900293620437,"Y21B12uffHTi":998894019950,"Cgocu0FylY7M":267989794425,"YcDaX4m4ExcK":67198804005,"neRnewrYrTmB":125949938115,"gcw7iiJhTgSI":290787015917,"rYRXdYRU4DbG":561788019557,"HgJoYPTIGrH8":251457134181,"HkM5tjJGMlhC":91951517655,"DSSjWtZtSlsB":193709426181,"c4EsdAGPVwcR":565031822593,"iZZkjx5dvJMl":901954190798,"bIR9ohmESPNK":352837655066,"ktyuTxkrgdDa":373346973701,"YNIHKZsAfVpl":834727320809,"BEqXaepAziBl":527645084603,"zATV263KrL2o":277981032781,"xJqVRWDa8y4w":542946435824,"vNCmHJX5vjDU":765958943275,"sbKo9Zi9jsDu":920561495102,"hzJMCj6MZj7b":897383255197,"8nuSmvFpXZpA":279467902397,"3d7zd7Rnbxyc":222015443503,"exMu3tZ5GZPN":703171553052,"lphp7F9DhlCq":199627547463,"TA63yTaqMoZY":825672401986,"2eL8YZpmLJHO":504797682098,"sM2HbUcrPxRQ":974999459738,"2i4Jaqogn9b0":404609584283,"stYc2JdNafpL":859294323068,"zv4ZEihVuCCs":17803181069,"E34J1oHoIxSc":414889610152,"JdRos6IygIrr":536826135954,"UzRrTGehW5rA":957508958741,"HYjcENoiMPKg":532409967632,"S89D0wFncpKq":732731910092,"4T0d7mSFa8qU":402378998073,"jYeqtobetI4j":196369210944,"Nve4zqI1nyDX":922842718977,"IacyN7JijZrI":986038513379,"Mm7IfIwAkDqX":625870747790,"AZvazJnK06Eu":299997354077,"sAhRmmdXjTAH":556102915605,"fnerohMkB0Ey":212538397511,"X8TkkXiwtjqM":512930469951,"xiGfNM9GOe8j":826917456502,"dqs6h1diloOe":985238845307,"UBAU6dWzRBC9":83880913706,"5Uh9jVDb6but":940681349622,"dBE423A3X5PY":43318644018,"jA4vwCRFpDjK":903009450952,"bOFajPDYc3EC":271072674628,"oMFmV0336Xw4":829748379671,"mfRkmBH1l2Im":588255306562,"Fwq4OU0X89aq":763750016871,"VaUnpWJio4aO":890791857246,"MDBvBttlQg0y":582482590854,"d51ebHyEBokD":537327853735,"f2oBZEFAXVO0":201521405267,"I5wY9jLNV8k2":822221094873,"sWlzt8ZGf6uK":936034031834,"Hu6TZua0DDRI":269710287890,"jhPiwGIbsapF":230177044942,"J30DQTYVeVaM":151261051843,"BMT9CCbMeIe8":210995215661,"w29zm0JIldlj":459256668722,"DqxYQRUkbQyx":992394387730,"K74cW4Cj6fV8":727275929688,"ib7FntS54Eav":968640861593,"00EgAXUOEFQv":557472260488,"QD9aBLCLj7BC":534206153668,"E6PoOxIFqJAH":784900653774,"4bIjojchozH8":796030452435,"IkZx8PSrt7US":568634927992,"SXq76ahzW6jt":794908843626,"V3dhWFdxWNYD":252421834301,"VTA1tWkLMyFf":552933110978,"wVvfpy3uWLbT":187430619607,"hipUhd5CVok1":199496410293,"afVICeaXxKgd":698051830534,"icouhyughgif":655749232843,"pWV76C7aYslJ":787963965808,"U9UIyfJ2Ta5M":269172785374,"vYhaZYCLTehA":723521038943,"8wVI7ulV0Ll0":704558446555,"Af0y8yQ4cLjE":988996964772,"An9SBqzf0y5E":101088195268,"wqGiPhWCLT0C":836103606014,"4dhoqhX8xzsv":269306685697,"her3TFm7fie5":388343070067,"Dsu3zvCAnDA5":238604207415,"oTG6VJgmdA8Q":678407208905,"BbnMPhk9zZzt":719067111108,"DKqEev2cAbuv":335910725240,"liOPZvBqC6K2":252590386874,"46OlUDWdwCtr":897975170772,"Tjj9i74Bjx5V":715184567997,"u0WRJaUrmlU5":267051339469,"7eaYW7VJxpIg":741335262354,"CXhKyBjDVmlv":573077451934,"X3c0pMyblFKo":329718737970,"p9TRfODBMKsJ":269690606739,"eaiaruGgUD0Z":342323684289,"8YN8T8mxQVOX":593809589827,"P3SxiDgLutez":91571829989,"BjWMLBObPo2W":762530107002,"QVAv3w0AF0ts":360134770223,"rDD7mdtdFtOa":391291392802,"kgIM7d4VVGcT":471636874018,"jL0sHZrz6QLH":397837767621,"fFHGnBtEXmJh":658210684840,"2c6yD06mTA1P":550918248290,"tJtDRSW4tR2C":315600751609,"Kv0zNVWHQEkg":481883716389,"FKXHW0OFLUkX":100680872368,"B0P3CoECJT5p":924065169100,"9h7UWn2PfOCC":15029864322,"WM9ZjSkVx0ff":717769865237,"TNHpvxohT1J2":517526862802,"p8IasJcLN0mw":817045048514,"FqR14ZVsy6mm":776095380664,"I1AkgEzy2BPL":94548344864,"CjFU1ahAdjnQ":862187677621,"bjcPAZDwJ0zR":877617962048,"NrzZRFlNbKTs":229798020251,"0cXJJKZAJuoN":556259811886,"wk22Z8CA4hir":317003846417,"fkHD4PPjI5NZ":498334875589,"ROb1NfFbPMZg":599010868312,"DzRi97cRclut":147658687348,"60vxwLyLzGu4":976678229906,"94LhJXLWLOcp":885453034636,"s0deAgvSZoer":667634047415,"iDpGfGIOnmCG":967817195814,"EkNTX6zsQPVb":542006890998,"HgJEFhTdisxL":306345813116,"LZdzxQpdP7CW":696779127139,"8zsN7Z47Re9i":679072948041,"9RVJJC2dEgDf":952217921039,"197kra9nSwFM":246944171237,"vCRoafoew9No":377350899473,"1gA0RHMAVwOH":134258602002,"8icOd3ywVarX":824876308261,"m3BNx1bDgVtA":393763315545,"LSnE5DAjjqnB":326228471240,"VuVHSxLstJtS":353372569504,"bIRP0z0DT91q":894759959089,"RzeSQQvyJbSR":691409234181,"zcdOrX1nqtrL":180783041431,"ugbfXyy0aIcn":531570046116,"SdpD33pqRd7Z":364084440182,"0uXLIc0cpb2C":249544886220,"IxCq8lsysWvl":524281128487,"EjMhhftaZmLA":905017470452,"fMMfMxRXgdbk":131575703186,"3TEqchBjltdf":167965612518,"MqTd5JcWucm5":902169065806,"IxhtUwVvRC6D":876712006700,"55ESnYaii3Ob":916716406443,"oaydF1PtUNjO":227318960983,"S9DUorplATJI":50929688555,"21BRR9d8rHHW":888024327102,"2vYcfGHvlVlY":28058693568,"WFTLIG9BxCgH":590133744791,"ZmXkA2Z3CDCT":711734651622,"fKq09NFkHxNU":129092070989,"ZdBVA2s5Gf4w":468785678432,"cy5riQEYUnfP":745684682211,"3XvzulTtlTzh":18020265271,"Sa9VXWGnTl0c":9351205891,"52HAzioo6Uh8":913651036803,"BPdRGMQDDp27":241696463572,"lo4XmHQZ7W1k":47214928691,"lnEd1P9v5fMF":927606422834,"zPTjqwxDCSsf":814728075548,"xzxfDYDlaAR6":340671601877,"HDr2JdCrnwfu":310960750078,"OWjxPjPNh0nN":531288687348,"NfE2Mlaj4pvs":37111151652,"PcYqVVI2W96r":346862736848,"gDCLsBYEEJXS":601539342440,"TbiU61CO7eud":486457632053,"4bTL32LdV0R2":723937358386,"ilQWKFmffrwr":97857633395,"mEh7pbQSKzSn":880896802121,"B5dYLWDzes8f":294688971404,"mqwbJuSRCxnU":581591961904,"5Ov5k7ORRPqi":695577064621,"sLqiK6ASFrzB":650661417186,"AWtzMPEMIfi0":543184286528,"sr3ADGGNG4yT":165572790646,"Npb4cDbzKe5T":606052901770,"j1pcHPKD7Wi2":770807829174,"D3jhon15xfj7":537637213731,"urO52puttbaP":52177906603,"EltyliC3sISF":244523299615,"iNAylTUHGCH5":139805811643,"DNuvHr3MObiD":800189541309,"T60qD0lKSLKK":651725454800,"PqPSNckhdGGo":965704999892,"6CaRjlxiJFhp":105258658309,"W9CvKBOvq1iP":959235144674,"kUo8zO0RoG9y":992759158873,"NlQVbd9ymnYk":648352229590,"LW2X3peCgOba":486158425077,"j5HBRMSPYOMN":539617594066,"iygGRXenvX9Q":813516753728,"LdtINH2bD5Ax":583824950203,"2mLpINmQ4jt2":603414192423,"XFXUJ1BfJ1NG":633137219554,"YkTMdevQwdwf":696901680420,"tPKOHFPBLD1V":643107070293,"SONRkxPK9JAS":209925339619,"TxSYICOEEKXA":713346882481,"LQGu7aRBxHAD":588516072521,"IfhOITzqj8w9":454614331029,"v0JQHA06zCVp":898567257488,"QlxUejTVLSNe":771785672947,"7k3YMBEGQNlp":465003915722,"68VW3e96ZW70":198428996722,"t3vmNUamz0Bi":23081033272,"WAAb3DnQJawE":133177928094,"sdeUBLX9wZLz":396828877375,"rE0gvCd3Ir8A":972004527534,"Zczu6Q2N7rgK":945015790182,"hOqDgUMZ6viU":646724875690,"9ud8xYjKuc55":66018893679,"bv9QJA5IO83K":123763270351,"Re4XFxb0WLzi":110045266064,"EsMcwUOZOEZJ":223335515606,"rmj3mRBwrKER":290550487463,"eyzDhue1kdQJ":98021482009,"YdIOJqahJ6KA":248095154058,"XICXnfALfwwk":538067644796,"CtjkZY8Rj8gG":804771417545,"3TvBwiBU9d9F":650777630115,"FF69sGuYezvA":493115579831,"58WYXuzp4Laq":589703089307,"NfW5W1ax6Zwa":813054973620,"HCC1GOlbFliA":634272160490,"nR98KkO37MZF":992806513939,"aFfyEiIG7rue":266906032835,"dFXPOgsC7e05":512598393189,"Sd0KmAw642Ql":735359970441,"LaiXjPmU4Krb":521132350841,"Ev2o5DDgnehz":729953806913,"A6bSHS0yXYKE":595565808317,"FcvzG6QtunoG":496278595398,"hayNmQ4ScaDm":997612635338,"zzQEZ6H32zpg":190539105988,"aghmDlD2yqFw":791525359221,"nR7kEiVvetN4":228346981135,"Fb1IjhiDkxpH":491075461138,"7OngkeSHflaO":597447638312,"Uuu1b1XY8tBo":722103130119,"5jvdEdtpOLFR":973261455180,"Bi5rfX0CcsS5":699132828035,"VY0dbwEh2MqP":936402330806,"xyfIvxS2XwqR":220931772210,"AZupN8noyKUs":192876874495,"yK0oHyVkA2wz":217721056096,"bElCsdpbxnjZ":858514680247,"UoNDNDBMUQk8":74288025939,"W1KHoH42wmtF":614096860182,"4dNf0rJ6EhCw":471296888186,"lFOOHUmzyMaI":776213571011,"HoLWbURpNnTa":835541238379,"VxlQwp7SGdDn":67158046426,"R0xuIa9gb5Ve":837761737302,"Jlh4EwIY0elc":521728757926,"nKbxLKACeEQm":114288973441,"qi4LXMr0Mswb":552734988685,"8wVS0iV0bSgF":597375714245,"MEkv1FQnK4RY":559881694128,"l0Hu4onMnoaw":483271074523,"cLkF7FbJ33Rv":9333433345,"ajUoGy4mitv0":859158269978,"LerjzTp6eEmI":74562378924,"lw6QX9kWBofE":858470015060,"G7DKsnBTEWn2":245757011296,"haLW1AluFmmk":274517551695,"P128JEZikIDg":811270042540,"FfuJAG5PzM3h":894301894662,"lRrXDVA5Mesz":711585034611,"5knnuuDW4Dii":979874580395,"kL035AX1PzcG":325621034200,"guJ7WbdXr4Y5":86747901126,"Cauj94q5HPUE":173904274691,"GdYkeCDOTXjP":107714791866,"2y5vYPDz5BxX":863569128268,"qwQeQaM6rfRZ":399416577916,"bZySIdBnR0uK":478308159803,"chadw6hszt87":27139336470,"twLMMWbZvdyv":165896207337,"UMk06rJgmbYt":76373033878,"WOimzboxvVrO":64779145092,"1VRkaiwYISTm":612264594596,"AzXsSAlBCzvC":524105180703,"7uKAACyfxc7f":778505578442,"ceYifI2Q3k40":505379979701,"R8DDJfjbWZsn":997935209312,"CPXQi2pCfVLB":517407579516,"L41tGKYlLeJu":93953505381,"ky7cF7X2GEfD":461447241673,"e7B1TshxLN4q":565793668334,"GfMJXtTpmPsh":166282737326,"vJqXaWDjTL6t":758115459594,"f2F4BW9N2Yli":359611321703,"7HmZxbC8b5VD":131575107081,"UHAr3zufWsVu":75841506034,"RqCLLkjDjTF2":749846453535,"RZS8T7islD9i":984164873819,"d09p9Sy9tIzt":273177212829,"RKS8V9XHrzCd":873819964914,"iUyIoEkVpU3t":91866042417,"oIWYTMTkGArm":911532065601,"ghsU4pj7iEUo":504034954082,"YU5UvnO5T9KW":239750231035,"jrrvvB7TCevM":296716270051,"ENWxWbpo8Nkg":675930650551,"FXCKmDR7Xx6a":993932352567,"TNOuwQI3LAwm":321433740830,"u4qUOvBrTqyZ":198971886209,"orJix2T2Zv7b":429907530718,"QEh9H0xmsfKS":987747404797,"MBwE97bV93iq":683630928588,"AkLOYHaBdxud":824885129752,"6uRwfz1KUr6I":344774414715,"iRmeNRoERziT":536074142363,"coM34qmcXlSu":33212720293,"p5JpP8tJbGa7":719100660846,"EoRcxE5VCtiJ":499017494383,"C0DipU8y6ASB":766314689812,"WZ1thNx4frZo":741710482076,"NVYRJy3ef049":598008109167,"XN5dgrgfliFH":995370104617,"hqKkFvoEH67N":92935099755,"gckG3D93yohC":620979721080,"xPbf1owHgAtk":492090776407,"uklbGbiCqFSq":244491381622,"IUgYXpGjaRXq":101969491071,"AiPpXMxhqKmf":657392094610,"u3B187bhpbWi":581066659136,"hXOicSk2kr0X":392209392434,"GdhxkQnMikBc":403170681574,"OmOjFOgLHAtc":796511663712,"dIXw93vGhLqd":709290266423,"kjhqUHlVV700":3046192106,"K7QlKGEMHczN":349356634,"Yz2lox2B0skr":766286922106,"RRIqW8e3vnO5":209288753308,"sdDp5ChGty64":170022109108,"TOcVhz37jDuI":775236726414,"cCMzGorlNifG":784122280354,"r3bUdi6eQc4W":181823545704,"TAlj9fxLf4iZ":217406192197,"82M4A0m16IX5":587492967589,"YSniPpK60CUI":807237635907,"Zjw3lfMsA8Ve":92757154721,"1zIUlcEaswfV":407663545583,"rIrEhmz2HZXR":680250042770,"rApYm5Ow84BW":172163159677,"S3roSPdJ8WIP":465776678882,"l2HuGUiUVKX9":494043748913,"R7qqgSb4q2na":737798888171,"K5zBx0zX18g0":222135489391,"l7DTyfMo5Wzc":18532842374,"Ww3bhdI5iPd6":276917369598,"mezq0e2e6KgN":426219943210,"Y0UgHxzwTB97":889545259722,"mxnGwUexAVgY":525594574180,"luPCbZC7aFFV":629078128465,"FmHi10fCKqaA":953381178949,"8ScumegXOI3F":329235464031,"5YLtmOk7ElrG":369090033381,"6MxfBbZM9IYI":310880240182,"u154APOLp30T":104877541824,"VnJLr2ysqqOS":937801727226,"JCXkodk2dSJc":133038902693,"w4qRLEy8YbYf":722146802723,"QOOJuUOS4m8h":326142692844,"2c8pcxGU3k8c":752923049524,"6GMaqwYmIPmL":823668029662,"0CceU33lZ43q":622545972615,"Hzb6gFYyY04Q":656892925044,"fx5mvjLqbs3K":822521885926,"JXstw9IPMNU6":235762760847,"iVa2EhTUB74X":455731166896,"7tCw8MeKIuwz":229639810503,"IWiwIudNxR7q":852006253073,"09tN5bfaGUmY":565741812842,"tM0VCE4TZqBW":115794996831,"yNprSPazPTlT":938769361105,"7fuNGxi74KUu":974604371147,"pjN0L62B7wsc":358971222706,"FwEAPKS92rTK":195751839031,"8GXGxEJ6G5bK":616183517502,"Etg3cOrmNNyo":817344843459,"zJv6vfametK2":400481749569,"dhdTvijlZYxT":764222849323,"QROuzKMdiNXn":702397683350,"u94SDviqn21g":940500729604,"4zR7TCEC88hl":650785598650,"TSicYQpSkK2k":431291724858,"NS5AziA9axYh":551285273040,"x4va0SIgHiMK":609281717925,"RpVe58stxrci":931329191317,"XtTalOSDZR5R":326223218065,"ZRiZ10O9whhu":685153904941,"e2pV3HglbSbX":945427103460,"ygi8cwtcMGJx":548506326084,"4UuzrXEIjcAr":122129756276,"M7taLm5nR1WZ":589707329200,"N09VXLwYu3bM":738642103751,"8lKgVyQW909f":715239126915,"6yhRZ7pLP7b8":838469165615,"WEFEyuPmwld2":86438159451,"mdG0RNB5qSZ3":313222155097,"RoyPH5uY6Xvy":896294802707,"493KXnyrCVJb":253867943422,"GJyVWkMla2bB":631452306897,"DrkSKIrdJ3sK":599493288176,"j7vim4Yp8Ick":938534427771,"htAEyc4t5Lag":916799102007,"PMkMUKSQmc2l":884625682236,"jK9ClXuukdCF":346694007081,"80nVvIQElOse":611550412803,"HEJSKZbeXrUe":857601975796,"5h9Mx37WN7Pl":930505971189,"G9KcvZ3CXrui":186719971638,"omIiNBxtsNLk":102787326400,"cRWP72ljrIx5":716236656832,"Bm1R5FQydyDy":661949929726,"77azto0ZzdlO":212827558883,"9qFBPROXlw4s":154311038733,"0LluLfEnTro7":406344858178,"71QEOFLGItRQ":438868977119,"Np8m026oEAsh":584610309337,"sQD0hFw36VCp":192381754701,"Xjry7jmougyr":406319056893,"svEFex6I96DK":159585565478,"2EF39chayMBp":170833755820,"OEoYH3d5pdIn":534622772943,"dUBGeNRNDS1A":479856827356,"DML9c2DK0jM1":65051754901,"MQUTVGdB2Fi2":209586926653,"RqqN52dOTp2a":432718234604,"z8GOvhE0Vk5C":746604224249,"itNK9snxDgTc":466640901467,"M6bDNfE4HPe4":103489366291,"SwIufXcwJB3a":696997819485,"91gS5hyRMw8e":622024692461,"Zbv6EPOnMSOx":618138322100,"xWrns8EEY88a":998605588721,"0sM0n9tNVdG9":854931476509,"VMLVXniaiim8":241367845761,"QuWkVPUbW9y0":841353011565,"U2BmrAHON8Q8":992297529530,"kRR7xOSbF9VQ":890978199394,"k5yEia7xf3A1":498046844545,"Ve6U2zGel3ls":497870522906,"w6VkbkewCLBZ":631672767108,"UDIxSrhFUuoa":197012717340,"OCVOGN6ReigY":493102233770,"3ZZKIVZJ9gpd":537905667753,"733TyzcV7Fpw":855793688312,"hnBzzu5VW6vY":711031159402,"RnQAcjjIWsqk":234321848888,"cxNOCJfCDXyz":227652258533,"Mf52ABCe5vxh":911123344874,"kQirn8Oudk1w":595196866403,"QWOb73eESKjB":755568939900,"FxBCb0F8ih3A":594904518940,"7Ly2EbtnRfLK":655778476759,"J67A8KzLrtN9":547559712351,"CxrWax5QcFOR":234861488522,"3EXLw4XK6yeY":434445603198,"YKz7jw04iO1u":931482104668,"57EYsdLaRaN8":659858063489,"dhAT5TaW6G3J":447537317375,"US15EIENMAN8":438207111961,"03INVg6lz3C5":272070760293,"jh9z3OsJILux":756676452484,"IZPzpQ0unL7e":325490656698,"9yD2DMhY7Fm9":166453371374,"zQD9pACMarZt":574636486125,"0BTSOcCxTcJF":656277760713,"QQZ9OCrumRsi":552406699277,"QfGTojSHRQCN":731698859222,"hFuTGcD6QQEm":686643933313,"Eh2ihNwBjXYN":253101285916,"U3ndc36nVHDP":786536559264,"LeoehXac59dD":202726189425,"PvXOua4lOwwn":141663836246,"q6LCe0y1Mkml":399225206216,"8DtdTgvo1U0G":669855967482,"KC7bJC4BTBre":471705506665,"fx6Xnj7fJLx2":214705103375,"bpFpmGQ9Vhus":883121233962,"trNohuxApof1":510091515517,"un58JuFtrIWW":348603196385,"JgniLlMnTAmh":796548304521,"6aMODPYJIzJW":173760144424,"VBYyX7zE6prl":971175410089,"OlGcsvxpOZXm":597297982692,"TGEcVA53TxPb":345299562403,"wu7mdXnGQfaz":480732245421,"d8fAN8R75Qq2":603320533922,"KjYHEvJCDZhc":87331810488,"4RZuSQd4fpqx":148242876866,"ryCSBHmpivEb":589526183874,"Ym4mDbtdiNsH":785745489887,"ijfaB9Ty3FDn":544636403328,"WKLITl9GEk63":924828593343,"iPFvuuYujIYi":63369641022,"pjwMGJbRDe9B":364132207139,"P57PGZdG4oDI":446209409749,"s0DFBn9jnXhQ":17223444614,"TqowsabSlcI4":883952044580,"bkFqKtvzz5nC":972734855978,"lBnXXM1zOhea":447942228010,"AiygWlVjEoZ1":937627047642,"RyrYnf6Vz3E6":707551334173,"0UkYZkzrZP5c":464799939419,"KoTtNhcHz6iI":468083413139,"tIaIugqELWXb":611074193450,"dDTIlRq71jdV":891939124874,"YHW5mjFfZXoH":404431724288,"2qJP6Yu55G5s":468493591481,"FTjVqimL2MPc":916078691906,"VDi7QaSOiFHI":605013663732,"DLrlB7hfqkzt":76073353535,"rLEftNuLZD9y":833378911436,"5wIscd7waJ4O":553206315220,"1yFNb96TdPBu":463616402597,"6S2Fr0DqueuA":776893825835,"FpMpvfENZC8W":980420741732,"QZwQHxiSUzzx":170558916227,"u4IyTjSI1Hma":313660911122,"5HZvy5K8xjMZ":658930101631,"1FcYCqEeBRO3":154116639689,"vBWSh0R3IzpI":402167598395,"rHYJm7tXj7Q1":962046166628,"MOcXMzzwtICN":397641337126,"vSILSwiMRfEO":103356893471,"29VHJfjqphpW":28792864021,"h6XRhIx3rsnA":922733430874,"nScR0M35TrgS":372073856272,"LhTWmKGNS8Yk":641641754955,"nYoJJJEbguuc":79774886753,"l3rTkHGC2OFo":157160212795,"ACYOuarsf5Fm":925379701699,"TMGUphwuijq3":835356395540,"fMhomVESEFXd":239787944296,"HDp6nCClOe5G":624014490613,"LOnf7r82a55j":467760350521,"6DTc150VQmAD":841550188291,"SjVzaJKWDUH3":767017073817,"qjHCij7GKdQi":773936266784,"CZAgM4c477qB":889559224697,"D214AMGIphdt":246116036265,"M461jKURUUrj":806829901684,"PaK3FF90w2Lh":346573931127,"zQAFFl4gtErJ":664560221976,"xtY8FwxMggng":537376817178,"Y9nSjl0kFgau":943094111617,"c51jCaNq014C":944639043732,"xXOuPZANc7Kq":304822209863,"bn46RtsBe5cM":714512157359,"m2Ye6AZA8c2W":87252129966,"bb4YKFr4j2pd":801275158944,"PoDm7howvBKs":867953913484,"VJ2DXS5giMGG":745770456084,"0gtTXlDRDpLw":248131598356,"6ya6uvXNxuoa":394424967256,"MaeMPLMxlbgC":749222402922,"tJ8P4qPdHZj3":766273085600,"7GuunHad35zo":710041351189,"Vag6hJr3tdgP":576947912613,"QXltnJgXqX67":712572183679,"dThgd2UigPnB":935591128427,"Kx73mqzqM5qs":230157756538,"POm7hhebPq4x":777637367315,"wImPcKU5G8UP":705597297547,"sn2h8IrfpHfK":374852776407,"JJbmzjAldeml":912587498874,"iE1CzXxjRJZ9":562282184668,"tSUFLwctGCjQ":276145228311,"NsCvj1Ownvl2":911121475705,"riefOatTu42o":667054159307,"kxAMLksUnUWN":346417145120,"7NbZ5PMzD2wp":763979830637,"ROx8Te7GE52z":838536705110,"OdYQZpOnuSgp":855837150461,"8SnTKQ3W1xIX":183010889966,"3yQHVAtspgSx":640865913617,"5GWQ5QyCOA89":800817439710,"lrk3bncN3oRb":990582414071,"hnkWeOnKnlAi":23460054857,"Ewi3QV3WbwxQ":354864456393,"RvGfbDxkMI75":775678001552,"6eU8U0OXJd5h":47283997233,"oxQX6iPc8hZX":13322073075,"TcL5FsQ36ZcM":307969253308,"7vgpVBXnyxoY":78063602360,"BK62Z1hjAppB":388810761925,"ih0Dc2KdalH5":141355249383,"6xMg4w1PY8uV":878212292880,"6ApOBhyIOclc":301754423338,"NRBkH2Tr6WkJ":349156342104,"iTXpDbx2eUtL":754523856929,"25eUKO7hdbHR":228680210915,"wUohux5gY6wJ":109792713109,"QfiZqIi1jA1A":208213024937,"AVAyXUQsY4aX":900890970576,"s6l0zRdr1iMr":713612365700,"ynhFdCaNjBc9":366514263375,"Ckkd0sBFhASJ":430398170302,"m0UcS9YWYucu":977676324667,"zz6DMiXYEOxS":770393328576,"yADBcMoSDGVy":15876788258,"UXlJRB4uYnE6":567729956050,"fGgJIAANkwpq":859127177443,"30kbjXQiNdDd":735027145585,"9XmZv1URnpSk":192957714635,"ALooFWDh46nN":878885183831,"C2kagCcIVvo9":200114204879,"1vXlRZ6Kctw3":763324010317,"JG66JYm9pDti":231879771031,"FWM1z7cXlWRA":146294353002,"9CwQfMbt4ead":440559659027,"Jc3zPHXMKElG":216815858656,"97EhoQYWPVOk":460612482564,"ImIArYdGfyhV":147039924460,"qaPVd9EVC680":240891934290,"biW7IQIxRwuT":83737760917,"ECYBbOcfMVuW":124344777457,"ITI5PsWZ579Y":321782114201,"UmJbILXLwnsH":93253337642,"eWfWFnvBdbZt":156883177671,"vktcrZaN7zRA":469108693377,"AhkIxotSN97U":970504756863,"pGMq9iH3XACv":103628590625,"jUjrnTUcnJZg":672531045209,"Ytxi1MVrJa0k":513681922667,"HFez0gdOB0fO":998569924764,"RsyVYSHxLn2p":555137040477,"q5w1SJ5JFyXJ":628164881625,"c5sSnA8qCt4Y":297103318046,"pOVPFbBTNd9l":441129175998,"qOs51sG5gm9g":621244264757,"rrt01bqbJizr":177826372293,"zSAyPYKSSX6w":280280284605,"BjHI8egKMCpn":393615140384,"flOix4MovttF":297653511454,"XrDP5F1avgq0":441150613877,"VEzOETiBRhXQ":588214576889,"FNI6aijDOWfC":606319011502,"EzUsJnZFdVSb":399603580239,"Ey1iRHf9AZ44":452828871483,"vb9Iahzl1xcE":237886518664,"1Qw1WSn2gVLG":34939168855,"opx9YuQz4wH6":54388423787,"YnV1Vn2uFGX0":667262936521,"t0ZjTnHC3pQn":244605742902,"UbvauWVr1aaN":86244914120,"VgbOae1VeB9g":856496825492,"DQmog9Lr7gA0":256361511170,"kfvqQdjJHxSh":194838771110,"ukpQN3p2cTWJ":273299025376,"13L0znUmFrah":888100576865,"oBgcYwDzACSS":89585435930,"GqvBFoKmATDl":713459111432,"IgnVInugkLsC":17710554282,"NyDgGTVlHBqh":58663943663,"R7L24iOqUuuK":350552970875,"A4IqEps2gJrq":120930154072,"4pma0M7Bbpti":84955963574,"lNduOjGWx4HX":657668956654,"T19Ab3R1yLhM":459307320083,"JG3ULAbScCY8":995247109151,"vDjHL5PW0bhv":58295453902,"5pUVV8c1mmi6":768562486631,"1cfJI1ns6rNK":280751107531,"1OBmLwyaRmJ7":419031701191,"hwvEdCyq9uxM":136143089935,"ahaxB1PUdg9p":696016589066,"6JkSkhPWNNDz":130672358525,"S6LDNPerTsa9":904837594098,"dsMdmO4yCDAe":166938275306,"Bb6tYxUuXYWG":339265760873,"d92VmwIeNo0u":460619534528,"YbOxaU8ppdiq":318593678797,"IkEqppwNzXlM":35464143862,"3LBWsDb208FN":146967134258,"Ll0YTvlj5q3l":448098826465,"Fmp6a86yZZi0":27300077522,"Yyt12lO0C6LJ":590382167259,"pVmfD0sApMpH":521618019042,"IFXvbptsIvwS":235967725770,"ncsD2p3YdIi8":409193416066,"hinnjKghLH3h":45390585507,"v2PBChadU8Ke":768321608646,"2Ytd1PTW7AzC":767388253947,"9iRmgaGCId1x":82684650869,"GpS0TZ5UPd25":219037728533,"P9l5MJ79JdVj":720312858670,"VqINo5kLhhnx":884454137410,"pB4z9VEIOexi":233076063369,"x2e4D0sws8iq":961092559082,"Ip4dnXIC6Tgt":458297410380,"k7QBaRxCUBp8":644942482023,"ZG4dU7V1zWRs":904361683880,"e6OrtudYPyhe":138680329664,"eGbVQLvqdAlX":675398827265,"vEu3pZRYN4sI":447877460064,"15eQDmAZRbcy":29743063530,"xmg16y5SSM8q":504840754984,"5a9aUm8Zz4TQ":188394116585,"fUfHmosqBoDv":281499478367,"UJeHN1dXrHxG":135809242779,"NxSG3fBnvMZG":633679725592,"KDk7y5JTXGFU":148701931037,"GYx1m2Or3wQq":322088594210,"pZkV3QbUhkVk":392549851229,"uwAGYwFlXVs9":165067698427,"Xvjf3L4ybgXz":509776379129,"daw5YyrNjdcn":522343017262,"BdNPWKPZ4Vm5":13889568319,"OVISmQzyY2Av":666663791509,"0Api1kBJPmE6":916539197441,"MnaWlHXwNey5":940526358811,"lvr53uQgti7j":620907666101,"4ebThHnah8G0":230933894910,"2DBxza8glzZN":167965670037,"jDBvUzI6N1XM":841746402013,"iuFmQp6xS3TJ":896077126237,"DULnfRE99X25":243565970048,"aaIvVBWv2iP9":325796702760,"PBJJzngFzZJi":736006110927,"90wKkKbX2d1B":736696238297,"CNl2pcumSXY5":613603698965,"A3wb9ma8oAGj":722954111670,"kK7kzT33nGdR":829789444244,"ROZ4Q8GSpDho":86605840437,"hYnAiTLlzH6S":390118556573,"lr8yjwt2cX7H":308909157154,"hHgVDzohgLay":679954014894,"i42gJGSm0LZP":596712540237,"4hf0sCFVyHK9":704125093267,"dadrVbdRQ1wR":741808479805,"0ceWloNUPS6F":234411389462,"DKKTUBCXHsp6":308928012742,"YRk7YBhOB9qU":639941508459,"QMFJINApHehJ":701282339204,"lWWBbNO6bTxf":991230019431,"B01ZxDomADGf":639587483193,"pRyiU5fQPZtE":5995681204,"46bF2EuGR3Gp":223578042385,"GlHJxlxuq8yx":404657814439,"USwoR78doLM5":529858744670,"cBsrDbDLT7vf":450854098603,"kKoBp4xWH9k7":493837368450,"Ubbnoi8PMaOx":338771888470,"0CTikI2JW2MN":430140886406,"SDy9uKATkouv":545908837438,"drgV7NV9Al4a":478260749960,"MCapRhE8aED7":456193409363,"TSHmSN8xWDgS":506910059458,"3MCIvaSzWQKU":550484280384,"78ri6fChjP01":555911419676,"FVDJLvj6iPns":931794042258,"d66drjhX5w57":698018365351,"vlmoXEFoLP1S":680884829563,"qj3Bds46AoFc":908913600243,"Zp1bKCumwjQL":529559462298,"rhrRzikSQCuy":199560215471,"6ifZgKfSimYV":508596957975,"YxZBDGDrN7Pz":432249479961,"F6lf9lScJTEw":852772095505,"LhowWfJjnvZB":238491607013,"lBAMOomNVbWG":590749431200,"Dh2ZVM3hFcfJ":566709092380,"RhWOoLEMk846":169661721588,"LZ5rxuIVaSkI":829103804055,"wkABzFFf6V3m":48923082863,"xe4ok4YnA7G9":992644421369,"C7jeiu5lUsA6":588730990500,"5a39hmIQzRUq":668271220713,"LdgQBcwPTUkI":260673599219,"krrpPAhBLdjF":571310750313,"sR4l37zUabEB":150512031170,"nysrunuZGLbd":195871838250,"MDOpXguXkddV":852719345277,"Fj92JXFVarRX":262427023992,"t8jA2Yg9c2v8":628301307984,"0GdnmW8p2zZd":786274933859,"Bd0oIGQDnufH":834043472528,"Pj7S20gtf2xU":743819319504,"SLgkwnhHQeIZ":70684960185,"oq6u0COM3kag":150522054254,"7bIGA1zDYkCe":645016219423,"JFfBgX71wpB7":178337873381,"Zhrhn7clsM13":648566466708,"VDBFyalpUvWU":27618374481,"ccdxv2aBKtkd":289263100607,"9kjFAyfqOuO7":611815475991,"TwdF5RYcNoYX":478868892919,"BjIQ3Igru1Mg":711404781432,"RVMKaTftgGcB":925223691025,"GG8ronLuWflC":968287356628,"RTh5FDhNOQK8":149013316479,"eQheVAaVYX2l":439477540538,"Le8JbsVf1Zs9":413450546605,"lUB7AjehnVTD":552498735352,"oWTDDl5rXhgE":577237758384,"bqFSA2ymNdET":817021725631,"pIN4VtP9e3Hm":417494407695,"XEjL9KwmnQSw":426183290968,"kfTflwqS2cVt":608152750239,"iuwzEHpMGEdm":758993878283,"cR2hLSEN5wQM":327597137296,"XbNPbtBvOAx3":9245602491,"pOBI7Pnic4DM":169130872738,"Xgo3CFixtH5R":288047156838,"geLl1SDLqEal":379637599756,"kBUHqZyPWAMS":296842380666,"Y2MkyS4rJPDC":787160306779,"4y9vI0wlgvIW":267271646771,"V1Py2XEE51gz":563090807351,"qF6iX8TQmINH":242748491078,"7fItXHeG1S1E":725152045744,"KA7bQ4dZ0Mew":459473457212,"rtJFUt9j8o8q":966422237315,"OPvWQDKJ1MMo":427325806858,"QDsHPSafNXOI":347877081543,"pC1g4KJCN47r":252309608756,"koJXsnsa2ZtL":383068151833,"gCaeeId7QYMc":306683566068,"peS2IbEJFWsD":587579458227,"fWiQ42dD1vOJ":362458950265,"PvJsVTXMFKCI":445100629024,"dC9R079MpH1J":210174769226,"tJOvBZUwJCTK":495483529595,"00dLGd6kE4UW":976993230966,"Omht5wkohxLt":219881587453,"zPqyZeudyn3A":172269124567,"S82Bszmg1xg9":461584258388,"NH9wzVJWVSUl":278139457207,"uXu3DWxm28SD":473096599262,"6xNxrLsVyK5o":826193313338,"8XVstL9AA1lu":434390684478,"W8YsMQ8QGGxW":181385702366,"xIvsJsVUZqlD":432309397688,"DswwBqmosiQt":674246864955,"rSnkWBxlIYQo":973376800120,"QmXNPXzMeAnp":975350309300,"Xld7daiGtd97":481869860179,"OuBTLBGvxE0M":12025037694,"lAeYjn49PLKA":246787516094,"vYtL1oDTH23Z":624378893555,"J6hqSHz9GFI5":456239699951,"hjjQyTDodIbT":470565356067,"DoHRdy1WW2DK":494235025855,"rKGQB1cyVZsa":439409355093,"yhuvUdU5ozbe":830633634113,"AvUNHyxcYWmu":641799450755,"OC4SOCX3japX":372232956817,"nXIsuNIErOCZ":140896465461,"SzctsrkQL4aQ":133395387913,"y2hhXBIG4nv9":624708390908,"FeCHXl3EnrK1":794171382064,"WJdldg8UaGls":103224435450,"eesNZMc5VCBk":600065211659,"jLu04GFfGLS8":679746983368,"FIonPC1Y6ibd":409597675560,"HNBirt8Ocfkj":578993592624,"DlQ2eqCf0nnf":317160647429,"0n8yzuAwtX2b":544608607327,"cZES7Azk40iR":754356299790,"MrPg6oeRtmoD":764360896145,"BwCY5zcid4lC":181099882022,"NJcGRZBGtVAv":521658064550,"af6fkVcg9MoK":820830093014,"x2yw5jrwYv2J":51195031060,"rhXZmmJL2Wta":854069435770,"JsdZdfXejcoG":254757597109,"mpesFekgyhNI":368455250473,"Ojoc9RrJtqrV":334143066158,"UUsy4zzU6FEg":780579346135,"JaArS9akUTAd":980639480996,"kkFebU1pICmJ":456039862920,"KDGWzVzWpqTe":947918933586,"ElLocOWBLuod":37655091249,"A5zv4Ts0QXEg":806470591772,"DkGzfAAIWjuX":138840400759,"uCrrWtdVcPhO":819323688398,"W6Y7RQV5TJc8":64328853741,"GngCAU1G3zD4":356525583872,"QxiK7RHR6qnL":590264617527,"rIDMuD87iI65":732615476292,"7IZ9s4FuKnqT":20458260025,"aX9Qs7DbC1DR":97003731082,"P3cD06GzLhVk":891962222856,"AQD2yMBOHynA":826451310373,"a7A76uD0iy7y":696706308218,"4t3WtLVrq7rB":871745039133,"3XnqLs0heVkB":499496379607,"aUZpC4wbEwol":454334766131,"1wunf06vYmOH":374139935047,"iOrWvYvcZh7u":651110661196,"LG7K7gb3sGO8":572889560347,"me8PBrkA92Lb":495584070531,"njoPYhjs72oP":735791536821,"rWKvVmsnkgq9":208865428247,"Ssj63QkWn2ia":405876660722,"ZYLWH07AaYIB":711198499046,"CtjPaKbDIkVf":642652827816,"TAANpM3aJVCR":632722143339,"Yk7n440VFt5B":27411297997,"4IzOIMkeFxh0":459027131185,"8zVqTSgEGVLm":388317418120,"h3RvNDLrTc2d":239030124156,"4ckhdGNd4hUo":585478247840,"K9IVTp2x9Jui":252987233920,"rNMDUCZ4I6yk":934659996761,"LgM6Bjql43z9":605318796423,"7CtcQ70ngwF1":834956358070,"mUjVREvjxcac":182159963163,"i4jkqinSeS5b":313262333821,"sjtyOHEsAP5j":982147811242,"JinvxdS7EktU":150936358002,"Vbla5lL8zPZi":611369644477,"WEEOMDKS1dEm":910804167811,"DoQndI5jxkZO":250408158814,"49TNjP4GijSZ":606600071120,"1Qb48wAqyjQ1":537006132324,"vB2hiUzl4L9A":965085310792,"qZYKlcAFzlRE":57770374487,"lhWUYSkCa8v7":882436633868,"TX0OjcUhoS75":837373600252,"DLzPs8hqaqnp":183244581001,"RuvsCzn0Q87U":862316850316,"HnWDmIl1PnlT":868138705756,"OqJggrTyzX9v":94362007217,"ngElRwlCb4xA":283347830051,"A4IexEClp2AM":324622509603,"zYxbEldOfJVK":427741576818,"IbmavWXBMaRl":547638502920,"wBd5bRKsLxJw":36282690097,"Xnh2lKyGr08j":262239803387,"LGLTNeFViBFI":982188320238,"atLfNYfoBWcw":734578755793,"WPqglIF2eJDa":98900083975,"lje6dnYeCFYK":171175524629,"J5Wc8dsOqI2D":120933587247,"bY9jko1fOTX7":135040815419,"oz95ggdfAm1H":47681925170,"YGhZKCIKk2C9":747720153824,"o634ej9Oh526":855188655004,"YsnWfvRuFNcf":486941385382,"nWFFIfWtuk16":222839632258,"vggfPhKiUdCr":756724487955,"59oODolDnxue":53307918175,"QoRkaZP8nIWX":469165084169,"AXJCzxFJVAPa":739748046011,"ksY9vvW02N2t":811078318212,"2OfimGQA3cBi":832037000480,"x12RLvde3xnr":893007284843,"A9TKrtZqi7zN":548912130457,"gPj3dYxFWe3i":486341088209,"NDHjxOwlI0ox":380299435476,"a8dlGOfXHmrX":395411064654,"XfqEwaFPiyNZ":931524755625,"81Va4U5z9dPD":7095863309,"CudwPGIaKMyE":83119850127,"p9WXfETnVMKc":78288749254,"vKDOp2pByPr2":655722826256,"auXPc9ZzdPnG":881314202197,"kmcr2Kb2hcpR":553806413151,"TdWDLHojwHpi":488603970932,"JMA8vLX3DyVw":105596316350,"f6oe83T91ivT":821505738440,"n4uGmu9DsJIA":527201995967,"53DHSvnFB5Sc":557562900677,"o1H0jvepBOn8":60656256559,"ra698Cy0OICU":700668720398,"76mW8ouBNU5G":531342443624,"15DDPfNWocDc":505467957461,"XK29MzD2au75":903922004758,"n4lLjjAFR8bg":477923412756,"Q1e1jUA9rJLy":295292257209,"NIZbcAWnX1S7":710418726900,"E0ry2YA9raQh":651170037461,"B7OVxrZdO99Y":781271297887,"cYWbv4ylrSfl":348503391841,"Gvs0gWR1x3xD":340529828424,"H3UwTtSxoSE5":169555595118,"eUBZRgVNNRAh":907436302052,"k7bbYgUxuUNF":973513288693,"uQEhAHunDyXl":86170919485,"5f7ARCsAfVQd":613418635863,"vDQDDLeZfqGM":437392051905,"6uVtixPIEEx8":609695499510,"TZVdMi9guM2u":725327522063,"KliTMHch2Nwp":82004117925,"4WWVIDfAwA1J":646825719754,"aAe5Lf3bBLu8":459722604224,"Mk1urtvEPcrk":276805935836,"cuDFr90jEN0k":820875577017,"sstreqNKZXtz":201002312280,"8Q6EeLG8dP3k":957428739792,"LtzXBB0MUetY":361592099574,"outczQJSiEQX":799852162092,"TTYMuFDR2Lqb":258243095771,"74geht7MsGOT":620683911519,"lQMadAuKKorw":935332929392,"EUyastR4Io2q":261564260472,"2q9wVwjO8Ni6":50963607018,"JTrxCp6NHHTs":190980836359,"e7LYd0GdqN6d":549678274641,"u8mXla01C5G5":62476861173,"AvV3P6Iy5o23":966131316548,"sDU0q3zmnQeu":141926197415,"2U6Yn1tQ5cqB":865435476957,"AmQf41fT99Hd":310194466736,"DJPm6fVlcpSf":118131695833,"cI3Q7jX7vX0O":562028592040,"xBc2Cm3rmnMa":250354185740,"EGHvxamCAKpc":833677276091,"HPW43Eu5AXgZ":243934397534,"mb33VfFzlFh6":746788875025,"WQGJhRl1XRwY":579196836851,"t0m5T3ZEWtvg":915694263324,"JyfVK1MbPBDZ":697187464686,"4JhvVSaRakrX":73799301839,"28HbYCy5LUD1":741669378347,"jj5P3Ry3zocw":366228572627,"nZdGBMUF8Z6a":146075649894,"04DKGj2TV1af":438210006551,"sLMz5Ju34Ehd":348631007686,"P4I3ZgoNVnPU":249283062465,"f67qFIoKqYXw":702882047285,"ccEraTf37JbV":428287575972,"3LkkaXf4pqLJ":513967699174,"7cajj3jg3rNe":266975611872,"NGsePat2OYWj":87298051336,"ciHXABCOruQU":196599553557,"JMZ63pDvso5s":85384132959,"3bcYiuWjUqIf":500390085152,"Kl8aC5GVGSpH":522407431051,"oejNM36iwaeO":193145751211,"Vj2G0zVBdLb8":854422780774,"CC8Z2ExoPmBj":923531946362,"dxnYCGm1iXwb":660624731615,"CGvZfnnS1te2":960527148969,"Ij1hlCVWE6D1":319391057509,"pafEvc9KFyVK":522674089881,"UzxbNBARNa1X":340091245381,"syIR00BZsAhA":472010136130,"1ripppJIEqpC":730600513007,"JctVyA5OfYSY":364179473562,"ykCPNzqb2Rxm":647376384900,"z2vYRF6hZrI8":454896041598,"7lfPXhdjVS3J":555054800898,"BviWcMSgKyzO":672549443856,"PIuU8ggcF91L":582459256462,"cvkudbdZAgVm":726853919905,"VoWJUj4jI0lI":993015610138,"qPrO2EWlgDe5":113195004978,"AKgxJPOtmrnS":673050751585,"2EYhDVzx3ZgU":928812224101,"E7eYQ8wPEEZN":154560080524,"mYaYRWeWow7m":749423208890,"J2p4F2xgH5E9":139841668556,"2iLpa8AlRxK2":799935197954,"Tf18I3L6ViHc":77351384436,"1mzbiJKlOnor":460471721622,"ijRls8pvBuXX":415222540442,"nrrf3dArhMB6":962499470453,"oHtSvA6Kagcy":227479551675,"01aMcBYX7I4E":522753060556,"0e3Thp2GfuaL":593486862576,"ad8xJgkY3vhp":907011136842,"JKEss1UpyXZU":515769456827,"6HdfsofBTVar":988606352629,"pfUUs9oK7E7z":290238125579,"tKfMFftsnqA3":449282093842,"1p5reJUq0B5o":844537065037,"ho3JVNVv0Rfb":331083383230,"5vmlgCA9hgI7":926736466629,"jpby5xmWqYAC":235968741137,"8fd87bMKablz":908019935843,"C6jL3KrD3Bol":639513197430,"nQkZcpzDEO5Q":350919775492,"6KNRjo7Udxbx":77880314511,"UvDifcjTixsI":338188973899,"xCPnxxxO8hjZ":607762010878,"p8Jqp5XLrJxe":288988172603,"luqbqm9nE8Hg":180747012470,"Ucz3HWEttiZt":841534532814,"ArZvHkTWOovF":557057983400,"PPJcoTp5eu78":966852904439,"BEuaphtmTFuw":519481885545,"yL0gwq7TEfpy":25471441078,"8QJDK3AdXhL4":143971745095,"2obhazPUOKiy":320795199350,"YknhSr2wc6SV":611062191418,"BkF0NY6XwjQq":740864078332,"7LImv37T1cB7":762600980378,"B08cmOqKYbiR":488721905205,"I6kniQldVSNE":895232215845,"tdiJVDCNqEOk":127144592845,"2liySRaLxVBz":476470721449,"AbZHGjVtCbDo":920034734103,"ZgH0GmYLE8WI":913555392051,"xLSbCTLzkRjf":760059646461,"PGTuRt4J0rkO":863608348518,"OYtK0Qtbxl9H":145823722327,"wyjzPmIZeoLN":488473287969,"CLtCFdxsTkf2":879882564048,"yaCtO4KNDdAn":300138640136,"NLZmg65Zf5cZ":574101415318,"B1XOWvJgfx9O":209042168073,"R1DzgZKlggM2":347345136918,"KX1qICMWYOxT":307249630082,"r0Oxp2oaytWE":198977615695,"Eb7LVqV7iyjX":850670752951,"i0Xx3aN4xlx3":445031707283,"6WjVm1gF8ddj":700801505892,"sINcmrremcUJ":209494224170,"w39k8AzuILfu":947576419169,"YQ56b9XR4H2F":471427108152,"kCXNAVo35E9k":639940041829,"jWExjCjesVyc":689213573833,"97mwTW0Hd374":295717482961,"5qS9ABvMJJE0":886167119318,"OPxKrtHDRYPg":543088554481,"RIMjTwqTqj28":480266239031,"r03rnnMXTUp2":40211661522,"ftyJ1TwtmAAy":47388058243,"uX7z8K9JR544":376207497537,"hIKFC2WtUg7l":691971732192,"W6Et6lQBKEI2":503453350603,"8GPq1v61y6Wl":817948535962,"6AbJwktOh7Ch":918642204653,"fU1uAxmZFxi1":28300655334,"NzyK7ntT6Z9f":358389727410,"0V8aw7WSTrvN":585794658052,"OJYL7uz3zah2":724148001594,"1Fv4R6Ki0NkT":890994460847,"ReXF4YfXZxlj":238121058524,"WEeGwOFSbo64":922090757334,"ztlM4XyyXV5D":910401520363,"nWeihDwcHBsQ":692535703689,"f5R5zLuI21Oi":112684055620,"KQysqE5rIEFq":514682617517,"PonXjCpdFHkD":947438235722,"PGU7o4KZa1UC":692661063640,"OW3Ddhw05nHS":529514523407,"pkmrkMEHcoqr":934425264316,"JGdi8gL2C5Jm":653966605991,"4Glb5KnaCsNu":455316873045,"m2DWCf9wgzuj":775162315013,"2X9X9k9fLDd0":371323801419,"RgcDaRM0ncjG":268318309552,"jMtaTD1E5r4j":473137666724,"6vV81qGbFtt5":897141224460,"6kwxgSq7FArZ":347456511469,"z8qC5d1izvBj":747552326660,"Uz70Y1d0NgqS":213285328846,"bl5nwISqfQRT":263630373624,"aXiALP4iqD3O":44183876974,"hhF6YuXuaBvO":356105383683,"KsqkUtiLzT3d":749638047131,"3BxyPmX0s8wb":3042852773,"9Uo2H1IFeZif":775125486793,"Gs3rwbru9Ixn":664098486983,"uDlT2Q44tnzH":79980990779,"IGyhqKhPZBa0":549936946538,"GFt71NHTQ7va":190225357704,"a9vgPrUOPZQz":934230855491,"IIWUUJRtcwvW":541938810502,"dvqGipKN2Ozv":983207877476,"fHu0FtKBkMkg":713526192005,"Blho0y2tXydg":546614983462,"8lPK5XRMOC3q":928568003701,"FNVXeeAcJxFG":912855849431,"9exf02OAfaIP":91394949834,"0CwFmhmOuaBn":199557663151,"mrQg93nVp3Ux":714275091474,"TslBbmXDCMhz":440641467604,"HZZ55rgaHVE1":286485867513,"lziJ7iPREoNE":748864409219,"PYFLs3YvnbDK":911849356625,"l7f03SaFwEnW":246006466976,"3Mc7xHRvGnyc":76733646612,"adfHxG5vXJPU":104354285064,"aIGTfP9Byhlv":308925805977,"QDGZoMAJIBRe":947258255091,"9bvNa1igK3ig":675851924680,"bbTZ69Peiyj2":573553774905,"wlO4bLBVMExE":58440445998,"E7yWUEXNyQSV":982417746029,"qZGEjRkmizzA":566023414932,"KMOrziBqP34a":589301746072,"RVc7lx5r0hji":899605220559,"O08Rh6dgKo40":602116347271,"rpYRZfx97jTQ":693981397917,"lpZt8X98Qptd":566974935280,"UaPdLbiHtZzT":762632722719,"79TvH33B06Zq":195464004677,"9KpJ7401qjjG":883400029730,"rsoYY48b5qIG":696203611920,"di2MErszNOJe":949968581958,"6DSOpQRqC5AD":142949699447,"CvL27xTbm2el":980153836239,"iQHeC5uOw1zR":137793392841,"w7ps5VEXRxme":475235711422,"plpCLVgKEiax":824979223554,"tkBsKNvkO1pU":147778600206,"yfAD3npMDex3":663379497381,"dYAL0y0QS0sv":878577598658,"CkerCnRIyYIt":508481071118,"omNEPgjbc6fF":418316186581,"dWcJVqnvH2av":968738368775,"h1pP8F8ULcA6":847387217305,"nebg8N7q6FuH":132234955545,"0J0AifTdXqyT":92736326808,"mpj2GkEcXjpv":784738381944,"dmBGEMpw2KAz":310638299430,"dasnO0v1MHvb":591632886295,"FG02obhwzfGA":695726137973,"BNFmKfYSqjZM":273263928421,"35npywnIcn0Q":768905079205,"QEF75Z8NHrV0":706079228526,"ek1yNUdHAYXq":966096932585,"5HOVxdY6MOGp":124923996090,"nlalZQn1vLtt":405039397166,"w17pEEjU79YK":456191407125,"uKFDLmaNFWpc":795838302244,"4OR9bzOwhGTX":326581769640,"djsW8my4QNLD":507865976937,"0dm5v6fSmSfB":986664579340,"DE1VKvbhOknR":493721912642,"tIweiMVMekmX":344253922401,"gp7hmLOjPdiS":987519447257,"JOjlmVQeJnSl":266755566462,"V69W3VAwwKOZ":868417055703,"sZ6aU2lE3RrU":557639073877,"OvZlMC8dKsHA":638905593761,"tdWk3d4D6aw6":562441227994,"FW16jv6jGipu":218217066842,"3Vc1DSNvs087":396641402612,"iv26ZyKHy8k6":425229894939,"gE0ycSfQUN87":49197356297,"kpzxVfZM08l0":601193045946,"oAY5Vwm5Atbm":544675819337,"oTUSn2zNS16q":64076045272,"sja8oSb7nq7L":686620001174,"u8Q6jgC9Y3C1":864576421519,"hUvAa66SHcn7":930969382364,"jmv9FhXHnIsQ":891184547551,"vWqyjoSdWTL6":531087588619,"HRPaM9e64aJ9":982023996296,"Mp5s8pucsmer":90195999875,"7dFTYll6tEQO":91542309769,"PLyGpK3fJGe4":29110222955,"C7djJlMtVFl5":350982176520,"1XFvgRLsU7Px":960370816036,"kKnOfFFAoDTq":972143759386,"rSEfYWHhbhqZ":968611857355,"W1AjkOybXt46":182528638340,"ZJvgxnK1Fp28":198500916200,"BwLFCuBOy40k":919123622921,"IHrAtmA97RdS":287857458034,"GxGUlLu0TlJN":301740957256,"GCF5zwYXGLVz":665035748229,"jLtwqv7YpOBB":111676769745,"Kl3DQi4awUT8":999479542786,"OubC8Xxntc77":483232263667,"kPAIhCozJWQg":768192957639,"NaX1jsOc3cqV":607994606088,"VidqUDTx6BXh":777674676201,"oKJK7v4GMzg7":899447501146,"XwavikfKwKqW":532469896753,"ubOiPxxUVauB":88044780222,"vy1eHPQ4gD1g":278694095015,"bzNOdJHX4QM1":517183046044,"4Ke8hZlAXLUz":744458029892,"X4wOacvh0SHV":9016752059,"no8aCMSBs85f":105895824357,"1KleW1XooEfs":563538146137,"sjtLldcbPlf6":913269744633,"8DOene5fEIq8":206500822811,"mE6tCTKMuUbE":286002724679,"LopP2qVSCLPy":25829837212,"9ca2I4JWoxQG":94389428903,"7JTlQdj4CDBu":512295526824,"nzgfdGohAENg":886372327648,"plnygIpJigqy":389785539727,"pAWlextkZcBd":496529932477,"YxTn0tkhXRty":255227491466,"MW7J5D56epJE":324753930380,"TYX76g112r3l":408861459641,"16uiOkQiZOP9":922981239267,"8YhwjT4eRwFD":218821700040,"5g9JrihuN1pg":280187030271,"McARhqIpPn4f":887602790061,"HdApb0fyzOCw":27452415519,"RF3YYKBiRmXb":255064271819,"cBr82ut1wtXj":890287492951,"3TnwAVgOVgco":638530956507,"9CW74GgqpvI5":447791668130,"x5MEBJewnkG9":106053572714,"5vTABTmk8cPv":469286019106,"mXB125JMpYWh":234146058762,"R998Y9ui0yam":397272183768,"Gc4OgIeKuNnv":600417065523,"xveen63ZX5z3":813427648677,"Wjb6O7Lqt3ud":181559394592,"g2rsYH6AW92H":959263847034,"N2qCjZho1Xdf":637988802970,"2915yx8ZS8O9":52279683023,"JVqwUll3DnIX":886818138564,"Ypjas0jgB8AN":976040622477,"d2lIlhCCRKQB":968376378321,"IQucuVtQdTzU":769097215988,"zuHqX6zT99wp":801027312699,"JAFkRWpfW2ec":804331171515,"a1DIgYrC4JIJ":777278451830,"KmJoaoafKlTH":529899975819,"8UEgs4Nmac6G":180406927698,"HijSYnlyofLG":206489423632,"IBmvGuzrQWDP":673596188813,"ov88b4sfwcub":884153373130,"t7ZBALHxTUBZ":16987968031,"HQNRfC0QTax6":343521192410,"uEKANZy5PqiL":841344675619,"GffdMe04Rm3M":182402320622,"RPYfMQhFUnDJ":411239030560,"sHGLMUWqta98":173070192180,"EAgKsOV7ZL7j":728888157981,"4oVyaEYuPPFv":263658603987,"ubf6hJiqSXVD":269352915057,"LQ2jpJq1HT2Z":104834761930,"RgRulvf5gtyW":237468458782,"Alh2H4KSKh5q":379686475453,"4fTtgGGwFTAA":31576517861,"wNiUAPN5EYwS":762067266562,"hwTt91zgWXhg":677105905607,"alh7bjf8Fzvj":748499750383,"QcJ387KeXxo6":784008785929,"3tcJn3nEWEE8":297986276177,"t15nwfuhj06M":579203583173,"3DM9gPgYCZAM":653653385590,"DWVAGhS5qT8G":694676261775,"joKx2pVtymQo":384249898520,"vTBpgMmZg4vR":986250350483,"g4n4f2H8kZlD":552515464965,"ajXfSVGfUHKI":816318941781,"XHfk24tlGFai":58929687227,"nlk5vN0tm9vV":135473381450,"lLY5CZWc9vWT":476089280046,"8xQR1TVViBhB":238231250938,"dZxj7ubkvKBA":970760458667,"JKeHtpZbT1WS":19733652795,"Txoiu2kOlQ4W":838137986580,"qTGp8BRFZ1dN":703436742116,"YCaJLhmSI015":815936391695,"LaTpdeuoIAnJ":51341714872,"sYsnvppIuRB6":740453101896,"ipZkZQ2XIJIM":493652354264,"yYLBInUWiG5E":113326771782,"LSt56yvubdqo":291972561120,"FaO8FlRkS7Dx":197236587706,"WwK7WvMOjd30":321948073592,"kS0xoNorZCYp":107832233723,"MOMDfuWKIc2N":282509276794,"EViMHoLc0pvF":656017035462,"kkjWuhjQYAEm":20536495049,"b2VmAN6nqp3P":821079805924,"0TuXrfdgY5bN":125835808286,"GjXVjtyi6aAz":231566969474,"XRJs6KT6rR3J":810478342011,"1vKzlIbDr56O":201051358962,"n78KPN8JPIJw":214701419013,"T5DXOnzscrKQ":782738332085,"Aq1yrR5r0oKB":867930070439,"9IEq6LF911va":627732076181,"O6esETR8a7lc":594428459996,"SISpyMafPnCf":510468341378,"B73cOZOVcd7B":8150246060,"aSkZQ2TseoKJ":460408858993,"2j7cmJypLaQD":876375664649,"zozjOFJfQbiO":269285751253,"h1sja7JgHDmL":446691518386,"DyF64G0ftbsG":368369680554,"8rkGsxxihyyJ":363407948123,"F4iTZRzB4kjN":539420705287,"bfeASUemlSwh":653119808156,"ztgWrcLXzsSi":693764559488,"JE0dqEsXc1ro":452356943426,"Vp07GaXVcgPe":419957176202,"AyeHRu3ThJqQ":97260505774,"mD4kIPAyVrms":671455740564,"p88kkuz7jjMM":374200607552,"dlixuR9s7QXm":395244487543,"kRDM7zfGUKYO":173603351446,"LN4zNUkUGLoD":490951288019,"Nza2PROCSf8B":445466537579,"mS4fBfgI0zOy":743564942497,"z8mypOgTOhZX":21921846119,"YaDNgkT8Irm4":108931461908,"UkjGJtJHHlS7":61097066697,"zJzDWfavo989":163160488805,"QtcNxZd1nYWL":586142387318,"1DGYWPFHifaA":140215271376,"LTaD60zA6LRv":448936445422,"hRcFCCvAiINn":482086518237,"9rP2c3NXUIo3":437806210429,"PDfVDnV6SYCj":159555811362,"dXxArrAV3fJu":736472882599,"EWJBPZEjrntp":368408722990,"gpOKHpC1bode":594271837072,"VGn9tdbKpx2S":628938066994,"I8pnwvdPaUui":258639555866,"PKQmpRmQ9ff5":801911045739,"GcXFXJUUjYLB":832061829356,"3Un9XkKIyrBS":511519867355,"BzsHADfagRZJ":331567404405,"DRrvgL5aTPXI":248646469758,"2Ah3fDnDKiE5":425186762098,"OPP9kyp7dliW":370408169729,"XVbH5yPVt9lk":611488576831,"yrpPaSr5bpPZ":522532394540,"N5C6xjODN3Eo":469647339560,"6MFJTiYiflsA":400837475286,"O9ZC8LYcnuzy":233656969704,"GHNSNfyoZbma":183847293393,"OMsdBYANyPCE":730637152094,"7rekmewFPT1U":650395103280,"97gdOu1CV10X":373145485895,"0fNohnEOL945":968328497536,"GKypMXyvdN7X":559066140326,"NxffrynZsgMY":662832995103,"WcQGOpFIF7Ho":379661864151,"tsKkPEHT9lvw":287994473416,"OFiwluGRhebk":410605445674,"ecFblbfjszZh":636238325485,"RuhbHbRuKo6Y":372593215780,"U51PnoO9mXl7":666629816766,"ShSfhQzcBrRx":527174482986,"ijj6c6Dn7E9h":625474910587,"yYbyRwMwNPRF":467137498673,"R4ymIBR2bnHr":369003080160,"Y6knQRv0rxWj":507641572762,"Hq5g70yI1QhA":429783875930,"Av0df404YPMG":412697692168,"fYHNCkpYAhE6":828489721374,"aWQ0efF3lFx9":949056756848,"reNhf9J6WEA5":417447015920,"zMu5u8BWijzs":706745081065,"LlyXkKRdcC4f":484986939945,"NVY3d4GLarSq":305514965559,"DayzMuzFZgBM":284218861372,"6MGwPInX2SD3":934149378614,"YDWrSJ8Q1jgh":569202205711,"8NqNdzNDCgiL":267915789639,"kkRwzcUKdZoX":96675209819,"v3VYZdENVxmq":135305989723,"8TO4ZY0OyWVi":699938747216,"cudGnbQwk0m8":736423683630,"kq0WaFN0NrOW":204311079000,"2Ctu2Bw9OpWB":940861708176,"INw7jkaa3poB":267445288390,"O939VEzwnxnn":732923960057,"54vzTrgCPmTp":275920928727,"MgRy90saQC0t":18158320398,"n2HMej5rZz5Y":526931842889,"KRqMuwMjrUZK":479351194590,"RfmR3M3FBeLC":831959168791,"bgcpovkLGdQ5":587800455198,"jVtCZBvhXtcO":880117025274,"ONhJpteVTNRi":456987040402,"2KvXmlQGE31Q":426389361379,"U5s3jGnHKGx5":641340333937,"f91MUL5xadzb":882637392548,"NcWKLALrTHHV":687690937008,"mdsTxxGxDqYK":684849553581,"550MIb2FWDc3":314406636584,"haTfKy3nH4NM":314415688719,"3YvVVo7H16f3":843174708405,"BEQMPzgYjd7A":50802851125,"XkdlcFQ67rgq":866380884882,"O2cdv9w6W8oV":865420550817,"uDsYgJqWuUvs":509935575153,"kyfhY0pK0mTV":707285801252,"xoTq7ZXomRF2":348726978086,"h2mn0Qa9LqpL":60038914709,"9lL6QZAQqO3g":447454045596,"Dsl0Sg4lcmLM":908799370928,"zqPel31ODtew":981022850410,"DEcmhapg4RNe":623492714673,"BsCGhIxcOHxO":591959585540,"H1w8dvn9QAUj":870390077136,"poAgb2wZyhyf":15480711491,"cNJnykG4vqOB":118295264708,"daabXr2NMyaV":342345122252,"gOEfMxWlydZN":88166653147,"lzgmv3FlTw9l":91353555215,"jCorOBNKWJ06":77775092552,"M22CLN5IRTUH":36697875650,"DRZveB90OOat":858379436984,"DgRNuRAZsErD":220331485180,"EZl55yOqzUyA":81978802179,"wlUb0VPF9ee9":704914043734,"Ii9hYOJTqh2W":993792241069,"xuvBDFWUB44K":731673523668,"xHUlozRg72k2":437248309521,"zZlUhk6oecXq":410898178205,"VP1slQjOZHWj":947521191496,"D8weQ42UnK4G":41196763294,"78KUHTL2STY0":266531980001,"mXNnMvfWmDdi":235213071233,"IgPLamq51acj":684473352105,"dy83ez9A5Z0q":121479632663,"tSi3MAYiPEDT":771761180627,"9OUBx9b7mNeF":563068469450,"uYMCZdvLgWh7":821389044125,"VBXOfB6m7lRA":129666007107,"Fx5ONzFdIqtd":189454053556,"3zDY2Yo74Ngr":22183738420,"LCfMhG6HUSHF":550848409426,"8spHQ3SUcNii":73563978823,"ywHSe9VWP5xG":357014207814,"ofTTQg8sosMX":967082026120,"pmOeUHI8buPB":56587468561,"o2L3lerDKqM3":529120751348,"ykFVHxplRBfg":854447483942,"AjfKtVZIqR33":46393597737,"w5Q9jc90RP0c":70516594890,"YkhzuMFmuGPL":634245694533,"UEkwSlfMZbqN":973066298998,"HKbbQlYwAl87":609930139670,"4fQro3fzosYg":840861658867,"ZmqjxQS4lW1v":936357285908,"rBxUb8hk3bkG":357752554628,"LVDNnhvGK7FQ":705774664404,"HcioTcaqYINE":342566755724,"E4rxctuOXkMi":689528520596,"n9IxjwKLJuWM":266412124382,"AYOWjkT5CRaw":281511746499,"XbrBdE7vDhtY":361605152770,"ILM7NO6i3qbD":309196501572,"NxOmJ3pIpUzA":513067069465,"fRxkM1MxtBA9":608591125892,"MbROaqtP36g7":108950063522,"ct4ACMO3XXLe":971190153667,"YvxQ9OH21LKP":782581762063,"zf8DwhT8gTw1":299024177353,"XY45VtWKZy7F":324318445460,"1vuLJhccbOV0":687418238353,"44kYWgEtMul1":609902752305,"54p7gynI7wTt":940385383101,"sEJkhho0Ga00":712230476627,"uMEDC6strdBg":574749112676,"WoPYJZ8in0l2":953914471647,"QE9Z6dVoJXsh":521908198001,"o68iIL2AAxNQ":146956966113,"FLyaFiGZ1P9h":722909668498,"IuYto3qagPIo":600706095093,"t6PINX0PwlYt":808331825968,"fTH1oOVvHkyd":57998541767,"0CtnwnIPKDvY":684539995846,"RVM7D0ZSNg2Q":752033578720,"7Nk5bbGgwC4W":566086082828,"4ipG7AaZykBf":25623802155,"HbXswwRW3Z53":337057966310,"Amtrpq18RHML":721364047884,"BNy5SBQpl8mJ":592910051105,"GQkDG2BbvOrB":581651111819,"BRQ1UAlEbhXe":525188304083,"KENKfyi9tTNL":383210604040,"98teVUo5KrKJ":464407022807,"gvPzcsKSswr8":330290254023,"Nnp04NTZ5emi":604206371791,"AwKAcgXgeRrF":770251747802,"96p0sx1m2ayu":694129828482,"OQJwI8uY0KUf":361771986729,"Cz5UrZgA7Ji9":358069702474,"lz3aM23hY9Cu":114045743612,"DijTBLA1l1WP":682630746772,"18e1TkwbpzTC":422881874434,"z779ezjCMhXw":22666589038,"ADMgV56gngFC":500481162587,"47LM765ieZJ0":261394446434,"mn6QQhOHWOcW":118206254616,"eBO617mstlaU":994037898821,"PzcBVwHsZ0Gn":527223352346,"OugfK8Qr9KAC":270310554356,"wtre9iK1kZJj":663605440988,"lR1H5FzRAp0v":27778674000,"4Yohyfy1Bwuq":229085060328,"JNwotsCKFDCH":4158918697,"3IQhuOG6VC7I":266149983519,"zjIl3WulubXd":671822953027,"hPNcPjBn6Zgu":191319195879,"d83MyrF9emXU":581475686329,"DoiLsqQmBNfr":258470225705,"lENOt0rduKnq":740837978317,"rh3YcPB5eJ21":833774648035,"PBRr9ElE9DmY":328372592614,"5z36dmHXCR95":958653780027,"MlbBUIORbMqj":791318158267,"uFfgr96up3I7":634263429678,"cSEjwQIF9jYd":782916856835,"YGzqGrUptegC":612469305035,"cDGfJ4TfH1Ku":280131415907,"ZClkkcXYwLmW":238091082524,"395EeTolNUmN":451077518854,"zE20wwgUW6NV":538568221338,"BtN0EVi0EZhA":494577901244,"R6WdWV5fdhzO":822245022041,"tBopZ5eLPl8S":877193163938,"xQgRr6fcylwd":330723322478,"oyiq44Bz0DSV":436455428293,"OSFo0yxXfvmf":43202019938,"Qkg2QVIM8y7C":233873580825,"x8mrrfIHXmmy":592257188266,"bUKJiD6l09Ed":302670067381,"Fg2Zw8u8HKQ4":20412053476,"3MmevozJq8Ar":922502174860,"r1MjvaMhzseS":888739342602,"CltkJoBpGUe2":662834609299,"aY2l99QcmonT":422467658030,"uERDKq470TfG":277748746006,"rVDIkqHvfwW1":755305293270,"i0vdX0wvQG3a":130572352938,"Tkip54kHG3jK":644332867947,"autpUi2uY3wu":639353221866,"r48wJr6u4tXx":74103674613,"9ZBLBrfNZ1sZ":71576145571,"NlufCoKmFKkw":146593264393,"8C7WVHcs14xX":141693125404,"ds6Xry6t8sof":840367308429,"i9fwxIGskEfS":742329874013,"zbVjvFPnfy84":505190557905,"iF4FGhaTV0Kk":269978173567,"qfbRZiWwgU89":981753892970,"bcSziFbGs63Q":656323802752,"ycu7WrJSJ7Fd":395007102692,"zAaqb53AU2lB":170996293214,"bnu23GuBUD6W":350754325948,"Xhg2AqDVCmpp":471496868146,"NDQ2ehESFY4z":521133673778,"g7lPcQexv0Yx":675683259703,"0vXjJOBAXaeK":86130509895,"kegek9SajCxE":854253484075,"OdLR9XtCfFXe":780104224957,"vS7y3NAjkdnI":253783582481,"66KHekWDRQes":532297690556,"qVbcvZ6P9yMF":417021341203,"pYyGgX0Eayu1":426499930147,"Qq0iMbirsUqd":946968835556,"XGGaHpL5no8W":770077250724,"z3tmKeqAFNkl":527123133578,"WXp3PJKfkxki":111583683905,"b5cZOXzHXocd":898096904094,"YhvREE42augh":122104239610,"lMQOGGOHsXFe":624711031724,"jLl4qXGcBHFf":942532829422,"2610R3vcCAxY":250550064847,"RZHO5YhZTkXR":836745482728,"DLCQOkYNKW9c":506247813306,"Av1UFDFUZJyM":146228910494,"TqrJG4ydBKuP":262725051363,"sC8raAA3nsmp":281589514812,"xgK2uWoXggjw":496071449294,"yX30WwUrqrOu":757429774263,"UldCuHBVoBlA":848467612649,"7S6VfyZlpF1a":746422027128,"pIobfHbUHxg5":129129183308,"8KsuWSXWhGty":755878715752,"tokC2klOo39W":184607716073,"D5gJOUGtk8Qu":606698707353,"20MrDXVcLfQk":957214506303,"GmDpmT8CYnQH":497795839966,"WrYaKz1yYLUh":240249752692,"6eOI5EKhOdrC":89609604393,"KIL0Mpw3WuhL":468711469040,"0B3cUMD63bnJ":380242848026,"Ou1NrzSBt0fu":982990094022,"8vb3COl6UaJ9":894216182370,"bDLE0Zqg5TvP":653286977273,"ckMWZpvh0kNz":668213232670,"volRrwkd09x6":973891390076,"vpRsqhabW3G9":180611601192,"BeO5Ds1pp9T6":792513968457,"bnIvi1gxvqXX":748862557906,"MIvumaYvbhAN":228733050912,"eSwfC2Y094t2":847616265234,"hA6Q6jDQyETk":750527389464,"eOVaU8ueziI8":679534119396,"6QwkPGXvA9iw":86869552521,"RVpUKiavuQiN":732684542758,"csAydVzDrHhu":546050430937,"klmaKoHKhwhP":387730282011,"0yYBEuEsK9qb":275474586674,"jWOhKbGh5UxM":361545358873,"OdV1nRV5NNa5":219612820515,"qYUVc4Fnjjy4":862306832662,"05NG8JLR7jhB":26701703665,"g5QZrEMZMbHK":218995751393,"5fKImRaf8yq6":789024849382,"ZjVSZuxP4fNA":587728010436,"LR4MmVmMesBT":297121352665,"sEnmyYxig3ul":806401163279,"DZBVlArRzc7A":933401114547,"xKnkhc8mLi7L":857919370764,"h2O0flKFDHSK":141502464217,"FbTKcPtrINUG":853579058139,"mRuZ3lvpiuGc":945317260626,"reSuzRyYxkvE":61570461022,"nYL3FLwiNBu9":298366396720,"g9tFojvzW0Rx":399634521679,"IhfxMg3Y6HPP":520601007002,"jZFss68snK7d":809377387767,"YclQnUjq038m":133616258648,"S2OIvG2sUBVD":854639756186,"mG2HDG64pyjH":721177596230,"8vLOo6LdNnDQ":836453199035,"hwO4OGzCNdpa":307499183808,"DFNP8edAjqUb":353451352275,"2zaSG4seKk9b":130794915646,"rjumVSToLM1f":677541977358,"RaFzCRTq0Uvz":767128068566,"Zw49E86Olfkf":131652619657,"aei4G5eDCu11":644436816710,"SO7NglR14ZS5":964103712536,"hDpvQHSBRtq2":463283585377,"mJ9hdOxEzvhA":806947509540,"HW1kuBLyRua7":860984054861,"vXAzjsZ9RoQ0":408439368953,"3pYWuryCzX4d":493310911125,"ORaB9R6cwoiw":819062822178,"6lTsqeSO4lRa":887665780763,"4f2ESFZpN0Fs":113255262232,"CreCd2dEvQvq":847707457294,"nGFlipE7PDS5":886909460188,"S9d4nJsaY0D3":816681690638,"RrJ04IF8Zo1o":412351108565,"2OFURAHwmJ1v":221089491488,"ypqyXtPpar2t":845989880330,"v9LTASVlmwYI":408122977399,"Kgf8Dv0dBHMG":335157295985,"qNuLvDHHd7Ay":315276806486,"5AoZcxBYu0Cv":282422106657,"TIbR3u0S1uA5":868557979879,"MBxmEzrsW1W5":503156740790,"9hwidTSrYzGp":159491802224,"PxGbkes9Ap3J":737069801585,"l0rvhyAsJc0S":12611321335,"ZPK9jr3WM5dm":89376292318,"ka4t2sQD3Ou3":714765460337,"4wQI4hV9BC3a":142725066455,"Kz9pALhorGsi":477336610844,"bzv8HlYmZlYY":341981801325,"BcizGCUVuodi":703752375026,"JXfYYd03EotF":450751583306,"y6J0Y1v95DKH":850856517242,"EX2H9aSJTEPE":614702773144,"XvQjTtOvUBOt":96486904713,"1kxtj0tbfeeE":505054403593,"zF1p9aEbTGFe":809048275575,"oQJT7VDVdR9S":423942532357,"2wKJag6a2BPo":549191972729,"tPpj75ghzVLj":638918556415,"21AEteQPSLTS":611826205058,"yi7csw8mK2km":98828593528,"1jAklGzrteHj":1115415842,"Qd2shSZWSw6o":667269292213,"DHY5NxmJjmjq":286775476152,"AIqqrvPTsNDF":354196621727,"rLiUQde99AQ9":400397557823,"iTlGp4vzk9n5":204047527303,"jiIkxK7PqSjw":801078124101,"XZwnaQ6CtaXG":896431697680,"QNVWZixpXKIs":921543402438,"hTRAtENKW3xR":181928487110,"N3L1jszuy28F":29809302668,"sBh3RFEyQVJz":474546184356,"1PkBfH0uXDfg":376314248916,"DTHEIgs5AZbE":750332331839,"NeIRX6MTGfKu":423943272510,"yFZule1OHBzg":2701260643,"bnIQ3q1wKZDb":226334840065,"2OPW25J43qHL":344323188652,"kOUxUwkJYlQQ":674868071505,"VyuhU9GLFYGP":887664920796,"ep1QK0DT10yW":473987841120,"3JgeqVQ7lPLH":258025896938,"Uwk06GGV0ZRK":222479504973,"bN3C3IH3yCQw":745951722115,"7OTsg5cuwdvh":697987082741,"TxacWF1JyZ2L":390374995145,"q4mZS888qRdA":678749644748,"j4FMKRFkQNQc":721108384519,"cnYrV3FXRfG0":110874610958,"B1b2pNFWxFKQ":911818266981,"1JKO1NVpPAco":50709561566,"o0Qe3nz84eDy":100986261354,"bKlABKxbFf9K":129216432128,"2EREnmSrgsfb":463239614313,"soe688l7g7NM":282047189405,"kMRdl4NZIPLr":157544946235,"vcsY1Smb4vzH":518423870456,"fjIVZGPccsYM":609184640026,"5olDnm1F9S4X":209130266373,"pAQCfjfM5fbx":62827369029,"X8Cxqaw7Pfkd":766652561614,"l1hXgWhuPuOq":670709881782,"AJVySDXgKH4A":330966694762,"LtK8E8te4fGs":619066369962,"4klx43bFXm2B":286021371278,"4C2HNMIQh8Q5":301747585198,"XzLAJZS14vz7":565864967405,"GBOT8fVphZPO":791481813254,"rNhHfEfv3Obt":810378127054,"mkRE0myoNFWV":286249955117,"T58ZuFIhGKE5":824804274799,"TFUEdDSOwNZS":405811887580,"oE3H5O2yRmgO":932931560254,"9JQ95Mt5UvcS":145103798836,"mdMUgCebI4hN":725746343361,"SoUwmwdwND36":508101960615,"wTHfEq5yWWHs":309523555113,"JtfhZ2AnGVq4":241128224519,"kH4U9gvvA3uL":985346031970,"SMnHyadiLRUw":955399637185,"hjJn3Z79keaX":924486535489,"lpFCQLkkCaNt":740761742518,"Y40tjlGffNmn":529299728605,"1wH6kdMuxc5e":963465287785,"Hf4ygZiJjsZM":869067482449,"6R7mucYOPN6w":844921051630,"ZSepBExCNkkJ":889431839304,"QUAwaaVIVf2c":429129711753,"Py2fJdC7NMCk":327002129936,"KI8Ow578gE29":954749617575,"YRd0WpUtXwIB":400436395156,"ax98VhQlUTFf":475945699399,"2ET542I2esDA":740113926547,"nPjBvb4bdICX":24054779345,"qD6WsPzm0NsY":894061385481,"FEmFJE8XNQPz":209422882388,"vESe8NxCbkhW":703987672642,"ohfVIu0P4jZD":643748749066,"npWPl0FsrgNd":235707242239,"zHnHgXvOKK1Z":856831101379,"5w9nbW8XUudZ":192135852297,"M7h1sa3APZkz":678168479733,"wEi336wXSaTU":742474960078,"bDiIUrBMcivl":702616425214,"nYsFYUwbRR3d":374437090211,"XpqWNWYVXlPM":91831307029,"oH6wCiGttrod":704566184263,"jDZIFADiaPxJ":124708153825,"LdeZw77I5nKs":284627887596,"qyYeqw38BuYM":790994799735,"NqTLv7ib94B4":99622047062,"HqPwR7TFvKKB":989550928335,"vC2epX9ryWI5":463201945293,"mh1ZbDA2sGVk":27981308927,"mm0UHdNv8Fp6":880977345606,"mYeDja4eqvYA":656211561898,"7KudGMuOCjb4":19771914797,"dmosczPCdbwy":668268165645,"68JUOYNckNFD":377983045820,"KKAIu4D3Lkn1":163456234630,"MTL31gtvzgm2":390445049832,"qmPlI6KM8bd7":807608852633,"4IFLU6LujRuk":609388040309,"o6iyG4HNJcfG":321969959055,"jdLqORGI8zui":498647447497,"lal2U20uwQMG":178136794615,"bLTWrxc3UrAH":342941723710,"0zFdGtLCToAf":435607365417,"YGhQxAXWWUmW":82393321785,"qNOrKd7CKyox":75001349803,"UgM7kinLvVJv":210608675692,"ra2bmjk1zKd8":420529234198,"aDAzGNHXDbCO":455422518913,"yWoYfxXEgG06":643393575584,"wjwZE2dwX7Gv":121344440470,"jpaaZNigWZsA":446771627984,"ZtBT4xewoDih":405313079044,"rhVAvp5Ti4yJ":246019089620,"I7WmkahGLWBg":177865933669,"dPjwEArg3o0v":10167362946,"78kdUv2LiqNh":382021133400,"l7Bu634EJ1oy":713059320363,"RVncjoNxI0zy":367416962772,"UI9Ri3z7AaCo":2876803758,"DXeSzvZIqHpU":107590282763,"aKyBVqak3gfq":760076544421,"11wVxog2onKG":689982612071,"vNbWIolgoo1m":502396742800,"hjrbhTW7qrLP":284451784011,"l9GGYBEeODNn":720093327601,"g9KXZBSM7DOq":102183986007,"kvsYDpS3X7dk":896154004650,"JQIpVnUjU7C2":202327782453,"2m02oROUdl0g":24614532423,"m9WhKun5BS5q":517231918685,"8Ih3JONLWzBP":144999233404,"ih2wSNgGvVn2":245872131956,"XtLxQE4s94KW":573297125675,"d1LYLUBMxeP4":154702418800,"cEIsrx7KtaRD":463077018201,"1sceN1J5UPDx":637765170400,"2XmUItQ9Q01v":573436872735,"wuIP2PijuGVx":120016285464,"I43yCxcghMAt":259823846891,"4jsJIQRAXnuh":526264537618,"G8URD5jEZXl3":349621203069,"hCa50Up4MKvC":163473280345,"XKR1wis1BC0Y":385638898651,"vBtpZ7vpnFwP":556288694949,"dTUYcpkAO8h5":658421085872,"jhnD6R7Ha8sw":419697906073,"9Haeoh3XBMzM":767842426868,"GZlFohuTLYR9":893960649574,"uAsl4iWMTiC0":672230861285,"pKTnUmzzajfm":731805745986,"wnG44VjQkuMo":737708608064,"s1UcXijPJnV0":619842955499,"k7t62hT5AGHU":552037117162,"qZFAT07WGpOj":16537653541,"Ot6Ob3YGt8Zy":958118396058,"X93FS96O63jY":163871563710,"C3cZ8z8HGcrW":436486897290,"mUo2g4JUD3QT":135047126509,"GfKgnclSL6hS":184694841203,"xOUJ75BLGinF":950501926950,"c5FWb3lhkPkC":616553973538,"aw04rGQAe3Un":44118904154,"SS43G40vNA3V":588739338183,"wWgTOszocsYD":15574861358,"TrHKYdyyBNWb":631097608613,"jyaoG8CYNmtL":228109948316,"5SZ8bMFNCuvW":881304713565,"oGnraDnpimeY":777193546003,"YvTG2DJRLsd5":770777512014,"Z3BM6Mqn4BUZ":169431405406,"ztjAedVhOilK":908961631261,"qE4Ij2OIwBBN":645394281905,"FoeEdnGEESXs":437153812339,"YjettlEqsNOS":899871945507,"boOesloKMYXW":922369048012,"dnLUqyRHDnSV":83746594007,"SZEOSyMpGYcy":800532565168,"9WFeCuAXDyGy":137378716226,"fwihUGMELsBX":647426510716,"RdmDzrfIGd64":393537248457,"1PR9Iqt5DBEq":203230817797,"IlanxA9S8nc2":731249947019,"uM6MxZGKffCm":980588653787,"JwrdCMFMtNVG":86934618386,"eRbxKJoAlRNs":806770036801,"daXTeCFM82Oq":159850449551,"pzvtE91JnLLA":789095775487,"P3Yk1gLaAyoL":836613794550,"nO0WvQqH2PRQ":536989993309,"fTmhyQYHyEt3":822391773552,"Osc0RdubLvaM":875927709626,"tKR5aPosVxlU":29097758595,"ElyuzdEUxVrw":147784601564,"YX16aWjouChF":817702683517,"w4mUkbs2pIvt":41208173274,"BZOKbtYHEOag":620165062050,"M7ckbUyjU280":831480467182,"IIN6I5w0vKMi":88275082024,"pGwrXxQZrsQF":50930713763,"1tHKTQZoAimW":28659380230,"RgdkA5Fka1V5":225426442461,"aUmbfWyq3hEa":14911621336,"ewirijooA4Mz":279679995142,"DyYkcjfefQDY":797086649610,"dm2baPiJfsOt":856256717298,"I19lq6PFyoeg":6099279290,"cl4acTJnYj37":715657477065,"VzXoHqE3DfUz":713163282637,"tEIuMtn1MzWP":414186112064,"gH1gsraJzzMB":264868483813,"ifLnwDnAZab8":70675220037,"AsnoWGq9vO0g":863911770398,"G6P6HC95f7M5":84705133528,"O3cQU0dMNNlw":203191753230,"hYDFb4pYLWfb":786510123558,"wy2Z2MOd1QOx":101673545007,"ycUDKWu4fpKg":671943787670,"fm26gzmdiJbp":926382218071,"gLh5jWvyyCcQ":761133992593,"AF6MFoLALuKs":806397790858,"BW2kl6UMFBcI":917955205270,"9MLxK30S6f8a":296072810300,"QQZmJ1utu4gM":445794108820,"JokA9qGz5r9V":254986787209,"2adhSVoGThTd":515929397383,"YoZ8ZEwC3p5W":438102042189,"6qqCEgBnnqg5":659833105596,"qg21PF1qrKv3":46320440757,"uUboapDRNgWW":776687815290,"hbRwaOEDzTi2":872633233889,"cvojQQzfpr6a":206073059067,"z7897wYrfCIe":312083523682,"sumQc8IesGYy":167544011957,"MmNkL0aMWERP":229151523172,"VwzbVCbbYzeO":735420162996,"iEpyM5cGyqqs":920161386948,"q3AFANDtZtBz":347772934978,"i5axogtLdynR":102412333467,"2qleuvDmP4db":523958217985,"VKxHIyp5os4Q":941485830159,"GnjYYFs7JyCk":874270120251,"1AZu41SzxTGG":560193078115,"ni1Llr9TQFtg":652456533204,"On149xZ3XvxE":454358194469,"nLfzjD9yF34E":311306886027,"hijbb8z1NRrN":166270928923,"bSFrGNihCh4O":47804668401,"sY3HOBjgCVNj":940175120900,"a8uOfuYAYXs8":905595180686,"yuNGQGxYO1RU":697759346044,"Iz8Z85eeXgoV":42476764441,"HP16mujpWKQ5":386636683497,"aAc9XLULqMTg":788427986153,"u0X5DVZTkddZ":371983413557,"VukWZ3gSq5Jg":124395677337,"eKBjVQzQYF3E":77311846730,"vmc63gEDjqmV":748553113264,"M8bXMY1jsweV":313939367675,"hq7ZjJCyIeRm":987878963227,"CgC9ptwimrp3":11000524571,"WtrqFcVcY2uq":808029917635,"K6TWr2u9fyyM":489649826679,"eCWXty5rvQZw":6043206095,"LHtVWyJBySTK":776263061741,"NR4sOoKk4YOq":37039126627,"6RobdNEyojD0":751489007646,"KeVcOFN17MSi":603231174486,"zN5A14uxy8Lv":652614425135,"ogwarEUAbJa6":818188174851,"HqspEnZ9dWIO":264880496323,"JD0dQ927spwB":257608216869,"ltCJQCC11vtK":726544736629,"lAd4DVaENuCn":650375665241,"EqCamKFRMMcs":156242484491,"uWga6hMpEh64":675819092393,"OXCvcunMDRUc":447331114135,"ziAPgZNZnw66":76074001694,"mU1pFLSOFb8I":797456352251,"wYH1SjJwqLeZ":956160783593,"6XCb2Cm9NbZN":160616708440,"5HorZLHtGHrV":303373721907,"GI4b9jLp3fEU":801582633721,"8oKp7HHMpX2c":9695546907,"Ieq8hFgfF4ev":943400454412,"ygpWtwePaDcA":859269009241,"OsKeli4a2Bq4":758014511403,"KfPRjuEl4UW3":193580108142,"eCowkY4w0Yp2":140593459001,"VeqxqV4QiKMa":928294081023,"0uMWrLsTmgbn":977086838721,"WaBCQ1QlQzXX":348212207093,"QjNolzS64FdE":74686321235,"nRsUSwUS8AdS":817633159115,"V9AiqvtX7CUt":297380381245,"OBryK83YjYer":160005886105,"4EEuhEj6SFhf":482130378656,"WhxgKBumcY6G":396730469918,"Ek76Wiz8Xjuf":643871882523,"SSR7mQf7VU6Q":868743141507,"KAczwINZ7yxy":594030818915,"0kWxn6Qt6Y8P":342257203839,"H0TdcJ0Emhtc":825119740497,"PgWmlp7Sn9Fl":803214858596,"DwFn2aEvlzbf":402947180858,"C6xioS9XEroJ":458667881281,"AjZDLegI3trX":176297755636,"EVGWzmMJOYyf":129583234836,"zkectk2Gvh4G":384446068738,"X4ar6feyk9Tz":732796826971,"he6b57kEfB90":885033406312,"IUPwG5DM9GKn":611661747336,"qkFQObv3o3zF":430880919939,"V8SsXu1aYqKD":158051535011,"KYLDp8UFL5LT":665217430864,"T9t6a5FiXr7x":113218357114,"NZB3E91AeQIb":373295360639,"S5rZVKeqULj2":485231267447,"8A6rntZ5NRCe":824435860185,"3VCyEhQddqCe":806172912295,"enccjDsR7ZAy":582670160894,"vxaiRr58HklW":419296041280,"CjD3niXF2gm3":407680109177,"p19pQfnrPfkn":602299426050,"ovxjusUGP71y":121670162068,"dZGewNO1J48s":125281152787,"89KwOJ4ajXIH":152166061006,"QFaUP3tPQJhH":1588398857,"BWqY7yrLVbLm":69423861699,"HVVpqlY6cjCn":841144495755,"bZnzLls5I0KN":728162585043,"8MsvgBsufXgp":80099400206,"nJO8vyWRIdS3":419512105675,"8UStRtCgPMp0":986965539779,"cJbt8x9atJMd":553024212645,"OYkbHx3NRFwT":265304283436,"95VRGlvZsPKN":604786209563,"ifOY92ArFaeU":839556860396,"MCTxuQbnhEvt":493105425644,"6iTAKAM4Vs1T":126949860411,"wCOStGfLeFfV":263034485597,"28tyH9Gvp5Dz":377286495407,"RWT2wLs7WJ8z":921025520303,"LB1bvuUpycuD":737980182080,"a2g219TU1kHR":999223234402,"vxdtG7VIotfy":268500006433,"5LGNh3ls5sw5":864225512726,"c44oMfcwQyxj":719294359092,"7Bws51bHGqrB":651414717450,"0CtVSM6qG6AI":578340445414,"vww3KN5gf86O":774958061614,"PSIC3VdFWnou":122474570859,"joC0aqOpK6PT":149798529137,"noT6NeJMojfK":260643353024,"Q9M1B4XikSDK":618493694177,"y4klJd93tZov":449636594224,"tnRUhGy6ms7o":658003280999,"9QX1LjlosTnB":522253391606,"MJP0B2ngI7TJ":605943848353,"xJnEOD44X0hZ":857519394521,"eIatEv2WmxR7":797776669697,"8p8vSgEusOeJ":964730336605,"5y8g4srOiq8A":992786005166,"mVeK3bvmrTqH":932005055276,"1vipVICVVa38":684095282492,"yUhXv8HvGqFb":174456141465,"Hu2Ddp1VT0Yv":525269878784,"eKgX4TtkHoeD":226920419486,"dqo3uQo2I7dA":155583057181,"iXMci2dHs2XT":453953274473,"bwZLIWOJj0FR":631674665424,"IrcANz4Ao47g":488173475566,"wx1FGSM5BmIQ":543107056919,"uAhOg9iPZnYU":403023731909,"GTGSHHJoY6VZ":888221878737,"pAYZMTjy0Ae9":124070677216,"IPu2VyhNzfrr":936602277634,"KGN55tAAx5OM":243490545473,"orlCYUMh7Vu8":580023377595,"C5vgFu0JmlTb":43388198310,"tWQj6wJvL5dq":473865533836,"od3hMYMEr8rO":843070671803,"2sH87i6MiNoA":453397064030,"5jBy200qhDK8":105920255130,"u7SsKyJIfKeq":620310856906,"bmPJEEMV4ObU":136669084020,"lJ8rM8TmwmLf":104779757167,"gWelivKhoofA":588984291563,"bbJiQkIcqRtt":936184121529,"ESV5xleyAvpS":718258464327,"ZGXLnLpgMLiE":246352476787,"hDW3ty9hJrQd":261662730492,"PRC07kaNl4rO":175430576819,"AFJ3b00keyLG":191038167539,"eaufNlWrIoAD":277963054855,"jReT1NRKi8t4":568243088680,"t4hUPcCxdf2r":613185362983,"j6bukMOKTPq0":127900562565,"MibnqAw5Qaxl":569116865272,"h4mLWgplH5Ri":97426859270,"SGuxx3bZzBbi":569265962763,"EftcRNtboUOS":384002432886,"7dq7ZLf6fZc3":234444221508,"chJkpxPrNv8g":295381752361,"kSRaCM8DQ8lb":80728356852,"ZazyItLOYyBb":237444631181,"NFHZ6P9okPgg":649627230711,"rKZkoAvNsM0F":309947930175,"6pu4zPhcPvfr":437244266293,"E6acb4EkT8zY":99029577111,"J5rMKB6FI5Vk":625756140087,"zIMxUXLmk3Ws":535651915290,"WSfkeVEv4Zm6":482058749268,"u3TiFeudM6Ks":471423866192,"nZaUkAABaeSM":188017360904,"vYIyROPxtWmd":430186681626,"68jDy3BkPzHh":575758172413,"hjLmIE8beTkg":98118719055,"blffVqKJlwWP":749164969905,"ZNBylD7OVlOZ":517357162497,"s3Mnq1wH0JQk":629700811624,"f7h3NuLBOo9y":338724561665,"S9jdpXf2wP9A":231865661932,"PQYoXVQgXpN8":858345870421,"EGJ8FswJDDad":916923042971,"GmF4mdTCuVR5":946402583639,"MotA2OlZjSNF":811781752676,"FPl6WJbO9HGw":111778052867,"OSCWlDmJyNDf":443288894440,"W9kh6yxjIFHW":16715945449,"jfTdfqZh0AZi":967514142149,"36hUo7hH99HI":870327237225,"6gjQbNKKVrYm":757349148352,"2MKWCdimCnS3":895884576606,"KZAAAYjuSZFD":11051200943,"wWqSRPCJXXI1":148855482644,"TeRfWlPtJjrA":686442603122,"UkforxCRMtyb":6554660231,"QdO5XeWtabBF":726690648736,"m5GutPZ6p0P4":609865557010,"GlCD5jlxEpxs":180572353976,"3esOeXKC30er":106835191787,"FjoW5qbLtoNp":486076620380,"YF7TNaDyFl4r":272423268385,"NmqAt4FulGHR":457649505999,"JKmpgf4D7K3t":12872947889,"H3oRM7JeLMCb":561865913120,"jXHg4iD18O4E":343839560902,"tPW4FumupzN6":568768169437,"MfEAILAzwWmS":440994443311,"U786LNsJeSWa":530810437819,"RKPmpoGEt66a":404548608803,"V8yptx3iukEf":599017661234,"vwt1ctaczKwY":241939786905,"ORPaVFIGrSA4":980073527444,"LtaM5qBoJTYL":469186792138,"5Vts2MDD0G9I":342716284042,"b9SHsyCgmHi2":33658452999,"AmIR7EXhdA8W":741698263731,"u6PbMEsajj8j":91895715385,"Fqr8WxoXfTdY":400335526921,"ZRgIymcBaKlM":546300860884,"N53CORTPjrFm":952250688913,"Cc5lb22LEmgJ":943670697492,"hGUZSibgcleH":544094076288,"LxdkxW1VBWdW":393404034408,"lwzn9cerg5Qj":862627322950,"tOG44DBZLPuq":287772613825,"oYgUTQtgnfvi":546513463201,"I32JXfvcJqGm":743031895987,"LQLNw7JV1Ska":483798093070,"TTPQGrIiGRGO":401984264966,"OXcUpipCr2cx":45032611137,"UjiebvGYWlFK":950309592002,"eEenp8eYpEc7":43195996351,"T6TsgCZ02AKC":860489369193,"fpkjiPqIdVGV":329949501219,"eMwfpHv0ErEO":970434232758,"JxGPGynuhxj3":964595476239,"NP99pEC6X2Eg":331116407012,"Gqga6lZwTYWM":671958152769,"cFHrVHaD0VCa":773128175267,"UxOg3T1KKS2v":293061110567,"3s0RpcH1dnZz":553263031145,"JpUQSx7ZstNn":679305913944,"kpD9y0qY2IOp":414838065652,"Bwpzf2WKV8W9":450960726116,"8zdwkK7Jiii5":251749048618,"tYusr06dX8Vd":336083710791,"wyeWygXXRB3l":433051015739,"VRVMySnyQpNH":118187130646,"ZVwWC1sb27IJ":434852496547,"Fp4zMxrEmuAf":935113680899,"kxnGPPAVjjZN":927935707386,"BJzdM2CGeFhX":130415960287,"58R3PSrAzaMQ":982416310389,"2h39H6iqlz2l":289216424911,"TJquEG9J8icj":586542588688,"OY89HaSlXiM6":827364381641,"8aeUFWxTaWpz":474377888671,"ugo4gzqx2qEZ":858389837008,"GnmlAPtAWYHa":882138497475,"6ztp6Z0V4Hty":184144121219,"VGvbVwH02dPL":571691106232,"GevU4gRO5xZf":763767292237,"A77aeI820ViG":269225058853,"2KEV1NrCcMLp":210987362870,"d00xV3WYNH8S":881676178555,"u114hChzaKAQ":120413948195,"p3seS2zwOAVz":96679335312,"xdQbfkAQzFVL":181706680547,"7tXA2MajhuCi":904384617383,"b3atBT9ug7zU":415267124687,"Opw4mQN4krcv":567187962909,"sCjFfFLINB5y":547775194495,"S6eESoJ4uLkr":99430781052,"eRmsQ7xrz6yd":322550779410,"ZjeDtbkjgmhD":636798250416,"aFUCV10sT2r9":957468904876,"dgTkaW2jfjcG":864464637670,"zMae5EokMsGg":69758997914,"ElNO781KhE2x":31230222191,"QiN9Sz6fSwvE":785038500693,"8bqAftfq7XND":764041055288,"z3VEwDQ3wVmT":922892409716,"57h8lZHhMWOz":55390364292,"TUKHMEYmRj6F":550894930501,"NXvmA1wyAHZl":332538241569,"GOAtvuJbdh96":668600099404,"3TrQ0e48a0b9":13765326178,"Qc7rwhe7gkS5":851181398149,"9JZPa9mjpuQP":752286942551,"cpfbFPa3AC9h":662176340008,"wTbwHQ8JjutZ":596599935061,"aE0jgaGcjXsU":595128812849,"vXuD7O03Axd2":385344610887,"ztPhTwgJUVRv":760463944255,"9kJtgpJArucJ":388649623938,"SmYs7riC37lK":873552892806,"ZxSMkWIj9dTX":883488897737,"GWhZMrgMFp8V":3151765198,"mlJ7ZBmkJgC0":254017871103,"iaVf7CceHe98":205076907421,"rgb1z4HxDdxu":756055348799,"IHk0izI4TnVW":111958976664,"u7R6CqKWrOCP":284574870458,"8aUORYtavOv9":142253856824,"B2XgpCJRT8ZU":555661494242,"g7wcxDi4dfjZ":861214596185,"tLT7dPfdfTsR":731339757064,"7fIUkUx2gunR":228729983612,"nRwup2LVrpOQ":748145503466,"AtJXKzeKz6Xe":164876593197,"mO7kAuZCebMm":733837969294,"VygGc7zdmDWe":728952622647,"ULTE3OD6pB3d":915971988048,"vF1ZSs3mnFBa":766023701743,"PD6NMpk4cq9v":726587984631,"5mGn0TcxgYE4":586402805161,"xNf3FYyF4XaD":57470992111,"dU6H68L6tkvz":333717561895,"3LMt0I0LuvHZ":684265242168,"XyxzmPs9VFCZ":577010788997,"SsNJWblUQypY":966991188814,"pI5Sdp3WdRHm":257973094748,"kUzGOnFpJN5T":112095552828,"t9e9oZRRuCEn":867874828030,"HRGnG95E2mDf":32832657074,"L0NGNxMW9zbT":563754140085,"f1nBQm0PIEfC":50582912385,"CijHTTEWYdqJ":967287636654,"DYdwPKtNLQur":565825794626,"QhBgxYxEBpbN":422462913331,"2kM1YDMEZPaV":789381797099,"GRfZxxJEjRxg":487889388582,"QNZPzjGqMf4e":633085838888,"b0hI8aISslR6":389734724309,"mvGhMsmm304A":28160064958,"yCw8SxqWwLEd":236833425306,"MArz2yZi6DS6":959925440603,"2AxPGYwnVhdX":578720679664,"etk1MEvkzaqi":942241789433,"KLQERpxyV2IP":870102916730,"gU9z8Cki9lS5":456931212918,"Z6QVqOztQ9NJ":101720935736,"jarZ2cdCR9pV":852399640320,"5n4JXkda664q":933696029416,"kW0sGZAaCBJK":257516845312,"aDlVnTvzg9Eg":632094032785,"bVHWFIQ7bdtO":819713295271,"i2RLxeyfvz3F":312372616409,"bysQVUaRbjlu":651298244962,"CysAxEqMOWLT":602757313629,"LrCBEJey9ex2":483755923621,"1ldxxD4vJqQ6":499373792463,"GCGHOYKXig0Y":982955375317,"cGfIiHe7gt7C":609239745118,"oUPf5CyQrp6I":328960415092,"qDuFuSfgzcMW":732393245646,"PIaCYCXRiYBY":719333881946,"TZ4Q9eptM7pd":973940521612,"xbQ9ryC8Jpg4":618623024072,"21hJj7i3jQoe":950987046814,"GHMIOywBlCFR":702118050981,"Adm3iVKkPiov":36955899419,"h3rKd4ckPsWc":378755421479,"MduSOxo17qKU":734947061412,"bhJhwBQZig36":839923197076,"NwoG1uRrhAOR":141038858193,"IQbmUwOWat08":91080573941,"ZP67Y3sP4L7u":286385645202,"DWuGxC8u08Ua":849096888905,"8DtWMRzWYzR6":549614840449,"sgnBu0G2OW1A":607327793900,"BK4xseZ29b0e":171005986293,"jK5U3maMLEC3":646268967560,"7Kt4md1CTJ5y":976494180129,"lNK7zR9muVUV":226837076896,"Sh1qCM8vyaai":934115988696,"wX4GgNaBhVQp":682020635935,"8Fv2TzQ4m19H":400509147304,"ZLUT1JLcMGOS":34872057117,"pl77ofS7WqPZ":405447887054,"f8SFQlGm0y9k":106572427340,"oMIoCm1oreTB":996912027824,"xpwU2fQwtYkj":170170848905,"sU9ZcP0CbMuQ":892334975491,"a41OC6Nn7UAP":94958312975,"iMA1yxt2WTBN":358963456984,"xRWfnx8Om9rm":608737424765,"EZrHjSPkKyiG":491600065752,"QZcQCuMHYMQm":192691892455,"NRUKYaOMjPyR":601044615994,"ZE3b40Cc8bFw":83557558783,"RAxbO0XHaOMm":453817324226,"IvgkevZ0t4ZO":575666038066,"N5Lw2Tbo6bAv":618347594981,"73R6d85FgFRA":604062827453,"5ZR9WF7gB3x4":327819627718,"B6xci5Z4OmQL":925248306382,"TnhpnckHWC18":318601375311,"deXV9NqXxuNj":927016311891,"aCFeIwZrZep3":486804531028,"B4JzItuMrMEo":489938333642,"QydTzTJ4lFgT":730052959034,"qR5ySTfQhhvu":519546783292,"2VznTfuahPPC":507133878265,"Nr7oLNyNa8GD":322423549674,"C30sWbMqLQ31":56145495769,"W9gkx8UmYHJJ":814266268065,"PFZrNH5Srp67":839245580077,"8ZF6BMtTK1pR":488276275822,"qweT8bph8Saq":747144713045,"QwX5H4St3IiP":952751867329,"9sygsm68Hcbw":726911746008,"KownqgTtHkkR":441859221697,"8OoRfajQEofy":858669719211,"UWlMr4TzgTVh":347619051402,"dIKhwa4SIi3l":624910771062,"ngLsSrIC3yta":366803342354,"PkdJD2J0eeUJ":359638125330,"frqsZSIdYHUz":10938812373,"SSZH1GCstXI0":935499519080,"RQ48XV5rwlMr":363967650711,"MCRJWxQP0VvF":937467974675,"f9mIKIdOdhXJ":352200084981,"2vdE4Cu6MAjm":216818461522,"SW53gQNgbNBG":358363020372,"HWzOcP0YgWDz":272304590434,"UHCyjAqiGqoC":463256382155,"b2JFOe1SUppD":329071592398,"MmqImmLAIUwf":210429890600,"fuMx2nQ3HkZT":158903317128,"8BtuWYatmQXO":543614620918,"PpKZNBSK2c8E":736623423404,"SLifHbPUINcI":437896878617,"bDE4EVax626i":390413867572,"6UBaJ4fjUV7w":225791329134,"EhqMGgHtoQqY":758087043834,"yaIBdHGL7Yqv":14189081453,"jrCcyEmJGZnF":488164619436,"lxgSE77P2sol":508159432506,"UUVJMWPRNdcb":508020237441,"2rLRzhDHSi9F":648335160170,"bHDCIZKdY0hj":994172700008,"0I9KbdCLyoaV":126255448688,"kapLH6BOZ4l7":231258713330,"AkUGug8yXb4t":751865919551,"JUo1wVUQvdIp":649002743350,"Xv6w1LClB4Dv":45742975846,"gMvGfuRfOFnr":522230781734,"ZD8wD7YEQfZK":30341897426,"k0sX7ZwWkv5I":930952854179,"wOx0oiWxVr47":518093058183,"aAi78eFHmaaA":789285500325,"W3Bb3EEF6XaN":937118235067,"J5oS9WRcFzQB":860394706767,"PyiEEXZq8M8m":566662170888,"TotyifqzcBgJ":549693236972,"omXVcPMv4ivn":139300353734,"pvjEDMoxqh9M":342114365076,"BvpI6hlIBlUy":809959081584,"NauJQIh8adAm":393336499726,"aDNGZWX4Zbqj":648702400351,"04vwZkZMIiBm":160711966763,"xJPOPgj7uHbU":475259323511,"nvmIzXYs2QCj":19524977691,"aHyrUe8R4C8r":389073838634,"lS5qeIu9tAUU":909729480132,"ezkO3tD2YG2C":318875012675,"Pkx2SXvqekOe":185582693298,"dPkypVuuLZO1":489248620560,"tpxZ1utL4ADy":830605507602,"Oc6HsIp4twkW":448307665350,"hQt3hJZ1sPZH":474890280105,"KlPsyTTZ70Nk":119707513582,"bVv70eLPENOV":873978509543,"vUEeMIOkBYXP":201740441441,"S1Hu5wx5JwbI":879208271268,"DzbZ9WTRKKng":961924313733,"dt7hbS2iIAkw":896284099703,"TWwYKFW2vkfj":578511167958,"sbVnyAug3rdz":333983022324,"JErgj81EqKDD":572554004815,"C6UZA7Osih0P":652814608403,"YVKVImSgkfvX":250728055996,"3MKlNmHXn6tt":824280904861,"z3FCMr2ixSzG":322142205280,"jSxNA81kfCTr":543244604545,"IJgSVP19VFU8":279945689839,"TMTjwbc4gSwk":528398190666,"l3ipjmC3Kfj0":749337396707,"eZNl2UPhawgn":207624534617,"gPnja51FxWnM":229713499562,"DSWE6fW4iULa":499623022538,"LiUKaTyi7rxO":279099556539,"I5bFmMuzecfQ":393136965895,"r2uyDfZ8GKKX":744089904340,"UY5YEYwzeXLT":274508189675,"mvBZ7bAuRili":640237360935,"ebbdTEPmjZrV":963755322481,"TJRUhaPCtLFg":64481432678,"F8pFx95fJEsN":226314512194,"0mqqueBNh239":18489561783,"2HTFlXKchf95":660736183010,"6oOWAYUYb0tf":943050727707,"GEzp6QkyDicA":548405914034,"oEQHDo0FDV7e":777360993872,"ge2eao1pi7no":755968914591,"MBq3kU1Q7TIU":238522391796,"ZkF55NGp7tIp":973770396364,"QLRG4Y6UAa1a":853979637487,"LP1WV4zF8rI0":117041181655,"OV9hzBHACnrw":870931175732,"mowoc5c2f7I6":920994611450,"Yutm3ftLQHsU":872241618228,"TvMhnnIHWNoH":252830186997,"1jNceJ4YyYwg":361564279970,"594UwLfnQxJX":300333961249,"cY1j1jmRAEQc":257009004129,"GlbTomDgVtMA":68432097319,"oEyN0pyV3Y8G":592841331703,"nXg1b9NvAoQD":876696119332,"7jaTa29eDUvP":377142603615,"EyXHYz3XtTdq":327539789980,"L28GkK6dItSN":617954532118,"xqNM6qEGF9zA":962262831077,"9PK1jIb5VQG2":949835443526,"r7MlKq0ix3Nz":665375865293,"T4lKupVNTGFs":265941931555,"pvlKglk2G9fF":757512488991,"ic6UrPfqkh3w":547330725904,"rjnc3rD6NkVb":861684094930,"RaVGwkpOScKN":903451517759,"TB1SHbvLeaK7":180128784738,"4FL09ATezCKZ":912067507967,"AVNiyuGl1hOS":827135670522,"GQm2qbqfItfR":546788452829,"mPZpqiCeidwf":418033689496,"OZjHhaFJ2xdL":217597010170,"ZdUxGc1Z3WOb":537875828472,"vHsi64CH4gxB":110274063181,"taEhtIla9ssV":79572912650,"Ip7X7x6OboDk":957391936803,"rP9EovDGxstD":127635339263,"F6M0hZFqeYrv":548624703833,"RIhaLeMyxOtY":206849003957,"YLa20qCJYQFa":819253178979,"1hIKGOyRszgV":56247432439,"y6L2DFevuW9g":10881109672,"CWMp5twRcy6x":686241755953,"PeN8IBGLra5w":136746619767,"flSkIT2xeivb":605766153482,"goK9WdzpqM5l":859863225594,"JRAwUBxSqclx":315547343252,"8uwd608QB2Rr":372086787244,"kdM9NMg0OwRH":317205901015,"dkMpJWsFgCbL":83524411160,"gkIZ8GmRdC4f":407277835372,"m4Ttb7LdTaIL":66967158712,"m6uKXmNVz91j":411975226594,"AI3OdGwxVwm5":532613430782,"zah4JLd2aabH":296166014142,"chgrq9IqiEFC":847294476764,"vjaQAtWt8ST7":122466935873,"bVzkfbPPjtmO":534137768006,"iMkE3jB3oSCD":195409525715,"pzcF2lU6kWmK":803439038217,"Iisx6QFYkUam":26336348018,"5OT4uBYKPcsd":98934809709,"YImyQttoH5SW":187338170216,"tOU8bIOlewKM":81387315924,"UuQSZWWe8BRU":671207042317,"khn9N5XSTDUX":605938533202,"x8vBKzZX7KhP":217277383093,"xBFqfkoI2vVj":159050441335,"MBeWFfsBCuIz":448515504976,"7Am03cqWFJDq":955495534763,"Na2V9CWwslQ3":886853599255,"JLcNud3YVnvr":701772406164,"g3QZZTjHYGK2":991022161561,"kjnstuJcFyGf":456797187879,"vX1Hs1JO34x8":951628387161,"udGNqSpGGRHB":836193328177,"qa5VFKxXrAZv":838698447344,"unBu9GXNtdlZ":36566825552,"zom5Y0i9rAiF":929452109404,"DbsmF1GVZjrp":537729908048,"X4Fow4oZci2N":675041618648,"l7mc4KFrfXDM":864202212928,"kSOWrBWDq7LY":113971465483,"Ck4RG9kXErEo":595702073622,"XtUb8mDJOa1O":243411180965,"a30gA1jr26zh":900743510897,"AP6oXSRYOMex":914633276017,"1vioqH2QWRiC":434139886684,"R0I7WKXH0Yam":695906060894,"vIT9vUFkprr4":138869575505,"Vy128vpapQyB":611980671472,"dblksIYGegBp":254795455553,"JhctOrofIqLT":413458333107,"R3y6tkcLshm6":768674032796,"LAOYcCw2pfoz":132217430343,"s5S4Uz9KFIFn":223713330720,"brxJw7Cc0ics":86547667870,"N3Vr6CvTMlXJ":678041647491,"zEpKZyPKwPcn":599731407281,"lGAbM4fZNnsz":631277967391,"ONKJU9n4UGyy":535650178408,"BgU0Cwch46X7":174650017972,"hc2IolqCoWG2":760038648948,"989yYD3Qp0ig":456484486214,"n1xwAyZHBFmM":190694603646,"N5thz8bY1lWb":327158435368,"CxmZ4m1mmwIH":795847027021,"uzZ4at2tGRz9":477802525745,"r2y6zthPS1at":606541901538,"mHMbxPL45CAj":661938939405,"YUewuFoI3mHt":798079969736,"o2LoKUsbvu5m":770926618152,"v5Ij8uRNJF25":325152127787,"WGQL1pxVJDfI":778758717623,"did6MbSDwTVu":27791884774,"5xDaX13FwWfJ":278318641969,"zdbNcwLkPhIH":807684163872,"C5tiTNp0Fekh":322948857259,"H656ACjHy4VY":267003124845,"CesNikEeMssR":212537689115,"sl91S6hSLsZA":813750138395,"zyEBXdgMKDQV":188722042866,"V5AVFnRA5Cks":189321989945,"gbSJPhrCgXAE":700224508218,"SARvMCgSw1fw":508694956885,"1U0hF8gJHVNp":635461329013,"DDjgrz6Oa9AQ":382767810967,"yTbdGrOrJkqp":668962194362,"X7qmlzN6ImWC":356943773718,"NicIb3UQoRKI":28931401642,"LSS0U7U7NYek":264393531854,"DzdRAzw0ycMr":531158440057,"MmJGdObVG7dC":202164199163,"txmITdYUhZuV":982214061805,"hhjBBdMmEkhX":717256104253,"HkZncqXxhTnf":460659631380,"flbxMwOBvtX1":812275263855,"q0pa5dYpUu5y":969442648994,"mD7zzRdDQjsX":994173724094,"rC31t5hvDnml":905761365531,"imGnx8JlPieH":89756054777,"MNeR3BN2UUwa":400804855595,"z58vfkxcZRvR":155545334137,"70MVWZvz6xSt":322790917523,"n6AoQU2K1z33":716320898229,"HhYYCwG4srKD":531667262465,"72QaMVoNSFPw":964381593981,"N3PeDsqnxSjj":336772200682,"ksubZN5BLRq7":219957938568,"sQuCLwBMcAhs":782063418795,"JlNHzqcxs6n6":107458526396,"b5Bdsx3dmm4C":972969041210,"MO7PtieMHinF":910329640853,"VJ5M5ZhITtHA":835615896177,"8ljJid4UwBKj":680141381147,"qTIWgE3jw5ot":703126224767,"7HHohfS3hk7j":662401211635,"FY9eGZkJx1TI":943970776785,"wopVxzauHBuD":191368283891,"pKw971n6G7Dw":363203920541,"045LKvoyu6mD":335322366778,"guDEpWcRI7To":82682969452,"2vIqKC6HoScS":115443398905,"f3DrA2MJOGir":269379377696,"tvAKQXXGkV9D":937511840602,"L1D5KY3ab9W5":367553217621,"ooRh07aQdsIN":427995066964,"hBXyoZO7b2t6":503163950077,"bwlLkxdYPC5k":658579980274,"tKonM2P7sRyy":558825419470,"RWgPDpD4n41O":711168283879,"3wDrQ44KSMnv":398600658373,"kweEbC7UliJ1":225180537123,"C3mMDxGnqvjW":70425135146,"wBGE5WoaIx77":757199077637,"aB6jvrnia3Uh":401659074720,"G8j2cqB9pZ3F":362095865034,"5CE2RPFUP0a5":783548426186,"aLSioVEx5DKi":762385381167,"FNT4lSMHaNoC":314671148116,"QyL6o9jNs3sn":668654663390,"BUc133q6UgT3":677056198830,"biRitx89z8nR":150337852109,"6xPJGu0U5i1L":361951937053,"Sj6vzR12d1IU":414634210213,"BK7TvmSLaNHj":208032207750,"Sk0rIwf7DNnW":159652215481,"mHNTRKEuFXU1":197731275969,"ccEY1K33HfA0":326795869084,"9OBm7VJ2khtc":221976448580,"J2zh7scl8BcH":216772671846,"t6ddyimY68uh":163911538487,"ZY5zKm52hw4g":847088567362,"nT1xdqWd0rPu":236206225606,"Ci8NrNM8EajK":334376243131,"pWsfaRNADRGF":848423779364,"qWksNleqBi3m":920444209483,"dYxonAIwcY0K":951760776882,"scUayJvorHJG":528645414445,"mrUHF9XvAMw4":879046904413,"T8hy96DU59Cw":338062403271,"6XKfBlQ4HIsL":154679168103,"z2zIqkbk39X6":813387697335,"uHo2j5zSX4RN":724774625154,"lhsbDyJheqmB":725082443666,"yAkolK61nRQp":257211388513,"J5rRswwNma3P":873597356903,"BUqEJE4z7NFu":416573590401,"mJoad6dNtZ1s":652615752242,"h6A9zofJ0uQv":528794565624,"jls71cMMw32M":702181460467,"a3Kcg6Y2kfUy":923014172129,"fR2n1kIRtOW1":277313269856,"FImQdq6QitOc":853479214321,"fKpWYYNyGHhv":59592807625,"qPThCP07YTUl":721135294217,"ZlIrjqxEuYjn":679263126076,"RN5Cltm5aYNg":274421078472,"IkOLgxPDYvMF":381458931353,"ij89okedg46h":758162583173,"PFgVerMEkl86":723142623064,"6JLOmq02yG30":947255547221,"ahOZMbEsKZ9s":432013989453,"S6gxzTNBRo7S":142697100079,"y2Aual5x96e2":585949258726,"DFi69smejIAx":556159730944,"60gr0zoItEO0":87694838535,"1hZwDreca6cO":368086150178,"Spt34YFAVQAn":730593873889,"dK3hKQkQVCqJ":995630822530,"bVY8q36Vkm3k":431144137839,"Z53ap7PvrbCa":787315173268,"wvTySGiPpi58":227720431901,"NB8DSBEUY21j":576586816473,"XddpD7lrKlAh":347979906117,"7xz6Zak87l9e":274463448228,"tCB4ANdNOfMf":50214574053,"KxSsNu8NnJic":603296168453,"Ut5sZr0FfqsH":43188215390,"Q8uAWYIN7HfF":2158207475,"lXfK4TKpWWlu":936815080088,"MhpOueu02lnN":471834663414,"RtNg22beBEHv":886831765795,"CeKKHHvBqkoi":428111384992,"H5rOH9WLNOzx":597726148709,"uWNEHQk6h9CZ":368348417243,"qVX2OVCLDbFi":259198387622,"OeOVuF5zwryK":388670275700,"AlQReKujJxmJ":521971375433,"g56Cf5ehIW5X":810653173291,"UaY1Fr32Y3Nn":358091935628,"RvVI9pPwe0x7":698119218022,"bTBgsXKNqjpa":929597549456,"nnzKp9dY1kVi":898420634713,"d5iAikEyp1WR":496929001820,"BxQSuWJqAgRJ":936089627333,"r4eC8wYjsWfx":869847806023,"xqrvilXLvZjF":36762059462,"bQJFKI6DpbLY":602532506751,"GNijzbaD96FQ":990887270284,"EFnhfphRdPYL":291017247882,"sAUScoHcILyd":546367639656,"iiJJUd7xQWdC":802771991470,"iQL6VPe1Mqqs":756802586671,"KdXWxm3pS4SL":232656062548,"848PsSS4wTVe":34077553600,"T312SsL8sZ3o":109296018766,"zMzVzlV0QFSq":329426381301,"HgNs7idwQaW4":540956075338,"YTnlKPOHvaPs":44574419272,"5pAw4BxHoIGZ":10070245041,"Kvoa70g1vGDN":429906504073,"r24z4vEBDhoF":823813617480,"DzeZfN1dakfa":693229520688,"QFJ7mqCyNlGq":874065370368,"9ktvF9qjioTR":19519569435,"fHKK3FbrYCWV":707924869315,"2t4AMgz5d5R7":526672396302,"tgrJOaKABort":626218611224,"hTCtXNsUDx92":484801593048,"PItLMa2XCff7":550695441876,"JiouZmNAzxMr":697955924021,"JywuEabpsYX8":928679378012,"PhV89J4PqbmS":217522191913,"YTNUp66xCV9F":641824401745,"BRQxqz1F2ZHx":587588654216,"4TPuRnZKf4eZ":919741946806,"ueCADF1HGfqq":290473837934,"6x5dT62yKUxx":259369111037,"VioKiiJLeo6x":92547995922,"j56O3PybE1Bp":615257000048,"yU5gBSVyuhfQ":757322689943,"QWYfixW4e1t6":368924745864,"z91b4xJN0Zjx":252343591830,"YN84TQbCGI3c":896916175055,"3LL436pQj0oP":989098403893,"SIO5gdJoA0U7":538483757843,"KGizQFMLdJ7p":358657518368,"DsxoxbyGguDI":911231475093,"ZkoiUaSHCeDj":922174337516,"cVVRmJWNw9yo":341443340882,"4msT4sHHPhxG":15662030612,"8zmwhWdkRYnG":105134066313,"03vgDfsolPbj":276525180190,"72l3fptvODkg":837648981209,"dCFy03FaDS9D":952971658491,"AyC93ja6vvoW":597422423943,"Obh1pRpepmzD":133920379509,"ixx6JMeMD9cq":381672540774,"5oMvrD7xiFC3":539336425231,"GEGRKPP8MocP":482524252495,"UN5k5Bs0pXDn":494159216445,"cTQPweCTdkTM":51335957213,"xbLH6jwsYfL7":183525419385,"d700IwvdsCOq":127520228377,"MGRIhac3Lru4":960419974949,"fYUfnUQIsd7T":29756671588,"5g0vP9GSEhR1":836007950813,"Euhkfzebt7b4":485989133276,"Ua9Jt90xhasW":446059875272,"vbBWpsCDbX7n":732150819515,"fC6kd0HlxwEJ":708322486181,"4cqCdDSviuBs":194205310836,"sOFzjLj9qYIu":519436384218,"cfRbfxqcBQNk":299793941740,"P2yB479NP3Ay":24172980052,"K42pDZn6TKF6":5918317394,"G5asgfcnLOe1":125992678353,"VKoSCOTtV6xw":852596517236,"ZmpJ5BBXZIs4":486590325769,"uM2iIga3uqn3":942127727734,"CitzYF1XLQgZ":491029097930,"nVEmpPJ4cNNO":927491411597,"1tkFfRQq7T5w":275932451636,"KPWdRgybCnYD":943989742779,"byvcNwduUFpN":992545470963,"HYkMXBdwUkBx":925628355991,"mDSuhKJmQVLl":765694903516,"PUb539gX47Ws":808828158824,"b2kPgVvx3YHn":786338952469,"lavulPCWQrgk":902592357854,"CoJNR9VYCLh8":287537407417,"0jxyGxpeiNrG":131630808859,"PATkvpjwKI45":297113677937,"kp4gjqfCK4DE":860769592832,"SEWLlKmhfKHI":237382944463,"vuG4Jr3d4jRy":118151712891,"wPM6ij143llZ":535572269300,"89WomrexO188":575731721495,"GxdKjby8oE1G":141536708504,"yoUsarxAVnUp":214655223340,"tBAAjIPm7VRq":488350911744,"FB8uLWSjOef7":326595262538,"Qz2liDudtzA9":268208301891,"AScrO4cMVltU":582438425653,"1dObHTzrxHgv":437513206913,"HEsrwJE1Wrhi":768114946148,"58ttcIwRI8l4":81656925979,"FRqUvWFPNr5L":908409523074,"CVRFFunXhtgm":83673318780,"T2kDyOrLeWjb":853665115066,"PXyyDxbPErA7":758132251361,"PPm1czap40iW":760885349049,"4YqjL33eTLos":244195755565,"ZpziONFScMoX":883766013929,"50TUsYrCJhzh":309015025210,"M5Xv70yaWDYJ":460478973612,"ZXE6p4qO4KzR":423552017218,"7wQZ7QHWSsP9":677836550224,"gCmR64m1yacf":711736648828,"CRR6AhDxhBqb":590573575153,"FzqHkKVGpudh":856369577049,"ILkPoz6aaiDX":339832083267,"YWfVwNroD8ZM":624955109899,"NhYjFnZ4Ibm0":904463085136,"kZnVPYUU81fX":417346319206,"Yqm5DGw8rWBZ":882265918928,"0ITg3SOOBTbx":701922399979,"KinJYrXeVRTx":29382807100,"qgGpeG4JoSYp":908221279974,"d69Cq6d8lK6l":217787240333,"HiJUudYTySTS":182448521067,"iv8d5Nl30YgT":925560533781,"wRmzzkzj5Wsm":2049734042,"B49IjfPBWONf":559444331565,"oGTP9tiBzhfY":449736688935,"fbI5aPvpnmQb":850479934702,"XQa2DNPOXBqt":287073015822,"RNGgirmG3GLp":393323362001,"nwPS3tV5ZyzP":787050545936,"MxxJp6Otl3iR":974324563814,"nHbAj3sOhsLP":508159471420,"cCacc7rcj8hL":140066644313,"aawRE4c69rci":122405085004,"8nFfxdOfYR7i":744214730406,"NHU7MKN1YwBk":159084474147,"5XipSiTGN1iO":588149817034,"y09bkbCB0WoV":679903587612,"HkeP6DC8JxtW":39321885738,"oiJCKne75zrs":342434951438,"Uxs3wylQST8w":691215768532,"H09TYmnuBcwq":936770452625,"mUgw9Az8kY0g":395600341525,"kIFAxnxYDSfA":687054945590,"F8T4BMrSopaG":896808730979,"GScmJI6xaZkb":941934183840,"ABkKKPvql78f":274415027574,"CaaVqAjf6BZH":543389383054,"DlN2eiKfiEHj":882005886734,"ZDOifEnbXzPD":783389490793,"L6DP1DwshRs7":773653179747,"5S1v6Pjpl2rR":636488838718,"fHBILe5NRHQZ":428906110602,"r5WtFB4NKM6I":851413405369,"oIDVNNOpNcUW":165470307561,"eukChyJm8545":939952333534,"7OFocyAxW1Fg":753337793115,"cfGiFAMiora8":528362937856,"KIDiPOmuUdzt":295936950800,"53BpisBK2CHw":844828069864,"sVfaDVFYEv1E":896785490773,"PDtcfdILgIAu":490361062129,"PVvl7213CIrZ":636518713876,"ZaSkerOCftes":454738622210,"jg4X1WmYH0vt":712715511629,"dK82fhcbYar2":238628098915,"nMVguuOdkDrw":299198512063,"8BSfzL2FVAk8":483157907060,"fdIsjzYsdDOH":444216839629,"Yio0SR8HhrGK":373181357557,"n6Vc6qa5k46R":159299231563,"2tDKFokwt0gd":640601348798,"HOg0ExE9Pb2H":424837610878,"4oXOpmTg4YOe":908649657435,"h4tIqItHy0Gt":396125099394,"xmYEVlP1i4e9":157327096200,"W9rrAG2w8pc5":332550176818,"kIdyBUBXtCow":626958641241,"3v5rcgHcGLzh":559126555787,"NNMeYxsg4vGe":220651873059,"0m8dpYcgxl7p":985933289252,"UDkqO4IVZwiX":512826584873,"D47yDGSJlhI0":852945573489,"WX39b4drKNDa":353708439161,"HlY9MckuaEP1":978209802413,"wBWE29dBHrnz":156911218876,"WgEDAVCUHmZB":997828078392,"M8w5IZghJRzU":639342117910,"5VZGocXo38Ub":88197825450,"WZT5kPy79R6q":603234596029,"6a9MvLDO0vdT":536572620828,"Ep6jqtddRE1S":813820774432,"AbrerxqabZfZ":555781436749,"9RV51c6kWZ0h":611022979255,"p2vsthYfGhOc":812069403922,"90XueSBRaPWL":112480042961,"ldJqV8t6lrI3":401824412311,"XrvVGAARcuY8":720987962887,"zn5gmnUB9kuQ":259627206488,"zYMcJ0ogHur9":973198794479,"rzQ9jDrh9bIe":738041889242,"o1mOWMYpoPt1":425931469097,"EDQFzBS8L8yH":895216502242,"hafzJb2DukZz":912253700223,"QdPJOh9MyKzf":201342198520,"qcdJRrl0LUM1":400855198197,"jRpwDCuEWYk9":555296265477,"D1t8m5xIjf82":894905569828,"5abS3d0NPZWF":752952361694,"NRbgeFXhwrIc":725917766585,"aorsIXbGZMZT":307552655529,"W5hhWReU8hp1":884670678978,"j3Rnz0X7LJ4G":35072719905,"g1I5kwKEMIB5":891273404774,"uXpqPn9SC7Ps":100864403851,"VQLsNIDETET2":454935982444,"n5UcMciHjrxb":351603993609,"JcYj6XiuC40s":231604301325,"xF13ztwLrONX":667679270610,"GvHWHuhFiMyN":953554779477,"RYC0pItdb9oP":268740048471,"LGsKVLC7DfOA":431775060086,"oSexfBlaY0ln":785014342105,"viACEm6RXTK7":714012617441,"sV7vKceVvnaG":117567765788,"9AEL1PPqM3uR":634082882362,"VM14S9FsgSU3":743037626766,"QB17c009nxzH":572134531849,"tVQI5c40KwP9":421229981611,"6jUrjmTutg39":619328812596,"UfRcBKfNxiUW":421370357057,"Df5cTCPWW4th":336562102023,"fVkt4dBp1zYk":121840012669,"XqWSmCqa69vK":691001947045,"EUDLvYwZNN6Q":674816631813,"wWnccS7JcdXZ":132113185436,"J6v6BpzEB1Lg":938471284056,"VqHNdOAwbJY0":19873897322,"C8PM1QFoGtzg":440491199450,"6w7UVuYaYsjg":442535796019,"ncYN5T74SEfK":829711089820,"IRzogJGJs8sp":925373255060,"ljUaTpJE3HkE":997730907428,"PG622Pazoxoq":313832587731,"icrzp61YfPw2":110660923434,"xPmVvuuhrOHA":513296075082,"frABAFucyeoJ":755636115301,"MwZcwGAF0VEz":164596319187,"vgAfKmTei3QR":706616758791,"rHnxzobIJfLq":423419903693,"8B5YflgHgR41":39064196360,"cRSdBV0qigsd":932794927775,"feBTBL5xZVpm":221457198387,"AAWfbpfUCBjS":728179519974,"Dz8XHjUWdZZe":812482073196,"z2PaeHUV1Lbk":151342044273,"JPxvTcPph5Js":866855201484,"gk4gMt0YK4Ux":136800966964,"dkYBwS7NGYkI":283336157740,"2996Ukxjzlof":509598367313,"dCbdYqC5Z5NC":132866171846,"wGBwxTa2umS8":167232896689,"2gfzSIUqwbhU":332555479933,"fYWvUbJ1GPVG":867008946803,"0I29NfSuAPFu":903009236973,"vbTvE6rXItaf":915240082763,"pz968ZRNG29m":863103707972,"2i35aGBVzZsl":47594575781,"FEhGwsPPXFfO":766967246487,"7SUOxPdK8r6s":702315730056,"Y8pZM73m9ymX":738404085846,"KG4odpw90E8W":743340492512,"h5ojZDAK0jy1":435819820998,"QPAFjjV2jVkH":895395519698,"QuKe5VvAcJxG":943849703759,"VzS5uFfTZ06C":738277993949,"0JbTiLuGMBwc":492233757536,"RooZ8l4BVZBU":126297428254,"dvxNB0B8ldnF":551037276575,"TjsclF3PP0Ef":225316252727,"mJ3ErsOleMdG":159405255534,"zeglRYWXzoFB":729218628076,"MLb6gIlyygcl":556223560012,"NpAyvOnJXCxj":383285716291,"95Dy6mwfd9aH":530801996258,"x6yNJm9LmvWI":75026239190,"X8TcUTJPI2LU":905228951463,"Y1Q5VHouyRD4":671115519439,"g3NnxYrs8jJO":223035910550,"IRBtQpojOMeV":733369207945,"Q5Q1XG7FDBg2":377952485095,"xkU0dyCyMfTr":814034460457,"f1zsjjh1EFCx":710295861861,"d6q5hEUtSpo4":806910454492,"jgciQ4WQHVjq":375667013148,"teWfChjoIxxP":619183228327,"AFPobstfYqEy":319956282940,"383y4wbAmsIB":867529182837,"0xu37JFmu5gD":301115825762,"x4PCnQKeO77C":19227575858,"M6apo7SwmD2E":747596047351,"YoI6O8POrgTS":386147610517,"N3dxriFqBmtL":364667951190,"w6ZQbu73Dj6q":474193647144,"jYDr0M4R7qpQ":649030434906,"ZzJ8329QOmoC":953710612222,"WFupwPnj8Otp":260437821764,"Pko97RAyoe0S":524021436293,"L0hkwbbe2bPQ":48924401060,"eJaIwD2TwBBw":50176149004,"fUN4vsO18s08":279048724352,"oRsx1r7pb7w3":295302256345,"q5oksb3r9BHp":471050094543,"IFT0I5ZaTll9":406875389986,"gudXncdzQ3v2":146382418034,"ufWnit4Eybgr":453907193187,"kHPeHOyKmCaa":132951032041,"y8AdlEdOlAjs":987203271016,"M69TZEJjkov9":481235452491,"dGuTDuEXWmjw":59320334735,"vmKcr49z640c":405434578495,"3SysjjBUeFPw":105554543939,"g9A8puHCU9cM":179879246718,"G2Cgpr2TmOun":98280535925,"rn1sXQ12Vryd":305167728752,"rTfKfqjLhFk7":197584709380,"Gd9ZumGSvgGQ":573634271036,"ekIx7byY4LWl":984000194640,"C900VNZQ422K":449042000583,"tffDpZkJ9nIC":892804767083,"80lzCvh08F0N":221518657779,"7lux9bmyPVcn":814500893357,"2aRE8KcWZAKK":485691800606,"gj1nT6thkJQ0":521336451047,"7dbPqQHAT6YU":898444552986,"5NFHnsix67zp":486796622994,"bDyiaWsDZeA0":78617801413,"HsIP5BgzxJ14":612102684911,"SyUKhXQWjd22":603690341168,"xGWQcokBeSz7":257695557879,"D8LUkC7sFu2p":865252255058,"m8R1F118Oa2s":434789348099,"Gzd2pmlr3eip":431584218269,"GimFer2A4Mzt":510422048633,"uxwCnfgfU8Zv":646982168505,"ZyYFiBHKSHu7":869020883797,"qHqednl6zsK2":980814291102,"8C9fJ0JgEPxb":858020708047,"SwTV0PnirAm1":206272928337,"hr1vK9X4EBkm":25434567683,"b5obH5Tq4a0J":285730303595,"Psw2wmgMDFI9":602548963232,"tO4KQcx2Jgm9":139154823575,"HpmHoRQNY2j1":739447216924,"f1xKPKGc6ktP":438126657443,"HifuN7yHkw3X":562348668622,"fuDutOeGCz8S":862034670982,"B9paemqUcDhR":190469495086,"we77aZWI98kE":64031773066,"ti0aipIbbgS3":961529525706,"clr0W2LRBHBJ":416516304146,"imsFLJzY8EKg":336803028319,"7vYIZWNGHTDh":539025021855,"JOg4FfYlY7Fd":527314385378,"0wwnfCH88HsK":860612038113,"6B2UhF2szPiy":146805664087,"urdysMY7CZ6g":218770888200,"w8X5xO49Z2AC":709950576320,"4Ug1JS14vrrS":410936423863,"nZ64ISo0pML8":88702633269,"NJz4UF3n0zJt":496750376338,"9GGneYrjEwDF":958202347387,"6RBR532I0WxD":554558892969,"dJgzTnNGZ7HX":978750082684,"jAF9sAPVpP78":71749965420,"ZWPwxmJzqzVL":739566556257,"OPKEZM8hCLiE":804179426680,"nPwzbkwvHRNe":459299821354,"GsSjhVkM4E2C":560670470903,"ItHPzAg1nDZr":638425750749,"exrh9IUOyCeS":826581757171,"QrEzMjWEIyjt":752033738780,"zQe9n93qBoNc":71354408671,"qKFubx8suHMJ":638668627453,"qVxHkBq7dQOy":921609615711,"IlCM44BM86Ls":968358253430,"Akn1UFV4xnyT":257049369467,"nAM4sp1HBR76":986343642221,"65kZYEob0mFc":872134170372,"3G5B5k2gN7iH":35555035204,"isJyh1oPJmtm":240948069727,"MLcZZObs0s5k":546697549220,"FhyP9sVNqbPQ":715930372300,"tiqNM1kaArJt":377080079995,"3otunHwvixFp":370010955603,"OkKjP9SSrayJ":322430682821,"htB65wL0ZYCU":706294232822,"5MC024fnNj4w":630362820640,"bJEVANcOow8W":639637332820,"jZksnFSCPnTe":86499283801,"XyuN5H4fFFLq":216219961994,"5seqhk5gz0oc":402687775499,"Oq3faZR1qSV8":887496490927,"A9VmSKgG2irA":206983173043,"tjlgf0GdwRB6":749503288967,"wXRkNCrJvvOe":89459087862,"uY3IuybuYZXK":870583674361,"jXW6eVK6T8N1":29976054243,"FxpJQdM5SVdX":491133574082,"nHsLETWYTgPa":787261905807,"xSZqdZt24cDb":471544983773,"oFwCTRcRGV8Q":956279298454,"sfSqcE2GWfvg":160465058724,"pebXwqQjTTMx":283119455643,"iXO6KihowlxF":492635375410,"jgSkxJGueeKY":395731647421,"0gRDZsAJbsZc":659235169531,"93XU5LrBmer8":562941148630,"E1A2VcE02KiB":371149762213,"Ejgt3VDfHwiV":322066616674,"gjAzAdxqdBfu":28629542769,"b3zLGxIegBb3":778897393785,"zmhCrtb5ekUT":701911053347,"PqeMyDxfhKfM":364211599493,"Tb6GJJ5WhqEV":43548772041,"JpJWYWpNk1Xd":180035427430,"VBbgsDxt74A9":284003454563,"tcZJfLSONI0e":525921190547,"VlMzj4I2RPIk":473600163269,"LTQKN3nKGEhQ":320468892342,"wcvhKw86wcUm":927817666321,"gIhjlsmpdxlh":729146784882,"k4w2E83nuhAI":440762923188,"0yrfSwlQTsN0":788304266137,"939DFngzv5HG":332297713526,"8LHhlbwwywj8":991037284119,"i4coYeyu6iVQ":253666929389,"0L32a51zUIti":502377537775,"bwFfb1Zi9Ag6":719417628864,"xsAiuvU8XHgW":53094349386,"ZmGquZosSoqX":575813942773,"kTcNLCTsJ5ws":980135618321,"JNEzhyWzLjoB":221228748312,"hpsRpjG4prO4":344997696691,"VNWhKsqeomDa":850788933516,"5aCBdh6EMnLM":942456645945,"8x5zZqWBHz2L":651385790296,"fmcXKAbG5hsa":241252672025,"IAM8TUAuZKgP":654927541641,"wwoGEyKAL3yv":901123808330,"XSMmHE6Ck53h":731938560846,"0pTLe7b8GcM9":212578278448,"Ky9n43kziZRh":616620304943,"qDkI9lr0xm8O":370921017744,"jA9DPtwLXdwg":971132215421,"uliJrZk0lPHg":782998838390,"EUuKX3gIZmtN":790250818137,"7RZKlF8xQAcW":554374789102,"QO09nzq8Cq46":915443086274,"Dv2J4DlEFtxj":162104750979,"kyXEDfLpfjcw":350199720556,"VogfFaLGR2AI":301138228479,"u9K7mjPSsWgt":380787408981,"mU9AedcrB6mS":196952084353,"cUoGUGM6EJ9F":834260403372,"BS3nUNcZNVAW":277280101233,"GuBTCLiafKR8":731645844404,"nUsCVdOrhgbZ":877263815331,"PDqeWCbF0VAS":587413548519,"UG87mNLxH8L3":393045181606,"Qe84domru9Vl":606248218839,"kb6C4q4N2fnk":446893796179,"QmyCirck075S":477345286532,"L7rlrvOeqmIz":862684136617,"nGcMMi9R57Xc":262197940075,"oXXxzoZrJ9nr":312543589206,"LzuY1Enz6C3Y":375700565848,"6oben2962BrG":914744728112,"7av4qWY3nHcv":327774589308,"qvsL61GmP5ln":695618586256,"whqS6YnWn9o1":583503347355,"HBLW9ooHDrsy":248878759326,"bSfpuLH4WXZa":187216675077,"83fmf83l95yj":620369870529,"MDmPpaHyzw9A":841148837105,"WFvHMk13AUvb":420429025624,"zggdP3VPMTtL":166147497396,"jWsiasTiTNbh":487028212981,"aw9oZIJ48ZQy":253196845941,"Algot9PugZrO":271769397411,"Snv4sgyYBt8Q":789511128469,"NjVt71zDBJhH":758302718726,"h5cBE3sT3PNR":715069980982,"ZRNZQTp8unuc":65755426026,"4CSHTwv4b8qD":424379277806,"awpsmxVxd6Xs":263278333930,"dpvlVdzm42so":483789943577,"1emadkeeHM1B":582098594904,"bhCqE51JR0lN":636246257030,"UBNgtIQjXqe1":287481159246,"3fbRlYuU1SFC":865735299211,"TTNpJRAsurCX":59105341019,"uBiIrABvneSy":45743285072,"D72y4fz8RsvH":983527368545,"cVHMOFmKEmm3":972153914657,"oDca61TCCEke":805601947114,"w38ZC2yDeTSf":342047412158,"RJ18cPBJbDn2":911272330373,"9FfuVEDWjg8m":638773270818,"4It5L0FYRnvi":367561741884,"JcQeCRebmBrK":586087582812,"0oXXgobKBZrm":439367081956,"dTOkwZOMxZO8":734601438451,"rZQVgPjXJcgf":758389073775,"HjcG04UFLw4M":725382197254,"bhPAaAfHwtzb":866700882639,"kUvMGTV29Nlv":891449022904,"Bty7cw5CrSj6":402693377416,"TpLwGjGA7fHJ":75441542246,"rJpdgiCd7auy":105881644230,"rwPvRqItl134":343840882070,"DDwPfkUx8t85":983166978822,"eMLFNAGMXiKz":364614490372,"9qBMyHnPlIt1":294413605533,"lLljDEFJiay8":863738715946,"JkfAFaylTN6p":124925763062,"EacdiFC4aDqJ":367091148540,"tQ4AWVm9qTrj":479959688492,"hVVDlThJFJ07":910946004490,"YTTVsje9fSZ0":83453269861,"281olyEjSKvc":875814793856,"HkniRwRt5si8":317118603921,"LnUm0kbAQZw4":946437765541,"OtjyrAkiZs5Y":634886795681,"hbzlM2BeS7Vd":868942924366,"LHjuSh45to5t":881787438587,"DBAMgSSO3OmA":626209689539,"j2G7tXfTXbYN":887762579504,"EWQ318nTUixb":537559905861,"cGJB9Lv8diyX":96508164521,"2D80nMNRio8S":35686408584,"HC2lMMER3wjk":86211103917,"lEAxoLFpN1g9":297737574911,"wTmO6v9RbzAO":116819638517,"FN8foXl4AEqm":686860119772,"HHOLfQRvNql8":374221873627,"E8aS8qa8ugIy":702860727467,"lddSDXitFHOU":879836379998,"RJJqvfNGcx2k":440048473755,"Ml62BBrySgAC":92563400535,"sAS4JqUg10op":347803077919,"HHgJIVRGdgJS":952463473483,"hsXglt7eTHzh":947603703131,"BAeOieAAWIKI":911500020533,"mzKfFcKNgD3q":689095904799,"iXkWC1sJpprp":129654601910,"XL7hZ6KoV0Pc":358297329767,"i4UazAvpPGo6":8975692137,"LjYi2VyfXmW9":186166780544,"HRIXIOlJnauM":637344920506,"1O9LvCvIbqjV":779189629712,"hcCF94oP8cm6":781359097756,"Vf30xszeNuaz":482345351925,"KpW1r2COq8Jd":329528920297,"tpUSNpRS1hU4":385922196766,"fB8HQSuWCl2z":725555921187,"uXtL38EmPQFA":738475061485,"BOKOvt9MvNxk":25253812502,"Adz1QwyV6s8M":333604866663,"YwFHRUeezSI7":575077932070,"RVn6eovMSlB4":432035405569,"BKcJpu95zzDV":293965004628,"EMCxfqL2nnkJ":195063029221,"DDwDa4ClCElN":220903985591,"Ox5244vGDMKY":614364605196,"ezW2nfmpxyc3":852561725548,"Yfq6LAgOegGg":890159387025,"XNcpGlO9mMen":537940379894,"x75gkytlZFWb":661291650963,"mwpmg8brrcqW":527223443002,"dQIVFP5hxXgg":718402061955,"xaLVBYyZCi4d":622340739290,"sH7zU5x7ea9M":195589226160,"b68vAMYInEDR":684819980673,"y01OgBuLQCxd":557209946060,"kO8iS0qXcOZ4":591411435408,"dEkLNlOr85YG":580736204941,"iCpJ2imRsZNW":977179728526,"toNedfxZIDRw":615822883123,"Mlk1HVpL6Yty":311070651356,"ecM5QXyqFCja":253713287172,"52tjMtNs97W9":482319960595,"umoaBJzoVIcB":14708678270,"XngKAYSSahYs":502784952557,"8vMLtplrQPzP":834635618687,"ipQh9B4sG3wY":614404318124,"3trV1dVtq8ty":148233175690,"njDOe7chEzRs":960535928658,"F1KsNpa9wmsR":733683865177,"70AMeEuiwYiG":691016598217,"DJcLsRLY273H":296271308819,"edkwW1yBpOAR":486806711466,"YSVTRIHDN02a":552920924961,"hoZSP7m4Qd1R":863815960465,"HZRfypOsuf7J":539990686760,"lKxcVyDicMfx":151826097417,"9c1qyUj6W7kX":893694244356,"rZfIOdavosT0":948138533868,"amvsUbhTY0k6":241064829600,"xXKNlzqDI06j":64095092469,"OMd8DJI6fqjq":928350985170,"Zc02rcHy6s27":555907517953,"3T2Qip5evufD":805566596131,"jVZdEP1yHS7G":195133567704,"YMfKZN3TRlGW":258336921226,"VtYNsmS2bI6w":273469785552,"dkO10xWP1zoH":574057288348,"Vs8yCRgPRVgE":488973519943,"LZiW798DHTWN":846941256859,"CriLpA3n3CIR":538174478671,"ZeGM0NLr0jSc":386883604313,"DcElcnoxtNkj":152015727401,"Uit5v89ASR5r":468664161722,"a9b0uuePyFGU":421452323423,"JI9qu50BxsSA":927490237405,"qyEEJU6ChlJw":319800061650,"YSz4UC6VYsrV":951646893013,"HQIidiooWBGv":570820543199,"VrGUJhQmH81E":340842588422,"fh3kKOqiaNA3":279697363740,"hVG3zHCGJ0Df":610026315917,"3lBAZPAfoFSS":21495668474,"2mdUF4zQT4DB":317332155495,"rQeBHde2KiXj":593556782452,"YfgvECQ3EhoW":237332011211,"yAq0nChmBBBG":485547650580,"FqNsqZSYwXpI":121493129410,"EPm9YNAc6nBG":675080415288,"qKppRkpUdcnu":44453557611,"lFyZmnIuDXri":415612773126,"BgUyuvfV2GvB":782605851393,"RM9AvhWX4brJ":611895891440,"fERIASIdswec":271260849203,"LWBd9jpptvwn":932655181410,"qIbkCsB3xQ3j":881062380354,"POndqzG5naNJ":479799874412,"cl1OIwfwScek":621538947448,"gsIs9l9vFm9z":360919072503,"EGJF40THOgvu":682682727183,"AtEk6edfMHcb":267222964694,"j94AxOdKWdfz":757947048405,"0yAfbmqicBLk":315925584974,"17ZvRTfCpn1i":200512219055,"Q3pE7Zz8LT45":725264087622,"61ZRiUX7vQih":250612394706,"pgLCE09BoqLx":390824056297,"WUqsEuu9fMNe":628641472039,"ic4ZFLYC0STK":489713845091,"5FOWrIolJCu7":141245904232,"U3uG8pIKONT7":779553364328,"iguSVEITNUlT":487448906805,"4G6Ct7smMQa3":614627703037,"5vyLXiFOdjdM":135852806620,"vVh8JzBRBuKQ":468865597402,"bTrLGklxlg8O":89681213409,"Vaw5JdPOIIDE":59775470033,"zxajfs0VcRHm":192383333114,"7P0jPRFfpMTn":5909616425,"5GdLdY1SnaAf":346760591478,"fZyMzTkmih1P":956803204139,"549WgEdWLe8i":741149966776,"LhEwm7bNLk0y":797018113206,"QNutKXgCQxXO":534991942837,"H0zoqnEjRMtY":890157627483,"qiP7vNJCeEUf":389956304192,"SvWBULPF55UM":199512901962,"UkMxxbS19OzX":573857114787,"FCSjQbkIq7Sv":662068580792,"hELvjIBXcwoL":823472696200,"E44Ft1JkspAL":862803767529,"fzj48VJvSaIx":920185735958,"JV8bvzx72uOy":375832385147,"WAKv25NPybUK":259814152115,"UX83xmzy2w9k":285076888148,"7fVaJJHqE4bD":292197744434,"Uu6UIujLcPcB":876980887943,"At5Wg0RMObxj":855576492027,"XUestYhR52W6":147043148023,"tdxL5sdVlp8A":406517903972,"OfLbFWT6W0i9":586324359241,"eHR6CvyIxYCG":753466013756,"XacxpWQxIhYu":3216711468,"CC3ZZD4U8XMs":573515176944,"wAGK9vY9D24v":787629291976,"NlOpiXsK0WLL":420511849102,"mrlyWpr1YFbi":507562914947,"3PrYhxdngO6x":887331981593,"rs8xZnzUcZ01":443571634283,"WZAKSfQCOuJp":456868172340,"3RYTBvLeSetD":867286097098,"vVzLclg4KSRA":561798769260,"4hEb1CgFcpPG":794890919431,"qKwOZcU8UCoN":80273236005,"z9XJJvlB4DYN":988537082625,"CezbpEkFNg6X":923271837565,"CxxZtpsJXvE9":482396664156,"2nIpfTVsqzsz":952050956847,"Y7ZXWlt9EdmJ":820716841556,"WPqoMHsUT1sU":787256676938,"kRfAFc6nbpS0":349593363613,"utVikuDAsqQY":239798328142,"HHbPtv5Db6Py":998755881643,"xaDKk3OqrRHI":201710329635,"OVgQFDwaKtag":843275451115,"srj7rZamYMHL":810643083413,"kCQfnr7j8cDS":495584573360,"EITWP5gUpM2Q":168133508404,"3VshxUaK8I3N":948360313758,"yivWCK6wT4bp":530607020192,"kVr9kohKO6Qe":652567504262,"m1g2qY5kt3DD":253442811623,"s3uVJxejS12n":791447867846,"017Qwt7v8SHh":811808157534,"eRS1QkF4GKcL":978204526372,"WvAfN6K2VHGY":177710388068,"da3vBY756nrt":424076466663,"ZetmyCO2GfR2":583527136197,"f4r0ak20Yi6H":731397437743,"fNOHOFnyXCdT":267803080185,"ePfZBTTU9uxW":642816573117,"jLlypDGPwCFr":282078877080,"NTN4l6W5KvZC":407330213991,"ILnaCwNnvgw9":653293399962,"x7231iyuNOWo":417738284677,"S2ktY1l4rpb8":147426695720,"tVsfzmdcPu9S":427577629451,"YHPWNcmypUm7":989417227878,"MKt5CEGZ5Zpr":945161424279,"GQX7bUT6LnJA":270016312964,"KsolHc3ktcbt":764217934102,"Xib3awsFcShk":262299857355,"Gr2iiXLJpyaP":515811887701,"sjIGNcgibShl":897312291303,"3xrwyol9lUiH":96095236395,"OqzzrkXvIk0b":95073989166,"tOhtM2bYJgGe":633324411568,"LtjSdgZVNfSa":555945070706,"MlIPcZs7e5FT":122795415792,"7lb7YnPH5FBD":521600397381,"ByBawPvLO1Jl":523875888965,"nlTdxYPlYHCW":97490724297,"d4MRK64IanfF":790224232463,"TfqLI5Eb5XmQ":508757829345,"x0LvlHTTSzah":192077675747,"AlgkOZixH6Zz":95186006109,"tpEKMteHnFJO":322882956909,"24CnqGm3DqBB":521242769763,"0ukFBk2OGy6a":82527278115,"WD39pud2PVUh":241525679394,"looXOUgVgIwg":581561602670,"0UiBjNgABSnG":877227431556,"2HYtA7hhR2qu":822730290659,"X3W1MaDwVDSf":960943581253,"B0uZdKxm67FJ":703627097254,"LCYxNy6Idtqz":869463074172,"rgMsObY2fzvU":231603510155,"yWGnulqnQSNA":620122470578,"O304QMIuCPFp":809851801222,"wr4RKo2iaOyT":367345117409,"A3ch1qlUN9jy":483914595647,"3MlkFtDcjqYE":644284340586,"mYN9AJLlfWzz":180420330878,"RRTBdVcvFrar":899584241493,"zLX24gCiQ6Fr":734025017254,"ELt0uSEYIZfV":328190857167,"q6JAQrdbh3Vs":759779575145,"3PazrKromoad":87083369786,"8wjBZXqD39QK":3922546700,"dsK5AXVxQft3":672169912366,"jPZ9VbaTJ7r9":192884799828,"b4elv1wIdWrO":98019913227,"LPSiUEB9cau9":88697742337,"3Mk1DAQdKPEY":302567710607,"E1FCMFe8Ljh2":27346499146,"iXbQnRdzjr2S":460550259055,"7HQU369WH3iv":778051416614,"s7EZOM2MiIZH":869966824394,"hHoTkZAbUAG2":458243164233,"I3ZbOa5bgbx5":714711757836,"geWEw7DCmqhA":427064540366,"aRdfSQgXgUGq":167312061855,"nZGWPWTfWbBp":744742806032,"e6Rezy1p4mC2":748394849257,"R4ki1UddZe3p":111903844903,"tOJnwO2ADPgZ":424094307,"nCFYGoQJr8Su":86692315607,"qy5sqiToYbvh":327985223301,"HcSe1vc2NeJl":169396695514,"pVXc74dxiJyv":423489466842,"qClFLlLNnGPh":482413144003,"UgqrJ1lppbD8":72729928825,"6nSxOdox1koh":313159124887,"750ojSEfsIic":707486478342,"Bt8ALipWY65S":626138515295,"ahOOasByxaRa":396459556482,"XYLRMiIbJSCF":567239181771,"YjIoEvoEOHm0":214884584917,"62tuaTjfvGNk":477355532093,"Kh3fux3CKSG4":552288758850,"n6WsDpBmjxZH":276404582444,"UhiD2McvrmZ3":488371999558,"wrPjV904fiE4":148977981270,"FkPvWBWIo00t":49355363069,"9MFlqDPIp83s":949384605456,"A3EExjRSAJQe":861745914703,"zI4RZBO6Lstn":908648423758,"2JIY9tRE0GO1":93922685714,"k4K5jSL8wPgK":965452898777,"qoewxE0ClRkP":428527195159,"aGPhuwofJF8Q":734551604020,"du6BGMUh8YE2":259016137589,"E1gNvRrHySs1":856808775289,"LpCWwqCJaJzF":9352623547,"ID5CKsWXGfH1":57471004499,"utk7x53OIjKZ":347772945917,"Jfp5D8UkjCTw":483349909301,"c9s2lOryw5jK":783409472048,"VDqyGzj8NGEX":886218772937,"tcgt490qHcE2":428566386608,"WtwIVFzjreQW":406083639116,"P7SjXWbOlsYo":855218434039,"rqy1Wav7IMSr":200876616932,"mi1S7GPineuQ":417093412196,"f57McEyuIx1k":164020822665,"4w83ogZoJF6s":807150030630,"ZMnf2lj356KT":373561289769,"mZjLZWqVLzHS":697620149122,"P7doUHO8QS7G":681713959017,"ZCihQ5xXP3Xt":968130057965,"R2FQPeycZIKI":163439211634,"eyd6E3LaLExa":720505297659,"rGYDMVodtqfs":841933987694,"DFxh2X2jbSuD":575878409594,"LzBol0KIAjR4":629021987855,"Z5Ou6wg98C7q":92969430146,"CVtpGbmxb3Xc":507901605981,"Pc6QQNigijgO":320842752749,"6yYop0LFSPcR":598978761062,"QHumVZXP4zDs":83754474814,"uJM0vINnTYuv":645591224181,"eLkcRyP7u3VB":140619979642,"eFIOBNce65XR":128567635347,"BPg9GnW68Wnw":677501226747,"j8I7NgbiS9w6":31079937929,"oGtcppdg9Wg6":942286704194,"sYGSAMMsEVCD":221081456624,"t9dWB8PnJMnL":237999187885,"T7Zl7iOHfFD8":982556135619,"E63eW483lXV2":512624006756,"d8LrwWjOp5Qv":587950275548,"JjiZludJygHN":94412097816,"iVyQFsTOyv6M":940037614022,"90D6TZbOmUeZ":582446304433,"LMlGERXDJQyH":873749357129,"29aUsHFcTDhx":319128056515,"ZpPdghoRhFLu":535333206485,"JbXJ87xcc1Jc":633345835130,"xaddjZoVo6Bh":419528143406,"VU4gRt2tnKGT":485854085417,"OABH6dCIuuMf":855284252434,"D7eEfinJ6c7k":258035496297,"kEpFFatlq0sw":215429119054,"oAz54qj1qDlX":923712172998,"WDXIYkWsk4xF":233451602260,"2fObw5sC0Jzo":473316676077,"FOd5xpQX6MBD":372690039966,"VQa6fEOPj6Z8":618550519977,"GrIsppGp0lCs":948695622423,"kk0R1BcbVUf0":5914257920,"pXEJzgtCeKFN":411621625174,"60HkYgtjaGWV":620940893018,"yBfx6A0ALxYA":735038214010,"t8HK5WfNBVuC":902881955998,"2uCPaRK5WTWP":528897361255,"FCxnu7lo6EMC":327566695708,"4A9b5HPvxcZY":637973113141,"CEdYlMuPmSdS":313026290596,"zbH2m4NxFOGa":399102207123,"hGiFvQhtGpKD":196739021139,"q6XBFMDysKHf":351906201910,"GyOCHgu4Tc9l":737623348399,"yZuz1PZJsMA2":410604925441,"kzqAQsR9dJSA":980989037820,"eOSoLCfGeA5U":17469118713,"cYEpEioYnvAz":92471684576,"kAF9ckNYusuI":696172494583,"AarHNKlp6e54":795431526836,"ZWrLraEVofrI":277072793037,"ec3as5ZDspu5":515920110565,"VB3iIy9lrZPo":769228508048,"piMULuv6HiKU":740761930174,"qr4d7fa5xweY":490330040349,"JJFbxaqFrwM1":723762228381,"jS06fk34v1AA":825250899148,"vo4pFhWvyIlE":514157788116,"igD5U1nYFqDg":727866544714,"r95iygSu94LI":57369696470,"0uQOFOfCOUTP":15903658658,"nrna4h6OwjFA":410022411437,"ADG5Qe3VUsU8":255798168282,"Qrvy7qqT28YO":489103453650,"inbHUpch4DFL":323513681274,"WUpcCM2hUtJq":868370485753,"Yj8FVW5Zrmml":890633981335,"hUm6Da0VWgn0":848982102399,"5kQpJK7up1OG":340732319140,"Ue6gpP5txGdY":246201454676,"ESbV2hm3BNIn":274973139392,"drgKfS3nmj32":544032440060,"O7oXK9kawdZR":38648982161,"C2u39BnwNsZS":604374741843,"VoKonYlI9JQQ":333355999539,"cn6fCCImeTIR":21947344544,"TRYBv7fYOGhB":238774012689,"0XY2UsgKRNSv":762578885797,"ZrrqOzlBFIST":997905980860,"BRyF3xNtNtjC":35391660409,"XuTRplgTZGdh":679350886973,"z6X9D0O0JTjv":909566650232,"1iMEG9dxDE6C":547012972637,"9RAXvc1hKl9b":515898352926,"p6dWS4ugpIzw":422248041600,"9DdCwzRUNlkI":514878854139,"D5ExWtfRHFYD":571600185077,"ttvsffhN1xcZ":499786151718,"rcG0iVC7Cbxd":261670886691,"y6H8EUT2eoL8":557840287578,"Vdss0xfcMTZ5":43989898735,"rX6gPv1J1dCL":442407226109,"0iEep023zLPT":176075119095,"nu59xjj6fArg":809403996581,"VSiBr8EgDfTa":903128206784,"sGs17xzlqqMa":82187057839,"7zSldgY9dn1W":872648315590,"bRNvauYAGxjJ":706258711677,"uqACih7RwO6v":473298742382,"XXPtif3eZa68":222825276681,"1TroCrqc7m25":311968100671,"Iv4mFBN1HSU9":17752819497,"OyDk9yrSInc2":949384189658,"vG9OZQna4bkY":802157016699,"VXVqAvod46zZ":969824125609,"q7ZdJIug56Dj":644191997756,"HuvSsxddLcK0":343620963263,"beIcbhtwoBOi":273125430201,"VYDUDw1oeQyG":721283751678,"juXYmMw70vP1":409187874587,"w8ndZJwok8MO":480969134499,"0UK4L9fyTnKC":106869945560,"triTi8ab781H":979631538768,"nVrIQXaRqsVK":220628401168,"P4HqRn8GalWQ":982109098492,"9gbnrgYVPaH9":890352537939,"mOj9H3umawvn":881758250542,"ecSLaydKfBms":75484746775,"oJF7muOdVSP4":280694202385,"PRjyRj5Znfxm":18138765250,"O5SBrjiRfZsB":707004225676,"jKxcklUgibvV":548778103534,"T4l9p6dBQNAP":215861385792,"1O2gogDPkaRX":585916550164,"869iskL0qYFS":431821736366,"MPLXkvoEG1Kc":482913137533,"rqc5ym5FMyha":167412013281,"Oc0Jid5AM5Dq":741125970748,"u8d0TDOi7aMP":855335605966,"LHIykr7WAfuv":238071346453,"9fGwvj5jV7Gu":274981426002,"K1s14qixVafa":96392663866,"EOj9I54QQfSf":237620440020,"womAozCGuAco":865365240331,"SHDsQZbEj4Fk":542364781984,"dyOiZsCKypdu":589309539923,"I4XiEma7GaK6":824823228387,"0ACEDm858LtY":603633216378,"RWYO87mHkU1F":564448698536,"yjRuB4UhWWhD":547984685870,"H42jFAaUZmOx":766789927886,"JL284gz9ZEnB":773605148531,"Exvgn9oMrIli":855998318416,"eUCu3KTHnjht":924042048480,"RJSZyoCIAJWf":791912884750,"zGz55wsLqWWY":447136046672,"ANQ0sr2dcYFa":267909425860,"iyrlliWuFckd":34621760980,"rN0ZM1MHh1Lq":863815025943,"3O9gf3d35Oy7":283615811517,"j2ema8gRNzGK":121289360562,"67tkd1f8c7Gb":526740525039,"lf9GBnQCWmGf":491327892014,"ypENP3wErDjb":18374078035,"iBdAsaof7nSF":378615426464,"TzZSuRLXM6zY":760496720290,"ogFaAgyP1Ju4":767748336103,"jJ4mMGwXR3qW":276477407272,"yRQtWuWz5KoN":979122679955,"b6TkqD8WmF1O":608523176706,"fo3ghL1V3OUs":421344896328,"UyQPNiq4Id6W":896583007818,"jP1rQsw4rEHA":851845413210,"KJiWuJBDWzgx":842046943182,"7Ika6xcACCtd":129001561522,"fYCjBQCd1wgG":641402600848,"vQJEoz52HXxM":368735201937,"7CoijJ4DdIyO":723865008237,"Y3AlX3azpmWN":819248920672,"tnmmWB73OBsl":598467457809,"AqrKklshOwae":339164944960,"jLTbPxYw2laY":231691084108,"1ClJbsqQT3bL":897983704181,"EJ2bEYzLYpee":944174335487,"NMppq7NnmF1x":124754066824,"VBpLCtK3fLg0":69842115379,"fQVdXEX9D4VQ":801022024399,"AYXZ4iWPGwVW":943372580628,"cgARnL3GwK5U":950933897692,"vIM7rHysLQ6Y":144197990345,"ZnSNuAgL8ayv":738300558856,"lA47rZd4v6SV":111488520812,"54T0W0qaELqs":187193595926,"JxMiLyVeRAWl":930853436693,"9vtdftpuzg3C":25906063743,"vBcBMMVjMcrW":711089178221,"JxKrQR1cX9ZK":81661331479,"ezgQEPcS7PMi":871173340112,"Zyh7hlsSJn8f":119938013506,"3eackblXQDUJ":282437096179,"8B349fr9bBsw":345656288459,"wHpit5NY4lyn":403052980137,"HJxOQw6f6W7q":653426407932,"Dt9UBIlKjHad":654855795148,"0EKqLNIAAwr6":990349885350,"y5warac63v2S":651930892252,"ieLq8HA6Zi3p":619391622560,"twrrd8LgDwUO":362976504899,"42fX6mI5ThIw":279541260520,"LtHXz9dVGLfu":985177960386,"eH4xTShwAiH5":506377849007,"gpykTH8iIEx7":887394457968,"1A8YnijeHKtN":130015332253,"CB38oga71gMV":75029789492,"KcZB0t4i9q7b":370151110932,"XwfFcp36RKjs":751209316739,"lyaNfWmVTnKw":739464463511,"YU8Xff6yN6Ln":295481399154,"qxl8dqfMcZLr":706790414265,"dhfaYVQRqRWl":348120785899,"eBh3aJiTaDIO":498300283875,"bEMi5NVmVFk8":830297107770,"MT1K3Fblie5g":731462063366,"aNo3e85xWC5F":164144582131,"PlDzZwhm3YW1":393401891789,"WG9HI0K3RVnV":911773415387,"95BEgm4YNv4p":881318333792,"Tj3jRY3aKOh5":514069525915,"UkaCcxxqoIsm":608029015484,"LgG8ov1mwHdE":230400248554,"GFrxS7O9sDUU":449687756209,"GVbtZKyq4JP6":576411410647,"CwiPJkpMANUa":981771909622,"9DUwWV4IbiTK":182055741395,"e09HK7R9gawQ":902360250819,"Jh6AcnoQx5P9":708810283369,"3aJ046ccPYyf":602303715631,"BJS36FGJ8ZLX":47509088304,"1MJra6GvbANm":5451108671,"3tGvSIqMVj2S":875680758032,"6BX8yAPe879g":308057902699,"vPgKcpEAkq3Y":955940690573,"W3f3xK0LbJJY":337280841858,"XN22klccr2pu":131094473318,"724XVpQlP34u":333612940935,"s3VRPidYUWs5":437152579191,"lrera8mSJUsM":543801788073,"TEmsj6exxgBk":269428009051,"IiNGWKUh23Ey":773154892776,"8CUz3Sb28fCO":869492312865,"ozfQVFwmXy8l":302001794763,"4xRuWf41wxGF":595102881382,"il6uBwHs3Eka":396137377925,"DB1YNG4wDQC3":584000074036,"vvgNws3f3XQd":993384282921,"VjybVgwXqz6G":441913847116,"i5T8zDdzujeP":858070830491,"SH32Nal3I0bt":68182083007,"1I4WaDHbzvyl":96797159202,"6P2H3EsIxIqO":933629112638,"uGwQMV59jdXX":308173209623,"TqyBtpUfTKL3":581264976293,"IrRuPh6Y6Xft":112357097070,"QsXVCCuG1w2N":100671864435,"tgC20s07Cxgj":966045816108,"hoUSVAC96KFB":938144562528,"sngsMTkkQwAP":306419828487,"1PFCB7duahFc":597544575343,"M0ci5H53k7sQ":142651576705,"Oyn8Sj84A79z":130809628665,"d144NgIKqJ7A":68539718423,"XeWJEJtD7UQ5":120426145710,"43KUM8qElhk2":506036919352,"Jlb4OwOknYIk":463305638928,"DCcBrGCmU18n":316700902358,"jCwHBuu9o3zr":313812656144,"YGPf0mY8FReu":647900099408,"2CCbv2GeGfgu":673208587429,"SdUfUsSsrSIa":857888860595,"VMpjSiXAPCKl":953951812449,"yof2qC254uwB":120691561201,"8jZvjKwoWlLS":449650828966,"CMGkRDpOKemO":192183552482,"HsAYY2hw2xlM":713431543490,"Gn5CO1zaolfN":226996311685,"ymhrXLtMSari":559964643664,"W0fxBRS7BtMX":137113371978,"0giGq0pGCdtT":328745235125,"jpoKHgfvaEpe":701043202308,"cXzPKUtZswgo":817224372071,"VXXaztHqmKlp":188282286269,"OohUiFS78gSy":435642582022,"Cj4WjLnvpP3E":855273704467,"e6ttA5UfbYWX":645308777190,"Q6stYWQqo9Ph":868992203052,"CD17x2rziypW":808639798295,"Q0Q9BvGtqb0t":513406475418,"zlspWNn2blbO":400811033220,"ax7t1vndImp4":390627801883,"K5KwC7PSb9Gl":563842653667,"EuctlzgFPc0W":708889183389,"sCdrm1dMCxrf":920761213722,"xjidEW166TZm":477048180160,"KjT75rI8nKbG":85024411389,"HNDx8HqH5d30":558837231903,"lbcojrewwM55":421201871180,"yaOU9kbCPT7k":656114615656,"phtL7dnKGnWS":152081657273,"hQNhKb9DkZcA":208463423777,"jzBiY1pE65Bx":136817799697,"xZAGOC9gFFBU":846675022729,"f341Q4Je3Zpn":485556135849,"y2I0PBAWK94h":923074861455,"MM0IW8fuSWtS":676505366868,"pJ0U6iugC85s":518963222255,"XhyvPYukxKSC":914941373944,"gZpBULXb7qKU":93669850627,"nVIgrLmL9cyQ":646593428064,"TULkjBb15fMc":661733762013,"NYFNhsWGDqqG":388049099766,"C3erOKPN0DUi":100501183042,"R7my8CPQgzEM":121391997240,"mjKCWaPzmr10":275425710625,"EIpvcSucFCum":203547034285,"M2pXbcLHe1rU":442068453725,"qcShq5CmlgXk":25626897325,"p4YjHq8LNIr5":365055107159,"H6m4XwzQ8Jym":108830900337,"VrTkYg0VidXx":128633867143,"oQDi0eYOjCcX":694205325604,"rCWJoMNpFxFF":607891281945,"pHNrkmUjJk5c":939161363381,"DGiv1EFeftbA":883547862719,"tqG5iFflwSSO":163348827675,"clVvrrwZcAm7":800080068109,"bbZplql77pYl":26061656671,"zywXzwS6GcGG":765598926199,"BxqAlB69uXYc":441962993886,"5jDuEcaEDdvz":892559140786,"pD49RHBmtyRQ":812266612325,"PIJJ2JwNUa6M":915531979074,"Or2Emz8uw9Eh":832945052955,"Rzga4YLUfLaH":565134330318,"Gt470hmrypM3":847960925746,"hz3s4rnWowxZ":524505019785,"bcjOZEIZJsze":743881062835,"RCRJ7Jv2U1XM":221085546238,"k8hivCEcCzev":504146600093,"iBKV0CaEyV8i":215161176209,"GVBLj4H4L1bz":614314609974,"iKr3fhJtUvxd":709852906454,"ylbNTBeU0PMX":943834209237,"v0QKCngsJqmJ":525689838940,"7mTwWb7Rf7VG":34604016121,"PxfrrnqMWb7f":165705104096,"8oZgHK2mrrtw":90793339334,"x0b4B7Sf06va":713956697020,"0mSM4QMtBUdI":928194407942,"u1DakULZRIBk":440177674962,"BO0hMYM3kmWj":264533521710,"ffi2anrGGk5F":897934315396,"WXlKxlZ83aKt":694874044770,"2u8rPArwPJBZ":551884180783,"gGzrpydYlQje":186355472060,"15Yzb9MLz3qk":140304909999,"8uWlw4AMwaCH":361227358847,"4S02qzt9eYWC":267080805975,"GdMSd0bo27Li":530915979010,"tgIKVSDpEdAE":254966761223,"YHgJH8s15uys":97491753792,"ULB5TPUTCzb2":42183561881,"1UZobWNyP7bO":899438142045,"9usZat4Vsb3C":109805397529,"V9N7zJtQOS58":187810677549,"Njny3a4pxTgP":15527936513,"wfyEFLnDMhP3":724528645188,"fJvgcNI74gYg":222708066594,"E3DbmgbZ03h5":467931952856,"g4QIlbi9m5LL":396053996250,"XWqDyaEF28cF":372864272573,"fxHvaPe6Paiz":579088969205,"tvFQuBWLkKGw":198548725940,"AciTTSfNUdnS":2604399751,"msbMLQcO1msV":487329128760,"6jMjlc0O8UTv":700875094372,"Jw1ZIXtCgWqV":79632470105,"4bW1Tcu1mZKm":722778473306,"Vn5iCEEd9nrJ":586981034587,"XePfSAuBl4rW":615011863802,"KeGLqEfIJdRc":542840566048,"cUIiAl1Fws5w":695984291949,"z5UaFPQXsJQg":750541719745,"IRN7WUq3yjWF":256433946948,"HZYyc5jTjCJK":195603219281,"1vN5gALFbouT":889915665342,"pbKrRheC69VQ":151728778426,"Ffwj9ZzOQWrC":552822896153,"jdaLOs3FXWfG":493059109095,"9GTqh3wbNzDB":345046791331,"eBHmFtgijkp7":365819437314,"jmJykPNyADlE":39819343539,"iVhnnrMuTlUC":342830969418,"TlfgouMSzYaJ":928209832276,"9FXkcMkJmnTS":744880559178,"GPPjTF0qX8q9":53554165370,"kEvMM844eQ3L":309244260039,"6ZG7NrlQuKVh":201376839343,"YROuAMjU3Cyn":846138641824,"OJXZkpq6Baxy":728743146003,"4GNOxOpywKRK":749057495036,"HMJ8WwFo6XGT":933680844228,"VtzyjVr4WEB2":998147767163,"hZISRiolI18m":11507709218,"vUspM8HusiwE":314420886329,"KrtjV87V4Ed6":661747691391,"uDVH2Z67LcVn":611658654408,"kaLNYLoDVpin":38924715350,"DxYBbXIikjqK":859911185854,"coaXpkF2jpMZ":19045800505,"p44FtnjZAkbf":810046072734,"0xBIRu5wt1E8":380539458740,"dx2rVJyGTa2G":644423603312,"pjWxh9oT0861":168136716512,"DZucgEawkt7o":967854626048,"Z9dPahJMXAGh":928010043165,"XOuZNoYgx9qV":276777093720,"GhrpJkbeyizt":782210371596,"msgFipYQz2qA":694739211841,"U5oZqrGYCqaR":887504648250,"88duL5ubNdwP":262767172993,"87WT7nfERDXg":34641999945,"lelLJp1rz39i":63401126323,"5AkJkNtXfnMc":939603703514,"IoaR88PUGYmm":529100891471,"qNYrIzi9qoG4":146019683469,"1gAfrJYSBEkM":857078191112,"WV6rwGWug07M":604590823411,"T5qNO5cNJxuW":530744288108,"AZR34DW0xvd8":254759021544,"KpeSet0sp9qI":431344982176,"RHvZLukEHN4k":561320126278,"VsdZM0819nP3":674712245232,"8m2OTgoAeUjQ":841194955428,"fqlwot4WTNTe":524683653724,"6ptgUTlDXkgz":856468615271,"V0O3UMF7BWjE":34175524845,"VInsLO9DJMhS":40669247180,"JfxnoadKlS7u":490288205538,"WcDKFoGsMcvB":131205635382,"TUfN3NY2AOUC":357817750690,"zQTMKoCkhEGV":556242677174,"dDNpYfo7wkQv":869500757462,"ViuyUBBsBcGH":98062581394,"mLjLm653AlpT":252389791972,"MdpZeuDpx9kI":910265405286,"2k4oEcQlU1gs":756314495139,"hEECAXiGfhBD":673234456158,"xWm5zctEujI2":703215938559,"sZ9aFQBsRsgV":728676430766,"jJsMXbEkcY9J":851000815020,"Iu6Lxc8pz8X8":517907648176,"tdgovyhS6tQf":262942290546,"1qxZNRpMuFRB":442994920980,"udZzANLHvOPq":128103260602,"uxxXj650Vqd4":746769893825,"OeDaxeTFz9Zc":246293890678,"qG7TvDe0ULBK":850376236773,"NlBfjLzIZsZ9":17248682677,"DSSUVkQURMvk":60328970510,"8ucPjKuMS1oI":191436380820,"rhFog7LDCG5u":937751585385,"4npMissO09uP":374161479240,"NB0kObyCqjy6":387520446490,"eFu5pL2OrmrM":739600463275,"1UF3DRj4JxRS":584925445181,"y4SjIUQ0IP9a":251603777430,"5VOHWgvz5WmZ":865567545396,"kGrd4y0i0qmQ":34846762868,"VBSU5CCyZnZP":30590214942,"Pz5qExsNlA5K":436652613262,"rm849QHcVFbd":858538981206,"baoDifGBqVvY":810328827166,"KcLGDxC8L2E7":822565347447,"ARhPY1XepGeE":880052157047,"ZwXNsOGQHdma":400563047446,"7BZwloWQqCrX":443641968614,"vEEQ2ysV1dLZ":854700777231,"ntEJkaQbeCoz":734008427696,"MkzsirY86wyp":755208323287,"PAcOEbj7sN6f":115823124558,"G5F64uPmu23W":687783202177,"GVCxQqBpmT7K":353331111410,"RfVO1zFxItiQ":133162048463,"wOAuK0ZehzGu":568189614254,"hKVNoER6vU3C":791382047551,"BZqPwU9dyVJ9":637964803028,"4FIkrhdNBMtY":275088419625,"UAn2jrl7xswX":923003833911,"43pi4VOSzLLt":621876809007,"dvBpnX9fIqzf":502767956160,"1K141PturrJf":437158501109,"f9tniKmtILvn":184182500932,"VDcl097ae3Mq":301845640792,"tb2x5oXdLPUi":176772769995,"Kj6uExdzKE8F":827987125434,"XFZdJUPBnmDU":136990415927,"Jn5xhXVEtlZH":846241024301,"zPymd4RNWAmP":740466512777,"B3qKnucq8D88":388433345300,"08mXEccduFb0":258047445853,"2uQxmMmfzltS":30952684292,"DaFxqGFkaqqq":18717701108,"etpEJyEkkKHB":988466524368,"VTEgnlL8Aq10":254148597269,"xHoOLYJuXLp5":722236622875,"eSOdqO3b30Bl":440641045994,"zlXPy9StJg7J":996218589092,"mBDbNVOWEaln":529270394203,"Kpi9bQBfjRqz":156164129178,"z70JHsAhPGkU":767854415696,"CKdU56OiK6vS":847517556471,"WoVnofP6skgD":162723342245,"f7wBJhW9QJfh":485835693138,"biboYT8viard":378257446863,"Z2EIHe8lQEwJ":919078880476,"9cecPXIP1XdR":450553703549,"xO3jK1Wmi6mN":18160402250,"WL59jWET8Zds":274029570276,"bi4OqvIJrWlj":922735851706,"XU1aXJCme2vv":39471950198,"NZjsPwZUQAYx":735662257736,"5Ehtjw8BX9aW":613544322649,"GGe0cO9g26hp":592041161183,"JGPnKY6WlNXx":690614454477,"ntvE1Tefduzj":709400625206,"XeWZryp0h4Nm":591623784682,"0S93Znb7Vgqy":352727504533,"GSNVGvGmVhkd":589428841527,"mMTSdM3u2A8u":195245023451,"rcEzlEQBfrSE":669484677161,"5FRYldnwK2rp":553929288403,"Slh5S6aHHA8z":389085504,"Lg1pWdEIaOlM":624517711087,"9xnIxAWdWPOc":810846903703,"loccsk5xhWm5":861708695338,"sfXllSsKr6I9":489106771391,"VECkQ9BCBmoJ":330637709449,"RONtYht4vDPo":196058844699,"gwudrJV7Ca5y":392223367927,"XPA2F5nr8IEN":15114027725,"P8emc2SoQsrB":546770741491,"wMg9am122Pa2":676939897007,"z1wNIl56aifj":325628912031,"266d5rnyAiyF":316903837564,"B3YWz6uF35hf":635399650967,"3jShnJzolUdr":778872126658,"SsrAXcSeE7mx":4614281529,"nEBcv3DjqbZw":710837731298,"iqt1dvJuBSjy":261065760528,"ozM6l3V8qKyo":328294030011,"1uCmTKTyTVuZ":803686469601,"fXkL1HmVcqPc":455442432352,"mpozhUemcm6E":554139611128,"PcgxSmRobhIx":246546942722,"9FwZhz0n6BYv":519134880755,"YAAuloP2bpDP":818661639468,"hIRkqaVTjc8i":102153365438,"2omggnF2YgMc":301129892158,"jpFmTMCwaZFO":158815494409,"dQ2ErJHxIfuV":283858006157,"VYerZDsC2xTJ":3995670392,"dYABleLRjQHw":126012265750,"4dkpaxUjSvtJ":61064099424,"qD1jBBT2kNum":911292351922,"kJWnltFV7lr9":290810426313,"xzwWcnpQXYk0":107868205167,"DVnY2Wdv2VNd":818518681269,"QZbDhxezT068":312726679988,"cvsLIcBwfi7D":812691284852,"EkPevm1LkB5A":694674531946,"JWUPOU8jJqaW":480174651405,"1X64hzC0jD7R":688522622064,"RNJVv9CAT7vt":107194658866,"PJJO7WhXqAWx":723243430467,"EwcWiyxFgeM8":680728793998,"xB8LsTHeZtwR":861215166839,"GCn4Oz1AYukY":980215022363,"p6N8on2bSXhf":72156727751,"muNQz8mQti3i":807903086983,"YdtNVXTKVJZq":383364799470,"NEVetCt857tb":900733402366,"oyyqQpCHbgbi":40078052327,"ST4BLenm3J4C":715288573229,"SINTGakyQto9":998484371548,"xeBiI5ezLn8h":932073634286,"2BiKkK4tCmxT":381930812056,"jCdpFASIXvJS":664814865107,"YERJNLQOZ5Ad":783693341350,"Y3txfxunXL0j":770276923307,"RmLGvYR5pIcF":977961609435,"hk8Exwa0OM80":373300342917,"OQcDfaDWYkL5":748962396080,"bBOTm0pSFooM":934308570139,"78izhZGXG5Mf":314550338734,"bdmiJOzorHHe":941039409530,"JH0eApeX8I7s":665618702521,"ffKYDxBQg1vQ":428223965152,"k8qvsI5qCYsa":72789299663,"S0wntLIEb6rU":185000701874,"cBvcrqgt2Jk3":264399646462,"6GuOSVJcNvgI":861919805612,"zv3J9UI55C80":761299857508,"9Oq7ZQT7qlsu":24422724411,"XQpSyaUmLlj5":456739650933,"7PcgSVVdtGGx":718704304286,"WXehwoehnJij":840536304029,"iKJd9jwp7v5i":320322333898,"VGfKcYXpDJBN":290759452584,"70iFoRpakH5y":338541190807,"2m87NhwFF3QP":583664418974,"uiW7O9sgtiJ0":567894652846,"zjSzNIBTDhWZ":584692258874,"b44B6aM3bID6":817322157405,"aZttioC1Tyyj":557126753550,"a3dswHNC6IEk":754634886767,"0XjYtNpdN2NV":363145440889,"Epl5BDZo45it":936698731023,"pAFtn84CnhnV":491432079557,"DIILSPYCnbSD":85174443293,"cXv8QGvAuidk":927482957550,"lFwWNZsBy1Zn":592900118067,"Uycb7vqGlkR1":65607400253,"urhJUw4HIBJc":626208827342,"LQ6xByapbQ7K":576400977993,"Ym9uphnu8VQ6":394222203799,"XKdEtQmPk1XX":20214260924,"j8nDjnIOpbrc":176355241445,"IPHabbI29lDH":919494504063,"poJAQaaXnWY2":726313640448,"i6Q20BASDQ5R":81395447795,"jfDXYtOMYo55":155672558239,"HED5xg0yhYiT":873144850313,"c1863DV2qlIA":396546515250,"qfyXpgzH5JZe":23771883723,"XS9PsKEbukAK":481970299726,"iEtdssceN2ur":691128518842,"dZyTEQ18xPYl":723231783469,"M1d4aHCiywgD":782331517324,"iHjHwYfVxoZ7":453902197876,"Vup829eq7ZtB":255212871235,"WTg3KjObTAG7":404077444104,"xQjBDvFV8tyL":25425491584,"m6I6qKUxS3zv":684456209841,"Enji2Z7yiFEj":345696858506,"V2RN37uuytk3":99572929098,"MUN6cTLS4Rtd":136414406952,"gd9q9U5QtCNR":260842420839,"U6Q8dEe4B5Ma":856966772111,"OlSqfuP6Y5g9":151935575661,"CgO1y2TuPAep":804342227750,"tNVsomASGq7v":947352346876,"28ldavdkMRnu":22364948003,"3hMqM1xcO2AN":829574252048,"w6Zqr8pa8Y4a":624202960477,"1eymFMQ4M3n0":693471087735,"Cr3QRf0zuc0v":654035326701,"O9nJXcPBuru5":900517347310,"EnVpmDm4kkiX":228988225457,"Lw0YWiLhnQd8":674187797859,"SdcCrflMMdHw":446494958064,"aUPmFTRSMyHG":35379313984,"drtgKLtI6rmB":852235487251,"lvjCLhzKoWZx":216877281333,"zg4U4WMhRX0M":365175177936,"tP3fsorowm1s":231851613974,"qivuCunFDLCd":116062046476,"rBolJyry0VQt":874402694126,"4aRPCiGjFZ5P":397988834542,"JuborJrqo0WT":897291191173,"mJx2RkfqBZ3S":932667561445,"SZBzt8rZr6iw":684127435372,"RoJrRT7OdSG9":95316230807,"aKOoxkSFjUfF":24060152405,"j0rFKEpGyzck":316209253299,"H40Vw5QDzGfU":252748658814,"OZACM8X07rFZ":822096865569,"CsNWRsAi6IL1":262916590232,"suKORgHa838v":755943488660,"3Xcg5r7QusSQ":960656552906,"ul3q1WsiOZNg":276865717388,"r9B5K3R35DlU":226188802172,"JgjV2xImri4M":850791632548,"59dumRVjRHe0":89676617686,"lWWMpYpv5f3y":108237650654,"1obZX8IlSGSu":586723570654,"FvIo6o5uuokD":706581705189,"A5omscsr0Wtr":387580069629,"Z2PQF6kNjF3z":512475728893,"f7xJT9nYOB2M":613614708224,"WcakFbjRfmfL":917489689134,"8hVothzRfOG1":994775466950,"wMoM1XMCmBpr":782779147963,"GJnpUFbjGgOI":60598006782,"SDS8KjEpbukP":136050022875,"FkyRLcn7MsX1":70435178719,"NGjSdjEFHNjr":926233943262,"NbRfbK4jAUJu":974542511084,"6F5FgqI55nzA":841410697758,"Bbl3I2DDjuJh":525849570102,"4XpmA4xdC4jt":768462536529,"rmBL5WG3IvkZ":116936620098,"ieHW3Sg5Z7bt":654945209193,"c5wq5DwFu347":691498405301,"02t07u6FORNR":733269122264,"vLUJSj9XcPWe":961552666714,"u1exOZSDB5hn":876234004047,"DSsLU4zt71rP":575004075061,"xF85HEbbYoyz":622289728868,"9Jg4Kzz3G6AN":999878749202,"arLDE08TtKvR":653092399042,"85vSjOFyBXgf":486499322056,"UzMjoCo00GSI":915797500591,"RblViHZy2vRO":910863289649,"VpH1zKAdUEkF":180492433362,"YXMRXxFDjg5q":949584579056,"XBM2WbwAsmt9":785653535831,"sobDjR21xHRs":524092468368,"JxSTq1WAv2Cf":927803176704,"J8S27MBlyqfy":32772736501,"IWnLQ3p2hzWb":409571047216,"W7Z30W67j5FP":890066879713,"q0bsqBqCLnrM":741260022590,"2NJMJ0AgNyta":636404474683,"TsCOjaNNxku6":973952724209,"xEEXPPQlK4h5":355870098677,"OY3mahSIlxVZ":992674066917,"fpKX418EOR7S":639308464553,"ma68i28v735t":98646642760,"qiQQpBhcM5y8":890541763324,"FKHxDGAS30O3":689845426756,"vbJaU3vA0a4W":948292096427,"1IuehZgf16Hy":431943224618,"8vBplX4XXuTD":397485900089,"B4jkE2Nync3h":512084599835,"Lu25JoWyoYUd":781737520606,"n9pKld8gmLLj":872416569809,"1sxstX3fIuts":664011015078,"cbjS3pmiESDO":352232414110,"DcT6TYw1UO8B":114136732683,"PTu9GKGQapJv":15187705019,"SDFt1OC1gu3T":988158826648,"5sGifyUCkDZH":415975675044,"XekW7wZistnK":465267377418,"wcZqoGi4I2vl":663444550096,"N3KMQprQMRku":273183329548,"Qo4zdb9xSv11":558325985456,"KV55RArblXh4":47917585588,"7YhyYIKsYjJi":324324565756,"tq5c8iQa68Vv":531978276487,"1v4yrahkKpai":914316499305,"h0bmYbZDwW39":281959707066,"SgHtbjklNaRt":273403239113,"bIKlPRYKp6vg":380366883544,"Ng2jbLS3SzGJ":273467472852,"ph1PyXhRlMPP":252478223716,"4SDKwYBQSnrb":284506222566,"PnelciiWu5wK":93718739740,"e8HXuxP9HB0O":845448552530,"GZ4K7hraMKe3":850928589599,"D1K1F4I6w5dq":846687334029,"H2q1L2tTLUY7":824279008688,"FDPKOGxl5Cua":891972213741,"QGx3VAsOBEdA":531846946824,"b9bRSYcRKYkK":764516026688,"XxfSxbB1Tzph":642887025677,"Rh25in2NWpZn":447372058173,"pjThEmszJzUV":33699018921,"X4NiWdfc81AG":50861011753,"jUpgYkWLreKY":962143338774,"Jo7beKvUMQwy":379400773802,"SGmx2KDIvMxn":254873061566,"IhoNS4nJDrcl":315388427087,"behqwJ3mUjgH":556265854570,"DGwv4oxIPaEW":772732203785,"8dk8QMW7ZBL4":478346899873,"WTeKEGwhkiHd":134269182391,"liPAFrabrFrr":435475710261,"WcABjnevn0ie":487404502434,"0LJFO7qQc5nE":991684674268,"ZVO5cSbygYcm":2784672409,"RJaUmQb8p7qL":223548252164,"zMQ1Zo3D1HM2":511572542396,"5RKrTsSI6rGW":150047742129,"BxShcUIFZ9AY":954777470418,"iXF7vumNztZl":59243461754,"AV2gNXYWGaU6":433919359146,"iB4tNmGvogHM":925601931678,"JTLQKeqnfdpx":925832411151,"os8ACkC9tqHD":605645560516,"fJeEvcWYnwj1":174901401422,"DgWZYxy1jZz0":565347288379,"PcbxFyLhrS18":570721456567,"YDqefk2R4uMK":830822079368,"ryzwA6771bgf":595017092614,"dV4juml6FVKT":743429778477,"MdO9T3SOJlw0":802961588140,"hQtnKv0xjJyo":363956928795,"Iobbxia8f03v":298781576523,"C00vDT2LVLYV":79036538809,"GiXKFgS4Wq2f":180685972629,"ANu5kQdAm3pw":961043858804,"dFJ27k38aLvt":907439319468,"Be9oiwNfhBjj":78961103405,"5UcGKw76J6rU":24837524179,"BMPe6fVTQZvA":295044078055,"z9vdhoqPGpwf":451860220466,"rzinHquu5YoB":597705782693,"DftEtlwXhMPq":55675835953,"lUjQtNTexBqd":584654622553,"3VF58HOyBQQP":525268881555,"a4JpYSVc6UPS":145019626751,"3jzIkiMl1CLF":428982442065,"CO4VoALz5hRv":399120239634,"1QEWrAk36NTC":547359673143,"5FKQ8bB6FEZh":709427488664,"kYVsSan1wSNB":38480935946,"62ak8NyGY1uS":454019826139,"Sf9rzFpJw5ZC":842903962602,"md1X8V4CGvig":606470619915,"nhaq8sGr7PJc":754486371298,"1tEE7AfgAxmi":103580390103,"SAhf5GGIkkKc":483704001201,"VMet6lPwe4Kg":403693566929,"DPEgRztYzclp":528888472162,"som1tFj3siOk":830746940771,"pFZGKrZalJhZ":462894842648,"J4RJK5d8KlTn":151594104176,"US3dM24uslHI":646492813579,"MPL0GnN3Q9Yy":974626654409,"GREhxw5TakkN":147063523842,"3ZUyPO3R0G93":857175062146,"yu9cDHPbKBWw":793933361708,"8xGrkANQcCrZ":699193429768,"YCis6YeHnt2y":450842261875,"FKQmpXhBb3gw":430274165050,"UOWSIhBTsikM":467291403882,"l9OeXyG2rgbE":46311585043,"D306ucjPPZZP":803470780652,"K726ZAQbA3RI":822610897097,"yoMtzA9CzNZA":312937859371,"NjypfOCvsxNJ":492123647915,"pGTHM7X1bv73":352989889057,"tyLAQIBSKCIo":697221959432,"xuIXRBLIAan7":939269820593,"7fy2K1bLNtD4":484548697644,"KXSGgkbieZv0":921787850553,"werW14LqXXwd":869019383964,"mfeCHK8PILN7":574542407975,"6ffL7epsdrTL":624087851941,"fm9nqzWUFgSx":988694534849,"bL22qr33XmMH":550385967505,"c4eGoFg278uj":157735345276,"Xf7hcs9V3iqv":659249230660,"kud7QpONEOqK":19485702503,"R9OWGQsZa61Z":524745663488,"8ZD4XKF8PW3J":290535839343,"8TnXT6Uj50C5":410236027036,"BQeAqFxX6th7":316596436509,"RHQHRxu5gFWK":438326734316,"gllUeP9zGw5X":903846930642,"AMbLoNED54CR":675592897848,"PeQGHCbgJMrs":958414469747,"mydc7TISHRfQ":176341273340,"yX0xc5SindHk":487912162858,"HP3c10ptZA2m":904024648703,"Oa66ZHcBolBJ":418878615207,"J5QxOoOwrnTF":241548127135,"PCme0WjV5L72":626000559711,"kdnZv3scK2dq":170476259845,"S3Ho5y3XgsRy":960912820021,"hW4oFKVRTFXF":825010020510,"sRBuW22yoFJk":674638012534,"2LofcgWnCqI0":770315831247,"s75SeTjo0ATJ":822825953087,"B6QbGhexGWId":871645008398,"Xa9aD6mLLHq4":969558915589,"uIsyGoJOESUU":509096140522,"M0YIVHvxZja8":94215416186,"pIYAbaO3eoRP":389950151751,"5CoSnrLZwgwH":126375875968,"cpunCVWKw8LK":808890395620,"Jx8y66YFZLVN":650533995932,"cktEyHX3ACxA":172316104146,"UudivB1LVYyK":928691016556,"A9r8ti2DfUkk":234150371240,"NrSdDjduL3XA":776718695017,"yI02lMbEN2Xq":340873648720,"kMhHXiW82wic":839405702440,"djhmI0mhdQOP":814607576143,"ztC87HY6fZ6x":42396507296,"FqQMduWJio6T":176650825309,"P7La1fEX6pLz":711498596540,"mEOCE5D9Fh4Z":951325841002,"UWumEO172dl7":632059274816,"0HZhFhOuinX1":251813864655,"fDZpXpjoGix7":606453774465,"66bg4lTGkQ2S":896163699258,"Okg4zkOqbUXF":621753146891,"odT5QHbRwjP0":565581381911,"B7lDkxvgHXyk":290086768276,"KQgxoZeMaJdH":523537974239,"OFjhJPhZfWp4":828268205170,"0xiZgeG04nVM":32328449857,"dNscVpUmAoDj":729555482647,"cOcSV2UuuxxB":486472148423,"gajVAU6ca0xC":41027305721,"4Rg1stTloNyA":708862723071,"o29t1jNMOT1x":992957200188,"IcuXI0BmhKlB":129272592141,"7r9aGBm7t8IP":711994627748,"bv2yKoscTZtV":332338351904,"vCnqtS9G4rBC":708708366964,"LzZeXA2CMw9J":160202223994,"WBOgGQAaw82f":520058851690,"r0H04VsiUfL4":914915392547,"qZc3IhE9LHIF":178375158477,"QZ0WVuNY5Xrd":228244825272,"C6Qn4w0D7JJF":896105754300,"Gc8KrCku87Dt":24042720029,"CennnCM0gvGh":774690173362,"I93MmAwgcZ87":757779676768,"5PaZbQOjYCCr":917555158330,"hBBRyLAmG2CU":564104975316,"KFoNQp5Qx0nT":914411721426,"jbIUufp0vNt6":576175647378,"7lJpc6A9lEqU":72643441625,"CuxxvazDxm82":416061549206,"72pHAKOVBr5l":754114843918,"6pTNy8w4IBb9":320665421121,"YT73NiKOb9iN":514449611939,"Hyt1ifsDn1wm":388858196592,"s6hFpYcOzycV":746973902731,"gf3LKPEcGUwU":90732676176,"2jUuS9O6vJeh":555825307444,"MX7V7YWVdaeu":494113187346,"BjJuKsFQLvrW":123187180134,"UlW1zmhZWJ0Q":915929551610,"LHsU15ZMOkbm":433987763642,"FZpFJYrZJ4gI":630450850266,"ATMi9r7nczoh":588414525997,"jDXINhxAE5Q5":19809456696,"DKAY2RDOjG2O":282692816020,"0lE9pSURsvqW":393223014417,"skfNrUoY6cHi":162584030954,"6oJLlOR4cdWg":344156233280,"9BLlnRVU0HWG":443629246120,"qq8Og9DPLd0Z":664466513283,"AvtefHDgyYUD":202010107529,"BFBtDyLGLJlR":393943880487,"P4sCXrfWBHO5":808560921626,"j1ydLXJYP5bf":966120391695,"cPSAAbTL05zC":493471556916,"ekRDd5gJXxzQ":154283740095,"QnFxNJ0wG8GR":727739101889,"twcQaySQtInn":32909588407,"yMA7UzfkKirc":801613141585,"AVEYjKnuDDRh":496691798233,"z0IO3OpldOHn":307327502596,"hd0FiAi0NXrv":459489117942,"zcF61422bGaY":195050591196,"eKQT4bG9WYMn":462441315313,"4UzfEMv1RvKe":899201635181,"RMe8N2VqLXD6":235040974793,"Yw4drINwkfj6":766408294009,"A4woBLGQWS3X":994133211475,"YMHcMHEDqk1W":790761551528,"8V3QXNWZa6SZ":440422153338,"tQo6k106DjmR":230140828804,"ZXqZYpFHkUL5":30121648931,"qeo7dwPQBiBS":416535338868,"yfr1XM1eAd62":652605211445,"99pR0yqiN0mO":441207819536,"yr6dyfo0Sm1I":161076134483,"CD6psxfK7csg":740022196493,"dMlwFU26rOdp":163597595740,"iZg31cQMKujy":738532541343,"LrRY2D2N6NFI":117696709766,"8s5wKrWE1xk4":272671334447,"EshF1SjX7lZP":615027779229,"cSaHUbovEoX2":845756909043,"Eb1yD9vhFRyU":302140713968,"Fpek8MIGoEQE":547917915923,"lFnyXt2XY9h9":521554051554,"UBiRNfOiY5TP":591796430765,"hIFayw9E6kzs":492897537325,"YOQjN5SKytbR":386531122903,"rUaUErgEeZzd":54135840207,"ZPJhYaGHTHir":339355193184,"jBHbz4tJeghD":185835669115,"T5a4eTIIRFHp":920840857300,"unmHC2c2XQ4b":482588147326,"EO8CJWKXRbRU":680098562488,"h5UPC41bvKsX":296960838894,"rtTc536L6MTm":394647904059,"9phJcwnIpe32":645939583937,"7a8foXWoS4ts":884441593848,"6mqsyvGHSYDH":426045556996,"ruWK23VFeAO2":102944545972,"T1ODBoBU05eT":873767354343,"mZVlV7qXLLF7":677793797822,"6RFBFThDpEW5":796540785769,"aaMI622LH6WQ":880300317788,"N0zvZf0A9Utp":832888638212,"AdujmzcoRAau":996971277989,"FCRkg1RPA0of":160377019432,"OKdU9vNHv7sQ":686178212724,"UHxDQZvOK9Sy":37915265297,"KyW60o7ybbl3":22151817810,"IAscS3QZSf2S":664497423292,"VU1jtgsC0WyY":239673882108,"qc4xxe5nfC8D":928820110835,"lZ1liNzIMf8l":215314916164,"Ti1cgNNCH3Rr":948665193423,"iZ0CU2kAWcLi":310016308682,"huvdgabssUGp":188091855412,"y837x3NSzNm8":949682544011,"qiN8IpXpkAHD":250118297028,"OsQYrWBAXjqe":17976444818,"CBYIrN7Dl90C":741671403055,"Yz8xd0nwfa1s":382462786897,"FZYZI3FFPDAQ":945066378729,"bD0kpThRMcuM":545817292339,"ZtRP4HPsTmD4":450597238819,"7SgiFM1Wasii":786975822069,"dgz74ezq0QIl":249814567387,"K9lvYoEZTgmt":594335751613,"StH30caW324f":211238828986,"VEB4BxfAbG6S":939301959053,"jYW870cno9Bo":851558506063,"pw7mXO1OIEP3":116454111784,"T4vTZDmhZLN1":355980451620,"oDum0APlE14e":704583353939,"DcSyRL8b8chj":343322449894,"JEgNhtQm3xTL":961679011793,"uAH2TnK7XkvQ":272249487991,"oSeq2qrnLMI8":146484424211,"3zoDDBVaQ5Yf":572900604585,"PxT8Bk9B7GJe":912959762236,"wXE4c9ltlHRd":650149624709,"CGS2gxqsWefB":410315651606,"Cpv6H9IiHDsO":634999493921,"t5V4qsyZcsTs":869206486059,"KE4Udvk2ziFS":148267430843,"3A9nwbK1IrTg":454735788458,"UeEHOrvvny6s":890215730035,"E9iCB6GdPO6f":426593003292,"LXNgdLK11lz8":937971486288,"hoZOvAnxkmIK":48334846508,"km3yjN9uG6ly":799587709344,"7rlV1CwRJ128":450680758469,"yRgUfckPCgkT":142010614095,"NMOptLCiZa0u":445249888955,"16OowSMz0qAt":108138864273,"pyJo5xYIk3FO":229161931640,"sPYEi9O5O1Gx":59460989477,"8AOhgypPe3Er":307890057417,"UXG5kRrrgFL9":299559590098,"YSz39mSttner":555096939703,"KzQn2jAd2Skz":490708420257,"xYVbRyfS6Vqu":919212805073,"MO7QkD1FXPAk":144086047534,"vyYJu5b7WiBF":247479004553,"R19XKYkFknHY":74737427188,"Iac2iI0WcwvP":858047827994,"0P6oOaSKSIyp":918777968426,"DE6v5wxXbfR2":84347466308,"7MUCPIuzPZVl":318268066685,"yLZPG8u2dOAs":233191692154,"aBFUVDLfZ8EA":206335967374,"DSWbIpXJeo9S":985806245312,"czdvnlbhBhjd":13489670270,"XWs0dkgeY1Ef":736754142366,"7mzlkG6Vsm7T":558246692703,"OQ0uAhYM0uMT":817219893855,"uWtlQ1Rkibcd":630244017816,"lnBL2NlmWtL5":732430789409,"MdFyO4sYWpM9":991550718148,"8l1J5aL2spYS":632678266785,"tc8ael8hAAvf":156023707774,"EKFHsKGGJgt9":156827523641,"uHFs0zOmE9A0":452727143002,"bA3mU354sGJp":37035963605,"dSqJHrgqTUoW":571095124582,"unm8imme3CFY":715774949442,"PuE377iIpr2O":120084025231,"dObixicrH3JM":53283341802,"Vo8HgekRCCxe":526878627181,"iMt1W0XMDSJO":452467143617,"XyeFQZI1YMzB":550975226794,"5mKI6zEwOlvL":116259154602,"Y45TPzKi1cbw":985618576505,"ihHxnN4hByIT":914283781933,"1hvHZ3PK9IJb":981325559088,"GUKySSVGlcCn":975128224480,"pSbCFtX3dJWA":4676646816,"7qO1W2P0OPgC":533371806932,"X3E3XHhfixKj":923678190938,"qCIGswGb7A0v":368785611033,"IncMoqwcQvTu":267007293632,"SodJn1abuPfb":703535967643,"REjCaDG8BTcN":843635917635,"0oNftPrHYB3R":68522833904,"YieOEfrGoZop":867909035405,"7iyqlz2fwJQS":628689260874,"yit3ElupGV87":452284019658,"SUto0accOdeb":443920836611,"HKdYySFJZO7S":813460292189,"Ox2rubADN6fK":654575435898,"oru1h0HYjwmm":745060829633,"oSSmXhj8dTgb":550098858209,"jZSr52wPksq8":644128860771,"8rD8Z6ycwf4Z":335786731970,"x7MDJteCuR7I":654357780708,"CGa7GXkYKL0o":60180771582,"uQrqbqnwapPl":266291127179,"dfqM4iIrscUh":721954136483,"4M52g5Y1OVWh":546440442963,"Rs24gcij2QDE":614176959162,"zoe24TKaoMSW":772863266423,"FLWD5e8o1Ggg":505717800297,"j5y4kGuvAwQ7":216320515388,"fkp85WAqmdSN":781841651368,"GYHAqENgPmKY":931181991633,"jjUqA3m0eJKm":430138127263,"GcIPRiqy53oI":98056792848,"t3gJtBuvHbE5":80311477174,"MJ7u2l7iAqw1":125455395634,"wgo9X5K0wfa4":301355868208,"7xTOqd3WHD9B":944511278406,"ALt8UDPygqlp":764637819917,"0aMaZX1BxA64":730442616776,"UN381hVJh0gt":730353703498,"Hlyns8wDEI0W":461582333689,"iWXx2NN3hXJ8":644005793405,"FCeb1NuGz1Mg":786213075791,"PfnC0FKI1s9g":968100558852,"AjDaMztQQQm2":928783708840,"CWOrWJ0CzxZw":957782212038,"0LbXYTEhTdaK":698539026359,"YkllZfR5q6xk":655080072930,"1RanS0AGqKnQ":678966920028,"Mqb9JSP8bCig":354419469515,"TwJCp9Sh9enF":170620614416,"zW0ovkE9Nr2L":350557369977,"ed1XENWH6MnL":381103866919,"cl4V1J9X3XPB":359380987937,"uUKkGB7ic2at":265055453323,"Ht6RaLpzwV5Y":158523165554,"no3vPL6Mt0K0":397419279922,"E8XkbNu5WbuS":945165223437,"tIGIQ8hI9MHM":163147141257,"iLIHEJVGZ0Br":661932425231,"m7RoHFUYob1X":66458850702,"JGYfpkXhvjr7":779434292687,"xr5WEI87BTXl":811650080403,"6xxOwV4RXLId":654599784943,"t34UWpKTjpQI":521929282037,"yYI4kA0WZ9wR":78125478012,"86W5wXmn4Y62":164242433050,"sc0YHSwuUfKl":5211899493,"zxnqUbfssZDL":797553683528,"fgxsOBsh3uL2":943259789180,"cEqrxEE7VKZ5":491792559605,"N8S5BjEhBGss":166732941577,"FLigXWF7Ughq":709478078804,"qNiflHp1i05U":744737922883,"nPQDDlnFgHl5":238084563688,"V9WSr06sreMI":17641428262,"iV9WriRMK29B":17236643125,"X139E6RBUEfg":579415567814,"uuRoHXymJFsT":243111222433,"IlVCOZdJLORK":904852822855,"lY2E9AhXLvoD":128504967615,"riOnqHbjaQ8E":324124418148,"BrELiPjF1y8g":267813019301,"VxJagiFmFW27":328826354267,"Z0csTIXLkft3":414187351007,"ghsYlFU2L4aO":760392522300,"H0ulrFcG9XEr":702643062182,"TrdlyKskzvYn":944397422075,"1fznpgXqDyqa":213565311582,"kZ7cgBe65q29":853816388562,"p3jsPvChdv15":830704427068,"6cxwkKPYRaLC":137272358634,"7oTNbmPFfYRV":916622095468,"FwLl1j4Q6LII":157647529371,"tRucKXcyNxsP":398155685838,"cyip5vQNn4Dl":481936839185,"MPJzHz3y63bE":53473821234,"1dGdXvbPK6B0":549784961870,"sbArn10p52c9":631651423157,"kbmnOXB2icaF":25326659609,"YzXnK8SQGNft":735766114772,"FJH4tzYkMOW4":661141735968,"REyF9mZC2nMD":54928272270,"M5tcq39tEDFq":949034455721,"mmD31TRtujIi":219377114149,"rJ7mb15Srbxu":630152985150,"hxaJDV3FBfWp":237232439873,"r7Ncpd2Wccx7":26902838303,"YIhvjEl11ujK":876531392298,"VJFkaymSk5vI":939077423340,"6t9ZGoKz5dea":180296994515,"LJbV39toa1rf":585691287419,"SyvhRPhZTpsn":47813486944,"vZTA3HdpbKOx":37552475187,"NNCBFh4d9TIV":578677067922,"c1mxS5Hd5TZL":609378968162,"xeZ4859dGhQI":706736991207,"TOC0Op4uHGUR":397689608289,"iOvzgLi2eNSO":50705818821,"T2Acda4A8raB":287815551419,"KOlCr9SkWMXe":88434577529,"LVgFQ8t88WwS":285016087843,"rob2m9439GEQ":860421365862,"aHjWnvnBpVbX":937493936711,"hdbNMbvQhzCU":623505112689,"rdNGkp4TouW4":269926288498,"vbNJfZ1Mck9j":871115054811,"9Uk02dJtaGGB":96584791277,"RUtYmB5ePqmB":97937446985,"dT4Pgjw1h2ws":407084219315,"PJoHH59OAoDi":954971948775,"QZEcRvsmOP2C":37325370836,"TchQMCMVMsQM":374626066517,"TthYhVjFAUjS":187755168791,"WJviBeOBDlvk":951769876642,"Gp6vrjnNEbUe":191457095766,"5acomb0KRXmW":53624642505,"m8kqnGxe2X2T":175200873499,"17WqYb2acEle":931877780518,"tu9LqMMYnm0b":539641740447,"RedQYEi7FkmS":796633060751,"fpyj332al7lD":160666923747,"hHRZWbOzocZ6":471411054033,"9TlPKORtCuEz":541343655923,"qTSXZuqEyi1h":886481187859,"BRUHfTUilt6m":258608764590,"OBqzZmRGrfoS":546249687964,"E6YywuPX3TCh":220270836479,"nzsp2LkvKW5x":389928052350,"RBiBErGYnAN4":65117960321,"6k5sdRv8eSXY":271386077143,"Z4FXgtwRUIP4":39323502748,"o4Z3wgMiDXYr":65264188393,"I3rWi4lcszwf":348561320066,"6chljHSTJatE":554098543977,"Ig5G8WWbybim":789955584507,"vxJUmivBhEJy":511578065336,"cQx04w9GTc0l":287636702614,"LxZEGnt88xnw":273759049340,"p6bz5vYtUyeK":720002726021,"VZxa1PmoZLwa":938807467525,"NhR9nAMLkPqh":969416896886,"jhkO84jVknOb":104369748341,"GiAkkiAybMGY":725163582238,"tTnI5C9cVIni":169382492571,"NDVb9hPrqitC":898349359685,"AVF3EdzWmxyo":689403242262,"OJ2baklvNHbV":43514695526,"7petuQB9CcZf":346209814142,"FUqwkyTCQGls":793772161732,"jtCbyE4VIQLO":400775941549,"dIbRBYl0CJce":96053167473,"E1UkL2saX10R":887563385711,"13VO5dV342nz":866611295408,"ffANufliWenr":415470035154,"SvqdM2teUjm3":535924282470,"Wvqt27Y58A7h":811407426712,"Pu5gnrC79GC1":997026629711,"bbllJpk8CbJo":238439976666,"X6985c8Wp1hu":615318389393,"r6axUyJ6sA6m":728689413592,"ppU58n4J6K6i":52264718106,"JtQct0jIZ9Wu":320603903763,"jG51uAOb7UX8":143323796176,"0ucSqBNmcFfL":305211886870,"dbyXF5J5nVl4":318688685197,"qlMhTg98L9Ad":765508009050,"uWZqTofVWz1p":209086307488,"c23evtxqVe9S":746397892702,"qRkNviRsIxCn":332096977275,"P8sYQfwJAYf6":237071643682,"yk8gFRzIw9wd":388827410566,"P2ww0H8Ik4lH":62737404382,"ZgeAJRFnkAOL":605733760541,"S6zDJkO5FUX1":528885205515,"z57uScj0Es5o":35385954184,"IvicFoAkEQAR":688935123763,"31a162xeX0ZI":119380700104,"672XXmjPWAYe":458185834168,"nTWQbCNIAD7J":430733189660,"hEBfgcwJZDh9":374266471863,"aWQyLqPGIOJF":763233127667,"bomUpHXZzM8Q":96177989880,"0j3alZqf9OUm":886527438428,"FJYpi7U1nJQ6":915457206270,"B4teh5kfv9Ww":142835924021,"WDrOrrD2aCCj":677641325639,"80L4xaJnwAeB":815015667365,"CiPr9kaLN1pl":574962390310,"0fwayYBgK9aP":248891814863,"Yj1o9whiYyPH":524442176128,"j8EGpUpm9hSm":409449266317,"sJK9hXAvWzIY":890309901807,"5vhmg7jSxLMZ":376003752202,"IcUBp0F4nDls":981318942195,"aIF8RETA9zCQ":544599591546,"TThr3si0WfmB":660354712561,"4aYLv66wssE2":762893015065,"hDFpnfJ1LqZi":830042993678,"JlEwpUUpuv6B":762165159706,"GLnxniLk40wW":802189840289,"8mAMZF649q3X":859242304481,"qGpgLj2l7ywQ":105957169884,"iHZBFIOvX1tu":451256564154,"rTwgOwOxNZFK":716118493701,"wKEYoUoQGJwh":767933869704,"H743gB08Pymw":655267643754,"za5Y4Z4syBRg":354087371640,"mgvbrYvOtwUT":69950948164,"keHU1aZqZ61z":560487393362,"SBgNQFDQ2PuJ":643334085554,"KkyqILuQUUsd":463888780649,"o5SrfzAjDOg1":442388686723,"XmRq7Qz51Oxr":414984891686,"lDyE50Rhp4nK":94572731761,"dH33MCxmkenU":442810242323,"BGL4wzxCcFeF":252275799473,"ZSvcUeGTdofJ":766570388060,"1KVRlhWcausa":920982191466,"UAjwHzaIRQS6":584114254310,"vYqkVByJqglC":739388796741,"l05BkJtb4LaN":97158218908,"ebgoxAOjqpYU":979509835371,"mHgmBMZSFj4j":201903197209,"mhaSWSoOsq0t":890481862769,"oyWBcNujU8g3":693966018356,"fzGgbaB97S0R":758753479833,"JRuif72N8VLJ":867409247938,"chf5sy586c3S":982638637018,"LVuOQkCAuLxF":980937474632,"P5aankVsY4Jk":894983836627,"wHV7sv6pEgEU":357889432749,"5ek5R2TXLOdf":546750227423,"i6veaF0WW3NN":852405103401,"X3eNrWpN49r8":91569126478,"fyTscGuQutG7":262424334155,"6ZwCjFXJbmGQ":119760583209,"j3YeyMnlMLy6":732045265453,"Mst78hDZyPNU":707558415153,"k0i3VlLAQcBN":176882050265,"XrF6l4rnsOQx":807879490690,"GdnrNME0ZCbn":152102209544,"y9rjtamIhTt3":355242117344,"AXZ0rS9BMG6m":660963363183,"SOLwwvkTKqrw":780953831542,"v2jlttyd0Ku0":714000518243,"dtDmzUwjMKW0":855962855220,"l5ZeDyeJwMoV":106799577486,"OyDwk2zpGIz7":407630792720,"z66j6RrE53Q8":971979486847,"fmpXrj9lhWCk":588036920338,"3xDfxkeGDxZz":540670826760,"RiyVJ8nSKb40":384182809309,"5DshrGMttB1f":723948000431,"Xg2Wdop9KQ6B":501347702786,"zazGk8KdmEI7":563431557542,"Sm3O6LV4eqGL":666480048793,"ZppE7Dytj91C":504825154822,"tMeVFKpgNMvN":318687264593,"A0N09xilYPEq":413990972829,"aZjfRCvxhJWW":473082571279,"umLRuuIcxv4k":987179753327,"3TyPDUhx23k6":681248522113,"YuN2uBRDWlUV":916418386157,"CPuY2ULvIsj3":515060298249,"G84dhL4HpR2m":774809716834,"q2oIk63ZHN2j":149610747187,"AWyPVDuZR1JI":114757585560,"4ZT98g9mLvAx":676719831203,"hNrAOcfdrdnM":138083700795,"ZYUHTgRZc29K":808418902468,"XIjZjqVVEvXn":58739654718,"HBXi1v3P6Lde":248051055083,"0fW86ovoEzR1":269263691752,"Pwv5HvlsIxJy":189371798676,"W9OuCK6dVXqU":579473085748,"nXmjTns40Lsz":915738228546,"0el7zozKZcr3":9604483501,"w36FzaQlYlyt":680986963571,"mZrUDvRy7VWz":566484729029,"RrZ7pLyGEOw6":201083520607,"CkzIchHolxbl":782521176266,"b9W4biiFqpB0":206087267488,"GkyE7oLqx3gM":59216095607,"95MjlM9UXn9j":733636775493,"UVDXAI5emag7":141116911146,"f24tgTyjFWD3":497946028369,"4BnExqex9fK2":544583707193,"OOVGP8EM4t7I":698589316747,"zgZiizj2SAkY":419774578284,"F6akYre3cR1z":854732235086,"vXPIV40JPclL":863491507818,"nIrLCSwnPt8M":503160161209,"B5XhwlHSKk6z":963959661730,"M0vf7XpmUrsH":948541417885,"fHTUseNlUAPD":69740849777,"caCMvXvELFod":20246442413,"EbKOJxxyX4H5":139513812963,"FHXeRlQjB0xY":988332329477,"zoILXefpx1vk":691979926064,"ILGO5Qqml3wi":193001869136,"NpEQXhxvJB3B":143031018878,"46RqyGevCLF8":316058088069,"iVqfiByZHWD7":897455847306,"RWhXbjCJsvz8":329910829732,"edpkhnmFdrCe":511648799433,"k11I8oMDCzSY":9141554754,"vPYk8czLgjet":168135501107,"U1AxVRgjjdeR":827982645235,"8MJcB3XFIh2k":516940518430,"4mz6xZ9TpbyB":857488088740,"vk8R0xp3aCAj":954028779331,"PJnt9ST9Wb78":151858654234,"sPjIeaPRradB":957885095633,"tKLDDBdBS0Rw":62586532945,"9oiLlHCnHMYu":695350278246,"D8BxcvuJxbfD":910878551081,"dKkIBTpB16Y4":402251573762,"rMtovmFla2Rz":301193953226,"PnZsGNrxVEBl":995684819026,"jkaM3TX77Kpe":836330160622,"hxgwmMtHRMVm":484192858187,"Afep5ZG2tsIV":372730165265,"Tkz0pDLObSs6":943255615302,"7IPYAYR8ge5j":376282082291,"h5xWdI6ku8Bz":591418022113,"CcBrffwduAck":926474861185,"1C4MJr4hfk3r":57850067197,"4EsMbvOreCih":508555591062,"Ikx6lzYP0okL":340851877852,"zpgLduQf3b7A":481974722223,"XQ8IWenh8IiU":13548235395,"6F0szNHkMHH1":590879000009,"HZJOxMooFPm6":180996841459,"PWDyuujHol0j":630412502670,"2cQb9cIMRBVm":970880313916,"fptl47X6Z86u":285886745460,"A9Pc3otpDaRV":651086498189,"YbVE1SEvpUBF":591883663289,"On183FP2QTwB":403356702216,"quPuHWImTSDO":95607232968,"VvZOslCUnROY":523658101519,"v9ZDsN90IpIC":821853820443,"5VWlDdNoZU3k":64415266735,"GSAUWjR3vhJ9":569087749696,"oWOcGWmCrVKV":731218217110,"0Aj9yJqSsQt9":590383169670,"FBfqH7xrziV8":901711270166,"C5xSYiMKLwfZ":51826703817,"sNUVScAvCKHh":467111690273,"3ru9sFzpIOun":161127669779,"pWzw3F3C6TQ8":967621899081,"4Wr1CIF2f1IC":492016719677,"tMNuTEYGkhxv":971186208084,"CETDF1W84pnT":138645851962,"83Y4QZJKOtSZ":387483776027,"tkHN41D4nVoC":973163988667,"yZ6L5OAxSjW7":934064447922,"IXUge6bKTUO4":314691413201,"UZEpXnsqxcId":901650362685,"6q0ocMpJIQYC":804615211798,"ZmqgdCXZ0GLa":516943812555,"3G0huiyDblUl":978719992747,"51FbZy1Vj9uh":358703480848,"1WlnsNDl5eh9":229973705073,"vvoTnXSjcYre":489045441053,"opRjRISSS9ZL":942573309382,"U8GTr1J8AjD0":395237917175,"xk2Gs3UAV2Hh":538337140939,"D5htz0EnoONk":873374969684,"hvTBEY9z2vnH":470830977128,"g7qPlkRYDC7w":877995582378,"u9EyonjifZV8":600842154184,"tzeBfHnlohCG":824206883778,"qurT4oHmmA2b":179115223328,"O4C6N8ossvMI":913241915874,"bOdPKsBIKcdF":435157186093,"H65VzHHXcF0g":913174384236,"cTJDRVs7f3lp":125568815901,"zCREs4CvdjE1":94974675782,"BadShCyyETRW":697464417298,"T0wLeoOfqaT9":918511791670,"fKSYDuFNFgNn":751414143509,"4r8W4Ae4vpUh":547174320215,"AF1k8BQkCJn5":625057461359,"4awfNMLrylnJ":156186745391,"4xsO2A08CIoE":549089340920,"7sldj6qFLf2g":360105133086,"b5QtX52No5cR":741116946962,"X1kQNLZgDUbd":762342723267,"Uk9HsqbsXTmc":658734165598,"6gh8pGD9O72E":164175317335,"0tN8nh0arZuq":797963689444,"3kaUwGp5ChS2":695174168251,"IJnWZ5DkGBdt":103517992666,"19WpiUoFm549":674398995592,"l7CTwWFcVdZE":509344662739,"v2B7H4OQQCnb":803875986116,"ljNWUoOP7a26":187786784934,"Eq8RQ6TnE6Mf":304050484753,"kMH0yCNyUOIG":993038749652,"9tNoQh3r7cqE":706408964577,"Ta6oglSoeE1w":322900714230,"rg9EXEOWSrSv":801406217091,"R41ZBdSCnaBg":227503075983,"FwKzJYKltzzS":968602720457,"vO1WMB4LIYD2":495364533470,"PBvpLFSWkhbY":202484993652,"aXeKGZJitPfl":867545213771,"HQ6MyOI7rV1T":79631321540,"Bv4rpB0DQoWp":75605268472,"AoVy295kmnp9":485912679714,"dc0cVPpwBcgI":217705540960,"IeK6RAb59mfD":307765538661,"hhSVvGe4v0G7":531978985574,"19BN4BHYUfQG":635453706372,"dRXNBPPamYHj":228428937168,"JjjlwWKYBkOT":660381958061,"AAREmlZ6pN8m":189343638485,"ZQJxHcQCcTo2":221304571065,"dng8RLvIJAJj":69066697084,"zxQK8lFuzBzO":263456732154,"LS4IdIRxYuMw":999781265070,"MVBeXFbfD9s2":312998267862,"FeKcu4S1PoXv":485280517116,"kagXJdA2r4N2":365971478343,"uhxNjlZWu3Zw":499204503666,"Rj6c6WfVeHyA":76706455765,"hvythzlMWbFq":331812197708,"MWtMli51Cfmt":752977219931,"TTpoRLMRGerM":483774750501,"8XrNcIVpRTZB":183467177658,"SNh7ZTot4ZwH":651514029241,"WaJoxyuLmApw":417055639306,"2QknoRiHYUBr":894779222809,"u4PmWEhW5GjD":965183392787,"jAzqqnwn60tO":571341442364,"HQMIUUmXSdfY":830975523603,"56697SG8accB":227827971380,"LaICxawMD2SV":206148079295,"CVeeiNwPxKqw":57733614223,"kXPXpozRoxyN":681217322599,"kylDotl7BNHG":629374087528,"8r4FfezYlKO8":880773838528,"wr3ajqo5jDL9":398670850854,"r4XpSUBBLF9b":840762808142,"F5K6LIZLH4b2":448129378310,"50S4rA4FEEOF":45322852284,"lnQ2qlis3gEc":75359060183,"adyolhPj7WsJ":623572107711,"cKm3TKd75ZAe":519544911565,"9MTfNOetswK4":3082135918,"x4CzIjqlwe4L":196355056568,"iV6hcJ7kdDpC":609234444543,"IZoPIFJ92XTd":913767716222,"juEBRCMfJEGM":993272610492,"kPXyylTn92z0":606462505704,"krJg1U7YFm5R":681221539690,"p1Z3PrDay8vV":138468722493,"BDZ2VYgmNinv":900730460255,"GvBimcDtxrgo":994517818141,"XBHbqsRaBDRy":147147986739,"CajyPSv4qbwW":686072359007,"Bjzxtk3SazF6":936953487432,"t9frHHGvetID":76541436681,"vzlYmdFqZ4TV":715134966688,"8uEDh0ISg23K":224722439113,"34OO1Lg0cXqC":6151778180,"Pw1peCiM7aSr":29887575873,"Q7cFTbnUQHVe":694288410923,"uG1LR569XYWF":718473795474,"4HAYJDZDwLNm":310984427975,"sfiOBUEvpt1U":23443161604,"KqvFq4MZBCQk":531516782102,"P558If3f9RRD":63489259722,"zfeiB5AuZ9BS":461578180385,"qUSuo0Sw1cwD":397523742325,"5hFb4MCMvZoK":662785763824,"ZAwXKr1FyKkT":398189995585,"qWyYUyfEC7Uj":684664971220,"oY7DBCCuf90H":72627129058,"AbTmMSnCeY3s":916477755952,"xN2ULn0hkjDK":874791733202,"qOB7XWhd7v6t":149457769117,"o0XLSrW09f0R":202738359188,"tbjG66D809Cm":321171393474,"O0bn3kOaFsEk":15329547382,"zKRPgx2wf0DV":194859702595,"2i8bRFwq625H":932672671939,"ytbKnneJycde":454934876100,"TsLoyolfJyCC":684173704507,"42e7A21d9YIt":852588204013,"NWboWg2p0WjY":621271416295,"h0it7SYFxCmF":237681364302,"fBytGIDcHVZO":911803397255,"6ULoqeXaiLZo":336722931174,"0810bmiYg0Ie":360248894660,"h8mAJ3OKqasD":780696831284,"lA15dWno7RGi":986514408340,"kGLkrGqBTpmf":875080735002,"zjprgAIbanQ5":135279164358,"17eGsriNG4Yu":494430884499,"EXftWJMUgM5A":227851697777,"cFaixc6pbZ82":90536501150,"k4WjJJOhYsu3":860053714247,"qrHPLMpzjyD7":735367463560,"yfbCi2uP7aN0":389121706986,"qSFIQdG4edtG":174879709208,"gdxIIEwGvhW9":698537907946,"LvtDtNH1Lbjw":701754777342,"3B1pGXs8jHgJ":487220216605,"Gk1qIsIZaDfi":357347819020,"EcYLk1O8EsAS":3473923820,"EpgyPDxgtMqi":498230202368,"tGfClCAxFw2o":387838729597,"7VVkxKdPHfpB":38454023202,"tUjQSJvMteme":243124467134,"48Qfe7eyu34a":432987828367,"wBKrcY5mSrnD":373604630311,"MoUjObOu2VEP":824869193612,"vreUvJmaKeie":825512364662,"YESj3JwievsE":782355662181,"41AarHct3ePX":305647629140,"HKOyDrUREjgr":33620135759,"5j1qxskDpuO2":72293763464,"xV1GqTDdkKFC":299391286504,"m0ACtH6Li7nf":323920808046,"qQGz2I8IMoPq":775557958289,"HF6mXPVmeI83":51419485452,"t1yjl61SWqw3":914028069923,"AUXGVWJTbxX4":641841360578,"fHNYCguRY2iL":723119613700,"Os0aZA0gz2zd":343653494693,"t3CDFj119p5f":761219179797,"BNFBSA4eJfG0":904996599249,"KxHXYwIbxO3l":712115969676,"aHVHh6LMh7pe":661348812355,"UUp6V2f1JQy2":487000638684,"Tjm3n1c91Nvr":128303716603,"ktR1nJdWcNcF":699009502415,"a5dZqYzHMVQx":991065595441,"0x9WjEu9i9QG":649107526931,"0zwAGESdxilK":912209099538,"BlGy15W4fmbo":597800135247,"oWCBgKkT7IeQ":734851594984,"Zcc8qc4flpFR":767147095766,"Pi0DXEkLf9L1":801916661949,"jWSF3KDGUU9R":290032453238,"oFO4O7L1x5qN":230111099691,"lXTHvxkEiGAz":629932653481,"kKn5eYXMOY12":18384121204,"9CrbMLYOz9nC":924276539802,"hftQkaPoG6yp":942338932135,"1m8KROupkzTz":795515153,"mUMSNQWy3K7r":267531267026,"J5yYkSYHwKYJ":114148760756,"hdc2dSgK6YtH":384732786571,"LkKthaeUOhBe":777976989874,"AfcolUPfy8TL":644506528594,"9lMcLjxm5Jih":916047170859,"1MBZyNaexB0F":81597382062,"bBNSqZ8dkFIM":809612440654,"dDdcWsMJM2tL":314762669841,"vFYqUEBN6UFe":587555357262,"1v0tkHLPWGZR":924514373907,"BaUhd5oEqm3V":154255397169,"cevh3LadXWZB":102610960940,"j2HCjHWmfvvL":154502821528,"09H4OABFgYIj":505521914044,"MhyljDxzhxoc":420259953325,"AZfZYMf6ngMa":528258789414,"Zj2O6JS5VX2e":763441451361,"ojaZMjhMrvNa":804820372215,"c55EkZy2xljg":762278687755,"LxA7krOBuI16":400924868565,"NE5GXSlbQvt1":547681773241,"E8f8Wqc0OabU":869813065904,"MpceBMyrjbOk":723398116722,"lAINygMderEX":423550023623,"LqJ3rFDM9TgH":834101699191,"FKK1p3It5JXu":11117430629,"MDN4LYXbvkA5":835468525184,"kGDVgJBsFGLK":470904858104,"WTO9eUC49kHQ":630936616419,"5gTnZqRfjVND":918387786471,"Jq9oDCIul2B0":176675122248,"LqjKZwXx21MK":482079111705,"Fj4ZWqaLN9qB":759665174535,"FWR27cTJBO6I":990847947229,"tVME8pe953Wn":55876632515,"R1pOgcKj927q":364409556800,"HbCZmrAtJj8s":417807187061,"WKJlKz9amdYt":884870996601,"tYQdIMBUAPLP":602949003859,"em1fjVhEIYf6":613305734771,"qfReja5XJsSj":471537473781,"qsGi0XeLmqdU":641379447434,"ufDi0cmi3cK3":506226945429,"VQ5CdTNSlHUn":178420974084,"9ALFAq8ZsVGt":262258833779,"wiZ5g0hYWEY4":853182409724,"SpbNovr9Hgh5":82837492849,"lJFwz5llYeGv":58568291705,"hPlKQ9TBav96":422712823913,"b6PxppZrzgPA":424715270056,"0dSiIVvsnP4o":91834859229,"NtP0TzbRM8Sb":489555130373,"tzwOds3D0vww":155521353082,"Q3XrFJQzpvDk":526523206314,"VyfrOL7p8LAA":260983006241,"5I6EB1Nn7XLY":787900104256,"l8KQvVuZq3w8":509000841605,"O0580RpjfLX6":438781853679,"Wf0sf5H7AemE":574452977423,"7z9bQ9bKnQeJ":151082464394,"HxndTv8qoMOI":35505672294,"xRtBwfcSi3XJ":661978305997,"dN0VQUBeHEms":624186175024,"IQZ8JDSaEJkb":818686405032,"XKLJgWOGvk3m":538850708748,"kxpmdTxZZYHG":194914374449,"gDbdDgMisTen":803683115678,"G4wcRXS4YebL":26710748317,"MnUVmrzpFZU0":737493610332,"auF3Zou4ltfC":54910970943,"DUz9Tb6EzEK1":854237188099,"HObgtoVuZ4X7":977384308051,"tWbSL0hkv3q3":962527324389,"ggscd8aYdnZk":782378502511,"IQV3Dg5LsyTt":288593843829,"nPQuYhTqCanO":522066627980,"xcFPgSukc7Q6":930247972702,"3erDoUPLrYgw":404132257745,"ktj8MP2eMvbN":309451304324,"gWWvJwxTCzq3":172768129784,"4lzrrQve25MJ":642379288630,"aVAKB05XlUnn":765221724688,"ubYvIquyaTUG":558087851817,"5Tmd42QbKy7S":771465892741,"zUhfKepqX433":748464378094,"jQVbTVrGTHYg":998961172023,"Wi5DX6zJ9jDy":701948564758,"aPVc4P6pgQbY":752432192624,"2TWjIceSgKX9":444687869709,"cTAESgD2VBSC":216286625573,"aFDjLr9LFrB3":167338671534,"Wo7nayK0vp8y":254125774368,"5utEkImxVLwh":293795009103,"2RLYFGvhX4Z9":169226636223,"nJ85TPQRZqAE":611638400635,"WZBASFx4jyC0":528764476467,"HArNn3tFRhj9":389100441780,"UqxA3JYDMY3z":802695279159,"7HMTTFnvnUTs":283998285425,"PmCoRJAQpSAd":731369292765,"ipEsq8dvbBN9":998024617355,"OjxolZeaLBRG":337359837677,"E9AEFNe4v9oB":508321495036,"tTUjXB3Wffme":142377970373,"EMVtpS8VwH7W":945206223063,"GYK8IM3tH51H":453893407253,"nAjC40tCvGsl":453300156491,"Ujjsjjet0MWj":875753168483,"advwKrqdeXOb":641352986255,"6v27h6HB1gWe":700105115626,"aJvQZOesswfc":346565604629,"3K1usGchfC13":200457051734,"9FxZHuKZipbo":789817162252,"yVFXPajErAMJ":336365684268,"pg7Cz61bRo9W":232577550314,"1J5cguIODOzw":334470562504,"PgXodwkQRCZb":513342304158,"0dbFroKRKqqi":755047011721,"GGhQWYAYD63n":281601281011,"aUAoTZP4ILpA":585384263578,"pThMn7Xwgpnu":640029701058,"qO1WBOYfkTy9":612581019765,"sx0Hyrrwz6jn":987440589892,"DVMFOoId8kIw":652177886218,"RMV4Ga69T2TP":810762168472,"5hWTs2Y9IBNz":91302968246,"1iwA8QFIk0v4":464392428750,"l26IhClP39oF":517558300063,"pqetENfscLPw":954485040935,"8CLeRDGBcb15":835275594812,"4bvVFnXCo2mW":410167796212,"aguXGID06rVh":593006867605,"05IEDont1Try":581296783170,"AVOEqNOTwJMg":26843182421,"CjTlBTirpQug":291055569091,"MSsgNm6Fq2kk":701892435775,"z3U7rTdyO49B":145524574514,"aoar9MVJWgD5":306492833561,"CFbpoTwv8Yzq":58301919297,"c6Y3IurdTeuG":925717712290,"6Wq9tY52N6jl":863180388925,"6yq6JuzB84Cs":551538578815,"3Ga8e4leTRIo":560748082689,"SE8eXji2XfE1":58898394353,"0MTdf27w5YDu":495446838175,"8jJszCAJocxo":565651237152,"WOTwf6tnO0Sw":910226728661,"NIRNAFdTj6c3":349767530551,"rAWYxDRrnOmp":525157394431,"xlXRZJEDdEq5":639160327686,"dWrCAs4f3DBu":617253965702,"Oqo1UNWSHfNx":795433205791,"MPdMNh0Nozgi":721863228132,"al9elpwK0Cyq":230284728632,"cJ9zD1akD6ja":226676280578,"i1TjMohFgQIF":110513453256,"PzNES9Wwn5wy":806614052252,"uZHABocr4jzV":462564920922,"Rq3SamTYZp8N":597315470195,"GzNICsVS2No2":633627291594,"u6U6y0yv22fn":961675515436,"5YLpFCuUnHw4":399226619243,"0TupKH6pumOC":91869113125,"WlpnIqbsjR2n":420925878969,"FR9ZXtq02HqQ":416060218907,"Tgz2CvSxRaOd":658861401881,"2TdIzF738lly":421214656845,"4BMnWrUmJquj":430368062255,"ZVQphp3votUI":530596368010,"oY9EzxwJgzYZ":855256211435,"OV0CchW7yefk":250606053537,"05semyOTIuFv":83556256685,"UCg35e1XtBsj":892957004387,"cL1BvutZUsPp":900890890253,"z933WqHJx3KA":65870562364,"mDGqloZoMNS2":830264943382,"4Y3D1KlT9Mcp":659421038846,"iOChWqRzoMYr":372340850813,"BdpPnu15GcWe":97707899528,"h0k8MJHCPU4T":775603140657,"QrzteHjVbXQQ":713646357635,"oEBDa1pvcybX":400921448132,"OFKGzYihxPGr":752353421236,"4x1Sj3XiIRmu":835296831182,"PCGeYFoumAs6":606266253611,"W5OSQXPRQBBx":331622735869,"pMlGaPYVva5Z":170306640008,"hVNYm4IeHzgZ":679685651272,"WHzNhjb6D3ys":752421112092,"WW5sLRA9OPPy":844622865257,"HVXZkwcbKXge":833401188650,"SxWsaWVuKVGk":446943372633,"akhExrVHf3KE":517276134369,"RdRYsy3HPXUi":354100129231,"7jPatlZK05eB":549020174381,"9A3dERksXgGK":162140420811,"EITwnGgIFF2d":281929106934,"KQ0JghNh3Oe6":657255623130,"oFrIhIzNctrC":267072906357,"ylEtyqzavddl":792115058383,"Vpkei5SceQ77":502122301954,"7yv3HyMK4Pyl":681633326477,"hLsxRaLqa3Rj":887565544758,"7qUqlxnlCX2X":907607665407,"PtEGGbWLKk85":455452554713,"FUriWpdw5YUf":806102427390,"j2BszoSqeX9V":554535012093,"LWBy72ebSjKv":551263845586,"L0RrHwr6LhnP":244366541919,"w94l9EiCSWdd":244693013240,"8CzeCfdzkIRX":840550500962,"rmJbuoVztRen":812625760846,"CkppMSvR0xyw":470589671737,"jW3a7QeXzOZp":312588239742,"duGETnnUJ7Jm":538472663550,"JDsNl8a6kJ1t":422367120629,"eea1krA13qSR":395139740071,"EI1mStAsMi6I":708961705883,"4oBb5QwmvWqC":423470544913,"CZPULskkWgcf":660106138810,"sDQ1Daqrg7Dz":131013021394,"KNCxvW3zRA4k":308467078882,"th7iZl87BvD3":157565618448,"Ot4YDBpUF01Q":903486440576,"Q6FoE8a87vCc":874386051900,"USbAgApxpkzc":744849082428,"RmOY3zwyyAnn":241914558550,"NY8wGvhoe00Q":342337639002,"2OVc5HSY5APh":219858891538,"0H3FyRlMhCJl":241375842236,"ic04mJraBsTs":369871986848,"ungDDHUzaGzT":503519823187,"Wk2woEFktKJb":635935166414,"rIyO9sBCWApt":539915354823,"i5GmI83J3CbV":586974743501,"EQtYY6PbKFp2":11258454343,"PGzGchoJ12F2":254289171199,"F1lMpjM3K9gb":324151980082,"1xC9H5IB6RXw":658370676600,"NFcOKGd2UfTU":640570414828,"ZK35wtnEILjE":392431099218,"ouB9pAay0B7R":393633872081,"esS9k6ALVHRc":410924640384,"eHLQKdtzmEPJ":187323824355,"4ChcPsmSGnYF":894734468779,"pnIcsWSsJSGw":779896153267,"6Wt0j5YzZ6jz":644315379387,"tuuxfDHn1L50":429792816363,"hIDb8fB7oORp":273149253421,"ygQaGHHabtDu":90337163375,"lDkfx8EptXu3":351149418964,"VBSmS87tW701":717798920943,"yKxFUj4I3VPy":574212384797,"wlbjFomezQRa":820466141196,"u1J26uAEjJsD":95749671490,"3ZNUnOgGEsxg":395402574225,"Mek5IKO8A8XR":469326787247,"wekfdpnpTbOm":466118790305,"ONxjY6DXsIZS":830835698668,"DObPrToMZ6bC":965049857499,"FZKTp5svcYis":281773655303,"cYvMQEwGvzqi":532893348003,"lmDGidwc72qd":410707604631,"ePyNWwPKmM7Y":296658662410,"bROUT2GiwBe9":393992650571,"R9O96IypZ6qv":994204290686,"OM741C6MX8qQ":220234424861,"to5uu2UbESY9":228541277560,"zJRRoPC8GRGd":999387974433,"06MFjvgzfHFb":352607199358,"hGaT7Gh0gIcK":250308455908,"BNxSH9VM0oEs":802167891319,"huUb4dX7X3OY":123580373315,"PbcoKmoKDbQX":519132223972,"LYMmlcydPqOM":239280607472,"iLvh2bpWAEpT":159911414414,"3ia9TKY3GxCk":992580737701,"cmJiRZJxvI6y":768953836957,"YqzHAWgecAER":275909268175,"9ncElMq8MYUK":535199284364,"3WJydZQlBuf4":4871759582,"clZ6x9axWsPE":11683935253,"13P91h49mIIj":698033148952,"Z0ArVCXm9Cp0":289154010468,"PzLL2QgsXwKH":291807077692,"l4Yq2lu6TJCp":892853988510,"p8EJwV2CnDcq":154916978261,"ftSOn6ajvKhF":829639124670,"VJTPi2rKfqv8":24543277279,"XuNbOPN5aBdS":150986008238,"hItr9TbO9FWC":781450738624,"qU54g3gtL6FY":307109941220,"jCgNXywYueOK":565081198412,"J3ucgHGkOsdh":455331626777,"uQSTQf2lYvVm":381020029782,"kD4Ej5AncIYs":416987883735,"xXXloSuYp2Z6":84525913838,"KrvC1qS3FZ7e":750627616682,"0GRCyQBTo9gJ":669790631304,"3mGKGHpwQENp":821455130648,"zBPbtlPzjA8n":434522459728,"H3UyiewandD3":122757709573,"rblkuRgWbFPI":156566559964,"zv6oQw86lgWZ":325400105054,"OR5gOcnEPBFg":140064777954,"SP8OJrCfASO2":960773078821,"YZilB40p9z11":911087641600,"FdTmLp958gtF":336572573655,"sigk6C64bDWO":929881809730,"fXsTOa0uycPs":154310782378,"Odusyzadr4Zi":511279348989,"SbvJ6LydhQkx":107735600472,"fqBLRrezRctP":844466360216,"1CyDa7EO0z36":396032468803,"vHDiTHC91W8t":25367907130,"w8PK3tErv8PB":494286201393,"5fQZvt1ElP0W":850132385197,"PXBkvkJ8so2P":522820100552,"Go3MVlGzeyBi":566469317796,"LNJ8d9F2Fu3f":242222848879,"eXFLpbiuBLC2":454195215650,"7uNPtRcQvt6Y":52722617632,"d38C60axn5KR":381414492945,"GcZy1z1mb4YT":503772133757,"ZEYgZIutEWAx":963270879351,"lIZNG1YvDeoA":581909451130,"m9Tgx9X3wvBC":826933748874,"cctn1j4SOkAb":409202676886,"hjDxolER5aCl":899222529775,"Il4za3d6FxmM":715483299794,"7eZysh3gKiuy":222276381017,"b97JcQswb47K":18816299322,"sYa3uylROi2M":785107268893,"shICLt8nlCOo":32216086364,"0LwQLlE7ZQMy":269524558131,"uRF9XY5gmLi7":229237839059,"EVfD1Ndus7Lj":842046048955,"Hui0vulrx4D5":915617829096,"9dAItcJed6Yy":767708717091,"9SaBMN9HACvk":494793289042,"Xj7D8fulBTzt":86757127581,"ySyT4Fc20HP7":502406519618,"O6j5dgQUKNrz":156103912690,"fLUV5yNwIdmA":758349341387,"ZOPZDZYoWQOT":506900588450,"mrQLKv29MrCg":141469769480,"U5MMBXzOXNwe":318668179586,"S0mVrGMTCVPl":395100618529,"8ZeGE2bw4A8U":909087819431,"cTfscgEvK7iL":958276820620,"xMxOVMy0GGes":712167744033,"dNq6sOmukI4K":623770438303,"5HvSrMzCPRx6":110904812863,"1vqRsEJDElc9":774833221991,"5C0FkKbFhuLU":530994240596,"RfsJmQnRpkTm":400955125172,"eQTtPXEaCJ4G":576798927276,"lexKWpr4Y8uD":378774528691,"VkstWVhgoaxz":655983574114,"c22vCVBQvGxS":708339825589,"M3xeqah04cQ1":658326692384,"pnOC5YHsq23o":613590125832,"ihjyx9Cr8mSp":376397330683,"g38QwpA79bDn":917610634200,"CzhDbKQoNzZA":316335198340,"PUyZVt4oPzNb":580124175482,"mVqKNZ8znGoH":184881353616,"iCSRfYEtoIdf":243491777179,"s0b5S4KhgZQ3":806760319795,"4xGYy7ZvkhsH":483681590637,"1qQrM5sLjWRY":80866655563,"tsMAq8Pmt8wG":804706698503,"AgFJlYN7gRhA":182479938284,"vp0OSgP5p1dB":718103897359,"iBUkWxMnYxyr":397069861696,"8pE8rgfPhesU":148881690700,"nOPLmO2kPzI6":124470091406,"LNetinbGn2Hf":544112136305,"cOckVSyj4PR5":909662428800,"Vx9OWMeTsIuG":450712957550,"nAa0u0LxL7Yu":47331524306,"4FqosCS0IUOn":627924123843,"PUwRLO5Svnhp":104817445074,"1KIViMDjZpTC":225910992711,"H67SAG43SOga":192985358702,"dNR9QeVHpMzP":367939813644,"z3j65Yyj0QFU":457510050383,"GqFor1mHtfac":213161625441,"KWcdUyrnECYP":145216386331,"nVPlvLBuIjsv":444472371491,"YMmENMm2JCpO":717940021969,"z0TQgDMxdyJ8":728709426819,"xiagNaLVk2pp":430758312606,"Tohv99lt7hDy":769961516345,"e6vYBV2N3jSJ":968152120353,"KW8aEIQ9qU9w":940410918084,"dyxJY1E63Ouj":538961682366,"m4YaHEkAGIOy":532877761969,"IEx9cgBT2onj":216725338201,"9Z9F8NpvlkRT":977957682058,"TWxA5cklqn1v":364212324810,"qQGxGRpG0cQV":320343787614,"a1N2xkhWKaqR":778513216462,"jOx6whVTzhca":248276037673,"6hqwNUqpoOXr":642217506761,"7KgNM5WqPYKf":56719452041,"7lcJe84ygILO":720206014401,"Tmh6EZDrmV42":424121359021,"vZi8PtiZQxhv":361316889277,"B6KbIjUneu21":101581283669,"sWxdWbgXeNgl":551502111436,"e1gfzX56W0KD":231104442205,"fqWEa85Lq5a3":392704701150,"Pwhs7lSzcbxQ":135956998636,"MNMIKtzLpBe2":446528128616,"a5Z4zl26rWPg":657634104476,"vXup3HvPenDO":724563006906,"PwMvgNVe78K9":397215241151,"IvG09cqlS9O7":668855620490,"csbXAZC5SXSK":410376844792,"qX4o632RtzgY":514141402712,"Gx8FHS5ISvEz":858121987604,"a2bhKg2faqaP":13805394542,"R4Ece4z4AynZ":27900528835,"Bdo56FFhxmRe":71035456368,"2fw8eN8evGcS":133411777831,"lky2jdQUxBh7":870522062203,"xwBfNXwJXZv0":191890783825,"d4bnzBaruSTn":616596278682,"K5auMtHUMOTK":563979937984,"mrf7gXKGBioI":451164504328,"uxHZkEqRJcEg":760931167785,"d6pgkMO7HjCV":395802742061,"EGlwehek5RRG":252117150559,"4KhCmDJ9T4Wh":564012955900,"Koqc7m31Thfp":669034603675,"jAk4pEmO1vl7":200365978868,"oEtxI5a9B1ZR":92654709973,"ffZcXHL3ixRc":776829288865,"gZZXaFzrrhwV":346740359351,"WmpRQQetxKfh":180560030922,"ho6eCuD4wbWo":166676135,"uPtrVoEeOIOh":337919505959,"FVl5mm8I20DT":787904498468,"fFI0a8ZbgU7K":502164503274,"NrcFwQpusmJu":212582304995,"kaVu8hw4VFyU":608689335439,"KDmQMloJenPE":589394481252,"Gph5zZTLBQ9q":853957196030,"KgYiV5VCzAUD":444008085730,"ceLa7pwVkNEo":569748684047,"3IPCS2IyPe0l":205811814782,"2kRlKetx9hUX":674690786497,"1fu60CSGm6KB":346574445664,"cKUKWSXaBZxv":854202952537,"sM5Mactp69V9":648428505852,"JEc0PBckBZhc":887695956692,"gxV9W3PYyJaC":269300388672,"1biGlOPhIu1a":879982394493,"CPV6QILdWsAN":925313449513,"o0OBOVPAixJ3":164542092234,"BuciX8fejVBS":750267026048,"yI2hoWM3jgbz":546212614770,"MeoUfbIBjNX1":906396898888,"0SA7fPCPH4yN":11382759909,"lnKdr3P8ss2m":342692630490,"I59n865lcgUM":408154235830,"KvClCRFseRqH":761282594125,"cZ1Ge03hiERF":872658872063,"1rbZ8sfJ2e0E":487980729524,"gvNw2DJz9xOX":240464470054,"RAWK1QWshaBV":638210747715,"5jqEzXy0pA1H":73932192457,"wpJ1H1A7PPoF":307245905306,"aCEvf7JGHlTK":99723187444,"qb1zKrxd6NIP":34297861577,"9CZEdSRSnNie":385186065636,"4rRjMW68SMjg":561859292230,"4uLb7rfIUTnL":927991754542,"jaI4DQLgiefB":592632281165,"W66QjzsH7GmV":21607997688,"Ypxjejb5hGmL":595809063425,"vPYh7GXtvPns":331544230889,"L74qfePoMquY":555039139965,"nEAVoUqLr2Vu":720473424643,"WAOgMcgGztjd":754817996235,"GtFWIIr8IT0X":634487451574,"sg9G4jcnbtX6":607233072157,"Yt3RXfk8IRln":462899011204,"km7V3lMNmrsh":929858945462,"BGlES87MBPY3":197442755379,"jolL04FloLk9":316434432936,"hBWGSgQmeaWF":503996260831,"qMu8WqoT4c64":880367788921,"tbLMmuAzJvLf":359201839974,"k83TiNCbNlxi":338936931649,"kscY8n7kSq2D":312568892587,"qn4PfLmZHHes":114184265210,"EUIDz1NcP1ng":284994465659,"TAcPqXBy7EAn":173028641507,"Zx2BJ2HSoicw":966046942239,"2pVn6Nlad5lf":275428130694,"XsgWYB1rBwkm":942433604895,"yJzeLv1W1CFm":588488218544,"XrJ2LfahpHCH":375220381558,"DhXlMGCaJqMP":238811947895,"8uHYqe1okgxD":105106378751,"TtsRTKqEtVay":881896654726,"0hxNfTuK5IZn":183009972764,"uhK4ZONvX213":780338251321,"FoNS0QNekHqc":447467573519,"vnmVkYFlXhzS":545834495980,"T3xVXMoQNwTm":631475598666,"9m05UXcKJd0v":405374714619,"I4H2vqAi46M7":635416751102,"gmwD4gEyReol":419678700879,"RCACrZ5KpQ14":218477426327,"vvf9rB7nIr4U":448373431800,"lee6vWCQEMB3":372688835662,"6KgFxHRNXLsl":163191345399,"iqXyIWYouLcl":792029288615,"83Oj7b6wE5Sp":336428352642,"5ogG1btVD4xa":728715013130,"XKAw8L9CBTXR":712687657919,"jbCtpNhj47G7":608119739508,"o1E1FkBcelXH":807947737275,"r6JfcvfvmVNn":737714645242,"RvppDehXnPl4":888328062902,"xzJhFdagfhHh":514401157910,"R8SAmx6ed0Xq":272733336740,"Pha56CLaoRQL":48296081403,"RT2lldhG0qBZ":910853216662,"DqttFKbBB47m":527345329582,"oNaNk3vdZw0I":60577842724,"7XmAesW5QZi3":915187334068,"vp22F5MM4Xto":791161825270,"s4qOkPe4Z2sX":715036002694,"DJ5xM97qDyoK":276722754260,"MkMl96N4ShSk":322411627339,"LwKetopx22n5":776588774451,"PYP4usITNgnj":12936989325,"rppJvNJX7Glo":768846174818,"hC939FpKl6Lv":515686377888,"PuC4w1wZFAQD":400866102079,"4ynI5zmySdNe":926671835168,"T5TEB778ICdb":305107670090,"0RkdAsUCbbs1":516377540203,"CufiIn0rNtGT":25760592417,"lFXgFahe20Gl":191232740718,"g2M0yVeCIHSf":277687993086,"YvGbmMpUt61J":710884470814,"w92iqz0Yf0Th":310247072998,"MIm3LUlnKxe4":580012635341,"TjXw2bhlSS7M":757565360535,"LoosECKDvSq2":403947167542,"hPrOByvpSHrc":511761991842,"IORtf7g7Y80o":97386448996,"Yc11UVqoRcil":121800620361,"ByUj0dk3QecD":253092086926,"QeYve7oMrVqd":529753729341,"QeuUhfdGdlSg":499539237075,"iXTY9xLkcuQ8":496255044381,"fn0eM7FWMrXm":746109523558,"LPXRvZmGA9oX":229613668482,"ZwO8z0Fn3O0w":520776947199,"WH8nk18uoz7K":779528752586,"WiX0cYo90m1G":672305908828,"efhWZU3gL604":615303119116,"QZbK8K2vNyUc":126883653676,"ZijpdOgQckLA":713478159513,"vWtGtaIso7HR":135460511191,"Ea9tVt7jw0VS":420151806617,"58t6y98LEiI6":99461359784,"ndO0qTFvag77":330338132386,"PFKEhO1mmQUl":374229749564,"IURX1PrqX27r":486490536989,"4mPxNPpiBmwv":410989561542,"73KTaDYYGisx":345180945455,"M0phtnPsd4w5":662669116996,"smLo49M9YxFr":997136814779,"xqA9INI5uqv1":986665476944,"1Ao0sonG0XFP":737806009867,"hM4fFksSCob2":389667437071,"YxckJy9BNtBm":442183193887,"qI4rVTJrtoog":719185156450,"aJyrPjY8M6oG":926476365615,"I3Q6eVWOE467":505092216424,"PSMH0iFJMwO1":582089911211,"AjTpZMxBeZ9K":443946947495,"f3IZgzF8nIIk":154718181089,"McCBbQuD6CFh":329486816628,"M54xhv6L6sIU":601624615856,"qPbmxAWiA2Tq":639421691861,"jEsj1yYTNABT":97146157128,"9EVKVOvQwNrn":657287343893,"RW6EC6ULIk7O":578784707535,"JhMnYL4Rr8cy":925000711887,"8qlNIy2A7O8r":522958672177,"QUD4Vzef4fFo":273046072628,"EMenkQSJ9hSw":677109382457,"g3zgxu8KJns4":391984622366,"sREsECmUijxL":52189694316,"ONT7k1oylOIm":856648658755,"qEJNytTByXhE":730140611823,"Lm417z43UX5D":727567789867,"A9F744CuVKrQ":557013712599,"m1txpgVCka45":110188791652,"2xNLOb5WiB20":391063602932,"XDvqDmavshMT":229725801988,"ZABhEhzkzO4b":633848388525,"kR8ApWBnpixF":123274953754,"sOpUq2AFevC6":587352865059,"SIdngpE1NMjg":223086122030,"cZzsaj27u1SV":841684555860,"bBDJ4HAm9tEA":311202089698,"wCLAYipFuB0t":83834294737,"2DjlmsH556UU":283584147672,"pHIpfMn3X6qU":935567678773,"SD1ymkUlpf6T":126208001596,"vqZ6rIYQwmX2":474964192391,"BhARUFrDJ9j5":599254420345,"gr2xqtT4tHtn":859565819221,"T9fuzTrqY2nA":58773585372,"ooA514lxgwr8":808823372305,"m1eZfIWTONG2":794103429772,"9GOgbQG5Fqzl":783806646251,"qGGjUQeqyQQx":850608975489,"FuDUTwjRBTdc":73649355127,"pnYrlTmRz5Wm":28076930465,"sgbCMiedpE6p":761201363622,"PNL9U2vHiN83":308907713656,"w1uTEZ3XoqcV":548679367341,"KFUBNTXRJJpe":430767369323,"bAPX1bBt3luQ":492629701918,"4rVBJqT2yS78":234142156834,"1i0jUBe1Z8Al":834580272124,"MY2FxVumUrSu":538887523588,"apquXz7MRg80":561356203372,"jOK036b5eQQi":503752987315,"8l8SHIYvg6xb":594334113483,"scxrNnc6FuIB":589451690900,"pWkgeQPdIY6A":182711350097,"7KcNJRISAVvu":18364943046,"UdS7zswiRz6D":427423879997,"rZff3QKEwrNj":587359327326,"hnZuyoajrZVi":257123158948,"w6LCRWmOWkw2":187232365029,"crqkrhDmV79s":729075016566,"IPYWuwfXkjO8":808881687741,"8eDl4gY0hV0a":125805676674,"F75cuTiQp08P":229560310335,"ffUTksd2zhzB":122325464868,"8ZilcEdpKd6j":34160458452,"uXad1EnQXY51":441956242093,"AuJEaF5Iyzfq":544293007482,"fbKVmaeH08lj":657810516096,"VMgoRslgRDp1":617506512271,"lZ8MfIO4UB6w":274650411115,"MxFyImJJP3Jd":302341536695,"MKYYHN70NImk":29376059994,"PQjZoh4frx3y":419826525774,"K90jsDJNqVSH":649340568960,"rT8JDJS1SDWD":595124323809,"80fEEug3TucJ":293793803105,"nMSM5RqNxHNn":840202256782,"33XabPZ2gsoS":952088571833,"lgdbJj7QRQ9n":660654699491,"l6u4yhgNs2fe":745230451013,"dKAz4sBX9SxR":280968723244,"CVl2c58EXy90":139718345795,"t9LlwQ54TBHE":237506868881,"50mEMiHODsfl":234010689583,"QggGUkr5oO03":325740529591,"RFe1vnEkxIiv":415203503321,"CM6GhB5MKKBb":345156934998,"cOqPeG9XBjMT":818404102820,"eEDhE0T5QGll":131673297984,"cLzElsiLFJev":79875011033,"3AF566rQBlGd":552640815426,"wbNP7X9P43Ls":291991098011,"X4fjblM6iANA":133395980640,"llmx0Qk90ggh":391282114380,"wwqCb3EJEXvN":806816902060,"yWtJ43QwNHiB":368452538475,"EXvimYw31CmA":223116551051,"gspkpbcxhGeH":854746804536,"fAjBQSZCUoGR":112355874142,"7J11iMC7YRFt":419754570021,"2LfB2VzsRoU3":56821867673,"DkMFtfmumN23":194741662250,"KTbZ4WcWjImJ":774154483243,"Aiod07260eMa":195772926987,"bGyKFUWTjxRA":72993468735,"xSOtCut4zeIz":435048655854,"swJDRkmypYD7":769523651747,"9M0t0xYvPJtr":995598434075,"5Zm51LzQQEbK":369968204028,"NuuKCwl19H0O":407842227433,"J9Yre0zfBBPS":436309801021,"iNSSQMK10OTb":27447129989,"Vgg7wceWMssO":717541030133,"SaebSJjBJLRN":761590813503,"fNqWntRlf0ua":891945224314,"2UAPZbX65chq":949980384253,"VtSGoZux3S5u":418274374331,"Wyrqhcy8KCjg":243412179559,"N6wb4WSmc5eB":132335564807,"c0PUdDDjeOBS":707204532420,"JRtmZENGI9jS":369416564916,"JjcdqJRlEoQG":671226926810,"DPTJLebd5g4T":42600594037,"Kiro3vUswiTm":625599630759,"DdezR3CKWn0G":859765068718,"YxL92wWaeT0T":89684584902,"5dyuY5QpycGh":386932867994,"iJXwyz5bPRSy":876165288110,"3RrkdR9jpr2I":686535512693,"9pGB4EvzKVRX":193721156139,"b5ADx64gEdiS":894603501833,"VeRi6tlUFL2Y":195141795935,"3tp1aZLXc3BE":80348847203,"MvNPXYbpEaqI":139806186665,"zqioDPXzco3u":964248264204,"bSUA9uFTMB6C":279282209072,"A7ve6cwLsqch":50080855435,"wn1CC3vbSyLp":818433356755,"24Tak7xgRFGe":111343709991,"GK1r6WXt81kO":383439025581,"PTQqtxl73XyO":627999270160,"lT3Clb0yEXk6":52381570697,"0gx6oKOOXpqh":309484292490,"fqgf0dZZQVLA":88494277850,"4FJUyUFyMFOy":122553380640,"pNNlf7wLn82O":626001876608,"0l1ySPIwjq0Z":94609649252,"sVj9NN3o3dTV":918730739058,"AYWrVOYpZS0N":919600603338,"ps6AZLj1nASs":930143540352,"VSe2bQvmfJ8m":716112511839,"LJqCYtmzLoMO":581336692904,"RMjv2NaOZaJ8":644871624129,"kOeixrJgN3Fx":975939889278,"AwB8lmJQ16Dw":661194538329,"wthBT5idEBLE":734425810942,"b39vYXBLIoM8":419134310471,"6JXoe3iwCxD2":591683370587,"T0r0GY6360c1":505530821761,"nFIh3VcYVrfU":436063917861,"HW8wJqqxN4l4":167133424200,"R32i4BZtrrla":279023185902,"oMaT6BoCqXC2":151390041401,"8nhpCPrCi9Ju":118950294208,"h70StZM8thFf":555261749497,"1LfBzapdao7r":671384929868,"Uut3ApLCnXo8":806128984512,"jogMdvQ5tuwa":825615121109,"dPVgmEyKLlF5":784982147315,"V25qwHLUlHpG":13460106395,"C8hLBSXhxaKc":569662909535,"RBvOnD9nvHvu":29113120745,"AQOgGcHwh4Hk":766782309509,"1tqXKzOBEymZ":441698888180,"vtyOrrcMP1Es":392467701034,"mFZOM6VnDCwn":472563903307,"NwLO0K5s5z3F":323605342772,"oWf4EYsqWMvC":993472880992,"EjoRSvQsxE2j":787046616937,"BcEtyyUScyds":43897272826,"fqbgFChNianI":741174381878,"xQJ7Kb1oScXJ":166042307975,"ihz9EtKPA19M":18546510189,"DDVDsOJE4S17":36702932085,"0KwpgDqXrrzL":180046773296,"L4EP3nqn1AbA":12988283383,"T0g365kQbZye":49221583960,"wdGGdBuCaBIk":97796426395,"9hdGwi7r1Mdm":251462431897,"SsERNrjH2fEb":624851751671,"7W1lPf0nQJGl":202398518313,"cfJvGWDeUlGw":504830228553,"RwlL8OCbi4S1":888919921784,"4kFyB7b3xWTs":36658807664,"uNk8ozDqi58m":216767532728,"D7KJC1mlQow1":961750492400,"sfJyonYkaCQd":724431465988,"dbwPF3J9MgsI":866003978318,"lBkfZc3OCa5b":427903774304,"sE56BgGpOFJP":297926770841,"QIZTFz1HxqFv":640258063669,"qOMYd5reyhTk":467346752156,"R4rj4AV01Caj":703126947725,"WbUnZXqo22Yj":613686664329,"4hxGP5UciNk3":787885872478,"ePKOgvr6cC9I":616829450527,"D7N96He4P89U":255913465737,"FgnCJGW4vLbz":376843202070,"D46pxmdQGMlR":154570887206,"l3Nf7x1KGxmL":330491777639,"2bsreqmTKaHK":357316127664,"em6zmDQ3ZQZY":189043496810,"O8YqvzkkDBsL":580860460349,"TvrJOSLxzldT":60616069662,"qhJbht3jxvWn":84463281978,"bUhHo0g8Tcg8":580368074581,"ux0SKPJXfsgu":554765744793,"bGlmu7oLPWbT":614120920790,"EoUj8DKyfwvn":110232484009,"6VVoUCHSy2qf":626097492271,"9h0ZkYutJUZz":149652790007,"lRTctnda2gBD":201695069524,"HxwOxAh8uxmR":402952805229,"xdu6O906IVwB":293071045711,"Ex5ckCTFckMb":452241754358,"i6rKFccPwnMO":869002480890,"WxVJZv40KHsC":748201081233,"zzVSh9q3lYkX":49211089801,"79f7yS9JtzW4":38678360104,"QAfYizaKOmoi":322754506875,"O1qPW2yPZefW":634948736899,"CS0o0TJJKIzM":497074350174,"n5FBeomaQsGW":941425068272,"YE6ny7xa9AAa":930168551200,"YHeUs4Dr7o8s":778908272891,"Oob9sevwTiej":137654051009,"IhnoTaxxUjGi":999379433935,"4uy4Rf0zVo3T":358924987852,"NeU4CjLLZ0y3":298702088273,"CPAxOk6e4oK2":384456404767,"hnBl66Z3y2JV":997081031857,"Wns51JpKiNwD":710898046952,"yd8SFlp8PcM2":989904225532,"ntTuMblBx8s4":162881757607,"frWGh25JAC3R":540220384242,"wXA2wnON0cWb":438591006901,"WJ32shiO69xJ":780571503137,"8Dqz4TqUa8zE":92565422925,"UC8Lf6WVEWx8":59571806400,"XHAVSjLtd46p":750150551678,"YOtzq2dtI3X3":256497897545,"LTfCXk1ZRfuc":890231506974,"zdZJbEVj2zmc":833467054643,"C4qidmoOynBk":733316449527,"eq1PmkNUvePP":259961282561,"hohKu1Becf9M":505184762786,"nqvAu7lW4KlR":725339289975,"WHTWmKB2cOUV":946908260127,"Avl09gaTGOlP":353937984729,"xvHsrKORZlg0":13649490813,"u5zB0EnHbAQB":545925993168,"9kQUjQHOGqQY":697802853066,"QZUE10auPgmo":372071141885,"haZMBPiJoP1S":83133079448,"WjZ2UHHKFypP":143139947920,"Pohyl4VO9z2U":579504510212,"62EzO0gxJKAF":861669957787,"CeBzPpAneEAV":40047649384,"RHwchH3n6yWd":345447059336,"QEFzVEABdos1":2672260088,"pDAjZ6RPpsxN":793619883479,"nA2TxpYTVxjy":183735323944,"CdFiZecnGNzS":672750746751,"v33cdSYSAgXw":321947540453,"QsXThKTEhTwe":778484491672,"w0jgDD1b1qdZ":197124593920,"JMka5SBRU15H":853988654696,"mHMLlMOIT4XW":907699544702,"qDDPTGb0G5Ph":391257806962,"HxlIJGEcd9lB":299234000975,"xEoSe2hNC8Kf":120699921507,"95Jis5S7IMFr":215777462971,"7lGrCdglmD6c":532293591619,"SPIb1k2P0XXL":931794303582,"HY3mKOlHPest":680121025940,"LteRNOJhY3KC":285117425345,"lYKejJb0vzHS":566208988236,"oVckdvj9QA14":485639637119,"qdT6WJNPkAMc":737848203667,"Fz47lvkZSZLU":264538960129,"om9dQhlANKqm":857056315886,"Ds1vNO6MIZlF":124301107111,"W3BIax5mroQV":87144797655,"il3WgLzbKLdx":339976029236,"4Jd1etcONqkN":748333846645,"flcUELkQxwDY":769481775955,"JS1uh9tb88pg":873876678981,"fFWQsuH6lwN8":555327960080,"acuYNgGBlniN":61451718426,"LNxLiV6u4BA7":559200403743,"FG0se2kl6GFB":259901092628,"W1ZClXI2t178":64189165403,"xLgewbvF4um4":653808200264,"ZZpThgnUt6Kr":394873846868,"Wl48O7Jv0NQn":347836428787,"bFLs7CtENVd2":278480240072,"VoASkMuwrGsb":180841692185,"OkeDA53eXLLX":709750058417,"0x1j6pkVE85l":27627119998,"ChYcftShfn2m":321383443452,"B8s5ihgXpkX2":220945829552,"7wj6Nei4KsVw":285297974430,"15XOcGwyfT5c":318540603797,"6KuKpILmCcKj":798158502073,"qjhDarTkt6o2":451492653205,"ocxsEu45ni7X":680753175607,"6uiGokjnFgA3":833801194188,"ozIVbQlOYOGE":738278015586,"4fbhIpv7sjGo":817979915869,"uM8Q4GfErYy4":698253972975,"Puf7Zm7Kyt8s":20644277219,"uHsnDaYgTuYB":74891426783,"boG1vZ0jNuoR":406877836189,"3L6YFlOYhutZ":778060694702,"mTV93GYNx8aO":736968649781,"iKCIiiLYBN8m":54875047164,"z0CNJBAiOpVr":791060310763,"mzxuucrItCTq":714736804832,"hiTYDVOdGRmP":679281060467,"fw0BBMvH2gnD":197738316728,"r1AcTTVyFqep":646675635165,"Z5UvarHShIdZ":687956976013,"CHDXxignbWqE":960887858923,"7OGRWJ2BPl8o":959728566458,"qT2tJWUkUshg":130912854564,"LH2XIvlgzSXX":843701679803,"71WsInMauDDX":674445754069,"ikGzAU6UELsM":732060714930,"pERNAT0MmcDT":151622182623,"hs61o21s70Si":142193521444,"XSqGZO2sitZl":404597087191,"t59JyT71bdal":213202084224,"oSBJfeCxZwgq":732146511799,"kssFFNbXTC7i":980064628650,"wDstYgux43cB":240564242326,"qKWoOiJgzXea":538916337784,"U2r6WiRdeWSi":258852947185,"Y0haFnGN0h9K":730281639700,"vPvoFwwWpyB2":568551565539,"GC3fAUz2caTW":667985524453,"usxDCNjfBpzR":845367683783,"W2I5726XsRg0":482800346563,"XWOjHzOfa6pc":117511972273,"aHUqPZaCE00c":752736205667,"C62uPdAmIV2i":403037279778,"klrg0mVmB0BU":237589573947,"rM06ARqePgiN":432891432548,"P1Ogn7RmvVEO":303442493908,"qfRjhUSAx6Rk":990499271706,"2aOgyy9Pl3G1":127156625042,"ufCyHSmHs0TR":866183952095,"bsYmloF0gfZu":775458263760,"itbppLLwbChc":549721666261,"9YAkRWsX2I4j":403294911324,"PDUWpjHGkWb0":57520303236,"VKVKcZ92QMlj":546527510051,"SaDlBGxS8uQ4":464829997376,"L2g9BXhRXEuT":480989923717,"TkHTefgK6s1w":54882911275,"xfRWS1r7ek4d":211024901224,"NUZnd4BsLMAt":407842155710,"W7QIEi3hIZOM":714179583239,"aJJWP0V4aBtB":606450109315,"1lKwdyqD99JF":954744002687,"wFV0uVltwdks":881983499174,"cWxGsJCv4oDL":380522358636,"kiGb6e4lIDtb":278041328335,"nvq5TUNLNLyc":942284801043,"GNt29uue62hV":38332486400,"5Lni9Qd7qcoP":269384038444,"J1oOH7g5bwtB":998270845408,"ybriADgxRxlN":820504812619,"Nk1BdHcWZFz1":586883039606,"T20mHMtvKWZb":291957787676,"E7odNGmCXCDr":129337194326,"Koe9gRHERwm0":3345061026,"6XWlCSdHsUdJ":123383303823,"JyHZl5voSgvS":593955423189,"wtV8LL8xpjUz":684887919512,"Yo6bYrz6adni":58547958705,"g7BvnC7NSCR5":425389952311,"9u41QN7Pbhoi":142044091342,"4M1PKIaLxJYU":740400389953,"pMM7wos6nqpv":597346977974,"4CpcLGLZdbpe":104693736152,"pZteOm2bMFFy":836248678615,"mse7ByHzjpST":880415422712,"O7uMCV8LIybe":900469556557,"AgW5AaQGjdtS":594486768786,"b6kyH3i6Lgx8":562835785897,"uOZ5Xpv73tk1":186883715663,"jDrWLjtgDiH1":753920861328,"WIeV9KCPXdmK":297455545518,"lwL8GOpjnpVS":454648713798,"GiAAwQNgNk8W":748831109845,"VQmOC4Ie9ehF":977288380311,"kvw7vwklkC0w":686405963942,"7rP25ixX9mAl":255059802076,"3d3TDjfWd5va":739394268703,"DRtBXyspceG9":995285446131,"Fae8WrRncvKA":412241529199,"9Rd0ZZEtuj59":458732050826,"BukLJsLDgNdw":575268803583,"xDomiWMb48EP":547698072983,"dVYrtt0AEovR":596815745603,"4jnT0k4EDKR2":387046843757,"u3rm0XMOmkzv":972388085301,"sgumTtXyi4yl":370160991854,"2yPV5gf1eBYx":237410581081,"AtDNeh3Vw3UN":188472057919,"bUaE8TT2xX2Z":366050235924,"fiYx0Qa2923X":79491985557,"5QiKNEXWIxai":799905290683,"g1g1JvwXqPyO":847256041042,"mkFfHUzyMZJ8":883483685769,"g7IeHtwhsZPI":504261665037,"3SaVTE4RB62L":980658907393,"DNPlGqLBWt6I":751024874178,"577mLhDpz1RY":858473295755,"CbvzvLXXy3ws":216868474734,"eydo0q0IMXBR":69241191174,"uNIhdOt65Hw8":431530846456,"DOMjBiENphrM":330138100880,"PC7TGb0URVc1":120610230746,"Q90zvoh5WIBO":318315945827,"Z0nN5yS46EGt":100221915245,"CaT4FPbwhWkC":14472605068,"ICNqfmXeeRIw":809114763429,"Y5KIRT0oidBw":143059060711,"ZQUjsUCRTQMn":330030226264,"X9aHJy6ve4Nb":734321769858,"wQZLuN273AAq":82901935826,"f5sLBBU2nJYr":925708797771,"uiU5JwnrV3nV":142878791091,"Fp1nW75oC0Os":618094162497,"NIW0uYlHEBWm":117626020290,"n20XhhrA19ya":169069150998,"IvzoLIqWRhmv":732450127048,"1otZ7DBdXY79":160457809072,"vcSiRiOznumW":272520656518,"8SZPRRTp5xeh":285121589567,"OEKnqPMeOCfr":294436709422,"RvLoFWtbJio5":253973687801,"rGoTq6xnEU9G":2862699115,"6k4hELu9vkNV":687445128282,"jmABjcjunbHj":137246716320,"LEdKzRVw5ntK":655630582019,"fEyGUL9QL3lG":839231624270,"2ZNig3T7MVuV":528047908582,"hgdDugRRXWfb":991059588546,"p7avqPkoBykx":382348318733,"tQIEyksNoxpN":458351188110,"uMDWo3SsmPfo":962700896967,"k93o9Npf5LVA":547695185822,"FX548JZoROLf":778408736009,"SmYbf2psRmmm":446953927351,"FrMvqgeT7g8C":837743711910,"qCwtk5ruVM5p":196564623916,"geJL23Bj7U4G":68062814571,"jlPctzTkx9e4":260551725494,"yEYUhxgZF9tQ":839882108573,"NTRD8ToNo578":307569407482,"XDfMYVeE63Ja":649455205087,"sW2E3tnTCA1r":113635472922,"do5Vs40uSnnL":94826877392,"T2sfqLTUKBtb":393924592461,"od2PHfkXMoro":41402158214,"xcmx98Bx704M":643059386067,"U9JGMhe12OFi":683199840263,"aFTwav7mMX3V":351105602661,"ZBAGGqEiQA4Z":778152344093,"0VnYP6ZX2IYN":169144246031,"oQ7o2onfIOiQ":149516290081,"sBBeaa7r9p2d":99270256173,"63p9ETOdmO8N":916626741299,"QLUrGnQPZaH8":589729018269,"Lwafotyq0zF5":280448043616,"Dq8v7l7U5vUu":828860843510,"NYhNfTBw7esU":64929183302,"805jxNZgSp8U":469999103724,"mVFmdaYJwDsm":99986297461,"Qr41hYGDc4dS":790682829416,"R7SK5o9C4SK7":347732669946,"VWzdcUqXSkpZ":108179876680,"Ike1dsE8I1s9":618732329999,"OliNMT30Bmo2":335047716351,"YPlaPFYLMnx2":224451363795,"P6mEMt5I32ue":248282612850,"sdoB8A0sIM8D":237203165788,"c1syFrzRW7AM":110225659312,"EAUchTlvxCp9":645775044551,"ckEnTiMe83Zd":413570282054,"JxO8Ph7sALfu":186134032910,"7ukor0Daa8Bb":433553353185,"mIQgYDajtO06":580014578214,"quvFnAVfVc5I":652935535573,"ujL0EYb1AZtx":281438900672,"4koqsk1IIeVa":654517286418,"i0W9XzdxEPWo":150363201671,"qzKOcgU0ucVu":719498361286,"yp9vdbLtpxg3":735285910307,"QdQBJxAROkQ6":399604486904,"zLBJRMPgeu12":178934219672,"2gFgU9SW7mXO":692926859720,"FY9BUbJPst4a":364099766893,"6vFUFdu0zP2z":516135797247,"K52oRBVWqBeI":911407619693,"1Ey6BKjKPLb1":480862273612,"QTl9pnBtrtoB":610494810369,"kdLwnNSPkpFP":953869667105,"lLPDzaWQ8Uqd":564536582032,"rPMHqsso1DOb":905897584443,"5LU6vTinJjrG":723886145180,"AZpvCeHrugyL":261924844893,"6gGeD4Xq370q":978798765368,"THeZLER4H6t3":695951953069,"XWdYYHogsfli":226336040874,"AnBHN4eTTmKQ":947123838749,"m02RzrOShsOw":627537772097,"8yEPz0znGGrv":639225240062,"86TYi4IZp1Yo":1344458033,"zy8owBaLYNDY":743234349354,"3MdvOutPx1g5":269803714088,"YIljCbp6KvbQ":6738787798,"8Apu93AbCg0a":881593815350,"v3KOlhdDWE0H":198179183088,"raPpSnq0fFXA":60152957653,"Cxt8c4pymdER":656415584989,"lLwMpgndxGvb":606227828351,"0Zrlyf4KkfPt":808674032549,"PFemN3zvb1PX":871584328111,"VIR6TZ5cJR2q":43951722472,"cCtlsaVDj1zt":487680981808,"q6QTjcRdh9WF":282640602011,"HyUnMoEo1na3":207622870270,"zZ8vGFW2xQTs":109434181764,"gulAW4fjU7ba":126616247105,"U4GonqSJs0zQ":225965839844,"sfBPDgEdUTSk":366118992314,"NvC7c6UWso9V":477050961211,"dYLTPwTZogDB":167325686374,"sLBQdbgnjoRF":302414805119,"oeq0Ey8BHc86":457473637635,"JwpgEQg5Oqvv":96096789272,"CTsFELfLJBFt":191965665770,"qEVQXgzgABlv":453836360962,"rrZiketcaII7":751342335001,"FGcfzOriaDaB":698749237617,"thpDWKpVD6JY":81801767644,"M1jE8U7FffwF":458496931731,"KjHBlxUTuGtX":764417201015,"szIzDr14iLFx":216313200318,"srU8Lb7aeHkv":597225315991,"brlysY2ZgO1M":989679656555,"CdJ8ejh6ZOEF":405077868516,"zw45DNpq8yoP":429568744885,"5Bn57lIE9ysC":815371132322,"2dU0RkoFtEGr":549813332787,"8VzSClVmBIR2":30159609234,"kkx2eVKyi1kW":136456743531,"hR4uA2fP9Ff8":363485542489,"o6jLK396su4T":781359204352,"jF1dbiZYsE2q":515750662334,"1y5aEz0Hm2kb":330529944884,"5B3ZsyijoyvE":71217596908,"EDQtoK5ysTeT":188675632148,"IGXm7wCCXbne":334662150193,"WMDyceHpbNh1":935179473006,"Eqe79KOq5oAN":103059330804,"WNyL5H8Qfgi5":193863051141,"lDKnIRY5F9iJ":377052757924,"RwszJcuxnIub":237742343279,"SSXLAtokfLnw":183627794754,"ax1Bzn4minl7":270829673250,"jdOr1uAeksEw":569355604075,"S35uxgakRRFe":384178621938,"U08btF7Ubilm":11967459578,"mwNCPhMJSgWL":75347314951,"gx3DSzl2ORMe":918236313981,"vDvw0OBKC6XF":763450400376,"Bv6F2t9lyi86":391257032292,"5X2fuIDEzeIb":436973812754,"YPXzG1mqW62r":41570404195,"CSJ2nOGo58yw":83828319366,"8Iqyg1tV36b8":884289369894,"Lh3HundF7YzQ":946515598012,"WgB35jjivifj":689470735703,"KuiKHlJASQ9p":58654966140,"sk63xl0Ifew5":40989966708,"Uhk4kADJzhvk":205820738934,"NhrEaizOxhIp":648889202841,"olWnW4Z8vBbM":257241252920,"SBAxzadzgo1C":121760993027,"DdPIws0vaMJl":178181841730,"EiiufKv9wqCb":67339692635,"tuFYT9EyeTKi":103427418173,"KZfDTSNKOdJY":694965642367,"z75LkmBiVqW5":789646872914,"WjGrRs1uPROk":723307258521,"GAhlExxchRtZ":409908121352,"9RfbEqOcIRNl":781122435376,"GcfzXDWvPzRl":41924237152,"pcpNTsf0GRc8":960074737588,"5EWAciV2zwlj":291767837002,"veOMoaj38lvq":191075009802,"9CobcUF90YCs":928725941287,"2XH5cESfimXt":590235574371,"hjrzXrRRKGAh":770056376909,"SJT0SQoW1hPp":35646677210,"EEliqb9nUxDj":548132023906,"Od1PM1Tnlqzq":222492962927,"4acSqgB8mlPG":452289187793,"fiZNdAgiASTd":616142800915,"t2ba3Hoh90hD":491887091267,"KcJuZ7hFYlYd":34504964380,"8cXumSVTExPy":477468166258,"ow8ZbiSkX09r":215047720931,"ws3RSocvsCRF":939348691950,"Zc5hDNaRikIz":929945641842,"Qr7ncdaOte27":393250558764,"Zcfg6aM8ImtO":950860400495,"49OM2GqVNOil":433294953679,"QzOsry0IkxiY":871954695466,"ast3v6LjMzlR":568254642349,"fbkFySsEgx0G":588919172105,"FiBBEs6mO7BW":704956220513,"oDVw0bi18VFw":301813726206,"tFnlz1yjo0m2":39535177287,"SaTRkZTHYiVP":424867528619,"fqitjHWxDsmH":903177274372,"szHAhp3XblUK":287877255994,"mP6cV1kVu9dV":351388968749,"eUE36RXtaWyO":436633897724,"fvRfe7O5CycQ":930977533496,"C14l9ne4Rg5v":520236741857,"JvG8VjgHacNW":444158313023,"ZCISgYuv3ckm":973082160294,"F8YAje0g5hUh":329538315137,"fTJ6iR6BIcXf":273221529540,"SZW9C3iHx9ro":414361172340,"3D5A2EEDPSb2":275096076251,"mzUhvV2idZZV":375770832478,"mmb6EakrwDsv":259431308415,"me2EQeLS0Q0l":668457057411,"sasx0nn48goU":377367141360,"vrVv3fFUXQRN":211127966307,"biuRYN6MOyhY":428965984304,"p55ZaZDBW2Ny":462760426468,"zym8CTiEamEP":470522922495,"4oIjahJ7l8OM":450925680547,"ZsbYTnszZaW0":673847606404,"MUd6S7a9fsrN":417763530739,"PgLLwyO9XjrP":103309163271,"qrwS4XbaKI64":969950121112,"hOhyJnYi3XcH":673646706723,"OC4VVV74VF9c":244137300699,"ufdzS5lcMoZz":717140468095,"YIi8CQ6r5vEZ":687978774130,"UYor5SBNy5o9":835416602622,"cSR4qtuj6mnC":22417059140,"nllV9euswSF6":232729051881,"kqUPqHJiUrFw":100969757994,"OnwhxDLp3fca":116516694618,"8ug8SlsSzmWm":176283499433,"DeYNmahMVtAE":493619023519,"LTctAF5NIjUP":924172403031,"lkRpYyE2ISTp":579702111431,"13C8WIRYBVU3":402015666703,"DOd1GSrFWWNZ":636868481171,"mdckaC8s4rRY":365315646113,"3stXQzqjVPbk":728527256631,"jabfy659IJXk":444963484642,"8l3807GdJwwu":599095545168,"WsJGMfToECTO":88868264284,"14zSdB3mvezt":540990377622,"heYzl1QP9UJ7":550671028252,"83RJ9XwwwrOR":666276806310,"CCUVDVM5q9Nu":760886953608,"kW5T3fTZAkjR":626585913214,"se3M8WWjbpgP":305451623633,"cfXOkhp9JN6b":82809897195,"nfWv9P7wPfzM":270344837683,"9Hr3ODSOQeSH":936069961437,"ReVyNlljMR3x":278597097937,"cOPwxLIap1yg":523349487526,"Ae2te6s27zQQ":341463848748,"7qvBJJwxCWXY":600359565617,"eZsPZzwpAVWS":123783219215,"bJCqxqdYhPSK":76878726057,"06LpQhzplccG":176365816830,"yrxtp4QQqSg6":480727241527,"bn9vcLAjIBQ6":51179996735,"kBpoHoeJQL5T":906409086890,"ARH163kbfrMv":845157157643,"4eaS2RRIBYP5":735268853833,"9ImCRMHE2Vyh":956408126398,"zVE05XyK2DiS":774714764982,"Ncq7QtpEpHvM":754705378645,"hqr6Ksp2ywUh":709358959513,"3BV52i3w1qvE":735907924622,"jgQcB04ZqgAJ":43683478101,"cx8evHxR6QWS":985811081551,"N8hhrkOWHMqy":844763902212,"DoBrTWm2LXyq":612931445938,"H1yZCOofQ7Nk":456892710098,"JxC5xxIALcwp":15601109141,"8JQstpUvjLXC":976893938468,"OOOZEsBwlZ46":880038848193,"q1mCwWN68RUY":741324035224,"3JLHLvV6HvCI":132259292308,"a2OqVjSAfqT6":834703368616,"N32V94ukXAv2":908089728589,"msdfchMXMztW":498348701846,"PZ5vlqDJEIP9":189485389954,"gMMNmecAYebd":575479415638,"8WZkDKXSs8zO":341497909129,"Ij2a6GEUpw7w":355845391731,"KOdfwB5SX7IV":273085674518,"Gpgw1ESXIpSh":497798925450,"OdBidYIZaWkc":399588849179,"itMyCHBinb94":340243350511,"bXr345WQb7Pv":655756598821,"dopSS6OMQdAX":50057234750,"YvZtm19xiqsr":219001127839,"WgbmUSqEmNnf":858919403403,"ZH8KM1utULtD":559591639211,"HkH1zJQidiQo":352532964029,"eiB83TV9rk5C":820795403283,"w8OqT8VUHrTO":377516787473,"z29kllavdVlu":708172536887,"sN3XOSxEvPAG":835962337338,"1jrjKHZKwNkn":419982514990,"E3qelUUgQl3B":424401233709,"mVmvXv3mGPa7":521910498606,"tb2VMQf07iG8":348854586798,"bd72UzVWFNPK":588634755134,"52BIMexhCgmc":875737248037,"mX4SuCVGyBp2":374080955103,"fqNM6yBApEBL":127990570617,"EHMMmxG3C7FN":451512911859,"o4eTPTWXf2Wj":917903967014,"MlSXYwAssQiY":536112411907,"OjhEc2RLR207":709864577054,"iRJqCMFT1XNY":624237293670,"gkKbBYPaF96G":905201730992,"v8LP93xIQuYy":927796132558,"fFOzdX5JhPKP":394839593715,"JS5pbCO1SUxg":878828575670,"MOMnAqaH5SVl":770086361187,"SmGiFCnhHfQv":503309902010,"2Y0OfsoghsbY":935150575530,"AHVwEYP2sfIq":187726130458,"5vjSBoW4C2d6":44817730523,"KRmHUrb6LKdW":93318084417,"43rgKMMlIjPe":583071924298,"3l6JJ2vwKLp1":416946149163,"5KBZDmJRUpNk":31119440483,"rGnSD4QX2Shf":133281532172,"jujUwxfe2SQ2":19593295392,"rtC9ikbvs2nX":530423467799,"Fa1QOvVgCxrt":622861985799,"2GdHBOsVOnit":225882412639,"tFjxoFdb7g07":388111997169,"hYxfDrudPgE5":855723146864,"waRSc1kyAFdU":56907283506,"5v1SaMUZ2fnA":727278441714,"kVAqjw9ZpMi3":844905003873,"uxuXib2wfpcM":23835778598,"llCu1aSlna6W":563264967248,"Mod3IOQexi73":157834463146,"g19jG04iaqSg":242816595664,"2ksCJxVHGhpz":31302371289,"6oH5rTaIf5mV":89010454935,"7s5WqBoD7Zpk":816186487774,"jWx0eOoGRviW":60152094721,"PrOmHaPAoFvx":360675763505,"iHMW5ZjqLp4u":368092233784,"55jMR6lNa5XE":777569415280,"KRRXM9GjxNbm":597101152500,"aPbVBeHp7AKz":780508719021,"DYpwEj4ePWLK":731568815793,"eaWyE7d9PPpR":433751564952,"XGJ6ZAPDlUnH":650155341491,"6dVQHDGAiig3":127991308905,"efEHSTyZveFE":411174247452,"T3DFsh1U5KE3":430656477112,"yy7SPgYDN5Df":77203570189,"JGgBZL6y1t6N":909427077756,"avNnVnA7VdUe":690199045259,"o6gXpHJI6XJX":584134695897,"SksKemdp2vHh":424737955188,"YNaC2s0OnLO2":503157804816,"HlPS6s3yKuuy":317936519326,"0m8ckjxDCERS":599565459001,"G6sWBH6gITBb":705419289810,"liH4euUUGaNG":440707038290,"nScaZOXqNkCf":786593509554,"IOA01j8iIP80":173163655374,"7buSzr10jdRp":639099056245,"lo67yIuTsOEq":130076248577,"S7PoDHj9dC4a":949824364674,"5574NuOMMWNx":16151495316,"A2vHJ6cfNLVE":309257009869,"v49EK2o44eez":830185200253,"2ah7D2xS4R4J":101971328515,"qH6UfUoON4Gx":194115521270,"ked9xSuTa6wU":138764693521,"Ab9XARi8jg39":211726001118,"PEntApvd5IGr":623388967117,"MjgxrLg6uY3m":35569223719,"C209rJ0Pcp2P":865180085124,"Vj85Pr5mTh8V":559917944366,"zvQTLBoEMuhu":989347920663,"lRYiXyeByVtD":430340866053,"vZSVXz4Fizip":277476480738,"Fy6tlI3ximoD":920905672940,"aybTo3kiEAxY":720409213991,"00OFeisE4QIU":115222028663,"bI16wsIVKSUD":659117735012,"oCbtK22nMQ2L":674965790750,"wnidAeIVBe4N":514071519597,"u6DBRiUfBE8v":250573597236,"Ls9mhE5iGube":654160818543,"R0N5gZE3sAy0":66949709912,"0ibLjaO5ZuN0":277522810424,"1BYDbn2juUrc":298846413703,"AyNfaQM6Ao9r":745253392308,"JpMH90Vt5CSw":340931096005,"cwbDOP1liQyy":84165987028,"OKwr2j305F1i":558481710866,"cHwDoBOzNrY8":472133327540,"pyah591Rj91p":450470129408,"nGry338T5LgB":150036380102,"LWOi8Kr43bhj":175701599254,"c0uHBYu9gL8v":931323908325,"w4HnwYzbl5cL":540239026548,"Ir4Oz13eU9W0":264235975746,"KuBU15PDWmsc":979334508794,"RB2GsZ71BiNH":762789743905,"hJJZntALYFEn":772479399541,"96UXfegG357D":902353684337,"Lb7F1VNHfzsu":383700065756,"Zv2FKa3t45Ho":247153426821,"mW8z3FEEhHLB":762731623320,"v1GBAwRWJUBR":861809183716,"zTr247jl6zLG":781051856028,"XWUrbRn68uLr":296689591480,"S9LrLDUpvXoO":927726382067,"lLJ0xXoqE0oj":693617784158,"YyJnyO1SbItt":319219479087,"YHvPoRAbHo1B":268543641108,"3JTeqFziscsF":526013294884,"Ug4lkFPNWEH2":840558642573,"5R2IMfJuPaTZ":972487384368,"JfKwK3CA37ak":317942886085,"fiBTVA5OecTx":508174356581,"4QTYY7aaWKM5":436442155408,"bYuM9QhxyB7Y":824606583024,"hPOvMyHspGpN":322777917384,"z9mZkCEzpFBv":231408026770,"eJ7uv2VpvQuN":376219482447,"nSOK2T2baXXx":766734647884,"XajsOVERQY85":952406797603,"afpXOXY1TLwn":659867219032,"qTizy2URl2h4":90987397159,"gES25J7bUV6w":473111184274,"9z7J93oX08QQ":975127158962,"bmytvZ6ad8Jq":645241321269,"PtPlx8QFDaUk":87525479076,"TO7I5Ygeg936":18819194417,"3wA2u6lXdUUe":319768846610,"QL1nlsWQZ0o9":437227116796,"D7GSNmB4tZI1":72181097606,"6Y1sBcTr6SiO":229488615687,"YfELR663e3B6":115440336814,"fZNsgtbjv4gi":75494590525,"HhkWULTdqfyo":28851549538,"Bths3tztN1AH":65041331164,"ZczzglYF6ikq":447116450475,"C4sXFd7MhrT4":691732310961,"lzMUgtuVButW":32976808158,"KIYMIJPzSl9E":771928003902,"K5tuMHi4LNLK":537629643414,"J9ufvBoEgCQn":718304046204,"9Hd3UUNAPjfV":499386067546,"mpVJoCl5f6af":551364122130,"H3uREeZQ9inO":475834363543,"KWUjpXFkfbA1":317985598249,"nFia1Hf9HauM":382954401424,"DEAcYuYIgPw3":923486298385,"bo3wiL7yEb3m":890986712584,"4Sbf0kB5AKJk":49403635913,"auzURHYKEagP":449373495009,"k2E1zAfUlfSz":253661489331,"yyzCJtdsrqvF":625384865457,"ZvzoLoI73YbU":375320963215,"8ce7hLsf6a5B":36791497609,"HoK7lMsty0vG":245207604763,"1Uo5INwAhEg9":365045475464,"XUOl4duSBbcZ":998506561771,"nJd6PxSeIh6W":988823665368,"EV7YPVMoC4XJ":57028463083,"zS5J4k2YeBWx":320228538053,"TwSK2R7ukf4Y":942607488253,"JGuGfn0mH8pR":119545451309,"EdcD67jvUB0h":879696687591,"H4INXpvEV0jT":205557416690,"MI3rvrQLgkDh":451590190875,"IjZoCcePmy8l":84287694439,"OnqvuZ779oTz":738783941435,"Bb8rZjtFcHNT":593317644639,"41AwJV1sRJmQ":962533273702,"ggfcltoUrHZL":556433438371,"HLASObKmwQvv":71252059703,"C5DzQEVE8gFw":932551862058,"IJUBoPkpnKWc":115076050620,"srk3LcrYgLxS":717544749106,"dCiBWEGCseFG":815592579112,"RdZaAAsCFsm0":573577644140,"aj7u6bH1kObV":565155902511,"qQFHuQy4OyAk":580642717398,"sVYL5qxh6qmb":758917390018,"nRrhJZGiiBOC":769350760220,"end1jO6DGITp":712497853162,"0Xo4qpBEzWS2":985039286871,"QzAB6oyvRiV1":991510845979,"qPcceD8lpOC5":491127218960,"ZR8H6nTDcpGk":176534792690,"GglOocxmhReH":950857446479,"K4OHnstJDn14":757974631004,"pcOePLrFGUcA":264330469996,"P0Qhs2pQfz5v":983955424143,"zD32HPgMtsIH":839712684741,"7HjIrve4sSAa":667909933782,"ipnecRlb8qFB":953776506457,"NBAog7xAFfQJ":373435734828,"J4kQ3TDflTxe":829517443841,"oE7iHFMHwF9n":871100585291,"RLqbwOKmac9X":78573698344,"4YW0d7xprDR2":670874162837,"qxlQzubjrguF":348411793732,"rkNCjUBslpEP":596144247592,"D7WMFVb6I5AJ":694438158170,"7eH0wfvn7H01":898012706463,"2mp6B0kYzNc2":225816666489,"33TNbfd8KgsA":655322734808,"AS48wnfZ6DQJ":663036631662,"07KZ8knLNpbo":191653404233,"WCdYAC6aeqJk":519105645200,"u56jxpMRkjCL":751227906218,"dIaqbzrCgCra":922841460761,"EHtZ3RTq6wI5":49541151300,"PUlxlkJjXCuW":243270287871,"Ra2nYcFH9k3I":133636339995,"zgywuONE0LXk":800568427762,"tqS3PToi21am":411091857163,"gGUhD167IJBc":49210169931,"PESN2014c16W":752827814414,"1erNggRzKqTw":814663245734,"4LAyFmG0d4MK":224386461762,"LIOqlxwOg5F0":510327135627,"xfbqlkGzty4b":567603415659,"k75Vhll649qn":723185345859,"TPVOJqT4LLJZ":833477131086,"0hc7i2RPqQF0":445822686501,"RtospAawM4Rq":521450195836,"hKOsiBVxOkSn":258289714117,"N29csbQZP1Tu":963018365713,"9GX48oOEJXD3":384777394208,"xSc73YbIXM1U":31469616216,"D754uO4SjcAy":591442375694,"6464YlO9L38m":788269423179,"cbGglSBcFVJ2":759675460801,"o3N3jR7RtjIL":167205067727,"UDJzNLjT6EQM":360894520412,"Ic4Xnncveft9":806605361845,"NEz9Js6o8t49":955771282764,"0mmy0fJHWhws":719000538357,"ZRpCisI4HoAy":153491146303,"npsElObhnNS2":733050757889,"0KpxpR7QGKOp":736749898967,"XPuNJxNbaW8B":148584265374,"AuD3J7AB6N72":803526948508,"PE5gcYFKLZt3":587642797631,"NdGgTxO5Zpq2":946430513662,"JmVIyUwkHEF7":789730033215,"KTwH1f0W366D":550865148875,"RdkqYxUOwU8t":440325422526,"TMbktKwtxiyL":807254500578,"P9k9y9sHbHka":10052778401,"ob8VZa1Jttjl":696130134838,"grX34eTZwQBC":298412864708,"CnQWnfu92u3o":261326399842,"RczgpGYePXHw":669305807895,"77nViUotO3Mz":610404294318,"jGGiSVay932K":110394484953,"2My7frjRr9Lc":418496518558,"64CV6dTscidL":921433737080,"D2B6QFiENmUs":359013267782,"Y2x3OBF5LGck":456548352809,"FFjskbnzcxAh":116064769820,"OUyXYA8dA3Rz":506547809520,"wNwsOsjAsdzI":539167007313,"mo1sd2EwuY4E":797373356263,"hOmBZj4AOwiM":551893152177,"ZVRft9chsSGO":585488378053,"ycdpg8ROJ4Yx":72788311334,"yKlS1JWQtPBt":614793952084,"lDKFVh5Nqxsa":79125464715,"tmz1rMBZjxp1":184946086424,"KdJBSbzzqXqa":233965226336,"dcvN1bqW0pJt":989390727709,"GJnW9woBaGIx":869720215906,"26ockdZsyLvM":260320926685,"Ra8CdkjxF4Gp":545019879674,"fuNV2ibxjUy9":379670472248,"yr1pxssxEqkd":254318677860,"HxvCpm3pfwGi":494207785748,"o2CtHQLGAIyK":493047785628,"9ZONt1aCbGi4":708916253615,"UghuTRoyC8WG":302412171915,"FsUyQSOjiteC":962602719761,"WXZcQvdzmyot":601757079567,"a7PaMAvzVuSI":848896074832,"RhwdVePW5XKp":168234913165,"MTE11eonEcFt":453800870424,"bCSza0aXGPQj":257532786583,"YhFFVNQmL19V":802859183068,"EOtWNYp3sa7I":644699290270,"za57tVLir2SP":265312138070,"VxCyiXkUu7gW":809374682183,"QeEADCWUcEdZ":759282603374,"WO4qQjF8mFou":393508804096,"QnKigfhkfgiZ":640270667360,"RoCIrDH6UbD0":329183714661,"er9nWIm3n4re":702081150553,"g0dNGfEDlJAP":496192584573,"6ca6AjEj7OHN":911463506555,"SusiG91VUS6q":286610976400,"RQHCWUlQvG0p":351183881241,"L4hyas95eoq1":378309129621,"qEOsuG6oj5tc":215898345232,"yp7M6i4rYur2":471597228602,"lj6Hb2Aijep2":901369460547,"zC4b9yjeCkLo":277831676772,"c9nWzAPHn3yw":651683680272,"6xlsgd9VQpm6":165492654556,"x8P9UN7Bc68T":821059178669,"gCDt3R5w3wjj":949468325656,"fP7x6awJqu4t":426982168065,"OVg5912OQTjl":962139579446,"OQzYafkEgiJ4":883841327175,"B61aNFvu9sVL":771867750439,"MbtcxLHMhd60":285753999227,"wF8k9PExVSPf":437868687932,"prXOimzWOk5M":577643902887,"4WcPOoh1GYEN":473372502425,"HuhXkewtvBaE":990723357782,"FlSiC4rINIZy":388364081022,"NIAHA3CjxJTN":900220376076,"eU2TGv7QIH8e":636187221676,"vA0bMsnG2539":972243097963,"Tg7HPnA33tos":166865712178,"u15CEQqXW1VD":762450529184,"I5tCUSYmCrJf":32197153037,"W5uduLUlDg2R":881416058856,"OjdicbBMYjt8":372664841626,"lPYL1tHASyTJ":448248973939,"QMva0LdYDnmK":555739096768,"yi3BKbedKpYC":967228274302,"mIvv6xmlRTtc":565762319970,"fP24kPs3H5Tg":739220845960,"Zedrn6oXx7Z5":745646816821,"u9HmeKi8gBeQ":74617343111,"ywgBGPNSZAn9":600849469515,"JXpFW4gRtrhB":111707474748,"gXUovZuqQZ2o":300351221916,"Y4sngzCScvo7":111969097614,"7u23iNIctjwV":961146387456,"wVHPyP3Yj8GX":904904573298,"dmic8D0OZk8I":867430266363,"jKtUbWLr6JrP":81526025138,"Oz8hzuU97uN5":791593940046,"UVpe81cFmdC2":582459325668,"RhQRoq9yy0gr":969223688826,"7gRWi9K1kuXm":976977549385,"8HUpTvgNmgyJ":590157677415,"EmbZkobVecxG":747354748005,"UAEN9A3YTVuU":183722329821,"empjZNltgqCj":225466730264,"MayLRkxLIzbT":540707393147,"BnuMUPR2DuWn":406245096424,"TcUJPvrGifFF":285543435411,"COHcoly3R8lT":463520425154,"y7lXHclCImQ8":130147285021,"gmkfBYgULe7i":505694255103,"PdfiK9HJQdWV":55994296803,"CWa9qEMA0O6m":813756895976,"JdVnlAZr5O0I":506608601834,"PzMcJWqAvbVx":308548115357,"tgERcR0Dr6ey":503535687221,"hHNU2dsfujsi":897355168135,"M5igLfDqUdGR":742240451785,"aslQAdGqtgZ2":937239319902,"HlMhFvHEjewW":947749113339,"c0zwWYdJw2ti":193290948739,"kMGb0XztmWlH":451488103962,"4XyApdbBiifI":434704713227,"4aWytamzzM4N":932331695957,"E3wAzVdMn3NV":170212970892,"49p3gd5ISIy8":778239723485,"lceC6UX0VJoz":706990314730,"gRflNJR3SoOq":54861599147,"bhyGXp7NBrm4":973247389878,"3w7bMAIQrxDk":908812970704,"S4zXJb1jZvhu":605078410662,"GpbEH9aL7NNc":118234778667,"pV3qEyr4XYMr":798417048548,"eVuYITAR9BW8":832571754345,"LsAn7QCtYXLy":705572070712,"H7RtpzsPvQCa":471392078260,"NBVhOsGrvAR6":743669626959,"Fic9iy8a4Y8S":16515516792,"nyx5yTX2Mwex":842495750126,"wwXGg4TCgPvm":365266452981,"F36f4UK5ZFqb":835968520988,"asy2sXpIYdod":373786723017,"6H2wDVrGfwRQ":117677392525,"MZyweqF3U40L":550218215521,"tGs74TadmBSn":168412049446,"puJPv4RFw00y":664006769531,"Vehsq0XWsBoK":584146297940,"AdWCcxOkwI3v":170972289206,"1HDpmRT0iuDX":134492804502,"VE2wKd7sumTi":275319783149,"TuCPNotHTaev":485275319252,"ViyYfP1LbDq7":660257675993,"iwQO8osoxffj":284656742920,"BSkxAWYsUH92":208695133047,"wLJR2TqQbvnZ":939696584334,"j0hWYZHUn5YD":427881574718,"LJBeToRko7A1":414051060936,"vt6WkKGXx4sR":236697505276,"iSNmF15AdFVs":977606007735,"h6dUiVOPhq1m":605518581152,"BrWE0krCbry1":439638484375,"hwMoCRh41KLq":34886990971,"SSzpDDEaSxNO":683029254230,"c30oKkOhdnzh":739501065565,"3uPeW12PQvTZ":110871650048,"knaCy9dsIVnN":890921289779,"5ydpbnJZXHuw":640611281155,"1KIK2QWDFIke":828785462476,"sx4AJLSJHfJ8":203319966683,"aILj6BzfNNqd":571138321171,"7pdvGsx4hNFg":200289999092,"3ZsTCQVgeaJN":523807532733,"3OpNWGepPJ4X":120489796805,"ccEGyLhDdDZs":255124367047,"e547XY3Wbf3g":132379163194,"QuYgxUwaPMUY":100583182113,"QC4DBdwniSHy":309788355479,"bidX16R6VoCB":931668457539,"ze6RLpIYtnDq":593793542248,"JOrSzP6zX4Kb":895060076783,"ZgvaKKecTAd6":678935387251,"8CYgmsO0cR7J":541594328557,"Q4j59sQ1yPmH":961786432546,"OXRRyMxXLVNY":699953601098,"K0GyOtneM19k":962606249803,"lmR6BEwT2UOh":611269310408,"n2yaxCYVj4iB":173620739656,"PouEQSkDEyzk":484474856894,"H6IJYGMZVuR7":226939664598,"hObek0uaLrcr":222908246500,"ucgq6rmRf2hV":336845358235,"wzhmPYOA0ny1":353828580085,"dM1IiTHWVyQZ":912681552910,"r7kxAIDgt7dn":219733729762,"UKgRuUIs7giZ":504493118150,"EWlyJai817B4":371886342176,"KxZkBtIukMaA":878483744934,"mWR0DzxO7ZGK":279092794187,"LC5qZNRQd6eN":146397184187,"I8lMsOIwzrpN":387450802065,"QoCMPfPPPQq3":872216855252,"THJacg4yjiws":286601671542,"071ZpaQUCS6g":661001872111,"NCH8TN7QAy9C":582719277217,"lmYh0dpuRhfB":27010738884,"2fZQ6TPgo2oq":323957295263,"kmh4YTzqTbMg":176026747496,"zMl1UL2GfhcH":328329846267,"gi45FlP72kYY":928213448916,"TzyhpfNESuNK":299200030131,"jNcFbD8fbO9L":83206139051,"1dROpTA33Cbu":884345200631,"sRspHqaht2Ly":568004205991,"5JoHq25NkSCc":328889676144,"FveW01MobGwx":220993936623,"nI42b4jcukBK":347710786078,"JZi7hCGkUJkH":899576214438,"qEriEhwodL75":922519365594,"KApPk461cv9u":342514958630,"sjfbtq1kQLWL":537746577400,"YWuT7kNm0Q0S":86376755319,"HcMTIXrSJzc8":777741428599,"rlky6hIETygh":263296394629,"0KTYSinTKZEQ":160379289798,"kw8ER9C8WTQK":422150023544,"TbFqSrnIy36T":418439461515,"6qgehBrFhEtX":619564752736,"GaZcg8ejDLPL":458402893969,"fyW5luJY57ua":894245058060,"RiFmPHUqaKed":411000547786,"IEPW0PZgAstN":72461740230,"cutECI0eNwQO":394016553449,"0B972cLHqCEs":538935615179,"D7AvMtogoRcG":201556917116,"JAEwhW5H7rfz":572755551749,"3lcdqhfr3WV2":724166980724,"HYqeSOAAqKu4":731383167356,"2pwGmAEbCkYk":33321445473,"0mmGMUy9bOMp":814863725201,"8hzK3le1zQI8":555969703674,"fHMMZSFegPRA":738453776493,"yDovJZ4FzFX4":843204417362,"qatDDBLvahXI":288144649802,"DsCtLcYaFWcE":107875473477,"lRTJ0Vf7dB44":826336606204,"wv9r7B1LsHuP":812401242602,"SA3nSLs6ag3d":638057526301,"l8NzJqfOKtpC":405309712094,"KVMTPFVwOtV2":108813764790,"NzWVYs6YAPCw":34447188934,"eU8thh7Z1GJN":525887554815,"yfpb9shAkWNJ":414990430283,"5rJ5cZt2Wsm8":845779217883,"NnLRlNJZdZiY":649412013903,"L20ywPwMRlYe":236319170018,"2YB2uqVWIE7x":971899931942,"F8JNylOvl1KF":200574971014,"cxsQUkzciRyJ":506469358696,"Q1Xj0Mh36y81":339864969962,"uU8qfvguH5eK":382880655489,"CA1id2Nipcuh":90815593253,"lJuae7j5UdXs":373141625967,"BvnmmVr8hXF1":18753076074,"NRJAh6RG4PpW":523151905124,"EzW3PzAbN7gc":675078427591,"RcnVi30ZZarC":285588084420,"p2DPYwDmy7vB":977110201629,"o5yNvZeVn1Bm":449997576897,"gdUaPYZ6agOR":760791578872,"Si4gTfs6JCrD":793626302602,"tgKIXs5NQS5t":376178773318,"LE3Bf4BNnDoA":44708420045,"R2Q9twCUKrRg":933394188602,"c3avfsiKeWB0":495442425818,"tgRPP23ID6bW":256339807433,"UJCR7RiOYTsT":764616356901,"jWM66E292tvU":789557952804,"8i1W1sX5f3iA":257322105938,"bgHbgSciyyIU":969902267147,"hi9rVG4TPLEt":702094029174,"P8mBKKDkEP4a":665910715389,"ZJvBNldaJMCs":53798714044,"z5biOVgM10PQ":917989974301,"LSht4UzrpYVn":604930355251,"oUSKI2fJIkJW":291799798110,"Q6jivX00UkSC":726095317413,"zdhkvCfHxTTr":308314485949,"h8rh2VJpzX4b":304910626565,"m2qW2Zea5NjT":293129553967,"As9ASm3S8bo6":739026207013,"r8n9O1J5uiBu":464130598148,"OQ1lLoL6IhLx":584357776708,"p2PVWwiBmONb":735175316103,"2fx4lWbmQCaV":905841820220,"ScHjy5lNayB7":778753319502,"0QPDZExbnmPx":724693692523,"Gp5bsvXqHKtq":666915853819,"JOTxJybOhpAV":482555763726,"ulmu6xMPPmRk":568948594636,"RJJwv2rEqO8u":432278736038,"y1xsrGYznDPd":566174451825,"MKeKOkVUar54":992343476167,"iknKpE45NANX":395071346860,"XXydVrx5K75b":266668876306,"dngrEhLL7sa3":299720976497,"dkpe72M9ohdJ":61924093536,"ebASAowjx8xx":404369262254,"pwwbKfrfpb4S":791435971065,"3Hk02PwBx1YZ":391071780835,"4ZilFvtfS2hM":231957852701,"YfqYR3NaK20S":757963894523,"GdSBPjH7rPej":955476442535,"4XdKDxHjh36d":239838100063,"ifru3x742ezW":486372579987,"nOzr9sCGpB8P":391111832436,"7702swf0vpC5":28785330532,"xeFg1Fm85qOs":76031800713,"vzFJZgXrV0w0":243850606748,"3QIAgCZtpPxo":454533524582,"DNrcfl6pUHuU":202202138614,"wXwBAQJMR4rs":714320945353,"HBOedEceU2g1":313777549605,"RMb1VDqJc672":117847404776,"jlOjnmIeBYRT":416677145961,"82EysMMBzvas":996855082174,"Hm71b0AvaQXk":676551610427,"d82NjLOPhnK5":440150493403,"QbNdiHZtqz8g":722794800234,"JvXqQ1n7Z5wI":546966602085,"M4IsNnu1OFZE":33718377238,"pxRK7JbvD0Ss":642792003018,"Rxy0QnsSUFGy":872129676881,"IZBDYbuav0gS":410695158249,"SsOrZX6TpYQb":541539715624,"9SksawxuBYDs":649027912571,"Ty3ekIL47ZWn":371394995211,"qUcOo7xmV6bJ":321100987292,"3HZadhbv1rfB":882744266163,"t6NeL2f5YGjP":338400025939,"Dx6Pz3kU245L":219471321809,"pK95Ty3MMHgj":377909666116,"MhQSmqsTIut5":173335918598,"RoOPuvVoDVRa":706925245484,"h1pQksGLRhNR":263267455466,"0Eyma1qPyu5z":978542528001,"MPkqbdLcq04m":34908285960,"XwZbjrVUnKe8":79654034918,"xgJYOJ1BfXK2":554561768082,"RAT7z6HO5WXc":91357974885,"2U5GSkFTgpY6":781311577045,"hKQOHU6pni1S":980276208015,"riSFdvt0YZMc":563203594263,"K8wiBn8D5kWb":505237236141,"fXK3ozj3KBp5":916046876489,"5nUHdBf4pI4K":693071474802,"F6qKgZhXO29Q":609136125896,"mtjktpLwQQHh":315516553686,"XuGPukIbINgQ":509692003141,"BWZ8prxlpUs3":594982587843,"8yzrg7b4htzZ":141788496511,"nM2AuBD8AZ3c":142276292341,"FV14F0ZKg93T":93361706348,"cXiDWkvjzD5S":852179551559,"h3MexaMDE5RZ":564856075968,"P0FKwFFo43tX":926669988994,"dIFpTD4nmdOc":642853388462,"UUrbl4v05Bvi":433965653713,"Yoz1WV7MXORv":778749153869,"8K7shjfsEw1e":17645246244,"3NVoJuEIGMas":702930549189,"5KDiBHUiExSv":511279131427,"1ZGETpgbHujg":326427783867,"hBI2UVaPnHhh":422906786486,"W0VRxcXuDisq":219514258848,"YZtPTuxxNA2g":709133459988,"BSa1FPtHdwgH":169912986431,"6dWK9EWk1fRm":632688585414,"Kxmf2Ylq7k12":857966788280,"uVqOB7GbimL6":498463805889,"qg4t9Ppneogz":187071281921,"dWWAb9TYrE1t":380023606894,"C178e8OzLJ3V":446028160582,"2KEMjPRBB4bc":784477529716,"ldUpKh9dgl1K":561591007261,"IvPGZ27Qgsr7":578241083278,"rNkl4dWAtkLV":987688454057,"i8JPYqcy57DI":994919670951,"Vl1MjZ0RZ5B8":682659401922,"2Og7RKa6syWf":490497166604,"EgozFCsnaPJT":795639695885,"xh6euNI5yuCn":140114856945,"yW4iirDJl9JQ":744263560078,"VHtWxOIfNSaM":813161650816,"pYn94VoxQNhy":16510763236,"r7OJ4YvK7Ply":124520022081,"6qbKdI38XuLD":217188161621,"wPPzW5RDJvwD":325544744885,"9iivQl05ImUw":81120166505,"mFeia5QfHYfJ":981577676629,"3u2HBJphSr2s":793598244664,"bx102DRG80Rg":308797583169,"N5DajIgPRzWt":679561104087,"aOsdU5W5fpTT":620463596387,"CdREVQ0kKPYD":669011453410,"v8O3nUYDjDp0":518146369999,"DtJDnXIvBr8t":687246411390,"CMGvexCyDfgM":846842347372,"ccGZYmnLdL3d":416551504844,"IrLPTPG98W4F":452102763654,"EZZck9jE9zIn":828312571624,"cUUKWHiNU16C":873620743730,"jWz2Vu8FwYtI":344909262530,"FCnTxDHN2v6c":428410942413,"ll8cpnTBR5dH":833092840728,"tXnz0RmTuPDi":858163242037,"16g39jKFvWOE":394370490261,"DuKBei87jt0o":725623079965,"jOjoy2tqqFrP":7872113617,"OkHBg1Xzfsj5":303596556114,"9mt8Rknv58zv":395365482299,"yglCNijzqmfc":733574672304,"b2aVvNZdvWDz":589759998406,"yVZBOBYnO4uN":84062755930,"Juim6lfi8Ssb":269425118715,"S7tLJvWOq5Xg":559795259178,"gPx5zg1fIGOW":612677217020,"jnL3Bzkbzy8v":948491443143,"KqJPC8RDtPKo":930920938695,"7k0Y40RUemh3":933228639639,"R5TfngcmROoh":563383410713,"VEIfge3MzSPv":825645948194,"6yViGcRKQGvl":674269416431,"Ao0jkPCNibFw":699696297686,"lld2TJZDdpGR":671687596356,"bHTDBGRKBetv":224988336284,"0YSQKXgftJlX":964928887070,"zLNKErhBKkag":87802978110,"1gUs1ocGE8vn":180458615796,"jJvGFRm9I4NS":752082389768,"gvReAv95dBOr":296727343766,"5ydGpokuQ7Xj":343245437589,"kbwZQSaeHCpA":143832251581,"KGPu7AyMqAdj":3057341578,"h6hT4vj3BgvQ":98251917026,"zWmLnAMsYca4":702298848801,"ZHNpsohxA35L":46259977922,"Hmhp2H4qTP3v":521474168464,"R3NyGpxLCS8d":515043788315,"7pfeFgO6ixz6":752182733246,"4EKMTBbdW5GW":491336885611,"EcQ45qJiv1HB":753067464039,"hdpJw34GD2kw":621869121082,"rPjzOwep6K0t":897774107530,"WZrFdSJx3b1H":29583358606,"hNMqWWYBZ3m6":438904952365,"dC3uETu8rbVE":733566693737,"ExWhpQRfnkMC":351957727534,"FpkjRKzAd34I":918211596518,"HdybnHRVbQ66":950454538403,"sztE8L8MxwUe":348125396430,"0vZnHfqDGbhW":456770335614,"sjkcnO01esoT":679207762373,"oCqpBcnnsMF4":21728165606,"zQyTrfRtNmlr":116082532640,"Rq9g6jCXEsOp":204833877147,"dSOOGeVhwycP":991433810615,"Xw9NqH6qdXWv":668195618300,"c97Tylm6nAd8":48741723656,"hyVZVsnTh5T4":504523123675,"ZTn5gC1RaDAG":287334554399,"1evoQ0GlVZ0d":627063257353,"BjGL4lEb8JsH":106871198161,"OsPLOL9ZcGXt":45147013455,"6cSzFpVyezhq":798721333325,"TsbrDpaeCYvq":923896961215,"XvfukanzLOdS":107132979054,"6i26U2JtjPNh":530642472283,"TdCiWCqSgAiN":597146247854,"JWrcGdOUDCh1":995253862978,"37XsIWyoZZGQ":416591925243,"orpk5AIRzZeK":798300207361,"fXDAGFwz0Jb7":254843600844,"YaO0iOzPpZtg":628074146685,"895Sv7yzif35":421302332405,"34xVFXHPdwzm":485511291503,"KEGajp0vWNUW":598642055629,"OYTPoSbDkoDQ":189997445968,"Gepl6frKCLo6":611418511411,"RTrJN8yqICK5":234290746641,"PsWTsEYvuRPR":576111794974,"T2mkehI1c49v":365546794770,"FirUaVOjxRzi":338639402104,"JL1vBKPNTMHA":689943857476,"H1DWxjsHk9LM":282520813452,"jkXLRXrkHYeL":787628624649,"Dg7SVyyZ77Yr":816906979259,"pnqLCn6prPot":732637852577,"koR2wUs2PjaP":550891070457,"OqQyBFsTS4IO":542527576014,"D6wH4YhZOeH4":685611474740,"Wi1uLsJFrxSF":242220987838,"CpjHVOCaQfzV":809302657290,"ABxQAO0OpvQL":842079082275,"xVax5kFs5MSc":240478201065,"ThIVqJUOjXXI":978638795983,"NwqgWGZ7tWNm":253570200044,"XrTXdiuXMGmx":663543288621,"7JP0SQAoNLDM":727831186204,"tl6Qs9841y9r":366275643572,"5KLoVLDMG1Xm":113973549,"xt3uId5NZYHH":451692269548,"6hn9CovL92Y1":637636209634,"rd552CqRagUc":132823968006,"pg8i2UhR4hYw":626474665997,"Kbm3KTS6opYR":747605247866,"V5fbElEZiTwk":611393619761,"lOfh9n9z7IKr":128090910933,"AAQoEPvtsXwL":212545992338,"AOjZpkp6lE3p":202373013946,"i8wdjTsmIMpq":33444267991,"36gH9m4q01hS":877102004777,"PkrffScytjUl":50638132041,"zSnQA74ecO4J":876301176177,"DU5PDoHe3PQB":78236330730,"sDkBtmChIpte":574066534086,"DocWGCYZEL9K":381737648647,"G6TJtHXzx35v":705805645103,"6HOJLIwx8kBx":633987897228,"VKShi0tVaFBo":4014752576,"l2hTBf56wzPV":35860064458,"IVNx700yyON5":846813207121,"tNR9mPK8J7By":210880063865,"ll8OvgpgNIXn":10103494143,"0pn4QdUMQFVN":764169301291,"8I21aF72PJPn":13979761506,"IQ5YmEbYdKcs":533176349188,"BCQcgMSAvj8m":959094543478,"FITQ5ZxrJVMN":999697077573,"7tmBOia3LOrG":979285564510,"yFEAtGNKU0tw":578961723710,"UPDaMD0pkHYL":141130198556,"EQuuJtwhDXBB":987323744401,"WWCgUxl57LOZ":894239343313,"mFe5wL3jdtIA":207780967374,"Jc6FFOhpTetI":206759991985,"n8HoXEnHGOp8":550349468148,"r1kAjkvx8WR8":474321484491,"UiFtXjPLRQm1":403486705675,"UrQLkCoEMnoy":37124245477,"QF0lXuc5Wrep":878748258032,"yYgBNKYrctoO":2316463948,"c0gkHfhf5LPu":132011936929,"UaOm8gH3VidA":121027805907,"Xk00BrREzrpB":6353759334,"0GTxCipxDtC1":362315058123,"HJKUrGrHnJta":259808579158,"LLYX6otpCTSt":43519949932,"JZZHbqR0Ofvz":70206989769,"AFkE1mS6k3rb":918779661506,"BWStOz6YzAcY":268345315654,"4j6xxBUwCoqZ":734630209772,"fmw4MmWYJVEK":265807074631,"j3VDSdfvpYA6":683252321536,"DI8IQIcvFql4":558362504257,"qewBwLwtaS91":655556650653,"N6XTbISheviO":815703916020,"UBrQyOuaTaFO":60745498291,"xHjS7hAnIvLx":769876260537,"pnksxwGhv5aO":566382672489,"1OgzSGWQfBYv":538138180728,"agJnnAy9vUSO":567806280801,"3gHRK76NzFtr":87615878224,"UZp32I8WCiiz":777308928569,"m7a0iPVKXsvy":570729996523,"dxkTLELyXqAa":758941288039,"VKl47E14TUPy":410133751122,"fcUjzZLksxJy":328477452116,"Rz285BqZH5Lz":950129051153,"QKX7oUNmdmi5":71694575420,"jNEa6Toa4k6O":733622447141,"m3coB14Wkgsq":456840338673,"1gF9so6kPYAM":462022648617,"7n5dwienhBR3":866269808570,"T9vHUyMFjcFx":134964691231,"sTegOIq3eHaq":717955799895,"J3HgmYvPy0Ah":766927361037,"vFzOBMjh311w":148894690628,"uoOw6wco4PvP":480154674604,"6jyobBzQLdnC":850002421907,"lJMP1gA384A8":455363784833,"roHrUTBHgPOD":949201986920,"pyNTDft22Het":978457241494,"LF5pF9z9UxeV":368708875006,"hOkJm6wbgXZt":378122500778,"SCc2EAFnE7dC":970253436157,"MlAFjtJqK6ps":147797541293,"ppu1DaHERtCf":655466855739,"RJwLmWLxVshF":305332631319,"LZtV5sfa2a3k":630273047563,"VBar9WQXkA5D":266804642091,"1PVrtawaT2U3":8391036768,"xGThmJbPgWKK":767197314499,"T6KSuyrZvgoJ":848346367396,"gvHsPip0CBOx":247453836741,"Cu1nYarjkGF1":217892223594,"ZTXVgRRvpp2L":265714731443,"nahSXoogIMeP":192883079242,"1q0oy0gh6Hug":164453739411,"JC1TZGS6UQp0":535949279239,"Dh2jzbQpMRKt":142606751155,"cosOJhdbo0lX":548765721762,"pCv9YSiV4b3x":3214492345,"MHwccvazt7YB":132677531830,"mKMlMU5i2pk3":520833126330,"hmnooDAIomwS":721898145850,"B8I7OfgnMnuL":68604768334,"j7jvRfxSg0TD":726489444197,"mXQSdxojDiv3":192091269279,"0yX4xNZsTOK3":881656798988,"WUaaPRJKal1A":619207587881,"N1H0ur58oO0V":473576656565,"6iPBY9k2RKug":773789817110,"8WUCH54J4LcT":387922964077,"KUm38IP4NVUT":644394087795,"Hfq7P9ra5cRO":169948304100,"TJH1DPUOUNDR":240559505362,"rS6cFAigh2ys":35325882893,"X0bzIg2xYtbV":198488150606,"EKGy7FQK4cQK":109274719290,"z5L3kD6JqRFN":601864560436,"1NqrHfiSYQ8j":103998318027,"pHXpqjnK04pM":757163157640,"XP0k4m66cO46":938925556205,"0dgXehb6anHm":591901844751,"12sIcUTVzhZb":613044705532,"JcnvNaMFaGhu":166271365301,"Sbzv1UfKyd4z":258270254507,"O6OEVx6F6LX6":894218927823,"KuvWAz1yJtqU":174706345742,"ePC6MADHKu1f":548207698419,"tmva95xOzNX8":86182744889,"PHlUeImuWtDF":674544288582,"6DX8C36Zwdqc":267793266541,"tZ2MnL1ULsOD":734575681893,"S028R6Ub2avM":118663138503,"NE2P9bkQ9lJC":929546011218,"1wZyQPCSnEq3":849629318809,"cc9zF2X4NpCP":906168575214,"7MOlmEOzCbPP":345917807129,"7PoKpcp8tp7L":702992508233,"sSaPGjYtVGpY":472846261466,"Zbzwl86bvivc":582633491088,"WR8rLSMq8NiH":275293558896,"8qYwy2Gi9C63":808434528411,"r3CcvG4rLtRS":667460054195,"V89f3mg3uNIl":989907300795,"bf7O2At4i4RU":85235480332,"dJEuwqQebmG8":137879275285,"nObJRD7N4f3k":70686202336,"b4qY3cPslG17":166160528121,"7pNjcw8fxzan":127112621653,"zAmi6J0LNqXX":408413604912,"yffvMFrwrLeR":105475530337,"kSYaEZLtxbLE":838614629565,"JdhHLQk6uCOa":636127352146,"3Kt2mWrtDMT7":281104015285,"kYXmMNWxCH6u":801398808922,"CCGs4HrDbZSw":66062064743,"JZ9kDucMgUFV":64934397579,"FTKyvedCh4Mk":496590922515,"F3g2lAcb5ER9":916031368615,"Z0XJ1XhQYrNI":125855876897,"jTFc5H8WYS4f":35499035095,"klkEH0LWWgEF":803702715671,"ij9Y0tW19hyd":193012363302,"HyT9gaZYGs03":472180109274,"6DRxlfbVSjY1":323753720634,"gYrREpYxkT9e":826584339932,"o3laLcI7vnvI":216199909850,"LVHV6WfjcvvD":314181686802,"U2aSYgmOtenB":964671284039,"ueGSh6qqPcS2":80215211891,"WggBlTXzsun5":670331082213,"bkfSNK2igz6g":814772426306,"dUM48gDodRoH":470569176637,"2ilE9XqD764Y":224270872669,"S81OxCJw8lzs":768758953898,"9SurMReo7wox":279010963483,"kihtIw6giUEF":891175697376,"RGemkOL0gTCw":852556876152,"2UiVdY4BWYKw":690470482272,"fJNZpPITpJzu":411594047647,"GWHasteRTIza":722152007858,"Weugz5KVUclv":361651568082,"qHgVhi2nmc59":558032570983,"vKAjp8xdqpJl":27789645321,"RSD7U0vtacAQ":458322835389,"cR39WRL1fXc0":542348374504,"rFFUdyCi9lEY":699416343374,"K7PhGHTIeR8O":685300804871,"3mz5g59pYSNS":795824298151,"VKRU9tGG8IRS":679330288653,"ZdvpVrc7fuAn":827534975659,"TSY4S2UpF2lq":212744483504,"RUmvMRTxFUOs":459084613874,"DYlwIkvEaYG4":127048855535,"fJJH9X234cqH":387301559699,"CxxxsseqQMTC":504643636244,"yhcPTIoqKyyL":988120365094,"WavKNLnGpoGk":219308837778,"qHI4XFZgwDI9":293574450929,"PPzXvs0OaZ7r":180546419041,"XMXdtOVFlAn8":806427298541,"SNJ2NE5QWID3":12651845295,"BiUsqsnqrxqz":494790635815,"aNR3P6I1C8sS":21505020907,"AqT9VoiHI5Pi":46003873676,"ZZYkasvRoLOR":843451081149,"tihk3GkeJME6":673734404866,"X8221lLHRU0r":560969273748,"zihH0rPhAQ0h":610191069834,"cGShi7pU3Qei":95685451300,"IGoH8K9OR3yZ":78762326546,"xRUAgJa8Rpjv":499323896902,"FqwWt1SoUoVu":127000833721,"Q1wjhwNahWuA":914056994447,"NyKeRfNVPmKj":648005742269,"vWBaK798m9oq":26639759387,"gcTA5p17cLF4":951229299088,"FA8LK4rqY6Qu":995973736773,"FK5m5Mq2y5Op":365748718109,"a9MaV22XDkzs":697710960100,"mDrAH5Lr9bj5":693608268273,"DJtvQhSuKok5":872808631192,"92Z6jChpwR5J":575928185953,"PAzQqM4LGjWO":553159802275,"1C9WcVqHDgFS":576222179022,"4P3609D6zT57":803465204904,"hDzcVEMGHfJV":657417229966,"9NCGwjeYbXja":49527020726,"maonZ01jELlI":632096130128,"AlMBGCDvnxhL":849209124421,"v1ShWEOzJmPd":18239080877,"gxpkaUwkltji":998897507688,"AUnFdwN9NON1":737005610906,"Ed7AKgvFS0HW":535421201948,"EcHzucoyGQDC":505719020803,"lStRkW8ThudA":110449161481,"fzWbG7EmLzrS":911430838133,"WLCqCQCCZ2a5":332108054933,"kT7CxSocqC98":121432971930,"KSymfMrC9RhT":916082627457,"PkYVWXvOYvwa":766471326454,"HN6VARLj9CzJ":862483318438,"qc5JOb27QITg":882485416851,"uIyjH0NERHoA":750946370865,"sZ2uvsg5GY8h":649868717875,"FTxkUqjfvhYm":251847251897,"KQEPBcY6YX0c":537558762996,"UQc372vwg76D":17238450901,"2fCQ4roDVfRF":6638643060,"c6XteMt2Tlrm":806109645753,"jBxv1pygsPKW":655064320268,"i1RNVGPYn4Zt":508234874173,"tFovX9pV04gD":654366832406,"Jg981WbhCc7O":173793568858,"b67A7OsFtjuI":478488626867,"M3IBdvdlUwW2":52985246497,"GhAmAuc8FcTm":444999693017,"zbBSYxOZdJJr":89000691349,"o9Tn45ITJzh2":391514332923,"S5PHOWivbfEg":199738659856,"cddGi9yoBrzt":799478295984,"H9NWOFOFTOgV":746064611470,"d55BL0FTEMze":633578460074,"o92m1RnVfknb":607607798844,"ctTMa5AlyxBq":200475889594,"vgYLMDqMp39q":30883408189,"CjW6EmUur1Ac":455911012839,"Jd4hadjSNYhg":991631193686,"ZvV2cZpFi5Kv":500467417962,"7dVpJaVpSR7l":922883631293,"WdD178eSFZSg":140173348143,"epY8UDqeqec2":32362791433,"AeOuPhq7b6hb":519979458225,"nr8jkYpe0D8i":454414473871,"7Cv9kDZBjcfe":659761568978,"BCp9KcaXhXGx":735091840750,"ANCUoQ9j97xY":851024941041,"TanA79B2afvU":566455873705,"oxLpS0LAUIXh":595178560437,"vYeDUsmG6Xx0":821729099792,"bZ3ogFDTq18F":519031801820,"wf1g5eA7VYPk":894281580640,"TKzXawE6r8bI":716854779916,"80ToYroFQx11":588585809255,"3qjwyZJjf0ty":863675991369,"r77DWYIj9UL4":386959961734,"FBwnFjI2fyIp":734316670747,"G7E0HK8K1KMm":911852672779,"VQnDzVirY7w5":662923552410,"pko9zLHxJmzn":650304337070,"uEGEImiGjaRF":366060136189,"AAkumN42Ubgo":476411482617,"wO1G8S11fY4m":805222564897,"fiyDI19n0hsZ":535809344961,"heIbS0duHENX":481492996843,"oAkmCBv8KGDZ":609098070270,"pnbsljTC00UP":679023067250,"bm9Dmb39mSiC":399198764101,"fQdcGckh3AsM":889409360933,"VJa53J5CEZJF":970710415488,"RBanI28da1iw":311220812598,"nRkFtf7odcuj":189956206814,"ZrPejYMnEbcE":969742332358,"jsez959YpPOR":120835525470,"YnnvZ1c5Qm5Y":86502624627,"wC1NAaYlTDWf":197065374861,"N1SHPwjiBpk8":350066834120,"36EJwjLeyxpE":519448504278,"Sro2t9D9EPKi":892720819722,"ssC6Sm5JDCIx":783504202946,"5SYhc9ziWrB7":274470115298,"6LvetxFzY4x5":156214651953,"AvT6EZB2usDZ":214642482090,"x1VUsZ6YYhrL":742406039146,"RYbLeMUfjCLW":214370846491,"l7YGIpo1ppBF":913368265631,"be5Z5YlKvKiN":896601517206,"JlAzEvuX7Us7":15556620351,"Y6yHUW34PE8w":713367235316,"21X9JxP9XBqL":420368568208,"SA0pTWt1O27g":273090686556,"mF6Ccjyh8oLv":196874335981,"Yj82RQTOHCy3":552579815323,"RChqe11MlrBq":382032000326,"1ubDqodWizVY":201283100858,"REDEU4JyHVLW":543420193694,"5P3S1ZdtBkBe":265653789060,"tDSCGFgIL5W8":850953058514,"vs0CQf05mJgH":987366369868,"yxkiriGRiPd4":733021944502,"z4tkyt8XU4Wr":527897267293,"xKpCCw5S0xwz":364213866089,"dQ8dQILHSRYe":190146131211,"ZhjbkESVGXYO":678063825516,"Hn8PZtWgoxO5":187627440679,"4Z4JmoyJq5I9":358915031455,"7fyB7YljlOJw":605598843637,"5vdAbT3k2SjZ":322046952634,"A1t2H51VtfX3":588353889866,"DEu6yvPh6VwK":360500916963,"WLNJ2VWRXlCr":844877296187,"md2WgybnlPrU":52010422572,"zftRvoSvVwj5":699588844967,"fpfLLXoHVSvz":55359355309,"wKPWwKfwJaNa":58967344029,"3KVmbg0cqjNm":918798759979,"XZE2cXi1aqzD":433385531701,"vDiccPFlLwy1":388712723465,"OWYGjz0J1tgw":48740135288,"1vDPWeS3nQnN":354573802542,"hsA5lP43PJdV":92738431623,"DxfRKAOwMdX2":905045546063,"SA1dZc5PQ2qj":826371528190,"4Mq1ZQKf69oH":566182826857,"fPa7tUuR1YwP":990451563958,"pBtiWY5R1dgQ":405290126560,"bMxMfd6oJ7hF":94508930777,"RAn0hqhaTAPs":888436352343,"L6SMFUQt2SDd":833575690957,"Siu8QmwgC3TC":399237752177,"QmyvnE9GHYGs":968233530720,"icUXPGWuW454":492747063190,"qVfqi9qMF5m7":913825003576,"PjNAA9Wo3WNg":763455072617,"j5CG6QPMWN7O":936669633840,"30h2qO2jsnRV":262096774766,"ACB3dPGpwNVx":914994299998,"XsGu0uTOnON0":674610501462,"0ewa81KGgB7y":847305104459,"FbGWRSUhznhk":680703446334,"pA4ah7X0M7BV":197121195404,"Y7qaBSo12M7x":861330005469,"4Supzp0mPQsc":616374765879,"YkKEGresRQTI":32345462207,"tft9Lmeanhox":445491248442,"cKOr2XBIvDPU":378305489994,"S26QeOJIenDR":593523355475,"XY01gJNu3vRK":959625429964,"eMFV5yNokjtP":587767065322,"MOMMafEw2YyG":140663876703,"Tqlai2qIL0GS":442107010189,"w1RdW59Lrrja":803300959757,"L2PHGfMTqFGX":177353456523,"kEyZthz0yqdu":481411260658,"WihQTkl1qZFE":496387452276,"hTiEfXHj7GQy":21420071101,"lIg0ZcOhEBnw":182884197536,"DgK7VFS9FrnP":878151537827,"6EoxbFtQqoST":294124303655,"acjEzMh9qMul":608851110023,"Nx02nbXUwxNH":976715201191,"1NyxFTRU9AUb":891091173421,"XbYRw2a5TUIp":599104236260,"joxoZH6gLOu9":551345529287,"DmoxgdAUxhfI":254983601206,"cDTvqnL8hnXg":833602040740,"96MSqypVVuvP":676340210262,"Pu2RvRILrB8U":702284804288,"6Q5hlAWyKNQV":737906048759,"eEOrVmJQHMgL":52601069488,"nZ36MyhYhrDH":888704436085,"g4wYOx6N6RyQ":487977091829,"w2oKSLKTVXZ5":674581177051,"RvdQ6sI8his0":958509666559,"4w8iNWOh5lmf":603563636733,"1jAyvkp6Teop":841779991910,"HYCHFlF5lkv3":823617967018,"EN4daDWFF9c8":598950975166,"lLFmHd402U1Y":986242151661,"EgdJfFUjfKhN":428190950154,"3U8XV6qfUfYo":467129519628,"rBM6v9hmma2Q":626982476383,"IJz822w8tbWX":697595894014,"4vnFMjByexmR":899240095291,"tmsLTRZ5dCVU":645221986066,"6iXwynglCPfS":317379572350,"nPHpGynyQyly":610709984205,"LiZHx9AHbqNZ":206521558165,"elCZj6Wsi7U4":114949461520,"V2UzFLPX35DA":33410207445,"8P3NitdtdJhu":570668686956,"PH69wb3hQ934":90597150798,"mqpwiXv6mnVD":150331463220,"wF6E7m1ST2Ne":521804029408,"RRKhPkjMgZ7H":949577886938,"iU5QiC7PbK1d":848407448630,"C1W20tFFJefU":164946110689,"KhNqt8Gz4rwo":896591047723,"Do6GNt92xbXl":173587854183,"1A9FP5x924J6":163606509255,"lDVqYTDrzI1w":976387204764,"sPMGvNxJH0uW":872853237927,"dTwePjwFOuAk":222535628245,"PFLMWb8r4Txr":371807542902,"scWA29eCq1fL":312213327936,"RCCQOQV0LQc1":93846613360,"SX7xiVRWG7fw":888678789344,"uYagBJHtTkKR":915665872711,"8W5Ji0Kc6ikK":952181550764,"DCyzv3bDLnMl":975553878233,"VKKmqkb7w3ma":423783927206,"wlK8XJpyN0vb":768674720303,"cNRtopBHd3xf":697947955160,"twwPJZhVuUGN":584195667966,"rW6m7dwQQIWV":743826410542,"OnV2YH2xL0Fa":332011583491,"dp1ttqsyGbBr":369044224783,"1vvN4Z4rNVrX":377638273148,"gZUpekSM3Z2f":759565318160,"mrcyVvlJpY7h":200176731428,"3o7dL8n5hWwF":782493303222,"rrm9lMcseWmF":712664064442,"9lzxVhoaCpwM":924606945749,"kZsZp8w0iX7r":178076601556,"iHN3fJvRfala":424013738452,"O6yV7LLIJCKi":695979812309,"HpgHwv2P9zXj":214128533449,"2iLBEK8WXd6J":865821416480,"2PhrMyX2qwMV":643595793518,"X273iGKldO6m":566334849457,"eU0Db7VJjqrv":499869938560,"yytCxj78IM3u":507635251546,"aGodCPaYVhPd":123527955421,"7e5ZQPVdRmsW":147700251171,"C8U0yfLLB0O3":459347072202,"C9Kd4Jm2XpwJ":968889374367,"TiZnpwt07fjw":485721078312,"bqd3ZvXmvAEo":903453348388,"MMYNznzaMSVH":497647827835,"PJPxW9B01dey":6611999335,"tEk4NP1bv826":878567765464,"w72o03TZ8jxD":429583383362,"jOcFeCIFacBt":738964735234,"qsiaozuxrlWZ":566350001414,"TYfpf1wxHXQB":623688564302,"PbwrbwCGCrfU":618554994713,"HjzQWx6yLt3k":607544357428,"B4bQJbafb7j7":704153450837,"CMG7DClOYHqg":53499672188,"MDqUShnPFe3d":27029162769,"fhDt6gGktksL":929818012491,"JMoJCGahEDae":299676455710,"kIPRPErpgq8w":448190941340,"9yqtzPsGN457":235054026604,"Stwvl9BUYy4n":232556568603,"3ydpdmpvpAXd":925577268088,"CmAJB6CVD9bq":972105024754,"OVp6gFFQuAyy":637393105116,"9lAxKQsKgfZu":56749767571,"jdyiKdTu9yuG":621355111725,"WJObHnUQrHGQ":78258631119,"jeYiMdfhMkEi":526153077812,"IxF5mubQf1NP":868571892203,"iUdABLc2VFLe":826661283557,"6EdqkO2OZG3R":686527062424,"BCrjSr1FZxYc":294748560934,"g6xWUZHqrAxP":556303840333,"2sR4QrefXZ1m":995606630338,"sqPEvCPWmJZR":810234251041,"EeBLReEpZiGV":86561230695,"Vbi3UaFaPPjh":999894481641,"4GF9dBTVM6cy":549667353445,"HmCxAfPTg594":138773530313,"pC92Yk0NlD7k":741549497850,"qFYxr9zj8Gk0":180806884957,"3vYMWNPA4Abe":654613215875,"MDyVjW6bLu02":643990138082,"lGP2rGOQU94X":571156270621,"4P285E5PhfIg":29906632698,"t3F6DGDQjkpm":550843369862,"kflTVrQKVpya":132054381637,"O9wACFrp3zBf":949064151578,"3ZgpOQZcdzmI":687655540570,"kTt0EhOtZi6L":677412695973,"06Mi7icBGpcx":815454857593,"hR7STaFLAkFD":254010219423,"GpW319UJtJRr":205743295584,"Bcuq8XqRkwJ0":195916202202,"0oE7mRC7Km6E":539212805902,"DZcMRTRPQK2Y":290893428177,"xBAI31LI23sj":958338064863,"FeQzrmroJqDj":446688141878,"VaXhAwjZa5S4":636986459821,"YOFspkIW7iAY":249646052957,"WMeT5txW4D8T":219290270880,"dyhT4xLm6kyP":288363271894,"FhgWPxKSZhyv":993225948649,"yidkcq3ZkPlC":899176455525,"pAd2FjsKLyFq":143244648068,"PK4Pfc74mQDM":566199930358,"jpPlXZtM3j7A":794545845716,"axFbaN9PVL9c":177269645432,"t4Ix8WD5XTbt":116209107153,"0yC6tc8Utndy":486804769170,"ocKJMe5cI68I":791492877030,"CGPzwRumfDIX":228043666416,"nNvAlc7KwQU1":221524141929,"xknc3ifuvI9i":925755959969,"LezWT1lrvtkb":32650721014,"0rIT1kDiHDuy":470035720907,"t4rZn7wzU2k9":857413732395,"BcHylUlFIm4Q":549731225553,"44ZDK2QqqJqu":241805226602,"yc4ig2XzxhTi":864091096661,"E5yXpntD7ErI":427842845403,"ZKCfdnBoTqNz":312337789320,"IcYydBXLuOzZ":430854621878,"PWBiSJvytnwS":852413910504,"pzOj1erdsHdi":594380726648,"uLuk5MsqCXTp":335795862388,"qL02EXdXpmd3":221550247183,"9N98lqv86bsQ":600644655562,"9LpjHqbv0ZRk":386584676513,"NSSTr2AaEvej":473664886416,"fUSAE8vfqRC7":394733390183,"mJxBsjTzWMuf":315188148267,"VvgnzBSYHBC8":334738997458,"UCSUU61D8vcY":244071212262,"DH4bvxfnFHFt":530768573933,"ZQnGTIg7tRqn":87215718252,"W3XA6o4aS6OL":137459195811,"FCeQc1yyTDYu":207618554981,"39meQCwzV1k7":42270025892,"8RHYPJn9FZHI":47310128867,"nF91YRp4UTml":643114851071,"IIa4KrmiPj3t":675128281488,"YmS7fBPoAICy":884124808886,"akYBJlkzLmyn":95229939880,"0733teVr8FG5":150792112554,"8M6oT4PbNFeA":947743682987,"N9dq6SRFdWLe":577320166378,"yW7t7K46JxWM":712789387401,"WBQDz0lyrii8":686420257337,"38rHJEYlfSCG":666041106843,"1Zbf7qstOwZq":47628162259,"f35lALgsmUeX":722125332751,"CBQnS0sSJWkD":814040079836,"UT3EPoCY2tKp":836040050276,"UcuyImrZt0P8":416750638267,"D5nt2dBHBUyR":997821851050,"4gsAGimyhqbW":615509602365,"JfbdWCC5AUt9":71382255728,"5Bdqmzc70vqQ":463647587704,"mvYjtfYXebMx":573128593957,"Ic555LiQJtci":823861239696,"tLoG2MrxoNF8":291185193378,"cPLQWcXmTfw8":315905528433,"HBVGLehdIDUd":601645964278,"DjnZgkhRxHbY":621316282728,"1x2sg12N5rl4":376439988555,"JEUpv9KdnGmL":767798991399,"ge5v828aRgjp":69276985893,"m5zrSU62hNNr":598181821319,"dIsQGdqmYrUD":765897856596,"ybDx2VOtpAxG":321757040957,"pnQRASZt1iJ9":291819249597,"sIlOIiKXmhCB":371922756929,"PVNUKdS1QtZR":241638895377,"JzdizHji1nZH":11925395134,"2RT7ZJi9EwHw":223364060554,"lZ2P8ifwQ3bI":788538738359,"9MSWNvT31n7O":605466504429,"k13hwzuZUaGG":581074561291,"yMCzYke7fA9n":10384352536,"PqNgKmeUZzQP":907949443153,"KkVxzPpVS80p":851805667532,"Bglkcv8XL2mn":937259004652,"ErAmBHxIA7Bn":233460858597,"5z8qzWiveO1y":853626846526,"7C8EUtRkUvDe":344289426537,"49lnd5HZpFV1":543716161143,"9cVIHCST3oO2":777828251560,"sEkB7Y2BRnSa":203676225530,"oEXKLQdfgzNm":869691649091,"L4aE8DwAABSH":631977190626,"h2flrqXZe5OK":692287985776,"UhutxmdQVSbd":818680757372,"E47sYXG520HC":133640884262,"8c8iYLbjG3Qm":235037460592,"Ji8hr59WYF5r":572294489231,"GT4pPA6n76gw":773236793402,"y1PW3UU9TOYL":599415910632,"e3FIu5CFstQ6":451911187476,"MXxqAUj3wg3r":151458856156,"tCaeJk8oc6yZ":498014297885,"AlALpxoHa2sk":288959999933,"V1b33IRAO3KP":636834977147,"tYsV6ttipnFl":842222064299,"YsOJJC5q7UPS":694227810179,"QlIMjQh5FdTR":718538645454,"T9woFheBHDVT":660752713805,"kDrayr1UzbKl":658740430637,"ZrXFy8h7mhJA":285690648702,"LcRBTxVvfFBK":341337408633,"yVcYZZRzhJRs":552061531881,"mBYlMZu88G6m":22869433532,"q0t04EqOkuzj":892297832475,"gFb2HpE3MHkI":512592748735,"6xeI9Ipcwmu0":178902445953,"ovV9VWQOGgjs":926878037919,"RrDGN4UzxNCh":503455893832,"z85FwyBtOr1w":361880004621,"10tjBHw3QNRp":764418398049,"FxYDP2JREVfH":221598272394,"ZAkPWSSQtyYW":134638404984,"0E4hEruSP7F7":247634535022,"uzX86g37Y6eW":872163829598,"5mgirT9P5Rqn":749770947771,"O2iR4UCSF2Vu":621264966381,"UMZbNRvpPNNd":109912164159,"mVdN5n5uGrRP":704679510109,"mwvbEktZXnXx":875870617540,"M5jeXSSAZ4PG":920638817343,"Niefi49wp9ZA":228111015479,"4N72RCOK81Vo":124229007673,"U6E4XswlmKOF":130405944900,"vHofPW5pBkew":584951366914,"Z6eIWJM3XVm6":854971123194,"8SDeHMAw0aiN":789433895625,"cvvDul1qDcgQ":100816753268,"70jkepPPvrnN":2363901139,"fCp6KJt5eXgh":474339520050,"GIL6xJlJAoir":142343311791,"8oCKU9GSErea":328508175717,"Q0PNjr0T4Hqb":547393861888,"2QqIpemc9a8l":534531275656,"eakbiSdVo0qD":204344686032,"MxlpkfCbLyth":139637639951,"npSEwfXgW5fn":957128014118,"F9Is9zHDFPc1":109222400475,"ybxARLMSg85e":417601073242,"0KOF3NtGKyCZ":182369917691,"x8SVTSAMnbHj":723052903821,"BNKM87DgOMHP":358993408901,"W7aCdLqrll9D":721037324778,"SRx3R1zTszNn":592389702369,"y0y3xyB6jmzG":369986197034,"1D47CCmKt8aN":495590261872,"nfHnyEtMbIXi":568983983659,"SjsPrQ4C21wo":3797045336,"Jllz4l6bM8K1":200146549374,"6bShLD7fHgGJ":773587104739,"dNWADDQd0CMA":791656808892,"YCyDf2gKtCDo":76156919515,"p4ty0h8kH0rF":292325828864,"lvLur7mUrRv1":282564026175,"e1rJUjwQr7DQ":716007054896,"Nd9ZHpAFUTTY":6834105200,"54AH21bhqnxz":156390498015,"4Mx6pc1SVxBc":971871751675,"m30CxgzC4boH":495369241033,"hFcmMxM93OCt":302518561829,"RPLFFve3Rkz0":470272123710,"MGzLnhaPKRJG":256351662789,"uUbRQimFVyaP":222607094776,"W37NjqVnsHe3":978482384592,"uaFSNGx2jl7V":74449361906,"CAIvMXmNB6O2":126341858387,"TZk1ZiRAZsY2":410982201183,"mrWiV1NvxLPu":418186875384,"LabWWJVrvoEE":708373893011,"PoRoZy8x2UBv":867879128783,"3ZnAoW50BfZF":580953092698,"SLvIvsDMCy7n":100624120381,"HGLY0zmnj2Gl":157849150007,"XQGeIM1xyBxS":928712507251,"zoBV4OH3vOfj":90889629969,"l1QBKa3VsKYv":915660253719,"tk3Ai8xqmz8q":68247160958,"VWrP7Im6wtPI":204078605198,"uLFJhBmMS4oA":902399251301,"eSGB4WdXhtqt":93984129209,"k3mxDhUmu4IP":95546817092,"QfCN2vlka2sB":233406224308,"gsY4CdbbTcjS":192315173585,"eROx2kGd2yge":182566565707,"daabDS33QCNj":39927401231,"GlqKjaDLcJbV":564317728754,"tSPlmzWEi7rt":947562895186,"KlojKRA1VYh5":103750372780,"cbfLtfrEbuEL":474135665636,"cSaT6Qdnjld6":898667542591,"LqTMCkFyodYA":481466026547,"W9z1OEPwqETo":671514016143,"WH7ONWFLCQpX":825096903692,"EmAGM7YNOQt4":88742418908,"DX4yLAK4zgVk":405921675503,"JfEHReIECyb5":357595464177,"abiU4wwWkerA":228281294230,"5ODgvbxYSrJa":744079526548,"G70jaxKkgR3I":661642454016,"hna37UcLkhWj":6777437437,"5McV7vDc9icE":594372927061,"ulBN2EfxQYvi":200431466901,"6CgjQPxy2ti4":574056595448,"3iPcNKnS4ETi":962847363514,"sOS6e2q8MaSA":438913724455,"2DN95Mz6dApm":564601680342,"MriNPRSr3i2A":334893919198,"uroOSgWBqJWV":13894425861,"pf58EORtw2M6":406708146556,"Vna04KsNJb6g":973675493815,"RQc4W3aCS0CY":340573893544,"SIosc4v5Gfp8":999301852006,"b9TmDvrcLYMc":45103735315,"e3ubc18xsIPB":818153173284,"J3CLZkodTZ9S":462494225032,"yvYOp3mwDfJV":237289864760,"BT7BTF3nAwIa":428057528191,"CmWPUpOqSpEl":831917701442,"GBXF7BlBhN1X":656433663839,"kMdJ5MX3bUH0":537473651606,"FR5JJXKIa9Op":815045891720,"xaOwAVQriOlg":679974520359,"wZvprI49UA7s":923047558674,"0JJ2DP6EMxu4":388017924513,"VdsET23jhVGb":570702763798,"BXasiFie8M22":462072282750,"IRTv1NF9YYML":704715087481,"OEiOzecdYLKk":563380728620,"ZBjXOZ1a7lQw":173294094102,"Xgxj3J3x7ZA6":802068168620,"0lw69nYYVT5q":316331302873,"ZtTf53bs5yvp":973178873847,"zhCH2m97hGme":631965509351,"9CULO9LOoEto":486516728508,"G5klipZHCMu8":958842871120,"0YNQLDBpHCsC":322421780205,"3WcZNSWS97yg":486786814379,"GowofBzAM3xn":515807780538,"A5yQY7epEdIN":944867231041,"hiwRw9uSj6xN":930021011312,"I4ySqUj5GCCn":510582073192,"g7eaJKCeIIlr":45167153005,"5wtE0KITkDSt":475183517079,"gcuJLxsViBGw":309407702077,"SV7xiTZofft7":263973135856,"TgjrlL340Fjn":140519329615,"61dEDrxqjmex":633863737526,"LkTKByfsdQPM":219180966545,"oh8iALlptMre":367791789915,"K7mifMdCMTX8":778397815189,"PgH7epLFed1X":967730675763,"IqLSfjasZkrS":160356045311,"xjQP7WV4nPjx":879891631316,"9hSQHy0qDeNn":705847615580,"lvEIAhGg02My":184309330138,"Gek7kYcAQFxV":671857311206,"p0kdXpps2tLs":995384560804,"PFkXau2W7MpA":49992772227,"hrx4vFOqhP6I":308105035452,"m3zignklPv7F":583379288840,"9uCXIao1VLaZ":223231196944,"Of0WhJVrjXV8":416969035560,"8yMrG5anoYS6":451401901424,"CgpOhBIyrju7":375891911510,"UaaIekAtVFab":437457705025,"8KhWGFO8uDU2":340647768826,"LcGaddYhy9nP":789369898002,"WUtY50m798Bb":771138434179,"3mFkRZRWuJRw":298541542249,"fuo0xy6EeFl7":443660271960,"150GSKrqC0cN":255460522141,"QilpRq6SUvJ6":963323585096,"UyPHvpYMVUfF":86300786085,"0JFY4PJapHkX":994657973947,"pdN7JPNz0pef":384100203422,"9dZABVGanrge":50647927536,"OGAInS5tuNdq":536080591633,"oCE1vaEFCNCD":426345948969,"Z1NeVK9ISPNp":752696305166,"hRhhN6dDXSq4":753148335382,"5Nls6bS1SqSt":802178775837,"HVinYT6rIVkV":70567976639,"ODKik5F3eNtn":318563670641,"mrXeobr4tSaA":53274092741,"JK37Mb723ghB":140253726005,"0445eExoJ5cz":354764771231,"QrCOwyIfPTVK":198538318306,"foQMH0JpK454":601001552238,"Fgh9mnPDdDaF":718923133644,"siQq6NiNu81d":119697697992,"e1bsbgoKXrim":242457003174,"G54HRk5yiHPK":16202234698,"zbNiKp4NEOxR":433618119582,"QhgGQekaaCk3":99022817296,"FrG7ye9z8rDS":356126940256,"1bbcyKQb76Tk":121559714692,"myt5ZWdVYuO2":457652655055,"ZLnJ1Eu2bhxW":557891729497,"Zk8y3IKaGAcg":137013312883,"7B3fJZJnlZii":90995378163,"ohQyDW0bUUwU":341216325824,"MQnqdRU5rsUc":477537493209,"7tCFIQKKJO8T":453644410569,"ZjSPCZFWOgd2":306520265717,"dgzy627XDmJK":21438505968,"0ZraLMCyUU5W":530854297640,"qZDV4cMJvgyg":470231339081,"cfdSJAcTgM6d":949877123307,"Y2ohgZ9dUYkK":573359134608,"XQP36tcDAEd3":834649635114,"4qKRKEr1aYjl":432925989817,"yBF8HsTs2WyM":697295987461,"qNCDnSxL0FiW":703605519225,"JYxwV5NhuTJu":562639057392,"y6Cb0A2hnETA":223598199711,"J4dpKfR1z6Nj":485037463925,"cmOMcc7RfzyF":870247763245,"vKvHHQoeJKiD":871370441287,"qqVU8r0I6wLW":119111470848,"SUSCx2oLgFDo":203952288516,"Q44wZ01GY56G":388322881070,"PcChiRgpbL7F":193894964218,"NzPPneO0j7Dl":365753532847,"zczBYYOHvtAI":402032538667,"VV25WLbfmkcd":418891246657,"VX3nVcL7Mtlw":518095722284,"8WhBB0sJqBEO":118060796767,"niWOosbKkfW8":981870861163,"V44cfappPdwU":555528276458,"5BtC2YnsHuyp":684513983682,"4aJUdNFHhMJS":647332199618,"0TvmPuwLSdct":804340302743,"m3G1fcOK1gdv":48655239555,"FIkG5cyS6BZT":349368184838,"JQXQZsGLNaE3":700343777327,"lZZoi9jpYGaY":847331295709,"uvWmzpOOyiio":173788454071,"IrufZS4gPjbi":436139210949,"qNde5F4C2E6X":483025790522,"FWVLJQ2GPcWq":945452573192,"zrGqE0W8AFDI":666378542568,"nqFVIddkacL2":829084547496,"wSoe3RaPvhZA":883944899205,"5JkqLX9m7dS3":234345601191,"Xr10NbiZCaa8":141866217726,"OCr91BhbXXGT":398896882095,"uY8NRhBD5fAl":721271223721,"1Gucb8iYEatJ":790786219391,"8SaXFDOuNmI4":718895646479,"kWzbB7ETY6QJ":922801755324,"hMjSpRWsRfIq":207274160762,"qxubVPhOOmMB":766598641171,"ZeAau4Q91Os7":535234921365,"nPlN7yispLoL":603205030031,"IHMrdSqcxT84":406742754436,"bvGx3yviVEfA":23775982975,"XouWjf4Wr1LA":537516420422,"gpkJoZLijKYH":776879977402,"mZY2x0fhE80P":574912943651,"KTZ1hmlwEruF":953503703774,"Owbfc7C3rX2B":763343279288,"KqQrZxz97HHo":83874433946,"wMmh0OvdmaZ7":7732682706,"ymSf4NJjFJan":975498618069,"VOh1ibKBpRIe":1258076382,"t6OQggluq2qo":710366386105,"HazWmN4a0ECx":480006274172,"2N1wojLY4tVc":514252513776,"Wf9PzPIba65i":9641117666,"Zz2MOlCxDhLl":707604547936} diff --git a/benchmark/src/Fixtures/data/ldjson.json b/benchmark/src/Fixtures/data/ldjson.json new file mode 100644 index 000000000..3b1421232 --- /dev/null +++ b/benchmark/src/Fixtures/data/ldjson.json @@ -0,0 +1 @@ +{"text":"@wildfits you're not getting one.....","in_reply_to_status_id":22773233453,"retweet_count":null,"contributors":null,"created_at":"Thu Sep 02 19:38:18 +0000 2010","geo":null,"source":"web","coordinates":null,"in_reply_to_screen_name":"wildfits","truncated":false,"entities":{"user_mentions":[{"indices":[0,9],"screen_name":"wildfits","name":"Mairin Goetzinger","id":41832464}],"urls":[],"hashtags":[]},"retweeted":false,"place":null,"user":{"friends_count":179,"profile_sidebar_fill_color":"7a7a7a","location":"Minneapols, MN/Brookings SD","verified":false,"follow_request_sent":null,"favourites_count":0,"profile_sidebar_border_color":"a3a3a3","profile_image_url":"http://a1.twimg.com/profile_images/1110614677/Screen_shot_2010-08-25_at_10.12.40_AM_normal.png","geo_enabled":false,"created_at":"Sun Aug 17 00:23:13 +0000 2008","description":"graphic designer + foodie, with a love of music, movies, running, design, + the outdoors!","time_zone":"Mountain Time (US & Canada)","url":"http://jessiefarris.com/","screen_name":"jessiekf","notifications":null,"profile_background_color":"303030","listed_count":1,"lang":"en"}} diff --git a/benchmark/src/Fixtures/data/small_doc.json b/benchmark/src/Fixtures/data/small_doc.json new file mode 100644 index 000000000..bcee768fd --- /dev/null +++ b/benchmark/src/Fixtures/data/small_doc.json @@ -0,0 +1 @@ +{"oggaoR4O":"miNVpaKW","Vxri7mmI":"CS5VwrwN","IQ8K4ZMG":"Oq5Csk1w","WzI8s1W0":"ZPm57dhu","E5Aj2zB3":"gxUpzIjg","3MXe8Wi7":"Smo9whci","PHPSSV51":"TW34kfzq","CxSCo4jD":55336395,"5C8GSDC5":41992681,"fC63DsLR":72188733,"l6e0U4bR":46660880,"TLRpkltp":3527055,"Ph9CZN5L":74094448} diff --git a/benchmark/src/Fixtures/data/tweet.json b/benchmark/src/Fixtures/data/tweet.json new file mode 100644 index 000000000..6125dc318 --- /dev/null +++ b/benchmark/src/Fixtures/data/tweet.json @@ -0,0 +1,66 @@ +{ + "text": "@wildfits you're not getting one.....", + "in_reply_to_status_id": 22773233453, + "retweet_count": null, + "contributors": null, + "created_at": "Thu Sep 02 19:38:18 +0000 2010", + "geo": null, + "source": "web", + "coordinates": null, + "in_reply_to_screen_name": "wildfits", + "truncated": false, + "entities": { + "user_mentions": [ + { + "indices": [ + 0, + 9 + ], + "screen_name": "wildfits", + "name": "Mairin Goetzinger", + "id": 41832464 + } + ], + "urls": [], + "hashtags": [] + }, + "retweeted": false, + "place": null, + "user": { + "friends_count": 179, + "profile_sidebar_fill_color": "7a7a7a", + "location": "Minneapols, MN/Brookings SD", + "verified": false, + "follow_request_sent": null, + "favourites_count": 0, + "profile_sidebar_border_color": "a3a3a3", + "profile_image_url": "http://a1.twimg.com/profile_images/1110614677/Screen_shot_2010-08-25_at_10.12.40_AM_normal.png", + "geo_enabled": false, + "created_at": "Sun Aug 17 00:23:13 +0000 2008", + "description": "graphic designer + foodie, with a love of music, movies, running, design, + the outdoors!", + "time_zone": "Mountain Time (US & Canada)", + "url": "http://jessiefarris.com/", + "screen_name": "jessiekf", + "notifications": null, + "profile_background_color": "303030", + "listed_count": 1, + "lang": "en", + "profile_background_image_url": "http://a3.twimg.com/profile_background_images/133733613/Picture_4.png", + "statuses_count": 1010, + "following": null, + "profile_text_color": "d9a980", + "protected": false, + "show_all_inline_media": false, + "profile_background_tile": true, + "name": "Jessie Farris", + "contributors_enabled": false, + "profile_link_color": "363636", + "followers_count": 218, + "id": 15878015, + "profile_use_background_image": true, + "utc_offset": -25200 + }, + "favorited": false, + "in_reply_to_user_id": 41832464, + "id": 22824602300 +} diff --git a/benchmark/src/ReadLargeDocumentBench.php b/benchmark/src/ReadLargeDocumentBench.php new file mode 100644 index 000000000..bdf5b021d --- /dev/null +++ b/benchmark/src/ReadLargeDocumentBench.php @@ -0,0 +1,201 @@ +drop(); + + $document = Data::readJsonFile(Data::LARGE_FILE_PATH); + + $documents = array_fill(0, 10, $document); + + $collection->insertMany($documents); + } + + public function provideParams(): Generator + { + yield 'Driver default typemap' => [ + 'codec' => null, + 'typeMap' => null, + 'accessor' => 'object', + ]; + + yield 'Array typemap' => [ + 'codec' => null, + 'typeMap' => ['root' => 'array', 'array' => 'array', 'document' => 'array'], + 'accessor' => 'array', + ]; + + yield 'Library default typemap' => [ + 'codec' => null, + 'typeMap' => [ + 'array' => BSONArray::class, + 'document' => BSONDocument::class, + 'root' => BSONDocument::class, + ], + 'accessor' => 'object', + ]; + + yield 'Raw BSON' => [ + 'codec' => null, + 'typeMap' => ['root' => 'bson'], + 'accessor' => 'bson', + ]; + + yield 'Codec (pass thru)' => [ + 'codec' => new PassThruCodec(), + 'typeMap' => null, + 'accessor' => 'bson', + ]; + + yield 'Codec (to object)' => [ + 'codec' => new ToObjectCodec(), + 'typeMap' => null, + 'accessor' => 'object', + ]; + } + + #[ParamProviders('provideParams')] + public function benchCursorToArray(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + $collection->find([], $options)->toArray(); + } + + #[ParamProviders('provideParams')] + public function benchAccessId(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + // Exhaust cursor and access identifier on each document + foreach ($collection->find([], $options) as $document) { + $this->accessId($document, $params['accessor']); + } + } + + #[ParamProviders('provideParams')] + public function benchAccessFirstField(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + // Exhaust cursor and access identifier on each document + foreach ($collection->find([], $options) as $document) { + $this->accessFirstField($document, $params['accessor']); + } + } + + #[ParamProviders('provideParams')] + public function benchAccessLastField(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + // Exhaust cursor and access identifier on each document + foreach ($collection->find([], $options) as $document) { + $this->accessLastField($document, $params['accessor']); + } + } + + private function accessId(array|object $document, string $accessor): void + { + switch ($accessor) { + case 'array': + assert(is_array($document)); + $document['_id']; + break; + + case 'bson': + assert($document instanceof Document); + $document->get('_id'); + break; + + case 'object': + assert(is_object($document)); + $document->_id; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid accessor "%s"', $accessor)); + } + } + + private function accessLastField(array|object $document, string $accessor): void + { + switch ($accessor) { + case 'array': + assert(is_array($document)); + $document['Zz2MOlCxDhLl']; + break; + + case 'bson': + assert($document instanceof Document); + $document->get('Zz2MOlCxDhLl'); + break; + + case 'object': + assert(is_object($document)); + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + $document->Zz2MOlCxDhLl; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid accessor "%s"', $accessor)); + } + } + + private function accessFirstField(array|object $document, string $accessor): void + { + switch ($accessor) { + case 'array': + assert(is_array($document)); + $document['qx3MigjubFSm']; + break; + + case 'bson': + assert($document instanceof Document); + $document->get('qx3MigjubFSm'); + break; + + case 'object': + assert(is_object($document)); + $document->qx3MigjubFSm; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid accessor "%s"', $accessor)); + } + } +} diff --git a/benchmark/src/ReadMultipleDocumentsBench.php b/benchmark/src/ReadMultipleDocumentsBench.php new file mode 100644 index 000000000..74dbafe13 --- /dev/null +++ b/benchmark/src/ReadMultipleDocumentsBench.php @@ -0,0 +1,164 @@ +drop(); + + $tweet = Data::readJsonFile(Data::TWEET_FILE_PATH); + + $documents = array_fill(0, 1000, $tweet); + + $collection->insertMany($documents); + } + + public function provideParams(): Generator + { + yield 'Driver default typemap' => [ + 'codec' => null, + 'typeMap' => [], + 'accessor' => 'object', + ]; + + yield 'Array typemap' => [ + 'codec' => null, + 'typeMap' => ['root' => 'array', 'array' => 'array', 'document' => 'array'], + 'accessor' => 'array', + ]; + + yield 'Library default typemap' => [ + 'codec' => null, + 'typeMap' => [ + 'array' => BSONArray::class, + 'document' => BSONDocument::class, + 'root' => BSONDocument::class, + ], + 'accessor' => 'object', + ]; + + yield 'Raw BSON' => [ + 'codec' => null, + 'typeMap' => ['root' => 'bson'], + 'accessor' => 'bson', + ]; + + yield 'Codec (pass thru)' => [ + 'codec' => new PassThruCodec(), + 'typeMap' => null, + 'accessor' => 'bson', + ]; + + yield 'Codec (to object)' => [ + 'codec' => new ToObjectCodec(), + 'typeMap' => null, + 'accessor' => 'object', + ]; + } + + #[ParamProviders('provideParams')] + public function benchCursorToArray(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + $collection->find([], $options)->toArray(); + } + + #[ParamProviders('provideParams')] + public function benchAccessId(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + // Exhaust cursor and access identifier on each document + foreach ($collection->find([], $options) as $document) { + $this->accessId($document, $params['accessor']); + } + } + + #[ParamProviders('provideParams')] + public function benchAccessNestedItem(array $params): void + { + $options = array_intersect_key($params, ['codec' => true, 'typeMap' => true]); + + $collection = Utils::getCollection(); + + // Exhaust cursor and access identifier on each document + foreach ($collection->find([], $options) as $document) { + $this->accessNestedItem($document, $params['accessor']); + } + } + + private function accessId(array|object $document, string $accessor): void + { + switch ($accessor) { + case 'array': + assert(is_array($document)); + $document['_id']; + break; + + case 'bson': + assert($document instanceof Document); + $document->get('_id'); + break; + + case 'object': + assert(is_object($document)); + $document->_id; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid accessor "%s"', $accessor)); + } + } + + private function accessNestedItem(array|object $document, string $accessor): void + { + switch ($accessor) { + case 'array': + assert(is_array($document)); + $document['entities']['user_mentions'][0]['screen_name']; + break; + + case 'bson': + assert($document instanceof Document); + $document->get('entities')->get('user_mentions')->get(0)->get('screen_name'); + break; + + case 'object': + assert(is_object($document)); + $document->entities->user_mentions[0]->screen_name; + break; + + default: + throw new InvalidArgumentException(sprintf('Invalid accessor "%s"', $accessor)); + } + } +} diff --git a/benchmark/src/Utils.php b/benchmark/src/Utils.php new file mode 100644 index 000000000..b77699d5c --- /dev/null +++ b/benchmark/src/Utils.php @@ -0,0 +1,53 @@ + true]); + } + + public static function getDatabase(): Database + { + return self::$database ??= self::getClient()->selectDatabase(self::getDatabaseName()); + } + + public static function getCollection(): Collection + { + return self::$collection ??= self::getDatabase()->selectCollection(self::getCollectionName()); + } + + public static function getUri(): string + { + return getenv('MONGODB_URI') ?: 'mongodb://localhost:27017/'; + } + + public static function getDatabaseName(): string + { + return getenv('MONGODB_DATABASE') ?: 'phplib_test'; + } + + public static function getCollectionName(): string + { + return 'perftest'; + } + + public static function reset(): void + { + self::$client = null; + self::$database = null; + self::$collection = null; + } +} diff --git a/composer.json b/composer.json index 8e55fabff..285255e48 100644 --- a/composer.json +++ b/composer.json @@ -6,38 +6,61 @@ "license": "Apache-2.0", "authors": [ { "name": "Andreas Braun", "email": "andreas.braun@mongodb.com" }, - { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" } + { "name": "Jeremy Mikola", "email": "jmikola@gmail.com" }, + { "name": "Jérôme Tamarelle", "email": "jerome.tamarelle@mongodb.com" } ], "require": { - "php": "^7.2 || ^8.0", - "ext-hash": "*", - "ext-json": "*", - "ext-mongodb": "^1.15.0", - "jean85/pretty-package-versions": "^1.2 || ^2.0.1", - "symfony/polyfill-php80": "^1.19" + "php": "^8.1", + "ext-mongodb": "^2.1", + "composer-runtime-api": "^2.0", + "psr/log": "^1.1.4|^2|^3", + "symfony/polyfill-php85": "^1.32" }, "require-dev": { - "squizlabs/php_codesniffer": "^3.6", - "doctrine/coding-standard": "^9.0", - "symfony/phpunit-bridge": "^5.2", - "vimeo/psalm": "^4.28" + "doctrine/coding-standard": "^12.0", + "phpunit/phpunit": "^10.5.35", + "rector/rector": "^2.1.4", + "squizlabs/php_codesniffer": "^3.7", + "vimeo/psalm": "6.5.*" + }, + "replace": { + "mongodb/builder": "*" }, "autoload": { "psr-4": { "MongoDB\\": "src/" }, "files": [ "src/functions.php" ] }, "autoload-dev": { - "psr-4": { "MongoDB\\Tests\\": "tests/" }, - "files": [ "tests/PHPUnit/Functions.php" ] + "psr-4": { + "MongoDB\\Tests\\": "tests/" + } + }, + "scripts": { + "pre-install-cmd": "git submodule update --init", + "pre-update-cmd": "git submodule update --init", + "bench": "cd benchmark && composer update && vendor/bin/phpbench run --report=aggregate", + "checks": [ + "@check:cs", + "@check:psalm", + "@check:rector" + ], + "check:cs": "phpcs", + "check:psalm": "psalm", + "check:rector": "rector --ansi --dry-run", + "fix:cs": "phpcbf", + "fix:psalm:baseline": "psalm --set-baseline=psalm-baseline.xml", + "fix:rector": "rector process --ansi", + "test": "phpunit" }, "extra": { "branch-alias": { - "dev-master": "1.15.x-dev" + "dev-master": "1.x-dev" } }, "config": { "allow-plugins": { "dealerdirect/phpcodesniffer-composer-installer": true - } + }, + "sort-packages": true } } diff --git a/docs/.static/.mongodb b/docs/.static/.mongodb deleted file mode 100644 index e69de29bb..000000000 diff --git a/docs/faq.txt b/docs/faq.txt deleted file mode 100644 index 72801e161..000000000 --- a/docs/faq.txt +++ /dev/null @@ -1,182 +0,0 @@ -========================== -Frequently Asked Questions -========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Common Extension Installation Errors ------------------------------------- - -PHP Headers Not Found -~~~~~~~~~~~~~~~~~~~~~ - -For example: - -.. code-block:: none - - /private/tmp/pear/install/mongodb/php_phongo.c:24:10: fatal error: 'php.h' file not found - - #include - ^~~~~~~ - -This error indicates that PHP's build system cannot find the necessary headers. -All PHP extensions require headers in order to compile. Additionally, those -headers must correspond to the PHP runtime for which the extension will be used. -Generally, the ``phpize`` command (invoked by ``pecl``) will ensure that the -extension builds with the correct headers. - -Note that the mere presence of a PHP runtime does not mean that headers are -available. On various Linux distributions, headers are often published under a -separate ``php-dev`` or ``php-devel`` package. On macOS, the default PHP runtime -does not include headers and users typically need to install PHP (and headers) -via `Homebrew `_ in order to build an extension. - -Multiple PHP Runtimes Installed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If your system has multiple versions of PHP installed, each version will have -its own ``pecl`` and ``phpize`` commands. Additionally, each PHP runtime may -have separate ``php.ini`` files for each SAPI (e.g. FPM, CLI). If the extension -has been installed but is not available at runtime, double-check that you have -used the correct ``pecl`` command and have modified the appropriate ``php.ini`` -file(s). - -If there is any doubt about the ``php.ini`` file being used by a PHP runtime, -you should examine the output of :php:`phpinfo() ` for that particular -SAPI. Additionally, :php:`php_ini_loaded_file() ` and -:php:`php_ini_scanned_files() ` may be used to determine -exactly which INI files have been loaded by PHP. - -To debug issues with the extension not being loaded, you can use the -``detect-extension`` script provided in the tools directory. You can run this -script from the CLI or include it in a script accessible via your web server. -The tool will point out potential issues and installation instructions for your -system. Assuming you've installed the library through Composer, you can call the -script from the vendor directory: - -.. code-block:: none - - php vendor/mongodb/mongodb/tools/detect-extension.php - -If you want to check configuration for a web server SAPI, include the file in -a script accessible from the web server and open it in your browser. Remember to -wrap the script in ``
`` tags to properly format its output:
-
-.. code-block:: php
-
-   
- -Loading an Incompatible DLL on Windows -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Windows binaries are available for various combinations of PHP version, -thread safety (TS or NTS), and architecture (x86 or x64). Failure to select the -correct binary will result in an error when attempting to load the extension DLL -at runtime: - -.. code-block:: none - - PHP Warning: PHP Startup: Unable to load dynamic library 'mongodb' - -Ensure that you have downloaded a DLL that corresponds to the following PHP -runtime properties: - -- PHP version (``PHP_VERSION``) -- Thread safety (``PHP_ZTS``) -- Architecture (``PHP_INT_SIZE``) - -In addition to the aforementioned constants, these properties can also be -inferred from :php:`phpinfo() `. If your system has multiple PHP -runtimes installed, double-check that you are examining the ``phpinfo()`` output -for the correct environment. - -The aforementioned ``detect-extension`` script can also be used to determine the -appropriate DLL for your PHP environment. - -Server Selection Failures -------------------------- - -The following are all examples of -:doc:`Server Selection ` failures: - -.. code-block:: none - - No suitable servers found (`serverSelectionTryOnce` set): - [connection refused calling hello on 'a.example.com:27017'] - [connection refused calling hello on 'b.example.com:27017'] - - No suitable servers found: `serverSelectionTimeoutMS` expired: - [socket timeout calling hello on 'example.com:27017'] - - No suitable servers found: `serverSelectionTimeoutMS` expired: - [connection timeout calling hello on 'a.example.com:27017'] - [connection timeout calling hello on 'b.example.com:27017'] - [TLS handshake failed: -9806 calling hello on 'c.example.com:27017'] - - No suitable servers found: `serverselectiontimeoutms` timed out: - [TLS handshake failed: certificate verify failed (64): IP address mismatch calling hello on 'a.example.com:27017'] - [TLS handshake failed: certificate verify failed (64): IP address mismatch calling hello on 'b.example.com:27017'] - -These errors typically manifest as a -:php:`MongoDB\\Driver\\Exception\\ConnectionTimeoutException ` -exception from the driver. The actual exception messages originate from -libmongoc, which is the underlying library used by the PHP driver. Since these -messages can take many forms, it's helpful to break down the structure of the -message so you can better diagnose errors in your application. - -Messages will typically start with "No suitable servers found". The next part of -the message indicates *how* server selection failed. By default, the PHP driver -avoids a server selection loop and instead makes a single attempt (according to -the ``serverSelectionTryOnce`` connection string option). If the driver is -configured to utilize a loop, a message like "serverSelectionTimeoutMS expired" -will tell us that we exhausted its time limit. - -The last component of the message tells us *why* server selection failed, and -includes one or more errors directly from the topology scanner, which is the -service responsible for connecting to and monitoring each host. Any host that -last experienced an error during monitoring will be included in this list. These -messages typically originate from low-level socket or TLS functions. - -The following is not meant to be exhaustive, but will hopefully point you in the -right direction for analyzing the contributing factor(s) for a server selection -failure: - -- "connection refused" likely indicates that the remote host is not listening on - the expected port. -- "connection timeout" could indicate a routing or firewall issue, or perhaps - a timeout due to latency. -- "socket timeout" suggests that a connection *was* established at some point - but was dropped or otherwise timeout out due to latency. -- "TLS handshake failed" suggests something related to TLS or OCSP verification - and is sometimes indicative of misconfigured TLS certificates. - -In the case of a connection failure, you can use the ``connect`` tool to try and -receive more information. This tool attempts to connect to each host in a -connection string using socket functions to see if it is able to establish a -connection, send, and receive data. The tool takes the connection string to a -MongoDB deployment as its only argument. Assuming you've installed the library -through Composer, you would call the script from the vendor directory: - -.. code-block:: none - - php vendor/mongodb/mongodb/tools/connect.php mongodb://127.0.0.1:27017 - -In case the server does not accept connections, the output will look like this: - -.. code-block:: none - - Looking up MongoDB at mongodb://127.0.0.1:27017 - Found 1 host(s) in the URI. Will attempt to connect to each. - - Could not connect to 127.0.0.1:27017: Connection refused - -.. note:: - - The tool only supports the ``mongodb://`` URI schema. Using the - ``mongodb+srv`` scheme is not supported. diff --git a/docs/includes/apiargs-MongoDBClient-common-option.yaml b/docs/includes/apiargs-MongoDBClient-common-option.yaml deleted file mode 100644 index 4ffc52a92..000000000 --- a/docs/includes/apiargs-MongoDBClient-common-option.yaml +++ /dev/null @@ -1,34 +0,0 @@ -arg_name: option -name: readConcern -type: :php:`MongoDB\\Driver\\ReadConcern ` -description: | - :manual:`Read concern ` to use for the operation. - Defaults to the client's read concern. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: readPreference -type: :php:`MongoDB\\Driver\\ReadPreference ` -description: | - :manual:`Read preference ` to use for the - operation. Defaults to the client's read preference. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: typeMap ---- -arg_name: option -name: writeConcern -type: :php:`MongoDB\\Driver\\WriteConcern ` -description: | - :manual:`Write concern ` to use for the operation. - Defaults to the client's write concern. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml b/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml deleted file mode 100644 index 8406bd091..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-construct-driverOptions.yaml +++ /dev/null @@ -1,189 +0,0 @@ -arg_name: option -name: autoEncryption -type: array -description: | - Options to configure client-side field-level encryption in the driver. The - encryption options are documented in the :php:`extension documentation - `. - For the ``keyVaultClient`` option, you may pass a :phpclass:`MongoDB\\Client` - instance, which will be unwrapped to provide a :php:`MongoDB\\Driver\\Manager ` - to the extension. - - .. versionadded:: 1.6 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: driver -type: array -description: | - Additional driver metadata to be passed on to the server handshake. This is an - array containing ``name``, ``version``, and ``platform`` fields: - - .. code-block:: php - - [ - 'name' => 'my-driver', - 'version' => '1.2.3-dev', - 'platform' => 'some-platform', - ] - - .. note:: - - This feature is primarily designed for custom drivers and ODMs, which may - want to identify themselves to the server for diagnostic purposes. - Applications should use the ``appName`` URI option instead of driver - metadata. - - .. versionadded:: 1.7 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: serverApi -type: :php:`MongoDB\\Driver\\ServerApi ` -description: | - Used to declare an API version on the client. See the - :manual:`Stable API tutorial ` for usage. - - .. versionadded:: 1.9 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: typeMap -type: array -description: | - Default :php:`type map - ` - to apply to cursors, which determines how BSON documents are converted to PHP - values. The |php-library| uses the following type map by default: - - .. code-block:: php - - [ - 'array' => 'MongoDB\Model\BSONArray', - 'document' => 'MongoDB\Model\BSONDocument', - 'root' => 'MongoDB\Model\BSONDocument', - ] -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: allow_invalid_hostname -type: boolean -description: | - Disables hostname validation if ``true``. Defaults to ``false``. - - Allowing invalid hostnames may expose the driver to a `man-in-the-middle - attack `_. - - .. deprecated:: 1.6 - This option has been deprecated. Use the ``tlsAllowInvalidHostnames`` URI - option instead. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: ca_dir -type: string -description: | - Path to a correctly hashed certificate directory. The system certificate store - will be used by default. - - Falls back to the deprecated ``capath`` SSL context option if not specified. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: ca_file -type: string -description: | - Path to a certificate authority file. The system certificate store will be - used by default. - - Falls back to the deprecated ``cafile`` SSL context option if not specified. - - .. deprecated:: 1.6 - This option has been deprecated. Use the ``tlsCAFile`` URI option instead. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: crl_file -type: string -description: | - Path to a certificate revocation list file. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: pem_file -type: string -description: | - Path to a PEM encoded certificate to use for client authentication. - - Falls back to the deprecated ``local_cert`` SSL context option if not - specified. - - .. deprecated:: 1.6 - This option has been deprecated. Use the ``tlsCertificateKeyFile`` URI - option instead. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: pem_pwd -type: string -description: | - Passphrase for the PEM encoded certificate (if applicable). - - Falls back to the deprecated ``passphrase`` SSL context option if not - specified. - - .. deprecated:: 1.6 - This option has been deprecated. Use the ``tlsCertificateKeyFilePassword`` - URI option instead. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: weak_cert_validation -type: boolean -description: | - Disables certificate validation ``true``. Defaults to ``false``. - - Falls back to the deprecated ``allow_self_signed`` SSL context option if not - specified. - - .. deprecated:: 1.6 - This option has been deprecated. Use the ``tlsAllowInvalidCertificates`` - URI option instead. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: context -type: resource -description: | - :php:`SSL context options ` to be used as fallbacks - for other driver options (as specified). Note that the driver does not consult - the default stream context. - - This option is supported for backwards compatibility, but should be considered - deprecated. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml b/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml deleted file mode 100644 index 62998287e..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-construct-param.yaml +++ /dev/null @@ -1,51 +0,0 @@ -arg_name: param -name: $uri -type: string -description: | - The URI of the standalone, replica set, or sharded cluster to which to - connect. Refer to :manual:`Connection String URI Format - ` in the MongoDB manual for more information. - - Defaults to ``"mongodb://127.0.0.1:27017"`` if unspecified. - - Any special characters in the URI components need to be encoded according to - `RFC 3986 `_. This is particularly - relevant to the username and password, which can often include special - characters such as ``@``, ``:``, or ``%``. When connecting via a Unix domain - socket, the socket path may contain special characters such as slashes and - must be encoded. The :php:`rawurlencode() ` function may be used - to encode constituent parts of the URI. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: param -name: $uriOptions -type: array -description: | - Specifies additional URI options, such as authentication credentials or query - string parameters. The options specified in ``$uriOptions`` take precedence - over any analogous options present in the ``$uri`` string and do not need to - be encoded according to `RFC 3986 `_. - - Refer to the :php:`MongoDB\\Driver\\Manager::__construct() - ` extension reference and :manual:`MongoDB - connection string ` documentation for accepted - options. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: param -name: $driverOptions -type: array -description: | - Specify driver-specific options, such as SSL options. In addition to any - options supported by the :php:`extension `, the - |php-library| allows you to specify a default :php:`type map - ` - to apply to the cursors it creates. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBClient-method-createClientEncryption-param.yaml b/docs/includes/apiargs-MongoDBClient-method-createClientEncryption-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-createClientEncryption-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml b/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml deleted file mode 100644 index be0a19aa9..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-option.yaml +++ /dev/null @@ -1,25 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBClient-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-param.yaml b/docs/includes/apiargs-MongoDBClient-method-dropDatabase-param.yaml deleted file mode 100644 index 07b768348..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-dropDatabase-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $databaseName -replacement: - action: " to drop" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBClient-method-get-param.yaml b/docs/includes/apiargs-MongoDBClient-method-get-param.yaml deleted file mode 100644 index e9d3ccc6c..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-get-param.yaml +++ /dev/null @@ -1,6 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $databaseName -replacement: - action: " to select" -... diff --git a/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml b/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml deleted file mode 100644 index 38baf93ad..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-listDatabases-option.yaml +++ /dev/null @@ -1,47 +0,0 @@ -arg_name: option -name: authorizedDatabases -type: boolean -description: | - A flag that determines which databases are returned based on the user - privileges when access control is enabled. For more information, see the - `listDatabases command documentation `_. - - For servers < 4.0.5, this option is ignored. - - .. versionadded:: 1.7 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: filter -type: array|object -description: | - A query expression to filter the list of databases. - - You can specify a query expression for database fields (e.g. ``name``, - ``sizeOnDisk``, ``empty``). - - .. versionadded:: 1.3 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 diff --git a/docs/includes/apiargs-MongoDBClient-method-listDatabases-param.yaml b/docs/includes/apiargs-MongoDBClient-method-listDatabases-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-listDatabases-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBClient-method-selectCollection-option.yaml b/docs/includes/apiargs-MongoDBClient-method-selectCollection-option.yaml deleted file mode 100644 index d81d31811..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-selectCollection-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "collection" - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "collection" - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "collection" - parent: "client" -... diff --git a/docs/includes/apiargs-MongoDBClient-method-selectCollection-param.yaml b/docs/includes/apiargs-MongoDBClient-method-selectCollection-param.yaml deleted file mode 100644 index 99c764460..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-selectCollection-param.yaml +++ /dev/null @@ -1,16 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $databaseName -replacement: - action: " containing the collection to select" ---- -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - action: " to select" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBClient-method-selectDatabase-option.yaml b/docs/includes/apiargs-MongoDBClient-method-selectDatabase-option.yaml deleted file mode 100644 index e4ffd3c66..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-selectDatabase-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "database" - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "database" - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "client" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "database" - parent: "client" -... diff --git a/docs/includes/apiargs-MongoDBClient-method-selectDatabase-param.yaml b/docs/includes/apiargs-MongoDBClient-method-selectDatabase-param.yaml deleted file mode 100644 index 8e5f9d45f..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-selectDatabase-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $databaseName -replacement: - action: " to select" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml b/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml deleted file mode 100644 index cad9c3f75..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-watch-option.yaml +++ /dev/null @@ -1,71 +0,0 @@ ---- -source: - file: apiargs-method-watch-option.yaml - ref: batchSize ---- -source: - file: apiargs-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocument ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocumentBeforeChange -post: | - .. versionadded: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: maxAwaitTimeMS ---- -source: - file: apiargs-MongoDBClient-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBClient-common-option.yaml - ref: readPreference -post: | - This is used for both the initial change stream aggregation and for - server selection during an automatic resume. ---- -source: - file: apiargs-method-watch-option.yaml - ref: resumeAfter ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-method-watch-option.yaml - ref: showExpandedEvents -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAfter -post: | - .. versionadded: 1.5 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAtOperationTime ---- -source: - file: apiargs-MongoDBClient-common-option.yaml - ref: typeMap -... diff --git a/docs/includes/apiargs-MongoDBClient-method-watch-param.yaml b/docs/includes/apiargs-MongoDBClient-method-watch-param.yaml deleted file mode 100644 index b00e450cf..000000000 --- a/docs/includes/apiargs-MongoDBClient-method-watch-param.yaml +++ /dev/null @@ -1,8 +0,0 @@ -source: - file: apiargs-method-watch-param.yaml - ref: $pipeline ---- -source: - file: apiargs-method-watch-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-common-option.yaml b/docs/includes/apiargs-MongoDBCollection-common-option.yaml deleted file mode 100644 index fb919ea42..000000000 --- a/docs/includes/apiargs-MongoDBCollection-common-option.yaml +++ /dev/null @@ -1,86 +0,0 @@ -arg_name: option -name: arrayFilters -type: array -description: | - An array of filter documents that determines which array elements to modify - for an update operation on an array field. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: bypassDocumentValidation -type: boolean -description: | - If ``true``, allows the write operation to circumvent document level - validation. Defaults to ``false``. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: collation -post: | - If the collation is unspecified but the collection has a default collation, - the operation uses the collation specified for the collection. If no - collation is specified for the collection or for the operation, MongoDB uses - the simple binary comparison used in prior versions for string comparisons. ---- -arg_name: option -name: readConcern -type: :php:`MongoDB\\Driver\\ReadConcern ` -description: | - :manual:`Read concern ` to use for the operation. - Defaults to the collection's read concern. - - It is not possible to specify a :manual:`read concern - ` for individual operations as part of a - transaction. Instead, set the ``readConcern`` option when starting the - transaction with :php:`startTransaction `. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: readPreference -type: :php:`MongoDB\\Driver\\ReadPreference ` -description: | - :manual:`Read preference ` to use for the - operation. Defaults to the collection's read preference. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "collection" ---- -arg_name: option -name: writeConcern -type: :php:`MongoDB\\Driver\\WriteConcern ` -description: | - :manual:`Write concern ` to use for the operation. - Defaults to the collection's write concern. - - It is not possible to specify a :manual:`write concern - ` for individual operations as part of a - transaction. Instead, set the ``writeConcern`` option when starting the - transaction with :php:`startTransaction `. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: upsert -type: boolean -description: | - If set to ``true``, creates a new document when no document matches the query - criteria. The default value is ``false``, which does not insert a new - document when no match is found. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBCollection-common-param.yaml b/docs/includes/apiargs-MongoDBCollection-common-param.yaml deleted file mode 100644 index 47100d19a..000000000 --- a/docs/includes/apiargs-MongoDBCollection-common-param.yaml +++ /dev/null @@ -1,33 +0,0 @@ -arg_name: param -name: $filter -type: array|object -description: | - The filter criteria that specifies the documents{{action}}. -interface: phpmethod -operation: ~ -optional: false -replacement: - action: "" ---- -arg_name: param -name: $replacement -type: array|object -description: | - The replacement document. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $update -type: array|object -description: | - Specifies the field and value combinations to update and any relevant update - operators. ``$update`` uses MongoDB's :method:`update operators - `. Starting with MongoDB 4.2, an `aggregation - pipeline `_ - can be passed as this parameter. -interface: phpmethod -operation: ~ -optional: false -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml deleted file mode 100644 index b3d19c3e5..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-aggregate-option.yaml +++ /dev/null @@ -1,84 +0,0 @@ -source: - file: apiargs-aggregate-option.yaml - ref: allowDiskUse ---- -source: - file: apiargs-aggregate-option.yaml - ref: batchSize ---- -source: - file: apiargs-aggregate-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. - - .. versionadded:: 1.3 ---- -source: - file: apiargs-aggregate-option.yaml - ref: explain -post: | - .. versionadded:: 1.4 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.9 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap ---- -arg_name: option -name: useCursor -type: boolean -description: | - Indicates whether the command will request that the server provide results - using a cursor. The default is ``true``. - - .. note:: - - MongoDB 3.6+ no longer supports returning results without a cursor (excluding - when the explain option is used) and specifying false for this option will - result in a server error. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -post: | - This only applies when a :ref:`$out ` or :ref:`$merge ` - stage is specified. -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-aggregate-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-aggregate-param.yaml deleted file mode 100644 index cbad3b49b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-aggregate-param.yaml +++ /dev/null @@ -1,14 +0,0 @@ -arg_name: param -name: $pipeline -type: array -description: | - Specifies an :manual:`aggregation pipeline ` - operation. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml deleted file mode 100644 index 25d8e24d3..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-option.yaml +++ /dev/null @@ -1,44 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -arg_name: option -name: ordered -type: boolean -description: | - If ``true``: when a single write fails, the operation will stop without - performing the remaining writes and throw an exception. - - If ``false``: when a single write fails, the operation will continue with the - remaining writes, if any, and throw an exception. - - The default is ``true``. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-param.yaml deleted file mode 100644 index 79423a637..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-bulkWrite-param.yaml +++ /dev/null @@ -1,37 +0,0 @@ -arg_name: param -name: $operations -type: array -description: | - An array containing the write operations to perform. - :phpmethod:`MongoDB\\Collection::bulkWrite()` supports - :phpmethod:`deleteMany() `, - :phpmethod:`deleteOne() `, - :phpmethod:`insertOne() `, - :phpmethod:`replaceOne() `, - :phpmethod:`updateMany() `, and - :phpmethod:`updateOne() ` operations in the - following array structure: - - .. code-block:: php - - [ - [ 'deleteMany' => [ $filter ] ], - [ 'deleteOne' => [ $filter ] ], - [ 'insertOne' => [ $document ] ], - [ 'replaceOne' => [ $filter, $replacement, $options ] ], - [ 'updateMany' => [ $filter, $update, $options ] ], - [ 'updateOne' => [ $filter, $update, $options ] ], - ] - - Arguments correspond to the respective operation methods. However, the - ``writeConcern`` option is specified as a top-level option to - :phpmethod:`MongoDB\\Collection::bulkWrite()` instead of each individual - operation. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-construct-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-construct-option.yaml deleted file mode 100644 index 7d422ca05..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-construct-option.yaml +++ /dev/null @@ -1,25 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "collection" - parent: "manager" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "collection" - parent: "manager" ---- -source: - file: apiargs-MongoDBClient-method-construct-driverOptions.yaml - ref: typeMap ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "collection" - parent: "manager" -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-construct-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-construct-param.yaml deleted file mode 100644 index 0827800ba..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-construct-param.yaml +++ /dev/null @@ -1,16 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $manager ---- -source: - file: apiargs-common-param.yaml - ref: $databaseName ---- -source: - file: apiargs-common-param.yaml - ref: $collectionName ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml deleted file mode 100644 index 3d839e04a..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-count-option.yaml +++ /dev/null @@ -1,64 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: hint -type: string|array|object -description: | - The index to use. Specify either the index name as a string or the index key - pattern as a document. If specified, then the query system will only consider - plans using the hinted index. - - .. versionchanged:: 1.2 - If a document is provided, it is passed to the command as-is. Previously, - the library would convert the key pattern to an index name. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: limit -type: integer -description: | - The maximum number of matching documents to return. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -arg_name: option -name: skip -type: integer -description: | - The number of matching documents to skip before returning results. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-count-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-count-param.yaml deleted file mode 100644 index e18c616bc..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-count-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: true -replacement: - action: " to count" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml deleted file mode 100644 index da1f01138..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-option.yaml +++ /dev/null @@ -1,56 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. ---- -arg_name: option -name: hint -type: string|array|object -description: | - The index to use. Specify either the index name as a string or the index key - pattern as a document. If specified, then the query system will only consider - plans using the hinted index. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: limit -type: integer -description: | - The maximum number of matching documents to return. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -arg_name: option -name: skip -type: integer -description: | - The number of matching documents to skip before returning results. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-countDocuments-param.yaml deleted file mode 100644 index e18c616bc..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-countDocuments-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: true -replacement: - action: " to count" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml deleted file mode 100644 index 182837674..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndex-option.yaml +++ /dev/null @@ -1,117 +0,0 @@ -arg_name: option -name: commitQuorum -type: string|integer -description: | - Specifies how many data-bearing members of a replica set, including the - primary, must complete the index builds successfully before the primary marks - the indexes as ready. - - This option accepts the same values for the ``w`` field in a write concern - plus ``"votingMembers"``, which indicates all voting data-bearing nodes. - - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.7 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: unique -type: boolean -description: | - Creates a :manual:`unique ` index. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation -pre: | - Specifies the :manual:`collation - ` for the index. ---- -arg_name: option -name: partialFilterExpression -type: array|object -description: | - Creates a :manual:`partial ` index. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: sparse -type: boolean -description: | - Creates a :manual:`sparse ` index. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: expireAfterSeconds -type: integer -description: | - Creates a :manual:`TTL ` index. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS -post: | - .. versionadded:: 1.3 ---- -arg_name: option -name: name -type: string -description: | - A name that uniquely identifies the index. By default, MongoDB creates index - names based on the key. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: background -type: string -description: | - Instructs MongoDB to build the index :manual:`as a background - ` process. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: 2dsphereIndexVersion -type: integer -description: | - Overrides the server's default version for a :manual:`2dsphere - ` index. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndex-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndex-param.yaml deleted file mode 100644 index c2979d913..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndex-param.yaml +++ /dev/null @@ -1,20 +0,0 @@ -arg_name: param -name: $key -type: array|object -description: | - Specifies the field or fields to index and the index order. - - For example, the following specifies a descending index on the ``username`` - field: - - .. code-block:: php - - [ 'username' => -1 ] -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml deleted file mode 100644 index 54c33bfa0..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-option.yaml +++ /dev/null @@ -1,29 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-MongoDBCollection-method-createIndex-option.yaml - ref: commitQuorum ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-createIndexes-param.yaml deleted file mode 100644 index e98d9aad0..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-createIndexes-param.yaml +++ /dev/null @@ -1,23 +0,0 @@ -arg_name: param -name: $indexes -type: array -description: | - The indexes to create on the collection. - - For example, the following specifies a unique index on the ``username`` field - and a compound index on the ``email`` and ``createdAt`` fields: - - .. code-block:: php - - [ - [ 'key' => [ 'username' => -1 ], 'unique' => true ], - [ 'key' => [ 'email' => 1, 'createdAt' => 1 ] ], - ] -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml deleted file mode 100644 index 110911f51..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-option.yaml +++ /dev/null @@ -1,38 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.4+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.7 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteMany-param.yaml deleted file mode 100644 index 92797eb53..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteMany-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to delete" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml deleted file mode 100644 index 110911f51..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-option.yaml +++ /dev/null @@ -1,38 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.4+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.7 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-deleteOne-param.yaml deleted file mode 100644 index 92797eb53..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-deleteOne-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to delete" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml deleted file mode 100644 index 05a1b2853..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-distinct-option.yaml +++ /dev/null @@ -1,37 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - .. versionadded:: 1.5 -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-distinct-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-distinct-param.yaml deleted file mode 100644 index 37cd9b50b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-distinct-param.yaml +++ /dev/null @@ -1,20 +0,0 @@ -arg_name: param -name: $fieldName -type: string -description: | - The field for which to return distinct values. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: true -replacement: - action: " from which to retrieve the distinct values" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml deleted file mode 100644 index 5eb5efaba..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-drop-option.yaml +++ /dev/null @@ -1,31 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-dropCollection-option.yaml - ref: encryptedFields -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-drop-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-drop-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-drop-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml deleted file mode 100644 index b87e09b74..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-option.yaml +++ /dev/null @@ -1,31 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndex-param.yaml deleted file mode 100644 index 7cac6d1de..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndex-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -arg_name: param -name: $indexName -type: string| :phpclass:`MongoDB\\Model\\IndexInfo` -description: | - The name or model object of the index to drop. View the existing indexes on - the collection using the :phpmethod:`listIndexes() - ` method. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml deleted file mode 100644 index aff9f4d75..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-option.yaml +++ /dev/null @@ -1,29 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-dropIndexes-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml deleted file mode 100644 index 7289296e9..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-option.yaml +++ /dev/null @@ -1,25 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-estimateDocumentCount-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml deleted file mode 100644 index 650633bce..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-explain-option.yaml +++ /dev/null @@ -1,31 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - Defaults to the ``comment`` of the explained operation (if any). - - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -arg_name: option -name: verbosity -type: string -description: | - The verbosity level at which to run the command. See the :manual:`explain - ` command for more information. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-explain-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-explain-param.yaml deleted file mode 100644 index 59f156376..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-explain-param.yaml +++ /dev/null @@ -1,13 +0,0 @@ -arg_name: param -name: $explainable -type: :phpclass:`MongoDB\\Operation\\Explainable` -description: | - The command to explain. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml deleted file mode 100644 index 1b01599ec..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-find-option.yaml +++ /dev/null @@ -1,263 +0,0 @@ -arg_name: option -name: projection -type: array|object -description: | - The :ref:`projection specification ` to determine which fields to - include in the returned documents. See :manual:`Project Fields to Return from - Query ` and - :manual:`Projection Operators ` in the MongoDB - manual. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: sort -type: array|object -description: | - The sort specification for the ordering of the results. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: skip -type: integer -description: | - Number of documents to skip. Defaults to ``0``. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: limit -type: integer -description: | - The maximum number of documents to return. If unspecified, then defaults to no - limit. A limit of ``0`` is equivalent to setting no limit. - - A negative limit is similar to a positive limit but closes the cursor after - returning a single batch of results. As such, with a negative limit, if the - limited result set does not fit into a single batch, the number of documents - received will be less than the specified limit. By passing a negative limit, the - client indicates to the server that it will not ask for a subsequent batch via - getMore. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: allowDiskUse -type: boolean -description: | - Enables writing to temporary files. When set to ``true``, queries can write - data to the ``_tmp`` sub-directory in the ``dbPath`` directory. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: batchSize -type: integer -description: | - The number of documents to return in the first batch. Defaults to ``101``. A - batchSize of ``0`` means that the cursor will be established, but no documents - will be returned in the first batch. - - Unlike the previous wire protocol version, a batchSize of ``1`` for the - :dbcommand:`find` command does not close the cursor. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. ---- -arg_name: option -name: cursorType -type: integer -description: | - Indicates the type of cursor to use. ``cursorType`` supports the following - values: - - - ``MongoDB\Operation\Find::NON_TAILABLE`` (*default*) - - ``MongoDB\Operation\Find::TAILABLE`` -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - .. versionadded:: 1.2 ---- -arg_name: option -name: maxAwaitTimeMS -type: integer -description: | - Positive integer denoting the time limit in milliseconds for the server to - block a getMore operation if no data is available. This option should only be - used if cursorType is TAILABLE_AWAIT. - - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -arg_name: option -name: max -type: array|object -description: | - The exclusive upper bound for a specific index. - - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: maxScan -type: integer -description: | - Maximum number of documents or index keys to scan when executing the query. - - .. deprecated:: 1.4 - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: min -type: array|object -description: | - The inclusive lower bound for a specific index. - - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: oplogReplay -type: boolean -description: | - Internal use for replica sets. To use ``oplogReplay``, you must include the - following condition in the filter: - - .. code-block:: javascript - - { ts: { $gte: } } - - The :php:`MongoDB\\BSON\\Timestamp ` class - reference describes how to represent MongoDB's BSON timestamp type with PHP. - - .. deprecated:: 1.7 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: noCursorTimeout -type: boolean -description: | - Prevents the server from timing out idle cursors after an inactivity period - (10 minutes). -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: returnKey -type: boolean -description: | - If true, returns only the index keys in the resulting documents. - - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: showRecordId -type: boolean -description: | - Determines whether to return the record identifier for each document. If true, - adds a field $recordId to the returned documents. - - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: snapshot -type: boolean -description: | - Prevents the cursor from returning a document more than once because of an - intervening write operation. - - .. deprecated:: 1.4 - .. versionadded:: 1.2 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: allowPartialResults -type: boolean -description: | - For queries against a sharded collection, returns partial results from the - :program:`mongos` if some shards are unavailable instead of throwing an error. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap ---- -arg_name: option -name: modifiers -type: array|object -description: | - :manual:`Meta operators ` that modify the - output or behavior of a query. Use of these operators is deprecated in favor - of named options. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-find-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-find-param.yaml deleted file mode 100644 index 5683a7bba..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-find-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: true -replacement: - action: " to query" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml deleted file mode 100644 index 139e700ac..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOne-option.yaml +++ /dev/null @@ -1,84 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: skip ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: allowDiskUse ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: comment ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - .. versionadded:: 1.2 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned result document. ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: max ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: maxScan ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: min ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: returnKey ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: showRecordId ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: modifiers ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOne-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOne-param.yaml deleted file mode 100644 index 5683a7bba..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOne-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: true -replacement: - action: " to query" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml deleted file mode 100644 index d8f2b3a4a..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-option.yaml +++ /dev/null @@ -1,56 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.4+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.7 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-param.yaml deleted file mode 100644 index 92797eb53..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndDelete-param.yaml +++ /dev/null @@ -1,11 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to delete" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml deleted file mode 100644 index b509ac947..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-option.yaml +++ /dev/null @@ -1,77 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.4+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.7 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -arg_name: option -name: returnDocument -type: integer -description: | - Specifies whether to return the document before the replacement is applied, or - after. ``returnDocument`` supports the following values: - - - ``MongoDB\Operation\FindOneAndReplace::RETURN_DOCUMENT_BEFORE`` (*default*) - - ``MongoDB\Operation\FindOneAndReplace::RETURN_DOCUMENT_AFTER`` -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: upsert ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-param.yaml deleted file mode 100644 index 32ed6b35f..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndReplace-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to replace" ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $replacement ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml deleted file mode 100644 index a8f895f1b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-option.yaml +++ /dev/null @@ -1,83 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: arrayFilters -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.4+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.7 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -arg_name: option -name: returnDocument -type: integer -description: | - Specifies whether to return the document before the update is applied, or - after. ``returnDocument`` supports the following values: - - - ``MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_BEFORE`` (*default*) - - ``MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER`` -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned result document. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: upsert ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-param.yaml deleted file mode 100644 index a335678a3..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-findOneAndUpdate-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to update" ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $update ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml deleted file mode 100644 index 60f197d2b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-insertMany-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-MongoDBCollection-method-bulkWrite-option.yaml - ref: ordered ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertMany-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertMany-param.yaml deleted file mode 100644 index 78246d30d..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-insertMany-param.yaml +++ /dev/null @@ -1,13 +0,0 @@ -arg_name: param -name: $documents -type: array -description: | - The documents to insert into the collection. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml deleted file mode 100644 index adf17cb61..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-insertOne-option.yaml +++ /dev/null @@ -1,23 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-insertOne-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-insertOne-param.yaml deleted file mode 100644 index 5dc231d30..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-insertOne-param.yaml +++ /dev/null @@ -1,13 +0,0 @@ -arg_name: param -name: $document -type: array|object -description: | - The document to insert into the collection. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml deleted file mode 100644 index aa939b950..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-option.yaml +++ /dev/null @@ -1,19 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-listIndexes-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-listIndexes-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml deleted file mode 100644 index 478d12dda..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-option.yaml +++ /dev/null @@ -1,113 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation -post: | - This only applies when results are output to a collection. ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: finalize -type: :php:`MongoDB\\BSON\\Javascript ` -description: | - Follows the reduce method and modifies the output. - - .. note:: - - Passing a Javascript instance with a scope is deprecated. Put all scope - variables in the ``scope`` option of the MapReduce operation. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: jsMode -type: boolean -description: | - Specifies whether to convert intermediate data into BSON format between the - execution of the map and reduce functions. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: limit -type: integer -description: | - Specifies a maximum number of documents for the input into the map function. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -arg_name: option -name: query -type: array|object -description: | - Specifies the selection criteria using query operators for determining the - documents input to the map function. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference -post: | - This option will be ignored when results are output to a collection. ---- -arg_name: option -name: scope -type: array|object -description: | - Specifies global variables that are accessible in the map, reduce, and finalize - functions. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap ---- -arg_name: option -name: verbose -type: boolean -description: | - Specifies whether to include the timing information in the result information. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml deleted file mode 100644 index 6a76d6365..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-mapReduce-param.yaml +++ /dev/null @@ -1,46 +0,0 @@ -arg_name: param -name: $map -type: :php:`MongoDB\\BSON\\Javascript ` -description: | - A JavaScript function that associates or "maps" a value with a key and emits - the key and value pair. - - .. note:: - - Passing a Javascript instance with a scope is deprecated. Put all scope - variables in the ``scope`` option of the MapReduce operation. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $reduce -type: :php:`MongoDB\\BSON\\Javascript ` -description: | - A JavaScript function that "reduces" to a single object all the values - associated with a particular key. - - .. note:: - - Passing a Javascript instance with a scope is deprecated. Put all scope - variables in the ``scope`` option of the MapReduce operation. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $out -type: string|array|object -description: | - Specifies where to output the result of the map-reduce operation. You can - either output to a collection or return the result inline. On a primary member - of a replica set you can output either to a collection or inline, but on a - secondary, only inline output is possible. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml deleted file mode 100644 index 0b0b0809c..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-rename-option.yaml +++ /dev/null @@ -1,33 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern ---- -arg_name: option -name: dropTarget -type: boolean -description: | - If ``true``, MongoDB will drop the target before renaming the collection. The - default value is ``false``. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml deleted file mode 100644 index 459b2789d..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-rename-param.yaml +++ /dev/null @@ -1,25 +0,0 @@ -arg_name: param -name: $toCollectionName -type: string -description: | - The new name of the collection. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $toDatabaseName -type: string -description: | - The new database name of the collection. If a new database name is not - specified, the database of the original collection will be used. If the new - name specifies a different database, the command copies the collection - to the new database and drops the source collection. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml deleted file mode 100644 index 676296d26..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-option.yaml +++ /dev/null @@ -1,46 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: upsert ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.2+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.6 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-replaceOne-param.yaml deleted file mode 100644 index 32ed6b35f..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-replaceOne-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to replace" ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $replacement ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml deleted file mode 100644 index 64eec34e0..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-updateMany-option.yaml +++ /dev/null @@ -1,52 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: upsert ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: arrayFilters -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.2+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.6 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateMany-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateMany-param.yaml deleted file mode 100644 index a335678a3..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-updateMany-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to update" ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $update ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml deleted file mode 100644 index 64eec34e0..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-updateOne-option.yaml +++ /dev/null @@ -1,52 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: upsert ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: arrayFilters -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: hint -post: | - This option is available in MongoDB 4.2+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.6 ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-updateOne-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-updateOne-param.yaml deleted file mode 100644 index a335678a3..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-updateOne-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $filter -optional: false -replacement: - action: " to update" ---- -source: - file: apiargs-MongoDBCollection-common-param.yaml - ref: $update ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml deleted file mode 100644 index 0a47ae623..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-watch-option.yaml +++ /dev/null @@ -1,73 +0,0 @@ ---- -source: - file: apiargs-method-watch-option.yaml - ref: batchSize ---- -source: - file: apiargs-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocument ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocumentBeforeChange -post: | - .. versionadded: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: maxAwaitTimeMS ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: readPreference -post: | - This is used for both the initial change stream aggregation and for - server selection during an automatic resume. ---- -source: - file: apiargs-method-watch-option.yaml - ref: resumeAfter ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-method-watch-option.yaml - ref: showExpandedEvents -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAfter -post: | - .. versionadded: 1.5 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAtOperationTime -post: | - .. versionadded:: 1.4 ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: typeMap -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-watch-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-watch-param.yaml deleted file mode 100644 index b00e450cf..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-watch-param.yaml +++ /dev/null @@ -1,8 +0,0 @@ -source: - file: apiargs-method-watch-param.yaml - ref: $pipeline ---- -source: - file: apiargs-method-watch-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-withOptions-option.yaml b/docs/includes/apiargs-MongoDBCollection-method-withOptions-option.yaml deleted file mode 100644 index 681144f3a..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-withOptions-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "collection" - parent: "original collection" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "collection" - parent: "original collection" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "original collection" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "collection" - parent: "original collection" -... diff --git a/docs/includes/apiargs-MongoDBCollection-method-withOptions-param.yaml b/docs/includes/apiargs-MongoDBCollection-method-withOptions-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBCollection-method-withOptions-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-common-option.yaml b/docs/includes/apiargs-MongoDBDatabase-common-option.yaml deleted file mode 100644 index b03dac7a7..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-common-option.yaml +++ /dev/null @@ -1,36 +0,0 @@ -arg_name: option -name: readConcern -type: :php:`MongoDB\\Driver\\ReadConcern ` -description: | - :manual:`Read concern ` to use for the operation. - Defaults to the database's read concern. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: readPreference -type: :php:`MongoDB\\Driver\\ReadPreference ` -description: | - :manual:`Read preference ` to use for the - operation. Defaults to the database's read preference. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "database" ---- -arg_name: option -name: writeConcern -type: :php:`MongoDB\\Driver\\WriteConcern ` -description: | - :manual:`Write concern ` to use for the operation. - Defaults to the database's write concern. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml deleted file mode 100644 index f59192a0f..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-option.yaml +++ /dev/null @@ -1,60 +0,0 @@ -source: - file: apiargs-aggregate-option.yaml - ref: allowDiskUse ---- -source: - file: apiargs-aggregate-option.yaml - ref: batchSize ---- -source: - file: apiargs-aggregate-option.yaml - ref: bypassDocumentValidation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. ---- -source: - file: apiargs-aggregate-option.yaml - ref: explain ---- -source: - file: apiargs-common-option.yaml - ref: hint ---- -source: - file: apiargs-common-option.yaml - ref: let -post: | - .. versionadded:: 1.9 ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: readPreference ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern -post: | - This only applies when a :ref:`$out ` or :ref:`$merge ` - stage is specified. -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-aggregate-param.yaml deleted file mode 100644 index cbad3b49b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-aggregate-param.yaml +++ /dev/null @@ -1,14 +0,0 @@ -arg_name: param -name: $pipeline -type: array -description: | - Specifies an :manual:`aggregation pipeline ` - operation. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-command-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-command-option.yaml deleted file mode 100644 index 934e325db..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-command-option.yaml +++ /dev/null @@ -1,17 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readPreference -description: | - :manual:`Read preference ` to use for the - operation. Defaults to the database's read preference. ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-command-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-command-param.yaml deleted file mode 100644 index 5a4b03d7b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-command-param.yaml +++ /dev/null @@ -1,13 +0,0 @@ -arg_name: param -name: $command -type: array|object -description: | - The :manual:`database command ` document. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-construct-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-construct-option.yaml deleted file mode 100644 index d398069a8..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-construct-option.yaml +++ /dev/null @@ -1,25 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "database" - parent: "manager" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "database" - parent: "manager" ---- -source: - file: apiargs-MongoDBClient-method-construct-driverOptions.yaml - ref: typeMap ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "database" - parent: "manager" -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-construct-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-construct-param.yaml deleted file mode 100644 index 5ef826c17..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-construct-param.yaml +++ /dev/null @@ -1,12 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $manager ---- -source: - file: apiargs-common-param.yaml - ref: $databaseName ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml deleted file mode 100644 index a970a3989..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-option.yaml +++ /dev/null @@ -1,367 +0,0 @@ -arg_name: option -name: autoIndexId -type: boolean -description: | - Specify ``false`` to disable the automatic creation of an index on the ``_id`` - field. - - .. important:: - - For replica sets, do not set ``autoIndexId`` to ``false``. - - .. deprecated:: 1.4 - This option has been deprecated since MongoDB 3.2. As of MongoDB 4.0, this - option cannot be ``false`` when creating a replicated collection (i.e. a - collection outside of the ``local`` database in any mongod mode). -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: capped -type: boolean -description: | - To create a capped collection, specify ``true``. If you specify ``true``, you - must also set a maximum size in the ``size`` option. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: changeStreamPreAndPostImages -type: document -description: | - Used to configure support for pre- and post-images in change streams. See the - :manual:`create ` command documentation for more - information. - - This option is available in MongoDB 6.0+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.13 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: clusteredIndex -type: document -description: | - A clustered index specification. See - :manual:`Clustered Collections ` or the - :manual:`create ` command documentation for more - information. - - This option is available in MongoDB 5.3+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.13 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: collation -pre: | - Specifies the :manual:`collation - ` for the collection. ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: encryptedFields -type: document -description: | - A document describing encrypted fields for queryable encryption. If omitted, - the ``encryptedFieldsMap`` option within the ``autoEncryption`` driver option - will be consulted. See the - `Client Side Encryption specification `_ - for more information. - - This option is available in MongoDB 6.0+ and will result in an exception at - execution time if specified for an older server version. - - .. note:: - - Queryable Encryption is in public preview and available for evaluation - purposes. It is not yet recommended for production deployments as breaking - changes may be introduced. See the - `Queryable Encryption Preview `_ - blog post for more information. - - .. versionadded:: 1.13 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: expireAfterSeconds -type: integer -description: | - Used to automatically delete documents in time series collections. See the - :manual:`create ` command documentation for more - information. - - This option is available in MongoDB 5.0+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.9 -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: flags -type: integer -description: | - Available for the MMAPv1 storage engine only to set the ``usePowerOf2Sizes`` - and ``noPadding`` flags. - - The |php-library| provides constants that you can combine with a :php:`bitwise - OR operator ` to set the flag values: - - - ``MongoDB\Operation\CreateCollection::USE_POWER_OF_2_SIZES``: ``1`` - - ``MongoDB\Operation\CreateCollection::NO_PADDING``: ``2`` - - Defaults to ``1``. - - .. note:: - - MongoDB 3.0 and later ignores the ``usePowerOf2Sizes`` flag. See - :manual:`collMod ` and - :manual:`db.createCollection() - ` for more information. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: indexOptionDefaults -type: array|object -description: | - Allows users to specify a default configuration for indexes when creating a - collection. - - The ``indexOptionDefaults`` option accepts a ``storageEngine`` document, - which should take the following form:: - - { : } - - Storage engine configurations specified when creating indexes are validated - and logged to the :term:`oplog` during replication to support replica sets - with members that use different storage engines. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: max -type: integer -description: | - The maximum number of documents allowed in the capped collection. The ``size`` - option takes precedence over this limit. If a capped collection reaches the - ``size`` limit before it reaches the maximum number of documents, MongoDB - removes old documents. If you prefer to use the ``max`` limit, ensure that the - ``size`` limit, which is required for a capped collection, is sufficient to - contain the maximum number of documents. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -arg_name: option -name: pipeline -type: array -description: | - An array that consists of the aggregation pipeline stage(s), which will be - applied to the collection or view specified by ``viewOn``. See the - :manual:`create ` command documentation for more - information. - - .. versionadded:: 1.13 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -arg_name: option -name: size -type: integer -description: | - Specify a maximum size in bytes for a capped collection. Once a capped - collection reaches its maximum size, MongoDB removes the older documents to - make space for the new documents. The ``size`` option is required for capped - collections and ignored for other collections. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: storageEngine -type: array|object -description: | - Available for the WiredTiger storage engine only. - - Allows users to specify configuration to the storage engine on a - per-collection basis when creating a collection. The value of the - ``storageEngine`` option should take the following form:: - - { : } - - Storage engine configurations specified when creating collections are - validated and logged to the :term:`oplog` during replication to support - replica sets with members that use different storage engines. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: timeseries -type: array|object -description: | - An object containing options for creating time series collections. See the - :manual:`create ` command documentation for - supported options. - - This option is available in MongoDB 5.0+ and will result in an exception at - execution time if specified for an older server version. - - .. versionadded:: 1.9 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -arg_name: option -name: validator -type: array|object -description: | - Allows users to specify :manual:`validation rules or expressions - ` for the collection. For more information, see - :manual:`Document Validation ` in the MongoDB - manual. - - The ``validator`` option takes an array that specifies the validation rules or - expressions. You can specify the expressions using the same operators as - MongoDB's :manual:`query operators ` with the - exception of :query:`$geoNear`, :query:`$near`, :query:`$nearSphere`, - :query:`$text`, and :query:`$where`. - - .. note:: - - - Validation occurs during updates and inserts. Existing documents do not - undergo validation checks until modification. - - - You cannot specify a validator for collections in the ``admin``, - ``local``, and ``config`` databases. - - - You cannot specify a validator for ``system.*`` collections. -operation: ~ -interface: phpmethod -optional: true ---- -arg_name: option -name: validationAction -type: string -description: | - Determines whether to ``error`` on invalid documents or just ``warn`` about - the violations but allow invalid documents to be inserted. - - .. important:: - - Validation of documents only applies to those documents as determined by - the ``validationLevel``. - - .. list-table:: - :header-rows: 1 - - * - ``validationAction`` - - - Description - - * - ``"error"`` - - - **Default**. Documents must pass validation before the write occurs. - Otherwise, the write operation fails. - - * - ``"warn"`` - - - Documents do not have to pass validation. If the document fails - validation, the write operation logs the validation failure. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: validationLevel -type: string -description: | - Determines how strictly MongoDB applies the validation rules to existing - documents during an update. - - .. list-table:: - :header-rows: 1 - - * - ``validationLevel`` - - - Description - - * - ``"off"`` - - - No validation for inserts or updates. - - * - ``"strict"`` - - - **Default**. Apply validation rules to all inserts and all updates. - - * - ``"moderate"`` - - - Apply validation rules to inserts and to updates on existing *valid* - documents. Do not apply rules to updates on existing *invalid* - documents. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: viewOn -type: string -description: | - The name of the source collection or view from which to create the view. - - .. note:: - - The name is not the full namespace of the collection or view (i.e. it does - not include the database name). Views must be created in the same databases - as the source collection or view. - - .. versionadded:: 1.13 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-createCollection-param.yaml deleted file mode 100644 index b6bb2dccd..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-createCollection-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - action: " to create" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml deleted file mode 100644 index e725fc1be..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-drop-option.yaml +++ /dev/null @@ -1,25 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-drop-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-drop-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-drop-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml deleted file mode 100644 index 0034b5e88..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-option.yaml +++ /dev/null @@ -1,31 +0,0 @@ -source: - file: apiargs-dropCollection-option.yaml - ref: encryptedFields -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-param.yaml deleted file mode 100644 index c8e0a614e..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-dropCollection-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - action: " to drop" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-get-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-get-param.yaml deleted file mode 100644 index 651c85f94..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-get-param.yaml +++ /dev/null @@ -1,6 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - action: " to select" -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml deleted file mode 100644 index bacf7280a..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-option.yaml +++ /dev/null @@ -1,46 +0,0 @@ -arg_name: option -name: authorizedCollections -type: boolean -description: | - A flag that determines which collections are returned based on the user - privileges when access control is enabled. For more information, see the - `listCollections command documentation `_. - - For servers < 4.0, this option is ignored. - - .. versionadded:: 1.12 -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -arg_name: option -name: filter -type: array|object -description: | - A query expression to filter the list of collections. - - You can specify a query expression for collection fields (e.g. ``name``, - ``options``). -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-common-option.yaml - ref: session -post: | - .. versionadded:: 1.3 -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-listCollections-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-listCollections-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml deleted file mode 100644 index b27c16cfb..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-option.yaml +++ /dev/null @@ -1,23 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-param.yaml deleted file mode 100644 index 16597b3af..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-modifyCollection-param.yaml +++ /dev/null @@ -1,20 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - subject: "collection or view" - action: " to modify" ---- -arg_name: param -name: $collectionOptions -type: array -description: | - Collection or view options to assign. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml deleted file mode 100644 index bb026bd9c..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-option.yaml +++ /dev/null @@ -1,33 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: comment -post: | - This is not supported for server versions prior to 4.4 and will result in an - exception at execution time if used. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -post: | - This will be used for the returned command result document. ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: writeConcern ---- -arg_name: option -name: dropTarget -type: boolean -description: | - If ``true``, MongoDB will drop the target before renaming the collection. The - default value is ``false``. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml deleted file mode 100644 index 3043f4dd5..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-renameCollection-param.yaml +++ /dev/null @@ -1,34 +0,0 @@ -arg_name: param -name: $fromCollectionName -type: string -description: | - The name of the collection to rename. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $toCollectionName -type: string -description: | - The new name of the collection. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $toDatabaseName -type: string -description: | - The new database name of the collection. If a new database name is not - specified, the current database will be used. If the new name specifies a - different database, the command copies the collection to the new database - and drops the source collection. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-option.yaml deleted file mode 100644 index 932c1b16b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "collection" - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "collection" - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "collection" - parent: "database" -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-param.yaml deleted file mode 100644 index 46d4e72a6..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-selectCollection-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $collectionName -replacement: - action: " to select" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-option.yaml deleted file mode 100644 index 2039d114a..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-option.yaml +++ /dev/null @@ -1,52 +0,0 @@ -arg_name: option -name: bucketName -type: string -description: | - The bucket name, which will be used as a prefix for the files and chunks - collections. Defaults to ``"fs"``. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: chunkSizeBytes -type: integer -description: | - The chunk size in bytes. Defaults to ``261120`` (i.e. 255 KiB). -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: disableMD5 -post: | - .. versionadded: 1.4 ---- -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "bucket" - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "bucket" - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "bucket" - parent: "database" -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-selectGridFSBucket-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml deleted file mode 100644 index b24efbec2..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-watch-option.yaml +++ /dev/null @@ -1,71 +0,0 @@ ---- -source: - file: apiargs-method-watch-option.yaml - ref: batchSize ---- -source: - file: apiargs-common-option.yaml - ref: collation ---- -source: - file: apiargs-common-option.yaml - ref: comment -post: | - The comment can be any valid BSON type for server versions 4.4 and above. - Earlier server versions only support string values. - - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocument ---- -source: - file: apiargs-method-watch-option.yaml - ref: fullDocumentBeforeChange -post: | - .. versionadded: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: maxAwaitTimeMS ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: readPreference -post: | - This is used for both the initial change stream aggregation and for - server selection during an automatic resume. ---- -source: - file: apiargs-method-watch-option.yaml - ref: resumeAfter ---- -source: - file: apiargs-common-option.yaml - ref: session ---- -source: - file: apiargs-method-watch-option.yaml - ref: showExpandedEvents -post: | - .. versionadded:: 1.13 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAfter -post: | - .. versionadded: 1.5 ---- -source: - file: apiargs-method-watch-option.yaml - ref: startAtOperationTime ---- -source: - file: apiargs-MongoDBDatabase-common-option.yaml - ref: typeMap -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-watch-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-watch-param.yaml deleted file mode 100644 index b00e450cf..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-watch-param.yaml +++ /dev/null @@ -1,8 +0,0 @@ -source: - file: apiargs-method-watch-param.yaml - ref: $pipeline ---- -source: - file: apiargs-method-watch-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-withOptions-option.yaml b/docs/includes/apiargs-MongoDBDatabase-method-withOptions-option.yaml deleted file mode 100644 index c048182c1..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-withOptions-option.yaml +++ /dev/null @@ -1,27 +0,0 @@ -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "database" - parent: "original database" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "database" - parent: "original database" ---- -source: - file: apiargs-common-option.yaml - ref: typeMap -replacement: - parent: "original database" ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "database" - parent: "original database" -... diff --git a/docs/includes/apiargs-MongoDBDatabase-method-withOptions-param.yaml b/docs/includes/apiargs-MongoDBDatabase-method-withOptions-param.yaml deleted file mode 100644 index 73ad04d0b..000000000 --- a/docs/includes/apiargs-MongoDBDatabase-method-withOptions-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-common-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-common-option.yaml deleted file mode 100644 index bf2de660a..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-common-option.yaml +++ /dev/null @@ -1,61 +0,0 @@ -arg_name: option -name: _id -type: mixed -description: | - Value to use as the file document identifier. Defaults to a new - :php:`MongoDB\\BSON\\ObjectId ` object. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: chunkSizeBytes -type: integer -description: | - The chunk size in bytes. Defaults to the bucket's ``chunkSizeBytes`` option. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: disableMD5 -type: boolean -description: | - Whether to disable automatic MD5 generation when storing files. - - Defaults to ``false``. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: metadata -type: array|object -description: | - User data for the ``metadata`` field of the file document. If not specified, - the ``metadata`` field will not be set on the file document. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: revision -type: integer -description: | - The revision of the file to retrieve. Files with the name ``filename`` will be - differentiated by their ``uploadDate`` field. - - Revision numbers are defined as follows: - - - 0 = the original stored file - - 1 = the first revision - - 2 = the second revision - - etc... - - -2 = the second most recent revision - - -1 = the most recent revision - - Defaults to -1 (i.e. the most recent revision). -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-common-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-common-param.yaml deleted file mode 100644 index f1d6b136a..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-common-param.yaml +++ /dev/null @@ -1,39 +0,0 @@ -arg_name: param -name: $filename -type: string -description: | - The ``filename`` of the file{{action}}. -interface: phpmethod -operation: ~ -optional: false -replacement: - action: "" ---- -arg_name: param -name: $id -type: mixed -description: | - The ``_id`` of the file{{action}}. -interface: phpmethod -operation: ~ -optional: false -replacement: - action: "" ---- -arg_name: param -name: $stream -type: resource -description: | - The GridFS stream resource. -interface: phpmethod -operation: ~ ---- -arg_name: param -name: $destination -type: resource -description: | - Writable stream, to which the GridFS file's contents will be written. -interface: phpmethod -operation: ~ -optional: false -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-option.yaml deleted file mode 100644 index c8a7eb795..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-option.yaml +++ /dev/null @@ -1,50 +0,0 @@ -arg_name: option -name: bucketName -type: string -description: | - The bucket name, which will be used as a prefix for the files and chunks - collections. Defaults to ``"fs"``. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: chunkSizeBytes -type: integer -description: | - The chunk size in bytes. Defaults to ``261120`` (i.e. 255 KiB). -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: disableMD5 -post: | - .. versionadded: 1.4 ---- -source: - file: apiargs-common-option.yaml - ref: readConcern -replacement: - resource: "bucket" - parent: "database" ---- -source: - file: apiargs-common-option.yaml - ref: readPreference -replacement: - resource: "bucket" - parent: "database" ---- -source: - file: apiargs-MongoDBClient-method-construct-driverOptions.yaml - ref: typeMap ---- -source: - file: apiargs-common-option.yaml - ref: writeConcern -replacement: - resource: "bucket" - parent: "database" -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-param.yaml deleted file mode 100644 index 5ef826c17..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-construct-param.yaml +++ /dev/null @@ -1,12 +0,0 @@ -source: - file: apiargs-common-param.yaml - ref: $manager ---- -source: - file: apiargs-common-param.yaml - ref: $databaseName ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-delete-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-delete-param.yaml deleted file mode 100644 index 7e8baa214..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-delete-param.yaml +++ /dev/null @@ -1,6 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $id -replacement: - resource: " to delete" -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStream-param.yaml deleted file mode 100644 index 39d48dc5d..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStream-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $id -replacement: - resource: " to download" ---- -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $destination -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-option.yaml deleted file mode 100644 index 9dc941d97..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-option.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: revision -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-param.yaml deleted file mode 100644 index 2704877be..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-downloadToStreamByName-param.yaml +++ /dev/null @@ -1,14 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $filename -replacement: - resource: " to download" ---- -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $destination ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-find-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-find-option.yaml deleted file mode 100644 index 1f2e9d8eb..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-find-option.yaml +++ /dev/null @@ -1,76 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: skip ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: limit ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: allowDiskUse ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: batchSize ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: collation ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: comment ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: cursorType ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: readConcern -description: | - :manual:`Read concern ` to use for the operation. - Defaults to the bucket's read concern. ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: readPreference -description: | - :manual:`Read preference ` to use for the - operation. Defaults to the bucket's read preference. ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: oplogReplay ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: noCursorTimeout ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: allowPartialResults ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: typeMap -replacement: - parent: "bucket" ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: modifiers -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-findOne-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-findOne-option.yaml deleted file mode 100644 index 3d0d0ed8e..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-findOne-option.yaml +++ /dev/null @@ -1,46 +0,0 @@ -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: projection ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: sort ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: skip ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: allowDiskUse ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: collation ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: comment ---- -source: - file: apiargs-common-option.yaml - ref: maxTimeMS ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: readConcern ---- -source: - file: apiargs-MongoDBGridFSBucket-method-find-option.yaml - ref: readPreference ---- -source: - file: apiargs-MongoDBGridFSBucket-method-find-option.yaml - ref: typeMap -post: | - This will be used for the returned result document. ---- -source: - file: apiargs-MongoDBCollection-method-find-option.yaml - ref: modifiers -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileDocumentForStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileDocumentForStream-param.yaml deleted file mode 100644 index 0801cb21d..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileDocumentForStream-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $stream -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileIdForStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileIdForStream-param.yaml deleted file mode 100644 index 0801cb21d..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-getFileIdForStream-param.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $stream -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStream-param.yaml deleted file mode 100644 index 54e7c8c21..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStream-param.yaml +++ /dev/null @@ -1,6 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $id -replacement: - resource: " to download" -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-option.yaml deleted file mode 100644 index 9dc941d97..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-option.yaml +++ /dev/null @@ -1,4 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: revision -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-param.yaml deleted file mode 100644 index eb8ec932c..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-openDownloadStreamByName-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $filename -replacement: - resource: " to download" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-option.yaml deleted file mode 100644 index 592040d69..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-option.yaml +++ /dev/null @@ -1,18 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: _id ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: chunkSizeBytes ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: disableMD5 -post: | - .. versionadded: 1.4 ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: metadata -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-param.yaml deleted file mode 100644 index 6b8efb347..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-openUploadStream-param.yaml +++ /dev/null @@ -1,10 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $filename -replacement: - resource: " to create" ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-rename-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-rename-param.yaml deleted file mode 100644 index e1b140aea..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-rename-param.yaml +++ /dev/null @@ -1,15 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $id -replacement: - resource: " to rename" ---- -arg_name: param -name: $newFilename -type: string -description: | - The new ``filename`` value. -interface: phpmethod -operation: ~ -optional: false -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-option.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-option.yaml deleted file mode 100644 index 592040d69..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-option.yaml +++ /dev/null @@ -1,18 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: _id ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: chunkSizeBytes ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: disableMD5 -post: | - .. versionadded: 1.4 ---- -source: - file: apiargs-MongoDBGridFSBucket-common-option.yaml - ref: metadata -... diff --git a/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-param.yaml b/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-param.yaml deleted file mode 100644 index 48fa2db4c..000000000 --- a/docs/includes/apiargs-MongoDBGridFSBucket-method-uploadFromStream-param.yaml +++ /dev/null @@ -1,19 +0,0 @@ -source: - file: apiargs-MongoDBGridFSBucket-common-param.yaml - ref: $filename -replacement: - resource: " to create" ---- -arg_name: param -name: $source -type: resource -description: | - Readable stream, from which the new GridFS file's contents will be read. -interface: phpmethod -operation: ~ -optional: false ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/apiargs-aggregate-option.yaml b/docs/includes/apiargs-aggregate-option.yaml deleted file mode 100644 index e5b410c4f..000000000 --- a/docs/includes/apiargs-aggregate-option.yaml +++ /dev/null @@ -1,43 +0,0 @@ -arg_name: option -name: allowDiskUse -type: boolean -description: | - Enables writing to temporary files. When set to ``true``, aggregation stages - can write data to the ``_tmp`` sub-directory in the ``dbPath`` directory. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: batchSize -type: integer -description: | - Specifies the batch size for the cursor, which will apply to both the initial - ``aggregate`` command and any subsequent ``getMore`` commands. This determines - the maximum number of documents to return in each response from the server. - - A batchSize of ``0`` is special in that and will only apply to the initial - ``aggregate`` command; subsequent ``getMore`` commands will use the server's - default batch size. This may be useful for quickly returning a cursor or - failure from ``aggregate`` without doing significant server-side work. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-MongoDBCollection-common-option.yaml - ref: bypassDocumentValidation -post: | - This only applies when using the :ref:`$out ` and - :ref:`$out ` stages. ---- -arg_name: option -name: explain -type: boolean -description: | - Specifies whether or not to return the information on the processing of the - pipeline. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-common-option.yaml b/docs/includes/apiargs-common-option.yaml deleted file mode 100644 index cae141871..000000000 --- a/docs/includes/apiargs-common-option.yaml +++ /dev/null @@ -1,124 +0,0 @@ -arg_name: option -name: collation -type: array|object -description: | - :manual:`Collation ` allows users to specify - language-specific rules for string comparison, such as rules for lettercase - and accent marks. When specifying collation, the ``locale`` field is - mandatory; all other collation fields are optional. For descriptions of the - fields, see :manual:`Collation Document - `. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: comment -type: mixed -description: | - Enables users to specify an arbitrary comment to help trace the operation - through the :manual:`database profiler `, - :manual:`currentOp ` output, and - :manual:`logs `. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: hint -type: string|array|object -description: | - The index to use. Specify either the index name as a string or the index key - pattern as a document. If specified, then the query system will only consider - plans using the hinted index. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: let -type: array|object -description: | - Map of parameter names and values. Values must be constant or closed - expressions that do not reference document fields. Parameters can then be - accessed as variables in an aggregate expression context (e.g. ``$$var``). - - This is not supported for server versions prior to 5.0 and will result in an - exception at execution time if used. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: maxTimeMS -type: integer -description: | - The cumulative time limit in milliseconds for processing operations on the - cursor. MongoDB aborts the operation at the earliest following - :term:`interrupt point`. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: readConcern -type: :php:`MongoDB\\Driver\\ReadConcern ` -description: | - The default read concern to use for {{resource}} operations. Defaults to the - {{parent}}'s read concern. -interface: phpmethod -operation: ~ -optional: true -replacement: - resource: "collection" - parent: "client" ---- -arg_name: option -name: readPreference -type: :php:`MongoDB\\Driver\\ReadPreference ` -description: | - The default read preference to use for {{resource}} operations. Defaults to - the {{parent}}'s read preference. -interface: phpmethod -operation: ~ -optional: true -replacement: - resource: "collection" - parent: "client" ---- -arg_name: option -name: session -type: :php:`MongoDB\\Driver\\Session ` -description: | - Client session to associate with the operation. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: typeMap -type: array -description: | - The :php:`type map - ` - to apply to cursors, which determines how BSON documents are converted to PHP - values. Defaults to the {{parent}}'s type map. -interface: phpmethod -operation: ~ -optional: true -replacement: - parent: "client" ---- -arg_name: option -name: writeConcern -type: :php:`MongoDB\\Driver\\WriteConcern ` -description: | - The default write concern to use for {{resource}} operations. Defaults - to the {{parent}}'s write concern. -interface: phpmethod -operation: ~ -optional: true -replacement: - resource: "collection" - parent: "client" -... diff --git a/docs/includes/apiargs-common-param.yaml b/docs/includes/apiargs-common-param.yaml deleted file mode 100644 index a8d790825..000000000 --- a/docs/includes/apiargs-common-param.yaml +++ /dev/null @@ -1,42 +0,0 @@ -arg_name: param -name: $manager -type: :php:`MongoDB\\Driver\\Manager ` -description: | - The :php:`Manager ` instance from the driver. The - manager maintains connections between the driver and your MongoDB instances. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $databaseName -type: string -description: | - The name of the database{{action}}. -interface: phpmethod -operation: ~ -optional: false -replacement: - action: "" ---- -arg_name: param -name: $collectionName -type: string -description: | - The name of the {{subject}}{{action}}. -interface: phpmethod -operation: ~ -optional: false -replacement: - subject: "collection" - action: "" ---- -arg_name: param -name: $options -type: array -description: | - An array specifying the desired options. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-dropCollection-option.yaml b/docs/includes/apiargs-dropCollection-option.yaml deleted file mode 100644 index 29c945e60..000000000 --- a/docs/includes/apiargs-dropCollection-option.yaml +++ /dev/null @@ -1,23 +0,0 @@ -arg_name: option -name: encryptedFields -type: array|object -description: | - A document describing encrypted fields for queryable encryption. If omitted, - the ``encryptedFieldsMap`` option within the ``autoEncryption`` driver option - will be consulted. If ``encryptedFieldsMap`` was defined but does not specify - this collection, the library will make a final attempt to consult the - server-side value for ``encryptedFields``. See the - `Client Side Encryption specification `_ - for more information. - - .. note:: - - Queryable Encryption is in public preview and available for evaluation - purposes. It is not yet recommended for production deployments as breaking - changes may be introduced. See the - `Queryable Encryption Preview `_ - blog post for more information. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-function-with_transaction-param.yaml b/docs/includes/apiargs-function-with_transaction-param.yaml deleted file mode 100644 index 78875e913..000000000 --- a/docs/includes/apiargs-function-with_transaction-param.yaml +++ /dev/null @@ -1,31 +0,0 @@ -arg_name: param -name: $session -type: :php:`MongoDB\\Driver\\Session ` -description: | - A client session used to execute the transaction. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $callback -type: callback -description: | - A callback that will be run inside the transaction. The callback must accept a - :php:`MongoDB\\Driver\\Session ` object as first - argument. -interface: phpmethod -operation: ~ -optional: false ---- -arg_name: param -name: $transactionOptions -type: array -description: | - Transaction options, which will be passed to - :php:`MongoDB\\Driver\\Session::startTransaction `. - See the extension documentation for a list of supported options. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-method-watch-option.yaml b/docs/includes/apiargs-method-watch-option.yaml deleted file mode 100644 index d09278286..000000000 --- a/docs/includes/apiargs-method-watch-option.yaml +++ /dev/null @@ -1,178 +0,0 @@ ---- -arg_name: option -name: batchSize -type: integer -description: | - Specifies the batch size for the cursor, which will apply to both the initial - ``aggregate`` command and any subsequent ``getMore`` commands. This determines - the maximum number of change events to return in each response from the - server. - - .. note:: - - Irrespective of the ``batchSize`` option, the initial ``aggregate`` command - response for a change stream generally does not include any documents - unless another option is used to configure its starting point (e.g. - ``startAfter``). -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: fullDocument -type: string -description: | - Determines how the "fullDocument" response field will be populated for update - operations. - - By default, change streams only return the delta of fields (via an - "updateDescription" field) for update operations and "fullDocument" is - omitted. Insert and replace operations always include the "fullDocument" - field. Delete operations omit the field as the document no longer exists. - - Specify "updateLookup" to return the current majority-committed version of the - updated document. - - MongoDB 6.0+ allows returning the post-image of the modified document if the - collection has ``changeStreamPreAndPostImages`` enabled. Specify - "whenAvailable" to return the post-image if available or a null value if not. - Specify "required" to return the post-image if available or raise an error if - not. - - The following values are supported: - - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_UPDATE_LOOKUP`` - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_WHEN_AVAILABLE`` - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_REQUIRED`` - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: fullDocumentBeforeChange -type: string -description: | - Determines how the "fullDocumentBeforeChange" response field will be - populated. By default, the field is omitted. - - MongoDB 6.0+ allows returning the pre-image of the modified document if the - collection has ``changeStreamPreAndPostImages`` enabled. Specify - "whenAvailable" to return the pre-image if available or a null value if not. - Specify "required" to return the pre-image if available or raise an error if - not. - - The following values are supported: - - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE`` - - ``MongoDB\Operation\Watch::FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED`` - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: maxAwaitTimeMS -type: integer -description: | - Positive integer denoting the time limit in milliseconds for the server to - block a getMore operation if no data is available. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: resumeAfter -type: array|object -description: | - Specifies the logical starting point for the new change stream. The ``_id`` - field in documents returned by the change stream may be used here. - - Using this option in conjunction with ``startAfter`` and/or - ``startAtOperationTime`` will result in a server error. The options are - mutually exclusive. - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: showExpandedEvents -type: boolean -description: | - If true, instructs the server to include additional DDL events in the change - stream. The additional events that may be included are: - - - ``createIndexes`` - - ``dropIndexes`` - - ``modify`` - - ``create`` - - ``shardCollection`` - - ``reshardCollection`` (server 6.1+) - - ``refineCollectionShardKey`` (server 6.1+) - - This is not supported for server versions prior to 6.0 and will result in an - exception at execution time if used. - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: startAfter -type: array|object -description: | - Specifies the logical starting point for the new change stream. The ``_id`` - field in documents returned by the change stream may be used here. Unlike - ``resumeAfter``, this option can be used with a resume token from an - "invalidate" event. - - Using this option in conjunction with ``resumeAfter`` and/or - ``startAtOperationTime`` will result in a server error. The options are - mutually exclusive. - - This is not supported for server versions prior to 4.2 and will result in an - exception at execution time if used. - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true ---- -arg_name: option -name: startAtOperationTime -type: :php:`MongoDB\\BSON\\TimestampInterface ` -description: | - If specified, the change stream will only provide changes that occurred at or - after the specified timestamp. Command responses from a MongoDB 4.0+ server - include an ``operationTime`` that can be used here. By default, the - ``operationTime`` returned by the initial ``aggregate`` command will be used - if available. - - Using this option in conjunction with ``resumeAfter`` and/or ``startAfter`` - will result in a server error. The options are mutually exclusive. - - This is not supported for server versions prior to 4.0 and will result in an - exception at execution time if used. - - .. note:: - - This is an option of the ``$changeStream`` pipeline stage. -interface: phpmethod -operation: ~ -optional: true -... diff --git a/docs/includes/apiargs-method-watch-param.yaml b/docs/includes/apiargs-method-watch-param.yaml deleted file mode 100644 index e0da852c4..000000000 --- a/docs/includes/apiargs-method-watch-param.yaml +++ /dev/null @@ -1,13 +0,0 @@ -arg_name: param -name: $pipeline -type: array|object -description: | - The pipeline of stages to append to an initial ``$changeStream`` stage. -interface: phpmethod -operation: ~ -optional: true ---- -source: - file: apiargs-common-param.yaml - ref: $options -... diff --git a/docs/includes/extracts-bulkwriteexception.yaml b/docs/includes/extracts-bulkwriteexception.yaml deleted file mode 100644 index f002063f0..000000000 --- a/docs/includes/extracts-bulkwriteexception.yaml +++ /dev/null @@ -1,21 +0,0 @@ -ref: bulkwriteexception-result -content: | - If a :php:`MongoDB\\Driver\\Exception\\BulkWriteException - ` is thrown, users should call - :php:`getWriteResult() ` and - inspect the returned :php:`MongoDB\\Driver\\WriteResult - ` object to determine the nature of the error. - - For example, a write operation may have been successfully applied to the - primary server but failed to satisfy the write concern (e.g. replication took - too long). Alternatively, a write operation may have failed outright (e.g. - unique key violation). ---- -ref: bulkwriteexception-ordered -content: | - In the case of a bulk write, the result may indicate multiple successful write - operations and/or errors. If the ``ordered`` option is ``true``, some - operations may have succeeded before the first error was encountered and the - exception thrown. If the ``ordered`` option is ``false``, multiple errors may - have been encountered. -... diff --git a/docs/includes/extracts-error.yaml b/docs/includes/extracts-error.yaml deleted file mode 100644 index cfada049a..000000000 --- a/docs/includes/extracts-error.yaml +++ /dev/null @@ -1,52 +0,0 @@ -ref: error-driver-bulkwriteexception -content: | - :php:`MongoDB\\Driver\\Exception\\BulkWriteException - ` for errors related to the write - operation. Users should inspect the value returned by :php:`getWriteResult() - ` to determine the nature of the - error. ---- -ref: error-driver-invalidargumentexception -content: | - :php:`MongoDB\\Driver\\Exception\\InvalidArgumentException - ` for errors related to the - parsing of parameters or options at the driver level. ---- -ref: error-driver-runtimeexception -content: | - :php:`MongoDB\\Driver\\Exception\\RuntimeException - ` for other errors at the driver - level (e.g. connection errors). ---- -ref: error-badmethodcallexception-write-result -content: | - :phpclass:`MongoDB\\Exception\\BadMethodCallException` if this method is - called and the write operation used an unacknowledged :manual:`write concern - `. ---- -ref: error-invalidargumentexception -content: | - :phpclass:`MongoDB\\Exception\\InvalidArgumentException` for errors related to - the parsing of parameters or options. ---- -ref: error-unexpectedvalueexception -content: | - :phpclass:`MongoDB\\Exception\\UnexpectedValueException` if the command - response from the server was malformed. ---- -ref: error-unsupportedexception -content: | - :phpclass:`MongoDB\\Exception\\UnsupportedException` if options are used and - not supported by the selected server (e.g. ``collation``, ``readConcern``, - ``writeConcern``). ---- -ref: error-gridfs-filenotfoundexception -content: | - :phpclass:`MongoDB\\GridFS\\Exception\\FileNotFoundException` if no file was - found for the selection criteria. ---- -ref: error-gridfs-corruptfileexception -content: | - :phpclass:`MongoDB\\GridFS\\Exception\\CorruptFileException` if the file's - metadata or chunk documents contain unexpected or invalid data. -... diff --git a/docs/includes/extracts-note.yaml b/docs/includes/extracts-note.yaml deleted file mode 100644 index 7790f0c32..000000000 --- a/docs/includes/extracts-note.yaml +++ /dev/null @@ -1,12 +0,0 @@ -ref: note-bson-comparison -content: | - When evaluating query criteria, MongoDB compares types and values according to - its own :manual:`comparison rules for BSON types - `, which differs from PHP's - :php:`comparison ` and :php:`type juggling - ` rules. When matching a special - BSON type the query criteria should use the respective :php:`BSON class - ` in the driver (e.g. use - :php:`MongoDB\\BSON\\ObjectId ` to match an - :manual:`ObjectId `). -... diff --git a/docs/index.txt b/docs/index.txt deleted file mode 100644 index 5cbf328a4..000000000 --- a/docs/index.txt +++ /dev/null @@ -1,73 +0,0 @@ -=================== -MongoDB PHP Library -=================== - -.. default-domain:: mongodb - -The |php-library| provides a high-level abstraction around the lower-level -`PHP driver `_, also known as the ``mongodb`` -extension. - -The ``mongodb`` extension provides a limited API to connect to the database and -execute generic commands, queries, and write operations. In contrast, the -|php-library| provides a full-featured API and models client, database, and -collection objects. Each of those classes provide various helper methods for -performing operations in context. For example, :phpclass:`MongoDB\\Collection` -implements methods for executing CRUD operations and managing indexes on the -collection, among other things. - -If you are developing a PHP application with MongoDB, you should consider using -the |php-library| instead of the extension alone. - -New to the PHP Library? ------------------------ - -If you have some experience with MongoDB but are new to the PHP library, the -following pages should help you get started: - -- :doc:`/tutorial/install-php-library` - -- :doc:`/tutorial/connecting` - -- :doc:`/tutorial/crud` - -- :doc:`/tutorial/commands` - -- :doc:`/tutorial/gridfs` - -- :doc:`/tutorial/modeling-bson-data` - -- :doc:`/reference/bson` - -Code examples can be found in the ``examples`` directory in the source code. - -If you have previously worked with the legacy ``mongo`` extension, it will be -helpful to review the :doc:`/upgrade` for a summary of API changes between the -old driver and this library. - -New to MongoDB? ---------------- - -If you are a new MongoDB user, the following links should help you become more -familiar with MongoDB and introduce some of the concepts and terms you will -encounter in the library documentation: - -- :manual:`Introduction to MongoDB ` - -- :manual:`Databases and Collections ` - -- :manual:`Documents ` and - :manual:`BSON Types ` - -- :manual:`MongoDB CRUD Operations ` - -.. class:: hidden - - .. toctree:: - :titlesonly: - - Installation - /tutorial - /upgrade - /reference - FAQ diff --git a/docs/pretty.js b/docs/pretty.js deleted file mode 100644 index cd0aa1e1a..000000000 --- a/docs/pretty.js +++ /dev/null @@ -1,4 +0,0 @@ -$(document).ready(function() { - $('pre code').parent().addClass('prettyprint well'); - prettyPrint(); -}); diff --git a/docs/reference.txt b/docs/reference.txt deleted file mode 100644 index 1d2c76db3..000000000 --- a/docs/reference.txt +++ /dev/null @@ -1,19 +0,0 @@ -========= -Reference -========= - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - /reference/bson - /reference/class/MongoDBClient - /reference/class/MongoDBDatabase - /reference/class/MongoDBCollection - /reference/class/MongoDBGridFSBucket - /reference/write-result-classes - /reference/result-classes - /reference/enumeration-classes - /reference/functions - /reference/exception-classes diff --git a/docs/reference/bson.txt b/docs/reference/bson.txt deleted file mode 100644 index b9fbbaa2c..000000000 --- a/docs/reference/bson.txt +++ /dev/null @@ -1,51 +0,0 @@ -==== -BSON -==== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Overview --------- - -MongoDB stores data records as BSON documents. BSON is a binary representation -of JSON documents, though it contains more data types than JSON. For the BSON -spec, see `bsonspec.org `_. - -By default, the |php-library| returns BSON documents as -:phpclass:`MongoDB\\Model\\BSONDocument` objects and BSON arrays as -:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. - -Classes -------- - -.. phpclass:: MongoDB\\Model\\BSONArray - - This class extends PHP's :php:`ArrayObject ` class. It also - implements PHP's :php:`JsonSerializable ` interface and the - driver's :php:`MongoDB\\BSON\\Serializable ` and - :php:`MongoDB\\BSON\\Unserializable ` - interfaces. - - By default, the library will deserialize BSON arrays as instances of this - class. During BSON and JSON serialization, instances of this class will - serialize as an array type (:php:`array_values() ` is used - internally to numerically reindex the array). - -.. phpclass:: MongoDB\\Model\\BSONDocument - - This class extends PHP's :php:`ArrayObject ` class. It also - implements PHP's :php:`JsonSerializable ` interface and the - driver's :php:`MongoDB\\BSON\\Serializable ` and - :php:`MongoDB\\BSON\\Unserializable ` - interfaces. - - By default, the library will deserialize BSON documents as instances of this - class. During BSON and JSON serialization, instances of this class will - serialize as a document type (:php:`object casting - ` is used internally). diff --git a/docs/reference/class/MongoDBClient.txt b/docs/reference/class/MongoDBClient.txt deleted file mode 100644 index b677ebb1d..000000000 --- a/docs/reference/class/MongoDBClient.txt +++ /dev/null @@ -1,45 +0,0 @@ -===================== -MongoDB\\Client Class -===================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpclass:: MongoDB\\Client - - This class serves as an entry point for the |php-library|. It is the - preferred class for connecting to a MongoDB server or cluster of servers and - acts as a gateway for accessing individual databases and collections. - :phpclass:`MongoDB\\Client` is analogous to the driver's - :php:`MongoDB\\Driver\\Manager ` class, which it - `composes `_. - -Methods -------- - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBClient__construct - /reference/method/MongoDBClient__get - /reference/method/MongoDBClient-createClientEncryption - /reference/method/MongoDBClient-dropDatabase - /reference/method/MongoDBClient-getManager - /reference/method/MongoDBClient-getReadConcern - /reference/method/MongoDBClient-getReadPreference - /reference/method/MongoDBClient-getTypeMap - /reference/method/MongoDBClient-getWriteConcern - /reference/method/MongoDBClient-listDatabaseNames - /reference/method/MongoDBClient-listDatabases - /reference/method/MongoDBClient-selectCollection - /reference/method/MongoDBClient-selectDatabase - /reference/method/MongoDBClient-startSession - /reference/method/MongoDBClient-watch diff --git a/docs/reference/class/MongoDBCollection.txt b/docs/reference/class/MongoDBCollection.txt deleted file mode 100644 index 57625ed02..000000000 --- a/docs/reference/class/MongoDBCollection.txt +++ /dev/null @@ -1,99 +0,0 @@ -========================= -MongoDB\\Collection Class -========================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpclass:: MongoDB\\Collection - - Provides methods for common operations on collections and documents, - including CRUD operations and index management. - - You can construct collections directly using the driver's - :php:`MongoDB\\Driver\\Manager ` class or - select a collection from the library's :phpclass:`MongoDB\\Client` or - :phpclass:`MongoDB\\Database` classes. A collection may also be cloned from - an existing :phpclass:`MongoDB\\Collection` object via the - :phpmethod:`withOptions() ` method. - - :phpclass:`MongoDB\\Collection` supports the :php:`readConcern - `, :php:`readPreference - `, :php:`typeMap - `, - and :php:`writeConcern ` options. If you omit an - option, the collection inherits the value from the :php:`Manager - ` constructor argument or the :phpclass:`Client - ` or :phpclass:`Database ` object used to - select the collection. - - Operations within the :phpclass:`MongoDB\\Collection` class inherit the - collection's options. - -Type Map Limitations --------------------- - - The :manual:`aggregate ` (when not using a - cursor), :manual:`distinct `, and - :manual:`findAndModify ` helpers do not - support a ``typeMap`` option due to a driver limitation. The - :phpmethod:`aggregate() `, - :phpmethod:`distinct() `, - :phpmethod:`findOneAndReplace() `, - :phpmethod:`findOneAndUpdate() `, and - :phpmethod:`findOneAndDelete() ` - methods return BSON documents as `stdClass` objects and BSON arrays as - arrays. - -Methods -------- - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBCollection__construct - /reference/method/MongoDBCollection-aggregate - /reference/method/MongoDBCollection-bulkWrite - /reference/method/MongoDBCollection-count - /reference/method/MongoDBCollection-countDocuments - /reference/method/MongoDBCollection-createIndex - /reference/method/MongoDBCollection-createIndexes - /reference/method/MongoDBCollection-deleteMany - /reference/method/MongoDBCollection-deleteOne - /reference/method/MongoDBCollection-distinct - /reference/method/MongoDBCollection-drop - /reference/method/MongoDBCollection-dropIndex - /reference/method/MongoDBCollection-dropIndexes - /reference/method/MongoDBCollection-estimatedDocumentCount - /reference/method/MongoDBCollection-explain - /reference/method/MongoDBCollection-find - /reference/method/MongoDBCollection-findOne - /reference/method/MongoDBCollection-findOneAndDelete - /reference/method/MongoDBCollection-findOneAndReplace - /reference/method/MongoDBCollection-findOneAndUpdate - /reference/method/MongoDBCollection-getCollectionName - /reference/method/MongoDBCollection-getDatabaseName - /reference/method/MongoDBCollection-getManager - /reference/method/MongoDBCollection-getNamespace - /reference/method/MongoDBCollection-getReadConcern - /reference/method/MongoDBCollection-getReadPreference - /reference/method/MongoDBCollection-getTypeMap - /reference/method/MongoDBCollection-getWriteConcern - /reference/method/MongoDBCollection-insertMany - /reference/method/MongoDBCollection-insertOne - /reference/method/MongoDBCollection-listIndexes - /reference/method/MongoDBCollection-mapReduce - /reference/method/MongoDBCollection-rename - /reference/method/MongoDBCollection-replaceOne - /reference/method/MongoDBCollection-updateMany - /reference/method/MongoDBCollection-updateOne - /reference/method/MongoDBCollection-watch - /reference/method/MongoDBCollection-withOptions diff --git a/docs/reference/class/MongoDBDatabase.txt b/docs/reference/class/MongoDBDatabase.txt deleted file mode 100644 index 7e7cb73d9..000000000 --- a/docs/reference/class/MongoDBDatabase.txt +++ /dev/null @@ -1,67 +0,0 @@ -======================= -MongoDB\\Database Class -======================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpclass:: MongoDB\\Database - - Provides methods for common operations on a database, such as executing - database commands and managing collections. - - You can construct a database directly using the driver's - :php:`MongoDB\\Driver\\Manager ` class or - select a database from the library's :phpclass:`MongoDB\\Client` class. A - database may also be cloned from an existing :phpclass:`MongoDB\\Database` - object via the :phpmethod:`withOptions() ` - method. - - :phpclass:`MongoDB\\Database` supports the :php:`readConcern - `, :php:`readPreference - `, :php:`typeMap - `, - and :php:`writeConcern ` options. If you omit an - option, the database inherits the value from the :php:`Manager - ` constructor argument or the :phpclass:`Client - ` object used to select the database. - - Operations within the :phpclass:`MongoDB\\Database` class inherit the - Database's options. - -Methods -------- - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBDatabase__construct - /reference/method/MongoDBDatabase__get - /reference/method/MongoDBDatabase-aggregate - /reference/method/MongoDBDatabase-command - /reference/method/MongoDBDatabase-createCollection - /reference/method/MongoDBDatabase-drop - /reference/method/MongoDBDatabase-dropCollection - /reference/method/MongoDBDatabase-getDatabaseName - /reference/method/MongoDBDatabase-getManager - /reference/method/MongoDBDatabase-getReadConcern - /reference/method/MongoDBDatabase-getReadPreference - /reference/method/MongoDBDatabase-getTypeMap - /reference/method/MongoDBDatabase-getWriteConcern - /reference/method/MongoDBDatabase-listCollectionNames - /reference/method/MongoDBDatabase-listCollections - /reference/method/MongoDBDatabase-modifyCollection - /reference/method/MongoDBDatabase-renameCollection - /reference/method/MongoDBDatabase-selectCollection - /reference/method/MongoDBDatabase-selectGridFSBucket - /reference/method/MongoDBDatabase-watch - /reference/method/MongoDBDatabase-withOptions - diff --git a/docs/reference/class/MongoDBGridFSBucket.txt b/docs/reference/class/MongoDBGridFSBucket.txt deleted file mode 100644 index cea877f43..000000000 --- a/docs/reference/class/MongoDBGridFSBucket.txt +++ /dev/null @@ -1,59 +0,0 @@ -============================= -MongoDB\\GridFS\\Bucket Class -============================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpclass:: MongoDB\\GridFS\\Bucket - - :manual:`GridFS ` is a specification for storing and retrieving - files in MongoDB. GridFS uses two collections to store files. One collection - stores the file chunks (e.g. ``fs.chunks``), and the other stores file - metadata (e.g. ``fs.files``). The :phpclass:`MongoDB\\GridFS\\Bucket` class - provides an interface around these collections for working with the files as - PHP :php:`Streams `. - - You can construct a GridFS bucket using the driver's - :php:`Manager ` class, or select a bucket from - the library's :phpclass:`MongoDB\\Database` class via the - :phpmethod:`selectGridFSBucket() ` - method. - -Methods -------- - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBGridFSBucket__construct - /reference/method/MongoDBGridFSBucket-delete - /reference/method/MongoDBGridFSBucket-downloadToStream - /reference/method/MongoDBGridFSBucket-downloadToStreamByName - /reference/method/MongoDBGridFSBucket-drop - /reference/method/MongoDBGridFSBucket-find - /reference/method/MongoDBGridFSBucket-findOne - /reference/method/MongoDBGridFSBucket-getBucketName - /reference/method/MongoDBGridFSBucket-getChunksCollection - /reference/method/MongoDBGridFSBucket-getChunkSizeBytes - /reference/method/MongoDBGridFSBucket-getDatabaseName - /reference/method/MongoDBGridFSBucket-getFileDocumentForStream - /reference/method/MongoDBGridFSBucket-getFileIdForStream - /reference/method/MongoDBGridFSBucket-getFilesCollection - /reference/method/MongoDBGridFSBucket-getReadConcern - /reference/method/MongoDBGridFSBucket-getReadPreference - /reference/method/MongoDBGridFSBucket-getTypeMap - /reference/method/MongoDBGridFSBucket-getWriteConcern - /reference/method/MongoDBGridFSBucket-openDownloadStream - /reference/method/MongoDBGridFSBucket-openDownloadStreamByName - /reference/method/MongoDBGridFSBucket-openUploadStream - /reference/method/MongoDBGridFSBucket-rename - /reference/method/MongoDBGridFSBucket-uploadFromStream diff --git a/docs/reference/enumeration-classes.txt b/docs/reference/enumeration-classes.txt deleted file mode 100644 index bb2a629a8..000000000 --- a/docs/reference/enumeration-classes.txt +++ /dev/null @@ -1,193 +0,0 @@ -=================== -Enumeration Classes -=================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB\\Model\\CollectionInfo ------------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\Model\\CollectionInfo - - This class models information about a collection. Instances of this class are - returned by traversing a :phpclass:`MongoDB\\Model\\CollectionInfoIterator`, - which is returned by :phpmethod:`MongoDB\\Database::listCollections()`. - -.. versionchanged:: 1.4 - - This class implements PHP's :php:`ArrayAccess ` interface. This - provides a mechanism for accessing index fields for which there exists no - helper method. :php:`isset() ` may be used to check for the existence - of a field before accessing it with ``[]``. - - .. note:: - - The :phpclass:`MongoDB\\Model\\CollectionInfo` class is immutable. Attempting - to modify it via the :php:`ArrayAccess ` interface will - result in a :phpclass:`MongoDB\\Exception\\BadMethodCallException`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBModelCollectionInfo-getCappedMax - /reference/method/MongoDBModelCollectionInfo-getCappedSize - /reference/method/MongoDBModelCollectionInfo-getIdIndex - /reference/method/MongoDBModelCollectionInfo-getInfo - /reference/method/MongoDBModelCollectionInfo-getName - /reference/method/MongoDBModelCollectionInfo-getOptions - /reference/method/MongoDBModelCollectionInfo-getType - /reference/method/MongoDBModelCollectionInfo-isCapped - ----- - -MongoDB\\Model\\CollectionInfoIterator --------------------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\Model\\CollectionInfoIterator - - This interface extends PHP's :php:`Iterator ` - interface. An instance of this interface is returned by - :phpmethod:`MongoDB\\Database::listCollections()`. - -Methods -~~~~~~~ - -This interface adds no new methods to :php:`Iterator -`, but specifies that :php:`current() -` will return an instance of -:phpclass:`MongoDB\\Model\\CollectionInfo`. - ----- - -MongoDB\\Model\\DatabaseInfo ----------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\Model\\DatabaseInfo - - This class models information about a database. Instances of this class are - returned by traversing a :phpclass:`MongoDB\\Model\\DatabaseInfoIterator`, - which is returned by :phpmethod:`MongoDB\\Client::listDatabases()`. - -.. versionchanged:: 1.4 - - This class implements PHP's :php:`ArrayAccess ` interface. This - provides a mechanism for accessing index fields for which there exists no - helper method. :php:`isset() ` may be used to check for the existence - of a field before accessing it with ``[]``. - - .. note:: - - The :phpclass:`MongoDB\\Model\\DatabaseInfo` class is immutable. Attempting - to modify it via the :php:`ArrayAccess ` interface will - result in a :phpclass:`MongoDB\\Exception\\BadMethodCallException`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBModelDatabaseInfo-getName - /reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk - /reference/method/MongoDBModelDatabaseInfo-isEmpty - ----- - -MongoDB\\Model\\DatabaseInfoIterator ------------------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\Model\\DatabaseInfoIterator - - This interface extends PHP's :php:`Iterator ` - interface. An instance of this interface is returned by - :phpmethod:`MongoDB\\Client::listDatabases()`. - -Methods -~~~~~~~ - -This interface adds no new methods to :php:`Iterator -`, but specifies that :php:`current() -` will return an instance of -:phpclass:`MongoDB\\Model\\DatabaseInfo`. - ----- - -MongoDB\\Model\\IndexInfo -------------------------- - -.. phpclass:: MongoDB\\Model\\IndexInfo - - This class models information about an index. Instances of this class are - returned by traversing a :phpclass:`MongoDB\\Model\\IndexInfoIterator`, - which is returned by :phpmethod:`MongoDB\\Collection::listIndexes()`. - - This class implements PHP's :php:`ArrayAccess ` interface. This - provides a mechanism for accessing index fields for which there exists no - helper method. :php:`isset() ` may be used to check for the existence - of a field before accessing it with ``[]``. - - .. note:: - - The :phpclass:`MongoDB\\Model\\IndexInfo` class is immutable. Attempting - to modify it via the :php:`ArrayAccess ` interface will - result in a :phpclass:`MongoDB\\Exception\\BadMethodCallException`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBModelIndexInfo-getKey - /reference/method/MongoDBModelIndexInfo-getName - /reference/method/MongoDBModelIndexInfo-getNamespace - /reference/method/MongoDBModelIndexInfo-getVersion - /reference/method/MongoDBModelIndexInfo-is2dSphere - /reference/method/MongoDBModelIndexInfo-isGeoHaystack - /reference/method/MongoDBModelIndexInfo-isSparse - /reference/method/MongoDBModelIndexInfo-isText - /reference/method/MongoDBModelIndexInfo-isTtl - /reference/method/MongoDBModelIndexInfo-isUnique - ----- - -MongoDB\\Model\\IndexInfoIterator ---------------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\Model\\IndexInfoIterator - - This interface extends PHP's :php:`Iterator ` - interface. An instance of this interface is returned by - :phpmethod:`MongoDB\\Collection::listIndexes()`. - -Methods -~~~~~~~ - -This interface adds no new methods to :php:`Iterator -`, but specifies that :php:`current() -` will return an instance of -:phpclass:`MongoDB\\Model\\IndexInfo`. diff --git a/docs/reference/exception-classes.txt b/docs/reference/exception-classes.txt deleted file mode 100644 index b5f9fb0b6..000000000 --- a/docs/reference/exception-classes.txt +++ /dev/null @@ -1,134 +0,0 @@ -================= -Exception Classes -================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB\\Exception\\BadMethodCallException ------------------------------------------- - -.. phpclass:: MongoDB\\Exception\\BadMethodCallException - - This exception is thrown when an unsupported method is invoked on an object. - - For example, using an unacknowledged write concern with - :phpmethod:`MongoDB\\Collection::insertMany()` will return a - :phpclass:`MongoDB\\InsertManyResult` object. It is a logical error to call - :phpmethod:`MongoDB\\InsertManyResult::getInsertedCount()`, since the number - of inserted documents can only be determined from the response of an - acknowledged write operation. - - This class extends PHP's :php:`BadMethodCallException - ` class and implements the library's - :phpclass:`Exception ` interface. - ----- - -MongoDB\\Exception\\InvalidArgumentException --------------------------------------------- - -.. phpclass:: MongoDB\\Exception\\InvalidArgumentException - - Thrown for errors related to the parsing of parameters or options within the - library. - - This class extends the driver's :php:`InvalidArgumentException - ` class and implements the - library's :phpclass:`Exception ` interface. - ----- - -MongoDB\\Exception\\UnexpectedValueException --------------------------------------------- - -.. phpclass:: MongoDB\\Exception\\UnexpectedValueException - - This exception is thrown when a command response from the server is - malformed or not what the library expected. This exception means that an - assertion in some operation, which abstracts a database command, has failed. - It may indicate a corrupted BSON response or bug in the server, driver, or - library. - - This class extends the driver's :php:`UnexpectedValueException - ` class and implements the - library's :phpclass:`Exception ` interface. - ----- - -MongoDB\\Exception\\UnsupportedException ----------------------------------------- - -.. phpclass:: MongoDB\\Exception\\UnsupportedException - - This exception is thrown if an option is used and not supported by the - selected server. It is used sparingly in cases where silently ignoring the - unsupported option might otherwise lead to unexpected behavior. - - This class extends the library's :phpclass:`RuntimeException - ` class. - - .. note:: - - Unlike :phpclass:`InvalidArgumentException - `, which may be thrown when - an operation's parameters and options are parsed during construction, the - selected server is not known until an operation is executed. - ----- - -MongoDB\\GridFS\\Exception\\CorruptFileException ------------------------------------------------- - -.. phpclass:: MongoDB\\GridFS\\Exception\\CorruptFileException - - This exception is thrown if a GridFS file's metadata or chunk documents - contain unexpected or invalid data. - - When selecting a GridFS file, this may be thrown if a metadata field has an - incorrect type or its value is out of range (e.g. negative ``length``). When - reading a GridFS file, this may be thrown if a chunk's index is out of - sequence or its binary data's length out of range. - - This class extends the library's :phpclass:`RuntimeException - ` class. - ----- - -MongoDB\\GridFS\\Exception\\FileNotFoundException -------------------------------------------------- - -.. phpclass:: MongoDB\\GridFS\\Exception\\FileNotFoundException - - This exception is thrown if no GridFS file was found for the selection - criteria (e.g. ``id``, ``filename``). - - This class extends the library's :phpclass:`RuntimeException - ` class. - ----- - -MongoDB\\Exception\\Exception ------------------------------ - -.. phpclass:: MongoDB\\Exception\\Exception - - This interface extends the driver's :php:`Exception - ` interface and is implemented by all - exception classes within the library. - ----- - -MongoDB\\Exception\\RuntimeException ------------------------------------- - -.. phpclass:: MongoDB\\Exception\\RuntimeException - - This class extends the driver's :php:`RuntimeException - ` class, which in turn extends - PHP's :php:`RuntimeException ` class. diff --git a/docs/reference/function/with_transaction.txt b/docs/reference/function/with_transaction.txt deleted file mode 100644 index f7b7b92d5..000000000 --- a/docs/reference/function/with_transaction.txt +++ /dev/null @@ -1,79 +0,0 @@ -=========================== -MongoDB\\with_transaction() -=========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -.. versionadded:: 1.5 - -Definition ----------- - -.. phpmethod:: MongoDB\\with_transaction() - - Execute a callback within a transaction using the given client session - - .. code-block:: php - - function with_transaction(MongoDB\Driver\Session $session, callable $callback, array $transactionOptions = []): void - - This method has the following parameters: - - .. include:: /includes/apiargs/function-with_transaction-param.rst - -Behavior --------- - -This function is responsible for starting a transaction, invoking a callback, -and committing a transaction. It also applies logic to retry this process after -certain errors within a preset time limit. The callback is expected to execute -one or more operations within the transactionby passing the callback's -:php:`MongoDB\\Driver\\Session ` argument as an option to -those operations; however, that is not enforced. - -.. note:: - - Applications are strongly encouraged to use an - `idempotent `_ callback, since it - may be invoked multiple times if retryable errors are encountered during - either callback execution or committing. - -Any exception thrown during execution of the callback will be caught and -evaluated. If an exception has a ``TransientTransactionError`` error label, the -transaction will be aborted, restarted, and the callback will be invoked again. -For any other exception, the transaction will be aborted and the exception -re-thrown to propagate the error to the caller of ``with_transaction()``. - -Following successful execution of the callback, the transaction will be -committed. If an exception with an UnknownTransactionCommitResult error label is -encountered, the commit will be retried. If an exception with a -``TransientTransactionError`` error label is encountered, the transaction will -be restarted and control will return to invoking the callback. Any other -exception will be re-thrown to propagate the error to the caller of -``with_transaction()``. - -When an error occurs during callback execution or committing, the process is -only retried if fewer than 120 seconds have elapsed since ``with_transaction()`` -was first called. This time limit is not configurable. After this time, any -exception that would normally result in a retry attempt will instead be -re-thrown. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\Session::startTransaction ` -- :php:`MongoDB\\Driver\\Session::commitTransaction ` -- :manual:`Transactions: Drivers API ` documentation in the MongoDB manual -- `Convenient API for Transactions `_ specification diff --git a/docs/reference/functions.txt b/docs/reference/functions.txt deleted file mode 100644 index 4ce49e3f9..000000000 --- a/docs/reference/functions.txt +++ /dev/null @@ -1,16 +0,0 @@ -========= -Functions -========= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -.. toctree:: - :titlesonly: - - /reference/function/with_transaction diff --git a/docs/reference/method/MongoDBBulkWriteResult-getDeletedCount.txt b/docs/reference/method/MongoDBBulkWriteResult-getDeletedCount.txt deleted file mode 100644 index d0501d3f5..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getDeletedCount.txt +++ /dev/null @@ -1,42 +0,0 @@ -=========================================== -MongoDB\\BulkWriteResult::getDeletedCount() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getDeletedCount() - - Return the total number of documents that were deleted by all delete - operations in the bulk write. - - .. code-block:: php - - function getDeletedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The total number of documents that were deleted by all delete operations in the -bulk write. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getDeletedCount() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-getInsertedCount.txt b/docs/reference/method/MongoDBBulkWriteResult-getInsertedCount.txt deleted file mode 100644 index 6eb1b5de1..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getInsertedCount.txt +++ /dev/null @@ -1,42 +0,0 @@ -============================================ -MongoDB\\BulkWriteResult::getInsertedCount() -============================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getInsertedCount() - - Return the total number of documents that were inserted by all insert - operations in the bulk write. - - .. code-block:: php - - function getInsertedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The total number of documents that were inserted by all insert operations in the -bulk write. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getInsertedCount() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-getInsertedIds.txt b/docs/reference/method/MongoDBBulkWriteResult-getInsertedIds.txt deleted file mode 100644 index bcbcd536d..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getInsertedIds.txt +++ /dev/null @@ -1,38 +0,0 @@ -========================================== -MongoDB\\BulkWriteResult::getInsertedIds() -========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getInsertedIds() - - Return a map of IDs (i.e. ``_id`` field values) for documents that were - inserted by all insert operations in the bulk write. - - .. code-block:: php - - function getInsertedIds(): array - - Since IDs are created by the driver, this method may be called irrespective - of whether the write was acknowledged. - -Return Values -------------- - -A map of IDs (i.e. ``_id`` field values) for documents that were inserted by all -insert operations in the bulk write. - -The index of each ID in the map corresponds to each document's position in the -bulk operation. If a document had an ID prior to inserting (i.e. the driver did -not generate an ID), the index will contain its ``_id`` field value. Any -driver-generated ID will be a :php:`MongoDB\\BSON\\ObjectId -` instance. diff --git a/docs/reference/method/MongoDBBulkWriteResult-getMatchedCount.txt b/docs/reference/method/MongoDBBulkWriteResult-getMatchedCount.txt deleted file mode 100644 index af1ad98f6..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getMatchedCount.txt +++ /dev/null @@ -1,51 +0,0 @@ -=========================================== -MongoDB\\BulkWriteResult::getMatchedCount() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getMatchedCount() - - Return the total number of documents that were matched by all update and - replace operations in the bulk write. - - .. code-block:: php - - function getMatchedCount(): integer - - This method should only be called if the write was acknowledged. - - .. note:: - - If an update/replace operation results in no change to the document - (e.g. setting the value of a field to its current value), the matched - count may be greater than the value returned by - :phpmethod:`getModifiedCount() - `. - -Return Values -------------- - -The total number of documents that were matched by all update and replace -operations in the bulk write. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :phpmethod:`MongoDB\\BulkWriteResult::getModifiedCount()` -- :php:`MongoDB\\Driver\\WriteResult::getMatchedCount() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-getModifiedCount.txt b/docs/reference/method/MongoDBBulkWriteResult-getModifiedCount.txt deleted file mode 100644 index 2e8f708b0..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getModifiedCount.txt +++ /dev/null @@ -1,50 +0,0 @@ -============================================ -MongoDB\\BulkWriteResult::getModifiedCount() -============================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getModifiedCount() - - Return the total number of documents that were modified by all update and - replace operations in the bulk write. - - .. code-block:: php - - function getModifiedCount(): integer|null - - This method should only be called if the write was acknowledged. - - .. note:: - - If an update/replace operation results in no change to the document - (e.g. setting the value of a field to its current value), the modified - count may be less than the value returned by :phpmethod:`getMatchedCount() - `. - -Return Values -------------- - -The total number of documents that were modified by all update and replace -operations in the bulk write. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :phpmethod:`MongoDB\\BulkWriteResult::getMatchedCount()` -- :php:`MongoDB\\Driver\\WriteResult::getModifiedCount() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-getUpsertedCount.txt b/docs/reference/method/MongoDBBulkWriteResult-getUpsertedCount.txt deleted file mode 100644 index 1f0dff2b9..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getUpsertedCount.txt +++ /dev/null @@ -1,42 +0,0 @@ -============================================ -MongoDB\\BulkWriteResult::getUpsertedCount() -============================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getUpsertedCount() - - Return the total number of documents that were upserted by all update and - replace operations in the bulk write. - - .. code-block:: php - - function getUpsertedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The total number of documents that were upserted by all update and replace -operations in the bulk write. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getUpsertedCount() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-getUpsertedIds.txt b/docs/reference/method/MongoDBBulkWriteResult-getUpsertedIds.txt deleted file mode 100644 index 6abad98f2..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-getUpsertedIds.txt +++ /dev/null @@ -1,46 +0,0 @@ -========================================== -MongoDB\\BulkWriteResult::getUpsertedIds() -========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::getUpsertedIds() - - Return a map of IDs (i.e. ``_id`` field values) for documents that were - upserted by all update and replace operations in the bulk write. - - .. code-block:: php - - function getUpsertedIds(): array - -Return Values -------------- - -A map of IDs (i.e. ``_id`` field values) for documents that were upserted by all -update and replace operations in the bulk write. - -The index of each ID in the map corresponds to each document's position in the -bulk operation. If a document had an ID prior to upserting (i.e. the server did -not generate an ID), the index will contain its ``_id`` field value. Any -server-generated ID will be a :php:`MongoDB\\BSON\\ObjectId -` instance. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getUpsertedIds() - ` diff --git a/docs/reference/method/MongoDBBulkWriteResult-isAcknowledged.txt b/docs/reference/method/MongoDBBulkWriteResult-isAcknowledged.txt deleted file mode 100644 index 1a857caaa..000000000 --- a/docs/reference/method/MongoDBBulkWriteResult-isAcknowledged.txt +++ /dev/null @@ -1,34 +0,0 @@ -========================================== -MongoDB\\BulkWriteResult::isAcknowledged() -========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\BulkWriteResult::isAcknowledged() - - Return whether the write was acknowledged. - - .. code-block:: php - - function isAcknowledged(): boolean - -Return Values -------------- - -A boolean indicating whether the write was acknowledged. - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::isAcknowledged() - ` -- :manual:`Write Concern ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-current.txt b/docs/reference/method/MongoDBChangeStream-current.txt deleted file mode 100644 index a85786073..000000000 --- a/docs/reference/method/MongoDBChangeStream-current.txt +++ /dev/null @@ -1,106 +0,0 @@ -================================ -MongoDB\\ChangeStream::current() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::current() - - Returns the current event in the change stream. - - .. code-block:: php - - function current(): array|object|null - - The structure of each event document will vary based on the operation type. - See :manual:`Change Events ` in the MongoDB manual - for more information. - -Return Values -------------- - -An array or object for the current event in the change stream, or ``null`` if -there is no current event (i.e. :phpmethod:`MongoDB\\ChangeStream::valid()` -returns ``false``). The return type will depend on the ``typeMap`` option for -:phpmethod:`MongoDB\\Collection::watch()`. - -Examples --------- - -This example reports events while iterating a change stream. - -.. code-block:: php - - test->inventory; - - $changeStream = $collection->watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - $ns = sprintf('%s.%s', $event['ns']['db'], $event['ns']['coll']); - $id = json_encode($event['documentKey']['_id']); - - switch ($event['operationType']) { - case 'delete': - printf("Deleted document in %s with _id: %s\n\n", $ns, $id); - break; - - case 'insert': - printf("Inserted new document in %s\n", $ns); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'replace': - printf("Replaced new document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'update': - printf("Updated document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['updateDescription']), "\n\n"; - break; - } - } - -Assuming that a document was inserted, updated, and deleted while the above -script was iterating the change stream, the output would then resemble: - -.. code-block:: none - - Inserted new document in test.inventory - {"_id":{"$oid":"5a81fc0d6118fd1af1790d32"},"name":"Widget","quantity":5} - - Updated document in test.inventory with _id: {"$oid":"5a81fc0d6118fd1af1790d32"} - {"updatedFields":{"quantity":4},"removedFields":[]} - - Deleted document in test.inventory with _id: {"$oid":"5a81fc0d6118fd1af1790d32"} - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`Iterator::current() ` -- :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual -- :manual:`Change Events ` documentation in the - MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-getCursorId.txt b/docs/reference/method/MongoDBChangeStream-getCursorId.txt deleted file mode 100644 index 1938d3c00..000000000 --- a/docs/reference/method/MongoDBChangeStream-getCursorId.txt +++ /dev/null @@ -1,60 +0,0 @@ -==================================== -MongoDB\\ChangeStream::getCursorId() -==================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::getCursorId() - - Returns the change stream cursor's ID. - - .. code-block:: php - - function getCursorId(): MongoDB\Driver\CursorId - -Return Values -------------- - -A :php:`MongoDB\\Driver\\CursorId ` object. - -Examples --------- - -This example reports the cursor ID for a change stream. - -.. code-block:: php - - test->inventory; - - $changeStream = $collection->watch(); - - var_dump($changeStream->getCursorId()); - -The output would then resemble:: - - object(MongoDB\Driver\CursorId)#5 (1) { - ["id"]=> - int(8462642181784669708) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`MongoDB\\Driver\\CursorId ` -- :php:`MongoDB\\Driver\\Cursor::getId() ` diff --git a/docs/reference/method/MongoDBChangeStream-getResumeToken.txt b/docs/reference/method/MongoDBChangeStream-getResumeToken.txt deleted file mode 100644 index 0930d45cf..000000000 --- a/docs/reference/method/MongoDBChangeStream-getResumeToken.txt +++ /dev/null @@ -1,75 +0,0 @@ -======================================= -MongoDB\\ChangeStream::getResumeToken() -======================================= - -.. versionadded:: 1.5 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::getResumeToken() - - Returns the cached resume token that will be used to resume the change - stream. - - .. code-block:: php - - function getResumeToken(): array|object|null - -Return Values -------------- - -An array or object, or ``null`` if there is no cached resume token. The return -type will depend on the ``typeMap`` option for the ``watch()`` method used to -create the change stream. - -Examples --------- - -This example captures the resume token for a change stream after encountering -an ``invalidate`` event and uses it to construct a second change stream using -the ``startAfter`` option. - -.. code-block:: php - - test->inventory; - - $changeStream = $collection->watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - if ($event['operationType'] === 'invalidate') { - $startAfter = $changeStream->getResumeToken(); - break; - } - - printf("%d: %s\n", $changeStream->key(), $event['operationType']); - } - - $changeStream = $collection->watch([], ['startAfter' => $startAfter]); - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :manual:`Resume a Change Stream ` - documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-key.txt b/docs/reference/method/MongoDBChangeStream-key.txt deleted file mode 100644 index de4754b4f..000000000 --- a/docs/reference/method/MongoDBChangeStream-key.txt +++ /dev/null @@ -1,76 +0,0 @@ -============================ -MongoDB\\ChangeStream::key() -============================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::key() - - Returns the index of the current event in the change stream. - - .. code-block:: php - - function key(): integer|null - - The index of the first event in a change stream starts at zero and will - increment by one for each subsequent event. - -Return Values -------------- - -The index of the current event in the change stream, or ``null`` if there is no -current event (i.e. :phpmethod:`MongoDB\\ChangeStream::valid()` returns -``false``). - -Examples --------- - -This example reports the index of events while iterating a change stream. - -.. code-block:: php - - test->inventory; - - $changeStream = $collection->watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - printf("%d: %s\n", $changeStream->key(), $event['operationType']); - } - -Assuming that a document was inserted, updated, and deleted while the above -script was iterating the change stream, the output would then resemble: - -.. code-block:: none - - 0: insert - 1: update - 2: delete - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`Iterator::key() ` -- :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-next.txt b/docs/reference/method/MongoDBChangeStream-next.txt deleted file mode 100644 index c25f039b3..000000000 --- a/docs/reference/method/MongoDBChangeStream-next.txt +++ /dev/null @@ -1,44 +0,0 @@ -============================= -MongoDB\\ChangeStream::next() -============================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::next() - - Advances the change stream and attempts to load the next event. - - .. code-block:: php - - function next(): void - - .. note:: - - Advancing the change stream does not guarantee that there will be a - current event to access. You should still call - :phpmethod:`MongoDB\\ChangeStream::valid()` to check for a current event - at each step of iteration. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`Iterator::next() ` -- :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-rewind.txt b/docs/reference/method/MongoDBChangeStream-rewind.txt deleted file mode 100644 index e59a1399b..000000000 --- a/docs/reference/method/MongoDBChangeStream-rewind.txt +++ /dev/null @@ -1,54 +0,0 @@ -=============================== -MongoDB\\ChangeStream::rewind() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::rewind() - - Rewinds the change stream and attempts to load the first event. - - .. code-block:: php - - function rewind(): void - - This method should be called at the start of change stream iteration. - - .. note:: - - Rewinding the change stream does not guarantee that there will be a - current event to access. You should still call - :phpmethod:`MongoDB\\ChangeStream::valid()` to check for a current event - at each step of iteration. After initially rewinding the change stream, - :phpmethod:`MongoDB\\ChangeStream::next()` should be used to iterate - further. - -Errors/Exceptions ------------------ - -:php:`MongoDB\\Driver\\Exception\\LogicException -` if this method is called after a call -to :phpmethod:`MongoDB\\ChangeStream::next()` (i.e. the underlying -:php:`MongoDB\\Driver\\Cursor ` has already been -advanced). - -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`Iterator::rewind() ` -- :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBChangeStream-valid.txt b/docs/reference/method/MongoDBChangeStream-valid.txt deleted file mode 100644 index 69ea1cd68..000000000 --- a/docs/reference/method/MongoDBChangeStream-valid.txt +++ /dev/null @@ -1,42 +0,0 @@ -============================== -MongoDB\\ChangeStream::valid() -============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\ChangeStream::valid() - - Returns whether there is a current event in the change stream. - - .. code-block:: php - - function valid(): boolean - - When manually iterating the change stream using - :php:`Iterator ` methods, this method should - be used to determine if :phpmethod:`MongoDB\\ChangeStream::current()` and - :phpmethod:`MongoDB\\ChangeStream::key()` can be called. - -Return Values -------------- - -A boolean indicating whether there is a current event in the change stream. - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :php:`Iterator::valid() ` -- :ref:`Tailable Cursor Iteration ` -- :manual:`Change Streams ` documentation in the MongoDB manual diff --git a/docs/reference/method/MongoDBClient-createClientEncryption.txt b/docs/reference/method/MongoDBClient-createClientEncryption.txt deleted file mode 100644 index 9b6f817ea..000000000 --- a/docs/reference/method/MongoDBClient-createClientEncryption.txt +++ /dev/null @@ -1,51 +0,0 @@ -========================================= -MongoDB\\Client::createClientEncryption() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::createClientEncryption() - - Returns a :php:`MongoDB\\Driver\\ClientEncryption ` - object for manual encryption and decryption of values. - - .. code-block:: php - - function createClientEncryption(array $options): MongoDB\Driver\ClientEncryption - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-createClientEncryption-param.rst - - The ``$options`` parameter supports all options documented in the - :php:`extension manual `. - For the ``keyVaultClient`` option, an instance of :phpclass:`MongoDB\\Client` - is automatically unwrapped and the :php:`MongoDB\\Driver\\Manager ` - instance is passed to the extension. - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ClientEncryption ` -instance which can be used to encrypt and decrypt values. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-invalidargumentexception.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\Manager::createClientEncryption() - ` diff --git a/docs/reference/method/MongoDBClient-dropDatabase.txt b/docs/reference/method/MongoDBClient-dropDatabase.txt deleted file mode 100644 index 4369d07f2..000000000 --- a/docs/reference/method/MongoDBClient-dropDatabase.txt +++ /dev/null @@ -1,78 +0,0 @@ -=============================== -MongoDB\\Client::dropDatabase() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::dropDatabase() - - Drop a database on the server. - - .. code-block:: php - - function dropDatabase(string $databaseName, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-dropDatabase-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-dropDatabase-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`dropDatabase -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example drops the ``test`` database: - -.. code-block:: php - - dropDatabase('test'); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dropped"]=> - string(4) "test" - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::drop()` -- :manual:`dropDatabase ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBClient-getManager.txt b/docs/reference/method/MongoDBClient-getManager.txt deleted file mode 100644 index c320595d3..000000000 --- a/docs/reference/method/MongoDBClient-getManager.txt +++ /dev/null @@ -1,35 +0,0 @@ -============================= -MongoDB\\Client::getManager() -============================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::getManager() - - Accessor for the - :php:`MongoDB\\Driver\\Manager ` used by this - :phpclass:`Client `. - - .. code-block:: php - - function getManager(): MongoDB\Manager - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Manager ` object. - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::getManager()` -- :phpmethod:`MongoDB\\Database::getManager()` diff --git a/docs/reference/method/MongoDBClient-getReadConcern.txt b/docs/reference/method/MongoDBClient-getReadConcern.txt deleted file mode 100644 index b7c4c60bc..000000000 --- a/docs/reference/method/MongoDBClient-getReadConcern.txt +++ /dev/null @@ -1,58 +0,0 @@ -================================= -MongoDB\\Client::getReadConcern() -================================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::getReadConcern() - - Returns the read concern for this client. - - .. code-block:: php - - function getReadConcern(): MongoDB\Driver\ReadConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadConcern ` object. - -Example -------- - -.. code-block:: php - - 'majority', - ]); - - var_dump($client->getReadConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadConcern)#5 (1) { - ["level"]=> - string(8) "majority" - } - -See Also --------- - -- :manual:`Read Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\ReadConcern::isDefault() ` -- :phpmethod:`MongoDB\\Collection::getReadConcern()` -- :phpmethod:`MongoDB\\Database::getReadConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadConcern()` diff --git a/docs/reference/method/MongoDBClient-getReadPreference.txt b/docs/reference/method/MongoDBClient-getReadPreference.txt deleted file mode 100644 index 96d8eec59..000000000 --- a/docs/reference/method/MongoDBClient-getReadPreference.txt +++ /dev/null @@ -1,58 +0,0 @@ -==================================== -MongoDB\\Client::getReadPreference() -==================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::getReadPreference() - - Returns the read preference for this client. - - .. code-block:: php - - function getReadPreference(): MongoDB\Driver\ReadPreference - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadPreference ` -object. - -Example -------- - -.. code-block:: php - - 'primaryPreferred', - ]); - - var_dump($client->getReadPreference()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadPreference)#5 (1) { - ["mode"]=> - string(16) "primaryPreferred" - } - -See Also --------- - -- :manual:`Read Preference ` in the MongoDB manual -- :phpmethod:`MongoDB\\Collection::getReadPreference()` -- :phpmethod:`MongoDB\\Database::getReadPreference()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadPreference()` diff --git a/docs/reference/method/MongoDBClient-getTypeMap.txt b/docs/reference/method/MongoDBClient-getTypeMap.txt deleted file mode 100644 index 04697f335..000000000 --- a/docs/reference/method/MongoDBClient-getTypeMap.txt +++ /dev/null @@ -1,65 +0,0 @@ -============================= -MongoDB\\Client::getTypeMap() -============================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::getTypeMap() - - Returns the type map for this client. - - .. code-block:: php - - function getTypeMap(): array - -Return Values -------------- - -A :ref:`type map ` array. - -Example -------- - -.. code-block:: php - - [ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ], - ]); - - var_dump($client->getTypeMap()); - -The output would then resemble:: - - array(3) { - ["root"]=> - string(5) "array" - ["document"]=> - string(5) "array" - ["array"]=> - string(5) "array" - } - -See Also --------- - -- :doc:`/reference/bson` -- :phpmethod:`MongoDB\\Collection::getTypeMap()` -- :phpmethod:`MongoDB\\Database::getTypeMap()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getTypeMap()` diff --git a/docs/reference/method/MongoDBClient-getWriteConcern.txt b/docs/reference/method/MongoDBClient-getWriteConcern.txt deleted file mode 100644 index b779aae96..000000000 --- a/docs/reference/method/MongoDBClient-getWriteConcern.txt +++ /dev/null @@ -1,59 +0,0 @@ -================================== -MongoDB\\Client::getWriteConcern() -================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::getWriteConcern() - - Returns the write concern for this client. - - .. code-block:: php - - function getWriteConcern(): MongoDB\Driver\WriteConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\WriteConcern ` -object. - -Example -------- - -.. code-block:: php - - true, - ]); - - var_dump($client->getWriteConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\WriteConcern)#4 (1) { - ["j"]=> - bool(true) - } - -See Also --------- - -- :manual:`Write Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\WriteConcern::isDefault() ` -- :phpmethod:`MongoDB\\Collection::getWriteConcern()` -- :phpmethod:`MongoDB\\Database::getWriteConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getWriteConcern()` diff --git a/docs/reference/method/MongoDBClient-listDatabaseNames.txt b/docs/reference/method/MongoDBClient-listDatabaseNames.txt deleted file mode 100644 index 5047b88d7..000000000 --- a/docs/reference/method/MongoDBClient-listDatabaseNames.txt +++ /dev/null @@ -1,75 +0,0 @@ -==================================== -MongoDB\\Client::listDatabaseNames() -==================================== - -.. versionadded:: 1.7 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::listDatabaseNames() - - Returns names for all databases on the server. - - .. code-block:: php - - function listDatabaseNames(array $options = []): Iterator - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-listDatabases-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-listDatabases-option.rst - -Return Values -------------- - -An :php:`Iterator `, which provides the name of each -database on the server. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example lists all databases on the server: - -.. code-block:: php - - listDatabaseNames() as $databaseName) { - var_dump($databaseName); - } - -The output would then resemble:: - - string(5) "local" - string(4) "test" - -See Also --------- - -- :phpmethod:`MongoDB\\Client::listDatabases()` -- :manual:`listDatabases ` command reference - in the MongoDB manual -- `Enumerating Databases - `_ - specification diff --git a/docs/reference/method/MongoDBClient-listDatabases.txt b/docs/reference/method/MongoDBClient-listDatabases.txt deleted file mode 100644 index 7f27cee8e..000000000 --- a/docs/reference/method/MongoDBClient-listDatabases.txt +++ /dev/null @@ -1,88 +0,0 @@ -================================ -MongoDB\\Client::listDatabases() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::listDatabases() - - Returns information for all databases on the server. - - .. code-block:: php - - function listDatabases(array $options = []): MongoDB\Model\DatabaseInfoIterator - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-listDatabases-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-listDatabases-option.rst - -Return Values -------------- - -A traversable :phpclass:`MongoDB\\Model\\DatabaseInfoIterator`, which contains -a :phpclass:`MongoDB\\Model\\DatabaseInfo` object for each database on the -server. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example lists all databases on the server: - -.. code-block:: php - - listDatabases() as $databaseInfo) { - var_dump($databaseInfo); - } - -The output would then resemble:: - - object(MongoDB\Model\DatabaseInfo)#4 (3) { - ["name"]=> - string(5) "local" - ["sizeOnDisk"]=> - float(65536) - ["empty"]=> - bool(false) - } - object(MongoDB\Model\DatabaseInfo)#7 (3) { - ["name"]=> - string(4) "test" - ["sizeOnDisk"]=> - float(32768) - ["empty"]=> - bool(false) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Client::listDatabaseNames()` -- :manual:`listDatabases ` command reference - in the MongoDB manual -- `Enumerating Databases - `_ - specification diff --git a/docs/reference/method/MongoDBClient-selectCollection.txt b/docs/reference/method/MongoDBClient-selectCollection.txt deleted file mode 100644 index 34a251988..000000000 --- a/docs/reference/method/MongoDBClient-selectCollection.txt +++ /dev/null @@ -1,83 +0,0 @@ -=================================== -MongoDB\\Client::selectCollection() -=================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::selectCollection() - - Selects a collection on the server. - - .. code-block:: php - - function selectCollection(string $databaseName, string $collectionName, array $options = []): MongoDB\Collection - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-selectCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-selectCollection-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -The selected collection inherits options such as read preference and type -mapping from the :phpclass:`Client ` object. Options may be -overridden via the ``$options`` parameter. - -Example -------- - -The following example selects the ``users`` collection in the ``test`` database: - -.. code-block:: php - - selectCollection('test', 'users'); - -The following example selects the ``users`` collection in the ``test`` database -with a custom read preference: - -.. code-block:: php - - selectCollection( - 'test', - 'users', - [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ] - ); - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::__construct()` -- :phpmethod:`MongoDB\\Database::selectCollection()` diff --git a/docs/reference/method/MongoDBClient-selectDatabase.txt b/docs/reference/method/MongoDBClient-selectDatabase.txt deleted file mode 100644 index 73ecc2b95..000000000 --- a/docs/reference/method/MongoDBClient-selectDatabase.txt +++ /dev/null @@ -1,82 +0,0 @@ -================================= -MongoDB\\Client::selectDatabase() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::selectDatabase() - - Selects a database on the server. - - .. code-block:: php - - function selectDatabase(string $databaseName, array $options = []): MongoDB\Database - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-selectDatabase-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-selectDatabase-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Database` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -The selected database inherits options such as read preference and type mapping -from the :phpclass:`Client ` object. Options may be overridden -via the ``$options`` parameter. - -Example -------- - -The following example selects the ``test`` database: - -.. code-block:: php - - selectDatabase('test'); - -The following examples selects the ``test`` database with a custom read -preference: - -.. code-block:: php - - selectDatabase( - 'test', - [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ] - ); - -See Also --------- - -- :phpmethod:`MongoDB\\Client::__get()` -- :phpmethod:`MongoDB\\Database::__construct()` diff --git a/docs/reference/method/MongoDBClient-startSession.txt b/docs/reference/method/MongoDBClient-startSession.txt deleted file mode 100644 index c3fcce89a..000000000 --- a/docs/reference/method/MongoDBClient-startSession.txt +++ /dev/null @@ -1,82 +0,0 @@ -=============================== -MongoDB\\Client::startSession() -=============================== - -.. versionadded:: 1.3 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::startSession() - - Start a new client session for use with this client. - - .. code-block:: php - - function startSession(array $options = []): MongoDB\Driver\Session - - Refer to the :php:`MongoDB\\Driver\\Manager::startSession() - ` extension reference for accepted - options. - -Return Values -------------- - -A :php:`MongoDB\Driver\Session ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-driver-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example starts a new session: - -.. code-block:: php - - startSession(); - - var_dump($session); - -The output would then resemble:: - - object(MongoDB\Driver\Session)#2043 (4) { - ["logicalSessionId"]=> - array(1) { - ["id"]=> - object(MongoDB\BSON\Binary)#225 (2) { - ["data"]=> - string(16) "................" - ["type"]=> - int(4) - } - } - ["clusterTime"]=> - NULL - ["causalConsistency"]=> - bool(true) - ["operationTime"]=> - NULL - } - -See Also --------- - -- :php:`MongoDB\\Driver\\Manager::startSession() - ` -- :ref:`Causal Consistency ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBClient-watch.txt b/docs/reference/method/MongoDBClient-watch.txt deleted file mode 100644 index 0e7aab34a..000000000 --- a/docs/reference/method/MongoDBClient-watch.txt +++ /dev/null @@ -1,123 +0,0 @@ -======================== -MongoDB\\Client::watch() -======================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::watch() - - Executes a :manual:`change stream ` operation on the client. - The change stream can be watched for cluster-level changes. - - .. code-block:: php - - function watch(array $pipeline = [], array $options = []): MongoDB\ChangeStream - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-watch-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-watch-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\ChangeStream` object, which allows for iteration of -events in the change stream via the :php:`Iterator ` interface. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -This example reports events while iterating a change stream. - -.. code-block:: php - - watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - if ($event['operationType'] === 'invalidate') { - break; - } - - $ns = sprintf('%s.%s', $event['ns']['db'], $event['ns']['coll']); - $id = json_encode($event['documentKey']['_id']); - - switch ($event['operationType']) { - case 'delete': - printf("Deleted document in %s with _id: %s\n\n", $ns, $id); - break; - - case 'insert': - printf("Inserted new document in %s\n", $ns); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'replace': - printf("Replaced new document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'update': - printf("Updated document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['updateDescription']), "\n\n"; - break; - } - } - -Assuming that a document was inserted, updated, and deleted while the above -script was iterating the change stream, the output would then resemble: - -.. code-block:: none - - Inserted new document in app.user - {"_id":{"$oid":"5b329b6674083047cc05e607"},"username":"bob"} - - Inserted new document in app.products - {"_id":{"$oid":"5b329b6a74083047cc05e608"},"name":"Widget","quantity":5} - - Inserted new document in logs.messages - {"_id":{"$oid":"5b329b7374083047cc05e609"},"msg":"bob purchased a widget"} - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :manual:`Aggregation Pipeline ` documentation in - the MongoDB Manual -- :manual:`Change Streams ` documentation in the MongoDB manual -- :manual:`Change Events ` documentation in the - MongoDB manual diff --git a/docs/reference/method/MongoDBClient__construct.txt b/docs/reference/method/MongoDBClient__construct.txt deleted file mode 100644 index 07694c91e..000000000 --- a/docs/reference/method/MongoDBClient__construct.txt +++ /dev/null @@ -1,160 +0,0 @@ -============================== -MongoDB\\Client::__construct() -============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::__construct() - - Constructs a new :phpclass:`Client ` instance. - - .. code-block:: php - - function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = []) - - This constructor has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-construct-param.rst - - The ``$driverOptions`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBClient-method-construct-driverOptions.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -A :php:`MongoDB\\Driver\\Manager ` is constructed -internally. Per the `Server Discovery and Monitoring -`_ -specification, :php:`MongoDB\\Driver\\Manager::__construct() -` performs no I/O. Connections will be -initialized on demand, when the first operation is executed. - -Examples --------- - -.. start-connecting-include - -Connecting to a Standalone server -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -If you do not specify a ``$uri`` value, the driver connects to a standalone -:program:`mongod` on ``127.0.0.1`` via port ``27017``. To connect to a different -server, pass the corresponding connection string as the first parameter when -creating the :phpclass:`Client ` instance: - -.. code-block:: php - - 'secondaryPreferred', - ] - ); - -Connecting with SSL and Authentication -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example demonstrates how to connect to a MongoDB replica set with -SSL and authentication, as is used for `MongoDB Atlas -`_: - -.. code-block:: php - - 'myUsername', - 'password' => 'myPassword', - 'ssl' => true, - 'replicaSet' => 'myReplicaSet', - 'authSource' => 'admin', - ], - ); - -The driver supports additional :php:`SSL options -`, -which may be specified in the constructor's ``$driverOptions`` parameter. Those -options are covered in the :php:`MongoDB\\Driver\\Manager::__construct() -` documentation. - -.. end-connecting-include - -Specifying a Custom Type Map -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -By default, the |php-library| deserializes BSON documents and arrays -as :phpclass:`MongoDB\\Model\\BSONDocument` and -:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The following -example demonstrates how to have the library unserialize everything as a PHP -array, as was done in the legacy ``mongo`` extension. - -.. code-block:: php - - [ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ], - ] - ); - -See Also --------- - -- :php:`MongoDB\\Driver\\Manager::__construct() - ` -- :manual:`Connection String URI Format ` in the - MongoDB manual -- `Server Discovery and Monitoring - `_ - specification diff --git a/docs/reference/method/MongoDBClient__get.txt b/docs/reference/method/MongoDBClient__get.txt deleted file mode 100644 index c0af748c3..000000000 --- a/docs/reference/method/MongoDBClient__get.txt +++ /dev/null @@ -1,69 +0,0 @@ -======================== -MongoDB\\Client::__get() -======================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Client::__get() - - Selects a database on the server. This :php:`magic method ` is - an alias for the :phpmethod:`selectDatabase() - ` method. - - .. code-block:: php - - function __get(string $databaseName): MongoDB\Database - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBClient-method-get-param.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Database` object. - -Behavior --------- - -The selected database inherits options such as read preference and type mapping -from the :phpclass:`Client ` object. If you wish to override -any options, use the :phpmethod:`MongoDB\\Client::selectDatabase` method. - -.. note:: - - To select databases whose names contain special characters, such as - ``-``, use complex syntax, as in ``$client->{'that-database'}``. - - Alternatively, :phpmethod:`MongoDB\\Client::selectDatabase()` supports - selecting databases whose names contain special characters. - -Examples --------- - -The following example selects the ``test`` and ``another-app`` databases: - -.. code-block:: php - - test; - $anotherApp = $client->{'another-app'}; - -See Also --------- - -- :phpmethod:`MongoDB\\Client::selectDatabase()` -- :phpmethod:`MongoDB\\Database::__construct()` -- :php:`Property Overloading ` in the PHP Manual diff --git a/docs/reference/method/MongoDBCollection-aggregate.txt b/docs/reference/method/MongoDBCollection-aggregate.txt deleted file mode 100644 index d26d8c8e9..000000000 --- a/docs/reference/method/MongoDBCollection-aggregate.txt +++ /dev/null @@ -1,88 +0,0 @@ -================================ -MongoDB\\Collection::aggregate() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::aggregate() - - Executes an :manual:`aggregation framework pipeline - ` operation on the collection. - - .. code-block:: php - - function aggregate(array $pipeline, array $options = []): Traversable - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-aggregate-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-aggregate-option.rst - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Cursor ` or -:php:`ArrayIterator ` object. In both cases, the return value -will be :php:`Traversable `. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -.. _php-coll-agg-method-behavior: - -Behavior --------- - -:phpmethod:`MongoDB\\Collection::aggregate()`'s return value depends on the -MongoDB server version and whether the ``useCursor`` option is specified. If -``useCursor`` is ``true``, a :php:`MongoDB\\Driver\\Cursor -` object is returned. If ``useCursor`` is -``false``, an :php:`ArrayIterator ` is returned that wraps the -``result`` array from the command response document. In both cases, the return -value will be :php:`Traversable `. - -Examples --------- - -The following aggregation example uses a collection called ``names`` and groups -the ``first_name`` field together, counts the total number of results in each -group, and sorts the results by name. - -.. code-block:: php - - test->names; - - $cursor = $collection->aggregate( - [ - ['$group' => ['_id' => '$first_name', 'name_count' => ['$sum' => 1]]], - ['$sort' => ['_id' => 1]], - ] - ); - -See Also --------- - -- :phpmethod:`MongoDB\\Database::aggregate()` -- :manual:`aggregate ` command reference in the - MongoDB manual -- :manual:`Aggregation Pipeline ` documentation in - the MongoDB Manual diff --git a/docs/reference/method/MongoDBCollection-bulkWrite.txt b/docs/reference/method/MongoDBCollection-bulkWrite.txt deleted file mode 100644 index e58688cbd..000000000 --- a/docs/reference/method/MongoDBCollection-bulkWrite.txt +++ /dev/null @@ -1,64 +0,0 @@ -================================ -MongoDB\\Collection::bulkWrite() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::bulkWrite() - - Executes multiple write operations. - - .. code-block:: php - - function bulkWrite(array $operations, array $options = []): MongoDB\BulkWriteResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-bulkWrite-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-bulkWrite-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\BulkWriteResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/bulkwriteexception-result.rst -.. include:: /includes/extracts/bulkwriteexception-ordered.rst - -.. todo: add output and examples - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::deleteMany()` -- :phpmethod:`MongoDB\\Collection::deleteOne()` -- :phpmethod:`MongoDB\\Collection::insertMany()` -- :phpmethod:`MongoDB\\Collection::insertOne()` -- :phpmethod:`MongoDB\\Collection::replaceOne()` -- :phpmethod:`MongoDB\\Collection::updateMany()` -- :phpmethod:`MongoDB\\Collection::updateOne()` -- :doc:`/tutorial/crud` diff --git a/docs/reference/method/MongoDBCollection-count.txt b/docs/reference/method/MongoDBCollection-count.txt deleted file mode 100644 index 012b3cb6f..000000000 --- a/docs/reference/method/MongoDBCollection-count.txt +++ /dev/null @@ -1,69 +0,0 @@ -============================ -MongoDB\\Collection::count() -============================ - -.. deprecated:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::count() - - Count the number of documents that match the filter criteria. - - .. code-block:: php - - function count($filter = [], array $options = []): integer - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-count-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-count-option.rst - -Return Values -------------- - -The number of documents matching the filter criteria. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -This method is deprecated and cannot be executed within a transaction. It has -always been implemented using the :manual:`count ` -command. The behavior of the ``count`` command differs depending on the options -passed to it and may or may not provide an accurate count. When no query filter -is provided, the ``count`` command provides an estimate using collection -metadata. Even when provided with a query filter the ``count`` command can -return inaccurate results with a sharded cluster if orphaned documents exist or -if a chunk migration is in progress. The -:phpmethod:`MongoDB\\Collection::countDocuments()` method avoids these sharded -cluster problems entirely. - -.. include:: /includes/extracts/note-bson-comparison.rst - -See Also --------- - -- :manual:`count ` command reference in the MongoDB - manual -- :phpmethod:`MongoDB\\Collection::countDocuments()` -- :phpmethod:`MongoDB\\Collection::estimatedDocumentCount()` diff --git a/docs/reference/method/MongoDBCollection-countDocuments.txt b/docs/reference/method/MongoDBCollection-countDocuments.txt deleted file mode 100644 index 0b150361f..000000000 --- a/docs/reference/method/MongoDBCollection-countDocuments.txt +++ /dev/null @@ -1,89 +0,0 @@ -===================================== -MongoDB\\Collection::countDocuments() -===================================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::countDocuments() - - Count the number of documents that match the filter criteria. - - .. code-block:: php - - function countDocuments($filter = [], array $options = []): integer - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-countDocuments-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-countDocuments-option.rst - -Return Values -------------- - -The number of documents matching the filter criteria. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -Internally, this method uses the ``$group`` aggregation pipeline operator to -obtain the result. If a ``filter`` parameter is given, this is converted into -a ``$match`` pipeline operator. Optional ``$skip`` and ``$limit`` stages are -added between ``$match`` and ``group`` if present in the options. - -.. note:: - - This method counts documents on the server side. To obtain an approximate - total number of documents without filters, the - :phpmethod:`MongoDB\\Collection::estimatedDocumentCount()` method can be - used. This method estimates the number of documents based on collection - metadata, thus sacrificing accuracy for performance. - -Since this method uses an aggregation pipeline, some query operators accepted -within a :phpmethod:`MongoDB\\Collection::count()` ``filter`` cannot be used. -Consider the following alternatives to these restricted operators: - -.. list-table:: - :header-rows: 1 - - * - Restricted - - Alternative Syntax - - * - :query:`$near` - - :query:`$geoWithin` with :query:`$center` - - * - :query:`$nearSphere` - - :query:`$geoWithin` with :query:`$centerSphere` - - * - :query:`$where` - - :query:`$expr` - -.. include:: /includes/extracts/note-bson-comparison.rst - -.. todo: add output and examples - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::estimatedDocumentCount()` diff --git a/docs/reference/method/MongoDBCollection-createIndex.txt b/docs/reference/method/MongoDBCollection-createIndex.txt deleted file mode 100644 index bdae57123..000000000 --- a/docs/reference/method/MongoDBCollection-createIndex.txt +++ /dev/null @@ -1,109 +0,0 @@ -================================== -MongoDB\\Collection::createIndex() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::createIndex() - - Create an index for the collection. - - .. code-block:: php - - function createIndex($key, array $options = []): string - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-createIndex-param.rst - - The ``$options`` parameter accepts all index options that your MongoDB - version supports. MongoDB includes the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-createIndex-option.rst - - For a full list of the supported index creation options, refer to the - :manual:`createIndexes ` command reference - in the MongoDB manual. - -Return Values -------------- - -The name of the created index as a string. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -Create a Compound Index -~~~~~~~~~~~~~~~~~~~~~~~ - -The following example creates a :manual:`compound index ` -on the ``borough`` and ``cuisine`` fields in the ``restaurants`` collection in -the ``test`` database. - -.. code-block:: php - - selectCollection('test', 'restaurants'); - - $indexName = $collection->createIndex(['borough' => 1, 'cuisine' => 1]); - - var_dump($indexName); - -The output would then resemble:: - - string(19) "borough_1_cuisine_1" - -Create a Partial Index -~~~~~~~~~~~~~~~~~~~~~~ - -The following example adds a :manual:`partial index ` on -the ``borough`` field in the ``restaurants`` collection in the ``test`` -database. The partial index indexes only documents where the ``borough`` field -exists. - -.. code-block:: php - - selectCollection('test', 'restaurants'); - - $indexName = $collection->createIndex( - ['borough' => 1], - [ - 'partialFilterExpression' => [ - 'borough' => ['$exists' => true], - ], - ] - ); - - var_dump($indexName); - -The output would then resemble:: - - string(9) "borough_1" - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndexes()` -- :doc:`/tutorial/indexes` -- :manual:`createIndexes ` command reference - in the MongoDB manual -- :manual:`Index ` documentation in the MongoDB Manual diff --git a/docs/reference/method/MongoDBCollection-createIndexes.txt b/docs/reference/method/MongoDBCollection-createIndexes.txt deleted file mode 100644 index 05a3c0be3..000000000 --- a/docs/reference/method/MongoDBCollection-createIndexes.txt +++ /dev/null @@ -1,100 +0,0 @@ -==================================== -MongoDB\\Collection::createIndexes() -==================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::createIndexes($indexes) - - Create one or more indexes for the collection. - - .. code-block:: php - - function createIndexes(array $indexes, array $options = []): string[] - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-createIndexes-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-createIndexes-option.rst - -Return Values -------------- - -The names of the created indexes as an array of strings. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -``$indexes`` parameter ----------------------- - -The ``$indexes`` parameter is an array of index specification documents. Each -element in ``$indexes`` must itself be an array or object with a ``key`` field, -which corresponds to the ``$key`` parameter of :phpmethod:`createIndex() -`. The array or object may include other -fields that correspond to index options accepted by :phpmethod:`createIndex() -` (excluding ``writeConcern``). - -For example, the following ``$indexes`` parameter creates two indexes. The first -is an ascending unique index on the ``username`` field and the second is a -2dsphere index on the ``loc`` field with a custom name:: - - [ - [ 'key' => [ 'username' => 1 ], 'unique' => true ], - [ 'key' => [ 'loc' => '2dsphere' ], 'name' => 'geo_index' ], - ] - -Example -------- - -The following example creates two indexes on the ``restaurants`` collection in -the ``test`` database. One index is a compound index on the ``borough`` and -``cuisine`` fields and the other is 2dsphere index on the ``loc`` field with a -custom name. - -.. code-block:: php - - selectCollection('test', 'restaurants'); - - $indexNames = $collection->createIndexes([ - [ 'key' => [ 'borough' => 1, 'cuisine' => 1] ], - [ 'key' => [ 'loc' => '2dsphere'], 'name' => 'geo_index' ], - ]); - - var_dump($indexNames); - -The output would then resemble:: - - array(2) { - [0]=> - string(19) "borough_1_cuisine_1" - [1]=> - string(9) "geo_index" - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :doc:`/tutorial/indexes` -- :manual:`createIndexes ` command reference - in the MongoDB manual -- :manual:`Index ` documentation in the MongoDB Manual diff --git a/docs/reference/method/MongoDBCollection-deleteMany.txt b/docs/reference/method/MongoDBCollection-deleteMany.txt deleted file mode 100644 index df1a1ac81..000000000 --- a/docs/reference/method/MongoDBCollection-deleteMany.txt +++ /dev/null @@ -1,82 +0,0 @@ -================================= -MongoDB\\Collection::deleteMany() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::deleteMany() - - Deletes all documents that match the filter criteria. - - .. code-block:: php - - function deleteMany($filter, array $options = []): MongoDB\DeleteResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-deleteMany-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-deleteMany-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\DeleteResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Example -------- - -The following example deletes all of the documents in the ``users`` collection -that have ``"ny"`` as the value for the ``state`` field: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $deleteResult = $collection->deleteMany(['state' => 'ny']); - - printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); - -The output would then resemble:: - - Deleted 2 document(s) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::deleteOne()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`delete ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-deleteOne.txt b/docs/reference/method/MongoDBCollection-deleteOne.txt deleted file mode 100644 index 0c31bfb5f..000000000 --- a/docs/reference/method/MongoDBCollection-deleteOne.txt +++ /dev/null @@ -1,84 +0,0 @@ -================================ -MongoDB\\Collection::deleteOne() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::deleteOne() - - Deletes at most one document that matches the filter criteria. If multiple - documents match the filter criteria, only the :term:`first ` - matching document will be deleted. - - .. code-block:: php - - function deleteOne($filter, array $options = []): MongoDB\DeleteResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-deleteOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-deleteOne-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\DeleteResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Example -------- - -The following example deletes one document in the ``users`` collection that has -has ``"ny"`` as the value for the ``state`` field: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $deleteResult = $collection->deleteOne(['state' => 'ny']); - - printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); - -The output would then resemble:: - - Deleted 1 document(s) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::deleteMany()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`delete ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-distinct.txt b/docs/reference/method/MongoDBCollection-distinct.txt deleted file mode 100644 index bb0c94ca5..000000000 --- a/docs/reference/method/MongoDBCollection-distinct.txt +++ /dev/null @@ -1,262 +0,0 @@ -=============================== -MongoDB\\Collection::distinct() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::distinct() - - Finds the distinct values for a specified field across the collection. - - .. code-block:: php - - function distinct(string $fieldName, $filter = [], array $options = []): mixed[] - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-distinct-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-distinct-option.rst - -Return Values -------------- - -An array of the distinct values. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -Return Distinct Values for a Field -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example identifies the distinct values for the ``borough`` field -in the ``restaurants`` collection in the ``test`` database. - -.. code-block:: php - - test->restaurants; - - $distinct = $collection->distinct('borough'); - - var_dump($distinct); - -The output would then resemble:: - - array(6) { - [0]=> - string(5) "Bronx" - [1]=> - string(8) "Brooklyn" - [2]=> - string(9) "Manhattan" - [3]=> - string(7) "Missing" - [4]=> - string(6) "Queens" - [5]=> - string(13) "Staten Island" - } - -Return Distinct Values Using a Filter -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example identifies the distinct values for the ``cuisine`` field -in the ``restaurants`` collection in the ``test`` database for documents where -the ``borough`` is ``Queens``: - -.. code-block:: php - - test->restaurants; - - $distinct = $collection->distinct('cuisine', ['borough' => 'Queens']); - - var_dump($distinct); - -The output would then resemble:: - - array(75) { - [0]=> - string(6) "Afghan" - [1]=> - string(7) "African" - [2]=> - string(9) "American " - [3]=> - string(8) "Armenian" - [4]=> - string(5) "Asian" - [5]=> - string(10) "Australian" - [6]=> - string(15) "Bagels/Pretzels" - [7]=> - string(6) "Bakery" - [8]=> - string(11) "Bangladeshi" - [9]=> - string(8) "Barbecue" - [10]=> - string(55) "Bottled beverages, including water, sodas, juices, etc." - [11]=> - string(9) "Brazilian" - [12]=> - string(4) "Cafe" - [13]=> - string(16) "Café/Coffee/Tea" - [14]=> - string(5) "Cajun" - [15]=> - string(9) "Caribbean" - [16]=> - string(7) "Chicken" - [17]=> - string(7) "Chinese" - [18]=> - string(13) "Chinese/Cuban" - [19]=> - string(16) "Chinese/Japanese" - [20]=> - string(11) "Continental" - [21]=> - string(6) "Creole" - [22]=> - string(5) "Czech" - [23]=> - string(12) "Delicatessen" - [24]=> - string(6) "Donuts" - [25]=> - string(16) "Eastern European" - [26]=> - string(8) "Egyptian" - [27]=> - string(7) "English" - [28]=> - string(8) "Filipino" - [29]=> - string(6) "French" - [30]=> - string(17) "Fruits/Vegetables" - [31]=> - string(6) "German" - [32]=> - string(5) "Greek" - [33]=> - string(10) "Hamburgers" - [34]=> - string(16) "Hotdogs/Pretzels" - [35]=> - string(31) "Ice Cream, Gelato, Yogurt, Ices" - [36]=> - string(6) "Indian" - [37]=> - string(10) "Indonesian" - [38]=> - string(5) "Irish" - [39]=> - string(7) "Italian" - [40]=> - string(8) "Japanese" - [41]=> - string(13) "Jewish/Kosher" - [42]=> - string(30) "Juice, Smoothies, Fruit Salads" - [43]=> - string(6) "Korean" - [44]=> - string(64) "Latin (Cuban, Dominican, Puerto Rican, South & Central American)" - [45]=> - string(13) "Mediterranean" - [46]=> - string(7) "Mexican" - [47]=> - string(14) "Middle Eastern" - [48]=> - string(8) "Moroccan" - [49]=> - string(25) "Not Listed/Not Applicable" - [50]=> - string(18) "Nuts/Confectionary" - [51]=> - string(5) "Other" - [52]=> - string(9) "Pakistani" - [53]=> - string(16) "Pancakes/Waffles" - [54]=> - string(8) "Peruvian" - [55]=> - string(5) "Pizza" - [56]=> - string(13) "Pizza/Italian" - [57]=> - string(6) "Polish" - [58]=> - string(10) "Portuguese" - [59]=> - string(7) "Russian" - [60]=> - string(6) "Salads" - [61]=> - string(10) "Sandwiches" - [62]=> - string(30) "Sandwiches/Salads/Mixed Buffet" - [63]=> - string(7) "Seafood" - [64]=> - string(9) "Soul Food" - [65]=> - string(18) "Soups & Sandwiches" - [66]=> - string(12) "Southwestern" - [67]=> - string(7) "Spanish" - [68]=> - string(5) "Steak" - [69]=> - string(5) "Tapas" - [70]=> - string(7) "Tex-Mex" - [71]=> - string(4) "Thai" - [72]=> - string(7) "Turkish" - [73]=> - string(10) "Vegetarian" - [74]=> - string(29) "Vietnamese/Cambodian/Malaysia" - } - -See Also --------- - -- :manual:`distinct ` command reference in the - MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-drop.txt b/docs/reference/method/MongoDBCollection-drop.txt deleted file mode 100644 index c1762871b..000000000 --- a/docs/reference/method/MongoDBCollection-drop.txt +++ /dev/null @@ -1,81 +0,0 @@ -=========================== -MongoDB\\Collection::drop() -=========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::drop() - - Drop the collection. - - .. code-block:: php - - function drop(array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-drop-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-drop-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`drop -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following operation drops the ``restaurants`` collection in the ``test`` -database: - -.. code-block:: php - - test->restaurants; - - $result = $collection->drop(); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#9 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["ns"]=> - string(16) "test.restaurants" - ["nIndexesWas"]=> - int(3) - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::dropCollection()` -- :manual:`drop ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-dropIndex.txt b/docs/reference/method/MongoDBCollection-dropIndex.txt deleted file mode 100644 index 880349bdf..000000000 --- a/docs/reference/method/MongoDBCollection-dropIndex.txt +++ /dev/null @@ -1,81 +0,0 @@ -================================ -MongoDB\\Collection::dropIndex() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::dropIndex() - - Drop an index from the collection. - - .. code-block:: php - - function dropIndex($indexName, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-dropIndex-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-dropIndex-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`dropIndexes -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following drops an indexes with name ``borough_1`` from the ``restaurants`` -collection in the ``test`` database: - -.. code-block:: php - - test->restaurants; - - $result = $collection->dropIndex('borough_1'); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#9 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["nIndexesWas"]=> - int(2) - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::dropIndexes()` -- :doc:`/tutorial/indexes` -- :manual:`dropIndexes ` command reference in - the MongoDB manual -- :manual:`Index documentation ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-dropIndexes.txt b/docs/reference/method/MongoDBCollection-dropIndexes.txt deleted file mode 100644 index 8cdf9e43a..000000000 --- a/docs/reference/method/MongoDBCollection-dropIndexes.txt +++ /dev/null @@ -1,84 +0,0 @@ -================================== -MongoDB\\Collection::dropIndexes() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::dropIndexes() - - Drop all indexes in the collection, except for the required index on the - ``_id`` field. - - .. code-block:: php - - function dropIndexes(array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-dropIndex-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-dropIndex-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`dropIndexes -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following drops all indexes from the ``restaurants`` collection in the -``test`` database: - -.. code-block:: php - - test->restaurants; - - $result = $collection->dropIndexes(); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#9 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["nIndexesWas"]=> - int(3) - ["msg"]=> - string(38) "non-_id indexes dropped for collection" - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::dropIndex()` -- :doc:`/tutorial/indexes` -- :manual:`dropIndexes ` command reference in - the MongoDB manual -- :manual:`Index documentation ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt b/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt deleted file mode 100644 index 1a8316293..000000000 --- a/docs/reference/method/MongoDBCollection-estimatedDocumentCount.txt +++ /dev/null @@ -1,68 +0,0 @@ -============================================= -MongoDB\\Collection::estimatedDocumentCount() -============================================= - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::estimatedDocumentCount() - - Gets an estimated number of documents in the collection using collection metadata. - - .. code-block:: php - - function countDocuments(array $options = []): integer - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-estimateDocumentCount-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-estimateDocumentCount-option.rst - -Return Values -------------- - -An estimated number of documents in the collection. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -This method returns an estimate of the count of documents in the collection -using collection metadata, rather than counting the documents or consulting an -index. This method does not take a ``session`` option and cannot be executed -within a transaction. See -`Count: Behavior `_ -in the MongoDB manual for more information. - -This method is implemented using the :manual:`count ` -command. Due to an oversight in versions 5.0.0-5.0.8 of MongoDB, the ``count`` -command was not included in version "1" of the Stable API. Applications using -this method with the Stable API are recommended to upgrade their server version -to 5.0.9+ or disable strict mode to avoid encountering errors. - -See Also --------- - -- :manual:`count ` command reference in the MongoDB - manual -- :phpmethod:`MongoDB\\Collection::countDocuments()` diff --git a/docs/reference/method/MongoDBCollection-explain.txt b/docs/reference/method/MongoDBCollection-explain.txt deleted file mode 100644 index 5ae73c911..000000000 --- a/docs/reference/method/MongoDBCollection-explain.txt +++ /dev/null @@ -1,279 +0,0 @@ -============================== -MongoDB\\Collection::explain() -============================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::explain() - - Explain the given command. - - .. code-block:: php - - function explain(MongoDB\\Operation\\Explainable $explainable, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-explain-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-explain-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`explain -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Explainable Commands --------------------- - -Explainable commands include, but are not limited to: - - - :phpclass:`MongoDB\\Operation\\Aggregate` - - :phpclass:`MongoDB\\Operation\\Count` - - :phpclass:`MongoDB\\Operation\\DeleteMany` - - :phpclass:`MongoDB\\Operation\\DeleteOne` - - :phpclass:`MongoDB\\Operation\\Distinct` - - :phpclass:`MongoDB\\Operation\\Find` - - :phpclass:`MongoDB\\Operation\\FindOne` - - :phpclass:`MongoDB\\Operation\\FindOneAndDelete` - - :phpclass:`MongoDB\\Operation\\FindOneAndReplace` - - :phpclass:`MongoDB\\Operation\\FindOneAndUpdate` - - :phpclass:`MongoDB\\Operation\\UpdateMany` - - :phpclass:`MongoDB\\Operation\\UpdateOne` - -Examples --------- - -This example explains a count command. - -.. code-block:: php - - test->restaurants; - - $count = new MongoDB\Operation\Count( - $collection->getDatabaseName(), - $collection->getCollectionName(), - ['cuisine' => 'Italian'] - ); - - $result = $collection->explain($count); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#29 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["queryPlanner"]=> - object(MongoDB\Model\BSONDocument)#21 (1) { - ["storage":"ArrayObject":private]=> - array(6) { - ["plannerVersion"]=> - int(1) - ["namespace"]=> - string(16) "test.restaurants" - ["indexFilterSet"]=> - bool(false) - ["parsedQuery"]=> - object(MongoDB\Model\BSONDocument)#15 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["cuisine"]=> - object(MongoDB\Model\BSONDocument)#14 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["$eq"]=> - string(7) "Italian" - } - } - } - } - ["winningPlan"]=> - object(MongoDB\Model\BSONDocument)#19 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["stage"]=> - string(5) "COUNT" - ["inputStage"]=> - object(MongoDB\Model\BSONDocument)#18 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["stage"]=> - string(8) "COLLSCAN" - ["filter"]=> - object(MongoDB\Model\BSONDocument)#17 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["cuisine"]=> - object(MongoDB\Model\BSONDocument)#16 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["$eq"]=> - string(7) "Italian" - } - } - } - } - ["direction"]=> - string(7) "forward" - } - } - } - } - ["rejectedPlans"]=> - object(MongoDB\Model\BSONArray)#20 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - } - } - ["executionStats"]=> - object(MongoDB\Model\BSONDocument)#27 (1) { - ["storage":"ArrayObject":private]=> - array(7) { - ["executionSuccess"]=> - bool(true) - ["nReturned"]=> - int(0) - ["executionTimeMillis"]=> - int(24) - ["totalKeysExamined"]=> - int(0) - ["totalDocsExamined"]=> - int(25359) - ["executionStages"]=> - object(MongoDB\Model\BSONDocument)#25 (1) { - ["storage":"ArrayObject":private]=> - array(14) { - ["stage"]=> - string(5) "COUNT" - ["nReturned"]=> - int(0) - ["executionTimeMillisEstimate"]=> - int(20) - ["works"]=> - int(25361) - ["advanced"]=> - int(0) - ["needTime"]=> - int(25360) - ["needYield"]=> - int(0) - ["saveState"]=> - int(198) - ["restoreState"]=> - int(198) - ["isEOF"]=> - int(1) - ["invalidates"]=> - int(0) - ["nCounted"]=> - int(1069) - ["nSkipped"]=> - int(0) - ["inputStage"]=> - object(MongoDB\Model\BSONDocument)#24 (1) { - ["storage":"ArrayObject":private]=> - array(14) { - ["stage"]=> - string(8) "COLLSCAN" - ["filter"]=> - object(MongoDB\Model\BSONDocument)#23 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["cuisine"]=> - object(MongoDB\Model\BSONDocument)#22 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["$eq"]=> - string(7) "Italian" - } - } - } - } - ["nReturned"]=> - int(1069) - ["executionTimeMillisEstimate"]=> - int(20) - ["works"]=> - int(25361) - ["advanced"]=> - int(1069) - ["needTime"]=> - int(24291) - ["needYield"]=> - int(0) - ["saveState"]=> - int(198) - ["restoreState"]=> - int(198) - ["isEOF"]=> - int(1) - ["invalidates"]=> - int(0) - ["direction"]=> - string(7) "forward" - ["docsExamined"]=> - int(25359) - } - } - } - } - ["allPlansExecution"]=> - object(MongoDB\Model\BSONArray)#26 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - } - } - ["serverInfo"]=> - object(MongoDB\Model\BSONDocument)#28 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["host"]=> - string(9) "localhost" - ["port"]=> - int(27017) - ["version"]=> - string(5) "3.6.1" - ["gitVersion"]=> - string(40) "025d4f4fe61efd1fb6f0005be20cb45a004093d1" - } - } - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :manual:`explain ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-find.txt b/docs/reference/method/MongoDBCollection-find.txt deleted file mode 100644 index 34eed99ad..000000000 --- a/docs/reference/method/MongoDBCollection-find.txt +++ /dev/null @@ -1,168 +0,0 @@ -=========================== -MongoDB\\Collection::find() -=========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::find() - - Finds documents matching the query. - - .. code-block:: php - - function find($filter = [], array $options = []): MongoDB\Driver\Cursor - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-find-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-find-option.rst - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Cursor ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -The following example finds restaurants based on the ``cuisine`` and ``borough`` -fields and uses a :manual:`projection -` to limit the fields that are -returned. It also limits the results to 5 documents. - -.. code-block:: php - - $collection = (new MongoDB\Client)->test->restaurants; - - $cursor = $collection->find( - [ - 'cuisine' => 'Italian', - 'borough' => 'Manhattan', - ], - [ - 'limit' => 5, - 'projection' => [ - 'name' => 1, - 'borough' => 1, - 'cuisine' => 1, - ], - ] - ); - - foreach ($cursor as $restaurant) { - var_dump($restaurant); - }; - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#10 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#8 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f983" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(23) "Isle Of Capri Resturant" - } - } - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#12 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f98d" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(18) "Marchis Restaurant" - } - } - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#10 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f99b" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(19) "Forlinis Restaurant" - } - } - object(MongoDB\Model\BSONDocument)#12 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#13 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f9a8" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(22) "Angelo Of Mulberry St." - } - } - object(MongoDB\Model\BSONDocument)#10 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#8 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f9b4" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(16) "V & T Restaurant" - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::findOne()` -- :manual:`find ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-findOne.txt b/docs/reference/method/MongoDBCollection-findOne.txt deleted file mode 100644 index 0bf95f648..000000000 --- a/docs/reference/method/MongoDBCollection-findOne.txt +++ /dev/null @@ -1,125 +0,0 @@ -============================== -MongoDB\\Collection::findOne() -============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::findOne() - - Finds a single document matching the query. - - .. code-block:: php - - function findOne($filter = [], array $options = []): array|object|null - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOne-option.rst - -Return Values -------------- - -An array or object for the :term:`first document ` that matched -the query, or ``null`` if no document matched the query. The return type will -depend on the ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -Matching BSON Types in Query Criteria -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the following example, documents in the ``restaurants`` collection use an -:manual:`ObjectId ` for their identifier (the default) -and documents in the ``zips`` collection use a string. Since ObjectId is a -special BSON type, the query criteria for selecting a restaurant must use the -:php:`MongoDB\\BSON\\ObjectId ` class. - -.. code-block:: php - - $database = (new MongoDB\Client)->test; - - $zip = $database->zips->findOne(['_id' => '10036']); - - $restaurant = $database->restaurants->findOne([ - '_id' => new MongoDB\BSON\ObjectId('594d5ef280a846852a4b3f70'), - ]); - -Projecting Fields -~~~~~~~~~~~~~~~~~ - -The following example finds a restaurant based on the ``cuisine`` and -``borough`` fields and uses a :manual:`projection -` to limit the fields that are -returned. - -.. code-block:: php - - $collection = (new MongoDB\Client)->test->restaurants; - - $restaurant = $collection->findOne( - [ - 'cuisine' => 'Italian', - 'borough' => 'Manhattan', - ], - [ - 'projection' => [ - 'name' => 1, - 'borough' => 1, - 'cuisine' => 1, - ], - ] - ); - - var_dump($restaurant); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#10 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#8 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f983" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(23) "Isle Of Capri Resturant" - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::find()` -- :manual:`find ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-findOneAndDelete.txt b/docs/reference/method/MongoDBCollection-findOneAndDelete.txt deleted file mode 100644 index 1253bad40..000000000 --- a/docs/reference/method/MongoDBCollection-findOneAndDelete.txt +++ /dev/null @@ -1,101 +0,0 @@ -======================================= -MongoDB\\Collection::findOneAndDelete() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::findOneAndDelete() - - Finds a single document matching the query and deletes it. - - .. code-block:: php - - function findOneAndDelete($filter = [], array $options = []): object|null - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndDelete-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndDelete-option.rst - -Return Values -------------- - -An array or object for the document that was deleted, or ``null`` if no document -matched the query. The return type will depend on the ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -The following example finds and deletes the document with ``restaurant_id`` of -``"40375376"`` from the ``restaurants`` collection in the ``test`` database: - -.. code-block:: php - - test->restaurants; - - $deletedRestaurant = $collection->findOneAndDelete( - [ 'restaurant_id' => '40375376' ], - [ - 'projection' => [ - 'name' => 1, - 'borough' => 1, - 'restaurant_id' => 1, - ], - ] - ); - - var_dump($deletedRestaurant); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#17 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#11 (1) { - ["oid"]=> - string(24) "594d5ef280a846852a4b3f70" - } - ["borough"]=> - string(9) "Manhattan" - ["name"]=> - string(15) "Agra Restaurant" - ["restaurant_id"]=> - string(8) "40375376" - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::findOneAndReplace()` -- :phpmethod:`MongoDB\\Collection::findOneAndUpdate()` -- :manual:`findAndModify ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-findOneAndReplace.txt b/docs/reference/method/MongoDBCollection-findOneAndReplace.txt deleted file mode 100644 index 6a96a30b0..000000000 --- a/docs/reference/method/MongoDBCollection-findOneAndReplace.txt +++ /dev/null @@ -1,151 +0,0 @@ -======================================== -MongoDB\\Collection::findOneAndReplace() -======================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::findOneAndReplace() - - Finds a single document matching the query and replaces it. - - .. code-block:: php - - function findOneAndReplace($filter, $replacement, array $options = []): object|null - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndReplace-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndReplace-option.rst - -Return Values -------------- - -An array object for either the original or the replaced document, depending on -the specified value of the ``returnDocument`` option. By default, the original -document is returned. If no document matched the query, ``null`` is returned. -The return type will depend on the ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -Consider the following document in the ``restaurants`` collection in the -``test`` database: - -.. code-block:: javascript - - { - "_id" : ObjectId("576023c7b02fa9281da4139e"), - "address" : { - "building" : "977", - "coord" : [ - -74.06940569999999, - 40.6188443 - ], - "street" : "Bay Street", - "zipcode" : "10305" - }, - "borough" : "Staten Island", - "cuisine" : "French", - "grades" : [ - { - "date" : ISODate("2014-08-15T00:00:00Z"), - "grade" : "A", - "score" : 7 - }, - { - "date" : ISODate("2014-02-13T00:00:00Z"), - "grade" : "A", - "score" : 5 - }, - { - "date" : ISODate("2013-06-07T00:00:00Z"), - "grade" : "A", - "score" : 11 - } - ], - "name" : "Zest", - "restaurant_id" : "41220906" - } - -The following operation replaces the document with ``restaurant_id`` of -``"41220906"`` with a new document: - -.. code-block:: php - - teset->restaurants; - - $replacedRestaurant = $collection->findOneAndReplace( - [ 'restaurant_id' => '41220906' ], - [ - 'Borough' => 'Staten Island', - 'cuisine' => 'Italian', - 'grades' => [], - 'name' => 'Staten Island Pastaria', - 'restaurant_id' => '999999999', - ], - [ 'returnDocument' => MongoDB\Operation\FindOneAndReplace::RETURN_DOCUMENT_AFTER ] - ); - - var_dump($replacedRestaurant); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#18 (1) { - ["storage":"ArrayObject":private]=> - array(6) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#11 (1) { - ["oid"]=> - string(24) "594d5ef380a846852a4b5837" - } - ["Borough"]=> - string(13) "Staten Island" - ["cuisine"]=> - string(7) "Italian" - ["grades"]=> - object(MongoDB\Model\BSONArray)#17 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - ["name"]=> - string(22) "Staten Island Pastaria" - ["restaurant_id"]=> - string(9) "999999999" - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::findOneAndDelete()` -- :phpmethod:`MongoDB\\Collection::findOneAndUpdate()` -- :manual:`findAndModify ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt b/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt deleted file mode 100644 index 377d255ed..000000000 --- a/docs/reference/method/MongoDBCollection-findOneAndUpdate.txt +++ /dev/null @@ -1,118 +0,0 @@ -======================================= -MongoDB\\Collection::findOneAndUpdate() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::findOneAndUpdate() - - Finds a single document matching the query and updates it. - - .. code-block:: php - - function findOneAndUpdate($filter, $update, array $options = []): object|null - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndUpdate-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOneAndUpdate-option.rst - -Return Values -------------- - -An array or object for either the original or the updated document, depending on -the specified value of the ``returnDocument`` option. By default, the original -document is returned. If no document matched the query, ``null`` is returned. -The return type will depend on the ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -The following operation updates the restaurant with ``restaurant_id`` of -``"40361708"`` in the ``restaurants`` collection in the ``test`` database by -setting its building number to ``"761"``: - -.. code-block:: php - - test->restaurants; - - $updatedRestaurant = $collection->findOneAndUpdate( - [ 'restaurant_id' => '40361708' ], - [ '$set' => [ 'address.building' => '761' ]], - [ - 'projection' => [ 'address' => 1 ], - 'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER, - ] - ); - - var_dump($updatedRestaurant); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#20 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#12 (1) { - ["oid"]=> - string(24) "594d5ef280a846852a4b3dee" - } - ["address"]=> - object(MongoDB\Model\BSONDocument)#19 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["building"]=> - string(3) "761" - ["coord"]=> - object(MongoDB\Model\BSONArray)#18 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-73.9925306) - [1]=> - float(40.7309346) - } - } - ["street"]=> - string(8) "Broadway" - ["zipcode"]=> - string(5) "10003" - } - } - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::findOneAndDelete()` -- :phpmethod:`MongoDB\\Collection::findOneAndReplace()` -- :manual:`findAndModify ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-getCollectionName.txt b/docs/reference/method/MongoDBCollection-getCollectionName.txt deleted file mode 100644 index 86fe3e5ec..000000000 --- a/docs/reference/method/MongoDBCollection-getCollectionName.txt +++ /dev/null @@ -1,51 +0,0 @@ -======================================== -MongoDB\\Collection::getCollectionName() -======================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getCollectionName() - - Returns the name of this collection. - - .. code-block:: php - - function getCollectionName(): string - -Return Values -------------- - -The name of this collection as a string. - -Example -------- - -The following returns the collection name for the ``zips`` collection in the -``test`` database. - -.. code-block:: php - - test->zips; - - echo $collection->getCollectionName(); - -The output would then resemble:: - - zips - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::getDatabaseName()` -- :phpmethod:`MongoDB\\Collection::getNamespace()` diff --git a/docs/reference/method/MongoDBCollection-getDatabaseName.txt b/docs/reference/method/MongoDBCollection-getDatabaseName.txt deleted file mode 100644 index 93500c809..000000000 --- a/docs/reference/method/MongoDBCollection-getDatabaseName.txt +++ /dev/null @@ -1,52 +0,0 @@ -====================================== -MongoDB\\Collection::getDatabaseName() -====================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getDatabaseName() - - Returns the name of the database containing this collection. - - .. code-block:: php - - function getDatabaseName(): string - -Return Values -------------- - -The name of the database containing this collection as a string. - -Example -------- - -The following returns the database name for the ``zips`` collection in the -``test`` database. - -.. code-block:: php - - test->zips; - - echo $collection->getDatabaseName(); - -The output would then resemble:: - - test - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::getCollectionName()` -- :phpmethod:`MongoDB\\Collection::getNamespace()` - diff --git a/docs/reference/method/MongoDBCollection-getManager.txt b/docs/reference/method/MongoDBCollection-getManager.txt deleted file mode 100644 index e0bbea088..000000000 --- a/docs/reference/method/MongoDBCollection-getManager.txt +++ /dev/null @@ -1,35 +0,0 @@ -================================= -MongoDB\\Collection::getManager() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getManager() - - Accessor for the - :php:`MongoDB\\Driver\\Manager ` used by this - :phpclass:`Collection `. - - .. code-block:: php - - function getManager(): MongoDB\Manager - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Manager ` object. - -See Also --------- - -- :phpmethod:`MongoDB\\Client::getManager()` -- :phpmethod:`MongoDB\\Database::getManager()` diff --git a/docs/reference/method/MongoDBCollection-getNamespace.txt b/docs/reference/method/MongoDBCollection-getNamespace.txt deleted file mode 100644 index 74f1b022e..000000000 --- a/docs/reference/method/MongoDBCollection-getNamespace.txt +++ /dev/null @@ -1,52 +0,0 @@ -=================================== -MongoDB\\Collection::getNamespace() -=================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getNamespace() - - Returns the :term:`namespace` of the collection. A namespace is the canonical - name of an index or collection in MongoDB. - - .. code-block:: php - - function getNamespace(): string - -Return Values -------------- - -The namespace of this collection as a string. - -Example -------- - -The following returns the namespace of the ``zips`` collection in the ``test`` -database. - -.. code-block:: php - - test->zips; - - echo $collection->getNamespace(); - -The output would then resemble:: - - test.zips - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::getCollectionName()` -- :phpmethod:`MongoDB\\Collection::getDatabaseName()` diff --git a/docs/reference/method/MongoDBCollection-getReadConcern.txt b/docs/reference/method/MongoDBCollection-getReadConcern.txt deleted file mode 100644 index ff1cfeb04..000000000 --- a/docs/reference/method/MongoDBCollection-getReadConcern.txt +++ /dev/null @@ -1,58 +0,0 @@ -===================================== -MongoDB\\Collection::getReadConcern() -===================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getReadConcern() - - Returns the read concern for this collection. - - .. code-block:: php - - function getReadConcern(): MongoDB\Driver\ReadConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadConcern ` object. - -Example -------- - -.. code-block:: php - - selectCollection('test', 'users', [ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), - ]); - - var_dump($collection->getReadConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadConcern)#5 (1) { - ["level"]=> - string(8) "majority" - } - -See Also --------- - -- :manual:`Read Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\ReadConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getReadConcern()` -- :phpmethod:`MongoDB\\Database::getReadConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadConcern()` diff --git a/docs/reference/method/MongoDBCollection-getReadPreference.txt b/docs/reference/method/MongoDBCollection-getReadPreference.txt deleted file mode 100644 index 4eac30d3a..000000000 --- a/docs/reference/method/MongoDBCollection-getReadPreference.txt +++ /dev/null @@ -1,58 +0,0 @@ -======================================== -MongoDB\\Collection::getReadPreference() -======================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getReadPreference() - - Returns the read preference for this collection. - - .. code-block:: php - - function getReadPreference(): MongoDB\Driver\ReadPreference - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadPreference ` -object. - -Example -------- - -.. code-block:: php - - selectCollection('test', 'users', [ - 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), - ]); - - var_dump($collection->getReadPreference()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadPreference)#5 (1) { - ["mode"]=> - string(16) "primaryPreferred" - } - -See Also --------- - -- :manual:`Read Preference ` in the MongoDB manual -- :phpmethod:`MongoDB\\Client::getReadPreference()` -- :phpmethod:`MongoDB\\Database::getReadPreference()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadPreference()` diff --git a/docs/reference/method/MongoDBCollection-getTypeMap.txt b/docs/reference/method/MongoDBCollection-getTypeMap.txt deleted file mode 100644 index 11e56426a..000000000 --- a/docs/reference/method/MongoDBCollection-getTypeMap.txt +++ /dev/null @@ -1,65 +0,0 @@ -================================= -MongoDB\\Collection::getTypeMap() -================================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getTypeMap() - - Returns the type map for this collection. - - .. code-block:: php - - function getTypeMap(): array - -Return Values -------------- - -A :ref:`type map ` array. - -Example -------- - -.. code-block:: php - - selectCollection('test', 'users', [ - 'typeMap' => [ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ], - ]); - - var_dump($collection->getTypeMap()); - -The output would then resemble:: - - array(3) { - ["root"]=> - string(5) "array" - ["document"]=> - string(5) "array" - ["array"]=> - string(5) "array" - } - -See Also --------- - -- :doc:`/reference/bson` -- :phpmethod:`MongoDB\\Client::getTypeMap()` -- :phpmethod:`MongoDB\\Database::getTypeMap()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getTypeMap()` diff --git a/docs/reference/method/MongoDBCollection-getWriteConcern.txt b/docs/reference/method/MongoDBCollection-getWriteConcern.txt deleted file mode 100644 index 4fa26ee1f..000000000 --- a/docs/reference/method/MongoDBCollection-getWriteConcern.txt +++ /dev/null @@ -1,61 +0,0 @@ -====================================== -MongoDB\\Collection::getWriteConcern() -====================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::getWriteConcern() - - Returns the write concern for this collection. - - .. code-block:: php - - function getWriteConcern(): MongoDB\Driver\WriteConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\WriteConcern ` -object. - -Example -------- - -.. code-block:: php - - selectCollection('test', 'users', [ - 'writeConcern' => new MongoDB\Driver\WriteConcern(1, 0, true), - ]); - - var_dump($collection->getWriteConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\WriteConcern)#5 (2) { - ["w"]=> - int(1) - ["j"]=> - bool(true) - } - -See Also --------- - -- :manual:`Write Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\WriteConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getWriteConcern()` -- :phpmethod:`MongoDB\\Database::getWriteConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getWriteConcern()` diff --git a/docs/reference/method/MongoDBCollection-insertMany.txt b/docs/reference/method/MongoDBCollection-insertMany.txt deleted file mode 100644 index 8b7eb53b0..000000000 --- a/docs/reference/method/MongoDBCollection-insertMany.txt +++ /dev/null @@ -1,107 +0,0 @@ -================================= -MongoDB\\Collection::insertMany() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::insertMany() - - Insert multiple documents. - - .. code-block:: php - - function insertMany(array $documents, array $options = []): MongoDB\InsertManyResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-insertMany-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-insertMany-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\InsertManyResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/bulkwriteexception-result.rst -.. include:: /includes/extracts/bulkwriteexception-ordered.rst - -Example -------- - -.. start-crud-include - -The following operation inserts two documents into the ``users`` collection -in the ``test`` database: - -.. code-block:: php - - test->users; - - $insertManyResult = $collection->insertMany([ - [ - 'username' => 'admin', - 'email' => 'admin@example.com', - 'name' => 'Admin User', - ], - [ - 'username' => 'test', - 'email' => 'test@example.com', - 'name' => 'Test User', - ], - ]); - - printf("Inserted %d document(s)\n", $insertManyResult->getInsertedCount()); - - var_dump($insertManyResult->getInsertedIds()); - -The output would then resemble:: - - Inserted 2 document(s) - array(2) { - [0]=> - object(MongoDB\BSON\ObjectId)#11 (1) { - ["oid"]=> - string(24) "579a25921f417dd1e5518141" - } - [1]=> - object(MongoDB\BSON\ObjectId)#12 (1) { - ["oid"]=> - string(24) "579a25921f417dd1e5518142" - } - } - -.. end-crud-include - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::insertOne()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`insert ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-insertOne.txt b/docs/reference/method/MongoDBCollection-insertOne.txt deleted file mode 100644 index 0d25f6d51..000000000 --- a/docs/reference/method/MongoDBCollection-insertOne.txt +++ /dev/null @@ -1,91 +0,0 @@ -================================ -MongoDB\\Collection::insertOne() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::insertOne() - - Insert one document. - - .. code-block:: php - - function insertOne($document, array $options = []): MongoDB\InsertOneResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-insertOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-insertOne-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\InsertOneResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Example -------- - -.. start-crud-include - -The following operation inserts a document into the ``users`` collection in the -``test`` database: - -.. code-block:: php - - test->users; - - $insertOneResult = $collection->insertOne([ - 'username' => 'admin', - 'email' => 'admin@example.com', - 'name' => 'Admin User', - ]); - - printf("Inserted %d document(s)\n", $insertOneResult->getInsertedCount()); - - var_dump($insertOneResult->getInsertedId()); - -The output would then resemble:: - - Inserted 1 document(s) - object(MongoDB\BSON\ObjectId)#11 (1) { - ["oid"]=> - string(24) "579a25921f417dd1e5518141" - } - -.. end-crud-include - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::insertMany()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`insert ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-listIndexes.txt b/docs/reference/method/MongoDBCollection-listIndexes.txt deleted file mode 100644 index e39a9bbba..000000000 --- a/docs/reference/method/MongoDBCollection-listIndexes.txt +++ /dev/null @@ -1,111 +0,0 @@ -================================== -MongoDB\\Collection::listIndexes() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::listIndexes() - - Returns information for all indexes for this collection. - - .. code-block:: php - - function listIndexes(array $options = []): MongoDB\Model\IndexInfoIterator - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-listIndexes-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-listIndexes-option.rst - -Return Values -------------- - -A traversable :phpclass:`MongoDB\\Model\\IndexInfoIterator`, which contains a -:phpclass:`MongoDB\\Model\\IndexInfo` object for each index for the collection. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example lists all of the indexes for the ``restaurants`` -collection in the ``test`` database: - -.. code-block:: php - - test->restaurants; - - foreach ($collection->listIndexes() as $index) { - var_dump($index); - } - -The output would then resemble:: - - object(MongoDB\Model\IndexInfo)#8 (4) { - ["v"]=> - int(1) - ["key"]=> - array(1) { - ["_id"]=> - int(1) - } - ["name"]=> - string(4) "_id_" - ["ns"]=> - string(16) "test.restaurants" - } - object(MongoDB\Model\IndexInfo)#12 (4) { - ["v"]=> - int(1) - ["key"]=> - array(1) { - ["cuisine"]=> - float(-1) - } - ["name"]=> - string(10) "cuisine_-1" - ["ns"]=> - string(16) "test.restaurants" - } - object(MongoDB\Model\IndexInfo)#8 (4) { - ["v"]=> - int(1) - ["key"]=> - array(1) { - ["borough"]=> - float(1) - } - ["name"]=> - string(9) "borough_1" - ["ns"]=> - string(16) "test.restaurants" - } - -See Also --------- - -- :doc:`/tutorial/indexes` -- :manual:`listIndexes ` command reference in - the MongoDB manual -- :manual:`Index documentation ` in the MongoDB manual -- `Enumerating Collections - `_ - specification diff --git a/docs/reference/method/MongoDBCollection-mapReduce.txt b/docs/reference/method/MongoDBCollection-mapReduce.txt deleted file mode 100644 index 1da9c4bd3..000000000 --- a/docs/reference/method/MongoDBCollection-mapReduce.txt +++ /dev/null @@ -1,128 +0,0 @@ -================================= -MongoDB\\Collection::mapReduce() -================================= - -.. deprecated:: 1.12 - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::mapReduce() - - The :manual:`mapReduce ` command allows you to - run map-reduce aggregation operations over a collection. - - .. code-block:: php - - function mapReduce($map, $reduce, $out, array $options = []): MongoDB\MapReduceResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-mapReduce-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-mapReduce-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\MapReduceResult` object, which allows for iteration of -map-reduce results irrespective of the output method (e.g. inline, collection) -via the :php:`IteratorAggregate ` interface. It also -provides access to command statistics. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -In MongoDB, the map-reduce operation can write results to a collection -or return the results inline. If you write map-reduce output to a -collection, you can perform subsequent map-reduce operations on the -same input collection that merge replace, merge, or reduce new results -with previous results. See :manual:`Map-Reduce ` and -:manual:`Perform Incremental Map-Reduce ` -for details and examples. - -When returning the results of a map-reduce operation *inline*, the -result documents must be within the :limit:`BSON Document Size` limit, -which is currently 16 megabytes. - -MongoDB supports map-reduce operations on :manual:`sharded collections -`. Map-reduce operations can also output -the results to a sharded collection. See -:manual:`Map-Reduce and Sharded Collections `. - -Example -------- - -This example will use city populations to calculate the overall population of -each state. - -.. code-block:: php - - test->zips; - - $map = new MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'); - $reduce = new MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'); - $out = ['inline' => 1]; - - $populations = $collection->mapReduce($map, $reduce, $out); - - foreach ($populations as $pop) { - var_dump($pop); - }; - -The output would then resemble:: - - object(stdClass)#2293 (2) { - ["_id"]=> - string(2) "AK" - ["value"]=> - float(544698) - } - object(stdClass)#2300 (2) { - ["_id"]=> - string(2) "AL" - ["value"]=> - float(4040587) - } - object(stdClass)#2293 (2) { - ["_id"]=> - string(2) "AR" - ["value"]=> - float(2350725) - } - object(stdClass)#2300 (2) { - ["_id"]=> - string(2) "AZ" - ["value"]=> - float(3665228) - } - -See Also --------- - -- :manual:`mapReduce ` command reference in the MongoDB - manual -- :manual:`Map-Reduce ` documentation in the MongoDB manual - diff --git a/docs/reference/method/MongoDBCollection-rename.txt b/docs/reference/method/MongoDBCollection-rename.txt deleted file mode 100644 index 23a9290c3..000000000 --- a/docs/reference/method/MongoDBCollection-rename.txt +++ /dev/null @@ -1,79 +0,0 @@ -============================= -MongoDB\\Collection::rename() -============================= - -.. versionadded:: 1.10 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::rename() - - Rename the collection. - - .. code-block:: php - - function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-rename-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-rename-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`renameCollection -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following operation renames the ``restaurants`` collection in the ``test`` -database to ``places``: - -.. code-block:: php - - test->restaurants; - - $result = $collection->rename('places'); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#9 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::renameCollection()` -- :manual:`renameCollection ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-replaceOne.txt b/docs/reference/method/MongoDBCollection-replaceOne.txt deleted file mode 100644 index ba43452b8..000000000 --- a/docs/reference/method/MongoDBCollection-replaceOne.txt +++ /dev/null @@ -1,93 +0,0 @@ -================================= -MongoDB\\Collection::replaceOne() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::replaceOne() - - Replace at most one document that matches the filter criteria. If multiple - documents match the filter criteria, only the :term:`first ` - matching document will be replaced. - - .. code-block:: php - - function replaceOne($filter, $replacement, array $options = []): MongoDB\UpdateResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-replaceOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-replaceOne-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\UpdateResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Example -------- - -The following example replaces the document with ``restaurant_id`` of -``"40356068"`` in the ``restaurants`` collection in the ``test`` database: - -.. code-block:: php - - test->restaurants; - - $updateResult = $collection->replaceOne( - [ 'restaurant_id' => '40356068' ], - [ - 'name' => 'New Restaurant', - 'restaurant_id' => '99988877', - 'borough' => 'Queens', - 'cuisine' => 'Cafe', - 'grades' => [], - ] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -The output would then resemble:: - - Matched 1 document(s) - Modified 1 document(s) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::updateMany()` -- :phpmethod:`MongoDB\\Collection::updateOne()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`update ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-updateMany.txt b/docs/reference/method/MongoDBCollection-updateMany.txt deleted file mode 100644 index bd72fdf9c..000000000 --- a/docs/reference/method/MongoDBCollection-updateMany.txt +++ /dev/null @@ -1,83 +0,0 @@ -================================= -MongoDB\\Collection::updateMany() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::updateMany() - - Update all documents that match the filter criteria. - - .. code-block:: php - - function updateMany($filter, $update, array $options = []): MongoDB\UpdateResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-updateMany-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-updateMany-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\UpdateResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Examples --------- - -The following example updates all of the documents with the ``borough`` of -``"Queens"`` by setting the ``active`` field to ``true``: - -.. code-block:: php - - $collection = (new MongoDB\Client)->test->restaurants; - - $updateResult = $collection->updateMany( - [ 'borough' => 'Queens' ], - [ '$set' => [ 'active' => 'True' ]] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -The output would then resemble:: - - Matched 5656 document(s) - Modified 5656 document(s) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::replaceOne()` -- :phpmethod:`MongoDB\\Collection::updateOne()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`update ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-updateOne.txt b/docs/reference/method/MongoDBCollection-updateOne.txt deleted file mode 100644 index bed657f92..000000000 --- a/docs/reference/method/MongoDBCollection-updateOne.txt +++ /dev/null @@ -1,85 +0,0 @@ -================================ -MongoDB\\Collection::updateOne() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::updateOne() - - Update at most one document that matches the filter criteria. If multiple - documents match the filter criteria, only the :term:`first ` - matching document will be updated. - - .. code-block:: php - - function updateOne($filter, $update, array $options = []): MongoDB\UpdateResult - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-updateOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-updateOne-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\UpdateResult` object, which encapsulates a -:php:`MongoDB\\Driver\\WriteResult ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-bulkwriteexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst -.. include:: /includes/extracts/bulkwriteexception-result.rst - -Examples --------- - -The following example updates one document with the ``restaurant_id`` of -``"40356151"`` by setting the ``name`` field to ``"Brunos on Astoria"``: - -.. code-block:: php - - $collection = (new MongoDB\Client)->test->restaurants; - - $updateResult = $collection->updateOne( - [ 'restaurant_id' => '40356151' ], - [ '$set' => [ 'name' => 'Brunos on Astoria' ]] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -The output would then resemble:: - - Matched 1 document(s) - Modified 1 document(s) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::replaceOne()` -- :phpmethod:`MongoDB\\Collection::updateMany()` -- :phpmethod:`MongoDB\\Collection::bulkWrite()` -- :doc:`/tutorial/crud` -- :manual:`update ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBCollection-watch.txt b/docs/reference/method/MongoDBCollection-watch.txt deleted file mode 100644 index 50ef408dc..000000000 --- a/docs/reference/method/MongoDBCollection-watch.txt +++ /dev/null @@ -1,123 +0,0 @@ -============================ -MongoDB\\Collection::watch() -============================ - -.. versionadded:: 1.3 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::watch() - - Executes a :manual:`change stream ` operation on the - collection. The change stream can be watched for collection-level changes. - - .. code-block:: php - - function watch(array $pipeline = [], array $options = []): MongoDB\ChangeStream - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-watch-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-watch-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\ChangeStream` object, which allows for iteration of -events in the change stream via the :php:`Iterator ` interface. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -This example reports events while iterating a change stream. - -.. code-block:: php - - test->inventory; - - $changeStream = $collection->watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - if ($event['operationType'] === 'invalidate') { - break; - } - - $ns = sprintf('%s.%s', $event['ns']['db'], $event['ns']['coll']); - $id = json_encode($event['documentKey']['_id']); - - switch ($event['operationType']) { - case 'delete': - printf("Deleted document in %s with _id: %s\n\n", $ns, $id); - break; - - case 'insert': - printf("Inserted new document in %s\n", $ns); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'replace': - printf("Replaced new document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'update': - printf("Updated document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['updateDescription']), "\n\n"; - break; - } - } - -Assuming that a document was inserted, updated, and deleted while the above -script was iterating the change stream, the output would then resemble: - -.. code-block:: none - - Inserted new document in test.user - {"_id":{"$oid":"5b329c4874083047cc05e60a"},"username":"bob"} - - Inserted new document in test.products - {"_id":{"$oid":"5b329c4d74083047cc05e60b"},"name":"Widget","quantity":5} - - Updated document in test.user with _id: {"$oid":"5b329a4f74083047cc05e603"} - {"updatedFields":{"username":"robert"},"removedFields":[]} - -See Also --------- - -- :phpmethod:`MongoDB\\Client::watch()` -- :phpmethod:`MongoDB\\Database::watch()` -- :manual:`Aggregation Pipeline ` documentation in - the MongoDB Manual -- :manual:`Change Streams ` documentation in the MongoDB manual -- :manual:`Change Events ` documentation in the - MongoDB manual diff --git a/docs/reference/method/MongoDBCollection-withOptions.txt b/docs/reference/method/MongoDBCollection-withOptions.txt deleted file mode 100644 index 92e09f6e3..000000000 --- a/docs/reference/method/MongoDBCollection-withOptions.txt +++ /dev/null @@ -1,61 +0,0 @@ -================================== -MongoDB\\Collection::withOptions() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::withOptions() - - Returns a clone of the Collection object, but with different options. - - .. code-block:: php - - function withOptions(array $options = []): MongoDB\Collection - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-withOptions-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-withOptions-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Example -------- - -The following example clones an existing Collection object with a new read -preference: - -.. code-block:: php - - selectCollection('test', 'restaurants'); - - $newCollection = $sourceCollection->withOptions([ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ]); - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::__construct()` diff --git a/docs/reference/method/MongoDBCollection__construct.txt b/docs/reference/method/MongoDBCollection__construct.txt deleted file mode 100644 index e8077c73f..000000000 --- a/docs/reference/method/MongoDBCollection__construct.txt +++ /dev/null @@ -1,54 +0,0 @@ -================================== -MongoDB\\Collection::__construct() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Collection::__construct() - - Constructs a new :phpclass:`Collection ` instance. - - .. code-block:: php - - function __construct(MongoDB\Driver\Manager $manager, string $databaseName, string $collectionName, array $options = []) - - This constructor has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-construct-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBCollection-method-construct-option.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -If you construct a Collection explicitly, the Collection inherits any options -from the :php:`MongoDB\\Driver\\Manager ` object. -If you select the Collection from a :phpclass:`Client ` or -:phpclass:`Database ` object, the Collection inherits its -options from that object. - -.. todo: add an example - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::withOptions()` -- :phpmethod:`MongoDB\\Client::selectCollection()` -- :phpmethod:`MongoDB\\Database::selectCollection()` -- :phpmethod:`MongoDB\\Database::__get()` diff --git a/docs/reference/method/MongoDBDatabase-aggregate.txt b/docs/reference/method/MongoDBDatabase-aggregate.txt deleted file mode 100644 index a7884bfe1..000000000 --- a/docs/reference/method/MongoDBDatabase-aggregate.txt +++ /dev/null @@ -1,81 +0,0 @@ -============================== -MongoDB\\Database::aggregate() -============================== - -.. versionadded:: 1.5 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::aggregate() - - Runs a specified :manual:`admin/diagnostic pipeline - ` which does - not require an underlying collection. For aggregations on collection data, - see :phpmethod:`MongoDB\\Collection::aggregate()`. - - .. code-block:: php - - function aggregate(array $pipeline, array $options = []): Traversable - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-aggregate-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-aggregate-option.rst - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Cursor ` or -:php:`ArrayIterator ` object. In both cases, the return value -will be :php:`Traversable `. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -.. _php-db-agg-method-behavior: - -Examples --------- - -The following aggregation example lists all running commands using the -``$currentOp`` aggregation pipeline stage, then filters this list to only show -running command operations. - -.. code-block:: php - - admin; - - $cursor = $database->aggregate( - [ - ['$currentOp' => []], - ['$match' => ['op' => 'command'], - ] - ); - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::aggregate()` -- :manual:`aggregate ` command reference in the - MongoDB manual -- :manual:`Aggregation Pipeline ` documentation in - the MongoDB Manual diff --git a/docs/reference/method/MongoDBDatabase-command.txt b/docs/reference/method/MongoDBDatabase-command.txt deleted file mode 100644 index 63ddeeaa2..000000000 --- a/docs/reference/method/MongoDBDatabase-command.txt +++ /dev/null @@ -1,138 +0,0 @@ -============================ -MongoDB\\Database::command() -============================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::command() - - Execute a :manual:`command ` on the database. - - .. code-block:: php - - function command($command, array $options = []): MongoDB\Driver\Cursor - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-command-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-command-option.rst - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Cursor ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example executes a :manual:`ping -` command, which returns a cursor with a single -result document: - -.. code-block:: php - - test; - - $cursor = $database->command(['ping' => 1]); - - var_dump($c->toArray()[0]); - -The output would resemble:: - - object(MongoDB\Model\BSONDocument)#11 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -The following example executes a :manual:`listCollections -` command, which returns a cursor with -multiple result documents: - -.. code-block:: php - - test; - - $cursor = $database->command(['listCollections' => 1]); - - var_dump($c->toArray()); - -The output would resemble:: - - array(3) { - [0]=> - object(MongoDB\Model\BSONDocument)#11 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["name"]=> - string(11) "restaurants" - ["options"]=> - object(MongoDB\Model\BSONDocument)#3 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - } - } - [1]=> - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["name"]=> - string(5) "users" - ["options"]=> - object(MongoDB\Model\BSONDocument)#12 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - } - } - [2]=> - object(MongoDB\Model\BSONDocument)#15 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["name"]=> - string(6) "restos" - ["options"]=> - object(MongoDB\Model\BSONDocument)#14 (1) { - ["storage":"ArrayObject":private]=> - array(0) { - } - } - } - } - } - -See Also --------- - -- :doc:`/tutorial/commands` -- :manual:`Database Commands ` in the MongoDB manual -- :php:`MongoDB\\Driver\\Cursor ` -- :php:`MongoDB\\Driver\\Manager::executeCommand() - ` diff --git a/docs/reference/method/MongoDBDatabase-createCollection.txt b/docs/reference/method/MongoDBDatabase-createCollection.txt deleted file mode 100644 index a3e74ef5c..000000000 --- a/docs/reference/method/MongoDBDatabase-createCollection.txt +++ /dev/null @@ -1,98 +0,0 @@ -===================================== -MongoDB\\Database::createCollection() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::createCollection() - - Explicitly creates a collection. - - .. code-block:: php - - function createCollection($collectionName, array $options = []): array|object - - MongoDB creates collections implicitly when you first reference the - collection in a command, such as when inserting a document into a new - collection. You may also explicitly create a collection with specific options - using the :phpmethod:`MongoDB\\Database::createCollection()` method, or using - :manual:`db.createCollection() ` in - the :program:`mongo` shell. - - Explicitly creating collections enables you to create - :manual:`capped collections `, specify - :manual:`document validation criteria `, - or configure your storage engine or indexing options. - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-createCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-createCollection-option.rst - - Note that not all options are available on all versions of MongoDB. Refer to - the :manual:`create ` command reference in the - MongoDB manual for compatibility considerations. - -Return Values -------------- - -An array or object with the result document of the :manual:`create -` command. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example creates a ``users`` collection in the ``test`` -database with document validation criteria: - -.. code-block:: php - - test; - - $result = $db->createCollection('users', [ - 'validator' => [ - 'username' => ['$type' => 'string'], - 'email' => ['$regex' => '@mongodb\.com$'], - ], - ]); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#11 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :manual:`create ` command reference in the MongoDB - manual -- :manual:`db.createCollection() ` -- :manual:`Time Series Collections ` diff --git a/docs/reference/method/MongoDBDatabase-drop.txt b/docs/reference/method/MongoDBDatabase-drop.txt deleted file mode 100644 index bcf6ca88a..000000000 --- a/docs/reference/method/MongoDBDatabase-drop.txt +++ /dev/null @@ -1,78 +0,0 @@ -========================= -MongoDB\\Database::drop() -========================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::drop() - - Drop the database. - - .. code-block:: php - - function drop(array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-drop-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-drop-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`dropDatabase -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example drops the ``test`` database: - -.. code-block:: php - - test; - - $result = $db->drop(); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dropped"]=> - string(4) "test" - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Client::dropDatabase()` -- :manual:`dropDatabase ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBDatabase-dropCollection.txt b/docs/reference/method/MongoDBDatabase-dropCollection.txt deleted file mode 100644 index fbc005b86..000000000 --- a/docs/reference/method/MongoDBDatabase-dropCollection.txt +++ /dev/null @@ -1,80 +0,0 @@ -=================================== -MongoDB\\Database::dropCollection() -=================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::dropCollection() - - Drop a collection within the current database. - - .. code-block:: php - - function dropCollection($collectionName, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-dropCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-dropCollection-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`drop -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example drops the ``users`` collection in the ``test`` database: - -.. code-block:: php - - test; - - $result = $db->dropCollection('users'); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["ns"]=> - string(10) "test.users" - ["nIndexesWas"]=> - int(1) - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::drop()` -- :manual:`drop ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBDatabase-getDatabaseName.txt b/docs/reference/method/MongoDBDatabase-getDatabaseName.txt deleted file mode 100644 index 6ff9dcb96..000000000 --- a/docs/reference/method/MongoDBDatabase-getDatabaseName.txt +++ /dev/null @@ -1,44 +0,0 @@ -==================================== -MongoDB\\Database::getDatabaseName() -==================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getDatabaseName() - - Returns the name of this database. - - .. code-block:: php - - function getDatabaseName(): string - -Return Values -------------- - -The name of this database as a string. - -Example -------- - -The following example prints the name of a database object: - -.. code-block:: php - - test; - - echo $db->getDatabaseName(); - -The output would then resemble:: - - test diff --git a/docs/reference/method/MongoDBDatabase-getManager.txt b/docs/reference/method/MongoDBDatabase-getManager.txt deleted file mode 100644 index 72a2b0570..000000000 --- a/docs/reference/method/MongoDBDatabase-getManager.txt +++ /dev/null @@ -1,35 +0,0 @@ -=============================== -MongoDB\\Database::getManager() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getManager() - - Accessor for the - :php:`MongoDB\\Driver\\Manager ` used by this - :phpclass:`Database `. - - .. code-block:: php - - function getManager(): MongoDB\Manager - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Manager ` object. - -See Also --------- - -- :phpmethod:`MongoDB\\Client::getManager()` -- :phpmethod:`MongoDB\\Collection::getManager()` diff --git a/docs/reference/method/MongoDBDatabase-getReadConcern.txt b/docs/reference/method/MongoDBDatabase-getReadConcern.txt deleted file mode 100644 index fa277d884..000000000 --- a/docs/reference/method/MongoDBDatabase-getReadConcern.txt +++ /dev/null @@ -1,58 +0,0 @@ -=================================== -MongoDB\\Database::getReadConcern() -=================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getReadConcern() - - Returns the read concern for this database. - - .. code-block:: php - - function getReadConcern(): MongoDB\Driver\ReadConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadConcern ` object. - -Example -------- - -.. code-block:: php - - selectDatabase('test', [ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), - ]); - - var_dump($database->getReadConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadConcern)#5 (1) { - ["level"]=> - string(8) "majority" - } - -See Also --------- - -- :manual:`Read Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\ReadConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getReadConcern()` -- :phpmethod:`MongoDB\\Collection::getReadConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadConcern()` diff --git a/docs/reference/method/MongoDBDatabase-getReadPreference.txt b/docs/reference/method/MongoDBDatabase-getReadPreference.txt deleted file mode 100644 index c70b40268..000000000 --- a/docs/reference/method/MongoDBDatabase-getReadPreference.txt +++ /dev/null @@ -1,58 +0,0 @@ -====================================== -MongoDB\\Database::getReadPreference() -====================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getReadPreference() - - Returns the read preference for this database. - - .. code-block:: php - - function getReadPreference(): MongoDB\Driver\ReadPreference - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadPreference ` -object. - -Example -------- - -.. code-block:: php - - selectDatabase('test', [ - 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), - ]); - - var_dump($database->getReadPreference()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadPreference)#5 (1) { - ["mode"]=> - string(16) "primaryPreferred" - } - -See Also --------- - -- :manual:`Read Preference ` in the MongoDB manual -- :phpmethod:`MongoDB\\Client::getReadPreference()` -- :phpmethod:`MongoDB\\Collection::getReadPreference()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getReadPreference()` diff --git a/docs/reference/method/MongoDBDatabase-getTypeMap.txt b/docs/reference/method/MongoDBDatabase-getTypeMap.txt deleted file mode 100644 index bc1688d63..000000000 --- a/docs/reference/method/MongoDBDatabase-getTypeMap.txt +++ /dev/null @@ -1,65 +0,0 @@ -=============================== -MongoDB\\Database::getTypeMap() -=============================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getTypeMap() - - Returns the type map for this database. - - .. code-block:: php - - function getTypeMap(): array - -Return Values -------------- - -A :ref:`type map ` array. - -Example -------- - -.. code-block:: php - - selectDatabase('test', [ - 'typeMap' => [ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ], - ]); - - var_dump($database->getTypeMap()); - -The output would then resemble:: - - array(3) { - ["root"]=> - string(5) "array" - ["document"]=> - string(5) "array" - ["array"]=> - string(5) "array" - } - -See Also --------- - -- :doc:`/reference/bson` -- :phpmethod:`MongoDB\\Client::getTypeMap()` -- :phpmethod:`MongoDB\\Collection::getTypeMap()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getTypeMap()` diff --git a/docs/reference/method/MongoDBDatabase-getWriteConcern.txt b/docs/reference/method/MongoDBDatabase-getWriteConcern.txt deleted file mode 100644 index 31b0ded51..000000000 --- a/docs/reference/method/MongoDBDatabase-getWriteConcern.txt +++ /dev/null @@ -1,61 +0,0 @@ -==================================== -MongoDB\\Database::getWriteConcern() -==================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::getWriteConcern() - - Returns the write concern for this database. - - .. code-block:: php - - function getWriteConcern(): MongoDB\Driver\WriteConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\WriteConcern ` -object. - -Example -------- - -.. code-block:: php - - selectDatabase('test', [ - 'writeConcern' => new MongoDB\Driver\WriteConcern(1, 0, true), - ]); - - var_dump($database->getWriteConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\WriteConcern)#5 (2) { - ["w"]=> - int(1) - ["j"]=> - bool(true) - } - -See Also --------- - -- :manual:`Write Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\WriteConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getWriteConcern()` -- :phpmethod:`MongoDB\\Collection::getWriteConcern()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::getWriteConcern()` diff --git a/docs/reference/method/MongoDBDatabase-listCollectionNames.txt b/docs/reference/method/MongoDBDatabase-listCollectionNames.txt deleted file mode 100644 index 86872bbbc..000000000 --- a/docs/reference/method/MongoDBDatabase-listCollectionNames.txt +++ /dev/null @@ -1,98 +0,0 @@ -======================================== -MongoDB\\Database::listCollectionNames() -======================================== - -.. versionadded:: 1.7 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::listCollectionNames() - - Returns names for all collections in this database. - - .. code-block:: php - - function listCollectionNames(array $options = []): Iterator - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-listCollections-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-listCollections-option.rst - -Return Values -------------- - -An :php:`Iterator `, which provides the name of each -collection in the database. - -Example -------- - -The following example lists all of the collections in the ``test`` database: - -.. code-block:: php - - test; - - foreach ($database->listCollectionNames() as $collectionName) { - var_dump($collectionName); - } - -The output would then resemble:: - - string(11) "restaurants" - string(5) "users" - string(6) "restos" - -The following example lists all collections whose name starts with ``"rest"`` -in the ``test`` database: - -.. code-block:: php - - test; - - $collections = $database->listCollectionNames([ - 'filter' => [ - 'name' => new MongoDB\BSON\Regex('^rest.*'), - ], - ]); - - foreach ($collections as $collectionName) { - var_dump($collectionName); - } - -The output would then resemble:: - - string(11) "restaurants" - string(6) "restos" - -.. note:: - - When enumerating collection names, a filter expression can only filter based - on a collection's name and type. No other fields are available. - -See Also --------- - -- :phpmethod:`MongoDB\\Database::listCollections()` -- :manual:`listCollections ` command - reference in the MongoDB manual -- `Enumerating Collections - `_ - specification diff --git a/docs/reference/method/MongoDBDatabase-listCollections.txt b/docs/reference/method/MongoDBDatabase-listCollections.txt deleted file mode 100644 index cfaff6c3d..000000000 --- a/docs/reference/method/MongoDBDatabase-listCollections.txt +++ /dev/null @@ -1,122 +0,0 @@ -==================================== -MongoDB\\Database::listCollections() -==================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::listCollections() - - Returns information for all collections in this database. - - .. code-block:: php - - function listCollections(array $options = []): MongoDB\Model\CollectionInfoIterator - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-listCollections-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-listCollections-option.rst - -Return Values -------------- - -A traversable :phpclass:`MongoDB\\Model\\CollectionInfoIterator`, which contains -a :phpclass:`MongoDB\\Model\\CollectionInfo` object for each collection in the -database. - -Example -------- - -The following example lists all of the collections in the ``test`` database: - -.. code-block:: php - - test; - - foreach ($database->listCollections() as $collectionInfo) { - var_dump($collectionInfo); - } - -The output would then resemble:: - - object(MongoDB\Model\CollectionInfo)#3 (2) { - ["name"]=> - string(11) "restaurants" - ["options"]=> - array(0) { - } - } - object(MongoDB\Model\CollectionInfo)#3 (2) { - ["name"]=> - string(5) "users" - ["options"]=> - array(0) { - } - } - object(MongoDB\Model\CollectionInfo)#3 (2) { - ["name"]=> - string(6) "restos" - ["options"]=> - array(0) { - } - } - -The following example lists all collections whose name starts with ``"rest"`` -in the ``test`` database: - -.. code-block:: php - - test; - - $collections = $database->listCollections([ - 'filter' => [ - 'name' => new MongoDB\BSON\Regex('^rest.*'), - ], - ]); - - foreach ($collections as $collectionInfo) { - var_dump($collectionInfo); - } - -The output would then resemble:: - - object(MongoDB\Model\CollectionInfo)#3 (2) { - ["name"]=> - string(11) "restaurants" - ["options"]=> - array(0) { - } - } - object(MongoDB\Model\CollectionInfo)#3 (2) { - ["name"]=> - string(6) "restos" - ["options"]=> - array(0) { - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::listCollectionNames()` -- :manual:`listCollections ` command - reference in the MongoDB manual -- `Enumerating Collections - `_ - specification diff --git a/docs/reference/method/MongoDBDatabase-modifyCollection.txt b/docs/reference/method/MongoDBDatabase-modifyCollection.txt deleted file mode 100644 index 43eb2d259..000000000 --- a/docs/reference/method/MongoDBDatabase-modifyCollection.txt +++ /dev/null @@ -1,81 +0,0 @@ -===================================== -MongoDB\\Database::modifyCollection() -===================================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::modifyCollection() - - Modifies a collection or view according to the specified - ``$collectionOptions``. - - .. code-block:: php - - function modifyCollection($collectionName, array $collectionOptions, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-modifyCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-modifyCollection-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`collMod -` command. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example changes the expiration time of a TTL collection in the -``test`` database: - -.. code-block:: php - - test; - - $result = $db->modifyCollection('users', [ - 'keyPattern' => ['lastAccess' => 1], - 'expireAfterSeconds' => 1000 - ]); - - var_dump($result); - -The output would then resemble:: - - object(stdClass)#2779 { - ["expireAfterSeconds_old"]=> - int(3) - ["expireAfterSeconds_new"]=> - int(1000) - ["ok"]=> - float(1) - } - -See Also --------- - -- :manual:`collMod ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBDatabase-renameCollection.txt b/docs/reference/method/MongoDBDatabase-renameCollection.txt deleted file mode 100644 index 558443195..000000000 --- a/docs/reference/method/MongoDBDatabase-renameCollection.txt +++ /dev/null @@ -1,79 +0,0 @@ -===================================== -MongoDB\\Database::renameCollection() -===================================== - -.. versionadded:: 1.10 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::renameCollection() - - Rename a collection within the current database. - - .. code-block:: php - - function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-renameCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-renameCollection-option.rst - -Return Values -------------- - -An array or object with the result document of the :manual:`renameCollection -` command. The return type will depend on the -``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Example -------- - -The following example renames the ``restaurants`` collection in the ``test`` -database to ``places``: - -.. code-block:: php - - test; - - $result = $db->renameCollection('restaurants', 'places'); - - var_dump($result); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::rename()` -- :manual:`renameCollection ` command reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBDatabase-selectCollection.txt b/docs/reference/method/MongoDBDatabase-selectCollection.txt deleted file mode 100644 index 6294e1a75..000000000 --- a/docs/reference/method/MongoDBDatabase-selectCollection.txt +++ /dev/null @@ -1,83 +0,0 @@ -===================================== -MongoDB\\Database::selectCollection() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::selectCollection() - - Selects a collection within the database. - - .. code-block:: php - - function selectCollection($collectionName, array $options = []): MongoDB\Collection - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-selectCollection-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-selectCollection-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -The selected collection inherits options such as read preference and type -mapping from the :phpclass:`Database ` object. Options may be -overridden via the ``$options`` parameter. - -Example -------- - -The following example selects the ``users`` collection in the ``test`` database: - -.. code-block:: php - - test; - - $collection = $db->selectCollection('users'); - -The following example selects the ``users`` collection in the ``test`` -database with a custom read preference: - -.. code-block:: php - - test; - - $users = $db->selectCollection( - 'users', - [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ] - ); - -See Also --------- - -- :phpmethod:`MongoDB\\Database::__get()` -- :phpmethod:`MongoDB\\Client::selectCollection()` -- :phpmethod:`MongoDB\\Collection::__construct()` diff --git a/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt b/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt deleted file mode 100644 index 9c63fdf7c..000000000 --- a/docs/reference/method/MongoDBDatabase-selectGridFSBucket.txt +++ /dev/null @@ -1,80 +0,0 @@ -======================================= -MongoDB\\Database::selectGridFSBucket() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::selectGridFSBucket() - - Selects a GridFS bucket within the database. - - .. code-block:: php - - function selectGridFSBucket(array $options = []): MongoDB\GridFS\Bucket - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-selectGridFSBucket-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-selectGridFSBucket-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\GridFS\\Bucket` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -The selected bucket inherits options such as read preference and type -mapping from the :phpclass:`Database ` object. Options may be -overridden via the ``$options`` parameter. - -Example -------- - -The following example selects the default ``fs.files`` bucket in the ``test`` -database: - -.. code-block:: php - - test; - - $bucket = $db->selectGridFSBucket(); - -The following example selects the custom ``images.files`` bucket in the ``test`` -database with a custom read preference: - -.. code-block:: php - - test; - - $imagesBucket = $db->selectGridFSBucket([ - 'bucketName' => 'images', - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ]); - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::__construct()` diff --git a/docs/reference/method/MongoDBDatabase-watch.txt b/docs/reference/method/MongoDBDatabase-watch.txt deleted file mode 100644 index 7ff19b73a..000000000 --- a/docs/reference/method/MongoDBDatabase-watch.txt +++ /dev/null @@ -1,122 +0,0 @@ -========================== -MongoDB\\Database::watch() -========================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::watch() - - Executes a :manual:`change stream ` operation on the - database. The change stream can be watched for database-level changes. - - .. code-block:: php - - function watch(array $pipeline = [], array $options = []): MongoDB\ChangeStream - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-watch-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-watch-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\ChangeStream` object, which allows for iteration of -events in the change stream via the :php:`Iterator ` interface. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unexpectedvalueexception.rst -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -This example reports events while iterating a change stream. - -.. code-block:: php - - test; - - $changeStream = $database->watch(); - - for ($changeStream->rewind(); true; $changeStream->next()) { - if ( ! $changeStream->valid()) { - continue; - } - - $event = $changeStream->current(); - - if ($event['operationType'] === 'invalidate') { - break; - } - - $ns = sprintf('%s.%s', $event['ns']['db'], $event['ns']['coll']); - $id = json_encode($event['documentKey']['_id']); - - switch ($event['operationType']) { - case 'delete': - printf("Deleted document in %s with _id: %s\n\n", $ns, $id); - break; - - case 'insert': - printf("Inserted new document in %s\n", $ns); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'replace': - printf("Replaced new document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['fullDocument']), "\n\n"; - break; - - case 'update': - printf("Updated document in %s with _id: %s\n", $ns, $id); - echo json_encode($event['updateDescription']), "\n\n"; - break; - } - } - -Assuming that a document was inserted, updated, and deleted while the above -script was iterating the change stream, the output would then resemble: - -.. code-block:: none - - Inserted new document in test.inventory - {"_id":{"$oid":"5a81fc0d6118fd1af1790d32"},"name":"Widget","quantity":5} - - Updated document in test.inventory with _id: {"$oid":"5a81fc0d6118fd1af1790d32"} - {"updatedFields":{"quantity":4},"removedFields":[]} - - Deleted document in test.inventory with _id: {"$oid":"5a81fc0d6118fd1af1790d32"} - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::watch()` -- :phpmethod:`MongoDB\\Client::watch()` -- :manual:`Aggregation Pipeline ` documentation in - the MongoDB Manual -- :manual:`Change Streams ` documentation in the MongoDB manual -- :manual:`Change Events ` documentation in the - MongoDB manual diff --git a/docs/reference/method/MongoDBDatabase-withOptions.txt b/docs/reference/method/MongoDBDatabase-withOptions.txt deleted file mode 100644 index 9bc0a82ea..000000000 --- a/docs/reference/method/MongoDBDatabase-withOptions.txt +++ /dev/null @@ -1,61 +0,0 @@ -================================ -MongoDB\\Database::withOptions() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::withOptions() - - Returns a clone of the Database object, but with different options. - - .. code-block:: php - - function withOptions(array $options = []): MongoDB\Database - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-withOptions-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-withOptions-option.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Database` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Example -------- - -The following example clones an existing Database object with a new read -preference: - -.. code-block:: php - - test; - - $newDb = $db->withOptions([ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_SECONDARY), - ]); - -See Also --------- - -- :phpmethod:`MongoDB\\Database::__construct()` diff --git a/docs/reference/method/MongoDBDatabase__construct.txt b/docs/reference/method/MongoDBDatabase__construct.txt deleted file mode 100644 index 2d3e7f57b..000000000 --- a/docs/reference/method/MongoDBDatabase__construct.txt +++ /dev/null @@ -1,50 +0,0 @@ -================================ -MongoDB\\Database::__construct() -================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::__construct() - - Constructs a new :phpclass:`Database ` instance. - - .. code-block:: php - - function __construct(MongoDB\Driver\Manager $manager, $databaseName, array $options = []) - - This constructor has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-construct-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBDatabase-method-construct-option.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -If you construct a Database explicitly, the Database inherits any options from -the :php:`MongoDB\\Driver\\Manager ` object. If -you select the Database from a :phpclass:`Client ` object, the -Database inherits its options from that object. - -See Also --------- - -- :phpmethod:`MongoDB\\Database::withOptions()` -- :phpmethod:`MongoDB\\Client::selectDatabase()` -- :phpmethod:`MongoDB\\Client::__get()` diff --git a/docs/reference/method/MongoDBDatabase__get.txt b/docs/reference/method/MongoDBDatabase__get.txt deleted file mode 100644 index c41e3981d..000000000 --- a/docs/reference/method/MongoDBDatabase__get.txt +++ /dev/null @@ -1,69 +0,0 @@ -========================== -MongoDB\\Database::__get() -========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Database::__get() - - Select a collection within the database. - - .. code-block:: php - - function __get($collectionName): MongoDB\Collection - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBDatabase-method-get-param.rst - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object. - -Behavior --------- - -The selected collection inherits options such as read preference and type -mapping from the :phpclass:`Database ` object. If you wish to -override any options, use the :phpmethod:`MongoDB\\Database::selectCollection` -method. - -.. note:: - - To select collections whose names contain special characters, such as - ``.``, use complex syntax, as in ``$database->{'that.database'}``. - - Alternatively, :phpmethod:`MongoDB\\Database::selectCollection` supports - selecting collections whose names contain special characters. - -Examples --------- - -The following example selects the ``users`` and ``system.profile`` -collections from the ``test`` database: - -.. code-block:: php - - test; - - $users = $db->users; - $systemProfile = $db->{'system.profile'}; - -See Also --------- - -- :phpmethod:`MongoDB\\Database::selectCollection()` -- :phpmethod:`MongoDB\\Client::selectCollection()` -- :php:`Property Overloading ` in the PHP Manual diff --git a/docs/reference/method/MongoDBDeleteResult-getDeletedCount.txt b/docs/reference/method/MongoDBDeleteResult-getDeletedCount.txt deleted file mode 100644 index dcda25741..000000000 --- a/docs/reference/method/MongoDBDeleteResult-getDeletedCount.txt +++ /dev/null @@ -1,40 +0,0 @@ -======================================== -MongoDB\\DeleteResult::getDeletedCount() -======================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\DeleteResult::getDeletedCount() - - Return the number of documents that were deleted. - - .. code-block:: php - - function getDeletedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The number of documents that were deleted. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getDeletedCount() - ` diff --git a/docs/reference/method/MongoDBDeleteResult-isAcknowledged.txt b/docs/reference/method/MongoDBDeleteResult-isAcknowledged.txt deleted file mode 100644 index a82fa5238..000000000 --- a/docs/reference/method/MongoDBDeleteResult-isAcknowledged.txt +++ /dev/null @@ -1,34 +0,0 @@ -======================================= -MongoDB\\DeleteResult::isAcknowledged() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\DeleteResult::isAcknowledged() - - Return whether the write was acknowledged. - - .. code-block:: php - - function isAcknowledged(): boolean - -Return Values -------------- - -A boolean indicating whether the write was acknowledged. - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::isAcknowledged() - ` -- :manual:`Write Concern ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBGridFSBucket-delete.txt b/docs/reference/method/MongoDBGridFSBucket-delete.txt deleted file mode 100644 index b063337e7..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-delete.txt +++ /dev/null @@ -1,55 +0,0 @@ -================================= -MongoDB\\GridFS\\Bucket::delete() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::delete() - - Delete a file and its chunks from the GridFS bucket. - - .. code-block:: php - - function delete($id): void - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-delete-param.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -If the files collection document is not found, this method will still attempt to -delete orphaned chunks. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $id = $bucket->uploadFromStream('filename', $stream); - - $bucket->delete($id); diff --git a/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt b/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt deleted file mode 100644 index 3d8e6ebcf..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-downloadToStream.txt +++ /dev/null @@ -1,66 +0,0 @@ -=========================================== -MongoDB\\GridFS\\Bucket::downloadToStream() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::downloadToStream() - - Selects a GridFS file by its ``_id`` and copies its contents to a writable - stream. - - .. code-block:: php - - function downloadToStream($id, $destination): void - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-downloadToStream-param.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $id = $bucket->uploadFromStream('filename', $stream); - - $destination = fopen('php://temp', 'w+b'); - - $bucket->downloadToStream($id, $destination); - - var_dump(stream_get_contents($destination, -1, 0)); - -The output would then resemble:: - - string(6) "foobar" - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStreamByName()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStream()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStreamByName()` diff --git a/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt b/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt deleted file mode 100644 index a505e115c..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-downloadToStreamByName.txt +++ /dev/null @@ -1,70 +0,0 @@ -================================================= -MongoDB\\GridFS\\Bucket::downloadToStreamByName() -================================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::downloadToStreamByName() - - Selects a GridFS file by its ``filename`` and copies its contents to a - writable stream. - - .. code-block:: php - - function downloadToStreamByName(string $filename, $destination, array $options = []): void - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-downloadToStreamByName-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-downloadToStreamByName-option.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $bucket->uploadFromStream('filename', $stream); - - $destination = fopen('php://temp', 'w+b'); - - $bucket->downloadToStreamByName('filename', $destination); - - var_dump(stream_get_contents($destination, -1, 0)); - -The output would then resemble:: - - string(6) "foobar" - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStream()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStream()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStreamByName()` diff --git a/docs/reference/method/MongoDBGridFSBucket-drop.txt b/docs/reference/method/MongoDBGridFSBucket-drop.txt deleted file mode 100644 index 53a10091a..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-drop.txt +++ /dev/null @@ -1,46 +0,0 @@ -=============================== -MongoDB\\GridFS\\Bucket::drop() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::drop() - - Drops the files and chunks collections associated with this GridFS bucket. - - .. code-block:: php - - function drop(): void - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test; - - $bucket = $database->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $bucket->uploadFromStream('filename', $stream); - - $bucket->drop(); diff --git a/docs/reference/method/MongoDBGridFSBucket-find.txt b/docs/reference/method/MongoDBGridFSBucket-find.txt deleted file mode 100644 index c7418cede..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-find.txt +++ /dev/null @@ -1,97 +0,0 @@ -=============================== -MongoDB\\GridFS\\Bucket::find() -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::find() - - Finds documents from the GridFS bucket's files collection matching the query. - - .. code-block:: php - - function find($filter = [], array $options = []): MongoDB\Driver\Cursor - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-find-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-find-option.rst - -Return Values -------------- - -A :php:`MongoDB\\Driver\\Cursor ` object. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $bucket->uploadFromStream('b', $stream); - - $cursor = $bucket->find( - ['length' => ['$lte' => 6]], - [ - 'projection' => [ - 'filename' => 1, - 'length' => 1, - '_id' => 0, - ], - 'sort' => ['length' => -1], - ] - ); - - var_dump($cursor->toArray()); - -The output would then resemble:: - - array(1) { - [0]=> - object(MongoDB\Model\BSONDocument)#3015 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["filename"]=> - string(1) "b" - ["length"]=> - int(6) - } - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::find()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::findOne()` diff --git a/docs/reference/method/MongoDBGridFSBucket-findOne.txt b/docs/reference/method/MongoDBGridFSBucket-findOne.txt deleted file mode 100644 index 7b6fcc8fc..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-findOne.txt +++ /dev/null @@ -1,97 +0,0 @@ -================================== -MongoDB\\GridFS\\Bucket::findOne() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::findOne() - - Finds a single document from the GridFS bucket's files collection matching - the query. - - .. code-block:: php - - function findOne($filter = [], array $options = []): array|object|null - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBCollection-method-findOne-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-findOne-option.rst - -Return Values -------------- - -An array or object for the :term:`first document ` that matched -the query, or ``null`` if no document matched the query. The return type will -depend on the ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-unsupportedexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Behavior --------- - -.. include:: /includes/extracts/note-bson-comparison.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $bucket->uploadFromStream('b', $stream); - - $fileDocument = $bucket->findOne( - ['length' => ['$lte' => 6]], - [ - 'projection' => [ - 'filename' => 1, - 'length' => 1, - '_id' => 0, - ], - 'sort' => ['length' => -1], - ] - ); - - var_dump($fileDocument); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#3004 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["filename"]=> - string(1) "b" - ["length"]=> - int(6) - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::findOne()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::find()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt b/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt deleted file mode 100644 index 0452de0a7..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getBucketName.txt +++ /dev/null @@ -1,42 +0,0 @@ -======================================== -MongoDB\\GridFS\\Bucket::getBucketName() -======================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getBucketName() - - Returns the name of this bucket. - - .. code-block:: php - - function getBucketName(): string - -Return Values -------------- - -The name of this bucket as a string. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - var_dump($bucket->getBucketName()); - -The output would then resemble:: - - string(2) "fs" diff --git a/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt b/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt deleted file mode 100644 index f592030a2..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getChunkSizeBytes.txt +++ /dev/null @@ -1,44 +0,0 @@ -============================================ -MongoDB\\GridFS\\Bucket::getChunkSizeBytes() -============================================ - -.. versionchanged:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getChunkSizeBytes() - - Returns the chunk size of this bucket in bytes. - - .. code-block:: php - - function getChunkSizeBytes(): integer - -Return Values -------------- - -The chunk size of this bucket in bytes. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - var_dump($bucket->getChunkSizeBytes()); - -The output would then resemble:: - - int(261120) diff --git a/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt b/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt deleted file mode 100644 index fa5be8322..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getChunksCollection.txt +++ /dev/null @@ -1,44 +0,0 @@ -============================================== -MongoDB\\GridFS\\Bucket::getChunksCollection() -============================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getChunksCollection() - - Returns the chunks collection used by the bucket. - - .. code-block:: php - - function getChunksCollection(): MongoDB\Collection - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object for the chunks collection. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - var_dump((string) $bucket->getChunksCollection()); - -The output would then resemble:: - - string(14) "test.fs.chunks" diff --git a/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt b/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt deleted file mode 100644 index b270b7b57..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getDatabaseName.txt +++ /dev/null @@ -1,43 +0,0 @@ -========================================== -MongoDB\\GridFS\\Bucket::getDatabaseName() -========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getDatabaseName() - - Returns the name of the database containing this bucket. - - .. code-block:: php - - function getDatabaseName(): string - -Return Values -------------- - -The name of the database containing this bucket as a string. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - var_dump($bucket->getDatabaseName()); - -The output would then resemble:: - - string(4) "test" - diff --git a/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt b/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt deleted file mode 100644 index 052bc79c9..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getFileDocumentForStream.txt +++ /dev/null @@ -1,77 +0,0 @@ -=================================================== -MongoDB\\GridFS\\Bucket::getFileDocumentForStream() -=================================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getFileDocumentForStream() - - Gets the file document of the GridFS file associated with a stream. - - .. code-block:: php - - function getFileDocumentForStream($stream): array|object - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-getFileDocumentForStream-param.rst - -Return Values -------------- - -The metadata document associated with the GridFS stream. The return type will -depend on the bucket's ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openUploadStream('filename'); - - $fileDocument = $bucket->getFileDocumentForStream($stream); - - var_dump($fileDocument); - - fclose($stream); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#4956 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#4955 (1) { - ["oid"]=> - string(24) "5acfb05b7e21e83b5a29037c" - } - ["chunkSize"]=> - int(261120) - ["filename"]=> - string(8) "filename" - } - } - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::getFileIdForStream()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt b/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt deleted file mode 100644 index 976843b4d..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getFileIdForStream.txt +++ /dev/null @@ -1,68 +0,0 @@ -============================================= -MongoDB\\GridFS\\Bucket::getFileIdForStream() -============================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getFileIdForStream() - - Gets the file document's ID of the GridFS file associated with a stream. - - .. code-block:: php - - function getFileIdForStream($stream): mixed - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-getFileIdForStream-param.rst - -Return Values -------------- - -The ``_id`` field of the metadata document associated with the GridFS stream. -The return type will depend on the bucket's ``typeMap`` option. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-corruptfileexception.rst -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openUploadStream('filename'); - - $id = $bucket->getFileIdForStream($stream); - - var_dump($id); - - fclose($stream); - -The output would then resemble:: - - object(MongoDB\BSON\ObjectId)#3005 (1) { - ["oid"]=> - string(24) "5acfb37d7e21e83cdb3e1583" - } - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::getFileDocumentForStream()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt b/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt deleted file mode 100644 index de4196b2f..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getFilesCollection.txt +++ /dev/null @@ -1,47 +0,0 @@ -============================================= -MongoDB\\GridFS\\Bucket::getFilesCollection() -============================================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getFilesCollection() - - Returns the files collection used by the bucket. - - .. code-block:: php - - function getFilesCollection(): MongoDB\Collection - -Return Values -------------- - -A :phpclass:`MongoDB\\Collection` object for the files collection. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $filesCollection = $bucket->getFilesCollection(); - - var_dump($filesCollection->getCollectionName()); - -The output would then resemble:: - - string(8) "fs.files" - diff --git a/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt b/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt deleted file mode 100644 index 00517d220..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getReadConcern.txt +++ /dev/null @@ -1,59 +0,0 @@ -========================================= -MongoDB\\GridFS\\Bucket::getReadConcern() -========================================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getReadConcern() - - Returns the read concern for this GridFS bucket. - - .. code-block:: php - - function getReadConcern(): MongoDB\Driver\ReadConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadConcern ` object. - -Example -------- - -.. code-block:: php - - selectDatabase('test'); - $bucket = $database->selectGridFSBucket([ - 'readConcern' => new MongoDB\Driver\ReadConcern(MongoDB\Driver\ReadConcern::MAJORITY), - ]); - - var_dump($bucket->getReadConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadConcern)#3 (1) { - ["level"]=> - string(8) "majority" - } - -See Also --------- - -- :manual:`Read Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\ReadConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getReadConcern()` -- :phpmethod:`MongoDB\\Collection::getReadConcern()` -- :phpmethod:`MongoDB\\Database::getReadConcern()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt b/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt deleted file mode 100644 index 94003eea7..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getReadPreference.txt +++ /dev/null @@ -1,59 +0,0 @@ -============================================ -MongoDB\\GridFS\\Bucket::getReadPreference() -============================================ - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getReadPreference() - - Returns the read preference for this GridFS bucket. - - .. code-block:: php - - function getReadPreference(): MongoDB\Driver\ReadPreference - -Return Values -------------- - -A :php:`MongoDB\\Driver\\ReadPreference ` -object. - -Example -------- - -.. code-block:: php - - selectDatabase('test'); - $bucket = $database->selectGridFSBucket([ - 'readPreference' => new MongoDB\Driver\ReadPreference('primaryPreferred'), - ]); - - var_dump($bucket->getReadPreference()); - -The output would then resemble:: - - object(MongoDB\Driver\ReadPreference)#3 (1) { - ["mode"]=> - string(16) "primaryPreferred" - } - -See Also --------- - -- :manual:`Read Preference ` in the MongoDB manual -- :phpmethod:`MongoDB\\Client::getReadPreference()` -- :phpmethod:`MongoDB\\Collection::getReadPreference()` -- :phpmethod:`MongoDB\\Database::getReadPreference()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt b/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt deleted file mode 100644 index cd9c4a1be..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getTypeMap.txt +++ /dev/null @@ -1,66 +0,0 @@ -===================================== -MongoDB\\GridFS\\Bucket::getTypeMap() -===================================== - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getTypeMap() - - Returns the type map for this GridFS bucket. - - .. code-block:: php - - function getTypeMap(): array - -Return Values -------------- - -A :ref:`type map ` array. - -Example -------- - -.. code-block:: php - - selectDatabase('test'); - $bucket = $database->selectGridFSBucket([ - 'typeMap' => [ - 'root' => 'array', - 'document' => 'array', - 'array' => 'array', - ], - ]); - - var_dump($bucket->getTypeMap()); - -The output would then resemble:: - - array(3) { - ["root"]=> - string(5) "array" - ["document"]=> - string(5) "array" - ["array"]=> - string(5) "array" - } - -See Also --------- - -- :doc:`/reference/bson` -- :phpmethod:`MongoDB\\Client::getTypeMap()` -- :phpmethod:`MongoDB\\Collection::getTypeMap()` -- :phpmethod:`MongoDB\\Database::getTypeMap()` diff --git a/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt b/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt deleted file mode 100644 index 1b5577e6c..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-getWriteConcern.txt +++ /dev/null @@ -1,62 +0,0 @@ -========================================= -MongoDB\\GridFS\Bucket::getWriteConcern() -========================================= - -.. versionadded:: 1.2 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::getWriteConcern() - - Returns the write concern for this GridFS bucket. - - .. code-block:: php - - function getWriteConcern(): MongoDB\Driver\WriteConcern - -Return Values -------------- - -A :php:`MongoDB\\Driver\\WriteConcern ` -object. - -Example -------- - -.. code-block:: php - - selectDatabase('test'); - $bucket = $database->selectGridFSBucket([ - 'writeConcern' => new MongoDB\Driver\WriteConcern(1, 0, true), - ]); - - var_dump($bucket->getWriteConcern()); - -The output would then resemble:: - - object(MongoDB\Driver\WriteConcern)#3 (2) { - ["w"]=> - int(1) - ["j"]=> - bool(true) - } - -See Also --------- - -- :manual:`Write Concern ` in the MongoDB manual -- :php:`MongoDB\\Driver\\WriteConcern::isDefault() ` -- :phpmethod:`MongoDB\\Client::getWriteConcern()` -- :phpmethod:`MongoDB\\Collection::getWriteConcern()` -- :phpmethod:`MongoDB\\Database::getWriteConcern()` diff --git a/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt b/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt deleted file mode 100644 index 663a69c95..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-openDownloadStream.txt +++ /dev/null @@ -1,67 +0,0 @@ -============================================= -MongoDB\\GridFS\\Bucket::openDownloadStream() -============================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::openDownloadStream() - - Selects a GridFS file by its ``_id`` and opens it as a readable stream. - - .. code-block:: php - - function openDownloadStream($id): resource - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-openDownloadStream-param.rst - -Return Values -------------- - -A readable stream resource. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $uploadStream = fopen('php://temp', 'w+b'); - fwrite($uploadStream, "foobar"); - rewind($uploadStream); - - $id = $bucket->uploadFromStream('filename', $uploadStream); - - $downloadStream = $bucket->openDownloadStream($id); - - var_dump(stream_get_contents($downloadStream)); - -The output would then resemble:: - - string(6) "foobar" - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStream()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStreamByName()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStreamByName()` diff --git a/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt b/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt deleted file mode 100644 index f796fee03..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-openDownloadStreamByName.txt +++ /dev/null @@ -1,69 +0,0 @@ -=================================================== -MongoDB\\GridFS\\Bucket::openDownloadStreamByName() -=================================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::openDownloadStreamByName() - - Selects a GridFS file by its ``filename`` and opens it as a readable stream. - - .. code-block:: php - - function openDownloadStreamByName(string $filename, array $options = []): resource - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-openDownloadStreamByName-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-openDownloadStreamByName-option.rst - -Return Values -------------- - -A readable stream resource. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $bucket->uploadFromStream('filename', $stream); - - var_dump(stream_get_contents($bucket->openDownloadStreamByName('filename'))); - -The output would then resemble:: - - string(6) "foobar" - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStream()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::downloadToStreamByName()` -- :phpmethod:`MongoDB\\GridFS\\Bucket::openDownloadStream()` diff --git a/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt b/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt deleted file mode 100644 index b27420f20..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-openUploadStream.txt +++ /dev/null @@ -1,66 +0,0 @@ -=========================================== -MongoDB\\GridFS\\Bucket::openUploadStream() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::openUploadStream() - - Opens a writable stream for a new GridFS file. - - .. code-block:: php - - function openUploadStream(string $filename, array $options = []): resource - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-openUploadStream-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-openUploadStream-option.rst - -Return Values -------------- - -A writable stream resource. - -Behavior --------- - -Chunk documents will be created as data is written to the writable stream. The -metadata document will be created when the writable stream is closed. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $uploadStream = $bucket->openUploadStream('filename'); - fwrite($uploadStream, 'foobar'); - fclose($uploadStream); - - $downloadStream = $bucket->openDownloadStreamByName('filename'); - var_dump(stream_get_contents($downloadStream)); - -The output would then resemble:: - - string(6) "foobar" - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::uploadFromStream()` diff --git a/docs/reference/method/MongoDBGridFSBucket-rename.txt b/docs/reference/method/MongoDBGridFSBucket-rename.txt deleted file mode 100644 index a2fbfe32d..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-rename.txt +++ /dev/null @@ -1,55 +0,0 @@ -================================= -MongoDB\\GridFS\\Bucket::rename() -================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::rename() - - Selects a GridFS file by its ``_id`` and alters its ``filename``. - - .. code-block:: php - - function rename($id, string $newFilename): void - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-rename-param.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-gridfs-filenotfoundexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $id = $bucket->uploadFromStream('a', $stream); - - $bucket->rename($id, 'b'); - - var_dump(stream_get_contents($bucket->openDownloadStreamByName('b'))); - -The output would then resemble:: - - string(6) "foobar" diff --git a/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt b/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt deleted file mode 100644 index fd2eceee7..000000000 --- a/docs/reference/method/MongoDBGridFSBucket-uploadFromStream.txt +++ /dev/null @@ -1,73 +0,0 @@ -=========================================== -MongoDB\\GridFS\\Bucket::uploadFromStream() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::uploadFromStream() - - Creates a new GridFS file and copies the contents of a readable stream to it. - - .. code-block:: php - - function uploadFromStream(string $filename, $source, array $options = []): mixed - - This method has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-uploadFromStream-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-uploadFromStream-option.rst - -Return Values -------------- - -The ``_id`` field of the metadata document associated with the newly created -GridFS file. If the ``_id`` option is not specified, a new -:php:`MongoDB\\BSON\\ObjectId ` object will be used -by default. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst -.. include:: /includes/extracts/error-driver-runtimeexception.rst - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = fopen('php://temp', 'w+b'); - fwrite($stream, "foobar"); - rewind($stream); - - $id = $bucket->uploadFromStream('filename', $stream); - - var_dump($id); - -The output would then resemble:: - - object(MongoDB\BSON\ObjectId)#3009 (1) { - ["oid"]=> - string(24) "5acf81017e21e816e538d883" - } - -See Also --------- - -- :phpmethod:`MongoDB\\GridFS\\Bucket::openUploadStream()` diff --git a/docs/reference/method/MongoDBGridFSBucket__construct.txt b/docs/reference/method/MongoDBGridFSBucket__construct.txt deleted file mode 100644 index 5eeee5fc7..000000000 --- a/docs/reference/method/MongoDBGridFSBucket__construct.txt +++ /dev/null @@ -1,68 +0,0 @@ -====================================== -MongoDB\\GridFS\\Bucket::__construct() -====================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\GridFS\\Bucket::__construct() - - Constructs a new :phpclass:`Bucket ` instance. - - .. code-block:: php - - function __construct(MongoDB\Driver\Manager $manager, string $databaseName, array $options = []) - - This constructor has the following parameters: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-construct-param.rst - - The ``$options`` parameter supports the following options: - - .. include:: /includes/apiargs/MongoDBGridFSBucket-method-construct-option.rst - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-invalidargumentexception.rst - -Behavior --------- - -If you construct a Bucket explicitly, the Bucket inherits any options -from the :php:`MongoDB\\Driver\\Manager ` object. -If you select the Bucket from a :phpclass:`Database ` object, -the Bucket inherits its options from that object. - -Examples --------- - -.. code-block:: php - - test->selectGridFSBucket(); - - var_dump($bucket); - -The output would then resemble:: - - object(MongoDB\GridFS\Bucket)#3053 (2) { - ["bucketName"]=> - string(4) "test" - ["databaseName"]=> - string(11) "phplib_test" - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::selectGridFSBucket()` diff --git a/docs/reference/method/MongoDBInsertManyResult-getInsertedCount.txt b/docs/reference/method/MongoDBInsertManyResult-getInsertedCount.txt deleted file mode 100644 index 9297008bf..000000000 --- a/docs/reference/method/MongoDBInsertManyResult-getInsertedCount.txt +++ /dev/null @@ -1,40 +0,0 @@ -============================================= -MongoDB\\InsertManyResult::getInsertedCount() -============================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertManyResult::getInsertedCount() - - Return the number of documents that were inserted. - - .. code-block:: php - - function getInsertedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The number of documents that were inserted. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getInsertedCount() - ` diff --git a/docs/reference/method/MongoDBInsertManyResult-getInsertedIds.txt b/docs/reference/method/MongoDBInsertManyResult-getInsertedIds.txt deleted file mode 100644 index 75c638703..000000000 --- a/docs/reference/method/MongoDBInsertManyResult-getInsertedIds.txt +++ /dev/null @@ -1,36 +0,0 @@ -=========================================== -MongoDB\\InsertManyResult::getInsertedIds() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertManyResult::getInsertedIds() - - Return a map of IDs (i.e. ``_id`` field values) for the inserted documents. - - .. code-block:: php - - function getInsertedIds(): array - - Since IDs are created by the driver, this method may be called irrespective - of whether the write was acknowledged. - -Return Values -------------- - -A map of IDs (i.e. ``_id`` field values) for the inserted documents. - -The index of each ID in the map corresponds to each document's position in the -bulk operation. If a document had an ID prior to inserting (i.e. the driver did -not generate an ID), the index will contain its ``_id`` field value. Any -driver-generated ID will be a :php:`MongoDB\\BSON\\ObjectId -` instance. diff --git a/docs/reference/method/MongoDBInsertManyResult-isAcknowledged.txt b/docs/reference/method/MongoDBInsertManyResult-isAcknowledged.txt deleted file mode 100644 index db376e5fa..000000000 --- a/docs/reference/method/MongoDBInsertManyResult-isAcknowledged.txt +++ /dev/null @@ -1,34 +0,0 @@ -=========================================== -MongoDB\\InsertManyResult::isAcknowledged() -=========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertManyResult::isAcknowledged() - - Return whether the write was acknowledged. - - .. code-block:: php - - function isAcknowledged(): boolean - -Return Values -------------- - -A boolean indicating whether the write was acknowledged. - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::isAcknowledged() - ` -- :manual:`Write Concern ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBInsertOneResult-getInsertedCount.txt b/docs/reference/method/MongoDBInsertOneResult-getInsertedCount.txt deleted file mode 100644 index 355080150..000000000 --- a/docs/reference/method/MongoDBInsertOneResult-getInsertedCount.txt +++ /dev/null @@ -1,41 +0,0 @@ -============================================ -MongoDB\\InsertOneResult::getInsertedCount() -============================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertOneResult::getInsertedCount() - - Return the number of documents that were inserted. - - .. code-block:: php - - function getInsertedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The number of documents that were inserted. This should be ``1`` for an -acknowledged insert operation. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getInsertedCount() - ` diff --git a/docs/reference/method/MongoDBInsertOneResult-getInsertedId.txt b/docs/reference/method/MongoDBInsertOneResult-getInsertedId.txt deleted file mode 100644 index 8d4a01ddf..000000000 --- a/docs/reference/method/MongoDBInsertOneResult-getInsertedId.txt +++ /dev/null @@ -1,35 +0,0 @@ -========================================= -MongoDB\\InsertOneResult::getInsertedId() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertOneResult::getInsertedId() - - Return the ID (i.e. ``_id`` field value) for the inserted document. - - .. code-block:: php - - function getInsertedId(): mixed - - Since IDs are created by the driver, this method may be called irrespective - of whether the write was acknowledged. - -Return Values -------------- - -The ID (i.e. ``_id`` field value) of the inserted document. - -If the document had an ID prior to inserting (i.e. the driver did not need to -generate an ID), this will contain its ``_id`` field value. Any driver-generated -ID will be a :php:`MongoDB\\BSON\\ObjectId ` -instance. diff --git a/docs/reference/method/MongoDBInsertOneResult-isAcknowledged.txt b/docs/reference/method/MongoDBInsertOneResult-isAcknowledged.txt deleted file mode 100644 index 921e3a611..000000000 --- a/docs/reference/method/MongoDBInsertOneResult-isAcknowledged.txt +++ /dev/null @@ -1,34 +0,0 @@ -========================================== -MongoDB\\InsertOneResult::isAcknowledged() -========================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\InsertOneResult::isAcknowledged() - - Return whether the write was acknowledged. - - .. code-block:: php - - function isAcknowledged(): boolean - -Return Values -------------- - -A boolean indicating whether the write was acknowledged. - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::isAcknowledged() - ` -- :manual:`Write Concern ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBMapReduceResult-getCounts.txt b/docs/reference/method/MongoDBMapReduceResult-getCounts.txt deleted file mode 100644 index c2feab0ca..000000000 --- a/docs/reference/method/MongoDBMapReduceResult-getCounts.txt +++ /dev/null @@ -1,66 +0,0 @@ -===================================== -MongoDB\\MapReduceResult::getCounts() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\MapReduceResult::getCounts() - - Returns count statistics for the map-reduce operation. - - .. code-block:: php - - function getCounts(): array - -Return Values -------------- - -An array of count statistics for the map-reduce operation. - -Examples --------- - -This example reports the count statistics for a map-reduce operation. - -.. code-block:: php - - test->zips; - - $map = new MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'); - $reduce = new MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'); - $out = ['inline' => 1]; - - $result = $collection->mapReduce($map, $reduce, $out); - - var_dump($result->getCounts()); - -The output would then resemble:: - - array(4) { - ["input"]=> - int(29353) - ["emit"]=> - int(29353) - ["reduce"]=> - int(180) - ["output"]=> - int(51) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::mapReduce()` -- :manual:`mapReduce ` command reference in the - MongoDB manual diff --git a/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt b/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt deleted file mode 100644 index 06c5b012b..000000000 --- a/docs/reference/method/MongoDBMapReduceResult-getExecutionTimeMS.txt +++ /dev/null @@ -1,58 +0,0 @@ -============================================== -MongoDB\\MapReduceResult::getExecutionTimeMS() -============================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\MapReduceResult::getExecutionTimeMS() - - Returns the execution time in milliseconds of the map-reduce operation. - - .. code-block:: php - - function getExecutionTimeMS(): integer - -Return Values -------------- - -An integer denoting the execution time in milliseconds for the map-reduce -operation. - -Examples --------- - -This example reports the execution time for a map-reduce operation. - -.. code-block:: php - - test->zips; - - $map = new MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'); - $reduce = new MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'); - $out = ['inline' => 1]; - - $result = $collection->mapReduce($map, $reduce, $out); - - var_dump($result->getExecutionTimeMS()); - -The output would then resemble:: - - int(244) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::mapReduce()` -- :manual:`mapReduce ` command reference in the - MongoDB manual diff --git a/docs/reference/method/MongoDBMapReduceResult-getIterator.txt b/docs/reference/method/MongoDBMapReduceResult-getIterator.txt deleted file mode 100644 index b58040288..000000000 --- a/docs/reference/method/MongoDBMapReduceResult-getIterator.txt +++ /dev/null @@ -1,83 +0,0 @@ -======================================= -MongoDB\\MapReduceResult::getIterator() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\MapReduceResult::getIterator() - - Returns a php:`Traversable `, which may be used to iterate - through the results of the map-reduce operation. - - .. code-block:: php - - function getIterator(): Traversable - -Return Values -------------- - -A :php:`Traversable `, which may be used to iterate through the -results of the map-reduce operation. - -Example -------- - -This example iterates through the results of a map-reduce operation. - -.. code-block:: php - - test->zips; - - $map = new MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'); - $reduce = new MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'); - $out = ['inline' => 1]; - - $result = $collection->mapReduce($map, $reduce, $out); - - foreach ($result as $population) { - var_dump($population); - }; - -The output would then resemble:: - - object(stdClass)#2293 (2) { - ["_id"]=> - string(2) "AK" - ["value"]=> - float(544698) - } - object(stdClass)#2300 (2) { - ["_id"]=> - string(2) "AL" - ["value"]=> - float(4040587) - } - object(stdClass)#2293 (2) { - ["_id"]=> - string(2) "AR" - ["value"]=> - float(2350725) - } - object(stdClass)#2300 (2) { - ["_id"]=> - string(2) "AZ" - ["value"]=> - float(3665228) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::mapReduce()` -- :php:`IteratorAggregate::getIterator() ` diff --git a/docs/reference/method/MongoDBMapReduceResult-getTiming.txt b/docs/reference/method/MongoDBMapReduceResult-getTiming.txt deleted file mode 100644 index 75ab53939..000000000 --- a/docs/reference/method/MongoDBMapReduceResult-getTiming.txt +++ /dev/null @@ -1,74 +0,0 @@ -===================================== -MongoDB\\MapReduceResult::getTiming() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\MapReduceResult::getTiming() - - Returns timing statistics for the map-reduce operation. - - .. code-block:: php - - function getTiming(): array - - Timing statistics will only be available if the ``verbose`` option was - specified for :phpmethod:`MongoDB\\Collection::mapReduce()`. - -Return Values -------------- - -An array of timing statistics for the map-reduce operation. If no timing -statistics are available, the array will be empty. - -Examples --------- - -This example specifies the ``verbose`` option for -:phpmethod:`MongoDB\\Collection::mapReduce()` and reports the timing statistics -for a map-reduce operation. - -.. code-block:: php - - test->zips; - - $map = new MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'); - $reduce = new MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'); - $out = ['inline' => 1]; - - $result = $collection->mapReduce($map, $reduce, $out, ['verbose' => true]); - - var_dump($result->getTiming()); - -The output would then resemble:: - - array(5) { - ["mapTime"]=> - int(163) - ["emitLoop"]=> - int(233) - ["reduceTime"]=> - int(9) - ["mode"]=> - string(5) "mixed" - ["total"]=> - int(233) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::mapReduce()` -- :manual:`mapReduce ` command reference in the - MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt b/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt deleted file mode 100644 index 99a7d082e..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getCappedMax.txt +++ /dev/null @@ -1,67 +0,0 @@ -============================================== -MongoDB\\Model\\CollectionInfo::getCappedMax() -============================================== - -.. deprecated:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getCappedMax() - - Return the document limit for the capped collection. This correlates with the - ``max`` option for :phpmethod:`MongoDB\\Database::createCollection()`. - - .. code-block:: php - - function getCappedMax(): integer|null - -Return Values -------------- - -The document limit for the capped collection. If the collection is not capped, -``null`` will be returned. - -This method is deprecated in favor of using -:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the -``max`` key. - -Examples --------- - -.. code-block:: php - - 'foo', - 'options' => [ - 'capped' => true, - 'size' => 1048576, - 'max' => 100, - ] - ]); - - var_dump($info->getCappedMax()); - -The output would then resemble:: - - int(100) - -See Also --------- - -- :phpmethod:`MongoDB\\Model\\CollectionInfo::getCappedSize()` -- :phpmethod:`MongoDB\\Model\\CollectionInfo::isCapped()` -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`Capped Collections ` in the MongoDB manual -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt b/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt deleted file mode 100644 index 1d94a4042..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getCappedSize.txt +++ /dev/null @@ -1,67 +0,0 @@ -=============================================== -MongoDB\\Model\\CollectionInfo::getCappedSize() -=============================================== - -.. deprecated:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getCappedSize() - - Return the size limit for the capped collection in bytes. This correlates - with the ``size`` option for - :phpmethod:`MongoDB\\Database::createCollection()`. - - .. code-block:: php - - function getCappedSize(): integer|null - -Return Values -------------- - -The size limit for the capped collection in bytes. If the collection is not -capped, ``null`` will be returned. - -This method is deprecated in favor of using -:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the -``size`` key. - -Examples --------- - -.. code-block:: php - - 'foo', - 'options' => [ - 'capped' => true, - 'size' => 1048576, - ] - ]); - - var_dump($info->getCappedSize()); - -The output would then resemble:: - - int(1048576) - -See Also --------- - -- :phpmethod:`MongoDB\\Model\\CollectionInfo::getCappedMax()` -- :phpmethod:`MongoDB\\Model\\CollectionInfo::isCapped()` -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`Capped Collections ` in the MongoDB manual -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt b/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt deleted file mode 100644 index 4c0e4aa31..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getIdIndex.txt +++ /dev/null @@ -1,73 +0,0 @@ -============================================ -MongoDB\\Model\\CollectionInfo::getIdIndex() -============================================ - -.. versionadded:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getIdIndex() - - Returns information about the ``_id`` field index. - - .. code-block:: php - - function getIdIndex(): array - -Return Values -------------- - -An array containing information on the ``_id`` index. This corresponds to the -``idIndex`` field returned in the ``listCollections`` command reply. - -Examples --------- - -.. code-block:: php - - 'view', - 'name' => 'foo', - 'idIndex' => [ - 'v' => 2, - 'key' => ['_id' => 1], - 'name' => '_id', - 'ns' => 'test.foo', - ], - ]); - - var_dump($info->getIdIndex()); - -The output would then resemble:: - - array(4) { - ["v"]=> - int(2) - ["key"]=> - array(1) { - ["_id"]=> - int(1) - } - ["name"]=> - string(3) "_id" - ["ns"]=> - string(8) "test.foo" - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt b/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt deleted file mode 100644 index 40068e3b4..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getInfo.txt +++ /dev/null @@ -1,59 +0,0 @@ -========================================= -MongoDB\\Model\\CollectionInfo::getInfo() -========================================= - -.. versionadded:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getInfo() - - Returns additional information about the collection. - - .. code-block:: php - - function getInfo(): array - -Return Values -------------- - -An array containing extra information about the collection. This corresponds to -the ``info`` field returned in the ``listCollections`` command reply. - -Examples --------- - -.. code-block:: php - - 'view', - 'name' => 'foo', - 'info' => ['readOnly' => true] - ]); - - var_dump($info->getInfo()); - -The output would then resemble:: - - array(1) { - ["readOnly"]=> - bool(true) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getName.txt b/docs/reference/method/MongoDBModelCollectionInfo-getName.txt deleted file mode 100644 index 76af1fa9b..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getName.txt +++ /dev/null @@ -1,50 +0,0 @@ -========================================= -MongoDB\\Model\\CollectionInfo::getName() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getName() - - Return the collection name. - - .. code-block:: php - - function getName(): string - -Return Values -------------- - -The collection name. This corresponds to the ``name`` field returned in the -``listCollections`` command reply. - -Examples --------- - -.. code-block:: php - - 'foo']); - - echo $info->getName(); - -The output would then resemble:: - - foo - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::getCollectionName()` -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt b/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt deleted file mode 100644 index 4c7fabe55..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getOptions.txt +++ /dev/null @@ -1,63 +0,0 @@ -============================================ -MongoDB\\Model\\CollectionInfo::getOptions() -============================================ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getOptions() - - Return the collection options. This correlates with the options for - :phpmethod:`MongoDB\\Database::createCollection()`, but may include - additional fields set by the server. - - .. code-block:: php - - function getOptions(): array - -Return Values -------------- - -The collection options. This corresponds to the ``options`` field returned in -the ``listCollections`` command reply. - -Examples --------- - -.. code-block:: php - - 'foo', - 'options' => [ - 'capped' => true, - 'size' => 1048576, - ] - ]); - - var_dump($info->getOptions()); - -The output would then resemble:: - - array(2) { - ["capped"]=> - bool(true) - ["size"]=> - int(1048576) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-getType.txt b/docs/reference/method/MongoDBModelCollectionInfo-getType.txt deleted file mode 100644 index b900efed3..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-getType.txt +++ /dev/null @@ -1,52 +0,0 @@ -========================================= -MongoDB\\Model\\CollectionInfo::getType() -========================================= - -.. versionadded:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::getType() - - Return the collection type. - - .. code-block:: php - - function getType(): string - -Return Values -------------- - -The collection type. This corresponds to the ``type`` field returned in the -``listCollections`` command reply. - -Examples --------- - -.. code-block:: php - - 'collection', 'name' => 'foo']); - - echo $info->getType(); - -The output would then resemble:: - - collection - -See Also --------- - -- :phpmethod:`MongoDB\\Database::createCollection()` -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt b/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt deleted file mode 100644 index 15957ae80..000000000 --- a/docs/reference/method/MongoDBModelCollectionInfo-isCapped.txt +++ /dev/null @@ -1,64 +0,0 @@ -========================================== -MongoDB\\Model\\CollectionInfo::isCapped() -========================================== - -.. deprecated:: 1.9 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\CollectionInfo::isCapped() - - Return whether the collection is a :manual:`capped collection - `. - - .. code-block:: php - - function isCapped(): boolean - -Return Values -------------- - -A boolean indicating whether the collection is a capped collection. - -This method is deprecated in favor of using -:phpmethod:`MongoDB\\Model\\CollectionInfo::getOptions()` and accessing the -``capped`` key. - -Examples --------- - -.. code-block:: php - - 'foo', - 'options' => [ - 'capped' => true, - 'size' => 1048576, - ] - ]); - - var_dump($info->isCapped()); - -The output would then resemble:: - - bool(true) - -See Also --------- - -- :phpmethod:`MongoDB\\Model\\CollectionInfo::getCappedMax()` -- :phpmethod:`MongoDB\\Model\\CollectionInfo::getCappedSize()` -- :manual:`Capped Collections ` in the MongoDB manual -- :manual:`listCollections ` command - reference in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt b/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt deleted file mode 100644 index a2524a4b1..000000000 --- a/docs/reference/method/MongoDBModelDatabaseInfo-getName.txt +++ /dev/null @@ -1,49 +0,0 @@ -======================================= -MongoDB\\Model\\DatabaseInfo::getName() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\DatabaseInfo::getName() - - Return the database name. - - .. code-block:: php - - function getName(): string - -Return Values -------------- - -The database name. - -Examples --------- - -.. code-block:: php - - 'foo']); - - echo $info->getName(); - -The output would then resemble:: - - foo - -See Also --------- - -- :phpmethod:`MongoDB\\Database::getDatabaseName()` -- :manual:`listDatabases ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt b/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt deleted file mode 100644 index d42f23ff5..000000000 --- a/docs/reference/method/MongoDBModelDatabaseInfo-getSizeOnDisk.txt +++ /dev/null @@ -1,48 +0,0 @@ -============================================= -MongoDB\\Model\\DatabaseInfo::getSizeOnDisk() -============================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\DatabaseInfo::getSizeOnDisk() - - Return the total size of the database file on disk in bytes. - - .. code-block:: php - - function getSizeOnDisk(): integer - -Return Values -------------- - -The total size of the database file on disk in bytes. - -Examples --------- - -.. code-block:: php - - 1048576]); - - var_dump($info->getSizeOnDisk()); - -The output would then resemble:: - - int(1048576) - -See Also --------- - -- :manual:`listDatabases ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt b/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt deleted file mode 100644 index ae25e67f2..000000000 --- a/docs/reference/method/MongoDBModelDatabaseInfo-isEmpty.txt +++ /dev/null @@ -1,48 +0,0 @@ -======================================= -MongoDB\\Model\\DatabaseInfo::isEmpty() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\DatabaseInfo::isEmpty() - - Return whether the database has any data. - - .. code-block:: php - - function isEmpty(): boolean - -Return Values -------------- - -A boolean indicating whether the database has any data. - -Examples --------- - -.. code-block:: php - - true]); - - var_dump($info->isEmpty()); - -The output would then resemble:: - - bool(true) - -See Also --------- - -- :manual:`listDatabases ` command reference - in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-getKey.txt b/docs/reference/method/MongoDBModelIndexInfo-getKey.txt deleted file mode 100644 index 1a115f9cc..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-getKey.txt +++ /dev/null @@ -1,56 +0,0 @@ -=================================== -MongoDB\\Model\\IndexInfo::getKey() -=================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::getKey() - - Return the index specification (i.e. indexed field(s) and order). This - correlates with the ``$key`` parameter for - :phpmethod:`MongoDB\\Collection::createIndex()`. - - .. code-block:: php - - function getKey(): array - -Return Values -------------- - -The index specification as an associative array. - -Examples --------- - -.. code-block:: php - - ['x' => 1], - ]); - - var_dump($info->getKey()); - -The output would then resemble:: - - array(1) { - ["x"]=> - int(1) - } - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-getName.txt b/docs/reference/method/MongoDBModelIndexInfo-getName.txt deleted file mode 100644 index 97ad45fae..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-getName.txt +++ /dev/null @@ -1,54 +0,0 @@ -==================================== -MongoDB\\Model\\IndexInfo::getName() -==================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::getName() - - Return the index name. This correlates with the return value of - :phpmethod:`MongoDB\\Collection::createIndex()`. An index name may be derived - from the ``$key`` parameter or or explicitly specified via the ``name`` - option. - - .. code-block:: php - - function getName(): string - -Return Values -------------- - -The index name. - -Examples --------- - -.. code-block:: php - - 'x_1', - ]); - - echo $info->getName(); - -The output would then resemble:: - - x_1 - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt b/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt deleted file mode 100644 index f4a64bb66..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-getNamespace.txt +++ /dev/null @@ -1,53 +0,0 @@ -========================================= -MongoDB\\Model\\IndexInfo::getNamespace() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::getNamespace() - - Return the index namespace, which is the namespace of the collection - containing the index. - - .. code-block:: php - - function getNamespace(): string - -Return Values -------------- - -The index namespace. - -Examples --------- - -.. code-block:: php - - 'foo.bar', - ]); - - echo $info->getNamespace(); - -The output would then resemble:: - - foo.bar - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :phpmethod:`MongoDB\\Collection::getNamespace()` -- :manual:`listIndexes ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt b/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt deleted file mode 100644 index 728f38858..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-getVersion.txt +++ /dev/null @@ -1,51 +0,0 @@ -======================================= -MongoDB\\Model\\IndexInfo::getVersion() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::getVersion() - - Return the index version. - - .. code-block:: php - - function getVersion(): integer - -Return Values -------------- - -The index version. - -Examples --------- - -.. code-block:: php - - 1, - ]); - - var_dump($info->getVersion()); - -The output would then resemble:: - - int(1) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt b/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt deleted file mode 100644 index a6bb398be..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-is2dSphere.txt +++ /dev/null @@ -1,59 +0,0 @@ -======================================= -MongoDB\\Model\\IndexInfo::is2dSphere() -======================================= - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::is2dSphere() - - Return whether the index is a :manual:`2dsphere ` - index. - - .. code-block:: php - - function is2dSphere(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a 2dsphere index. - -Examples --------- - -.. code-block:: php - - selectCollection('test', 'places'); - - $collection->createIndex(['pos' => '2dsphere']); - - foreach ($collection->listIndexes() as $index) { - if ($index->is2dSphere()) { - printf("%s has 2dsphereIndexVersion: %d\n", $index->getName(), $index['2dsphereIndexVersion']); - } - } - -The output would then resemble:: - - pos_2dsphere has 2dsphereIndexVersion: 3 - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :phpmethod:`MongoDB\\Collection::listIndexes()` -- :manual:`2dsphere Indexes ` reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt b/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt deleted file mode 100644 index a2d9a028b..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-isGeoHaystack.txt +++ /dev/null @@ -1,59 +0,0 @@ -========================================== -MongoDB\\Model\\IndexInfo::isGeoHaystack() -========================================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::isGeoHaystack() - - Return whether the index is a :manual:`geoHaystack - ` index. - - .. code-block:: php - - function isGeoHaystack(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a geoHaystack index. - -Examples --------- - -.. code-block:: php - - selectCollection('test', 'places'); - - $collection->createIndex(['pos' => 'geoHaystack', 'x' => 1], ['bucketSize' => 5]); - - foreach ($collection->listIndexes() as $index) { - if ($index->isGeoHaystack()) { - printf("%s has bucketSize: %d\n", $index->getName(), $index['bucketSize']); - } - } - -The output would then resemble:: - - pos_geoHaystack_x_1 has bucketSize: 5 - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :phpmethod:`MongoDB\\Collection::listIndexes()` -- :manual:`geoHaystack Indexes ` reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt b/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt deleted file mode 100644 index 5bbe4dea4..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-isSparse.txt +++ /dev/null @@ -1,54 +0,0 @@ -===================================== -MongoDB\\Model\\IndexInfo::isSparse() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::isSparse() - - Return whether the index is a :manual:`sparse index `. - This correlates with the ``sparse`` option for - :phpmethod:`MongoDB\\Collection::createIndex()`. - - .. code-block:: php - - function isSparse(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a sparse index. - -Examples --------- - -.. code-block:: php - - true, - ]); - - var_dump($info->isSparse()); - -The output would then resemble:: - - bool(true) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual -- :manual:`Sparse Indexes ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-isText.txt b/docs/reference/method/MongoDBModelIndexInfo-isText.txt deleted file mode 100644 index 083335968..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-isText.txt +++ /dev/null @@ -1,58 +0,0 @@ -=================================== -MongoDB\\Model\\IndexInfo::isText() -=================================== - -.. versionadded:: 1.4 - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::isText() - - Return whether the index is a :manual:`text ` index. - - .. code-block:: php - - function isText(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a text index. - -Examples --------- - -.. code-block:: php - - selectCollection('test', 'restaurants'); - - $collection->createIndex(['name' => 'text']); - - foreach ($collection->listIndexes() as $index) { - if ($index->isText()) { - printf("%s has default language: %d\n", $index->getName(), $index['default_language']); - } - } - -The output would then resemble:: - - name_text has default language: english - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :phpmethod:`MongoDB\\Collection::listIndexes()` -- :manual:`Text Indexes ` reference in the MongoDB - manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt b/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt deleted file mode 100644 index a3e70d036..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-isTtl.txt +++ /dev/null @@ -1,54 +0,0 @@ -================================== -MongoDB\\Model\\IndexInfo::isTtl() -================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::isTtl() - - Return whether the index is a :manual:`TTL index `. This - correlates with the ``expireAfterSeconds`` option for - :phpmethod:`MongoDB\\Collection::createIndex()`. - - .. code-block:: php - - function isTtl(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a TTL index. - -Examples --------- - -.. code-block:: php - - 100, - ]); - - var_dump($info->isTtl()); - -The output would then resemble:: - - bool(true) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual -- :manual:`TTL Indexes ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt b/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt deleted file mode 100644 index 30cf1ece5..000000000 --- a/docs/reference/method/MongoDBModelIndexInfo-isUnique.txt +++ /dev/null @@ -1,54 +0,0 @@ -===================================== -MongoDB\\Model\\IndexInfo::isUnique() -===================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\Model\\IndexInfo::isUnique() - - Return whether the index is a :manual:`unique index `. - This correlates with the ``unique`` option for - :phpmethod:`MongoDB\\Collection::createIndex()`. - - .. code-block:: php - - function isUnique(): boolean - -Return Values -------------- - -A boolean indicating whether the index is a unique index. - -Examples --------- - -.. code-block:: php - - true, - ]); - - var_dump($info->isUnique()); - -The output would then resemble:: - - bool(true) - -See Also --------- - -- :phpmethod:`MongoDB\\Collection::createIndex()` -- :manual:`listIndexes ` command reference in - the MongoDB manual -- :manual:`Unique Indexes ` in the MongoDB manual diff --git a/docs/reference/method/MongoDBUpdateResult-getMatchedCount.txt b/docs/reference/method/MongoDBUpdateResult-getMatchedCount.txt deleted file mode 100644 index f347a8cf0..000000000 --- a/docs/reference/method/MongoDBUpdateResult-getMatchedCount.txt +++ /dev/null @@ -1,49 +0,0 @@ -======================================== -MongoDB\\UpdateResult::getMatchedCount() -======================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\UpdateResult::getMatchedCount() - - Return the number of documents that were matched. - - .. code-block:: php - - function getMatchedCount(): integer - - This method should only be called if the write was acknowledged. - - .. note:: - - If an update/replace operation results in no change to the document - (e.g. setting the value of a field to its current value), the matched - count may be greater than the value returned by - :phpmethod:`getModifiedCount() - `. - -Return Values -------------- - -The number of documents that were matched. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :phpmethod:`MongoDB\\UpdateResult::getModifiedCount()` -- :php:`MongoDB\\Driver\\WriteResult::getMatchedCount() - ` diff --git a/docs/reference/method/MongoDBUpdateResult-getModifiedCount.txt b/docs/reference/method/MongoDBUpdateResult-getModifiedCount.txt deleted file mode 100644 index 3e2a7a6ed..000000000 --- a/docs/reference/method/MongoDBUpdateResult-getModifiedCount.txt +++ /dev/null @@ -1,48 +0,0 @@ -========================================= -MongoDB\\UpdateResult::getModifiedCount() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\UpdateResult::getModifiedCount() - - Return the number of documents that were modified. - - .. code-block:: php - - function getModifiedCount(): integer|null - - This method should only be called if the write was acknowledged. - - .. note:: - - If an update/replace operation results in no change to the document - (e.g. setting the value of a field to its current value), the modified - count may be less than the value returned by :phpmethod:`getMatchedCount() - `. - -Return Values -------------- - -The number of documents that were modified. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :phpmethod:`MongoDB\\UpdateResult::getMatchedCount()` -- :php:`MongoDB\\Driver\\WriteResult::getModifiedCount() - ` diff --git a/docs/reference/method/MongoDBUpdateResult-getUpsertedCount.txt b/docs/reference/method/MongoDBUpdateResult-getUpsertedCount.txt deleted file mode 100644 index 0bbb39fbc..000000000 --- a/docs/reference/method/MongoDBUpdateResult-getUpsertedCount.txt +++ /dev/null @@ -1,42 +0,0 @@ -========================================= -MongoDB\\UpdateResult::getUpsertedCount() -========================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\UpdateResult::getUpsertedCount() - - Return the number of documents that were upserted. - - .. code-block:: php - - function getUpsertedCount(): integer - - This method should only be called if the write was acknowledged. - -Return Values -------------- - -The total number of documents that were upserted. This should be either ``0`` or -``1`` for an acknowledged update or replace operation, depending on whether an -upsert occurred. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getUpsertedCount() - ` diff --git a/docs/reference/method/MongoDBUpdateResult-getUpsertedId.txt b/docs/reference/method/MongoDBUpdateResult-getUpsertedId.txt deleted file mode 100644 index 52498708d..000000000 --- a/docs/reference/method/MongoDBUpdateResult-getUpsertedId.txt +++ /dev/null @@ -1,44 +0,0 @@ -====================================== -MongoDB\\UpdateResult::getUpsertedId() -====================================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\UpdateResult::getUpsertedId() - - Return the ID (i.e. ``_id`` field value) of the upserted document. - - .. code-block:: php - - function getUpsertedId(): mixed|null - -Return Values -------------- - -The ID (i.e. ``_id`` field value) of the upserted document. If no document was -upserted, ``null`` will be returned. - -If the document had an ID prior to upserting (i.e. the server did not need to -generate an ID), this will contain its ``_id`` field value. Any server-generated -ID will be a :php:`MongoDB\\BSON\\ObjectId ` -instance. - -Errors/Exceptions ------------------ - -.. include:: /includes/extracts/error-badmethodcallexception-write-result.rst - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::getUpsertedIds() - ` diff --git a/docs/reference/method/MongoDBUpdateResult-isAcknowledged.txt b/docs/reference/method/MongoDBUpdateResult-isAcknowledged.txt deleted file mode 100644 index af15d1f11..000000000 --- a/docs/reference/method/MongoDBUpdateResult-isAcknowledged.txt +++ /dev/null @@ -1,34 +0,0 @@ -======================================= -MongoDB\\UpdateResult::isAcknowledged() -======================================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Definition ----------- - -.. phpmethod:: MongoDB\\UpdateResult::isAcknowledged() - - Return whether the write was acknowledged. - - .. code-block:: php - - function isAcknowledged(): boolean - -Return Values -------------- - -A boolean indicating whether the write was acknowledged. - -See Also --------- - -- :php:`MongoDB\\Driver\\WriteResult::isAcknowledged() - ` -- :manual:`Write Concern ` in the MongoDB manual diff --git a/docs/reference/result-classes.txt b/docs/reference/result-classes.txt deleted file mode 100644 index 3371c137e..000000000 --- a/docs/reference/result-classes.txt +++ /dev/null @@ -1,76 +0,0 @@ -============== -Result Classes -============== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB\\ChangeStream ---------------------- - -.. versionadded:: 1.3 - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\ChangeStream - - This class extends PHP's :php:`Iterator ` - interface. An instance of this class is returned by - :phpmethod:`MongoDB\\Client::watch()`, - :phpmethod:`MongoDB\\Database::watch()`, and - :phpmethod:`MongoDB\\Collection::watch()`. - - This class allows for iteration of events in a change stream. It also allows - iteration to automatically resume after certain errors, such as a replica set - failover. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBChangeStream-current - /reference/method/MongoDBChangeStream-getCursorId - /reference/method/MongoDBChangeStream-getResumeToken - /reference/method/MongoDBChangeStream-key - /reference/method/MongoDBChangeStream-next - /reference/method/MongoDBChangeStream-rewind - /reference/method/MongoDBChangeStream-valid - ----- - -MongoDB\\MapReduceResult ------------------------- - -.. versionadded:: 1.2 - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\MapReduceResult - - This class extends PHP's :php:`IteratorAggregate ` - interface. An instance of this class is returned by - :phpmethod:`MongoDB\\Collection::mapReduce()`. - - This class allows for iteration of map-reduce results irrespective of the - output method (e.g. inline, collection). It also provides access to command - statistics. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBMapReduceResult-getCounts - /reference/method/MongoDBMapReduceResult-getExecutionTimeMS - /reference/method/MongoDBMapReduceResult-getIterator - /reference/method/MongoDBMapReduceResult-getTiming diff --git a/docs/reference/write-result-classes.txt b/docs/reference/write-result-classes.txt deleted file mode 100644 index f410c0834..000000000 --- a/docs/reference/write-result-classes.txt +++ /dev/null @@ -1,143 +0,0 @@ -==================== -Write Result Classes -==================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -MongoDB\\BulkWriteResult ------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\BulkWriteResult - - This class contains information about an executed bulk write operation. It - encapsulates a :php:`MongoDB\\Driver\\WriteResult - ` object and is returned from - :phpmethod:`MongoDB\\Collection::bulkWrite()`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBBulkWriteResult-getDeletedCount - /reference/method/MongoDBBulkWriteResult-getInsertedCount - /reference/method/MongoDBBulkWriteResult-getInsertedIds - /reference/method/MongoDBBulkWriteResult-getMatchedCount - /reference/method/MongoDBBulkWriteResult-getModifiedCount - /reference/method/MongoDBBulkWriteResult-getUpsertedCount - /reference/method/MongoDBBulkWriteResult-getUpsertedIds - /reference/method/MongoDBBulkWriteResult-isAcknowledged - ----- - -MongoDB\\DeleteResult ---------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\DeleteResult - - This class contains information about an executed delete operation. It - encapsulates a :php:`MongoDB\\Driver\\WriteResult - ` object and is returned from - :phpmethod:`MongoDB\\Collection::deleteMany()` or - :phpmethod:`MongoDB\\Collection::deleteOne()`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBDeleteResult-getDeletedCount - /reference/method/MongoDBDeleteResult-isAcknowledged - ----- - -MongoDB\\InsertManyResult -------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\InsertManyResult - - This class contains information about an executed bulk insert operation. It - encapsulates a :php:`MongoDB\\Driver\\WriteResult - ` object and is returned from - :phpmethod:`MongoDB\\Collection::insertMany()`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBInsertManyResult-getInsertedCount - /reference/method/MongoDBInsertManyResult-getInsertedIds - /reference/method/MongoDBInsertManyResult-isAcknowledged - ----- - -MongoDB\\InsertOneResult ------------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\InsertOneResult - - This class contains information about an executed insert operation. It - encapsulates a :php:`MongoDB\\Driver\\WriteResult - ` object and is returned from - :phpmethod:`MongoDB\\Collection::insertOne()`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBInsertOneResult-getInsertedCount - /reference/method/MongoDBInsertOneResult-getInsertedId - /reference/method/MongoDBInsertOneResult-isAcknowledged - ----- - -MongoDB\\UpdateResult ---------------------- - -Definition -~~~~~~~~~~ - -.. phpclass:: MongoDB\\UpdateResult - - This class contains information about an executed update or replace - operation. It encapsulates a :php:`MongoDB\\Driver\\WriteResult - ` object and is returned from - :phpmethod:`MongoDB\\Collection::replaceOne()`, - :phpmethod:`MongoDB\\Collection::updateMany()`, or - :phpmethod:`MongoDB\\Collection::updateOne()`. - -Methods -~~~~~~~ - -.. toctree:: - :titlesonly: - - /reference/method/MongoDBUpdateResult-getMatchedCount - /reference/method/MongoDBUpdateResult-getModifiedCount - /reference/method/MongoDBUpdateResult-getUpsertedCount - /reference/method/MongoDBUpdateResult-getUpsertedId - /reference/method/MongoDBUpdateResult-isAcknowledged diff --git a/docs/tutorial.txt b/docs/tutorial.txt deleted file mode 100644 index 91333fb45..000000000 --- a/docs/tutorial.txt +++ /dev/null @@ -1,21 +0,0 @@ -Tutorials -========= - -.. default-domain:: mongodb - -.. toctree:: - - /tutorial/connecting - /tutorial/server-selection - /tutorial/crud - /tutorial/collation - /tutorial/commands - /tutorial/custom-types - /tutorial/decimal128 - /tutorial/client-side-encryption - /tutorial/gridfs - /tutorial/indexes - /tutorial/tailable-cursor - /tutorial/example-data - /tutorial/modeling-bson-data - /tutorial/stable-api diff --git a/docs/tutorial/client-side-encryption.txt b/docs/tutorial/client-side-encryption.txt deleted file mode 100644 index bbb8196db..000000000 --- a/docs/tutorial/client-side-encryption.txt +++ /dev/null @@ -1,372 +0,0 @@ -====================== -Client-Side Encryption -====================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Client-Side Field Level Encryption allows administrators and developers to -encrypt specific data fields in addition to other MongoDB encryption features. - - -Creating an Encryption Key --------------------------- - -.. note:: - - The following examples use a local master key; however, other key providers - such as AWS KMS are also an option. This master key is used to encrypt data - keys that are stored locally. It is important that you keep this key secure. - -To create an encryption key, create a :php:`MongoDB\\Driver\\ClientEncryption ` -instance with encryption options and create a new data key. The method will -return the key ID which can be used to reference the key later. You can also -pass multiple alternate names for this key and reference the key by these names -instead of the key ID. Creating a new data encryption key would typically be -done on initial deployment, but depending on your use case you may want to use -more than one encryption key or create them dynamically. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - - $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - ]; - - $client = new Client(); - $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - - // Create an encryption key with an alternate name - // To store the key ID for later use, you can use serialize or var_export - $keyId = $clientEncryption->createDataKey('local', ['keyAltNames' => ['my-encryption-key']]); - -.. seealso:: :manual:`Encryption Key Management ` in the MongoDB manual - - -Automatic Encryption and Decryption ------------------------------------ - -.. note:: - - Auto encryption is an enterprise only feature. - -The following example sets up a collection with automatic encryption based on a -``$jsonSchema`` validator. The data in the ``encryptedField`` field is -automatically encrypted on insertion and decrypted when reading on the client -side. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - $encryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - ]; - - $client = new Client(); - - $database = $client->selectDatabase('test'); - $database->dropCollection('coll'); // remove old data - - // This uses the key ID from the first example. The key ID could be read from - // a configuration file. - $keyId = readDataKey(); - - $database->createCollection('coll', [ - 'validator' => [ - '$jsonSchema' => [ - 'bsonType' => 'object', - 'properties' => [ - 'encryptedField' => [ - 'encrypt' => [ - 'keyId' => [$keyId], - 'bsonType' => 'string', - 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, - ], - ], - ], - ], - ], - ]); - - $encryptedClient = new Client('mongodb://127.0.0.1', [], ['autoEncryption' => $encryptionOpts]); - - $collection = $encryptedClient->selectCollection('test', 'coll'); - - $collection->insertOne(['encryptedField' => '123456789']); - - var_dump($collection->findOne([])); - - -Specifying an Explicit Schema for Encryption --------------------------------------------- - -The following example uses the ``schemaMap`` encryption option to define -encrypted fields. - -.. note:: - - Supplying a ``schemaMap`` provides more security than relying on JSON schemas - obtained from the server. It protects against a malicious server advertising - a false JSON schema, which could trick the client into sending unencrypted - data that should be encrypted. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - - $client = new Client(); - - // This uses the key ID from the first example. The key ID could be read from - // a configuration file. - $keyId = readDataKey(); - - $autoEncryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - 'schemaMap' => [ - 'test.coll' => [ - 'bsonType' => 'object', - 'properties' => [ - 'encryptedField' => [ - 'encrypt' => [ - 'keyId' => [$keyId], - 'bsonType' => 'string', - 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, - ], - ], - ], - ], - ], - ]; - - $encryptedClient = new Client(null, [], ['autoEncryption' => $autoEncryptionOpts]); - - $collection = $encryptedClient->selectCollection('test', 'coll'); - $collection->drop(); // clear old data - - $collection->insertOne(['encryptedField' => '123456789']); - - var_dump($collection->findOne([])); - - -Manually Encrypting and Decrypting Values ------------------------------------------ - -In the MongoDB Community Edition, you will have to manually encrypt values -before storing them in the database. The following example assumes that you have -already created an encryption key in the key vault collection and explicitly -encrypts and decrypts values in the document. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - - $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - ]; - - $client = new Client(); - $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - - // This uses the key ID from the first example. The key ID could be read from - // a configuration file. - $keyId = readDataKey(); - - $collection = $client->selectCollection('test', 'coll'); - $collection->drop(); // clear old data - - $encryptionOpts = [ - 'keyId' => $keyId, - 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, - ]; - $encryptedValue = $clientEncryption->encrypt('123456789', $encryptionOpts); - - $collection->insertOne(['encryptedField' => $encryptedValue]); - - $document = $collection->findOne(); - var_dump($clientEncryption->decrypt($document->encryptedField)); - - -Referencing Encryption Keys by an Alternative Name --------------------------------------------------- - -While it is possible to create an encryption key every time data is encrypted, -this is not the recommended approach. Instead, you should create your encryption -keys depending on your use case, e.g. by creating a user-specific encryption -key. To reference keys in your software, you can use the keyAltName attribute -specified when creating the key. The following example creates an encryption key -with an alternative name, which could be done when deploying the application. -The software then encrypts data by referencing the key by its alternative name. - -To use an alternate name when referencing an encryption key, use the -``keyAltName`` option instead of ``keyId``. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - - $clientEncryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => [ - 'local' => ['key' => $localKey], - ], - ]; - - $client = new Client(); - $clientEncryption = $client->createClientEncryption($clientEncryptionOpts); - - $collection = $client->selectCollection('test', 'coll'); - $collection->drop(); // clear old data - - // Reference the encryption key created in the first example by its - // alternative name - $encryptionOpts = [ - 'keyAltName' => 'my-encryption-key', - 'algorithm' => ClientEncryption::AEAD_AES_256_CBC_HMAC_SHA_512_DETERMINISTIC, - ]; - $encryptedValue = $clientEncryption->encrypt('123456789', $encryptionOpts); - - $collection->insertOne(['encryptedField' => $encryptedValue]); - - $document = $collection->findOne(); - var_dump($clientEncryption->decrypt($document->encryptedField)); - - -Automatic Queryable Encryption ------------------------------- - -.. note:: - - Automatic queryable encryption is an enterprise only feature and requires - MongoDB 6.0+. Queryable Encryption is in public preview and available for - evaluation purposes. It is not yet recommended for production deployments as - breaking changes may be introduced. See the - `Queryable Encryption Preview `_ - blog post for more information. - -The following example uses a local key; however, other key providers such as AWS -are also an option. The data in the ``encryptedIndexed`` and -``encryptedUnindexed`` fields will be automatically encrypted on insertion and -decrypted when querying on the client side. Additionally, it is possible to -query on the ``encryptedIndexed`` field. - -.. code-block:: php - - ', Binary::TYPE_GENERIC); - - $encryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => ['local' => ['key' => $localKey]], - ]; - - $client = new Client(); - $clientEncryption = $client->createClientEncryption($encryptionOpts); - - // Create two data keys, one for each encrypted field - $dataKeyId1 = $clientEncryption->createDataKey('local'); - $dataKeyId2 = $clientEncryption->createDataKey('local'); - - $autoEncryptionOpts = [ - 'keyVaultNamespace' => 'encryption.__keyVault', - 'kmsProviders' => ['local' => ['key' => $localKey]], - 'encryptedFieldsMap' => [ - 'test.coll' => [ - 'fields' => [ - [ - 'path' => 'encryptedIndexed', - 'bsonType' => 'string', - 'keyId' => $dataKeyId1, - 'queries' => ['queryType' => 'equality'], - ], - [ - 'path' => 'encryptedUnindexed', - 'bsonType' => 'string', - 'keyId' => $dataKeyId2, - ], - ], - ], - ], - ]; - - $encryptedClient = new Client(null, [], ['autoEncryption' => $autoEncryptionOpts]); - - /* Drop and create the collection under test. The createCollection() helper - * will reference the client's encryptedFieldsMap and create additional, - * internal collections automatically. */ - $encryptedClient->selectDatabase('test')->dropCollection('coll'); - $encryptedClient->selectDatabase('test')->createCollection('coll'); - $encryptedCollection = $encryptedClient->selectCollection('test', 'coll'); - - /* Using a client with auto encryption, insert a document with encrypted - * fields and assert that those fields are automatically decrypted when - * querying. The encryptedIndexed and encryptedUnindexed fields should both - * be strings. */ - $indexedValue = 'indexedValue'; - $unindexedValue = 'unindexedValue'; - - $encryptedCollection->insertOne([ - '_id' => 1, - 'encryptedIndexed' => $indexedValue, - 'encryptedUnindexed' => $unindexedValue, - ]); - - var_dump($encryptedCollection->findOne(['encryptedIndexed' => $indexedValue])); - - /* Using a client without auto encryption, query for the same document and - * assert that encrypted data is returned. The encryptedIndexed and - * encryptedUnindexed fields should both be Binary objects. */ - $unencryptedCollection = $client->selectCollection('test', 'coll'); - - var_dump($unencryptedCollection->findOne(['_id' => 1])); diff --git a/docs/tutorial/collation.txt b/docs/tutorial/collation.txt deleted file mode 100644 index 76df18fe7..000000000 --- a/docs/tutorial/collation.txt +++ /dev/null @@ -1,373 +0,0 @@ -========= -Collation -========= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -.. versionadded:: 1.1 - -Overview --------- - -MongoDB 3.4 introduced support for :manual:`collations -`, which provide a set of rules to comply with the -conventions of a particular language when comparing strings. - -For example, in Canadian French, the last accent in a given word determines the -sorting order. Consider the following French words: - -.. code-block:: none - - cote < coté < côte < côté - -The sort order using the Canadian French collation would result in the -following: - -.. code-block:: none - - cote < côte < coté < côté - -If collation is unspecified, MongoDB uses simple binary comparison for strings. -As such, the sort order of the words would be: - -.. code-block:: none - - cote < coté < côte < côté - -Usage ------ - -You can specify a default collation for collections and indexes when they are -created, or specify a collation for CRUD operations and aggregations. For -operations that support collation, MongoDB uses the collection's default -collation unless the operation specifies a different collation. - -Collation Parameters -~~~~~~~~~~~~~~~~~~~~ - -.. code-block:: php - - 'collation' => [ - 'locale' => , - 'caseLevel' => , - 'caseFirst' => , - 'strength' => , - 'numericOrdering' => , - 'alternate' => , - 'maxVariable' => , - 'normalization' => , - 'backwards' => , - ] - -The only required parameter is ``locale``, which the server parses as an `ICU -format locale ID `_. For example, set -``locale`` to ``en_US`` to represent US English or ``fr_CA`` to represent -Canadian French. - -For a complete description of the available parameters, see :manual:`Collation -Document ` in the MongoDB manual. - -Assign a Default Collation to a Collection -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The following example creates a new collection called ``contacts`` on the -``test`` database and assigns a default collation with the ``fr_CA`` locale. -Specifying a collation when you create the collection ensures that all -operations involving a query that are run against the ``contacts`` collection -use the ``fr_CA`` collation, unless the query specifies another collation. Any -indexes on the new collection also inherit the default collation, unless the -creation command specifies another collation. - -.. code-block:: php - - test; - - $database->createCollection('contacts', [ - 'collation' => ['locale' => 'fr_CA'], - ]); - -Assign a Collation to an Index -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -To specify a collation for an index, use the ``collation`` option when you -create the index. - -The following example creates an index on the ``name`` field of the -``address_book`` collection, with the ``unique`` parameter enabled and a default -collation with ``locale`` set to ``en_US``. - -.. code-block:: php - - test->address_book; - - $collection->createIndex( - ['first_name' => 1], - [ - 'collation' => ['locale' => 'en_US'], - 'unique' => true, - ] - ); - -To use this index, make sure your queries also specify the same collation. The -following query uses the above index: - -.. code-block:: php - - test->address_book; - - $cursor = $collection->find( - ['first_name' => 'Adam'], - [ - 'collation' => ['locale' => 'en_US'], - ] - ); - -The following queries do **NOT** use the index. The first query uses no -collation, and the second uses a collation with a different ``strength`` value -than the collation on the index. - -.. code-block:: php - - test->address_book; - - $cursor1 = $collection->find(['first_name' => 'Adam']); - - $cursor2 = $collection->find( - ['first_name' => 'Adam'], - [ - 'collation' => [ - 'locale' => 'en_US', - 'strength' => 2, - ], - ] - ); - -Operations that Support Collation ---------------------------------- - -All reading, updating, and deleting methods support collation. Some examples are -listed below. - -``find()`` with ``sort`` -~~~~~~~~~~~~~~~~~~~~~~~~ - -Individual queries can specify a collation to use when matching and sorting -results. The following query and sort operation uses a German collation with the -``locale`` parameter set to ``de``. - -.. code-block:: php - - test->contacts; - - $cursor = $collection->find( - ['city' => 'New York'], - [ - 'collation' => ['locale' => 'de'], - 'sort' => ['name' => 1], - ] - ); - -``findOneAndUpdate()`` -~~~~~~~~~~~~~~~~~~~~~~ - -A collection called ``names`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "first_name" : "Hans" } - { "_id" : 2, "first_name" : "Gunter" } - { "_id" : 3, "first_name" : "Günter" } - { "_id" : 4, "first_name" : "Jürgen" } - -The following :phpmethod:`findOneAndUpdate() -` operation on the collection does not -specify a collation. - -.. code-block:: php - - test->names; - - $document = $collection->findOneAndUpdate( - ['first_name' => ['$lt' => 'Gunter']], - ['$set' => ['verified' => true]] - ); - -Because ``Gunter`` is lexically first in the collection, the above operation -returns no results and updates no documents. - -Consider the same :phpmethod:`findOneAndUpdate() -` operation but with a collation -specified, which uses the locale ``de@collation=phonebook``. - -.. note:: - - Some locales have a ``collation=phonebook`` option available for use with - languages which sort proper nouns differently from other words. According to - the ``de@collation=phonebook`` collation, characters with umlauts come before - the same characters without umlauts. - -.. code-block:: php - - test->names; - - $document = $collection->findOneAndUpdate( - ['first_name' => ['$lt' => 'Gunter']], - ['$set' => ['verified' => true]], - [ - 'collation' => ['locale' => 'de@collation=phonebook'], - 'returnDocument' => MongoDB\Operation\FindOneAndUpdate::RETURN_DOCUMENT_AFTER, - ] - ); - -The operation returns the following updated document: - -.. code-block:: javascript - - { "_id" => 3, "first_name" => "Günter", "verified" => true } - -``findOneAndDelete()`` -~~~~~~~~~~~~~~~~~~~~~~ - -Set the ``numericOrdering`` collation parameter to ``true`` to compare numeric -strings by their numeric values. - -The collection ``numbers`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "a" : "16" } - { "_id" : 2, "a" : "84" } - { "_id" : 3, "a" : "179" } - -The following example matches the first document in which field ``a`` has a -numeric value greater than 100 and deletes it. - -.. code-block:: php - - test->numbers; - - $document = $collection->findOneAndDelete( - ['a' => ['$gt' =-> '100']], - [ - 'collation' => [ - 'locale' => 'en', - 'numericOrdering' => true, - ], - ] - ); - -After the above operation, the following documents remain in the collection: - -.. code-block:: javascript - - { "_id" : 1, "a" : "16" } - { "_id" : 2, "a" : "84" } - -If you perform the same operation without collation, the server deletes the -first document it finds in which the lexical value of ``a`` is greater than -``"100"``. - -.. code-block:: php - - test->numbers; - - $document = $collection->findOneAndDelete(['a' => ['$gt' =-> '100']]); - -After the above operation is executed, the document in which ``a`` was equal to -``"16"`` has been deleted, and the following documents remain in the collection: - -.. code-block:: javascript - - { "_id" : 2, "a" : "84" } - { "_id" : 3, "a" : "179" } - -``deleteMany()`` -~~~~~~~~~~~~~~~~ - -You can use collations with all the various CRUD operations which exist in the -|php-library|. - -The collection ``recipes`` contains the following documents: - -.. code-block:: javascript - - { "_id" : 1, "dish" : "veggie empanadas", "cuisine" : "Spanish" } - { "_id" : 2, "dish" : "beef bourgignon", "cuisine" : "French" } - { "_id" : 3, "dish" : "chicken molé", "cuisine" : "Mexican" } - { "_id" : 4, "dish" : "chicken paillard", "cuisine" : "french" } - { "_id" : 5, "dish" : "pozole verde", "cuisine" : "Mexican" } - -Setting the ``strength`` parameter of the collation document to ``1`` or ``2`` -causes the server to disregard case in the query filter. The following example -uses a case-insensitive query filter to delete all records in which the -``cuisine`` field matches ``French``. - -.. code-block:: php - - test->recipes; - - $collection->deleteMany( - ['cuisine' => 'French'], - [ - 'collation' => [ - 'locale' => 'en_US', - 'strength' => 1, - ], - ] - ); - -After the above operation runs, the documents with ``_id`` values of ``2`` and -``4`` are deleted from the collection. - -Aggregation -~~~~~~~~~~~ - -To use collation with an :phpmethod:`aggregate() -` operation, specify a collation in the -aggregation options. - -The following aggregation example uses a collection called ``names`` and groups -the ``first_name`` field together, counts the total number of results in each -group, and sorts the results by German phonebook order. - -.. code-block:: php - - test->names; - - $cursor = $collection->aggregate( - [ - ['$group' => ['_id' => '$first_name', 'name_count' => ['$sum' => 1]]], - ['$sort' => ['_id' => 1]], - ], - [ - 'collation' => ['locale' => 'de@collation=phonebook'], - ] - ); diff --git a/docs/tutorial/commands.txt b/docs/tutorial/commands.txt deleted file mode 100644 index 88ce5ec9a..000000000 --- a/docs/tutorial/commands.txt +++ /dev/null @@ -1,325 +0,0 @@ -========================= -Execute Database Commands -========================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Overview --------- - -The |php-library| provides helper methods across the :phpclass:`Client -`, :phpclass:`Database `, and -:phpclass:`Collection ` classes for common -:manual:`database commands `. In addition, the -:phpmethod:`MongoDB\\Database::command()` method may be used to run database -commands that do not have a helper method. - -Execute Database Commands -------------------------- - -Basic Command -~~~~~~~~~~~~~ - -To execute a command on a :program:`mongod` instance, use the -:phpmethod:`MongoDB\\Database::command()` method. For instance, the following -operation uses the :manual:`geoNear ` command to -search for the three closest documents to longitude ``-74`` and latitude ``40`` -in the ``restos`` collection in the ``test`` database: - -.. code-block:: php - - test; - - $cursor = $database->command([ - 'geoNear' => 'restos', - 'near' => [ - 'type' => 'Point', - 'coordinates' => [-74.0, 40.0], - ], - 'spherical' => 'true', - 'num' => 3, - ]); - - $results = $cursor->toArray()[0]; - - var_dump($results); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#27 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["waitedMS"]=> - int(0) - ["results"]=> - object(MongoDB\Model\BSONArray)#25 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - [0]=> - object(MongoDB\Model\BSONDocument)#14 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(39463.618389163) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#3 (1) { - ["oid"]=> - string(24) "55cba2486c522cafdb059bed" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#12 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#11 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.1641319) - [1]=> - float(39.6686512) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(32) "Soul Food Kitchen Seafood Heaven" - } - } - } - } - [1]=> - object(MongoDB\Model\BSONDocument)#19 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(50686.851650416) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#18 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#15 (1) { - ["oid"]=> - string(24) "55cba2476c522cafdb0544df" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#17 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#16 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.2566332) - [1]=> - float(40.4109872) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(20) "Seguine Bagel Bakery" - } - } - } - } - [2]=> - object(MongoDB\Model\BSONDocument)#24 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["dis"]=> - float(58398.379630263) - ["obj"]=> - object(MongoDB\Model\BSONDocument)#23 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#20 (1) { - ["oid"]=> - string(24) "55cba2476c522cafdb053c92" - } - ["location"]=> - object(MongoDB\Model\BSONDocument)#22 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["coordinates"]=> - object(MongoDB\Model\BSONArray)#21 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-74.3731727) - [1]=> - float(40.4404759) - } - } - ["type"]=> - string(5) "Point" - } - } - ["name"]=> - string(17) "Water'S Edge Club" - } - } - } - } - } - } - ["stats"]=> - object(MongoDB\Model\BSONDocument)#26 (1) { - ["storage":"ArrayObject":private]=> - array(5) { - ["nscanned"]=> - int(25139) - ["objectsLoaded"]=> - int(25134) - ["avgDistance"]=> - float(49516.283223281) - ["maxDistance"]=> - float(58398.379630263) - ["time"]=> - int(126) - } - } - ["ok"]=> - float(1) - } - } - -Commands with Custom Read Preference -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some commands, such as :manual:`createUser `, may -only be executed on a :term:`primary` replica set member or a -:term:`standalone`. - -The command helper methods in the |php-library|, such as -:phpmethod:`MongoDB\\Database::drop()`, know to apply their own :term:`read -preference` if necessary. However, the :phpmethod:`MongoDB\\Database::command()` -method is a generic method and defaults to the read preference of the Database -object on which it is invoked. To execute commands that require a specific read -preference, specify the read preference in the ``$options`` parameter of the -method. - -The following example adds a user to the ``test`` database and specifies a -custom read preference: - -.. code-block:: php - - test; - - $cursor = $db->command( - [ - 'createUser' => 'username', - 'pwd' => 'password', - 'roles' => ['readWrite'], - ], - [ - 'readPreference' => new MongoDB\Driver\ReadPreference(MongoDB\Driver\ReadPreference::RP_PRIMARY), - ] - ); - - var_dump($cursor->toArray()[0]); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -View Command Results --------------------- - -View Single Result Documents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :phpmethod:`MongoDB\\Database::command()` method returns a -:php:`MongoDB\\Driver\\Cursor ` object. - -Many MongoDB commands return their responses as a single document. To read the -command response, you may either iterate on the cursor and access the first -document, or access the first result in the array, as in the following: - -.. code-block:: php - - test; - - $cursor = $database->command(['ping' => 1]); - - var_dump($cursor->toArray()[0]); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#2 (1) { - ["storage":"ArrayObject":private]=> - array(1) { - ["ok"]=> - float(1) - } - } - -Iterate Results from a Cursor -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Some commands, such as :manual:`listCollections -`, return their results via an iterable -cursor. To view the results, iterate through the cursor. - -The following example lists the collections in the ``test`` database by -iterating through the cursor returned by the ``listCollections`` command using a -``foreach`` loop: - -.. code-block:: php - - test; - - $cursor = $database->command(['listCollections' => 1]); - - foreach ($cursor as $collection) { - echo $collection['name'], "\n"; - } - -The output would then be a list of the values for the ``name`` key, for -instance:: - - persons - posts - zips - -.. note:: - - At the *protocol* level, commands that support a cursor return a single - result document with the essential ingredients for constructing the command - cursor (i.e. the cursor's ID, namespace, and the first batch of results). In - the PHP driver implementation, the - :php:`MongoDB\Driver\Manager::executeCommand() - ` method detects such a result and - constructs the iterable command cursor, which is returned rather than the - base result document. diff --git a/docs/tutorial/connecting.txt b/docs/tutorial/connecting.txt deleted file mode 100644 index c964362a0..000000000 --- a/docs/tutorial/connecting.txt +++ /dev/null @@ -1,25 +0,0 @@ -===================== -Connecting to MongoDB -===================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Creating a Client instance --------------------------------------------------------- - -.. include:: /reference/method/MongoDBClient__construct.txt - :start-after: start-connecting-include - :end-before: end-connecting-include - -Specifying connection options ------------------------------ - -Connection options can be passed via the ``$uri`` parameter, or through the -``$options`` and ``$driverOptions`` parameters. The available options are -documented in the :phpmethod:`MongoDB\\Client::__construct()` reference. diff --git a/docs/tutorial/crud.txt b/docs/tutorial/crud.txt deleted file mode 100644 index 1e9f95e2e..000000000 --- a/docs/tutorial/crud.txt +++ /dev/null @@ -1,746 +0,0 @@ -=============== -CRUD Operations -=============== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -CRUD operations *create*, *read*, *update*, and *delete* documents. The -|php-library|'s :phpclass:`MongoDB\\Collection` class implements MongoDB's -cross-driver `CRUD specification -`_, -providing access to methods for inserting, finding, updating, and deleting -documents in MongoDB. - -This document provides a general introduction to inserting, querying, updating, -and deleting documents using the |php-library|. The MongoDB Manual's -:manual:`CRUD Section ` provides a more thorough introduction to CRUD -operations with MongoDB. - -Insert Documents ----------------- - -Insert One Document -~~~~~~~~~~~~~~~~~~~ - -The :phpmethod:`MongoDB\\Collection::insertOne()` method inserts a single -document into MongoDB and returns an instance of -:phpclass:`MongoDB\\InsertOneResult`, which you can use to access the ID of the -inserted document. - -.. this uses the insertOne example from the method reference: - -.. include:: /reference/method/MongoDBCollection-insertOne.txt - :start-after: start-crud-include - :end-before: end-crud-include - -The output includes the ID of the inserted document. - -If you include an ``_id`` value when inserting a document, MongoDB checks to -ensure that the ``_id`` value is unique for the collection. If the ``_id`` value -is not unique, the insert operation fails due to a duplicate key error. - -The following example inserts a document while specifying the value for the -``_id``: - -.. code-block:: php - - test->users; - - $insertOneResult = $collection->insertOne(['_id' => 1, 'name' => 'Alice']); - - printf("Inserted %d document(s)\n", $insertOneResult->getInsertedCount()); - - var_dump($insertOneResult->getInsertedId()); - -The output would then resemble:: - - Inserted 1 document(s) - int(1) - -.. seealso:: :phpmethod:`MongoDB\\Collection::insertOne()` - -Insert Many Documents -~~~~~~~~~~~~~~~~~~~~~ - -The :phpmethod:`MongoDB\\Collection::insertMany()` method allows you to insert -multiple documents in one write operation and returns an instance of -:phpclass:`MongoDB\\InsertManyResult`, which you can use to access the IDs of -the inserted documents. - -.. this uses the insertMany example from the method reference: - -.. include:: /reference/method/MongoDBCollection-insertMany.txt - :start-after: start-crud-include - :end-before: end-crud-include - -.. seealso:: :phpmethod:`MongoDB\\Collection::insertMany()` - -Query Documents ---------------- - -The |php-library| provides the :phpmethod:`MongoDB\\Collection::findOne()` and -:phpmethod:`MongoDB\\Collection::find()` methods for querying documents and the -:phpmethod:`MongoDB\\Collection::aggregate()` method for performing -:manual:`aggregation operations `. - -.. include:: /includes/extracts/note-bson-comparison.rst - -Find One Document -~~~~~~~~~~~~~~~~~ - -:phpmethod:`MongoDB\\Collection::findOne()` returns the :term:`first document -` that matches the query or ``null`` if no document matches the -query. - -The following example searches for the document with ``_id`` of ``"94301"``: - -.. code-block:: php - - test->zips; - - $document = $collection->findOne(['_id' => '94301']); - - var_dump($document); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(5) { - ["_id"]=> - string(5) "94301" - ["city"]=> - string(9) "PALO ALTO" - ["loc"]=> - object(MongoDB\Model\BSONArray)#12 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - [0]=> - float(-122.149685) - [1]=> - float(37.444324) - } - } - ["pop"]=> - int(15965) - ["state"]=> - string(2) "CA" - } - } - -.. note:: - - The criteria in this example matched an ``_id`` with a string value of - ``"94301"``. The same criteria would not have matched a document with an - integer value of ``94301`` due to MongoDB's :manual:`comparison rules for - BSON types `. Similarly, users should - use a :php:`MongoDB\\BSON\\ObjectId ` object - when matching an ``_id`` with an :manual:`ObjectId ` - value, as strings and ObjectIds are not directly comparable. - -.. seealso:: :phpmethod:`MongoDB\\Collection::findOne()` - -.. _php-find-many-documents: - -Find Many Documents -~~~~~~~~~~~~~~~~~~~ - -:phpmethod:`MongoDB\\Collection::find()` returns a -:php:`MongoDB\\Driver\\Cursor ` object, which you can -iterate upon to access all matched documents. - -The following example lists the documents in the ``zips`` collection with the -specified city and state values: - -.. code-block:: php - - test->zips; - - $cursor = $collection->find(['city' => 'JERSEY CITY', 'state' => 'NJ']); - - foreach ($cursor as $document) { - echo $document['_id'], "\n"; - } - -The output would resemble:: - - 07302 - 07304 - 07305 - 07306 - 07307 - 07310 - -.. seealso:: :phpmethod:`MongoDB\\Collection::find()` - -.. _php-query-projection: - -Query Projection -~~~~~~~~~~~~~~~~ - -By default, queries in MongoDB return all fields in matching documents. To limit -the amount of data that MongoDB sends to applications, you can include a -:manual:`projection document ` in -the query operation. - -.. note:: - - MongoDB includes the ``_id`` field by default unless you explicitly exclude - it in a projection document. - -The following example finds restaurants based on the ``cuisine`` and ``borough`` -fields and uses a :manual:`projection -` to limit the fields that are -returned. It also limits the results to 5 documents. - -.. code-block:: php - - test->restaurants; - - $cursor = $collection->find( - [ - 'cuisine' => 'Italian', - 'borough' => 'Manhattan', - ], - [ - 'projection' => [ - 'name' => 1, - 'borough' => 1, - 'cuisine' => 1, - ], - 'limit' => 4, - ] - ); - - foreach($cursor as $restaurant) { - var_dump($restaurant); - }; - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#10 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#8 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f983" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(23) "Isle Of Capri Resturant" - } - } - object(MongoDB\Model\BSONDocument)#13 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#12 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f98d" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(18) "Marchis Restaurant" - } - } - object(MongoDB\Model\BSONDocument)#8 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#10 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f99b" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(19) "Forlinis Restaurant" - } - } - object(MongoDB\Model\BSONDocument)#12 (1) { - ["storage":"ArrayObject":private]=> - array(4) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#13 (1) { - ["oid"]=> - string(24) "576023c6b02fa9281da3f9a8" - } - ["borough"]=> - string(9) "Manhattan" - ["cuisine"]=> - string(7) "Italian" - ["name"]=> - string(22) "Angelo Of Mulberry St." - } - } - -Limit, Sort, and Skip Options -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In addition to :ref:`projection criteria `, you can -specify options to limit, sort, and skip documents during queries. - -The following example uses the ``limit`` and ``sort`` options to query for the -five most populous zip codes in the United States: - -.. code-block:: php - - test->zips; - - $cursor = $collection->find( - [], - [ - 'limit' => 5, - 'sort' => ['pop' => -1], - ] - ); - - foreach ($cursor as $document) { - printf("%s: %s, %s\n", $document['_id'], $document['city'], $document['state']); - } - -The output would then resemble:: - - 60623: CHICAGO, IL - 11226: BROOKLYN, NY - 10021: NEW YORK, NY - 10025: NEW YORK, NY - 90201: BELL GARDENS, CA - -Regular Expressions -~~~~~~~~~~~~~~~~~~~ - -Filter criteria may include regular expressions, either by using the -:php:`MongoDB\\BSON\\Regex ` class directory or the -:query:`$regex` operator. - -The following example lists documents in the ``zips`` collection where the city -name starts with "garden" and the state is Texas: - -.. code-block:: php - - test->zips; - - $cursor = $collection->find([ - 'city' => new MongoDB\BSON\Regex('^garden', 'i'), - 'state' => 'TX', - ]); - - foreach ($cursor as $document) { - printf("%s: %s, %s\n", $document['_id'], $document['city'], $document['state']); - } - -The output would then resemble:: - - 78266: GARDEN RIDGE, TX - 79739: GARDEN CITY, TX - 79758: GARDENDALE, TX - -An equivalent filter could be constructed using the :query:`$regex` operator: - -.. code-block:: php - - [ - 'city' => ['$regex' => '^garden', '$options' => 'i'], - 'state' => 'TX', - ] - -.. seealso:: :manual:`$regex ` in the MongoDB manual - -Although MongoDB's regular expression syntax is not exactly the same as PHP's -:php:`PCRE ` syntax, :php:`preg_quote() ` -may be used to escape special characters that should be matched as-is. The -following example finds restaurants whose name starts with "(Library)": - -.. code-block:: php - - test->restaurants; - - $cursor = $collection->find([ - 'name' => new MongoDB\BSON\Regex('^' . preg_quote('(Library)')), - ]); - -.. _php-aggregation: - -Complex Queries with Aggregation -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -MongoDB's :manual:`Aggregation Framework ` allows -you to issue complex queries that filter, transform, and group collection data. -The |php-library|\'s :phpmethod:`MongoDB\\Collection::aggregate()` method -returns a :php:`Traversable ` object, which you can iterate upon to -access the results of the aggregation operation. Refer to the -:phpmethod:`MongoDB\\Collection::aggregate()` method's :ref:`behavior -reference ` for more about the method's output. - -The following example lists the 5 US states with the most zip codes associated -with them: - -.. code-block:: php - - test->zips; - - $cursor = $collection->aggregate([ - ['$group' => ['_id' => '$state', 'count' => ['$sum' => 1]]], - ['$sort' => ['count' => -1]], - ['$limit' => 5], - ]); - - foreach ($cursor as $state) { - printf("%s has %d zip codes\n", $state['_id'], $state['count']); - } - -The output would then resemble:: - - TX has 1671 zip codes - NY has 1595 zip codes - CA has 1516 zip codes - PA has 1458 zip codes - IL has 1237 zip codes - -.. seealso:: :phpmethod:`MongoDB\\Collection::aggregate()` - -Update Documents ----------------- - -Update One Document -~~~~~~~~~~~~~~~~~~~ - -Use the :phpmethod:`MongoDB\\Collection::updateOne()` method to update a single -document matching a filter. :phpmethod:`MongoDB\\Collection::updateOne()` -returns a :phpclass:`MongoDB\\UpdateResult` object, which you can use to access -statistics about the update operation. - -Update methods have two required parameters: the query filter that identifies -the document or documents to update, and an update document that specifies what -updates to perform. The :phpmethod:`MongoDB\\Collection::updateOne()` reference -describes each parameter in detail. - -The following example inserts two documents into an empty ``users`` collection -in the ``test`` database using the :phpmethod:`MongoDB\\Collection::insertOne()` -method, and then updates the documents where the value for the ``state`` field -is ``"ny"`` to include a ``country`` field set to ``"us"``: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $updateResult = $collection->updateOne( - ['state' => 'ny'], - ['$set' => ['country' => 'us']] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -Since the update operation uses the -:phpmethod:`MongoDB\\Collection::updateOne()` method, which updates the first -document to match the filter criteria, the results would then resemble:: - - Matched 1 document(s) - Modified 1 document(s) - -It is possible for a document to match the filter but *not be modified* by an -update, as is the case where the update sets a field's value to its existing -value, as in this example: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $updateResult = $collection->updateOne( - ['name' => 'Bob'], - ['$set' => ['state' => 'ny']] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -The number of matched documents and the number of *modified* documents would -therefore not be equal, and the output from the operation would resemble:: - - Matched 1 document(s) - Modified 0 document(s) - -.. seealso:: - - - :phpmethod:`MongoDB\\Collection::updateOne()` - - :phpmethod:`MongoDB\\Collection::findOneAndUpdate()` - -Update Many Documents -~~~~~~~~~~~~~~~~~~~~~ - -:phpmethod:`MongoDB\\Collection::updateMany()` updates one or more documents -matching the filter criteria and returns a :phpclass:`MongoDB\\UpdateResult` -object, which you can use to access statistics about the update operation. - -Update methods have two required parameters: the query filter that identifies -the document or documents to update, and an update document that specifies what -updates to perform. The :phpmethod:`MongoDB\\Collection::updateMany()` reference -describes each parameter in detail. - -The following example inserts three documents into an empty ``users`` collection -in the ``test`` database and then uses the :query:`$set` operator to update the -documents matching the filter criteria to include the ``country`` field with -value ``"us"``: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny', 'country' => 'us']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $collection->insertOne(['name' => 'Sam', 'state' => 'ny']); - $updateResult = $collection->updateMany( - ['state' => 'ny'], - ['$set' => ['country' => 'us']] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -If an update operation results in no change to a document, such as setting the -value of the field to its current value, the number of modified documents can be -less than the number of *matched* documents. Since the update document with -``name`` of ``"Bob"`` results in no changes to the document, the output of the -operation therefore resembles:: - - Matched 3 document(s) - Modified 2 document(s) - -.. seealso:: :phpmethod:`MongoDB\\Collection::updateMany()` - -Replace Documents -~~~~~~~~~~~~~~~~~ - -Replacement operations are similar to update operations, but instead of updating -a document to include new fields or new field values, a replacement operation -replaces the entire document with a new document, but retains the original -document's ``_id`` value. - -The :phpmethod:`MongoDB\\Collection::replaceOne()` method replaces a single -document that matches the filter criteria and returns an instance of -:phpclass:`MongoDB\\UpdateResult`, which you can use to access statistics about -the replacement operation. - -:phpmethod:`MongoDB\\Collection::replaceOne()` has two required parameters: the -query filter that identifies the document or documents to replace, and a -replacement document that will replace the original document in MongoDB. The -:phpmethod:`MongoDB\\Collection::replaceOne()` reference describes each -parameter in detail. - -.. important:: - - Replacement operations replace all of the fields in a document except the - ``_id`` value. To avoid accidentally overwriting or deleting desired fields, - use the :phpmethod:`MongoDB\\Collection::updateOne()` or - :phpmethod:`MongoDB\\Collection::updateMany()` methods to update individual - fields in a document rather than replacing the entire document. - -The following example inserts one document into an empty ``users`` collection in -the ``test`` database, and then replaces that document with a new one: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $updateResult = $collection->replaceOne( - ['name' => 'Bob'], - ['name' => 'Robert', 'state' => 'ca'] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - -The output would then resemble:: - - Matched 1 document(s) - Modified 1 document(s) - -.. seealso:: - - - :phpmethod:`MongoDB\\Collection::replaceOne()` - - :phpmethod:`MongoDB\\Collection::findOneAndReplace()` - -Upsert -~~~~~~ - -Update and replace operations support an :manual:`upsert -` option. When ``upsert`` is ``true`` -*and* no documents match the specified filter, the operation creates a new -document and inserts it. If there *are* matching documents, then the operation -modifies or replaces the matching document or documents. - -When a document is upserted, the ID is accessible via -:phpmethod:`MongoDB\\UpdateResult::getUpsertedId()`. - -The following example uses :phpmethod:`MongoDB\\Collection::updateOne()` with -the ``upsert`` option set to ``true`` and an empty ``users`` collection in the -``test`` database, therefore inserting the document into the database: - -.. code-block:: php - - test->users; - $collection->drop(); - - $updateResult = $collection->updateOne( - ['name' => 'Bob'], - ['$set' => ['state' => 'ny']], - ['upsert' => true] - ); - - printf("Matched %d document(s)\n", $updateResult->getMatchedCount()); - printf("Modified %d document(s)\n", $updateResult->getModifiedCount()); - printf("Upserted %d document(s)\n", $updateResult->getUpsertedCount()); - - $upsertedDocument = $collection->findOne([ - '_id' => $updateResult->getUpsertedId(), - ]); - - var_dump($upsertedDocument); - -The output would then resemble:: - - Matched 0 document(s) - Modified 0 document(s) - Upserted 1 document(s) - object(MongoDB\Model\BSONDocument)#16 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - object(MongoDB\BSON\ObjectId)#15 (1) { - ["oid"]=> - string(24) "57509c4406d7241dad86e7c3" - } - ["name"]=> - string(3) "Bob" - ["state"]=> - string(2) "ny" - } - } - -Delete Documents ----------------- - -Delete One Document -~~~~~~~~~~~~~~~~~~~ - -The :phpmethod:`MongoDB\\Collection::deleteOne()` method deletes a single -document that matches the filter criteria and returns a -:phpclass:`MongoDB\\DeleteResult`, which you can use to access statistics about -the delete operation. - -If multiple documents match the filter criteria, -:phpmethod:`MongoDB\\Collection::deleteOne()` deletes the :term:`first -` matching document. - -:phpmethod:`MongoDB\\Collection::deleteOne()` has one required parameter: a -query filter that specifies the document to delete. Refer to the -:phpmethod:`MongoDB\\Collection::deleteOne()` reference for full method -documentation. - -The following operation deletes the first document where the ``state`` field's -value is ``"ny"``: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $deleteResult = $collection->deleteOne(['state' => 'ny']); - - printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); - -The output would then resemble:: - - Deleted 1 document(s) - -.. seealso:: :phpmethod:`MongoDB\\Collection::deleteOne()` - -Delete Many Documents -~~~~~~~~~~~~~~~~~~~~~ - -:phpmethod:`MongoDB\\Collection::deleteMany()` deletes all of the documents that -match the filter criteria and returns a :phpclass:`MongoDB\\DeleteResult`, which -you can use to access statistics about the delete operation. - -:phpmethod:`MongoDB\\Collection::deleteMany()` has one required parameter: a -query filter that specifies the document to delete. Refer to the -:phpmethod:`MongoDB\\Collection::deleteMany()` reference for full method -documentation. - -The following operation deletes all of the documents where the ``state`` field's -value is ``"ny"``: - -.. code-block:: php - - test->users; - $collection->drop(); - - $collection->insertOne(['name' => 'Bob', 'state' => 'ny']); - $collection->insertOne(['name' => 'Alice', 'state' => 'ny']); - $deleteResult = $collection->deleteMany(['state' => 'ny']); - - printf("Deleted %d document(s)\n", $deleteResult->getDeletedCount()); - -The output would then resemble:: - - Deleted 2 document(s) - -.. seealso:: :phpmethod:`MongoDB\\Collection::deleteMany()` diff --git a/docs/tutorial/custom-types.txt b/docs/tutorial/custom-types.txt deleted file mode 100644 index 574604302..000000000 --- a/docs/tutorial/custom-types.txt +++ /dev/null @@ -1,187 +0,0 @@ -================= -Custom Data-Types -================= - -.. default-domain:: mongodb - -The MongoDB PHP extension and library support custom classes while -serializing and deserializing. An example of where this might be useful is -if you want to store date/time information retaining the time zone -information that PHP's :php:`DateTimeImmutable ` -class stores with a point in time. - -The driver serializes PHP variables, including objects, into BSON when it -communicates to the server, and deserializes BSON back into PHP variables when -it receives data from the server. - -It is possible to influence the behaviour by implementing the -:php:`MongoDB\\BSON\\Persistable ` interface. -If a class implements this interface, then upon serialization the -:php:`bsonSerialize ` method is -called. This method is responsible for returning an array or stdClass object -to convert to BSON and store in the database. That data will later be used to -reconstruct the object upon reading from the database. - -As an example we present the ``LocalDateTime`` class. This class wraps around -the :php:`MongoDB\\BSON\\UTCDateTime ` data -type and a time zone. - -.. code-block:: php - - utc = new \MongoDB\BSON\UTCDateTime($milliseconds); - if ($timezone === null) { - $timezone = new \DateTimeZone(date_default_timezone_get()); - } - $this->tz = $timezone; - } - ?> - -As it implements the :php:`MongoDB\\BSON\\Persistable -` interface, the -class is required to implement the :php:`bsonSerialize -` and :php:`bsonUnserialize -` methods. In the -:php:`bsonSerialize ` method, we -return an array with the two values that we need to persist: the point in time -in milliseconds since the Epoch, represented by a -:php:`MongoDB\\BSON\\UTCDateTime ` object, and -a string containing the Olson time zone identifier: - -.. code-block:: php - - $this->utc, - 'tz' => $this->tz->getName(), - ]; - } - ?> - -The driver will additionally add a ``__pclass`` field to the document, and -store that in the database, too. This field contains the PHP class name so that -upon deserialization the driver knows which class to use for recreating the -stored object. - -When the document is read from the database, the driver detects whether a -``__pclass`` field is present and then executes -:php:`MongoDB\\BSON\\Persistable::bsonUnserialize -` method which is -responsible for restoring the object's original state. - -In the code below, we make sure that the data in the ``utc`` and ``tz`` fields -are of the right time, and then assign their values to the two private -properties. - -.. code-block:: php - - utc = $data['utc']; - $this->tz = new \DateTimeZone($data['tz']); - } - ?> - -You may have noticed that the class also implements the -:php:`MongoDB\\BSON\\UTCDateTimeInterface -` interface. This interface defines -the two non-constructor methods of the :php:`MongoDB\\BSON\\UTCDateTime -` class. - -It is recommended that wrappers around existing BSON classes implement their -respective interface (i.e. :php:`MongoDB\\BSON\\UTCDateTimeInterface -`) so that the wrapper objects can be -used in the same context as their original unwrapped version. It is also -recommended that you always type-hint against the interface (i.e. -:php:`MongoDB\\BSON\\UTCDateTimeInterface -`) and never against the concrete -class (i.e. :php:`MongoDB\\BSON\\UTCDateTime -`), as this would prevent wrapped objects from -being accepted into methods. - -In our new ``toDateTime`` method we return a :php:`DateTime ` -object with the local time zone set, instead of the UTC time zone that -:php:`MongoDB\\BSON\\UTCDateTime ` normally uses -in its return value. - -.. code-block:: php - - utc->toDateTime()->setTimezone($this->tz); - } - - public function __toString() - { - return (string) $this->utc; - } - } - ?> - -With the class defined, we can now use it in our documents. The snippet below -demonstrates the round tripping from the ``LocalDateTime`` object to BSON, and -back to ``LocalDateTime``. - -.. code-block:: php - - new LocalDateTime]); - $document = MongoDB\BSON\toPHP($bson); - - var_dump($document); - var_dump($document->date->toDateTime()); - ?> - -Which outputs:: - - object(stdClass)#1 (1) { - ["date"]=> - object(LocalDateTime)#2 (2) { - ["utc":"LocalDateTime":private]=> - object(MongoDB\BSON\UTCDateTime)#3 (1) { - ["milliseconds"]=> - string(13) "1533042443716" - } - ["tz":"LocalDateTime":private]=> - object(DateTimeZone)#4 (2) { - ["timezone_type"]=> - int(3) - ["timezone"]=> - string(13) "Europe/London" - } - } - } - object(DateTime)#5 (3) { - ["date"]=> - string(26) "2018-07-31 14:07:23.716000" - ["timezone_type"]=> - int(3) - ["timezone"]=> - string(13) "Europe/London" - } - -Storing the Olson time zone identifier in a separate field also works well -with MongoDB's :manual:`Aggregation Framework `, which allows -date manipulation, :manual:`formatting -`, and querying depending on a -specific time zone. diff --git a/docs/tutorial/decimal128.txt b/docs/tutorial/decimal128.txt deleted file mode 100644 index a94d0ea5e..000000000 --- a/docs/tutorial/decimal128.txt +++ /dev/null @@ -1,124 +0,0 @@ -========== -Decimal128 -========== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Overview --------- - -MongoDB 3.4 introduced support for a :manual:`Decimal128 BSON type -`, which is a 128-bit decimal-based -floating-point value capable of emulating decimal rounding with exact precision. -This functionality is intended for applications that handle :manual:`monetary -data `, such as financial and tax computations. - -The :php:`MongoDB\BSON\Decimal128 ` class, which was -introduced in :php:`PHP driver ` 1.2.0, may be used to work with this -type in PHP. - -Working with Decimal128 Values ------------------------------- - -Inserting a Decimal128 -~~~~~~~~~~~~~~~~~~~~~~ - -The following example inserts a value of type ``Decimal128`` into the ``price`` -field of a collection named ``inventory``: - -.. code-block:: php - - test->inventory; - - $collection->insertOne([ - '_id' => 1, - 'item' => '26-inch monitor', - 'price' => new MongoDB\BSON\Decimal128('428.79'), - ]); - - $item = $collection->findOne(['_id' => 1]); - - var_dump($item); - -The output would then resemble:: - - object(MongoDB\Model\BSONDocument)#9 (1) { - ["storage":"ArrayObject":private]=> - array(3) { - ["_id"]=> - int(1) - ["item"]=> - string(15) "26-inch monitor" - ["price"]=> - object(MongoDB\BSON\Decimal128)#13 (1) { - ["dec"]=> - string(6) "428.79" - } - } - } - -Mathematical Operations with BCMath -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The :php:`PHP driver ` does not provide any functionality for working -with ``Decimal128`` values; however, the string representation of a -:php:`MongoDB\BSON\Decimal128 ` object may be used -with PHP's :php:`BCMath ` extension. - -The following example adds two ``Decimal128`` values and creates a new -``Decimal128`` value with the result from :php:`bcadd() `: - -.. code-block:: php - - - string(1) "6" - } - -This does not match the expected result of "6.912". Each operation in the BCMath -API uses a scale to determine the number of decimal digits in the result. The -default scale is zero, which is why the above example produces a result with no -decimal precision. - -In the following example, we use a scale of three for :php:`bcadd() ` to -obtain the expected result: - -.. code-block:: php - - - string(5) "6.912" - } - -In lieu of specifying a scale for each operation, a default scale may be set via -:php:`bcscale() ` or the :php:`bcmath.scale INI setting -`. The ``Decimal128`` type -supports up to 34 decimal digits (i.e. significant digits). diff --git a/docs/tutorial/example-data.txt b/docs/tutorial/example-data.txt deleted file mode 100644 index cf3230952..000000000 --- a/docs/tutorial/example-data.txt +++ /dev/null @@ -1,45 +0,0 @@ -============ -Example Data -============ - -.. default-domain:: mongodb - -Some examples in this documentation use example data fixtures from -`zips.json `_ and -`primer-dataset.json `_. - -Importing the dataset into MongoDB can be done in several ways. The following -example imports the `zips.json` file into a `test.zips` collection: -:php:`driver ` directly: - -.. code-block:: php - - insert($document); - } - - $manager = new MongoDB\Driver\Manager('mongodb://127.0.0.1/'); - - $result = $manager->executeBulkWrite('test.zips', $bulk); - printf("Inserted %d documents\n", $result->getInsertedCount()); - -The output would then resemble:: - - Inserted 29353 documents - -You may also import the datasets using :manual:`mongoimport -`, which is included with MongoDB: - -.. code-block:: sh - - mongoimport --db test --collection zips --file zips.json --drop - mongoimport --db test --collection restaurants --file primer-dataset.json --drop diff --git a/docs/tutorial/gridfs.txt b/docs/tutorial/gridfs.txt deleted file mode 100644 index 81f6d2639..000000000 --- a/docs/tutorial/gridfs.txt +++ /dev/null @@ -1,214 +0,0 @@ -====== -GridFS -====== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -:manual:`GridFS ` is a specification for storing and retrieving -files in MongoDB. GridFS uses two collections to store files. One collection -stores the file chunks (e.g. ``fs.chunks``), and the other stores file metadata -(e.g. ``fs.files``). The :phpclass:`MongoDB\\GridFS\\Bucket` class provides an -interface around these collections for working with the files as PHP -:php:`Streams `. - -Creating a GridFS Bucket ------------------------- - -You can construct a GridFS bucket using the PHP extension's -:php:`MongoDB\\Driver\\Manager ` class, or select -a bucket from the |php-library|'s :phpclass:`MongoDB\\Database` class via the -:phpmethod:`selectGridFSBucket() ` -method. - -The bucket can be constructed with various options: - -- ``bucketName`` determines the prefix for the bucket's metadata and chunk - collections. The default value is ``"fs"``. -- ``chunkSizeBytes`` determines the size of each chunk. GridFS divides the file - into chunks of this length, except for the last, which is only as large as - needed. The default size is ``261120`` (i.e. 255 KiB). -- ``readConcern``, ``readPreference`` and ``writeConcern`` options can be used - to specify defaults for read and write operations, much like the - :phpclass:`MongoDB\\GridFS\\Collection` options. - -Uploading Files with Writable Streams -------------------------------------- - -To upload a file to GridFS using a writable stream, you can either open a stream -and write to it directly or write the entire contents of another readable stream -to GridFS all at once. - -To open an upload stream and write to it: - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openUploadStream('my-file.txt'); - - $contents = file_get_contents('/path/to/my-file.txt'); - fwrite($stream, $contents); - fclose($stream); - -To upload the entire contents of a readable stream in one call: - -.. code-block:: php - - test->selectGridFSBucket(); - - $file = fopen('/path/to/my-file.txt', 'rb'); - $bucket->uploadFromStream('my-file.txt', $file); - -Downloading Files with Readable Streams ---------------------------------------- - -To download a file from GridFS using a readable stream, you can either open a -stream and read from it directly or download the entire file all at once. - -To open a download stream and read from it: - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openDownloadStream($fileId); - $contents = stream_get_contents($stream); - -To download the file all at once and write it to a writable stream: - -.. code-block:: php - - test->selectGridFSBucket(); - - $file = fopen('/path/to/my-output-file.txt', 'wb'); - - $bucket->downloadToStream($fileId, $file); - -Selecting Files by Filename and Revision ----------------------------------------- - -You can also download a file specified by filename and (optionally) revision -number. Revision numbers are used to distinguish between files sharing the same -``filename`` metadata field, ordered by date of upload (i.e. the ``uploadDate`` -metadata field). The ``revision`` option accepted by -:phpmethod:`openDownloadStreamByName() -` and -:phpmethod:`downloadToStreamByName() -` can be positive or negative. - -A positive ``revision`` number may be used to select files in order of the -oldest upload date. A revision of ``0`` would denote the file with the oldest -upload date, a revision of ``1`` would denote the second oldest upload, and so -on. - -A negative revision may be used to select files in order of the most recent -upload date. A revision of ``-1`` would denote a file with the most recent -upload date, a revision of ``-2`` would denote the second most recent upload, -and so on. The ``revision`` option defaults to ``-1`` if not specified. - -The following example downloads the contents of the oldest version of a -particular file: - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openDownloadStreamByName('my-file.txt', ['revision' => 0]); - $contents = stream_get_contents($stream); - -Deleting Files --------------- - -You can delete a GridFS file by its ``_id``. - -.. code-block:: php - - test->selectGridFSBucket(); - - $bucket->delete($fileId); - -Finding File Metadata ---------------------- - -The :phpmethod:`find() ` method allows you to -retrieve documents from the GridFS files collection, which contain metadata -about the GridFS files. - -.. code-block:: php - - test->selectGridFSBucket(); - - $cursor = $bucket->find(['filename' => 'my-file.txt']); - -Accessing File Metadata for an Existing Stream ----------------------------------------------- - -The :phpmethod:`getFileDocumentForStream() -` method may be used to get -the file document for an existing readable or writable GridFS stream. - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openDownloadStream($fileId); - $metadata = $bucket->getFileDocumentForStream($stream); - -.. note:: - - Since the file document for a writable stream is not committed to MongoDB - until the stream is closed, - :phpmethod:`getFileDocumentForStream() - ` can only return an - in-memory document, which will be missing some fields (e.g. ``length``, - ``md5``). - -The :phpmethod:`getFileIdForStream() -` method may be used to get the -``_id`` for an existing readable or writable GridFS stream. This is a -convenience for accessing the ``_id`` property of the object returned by -:phpmethod:`getFileDocumentForStream() -`. - -.. code-block:: php - - test->selectGridFSBucket(); - - $stream = $bucket->openDownloadStreamByName('my-file.txt'); - $fileId = $bucket->getFileIdForStream($stream); diff --git a/docs/tutorial/indexes.txt b/docs/tutorial/indexes.txt deleted file mode 100644 index 0ffe7bdb9..000000000 --- a/docs/tutorial/indexes.txt +++ /dev/null @@ -1,133 +0,0 @@ -======= -Indexes -======= - -.. default-domain:: mongodb - -Indexes support the efficient execution of queries in MongoDB. Without indexes, -MongoDB must perform a *collection scan*, i.e. scan every document in a -collection, to select those documents that match the query statement. If an -appropriate index exists for a query, MongoDB can use the index to limit the -number of documents it must inspect. - -The PHP driver supports managing indexes through the -:phpclass:`MongoDB\\Collection` class, which implements MongoDB's -cross-driver `Index Management -`_ -and `Enumerating Indexes -`_ -specifications. - -This document provides an introduction to creating, listing, and dropping -indexes using the |php-library|. The MongoDB Manual's :manual:`Indexes -` reference provides more thorough information about indexing in -MongoDB. - -Create Indexes --------------- - -Create indexes with the :phpmethod:`MongoDB\\Collection::createIndex()` or -:phpmethod:`MongoDB\\Collection::createIndexes()` methods. Refer to the method -reference for more details about each method. - -The following example creates an ascending index on the ``state`` field using -the :phpmethod:`createIndex() ` method: - -.. code-block:: php - - test->zips; - - $result = $collection->createIndex(['state' => 1]); - - var_dump($result); - -When you create an index, the method returns its name, which is automatically -generated from its specification. The above example would output something -similar to:: - - string(7) "state_1" - -List Indexes ------------- - -The :phpmethod:`MongoDB\\Collection::listIndexes()` method provides information -about the indexes in a collection. The -:phpmethod:`MongoDB\\Collection::listIndexes()` method returns an iterator of -:phpclass:`MongoDB\\Model\\IndexInfo` objects, which you can use to view -information about each index. Refer to the method reference for more details. - -The following example lists all indexes in the ``zips`` collection in the -``test`` database: - -.. code-block:: php - - test->zips; - - foreach ($collection->listIndexes() as $indexInfo) { - var_dump($indexInfo); - } - -The output would resemble:: - - object(MongoDB\Model\IndexInfo)#10 (4) { - ["v"]=> - int(1) - ["key"]=> - array(1) { - ["_id"]=> - int(1) - } - ["name"]=> - string(4) "_id_" - ["ns"]=> - string(9) "test.zips" - } - object(MongoDB\Model\IndexInfo)#13 (4) { - ["v"]=> - int(1) - ["key"]=> - array(1) { - ["state"]=> - int(1) - } - ["name"]=> - string(7) "state_1" - ["ns"]=> - string(9) "test.zips" - } - -Drop Indexes ------------- - -The :phpmethod:`MongoDB\\Collection::dropIndex()` method lets you drop a single -index while :phpmethod:`MongoDB\\Collection::dropIndexes()` drops all of the -indexes on a collection. Refer to the method reference for more details about -each method. - -The following example drops a single index by its name, ``state_1``: - -.. code-block:: php - - test->zips; - - $result = $collection->dropIndex('state_1'); - - var_dump($result); - -The operation's output would resemble:: - - object(MongoDB\Model\BSONDocument)#11 (1) { - ["storage":"ArrayObject":private]=> - array(2) { - ["nIndexesWas"]=> - int(2) - ["ok"]=> - float(1) - } - } diff --git a/docs/tutorial/install-php-library.txt b/docs/tutorial/install-php-library.txt deleted file mode 100644 index af409c360..000000000 --- a/docs/tutorial/install-php-library.txt +++ /dev/null @@ -1,100 +0,0 @@ -========================= -Install the |php-library| -========================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -The |php-library| is a high-level abstraction for the -`PHP driver `_ (i.e. ``mongodb`` extension). This page -will briefly explain how to install both the ``mongodb`` extension and the -|php-library|. - -Installing the Extension ------------------------- - -Linux, Unix, and macOS users can either -:php:`install the extension with PECL ` -(recommended) or -:php:`manually compile from source `. -The following command may be used to install the extension with PECL: - -.. code-block:: sh - - sudo pecl install mongodb - -.. note:: - - If the build process for either installation method fails to find a TLS - library, check that the development packages (e.g. ``libssl-dev``) and - `pkg-config `_ are both installed. - -Once the extension is installed, add the following line to your ``php.ini`` -file: - -.. code-block:: ini - - extension=mongodb.so - -Windows users can download precompiled binaries of the extension from its -`GitHub releases `__. -After downloading the appropriate archive for your PHP environment, extract the -``php_mongodb.dll`` file to PHP's extension directory and add the following line -to your ``php.ini`` file: - -.. code-block:: ini - - extension=php_mongodb.dll - -See :php:`Installing the MongoDB PHP Driver on Windows ` -for additional information. - -Installing the Library ----------------------- - -Using Composer -~~~~~~~~~~~~~~ - -The preferred method of installing the |php-library| is with -`Composer `_ by running the following command from -your project root: - -.. code-block:: sh - - composer require mongodb/mongodb - -Once you have installed the library, ensure that your application includes -Composer's autoloader as in the following example: - -.. code-block:: php - - `_ for more -information about setting up autoloading. - -Manual Installation Without Composer -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -While not recommended, you may also manually install the library using a source -archive attached to the -`GitHub releases `__. -When installing the library without Composer, you must ensure that all library -classes *and* functions are loaded for your application: - -#. If you are using a `PSR-4 `_ autoloader, - map the top-level ``MongoDB\`` namespace to the ``src/`` directory. If you - are not using an autoloader, manually require _all_ class files found - recursively within the ``src/`` directory. - -#. Regardless of whether you are using an autoloader, manually require the - ``src/functions.php`` file. This is necessary because PHP does not support - autoloading for functions. diff --git a/docs/tutorial/modeling-bson-data.txt b/docs/tutorial/modeling-bson-data.txt deleted file mode 100644 index 38f44769f..000000000 --- a/docs/tutorial/modeling-bson-data.txt +++ /dev/null @@ -1,215 +0,0 @@ -================== -Modeling BSON Data -================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Type Maps ---------- - -Most methods that read data from MongoDB support a ``typeMap`` option, which -allows control over how BSON is converted to PHP. Additionally, -the :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and -:phpclass:`MongoDB\\Collection` classes accept a ``typeMap`` option, which can -be used to specify a default type map to apply to any supporting methods and -selected classes (e.g. :phpmethod:`MongoDB\\Client::selectDatabase()`). - -The :phpclass:`MongoDB\\Client`, :phpclass:`MongoDB\\Database`, and -:phpclass:`MongoDB\\Collection` classes use the following type map by -default: - -.. code-block:: php - - [ - 'array' => 'MongoDB\Model\BSONArray', - 'document' => 'MongoDB\Model\BSONDocument', - 'root' => 'MongoDB\Model\BSONDocument', - ] - -The type map above will convert BSON documents and arrays to -:phpclass:`MongoDB\\Model\\BSONDocument` and -:phpclass:`MongoDB\\Model\\BSONArray` objects, respectively. The ``root`` and -``document`` keys are used to distinguish the top-level BSON document from -embedded documents, respectively. - -A type map may specify any class that implements -:php:`MongoDB\\BSON\\Unserializable ` as well as -``"array"``, ``"stdClass``", and ``"object"`` (``"stdClass``" and ``"object"`` -are aliases of one another). - -.. seealso:: :php:`Deserialization from BSON ` in the PHP manual - - -Persistable Classes -------------------- - -The driver's :php:`persistence specification ` outlines how -classes implementing its :php:`MongoDB\\BSON\\Persistable -` interface are serialized to and deserialized from -BSON. The :php:`Persistable ` interface is analogous -to PHP's :php:`Serializable interface `. - -The driver automatically handles serialization and deserialization for classes -implementing the :php:`Persistable ` interface without -requiring the use of the ``typeMap`` option. This is done by encoding the name -of the PHP class in a special property within the BSON document. - -.. note:: - - When deserializing a PHP variable from BSON, the encoded class name of a - :php:`Persistable ` object will override any class - specified in the type map, but it will not override ``"array"`` and - ``"stdClass"`` or ``"object"``. This is discussed in the - :php:`persistence specification ` but it bears - repeating. - -Consider the following class definition: - -.. code-block:: php - - id = new MongoDB\BSON\ObjectId; - $this->name = $name; - $this->createdAt = new MongoDB\BSON\UTCDateTime; - } - - function bsonSerialize() - { - return [ - '_id' => $this->id, - 'name' => $this->name, - 'createdAt' => $this->createdAt, - ]; - } - - function bsonUnserialize(array $data) - { - $this->id = $data['_id']; - $this->name = $data['name']; - $this->createdAt = $data['createdAt']; - } - } - -The following example constructs a ``Person`` object, inserts it into the -database, and reads it back as an object of the same type: - -.. code-block:: php - - test->persons; - - $result = $collection->insertOne(new Person('Bob')); - - $person = $collection->findOne(['_id' => $result->getInsertedId()]); - - var_dump($person); - -The output would then resemble: - -.. code-block:: none - - object(Person)#18 (3) { - ["id":"Person":private]=> - object(MongoDB\BSON\ObjectId)#15 (1) { - ["oid"]=> - string(24) "56fad2c36118fd2e9820cfc1" - } - ["name":"Person":private]=> - string(3) "Bob" - ["createdAt":"Person":private]=> - object(MongoDB\BSON\UTCDateTime)#17 (1) { - ["milliseconds"]=> - int(1459278531218) - } - } - -The same document in the MongoDB shell might display as: - -.. code-block:: js - - { - "_id" : ObjectId("56fad2c36118fd2e9820cfc1"), - "__pclass" : BinData(128,"UGVyc29u"), - "name" : "Bob", - "createdAt" : ISODate("2016-03-29T19:08:51.218Z") - } - -.. note:: - - :php:`MongoDB\\BSON\\Persistable ` may only be used - for root and embedded BSON documents. It may not be used for BSON arrays. -.. _php-type-map: - -Working with Enums ------------------- - -:php:`Backed enums ` can be used with BSON and will -serialize as their case value (i.e. integer or string). -:php:`Pure enums `, which have no backed cases, cannot be -directly serialized. This is similar to how enums are handled by -:php:`json_encode() `. - -Round-tripping a backed enum through BSON requires special handling. In the -following example, the `bsonUnserialize()` method in the class containing the -enum is responsible for converting the value back to an enum case: - -.. code-block:: php - - $this->_id, - 'username' => $this->username, - 'role' => $this->role, - ]; - } - - public function bsonUnserialize(array $data): void - { - $this->_id = $data['_id']; - $this->username = $data['username']; - $this->role = Role::from($data['role']); - } - } - -Enums are prohibited from implementing -:php:`MongoDB\\BSON\\Unserializable ` and -:php:`MongoDB\\BSON\\Persistable `, since enum cases -have no state and cannot be instantiated like ordinary objects. Pure and backed -enums can, however, implement -:php:`MongoDB\\BSON\\Serializable `, which can be -used to overcome the default behavior whereby backed enums are serialized as -their case value and pure enums cannot be serialized. diff --git a/docs/tutorial/server-selection.txt b/docs/tutorial/server-selection.txt deleted file mode 100644 index 31047d936..000000000 --- a/docs/tutorial/server-selection.txt +++ /dev/null @@ -1,192 +0,0 @@ -=============================== -Server Selection and Monitoring -=============================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Server Selection and Monitoring -------------------------------- - -Before any operation can be executed, the |php-library| must first select a -server from the topology (e.g. replica set, sharded cluster). Selecting a server -requires an accurate view of the topology, so the driver (i.e. ``mongodb`` -extension) regularly monitors the servers to which it is connected. - -In most other drivers, server discovery and monitoring is handled by a -background thread; however, the PHP driver is single-threaded and must therefore -perform monitoring *between* operations initiated by the application. - -Consider the following example application: - -.. code-block:: php - - test; - - /** - * The library creates an internal object for this operation and must select - * a server to use for executing that operation. - * - * If this is the first operation on the underlying libmongoc client, it must - * first discover the topology. It does so by establishing connections to any - * host(s) in the seed list (this may entail TLS and OCSP verification) and - * issuing "hello" commands. - * - * In the case of a replica set, connecting to a single host in the seed list - * should allow the driver to discover all other members in the replica set. - * In the case of a sharded cluster, the driver will start with an initial - * seed list of mongos hosts and, if SRV polling is utilized, may discover - * additional mongos hosts over time. - * - * If the topology was already initialized (i.e. this is not the first - * operation on the client), the driver may still need to perform monitoring - * (i.e. "hello" commands) and refresh its view of the topology. This process - * may entail adding or removing hosts from the topology. - * - * Once the topology has been discovered and any necessary monitoring has - * been performed, the driver may select a server according to the rules - * outlined in the server selection specification (e.g. applying a read - * preference, filtering hosts by latency). - */ - $database->command(['ping' => 1]); - -Although the application consists of only a few lines of PHP, there is actually -quite a lot going on behind the scenes! Interested readers can find this process -discussed in greater detail in the following documents: - -- `Single-threaded Mode `_ in the libmongoc documentation -- `Server Discovery and Monitoring `_ specification -- `Server Selection `_ specification - -Connection String Options -------------------------- - -There are several connection string options relevant to server selection and -monitoring. - -connectTimeoutMS -~~~~~~~~~~~~~~~~ - -``connectTimeoutMS`` specifies the limit for both establishing a connection to -a server *and* the socket timeout for server monitoring (``hello`` commands). -This defaults to 10 seconds for single-threaded drivers such as PHP. - -When a server times out during monitoring, it will not be re-checked until at -least five seconds -(`cooldownMS `_) -have elapsed. This timeout is intended to avoid having single-threaded drivers -block for ``connectTimeoutMS`` on *each* subsequent scan after an error. - -Applications can consider setting this option to slightly more than the greatest -latency among servers in the cluster. For example, if the greatest ``ping`` time -between the PHP application server and a database server is 200ms, it may be -reasonable to specify a timeout of one second. This would allow ample time for -establishing a connection and monitoring an accessible server, while also -significantly reducing the time to detect an inaccessible server. - -heartbeatFrequencyMS -~~~~~~~~~~~~~~~~~~~~ - -``heartbeatFrequencyMS`` determines how often monitoring should occur. This -defaults to 60 seconds for single-threaded drivers and can be set as low as -500ms. - -serverSelectionTimeoutMS -~~~~~~~~~~~~~~~~~~~~~~~~ - -``serverSelectionTimeoutMS`` determines the maximum amount of time to spend in -the server selection loop. This defaults to 30 seconds, but applications will -typically fail sooner if ``serverSelectionTryOnce`` is ``true`` and a smaller -``connectTimeoutMS`` value is in effect. - -The original default was established at a time when replica set elections took -much longer to complete. Applications can consider setting this option to -slightly more than the expected completion time for an election. For example, -:manual:`Replica Set Elections ` states that -elections will not typically exceed 12 seconds, so a 15-second timeout may be -reasonable. Applications connecting to a sharded cluster may consider a smaller -value still, since ``mongos`` insulates the driver from elections. - -That said, ``serverSelectionTimeoutMS`` should generally not be set to a value -smaller than ``connectTimeoutMS``. - -serverSelectionTryOnce -~~~~~~~~~~~~~~~~~~~~~~ - -``serverSelectionTryOnce`` determines whether the driver should give up after -the first failed server selection attempt or continue waiting until -``serverSelectionTimeoutMS`` is reached. PHP defaults to ``true``, which allows -the driver to "fail fast" when a server cannot be selected (e.g. no primary -during a failover). - -The default behavior is generally desirable for a high-traffic web applications, -as it means the worker process will not be blocked in a server selection loop -and can instead return an error response and immediately go on to serve another -request. Additionally, other driver features such as retryable reads and writes -can still enable applications to avoid transient errors such as a failover. - -That said, applications that prioritize resiliency over response time (and -worker pool utilization) may want to specify ``false`` for -``serverSelectionTryOnce``. - -socketCheckIntervalMS -~~~~~~~~~~~~~~~~~~~~~ - -``socketCheckIntervalMS`` determines how often a socket should be checked (using -a ``ping`` command) if it has not been used recently. This defaults to 5 seconds -and is intentionally lower than ``heartbeatFrequencyMS`` to better allow -single-threaded drivers to recover dropped connections. - -socketTimeoutMS -~~~~~~~~~~~~~~~ - -``socketTimeoutMS`` determines the maximum amount of time to spend reading or -writing to a socket. Since server monitoring uses ``connectTimeoutMS`` for its -socket timeouts, ``socketTimeoutMS`` only applies to operations executed by the -application. - -``socketTimeoutMS`` defaults to 5 minutes; however, it's likely that a PHP web -request would be terminated sooner due to -`max_execution_time `_, -which defaults to 30 seconds for web SAPIs. In a CLI environment, where -``max_execution_time`` is unlimited by default, it is more likely that -``socketTimeoutMS`` could be reached. - -.. note:: - - ``socketTimeoutMS`` is not directly related to server selection and - monitoring; however, it is frequently associated with the other options and - therefore bears mentioning. diff --git a/docs/tutorial/stable-api.txt b/docs/tutorial/stable-api.txt deleted file mode 100644 index 1220f0326..000000000 --- a/docs/tutorial/stable-api.txt +++ /dev/null @@ -1,93 +0,0 @@ -========== -Stable API -========== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 1 - :class: singlecol - -Declaring an API Version ------------------------- - -To declare an API version, pass a ``serverApi`` driver option when creating your -client. The value is a -:php:`MongoDB\\Driver\\ServerApi ` instance that -contains API version information. This feature is introduced in MongoDB 5.0, -which will initially support only API version "1". Additional versions may be -introduced in future versions of the server. - -.. code-block:: php - - $serverApi]); - - // Command includes the declared API version - $client->database->collection->find([]); - -.. note:: - - Only declare an API version when connecting to a deployment that has no - pre-5.0 members. Older servers will error when encountering commands with a - declared API version. - -Strict API ----------- - -By default, declaring an API version guarantees behavior for commands that are -part of the stable API, but does not forbid using commands that are not part -of the API version. To only allow commands and options that are part of the -stable API, specify the ``strict`` option when creating the -:php:`MongoDB\\Driver\\ServerApi ` instance: - -.. code-block:: php - - $serverApi]); - - // Will fail as the tailable option is not supported in versioned API - $client->database->collection->find([], ['tailable' => true]); - -Fail on Deprecated Commands ---------------------------- - -The optional ``deprecationErrors`` option causes MongoDB to fail all commands -or behaviors that have been deprecated in the API version. This can be used in -testing to ensure a smooth transition to a future API version. - -.. code-block:: php - - $serverApi]); - -.. note:: - - At the time of this writing, no part of API version "1" has been deprecated. - -Usage with the Command Helper ------------------------------ - -When using the :phpmethod:`MongoDB\\Database::command()` method to run arbitrary -commands, the API version declared to the client is automatically appended to -the command document. Setting any of the ``apiVersion``, ``apiStrict``, or -``apiDeprecationErrors`` command options in the command document and calling -:phpmethod:`MongoDB\\Database::command()` from a client with a declared API -version is not supported and will lead to undefined behavior. diff --git a/docs/tutorial/tailable-cursor.txt b/docs/tutorial/tailable-cursor.txt deleted file mode 100644 index 2c84b080b..000000000 --- a/docs/tutorial/tailable-cursor.txt +++ /dev/null @@ -1,190 +0,0 @@ -.. _php-tailable-cursor: - -========================= -Tailable Cursor Iteration -========================= - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Overview --------- - -When the driver executes a query or command (e.g. -:manual:`aggregate `), results from the operation -are returned via a :php:`MongoDB\\Driver\\Cursor ` -object. The Cursor class implements PHP's :php:`Iterator ` -interface, which allows it to be iterated with ``foreach`` and interface with -any PHP functions that work with :php:`iterables `. Similar to -result objects in other database drivers, cursors in MongoDB only support -forward iteration, which means they cannot be rewound or used with ``foreach`` -multiple times. - -:manual:`Tailable cursors ` are a special type of -MongoDB cursor that allows the client to read some results and then wait until -more documents become available. These cursors are primarily used with -:manual:`Capped Collections ` and -:manual:`Change Streams `. - -While normal cursors can be iterated once with ``foreach``, that approach will -not work with tailable cursors. When ``foreach`` is used with a tailable cursor, -the loop will stop upon reaching the end of the initial result set. Attempting -to continue iteration on the cursor with a second ``foreach`` would throw an -exception, since PHP attempts to rewind the cursor. Therefore, reading from a -tailable cursor will require direct usage of the :php:`Iterator ` API. - -.. note:: - - Before version 1.9.0 of the ``ext-mongodb`` extension, the cursor class does - not implement the :php:`Iterator ` interface. To manually iterate - a cursor using the method below, it must first be wrapped with an - :php:`IteratorIterator `. - -Manually Iterating a Normal Cursor ----------------------------------- - -Before looking at how a tailable cursor can be iterated, we'll start by -examining how the ``Iterator`` methods interact with a normal cursor. - -The following example finds five restaurants and uses ``foreach`` to view the -results: - -.. code-block:: php - - test->restaurants; - - $cursor = $collection->find([], ['limit' => 5]); - - foreach ($cursor as $document) { - var_dump($document); - } - -While this example is quite concise, there is actually quite a bit going on. The -``foreach`` construct begins by rewinding the iterable (``$cursor`` in this -case). It then checks if the current position is valid. If the position is not -valid, the loop ends. Otherwise, the current key and value are accessed -accordingly and the loop body is executed. Assuming a :php:`break ` has -not occurred, the iterator then advances to the next position, control jumps -back to the validity check, and the loop continues. - -With the inner workings of ``foreach`` under our belt, we can now translate the -preceding example to use the Iterator methods directly: - -.. code-block:: php - - test->restaurants; - - $cursor = $collection->find([], ['limit' => 5]); - - $cursor->rewind(); - - while ($cursor->valid()) { - $document = $cursor->current(); - var_dump($document); - $cursor->next(); - } - -.. note:: - - Calling ``$cursor->next()`` after the ``while`` loop naturally ends would - throw an exception, since all results on the cursor have been exhausted. - -The purpose of this example is simply to demonstrate the functional equivalence -between ``foreach`` and manual iteration with PHP's :php:`Iterator ` -API. For normal cursors, there is little reason to manually iterate results -instead of a concise ``foreach`` loop. - -Iterating a Tailable Cursor ---------------------------- - -In order to demonstrate a tailable cursor in action, we'll need two scripts: a -"producer" and a "consumer". The producer script will create a new capped -collection using :phpmethod:`MongoDB\\Database::createCollection()` and proceed -to insert a new document into that collection each second. - -.. code-block:: php - - test; - - $database->createCollection('capped', [ - 'capped' => true, - 'size' => 16777216, - ]); - - $collection = $database->selectCollection('capped'); - - while (true) { - $collection->insertOne(['createdAt' => new MongoDB\BSON\UTCDateTime()]); - sleep(1); - } - -With the producer script still running, we will now execute a consumer script to -read the inserted documents using a tailable cursor, indicated by the -``cursorType`` option to :phpmethod:`MongoDB\\Collection::find()`. We'll start -by using ``foreach`` to illustrate its shortcomings: - -.. code-block:: php - - test->capped; - - $cursor = $collection->find([], [ - 'cursorType' => MongoDB\Operation\Find::TAILABLE_AWAIT, - 'maxAwaitTimeMS' => 100, - ]); - - foreach ($cursor as $document) { - printf("Consumed document created at: %s\n", $document->createdAt); - } - -If you execute this consumer script, you'll notice that it quickly exhausts all -results in the capped collection and then terminates. We cannot add a second -``foreach``, as that would throw an exception when attempting to rewind the -cursor. This is a ripe use case for directly controlling the iteration process -using the :php:`Iterator ` interface. - -.. code-block:: php - - test->capped; - - $cursor = $collection->find([], [ - 'cursorType' => MongoDB\Operation\Find::TAILABLE_AWAIT, - 'maxAwaitTimeMS' => 100, - ]); - - $cursor->rewind(); - - while (true) { - if ($cursor->valid()) { - $document = $cursor->current(); - printf("Consumed document created at: %s\n", $document->createdAt); - } - - $cursor->next(); - } - -Much like the ``foreach`` example, this version on the consumer script will -start by quickly printing all results in the capped collection; however, it will -not terminate upon reaching the end of the initial result set. Since we're -working with a tailable cursor, calling ``next()`` will block and wait for -additional results rather than throw an exception. We will also use ``valid()`` -to check if there is actually data available to read at each step. - -Since we've elected to use a ``TAILABLE_AWAIT`` cursor, the server will delay -its response to the driver for a set amount of time. In this example, we've -requested that the server block for approximately 100 milliseconds by specifying -the ``maxAwaitTimeMS`` option to :phpmethod:`MongoDB\\Collection::find()`. diff --git a/docs/upgrade.txt b/docs/upgrade.txt deleted file mode 100644 index 4a67b7fbe..000000000 --- a/docs/upgrade.txt +++ /dev/null @@ -1,370 +0,0 @@ -=========================== -Legacy Driver Upgrade Guide -=========================== - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Overview --------- - -The |php-library| and underlying :php:`mongodb extension ` have notable -API differences from the legacy ``mongo`` extension. This page will summarize -those differences for the benefit of those upgrading from the legacy driver. - -Additionally, a community-developed `mongo-php-adapter -`_ library exists, which -implements the ``mongo`` extension API using this library and the new driver. -While this adapter library is not officially supported by MongoDB, it does bear -mentioning. - -BSON ----- - -Type Classes -~~~~~~~~~~~~ - -When upgrading from the legacy driver, type classes such as MongoId must be -replaced with classes in the -`MongoDB\\BSON namespace `_. The -new driver also introduces interfaces for its BSON types, which should be -preferred if applications need to type hint against BSON values. - -The following table lists all legacy classes alongside the equivalent class in -the new driver. - -.. list-table:: - :header-rows: 1 - - * - Legacy class - - BSON type class - - BSON type interface - - * - MongoId - - :php:`MongoDB\\BSON\\ObjectId ` - - :php:`MongoDB\\BSON\\ObjectIdInterface ` - - * - MongoCode - - :php:`MongoDB\\BSON\\Javascript ` - - :php:`MongoDB\\BSON\\JavascriptInterface ` - - * - MongoDate - - :php:`MongoDB\\BSON\\UTCDateTime ` - - :php:`MongoDB\\BSON\\UTCDateTimeInterface ` - - * - MongoRegex - - :php:`MongoDB\\BSON\\Regex ` - - :php:`MongoDB\\BSON\\RegexInterface ` - - * - MongoBinData - - :php:`MongoDB\\BSON\\Binary ` - - :php:`MongoDB\\BSON\\BinaryInterface ` - - * - MongoInt32 - - Not implemented. [1]_ - - - - * - MongoInt64 - - :php:`MongoDB\\BSON\\Int64 ` - - Not implemented. [2]_ - - * - MongoDBRef - - Not implemented. [3]_ - - - - * - MongoMinKey - - :php:`MongoDB\\BSON\\MinKey ` - - :php:`MongoDB\\BSON\\MinKeyInterface ` - - * - MongoMaxKey - - :php:`MongoDB\\BSON\\MaxKey ` - - :php:`MongoDB\\BSON\\MaxKeyInterface ` - - * - MongoTimestamp - - :php:`MongoDB\\BSON\\Timestamp ` - - :php:`MongoDB\\BSON\\TimestampInterface ` - -.. [1] The new driver does not implement an equivalent class for MongoInt32. - When decoding BSON, 32-bit integers will always be represented as a PHP - integer. When encoding BSON, PHP integers will encode as either a 32-bit or - 64-bit integer depending on their value. - -.. [2] :php:`MongoDB\\BSON\\Int64 ` does not have an - interface defined. The new driver does not allow applications to instantiate - this type (i.e. its constructor is private) and it is only created during - BSON decoding when a 64-bit integer cannot be represented as a PHP integer on - a 32-bit platform. - -.. [3] The new driver does not implement an equivalent class for MongoDBRef - since :manual:`DBRefs ` are merely a BSON - document with a particular structure and not a proper BSON type. The new - driver also does not provide any helpers for working with DBRef objects, - since their use is not encouraged. - -Emulating the Legacy Driver -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The legacy ``mongo`` extension returned both BSON documents and arrays as PHP -arrays. While PHP arrays are convenient to work with, this behavior was -problematic: - -- Different BSON types could deserialize to the same PHP value (e.g. - ``{"0": "foo"}`` and ``["foo"]``), which made it impossible to infer the - original BSON type. - -- Numerically-indexed PHP arrays would be serialized as BSON documents if there - was a gap in their key sequence. Such gaps were easily caused by unsetting a - key to remove an element and forgetting to numerically reindex the array. - -The |php-library|'s :phpclass:`BSONDocument ` and -:phpclass:`BSONArray ` classes address these concerns -by preserving the BSON type information during serialization and -deserialization; however, some users may still prefer the legacy behavior. If -desired, you can use the ``typeMap`` option to have the library return -everything as a PHP array: - -.. code-block:: php - - [ - 'array' => 'array', - 'document' => 'array', - 'root' => 'array', - ], - ] - ); - - $document = $client->test->zips->findOne(['_id' => '94301']); - - var_dump($document); - -The above example would output something similar to: - -.. code-block:: php - - array(5) { - ["_id"]=> - string(5) "94301" - ["city"]=> - string(9) "PALO ALTO" - ["loc"]=> - array(2) { - [0]=> - float(-122.149685) - [1]=> - float(37.444324) - } - ["pop"]=> - int(15965) - ["state"]=> - string(2) "CA" - } - -Collection API --------------- - -This library's :phpclass:`MongoDB\\Collection` class implements MongoDB's -cross-driver `CRUD -`_ -and `Index Management -`_ -specifications. Although some method names have changed in accordance with the -new specifications, the new class provides the same functionality as the legacy -driver's MongoCollection class with some notable exceptions. - -A guiding principle in designing the new APIs was that explicit method names are -preferable to overloaded terms found in the old API. For instance, -``MongoCollection::save()`` and ``MongoCollection::findAndModify()`` have -different modes of operation, depending on their arguments. Methods were also -split to distinguish between :manual:`updating specific fields -` and :manual:`full-document replacement -`. - -The following table lists all legacy methods alongside the -equivalent method(s) in the new driver. - -.. list-table:: - :header-rows: 1 - - * - MongoCollection method - - :phpclass:`MongoDB\\Collection` method(s) - - * - ``MongoCollection::aggregate()`` - - :phpmethod:`MongoDB\\Collection::aggregate()` - - * - ``MongoCollection::aggregateCursor()`` - - :phpmethod:`MongoDB\\Collection::aggregate()` - - * - ``MongoCollection::batchInsert()`` - - :phpmethod:`MongoDB\\Collection::insertMany()` - - * - ``MongoCollection::count()`` - - :phpmethod:`MongoDB\\Collection::count()` - - * - ``MongoCollection::createDBRef()`` - - Not yet implemented. [3]_ - - * - ``MongoCollection::createIndex()`` - - :phpmethod:`MongoDB\\Collection::createIndex()` - - * - ``MongoCollection::deleteIndex()`` - - :phpmethod:`MongoDB\\Collection::dropIndex()` - - * - ``MongoCollection::deleteIndexes()`` - - :phpmethod:`MongoDB\\Collection::dropIndexes()` - - * - ``MongoCollection::drop()`` - - :phpmethod:`MongoDB\\Collection::drop()` - - * - ``MongoCollection::distinct()`` - - :phpmethod:`MongoDB\\Collection::distinct()` - - * - ``MongoCollection::ensureIndex()`` - - :phpmethod:`MongoDB\\Collection::createIndex()` - - * - ``MongoCollection::find()`` - - :phpmethod:`MongoDB\\Collection::find()` - - * - ``MongoCollection::findAndModify()`` - - :phpmethod:`MongoDB\\Collection::findOneAndDelete()`, - :phpmethod:`MongoDB\\Collection::findOneAndReplace()`, and - :phpmethod:`MongoDB\\Collection::findOneAndUpdate()` - - * - ``MongoCollection::findOne()`` - - :phpmethod:`MongoDB\\Collection::findOne()` - - * - ``MongoCollection::getDBRef()`` - - Not implemented. [3]_ - - * - ``MongoCollection::getIndexInfo()`` - - :phpmethod:`MongoDB\\Collection::listIndexes()` - - * - ``MongoCollection::getName()`` - - :phpmethod:`MongoDB\\Collection::getCollectionName()` - - * - ``MongoCollection::getReadPreference()`` - - :phpmethod:`MongoDB\\Collection::getReadPreference()` - - * - ``MongoCollection::getSlaveOkay()`` - - Not implemented. - - * - ``MongoCollection::getWriteConcern()`` - - :phpmethod:`MongoDB\\Collection::getWriteConcern()` - - * - ``MongoCollection::group()`` - - Not implemented. Use :phpmethod:`MongoDB\\Database::command()`. See - `Group Command Helper`_ for an example. - - * - ``MongoCollection::insert()`` - - :phpmethod:`MongoDB\\Collection::insertOne()` - - * - ``MongoCollection::parallelCollectionScan()`` - - Not implemented. - - * - ``MongoCollection::remove()`` - - :phpmethod:`MongoDB\\Collection::deleteMany()` and - :phpmethod:`MongoDB\\Collection::deleteOne()` - - * - ``MongoCollection::save()`` - - :phpmethod:`MongoDB\\Collection::insertOne()` or - :phpmethod:`MongoDB\\Collection::replaceOne()` with the ``upsert`` - option. - - * - ``MongoCollection::setReadPreference()`` - - Not implemented. Use :phpmethod:`MongoDB\\Collection::withOptions()`. - - * - ``MongoCollection::setSlaveOkay()`` - - Not implemented. - - * - ``MongoCollection::setWriteConcern()`` - - Not implemented. Use :phpmethod:`MongoDB\\Collection::withOptions()`. - - * - ``MongoCollection::update()`` - - :phpmethod:`MongoDB\\Collection::replaceOne()`, - :phpmethod:`MongoDB\\Collection::updateMany()`, and - :phpmethod:`MongoDB\\Collection::updateOne()`. - - * - ``MongoCollection::validate()`` - - Not implemented. - -Accessing IDs of Inserted Documents -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -In the legacy driver, ``MongoCollection::insert()``, -``MongoCollection::batchInsert()``, and ``MongoCollection::save()`` (when -inserting) would modify their input argument by injecting an ``_id`` key with a -generated ObjectId (i.e. MongoId object). This behavior was a bit of a hack, as -it did not rely on the argument being :php:`passed by reference -`; instead, it directly modified memory through the -extension API and could not be implemented in PHP userland. As such, it is no -longer done in the new driver and library. - -IDs of inserted documents (whether generated or not) may be accessed through the -following methods on the write result objects: - -- :phpmethod:`MongoDB\\InsertOneResult::getInsertedId()` for - :phpmethod:`MongoDB\\Collection::insertOne()` -- :phpmethod:`MongoDB\\InsertManyResult::getInsertedIds()` for - :phpmethod:`MongoDB\\Collection::insertMany()` -- :phpmethod:`MongoDB\\BulkWriteResult::getInsertedIds()` for - :phpmethod:`MongoDB\\Collection::bulkWrite()` - -Bulk Write Operations -~~~~~~~~~~~~~~~~~~~~~ - -The legacy driver's MongoWriteBatch classes have been replaced with a -general-purpose :phpmethod:`MongoDB\\Collection::bulkWrite()` method. Whereas -the legacy driver only allowed bulk operations of the same type, the new method -allows operations to be mixed (e.g. inserts, updates, and deletes). - -MongoCollection::save() Removed -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -``MongoCollection::save()``, which was syntactic sugar for an insert or upsert -operation, has been removed in favor of explicitly using -:phpmethod:`MongoDB\\Collection::insertOne` or -:phpmethod:`MongoDB\\Collection::replaceOne` (with the ``upsert`` option). - -While the ``save`` method does have its uses for interactive environments, such -as the ``mongo`` shell, it was intentionally excluded from the -`CRUD specification `_ -for language drivers. Generally, application code should know if the document -has an identifier and be able to explicitly insert or replace the document and -handle the returned :phpclass:`MongoDB\\InsertOneResult` or -:phpclass:`MongoDB\\UpdateResult`, respectively. This also helps avoid -inadvertent and potentially dangerous :manual:`full-document replacements -`. - -Group Command Helper -~~~~~~~~~~~~~~~~~~~~ - -:phpclass:`MongoDB\\Collection` does not have a helper method for the -:manual:`group ` command. The following example -demonstrates how to execute a group command using the -:phpmethod:`MongoDB\\Database::command()` method: - -.. code-block:: php - - selectDatabase('db_name'); - $cursor = $database->command([ - 'group' => [ - 'ns' => 'collection_name', - 'key' => ['field_name' => 1], - 'initial' => ['total' => 0], - '$reduce' => new MongoDB\BSON\Javascript('...'), - ], - ]); - - $resultDocument = $cursor->toArray()[0]; diff --git a/examples/aggregate.php b/examples/aggregate.php index 7f9da6cc3..6f3676e3c 100644 --- a/examples/aggregate.php +++ b/examples/aggregate.php @@ -1,23 +1,22 @@ toRelaxedExtendedJSON(); } $client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); @@ -28,7 +27,7 @@ function toJSON(object $document): string $documents = []; for ($i = 0; $i < 100; $i++) { - $documents[] = ['randomValue' => rand(0, 1000)]; + $documents[] = ['randomValue' => random_int(0, 1000)]; } $collection->insertMany($documents); diff --git a/examples/atlas_search.php b/examples/atlas_search.php new file mode 100644 index 000000000..eca894d4a --- /dev/null +++ b/examples/atlas_search.php @@ -0,0 +1,133 @@ +selectCollection($databaseName, $collectionName); + +$count = $collection->estimatedDocumentCount(); +if ($count === 0) { + echo 'This example requires the "', $databaseName, '" database with the "', $collectionName, '" collection.', "\n"; + echo 'Load the sample dataset in your MongoDB Atlas cluster before running this example:', "\n"; + echo ' https://www.mongodb.com/docs/atlas/sample-data/', "\n"; + exit(1); +} + +// Delete the index if it already exists +$indexes = iterator_to_array($collection->listSearchIndexes()); +foreach ($indexes as $index) { + if ($index->name === 'default') { + echo "The index already exists. Dropping it.\n"; + $collection->dropSearchIndex($index->name); + + // Wait for the index to be deleted. + wait(function () use ($collection) { + echo '.'; + foreach ($collection->listSearchIndexes() as $index) { + if ($index->name === 'default') { + return false; + } + } + + return true; + }); + } +} + +// Create the search index +echo "\nCreating the index.\n"; +$collection->createSearchIndex( + /* The index definition requires a mapping + * See: https://www.mongodb.com/docs/atlas/atlas-search/define-field-mappings/ */ + ['mappings' => ['dynamic' => true]], + // "default" is the default index name, this config can be omitted. + ['name' => 'default'], +); + +// Wait for the index to be queryable. +wait(function () use ($collection) { + echo '.'; + foreach ($collection->listSearchIndexes() as $index) { + if ($index->name === 'default') { + return $index->queryable; + } + } + + return false; +}); + +// Perform a text search +echo "\n", 'Performing a text search...', "\n"; +$results = $collection->aggregate([ + [ + '$search' => [ + 'index' => 'default', + 'text' => [ + 'query' => 'view beach ocean', + 'path' => ['name'], + ], + ], + ], + ['$project' => ['name' => 1, 'description' => 1]], + ['$limit' => 10], +])->toArray(); + +foreach ($results as $document) { + echo ' - ', $document['name'], "\n"; +} + +echo "\n", 'Enjoy MongoDB Atlas Search!', "\n\n"; + +/** + * This function waits until the callback returns true or the timeout is reached. + */ +function wait(Closure $callback): void +{ + $timeout = hrtime()[0] + WAIT_TIMEOUT_SEC; + while (hrtime()[0] < $timeout) { + if ($callback()) { + return; + } + + sleep(5); + } + + throw new RuntimeException('Time out'); +} diff --git a/examples/bulk.php b/examples/bulk.php index 316594f00..3c2a56cfe 100644 --- a/examples/bulk.php +++ b/examples/bulk.php @@ -1,23 +1,22 @@ toRelaxedExtendedJSON(); } $client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); @@ -68,7 +67,7 @@ function toJSON(object $document): string ['x' => 10], // Document ], ], - ] + ], ); $cursor = $collection->find([]); diff --git a/examples/changestream.php b/examples/changestream.php index 8c871f59c..e653b0220 100644 --- a/examples/changestream.php +++ b/examples/changestream.php @@ -1,15 +1,14 @@ toRelaxedExtendedJSON(); } // Change streams require a replica set or sharded cluster @@ -53,7 +52,7 @@ function toJSON(object $document): string $changeStream->next(); if (time() - $startTime > 3) { - printf("Aborting after 3 seconds...\n"); + echo "Aborting after 3 seconds...\n"; break; } } diff --git a/examples/command_logger.php b/examples/command_logger.php index 9d3f7c0fe..41ead8496 100644 --- a/examples/command_logger.php +++ b/examples/command_logger.php @@ -1,8 +1,11 @@ toRelaxedExtendedJSON(); } class CommandLogger implements CommandSubscriber { - public function commandStarted(CommandStartedEvent $event): void + /** @param Closure(object):void $handleOutput */ + public function __construct(private readonly Closure $handleOutput) { - printf("%s command started\n", $event->getCommandName()); + } - printf("command: %s\n", toJson($event->getCommand())); - printf("\n"); + public function commandStarted(CommandStartedEvent $event): void + { + $this->handleOutput->__invoke($event); } public function commandSucceeded(CommandSucceededEvent $event): void { - printf("%s command succeeded\n", $event->getCommandName()); - printf("reply: %s\n", toJson($event->getReply())); - printf("\n"); + $this->handleOutput->__invoke($event); } public function commandFailed(CommandFailedEvent $event): void { - printf("%s command failed\n", $event->getCommandName()); - printf("reply: %s\n", toJson($event->getReply())); - - $exception = $event->getError(); - printf("exception: %s\n", get_class($exception)); - printf("exception.code: %d\n", $exception->getCode()); - printf("exception.message: %s\n", $exception->getMessage()); - printf("\n"); + $this->handleOutput->__invoke($event); } } $client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); -$client->getManager()->addSubscriber(new CommandLogger()); +$handleOutput = function (object $event): void { + switch ($event::class) { + case CommandStartedEvent::class: + printf("%s command started\n", $event->getCommandName()); + printf("command: %s\n", toJson($event->getCommand())); + break; + case CommandSucceededEvent::class: + printf("%s command succeeded\n", $event->getCommandName()); + printf("reply: %s\n", toJson($event->getReply())); + break; + case CommandFailedEvent::class: + printf("%s command failed\n", $event->getCommandName()); + printf("reply: %s\n", toJson($event->getReply())); + + $exception = $event->getError(); + printf("exception: %s\n", $exception::class); + printf("exception.code: %d\n", $exception->getCode()); + printf("exception.message: %s\n", $exception->getMessage()); + break; + default: + throw new Exception('Event type not supported'); + } + + echo "\n"; +}; + +$client->addSubscriber(new CommandLogger($handleOutput)); $collection = $client->test->command_logger; $collection->drop(); @@ -69,7 +88,7 @@ public function commandFailed(CommandFailedEvent $event): void $collection->updateMany( ['x' => ['$gt' => 1]], - ['$set' => ['y' => 1]] + ['$set' => ['y' => 1]], ); $cursor = $collection->find([], ['batchSize' => 2]); diff --git a/examples/gridfs_stream.php b/examples/gridfs_stream.php new file mode 100644 index 000000000..60f102707 --- /dev/null +++ b/examples/gridfs_stream.php @@ -0,0 +1,54 @@ +test->selectGridFSBucket(['disableMD5' => true]); + +// Open a stream for writing, similar to fopen with mode 'w' +$stream = $bucket->openUploadStream('hello.txt'); + +for ($i = 0; $i < 1_000_000; $i++) { + fwrite($stream, 'Hello line ' . $i . "\n"); +} + +// Last data are flushed to the server when the stream is closed +fclose($stream); + +// Open a stream for reading, similar to fopen with mode 'r' +$stream = $bucket->openDownloadStreamByName('hello.txt'); + +$size = 0; +while (! feof($stream)) { + $data = fread($stream, 2 ** 10); + $size += strlen($data); +} + +echo 'Read a total of ' . $size . ' bytes' . "\n"; + +// Retrieve the file ID to delete it +$id = $bucket->getFileIdForStream($stream); +assert($id instanceof ObjectId); +$bucket->delete($id); + +echo 'Deleted file with ID: ' . $id . "\n"; diff --git a/examples/gridfs_stream_wrapper.php b/examples/gridfs_stream_wrapper.php new file mode 100644 index 000000000..d8e73ea4d --- /dev/null +++ b/examples/gridfs_stream_wrapper.php @@ -0,0 +1,58 @@ +/ + */ + +declare(strict_types=1); + +namespace MongoDB\Examples; + +use MongoDB\Client; + +use function file_exists; +use function file_get_contents; +use function file_put_contents; +use function getenv; +use function stream_context_create; + +require __DIR__ . '/../vendor/autoload.php'; + +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/'); +// Disable MD5 computation for faster uploads, this feature is deprecated +$bucket = $client->test->selectGridFSBucket(['disableMD5' => true]); +$bucket->drop(); + +// Register the alias "mybucket" for default bucket of the "test" database +$bucket->registerGlobalStreamWrapperAlias('mybucket'); + +echo 'File exists: '; +echo file_exists('gridfs://mybucket/hello.txt') ? 'yes' : 'no'; +echo "\n"; + +echo 'Writing file'; +file_put_contents('gridfs://mybucket/hello.txt', 'Hello, GridFS!'); +echo "\n"; + +echo 'File exists: '; +echo file_exists('gridfs://mybucket/hello.txt') ? 'yes' : 'no'; +echo "\n"; + +echo 'Reading file: '; +echo file_get_contents('gridfs://mybucket/hello.txt'); +echo "\n"; + +echo 'Writing new version of the file'; +file_put_contents('gridfs://mybucket/hello.txt', 'Hello, GridFS! (v2)'); +echo "\n"; + +echo 'Reading new version of the file: '; +echo file_get_contents('gridfs://mybucket/hello.txt'); +echo "\n"; + +echo 'Reading previous version of the file: '; +$context = stream_context_create(['gridfs' => ['revision' => -2]]); +echo file_get_contents('gridfs://mybucket/hello.txt', false, $context); +echo "\n"; diff --git a/examples/gridfs_upload.php b/examples/gridfs_upload.php new file mode 100644 index 000000000..64a030352 --- /dev/null +++ b/examples/gridfs_upload.php @@ -0,0 +1,49 @@ +test->selectGridFSBucket(['disableMD5' => true]); + +// Create an in-memory stream, this can be any stream source like STDIN or php://input for web requests +$stream = fopen('php://temp', 'w+'); +fwrite($stream, 'Hello world!'); +rewind($stream); + +// Upload to GridFS from the stream +$id = $gridfs->uploadFromStream('hello.txt', $stream); +assert($id instanceof ObjectId); +echo 'Inserted file with ID: ', $id, "\n"; +fclose($stream); + +// Download the file and print the contents directly to an in-memory stream, chunk by chunk +$stream = fopen('php://temp', 'w+'); +$gridfs->downloadToStreamByName('hello.txt', $stream); +rewind($stream); +echo 'File contents: ', stream_get_contents($stream), "\n"; + +// Delete the file +$gridfs->delete($id); + +echo 'Deleted file with ID: ', $id, "\n"; diff --git a/examples/persistable.php b/examples/persistable.php index e4a5c95e9..2a4c3614e 100644 --- a/examples/persistable.php +++ b/examples/persistable.php @@ -1,34 +1,30 @@ */ - public $emails = []; + public array $emails = []; - public function __construct(string $name) + public function __construct(public string $name) { $this->id = new ObjectId(); - $this->name = $name; } public function getId(): ObjectId @@ -36,7 +32,7 @@ public function getId(): ObjectId return $this->id; } - public function bsonSerialize(): object + public function bsonSerialize(): stdClass { return (object) [ '_id' => $this->id, @@ -65,19 +61,11 @@ public function bsonUnserialize(array $data): void class PersistableEmail implements Persistable { - /** @var string */ - public $type; - - /** @var string */ - public $address; - - public function __construct(string $type, string $address) + public function __construct(public string $type, public string $address) { - $this->type = $type; - $this->address = $address; } - public function bsonSerialize(): object + public function bsonSerialize(): stdClass { return (object) [ 'type' => $this->type, @@ -105,5 +93,4 @@ public function bsonUnserialize(array $data): void $foundEntry = $collection->findOne([]); -/** @psalm-suppress ForbiddenCode */ -var_dump($foundEntry); +print_r($foundEntry); diff --git a/examples/sdam_logger.php b/examples/sdam_logger.php new file mode 100644 index 000000000..fe4d02ea6 --- /dev/null +++ b/examples/sdam_logger.php @@ -0,0 +1,176 @@ +toRelaxedExtendedJSON(); +} + +class SDAMLogger implements SDAMSubscriber +{ + /** @param Closure(object):void $handleOutput */ + public function __construct(private readonly Closure $handleOutput) + { + } + + public function serverChanged(ServerChangedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function serverClosed(ServerClosedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function serverHeartbeatFailed(ServerHeartbeatFailedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function serverHeartbeatStarted(ServerHeartbeatStartedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function serverHeartbeatSucceeded(ServerHeartbeatSucceededEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function serverOpening(ServerOpeningEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function topologyChanged(TopologyChangedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function topologyClosed(TopologyClosedEvent $event): void + { + $this->handleOutput->__invoke($event); + } + + public function topologyOpening(TopologyOpeningEvent $event): void + { + $this->handleOutput->__invoke($event); + } +} + +/* Note: TopologyClosedEvent can only be observed for non-persistent clients. + * Persistent clients are destroyed in GSHUTDOWN, long after any PHP objects + * (including subscribers) are freed. */ +$client = new Client(getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1/', [], ['disableClientPersistence' => true]); + +$handleOutput = function (object $event): void { + switch ($event::class) { + case ServerChangedEvent::class: + printf( + "serverChanged: %s:%d changed from %s to %s\n", + $event->getHost(), + $event->getPort(), + $event->getPreviousDescription()->getType(), + $event->getNewDescription()->getType(), + ); + + printf("previous hello response: %s\n", toJson($event->getPreviousDescription()->getHelloResponse())); + printf("new hello response: %s\n", toJson($event->getNewDescription()->getHelloResponse())); + break; + case ServerClosedEvent::class: + printf( + "serverClosed: %s:%d was removed from topology %s\n", + $event->getHost(), + $event->getPort(), + $event->getTopologyId()->__toString(), + ); + break; + case ServerHeartbeatFailedEvent::class: + printf( + "serverHeartbeatFailed: %s:%d heartbeat failed after %dµs\n", + $event->getHost(), + $event->getPort(), + $event->getDurationMicros(), + ); + + $error = $event->getError(); + + printf("error: %s(%d): %s\n", $error::class, $error->getCode(), $error->getMessage()); + break; + case ServerHeartbeatStartedEvent::class: + printf( + "serverHeartbeatStarted: %s:%d heartbeat started\n", + $event->getHost(), + $event->getPort(), + ); + break; + case ServerHeartbeatSucceededEvent::class: + printf( + "serverHeartbeatSucceeded: %s:%d heartbeat succeeded after %dµs\n", + $event->getHost(), + $event->getPort(), + $event->getDurationMicros(), + ); + + printf("reply: %s\n", toJson($event->getReply())); + break; + case ServerOpeningEvent::class: + printf( + "serverOpening: %s:%d was added to topology %s\n", + $event->getHost(), + $event->getPort(), + $event->getTopologyId()->__toString(), + ); + break; + case TopologyChangedEvent::class: + printf( + "topologyChanged: %s changed from %s to %s\n", + $event->getTopologyId()->__toString(), + $event->getPreviousDescription()->getType(), + $event->getNewDescription()->getType(), + ); + break; + case TopologyClosedEvent::class: + printf("topologyClosed: %s was closed\n", $event->getTopologyId()->__toString()); + break; + case TopologyOpeningEvent::class: + printf("topologyOpening: %s was opened\n", $event->getTopologyId()->__toString()); + break; + default: + throw new Exception('Event type not supported'); + } + + echo "\n"; +}; + +$client->getManager()->addSubscriber(new SDAMLogger($handleOutput)); + +$client->test->command(['ping' => 1]); + +/* Events dispatched during mongoc_client_destroy can only be observed before + * RSHUTDOWN. Observing TopologyClosedEvent requires using a non-persistent + * client and freeing it before the script ends. */ +unset($client); diff --git a/examples/typemap.php b/examples/typemap.php index 51097bc90..304bd278c 100644 --- a/examples/typemap.php +++ b/examples/typemap.php @@ -1,7 +1,7 @@ */ - private $emails; + private array $emails; private function __construct() { @@ -64,11 +62,9 @@ public function bsonUnserialize(array $data): void class TypeMapEmail implements Unserializable { - /** @var string */ - private $type; + private string $type; - /** @var string */ - private $address; + private string $address; private function __construct() { @@ -116,5 +112,4 @@ public function bsonUnserialize(array $data): void $entry = $collection->findOne([], ['typeMap' => $typeMap]); -/** @psalm-suppress ForbiddenCode */ -var_dump($entry); +print_r($entry); diff --git a/examples/with_transaction.php b/examples/with_transaction.php index 15eba6524..0b583ac6d 100644 --- a/examples/with_transaction.php +++ b/examples/with_transaction.php @@ -1,16 +1,15 @@ toRelaxedExtendedJSON(); } // Transactions require a replica set (MongoDB >= 4.0) or sharded cluster (MongoDB >= 4.2) @@ -37,13 +36,13 @@ function toJSON(object $document): string ['x' => 2], ['x' => 3], ], - ['session' => $session] + ['session' => $session], ); $collection->updateMany( ['x' => ['$gt' => 1]], ['$set' => ['y' => 1]], - ['session' => $session] + ['session' => $session], ); }; diff --git a/generator/README.md b/generator/README.md new file mode 100644 index 000000000..02a3be4ec --- /dev/null +++ b/generator/README.md @@ -0,0 +1,49 @@ +# Code Generator for MongoDB PHP Library + +This subproject is used to generate the code that is committed to the repository. +The `generator` directory is not included in `mongodb/mongodb` package and is not installed by Composer. + +## Contributing + +Updating the generated code can be done only by modifying the code generator, or its configuration. + +To run the generator, you need to have PHP 8.1+ installed and Composer. + +1. Move to the `generator` directory: `cd generator` +2. Install dependencies: `composer install` +3. Run the generator: `./generate` + +## Configuration + +The `generator/config/*.yaml` files contains the list of operators and stages that are supported by the library. + +### Arguments + +| Field | Type | Description | +|---------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `name` | `string` | The name of the argument. If it starts with `$`, the dollar is trimmed from the class property name | +| `type` | list of `string` | The list of accepted types | +| `description` | `string` | The description of the argument from MongoDB's documentation | +| `optional` | `boolean` | Whether the argument is optional or not | +| `valueMin` | `number` | The minimum value for a numeric argument | +| `valueMax` | `number` | The maximum value for a numeric argument | +| `variadic` | `string` | If sent, the argument is variadic. Defines the format `array` for a list or `object` for a map | +| `variadicMin` | `integer` | The minimum number of arguments for a variadic parameter | +| `default` | `scalar` or `array` | The default value for the argument | +| `mergeObject` | `bool` | Default `false`. If `true`, the value must be an object and the properties of the value object are merged into the parent operator. `$group` stage uses it for the fields | + +### Test pipelines + +Each operator can contain a `tests` section with a list if pipelines. To represent specific BSON objects, it is necessary to use Yaml tags: + +| BSON Type | Example | +|-------------|--------------------------------------------------------| +| Regex | `!bson_regex '^abc'`
`!bson_regex ['^abc', 'i']` | +| Int64 | `!bson_int64 '123456789'` | +| Decimal128 | `!bson_decimal128 '0.9'` | +| UTCDateTime | `!bson_utcdatetime 0` | +| ObjectId | `!bson_ObjectId '5a9427648b0beebeb69589a1` | +| Binary | `!bson_binary 'IA=='` | +| Binary UUID | `!bson_uuid 'fac32260-b511-4c69-8485-a2be5b7dda9e'` | + +To add new test cases to operators, you can get inspiration from the official MongoDB documentation and use the `generator/js2yaml.html` web page to manually convert a pipeline array from JS to Yaml. diff --git a/generator/composer.json b/generator/composer.json new file mode 100644 index 000000000..c4dedcfb7 --- /dev/null +++ b/generator/composer.json @@ -0,0 +1,32 @@ +{ + "name": "mongodb/code-generator", + "type": "project", + "repositories": [ + { + "type": "path", + "url": "..", + "symlink": true + } + ], + "replace": { + "symfony/polyfill-php80": "*", + "symfony/polyfill-php81": "*" + }, + "require": { + "mongodb/mongodb": "@dev", + "nette/php-generator": "^4.1.5", + "nikic/php-parser": "^5", + "symfony/console": "^7", + "symfony/finder": "^7", + "symfony/yaml": "^7" + }, + "license": "Apache-2.0", + "autoload": { + "psr-4": { + "MongoDB\\CodeGenerator\\": "src/" + } + }, + "config": { + "sort-packages": true + } +} diff --git a/generator/config/accumulator/accumulator.yaml b/generator/config/accumulator/accumulator.yaml new file mode 100644 index 000000000..9cfdb6b53 --- /dev/null +++ b/generator/config/accumulator/accumulator.yaml @@ -0,0 +1,132 @@ +# $schema: ../schema.json +name: $accumulator +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/' +type: + - accumulator +encode: object +description: | + Defines a custom accumulator function. + New in MongoDB 4.4. +arguments: + - + name: init + type: + - javascript + description: | + Function used to initialize the state. The init function receives its arguments from the initArgs array expression. You can specify the function definition as either BSON type Code or String. + - + name: initArgs + type: + - resolvesToArray + optional: true + description: | + Arguments passed to the init function. + - + name: accumulate + type: + - javascript + description: | + Function used to accumulate documents. The accumulate function receives its arguments from the current state and accumulateArgs array expression. The result of the accumulate function becomes the new state. You can specify the function definition as either BSON type Code or String. + - + name: accumulateArgs + type: + - resolvesToArray + description: | + Arguments passed to the accumulate function. You can use accumulateArgs to specify what field value(s) to pass to the accumulate function. + - + name: merge + type: + - javascript + description: | + Function used to merge two internal states. merge must be either a String or Code BSON type. merge returns the combined result of the two merged states. For information on when the merge function is called, see Merge Two States with $merge. + - + name: finalize + type: + - javascript + optional: true + description: | + Function used to update the result of the accumulation. + - + name: lang + type: + - string + description: | + The language used in the $accumulator code. + +tests: + - + name: 'Use $accumulator to Implement the $avg Operator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/#use--accumulator-to-implement-the--avg-operator' + pipeline: + - + $group: + _id: '$author' + avgCopies: + $accumulator: + init: + $code: |- + function() { + return { count: 0, sum: 0 } + } + accumulate: + $code: |- + function(state, numCopies) { + return { count: state.count + 1, sum: state.sum + numCopies } + } + accumulateArgs: [ "$copies" ] + merge: + $code: |- + function(state1, state2) { + return { + count: state1.count + state2.count, + sum: state1.sum + state2.sum + } + } + finalize: + $code: |- + function(state) { + return (state.sum / state.count) + } + lang: 'js' + + - + name: 'Use initArgs to Vary the Initial State by Group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/#use-initargs-to-vary-the-initial-state-by-group' + pipeline: + - + $group: + _id: + city: '$city' + restaurants: + $accumulator: + init: + $code: |- + function(city, userProfileCity) { + return { max: city === userProfileCity ? 3 : 1, restaurants: [] } + } + initArgs: + - '$city' + - 'Bettles' + accumulate: + $code: |- + function(state, restaurantName) { + if (state.restaurants.length < state.max) { + state.restaurants.push(restaurantName); + } + return state; + } + accumulateArgs: ['$name'] + merge: + $code: |- + function(state1, state2) { + return { + max: state1.max, + restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max) + } + } + finalize: + $code: |- + function(state) { + return state.restaurants + } + lang: 'js' diff --git a/generator/config/accumulator/addToSet.yaml b/generator/config/accumulator/addToSet.yaml new file mode 100644 index 000000000..9566899eb --- /dev/null +++ b/generator/config/accumulator/addToSet.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $addToSet +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/' +type: + - accumulator + - window +encode: single +description: | + Returns an array of unique expression values for each group. Order of the array elements is undefined. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression + +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/#use-in--group-stage' + pipeline: + - $group: + _id: + day: + $dayOfYear: + date: '$date' + year: + $year: + date: '$date' + itemsSold: + $addToSet: '$item' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + cakeTypesForState: + $addToSet: '$type' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/avg.yaml b/generator/config/accumulator/avg.yaml new file mode 100644 index 000000000..3777bbf98 --- /dev/null +++ b/generator/config/accumulator/avg.yaml @@ -0,0 +1,45 @@ +# $schema: ../schema.json +name: $avg +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/' +type: + - accumulator + - window +encode: single +description: | + Returns an average of numerical values. Ignores non-numeric values. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#use-in--group-stage' + pipeline: + - $group: + _id: '$item' + avgAmount: + $avg: + $multiply: + - '$price' + - '$quantity' + avgQuantity: + $avg: '$quantity' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + averageQuantityForState: + $avg: '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/bottom.yaml b/generator/config/accumulator/bottom.yaml new file mode 100644 index 000000000..1e363d193 --- /dev/null +++ b/generator/config/accumulator/bottom.yaml @@ -0,0 +1,55 @@ +# $schema: ../schema.json +name: $bottom +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottom/' +type: + - accumulator + - window +encode: object +description: | + Returns the bottom element within a group according to the specified sort order. + New in MongoDB 5.2: Available in the $group and $setWindowFields stages. +arguments: + - + name: sortBy + type: + - sortBy + description: | + Specifies the order of results, with syntax similar to $sort. + - + name: output + type: + - expression + description: | + Represents the output for each element in the group and can be any expression. +tests: + - + name: 'Find the Bottom Score' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottom/#find-the-bottom-score' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + playerId: + $bottom: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + - + name: 'Finding the Bottom Score Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottom/#finding-the-bottom-score-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $bottom: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 diff --git a/generator/config/accumulator/bottomN.yaml b/generator/config/accumulator/bottomN.yaml new file mode 100644 index 000000000..355d8e09a --- /dev/null +++ b/generator/config/accumulator/bottomN.yaml @@ -0,0 +1,85 @@ +# $schema: ../schema.json +name: $bottomN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottomN/' +type: + - accumulator + - window +encode: object +description: | + Returns an aggregation of the bottom n elements within a group, according to the specified sort order. If the group contains fewer than n elements, $bottomN returns all elements in the group. + New in MongoDB 5.2. + Available in the $group and $setWindowFields stages. +arguments: + - + name: 'n' + type: + - resolvesToInt + description: | + Limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. + - + name: sortBy + type: + - sortBy + description: | + Specifies the order of results, with syntax similar to $sort. + - + name: output + type: + - expression + description: | + Represents the output for each element in the group and can be any expression. +tests: + - + name: 'Find the Three Lowest Scores' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottomN/#find-the-three-lowest-scores' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + playerId: + $bottomN: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + n: 3 + - + name: 'Finding the Three Lowest Score Documents Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottomN/#finding-the-three-lowest-score-documents-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $bottomN: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bottomN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $bottomN: + output: '$score' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 + sortBy: + score: -1 diff --git a/generator/config/accumulator/concatArrays.yaml b/generator/config/accumulator/concatArrays.yaml new file mode 100644 index 000000000..baf61b5e3 --- /dev/null +++ b/generator/config/accumulator/concatArrays.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $concatArrays +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concatArrays/' +type: + - accumulator + - window + - resolvesToArray +encode: single +description: | + Concatenates arrays to return the concatenated array. +arguments: + - + name: array + type: + - resolvesToArray # of arrays + variadic: array + description: | + An array of expressions that resolve to an array. + If any argument resolves to a value of null or refers to a field that is missing, `$concatArrays` returns `null`. +tests: + - + name: 'Warehouse collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concatArrays/#example' + pipeline: + - + $project: + items: + $concatArrays: + - '$instock' + - '$ordered' diff --git a/generator/config/accumulator/count.yaml b/generator/config/accumulator/count.yaml new file mode 100644 index 000000000..d9819056d --- /dev/null +++ b/generator/config/accumulator/count.yaml @@ -0,0 +1,37 @@ +# $schema: ../schema.json +name: $count +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/count-accumulator/' +type: + - accumulator + - window +encode: object +description: | + Returns the number of documents in the group or window. + Distinct from the $count pipeline stage. + New in MongoDB 5.0. +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/count-accumulator/#use-in--group-stage' + pipeline: + - + $group: + _id: '$state' + countNumberOfDocumentsForState: + $count: {} + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/count-accumulator/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + countNumberOfDocumentsForState: + $count: {} + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/covariancePop.yaml b/generator/config/accumulator/covariancePop.yaml new file mode 100644 index 000000000..b43a24022 --- /dev/null +++ b/generator/config/accumulator/covariancePop.yaml @@ -0,0 +1,41 @@ +# $schema: ../schema.json +name: $covariancePop +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covariancePop/' +type: + - window +encode: array +description: | + Returns the population covariance of two numeric expressions. + New in MongoDB 5.0. +arguments: + - + name: expression1 + type: + - resolvesToNumber + - + name: expression2 + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covariancePop/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + covariancePopForState: + $covariancePop: + - + # Example uses the short form, the builder always generates the verbose form + # $year: '$orderDate' + $year: + date: '$orderDate' + - '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/covarianceSamp.yaml b/generator/config/accumulator/covarianceSamp.yaml new file mode 100644 index 000000000..b6cc529af --- /dev/null +++ b/generator/config/accumulator/covarianceSamp.yaml @@ -0,0 +1,41 @@ +# $schema: ../schema.json +name: $covarianceSamp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covarianceSamp/' +type: + - window +encode: array +description: | + Returns the sample covariance of two numeric expressions. + New in MongoDB 5.0. +arguments: + - + name: expression1 + type: + - resolvesToNumber + - + name: expression2 + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/covarianceSamp/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + covarianceSampForState: + $covarianceSamp: + - + # Example uses the short form, the builder always generates the verbose form + # $year: '$orderDate' + $year: + date: '$orderDate' + - '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/denseRank.yaml b/generator/config/accumulator/denseRank.yaml new file mode 100644 index 000000000..0c50dd901 --- /dev/null +++ b/generator/config/accumulator/denseRank.yaml @@ -0,0 +1,34 @@ +# $schema: ../schema.json +name: $denseRank +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/' +type: + - window +encode: object +description: | + Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. There are no gaps in the ranks. Ties receive the same rank. + New in MongoDB 5.0. +tests: + - + name: 'Dense Rank Partitions by an Integer Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-an-integer-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + denseRankQuantityForState: + $denseRank: {} + - + name: 'Dense Rank Partitions by a Date Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/denseRank/#dense-rank-partitions-by-a-date-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + denseRankOrderDateForState: + $denseRank: {} diff --git a/generator/config/accumulator/derivative.yaml b/generator/config/accumulator/derivative.yaml new file mode 100644 index 000000000..5745e9380 --- /dev/null +++ b/generator/config/accumulator/derivative.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $derivative +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/derivative/' +type: + - window +encode: object +description: | + Returns the average rate of change within the specified window. + New in MongoDB 5.0. +arguments: + - + name: input + type: + - resolvesToNumber + - resolvesToDate + - + name: unit + type: + - timeUnit + optional: true + description: | + A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/derivative/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$truckID' + sortBy: + timeStamp: 1 + output: + truckAverageSpeed: + $derivative: + input: '$miles' + unit: 'hour' + window: + range: + - -30 + - 0 + unit: 'second' + - + $match: + truckAverageSpeed: + $gt: 50 diff --git a/generator/config/accumulator/documentNumber.yaml b/generator/config/accumulator/documentNumber.yaml new file mode 100644 index 000000000..b810ccd44 --- /dev/null +++ b/generator/config/accumulator/documentNumber.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $documentNumber +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documentNumber/' +type: + - window +encode: object +description: | + Returns the position of a document (known as the document number) in the $setWindowFields stage partition. Ties result in different adjacent document numbers. + New in MongoDB 5.0. +tests: + - + name: 'Document Number for Each State' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documentNumber/#document-number-for-each-state' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + documentNumberForState: + $documentNumber: {} diff --git a/generator/config/accumulator/expMovingAvg.yaml b/generator/config/accumulator/expMovingAvg.yaml new file mode 100644 index 000000000..3009dd115 --- /dev/null +++ b/generator/config/accumulator/expMovingAvg.yaml @@ -0,0 +1,60 @@ +# $schema: ../schema.json +name: $expMovingAvg +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/' +type: + - window +encode: object +description: | + Returns the exponential moving average for the numeric expression. + New in MongoDB 5.0. +arguments: + - + name: input + type: + - resolvesToNumber + - + name: 'N' + type: + - int + optional: true + description: | + An integer that specifies the number of historical documents that have a significant mathematical weight in the exponential moving average calculation, with the most recent documents contributing the most weight. + You must specify either N or alpha. You cannot specify both. + The N value is used in this formula to calculate the current result based on the expression value from the current document being read and the previous result of the calculation: + - + name: alpha + type: + - double + optional: true + description: | + A double that specifies the exponential decay value to use in the exponential moving average calculation. A higher alpha value assigns a lower mathematical significance to previous results from the calculation. + You must specify either N or alpha. You cannot specify both. +tests: + - + name: 'Exponential Moving Average Using N' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-n' + pipeline: + - + $setWindowFields: + partitionBy: '$stock' + sortBy: + date: 1 + output: + expMovingAvgForStock: + $expMovingAvg: + input: '$price' + N: 2 + - + name: 'Exponential Moving Average Using alpha' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/expMovingAvg/#exponential-moving-average-using-alpha' + pipeline: + - + $setWindowFields: + partitionBy: '$stock' + sortBy: + date: 1 + output: + expMovingAvgForStock: + $expMovingAvg: + input: '$price' + alpha: 0.75 diff --git a/generator/config/accumulator/first.yaml b/generator/config/accumulator/first.yaml new file mode 100644 index 000000000..d82f831a0 --- /dev/null +++ b/generator/config/accumulator/first.yaml @@ -0,0 +1,45 @@ +# $schema: ../schema.json +name: $first +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/' +type: + - accumulator + - window +encode: single +description: | + Returns the result of an expression for the first document in a group or window. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/#use-in--group-stage' + pipeline: + - + $sort: + item: 1 + date: 1 + - + $group: + _id: '$item' + firstSale: + $first: '$date' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + firstOrderTypeForState: + $first: '$type' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/firstN.yaml b/generator/config/accumulator/firstN.yaml new file mode 100644 index 000000000..cb7a6e96c --- /dev/null +++ b/generator/config/accumulator/firstN.yaml @@ -0,0 +1,122 @@ +# $schema: ../schema.json +name: $firstN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/' +type: + - accumulator + - window +encode: object +description: | + Returns an aggregation of the first n elements within a group. + The elements returned are meaningful only if in a specified sort order. + If the group contains fewer than n elements, $firstN returns all elements in the group. +arguments: + - + name: input + type: + - expression + description: | + An expression that resolves to the array from which to return n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + A positive integral expression that is either a constant or depends on the _id value for $group. +tests: + - + name: 'Null and Missing Values' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#null-and-missing-values' + pipeline: + - + $documents: + - + playerId: 'PlayerA' + gameId: 'G1' + score: 1 + - + playerId: 'PlayerB' + gameId: 'G1' + score: 2 + - + playerId: 'PlayerC' + gameId: 'G1' + score: 3 + - + playerId: 'PlayerD' + gameId: 'G1' + - + playerId: 'PlayerE' + gameId: 'G1' + score: ~ + - + $group: + _id: '$gameId' + firstFiveScores: + $firstN: + input: '$score' + n: 5 + - + name: 'Find the First Three Player Scores for a Single Game' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#find-the-first-three-player-scores-for-a-single-game' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + firstThreeScores: + $firstN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Finding the First Three Player Scores Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#finding-the-first-three-player-scores-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $firstN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Using $sort With $firstN' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#using--sort-with--firstn' + pipeline: + - + $sort: + score: -1 + - + $group: + _id: '$gameId' + playerId: + $firstN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $firstN: + input: '$score' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 + diff --git a/generator/config/accumulator/integral.yaml b/generator/config/accumulator/integral.yaml new file mode 100644 index 000000000..efc803597 --- /dev/null +++ b/generator/config/accumulator/integral.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $integral +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/' +type: + - window +encode: object +description: | + Returns the approximation of the area under a curve. + New in MongoDB 5.0. +arguments: + - + name: input + type: + - resolvesToNumber + - resolvesToDate + - + name: unit + type: + - timeUnit + optional: true + description: | + A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/integral/#example' + pipeline: + - + $setWindowFields: + partitionBy: '$powerMeterID' + sortBy: + timeStamp: 1 + output: + powerMeterKilowattHours: + $integral: + input: '$kilowatts' + unit: 'hour' + window: + range: + - 'unbounded' + - 'current' + unit: 'hour' diff --git a/generator/config/accumulator/last.yaml b/generator/config/accumulator/last.yaml new file mode 100644 index 000000000..969c05524 --- /dev/null +++ b/generator/config/accumulator/last.yaml @@ -0,0 +1,45 @@ +# $schema: ../schema.json +name: $last +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/' +type: + - accumulator + - window +encode: single +description: | + Returns the result of an expression for the last document in a group or window. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/' + pipeline: + - + $sort: + item: 1 + date: 1 + - + $group: + _id: '$item' + lastSalesDate: + $last: '$date' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + lastOrderTypeForState: + $last: '$type' + window: + documents: + - 'current' + - 'unbounded' diff --git a/generator/config/accumulator/lastN.yaml b/generator/config/accumulator/lastN.yaml new file mode 100644 index 000000000..13c9b72bd --- /dev/null +++ b/generator/config/accumulator/lastN.yaml @@ -0,0 +1,89 @@ +# $schema: ../schema.json +name: $lastN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/' +type: + - accumulator + - window +encode: object +description: | + Returns an aggregation of the last n elements within a group. + The elements returned are meaningful only if in a specified sort order. + If the group contains fewer than n elements, $lastN returns all elements in the group. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. +tests: + - + name: 'Find the Last Three Player Scores for a Single Game' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#find-the-last-three-player-scores-for-a-single-game' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + lastThreeScores: + $lastN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Finding the Last Three Player Scores Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#finding-the-last-three-player-scores-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $lastN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Using $sort With $lastN' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#using--sort-with--lastn' + pipeline: + - + $sort: + score: -1 + - + $group: + _id: '$gameId' + playerId: + $lastN: + input: + - '$playerId' + - '$score' + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $lastN: + input: '$score' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 diff --git a/generator/config/accumulator/linearFill.yaml b/generator/config/accumulator/linearFill.yaml new file mode 100644 index 000000000..034e6ab9e --- /dev/null +++ b/generator/config/accumulator/linearFill.yaml @@ -0,0 +1,40 @@ +# $schema: ../schema.json +name: $linearFill +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/' +type: + - window +encode: single +description: | + Fills null and missing fields in a window using linear interpolation based on surrounding field values. + Available in the $setWindowFields stage. + New in MongoDB 5.3. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Fill Missing Values with Linear Interpolation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#fill-missing-values-with-linear-interpolation' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + price: + $linearFill: '$price' + - + name: 'Use Multiple Fill Methods in a Single Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/linearFill/#use-multiple-fill-methods-in-a-single-stage' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + linearFillPrice: + $linearFill: '$price' + locfPrice: + $locf: '$price' diff --git a/generator/config/accumulator/locf.yaml b/generator/config/accumulator/locf.yaml new file mode 100644 index 000000000..63979bca4 --- /dev/null +++ b/generator/config/accumulator/locf.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $locf +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/' +type: + - window +encode: single +description: | + Last observation carried forward. Sets values for null and missing fields in a window to the last non-null value for the field. + Available in the $setWindowFields stage. + New in MongoDB 5.2. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Fill Missing Values with the Last Observed Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/locf/#fill-missing-values-with-the-last-observed-value' + pipeline: + - + $setWindowFields: + sortBy: + time: 1 + output: + price: + $locf: '$price' diff --git a/generator/config/accumulator/max.yaml b/generator/config/accumulator/max.yaml new file mode 100644 index 000000000..165cefc43 --- /dev/null +++ b/generator/config/accumulator/max.yaml @@ -0,0 +1,46 @@ +# $schema: ../schema.json +name: $max +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/' +type: + - accumulator + - window +encode: single +description: | + Returns the maximum value that results from applying an expression to each document. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/#use-in--group-stage' + pipeline: + - + $group: + _id: '$item' + maxTotalAmount: + $max: + $multiply: + - '$price' + - '$quantity' + maxQuantity: + $max: '$quantity' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + maximumQuantityForState: + $max: '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/maxN.yaml b/generator/config/accumulator/maxN.yaml new file mode 100644 index 000000000..4014782a8 --- /dev/null +++ b/generator/config/accumulator/maxN.yaml @@ -0,0 +1,73 @@ +# $schema: ../schema.json +name: $maxN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN/' +type: + - accumulator + - window +encode: object +description: | + Returns the n largest values in an array. Distinct from the $maxN accumulator. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return the maximal n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. +tests: + - + name: 'Find the Maximum Three Scores for a Single Game' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN/#find-the-maximum-three-scores-for-a-single-game' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + maxThreeScores: + $maxN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Finding the Maximum Three Scores Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN/#finding-the-maximum-three-scores-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + maxScores: + $maxN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $maxN: + input: + - '$score' + - '$playerId' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 diff --git a/generator/config/accumulator/median.yaml b/generator/config/accumulator/median.yaml new file mode 100644 index 000000000..e743c6982 --- /dev/null +++ b/generator/config/accumulator/median.yaml @@ -0,0 +1,61 @@ +# $schema: ../schema.json +name: $median +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/' +type: + - accumulator + - window +encode: object +description: | + Returns an approximation of the median, the 50th percentile, as a scalar value. + New in MongoDB 7.0. + This operator is available as an accumulator in these stages: + $group + $setWindowFields + It is also available as an aggregation expression. +arguments: + - + name: input + type: + - resolvesToNumber + description: | + $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. + - + name: method + type: + - accumulatorPercentile + description: | + The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. +tests: + - + name: 'Use $median as an Accumulator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/#use-operatorname-as-an-accumulator' + pipeline: + - + $group: + _id: ~ + test01_median: + $median: + input: '$test01' + method: 'approximate' + - + name: 'Use $median in a $setWindowField Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/#use-operatorname-in-a--setwindowfield-stage' + pipeline: + - + $setWindowFields: + sortBy: + test01: 1 + output: + test01_median: + $median: + input: '$test01' + method: 'approximate' + window: + range: + - -3 + - 3 + - + $project: + _id: 0 + studentId: 1 + test01_median: 1 diff --git a/generator/config/accumulator/mergeObjects.yaml b/generator/config/accumulator/mergeObjects.yaml new file mode 100644 index 000000000..d68728001 --- /dev/null +++ b/generator/config/accumulator/mergeObjects.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $mergeObjects +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/' +type: + - accumulator +encode: single +description: | + Combines multiple documents into a single document. +arguments: + - + name: document + type: + - resolvesToObject + description: | + Any valid expression that resolves to a document. +tests: + - + name: '$mergeObjects as an Accumulator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/#-mergeobjects-as-an-accumulator' + pipeline: + - + $group: + _id: '$item' + mergedSales: + $mergeObjects: '$quantity' diff --git a/generator/config/accumulator/min.yaml b/generator/config/accumulator/min.yaml new file mode 100644 index 000000000..226d56ec8 --- /dev/null +++ b/generator/config/accumulator/min.yaml @@ -0,0 +1,41 @@ +# $schema: ../schema.json +name: $min +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/' +type: + - accumulator + - window +encode: single +description: | + Returns the minimum value that results from applying an expression to each document. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/#use-in--group-stage' + pipeline: + - + $group: + _id: '$item' + minQuantity: + $min: '$quantity' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + minimumQuantityForState: + $min: '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/minN.yaml b/generator/config/accumulator/minN.yaml new file mode 100644 index 000000000..24719a22a --- /dev/null +++ b/generator/config/accumulator/minN.yaml @@ -0,0 +1,73 @@ +# $schema: ../schema.json +name: $minN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/' +type: + - accumulator + - window +encode: object +description: | + Returns the n smallest values in an array. Distinct from the $minN accumulator. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return the maximal n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. +tests: + - + name: 'Find the Minimum Three Scores for a Single Game' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#find-the-minimum-three-scores-for-a-single-game' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + minScores: + $minN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Finding the Minimum Three Documents Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#finding-the-minimum-three-documents-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + minScores: + $minN: + input: + - '$score' + - '$playerId' + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $minN: + input: + - '$score' + - '$playerId' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 diff --git a/generator/config/accumulator/percentile.yaml b/generator/config/accumulator/percentile.yaml new file mode 100644 index 000000000..b3c41b0e4 --- /dev/null +++ b/generator/config/accumulator/percentile.yaml @@ -0,0 +1,102 @@ +# $schema: ../schema.json +name: $percentile +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/' +type: + - accumulator + - window +encode: object +description: | + Returns an array of scalar values that correspond to specified percentile values. + New in MongoDB 7.0. + + This operator is available as an accumulator in these stages: + $group + + $setWindowFields + + It is also available as an aggregation expression. +arguments: + - + name: input + type: + - resolvesToNumber + description: | + $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. + - + name: p + type: + - resolvesToArray # of resolvesToNumber + description: | + $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + $percentile returns results in the same order as the elements in p. + - + name: method + type: + - accumulatorPercentile + description: | + The method that mongod uses to calculate the percentile value. The method must be 'approximate'. +tests: + - + name: 'Calculate a Single Value as an Accumulator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/#calculate-a-single-value-as-an-accumulator' + pipeline: + - + $group: + _id: ~ + test01_percentiles: + $percentile: + input: '$test01' + p: + - 0.95 + method: 'approximate' + - + name: 'Calculate Multiple Values as an Accumulator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/#calculate-multiple-values-as-an-accumulator' + pipeline: + - + $group: + _id: ~ + test01_percentiles: + $percentile: + input: '$test01' + p: [0.5, 0.75, 0.9, 0.95] + method: 'approximate' + test02_percentiles: + $percentile: + input: '$test02' + p: [0.5, 0.75, 0.9, 0.95] + method: 'approximate' + test03_percentiles: + $percentile: + input: '$test03' + p: [0.5, 0.75, 0.9, 0.95] + method: 'approximate' + test03_percent_alt: + $percentile: + input: '$test03' + p: [0.9, 0.5, 0.75, 0.95] + method: 'approximate' + - + name: 'Use $percentile in a $setWindowField Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/#use-operatorname-in-a--setwindowfield-stage' + pipeline: + - + $setWindowFields: + sortBy: + test01: 1 + output: + test01_95percentile: + $percentile: + input: '$test01' + p: + - 0.95 + method: 'approximate' + window: + range: + - -3 + - 3 + - + $project: + _id: 0 + studentId: 1 + test01_95percentile: 1 diff --git a/generator/config/accumulator/push.yaml b/generator/config/accumulator/push.yaml new file mode 100644 index 000000000..3fc367c59 --- /dev/null +++ b/generator/config/accumulator/push.yaml @@ -0,0 +1,55 @@ +# $schema: ../schema.json +name: $push +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/push/' +type: + - accumulator + - window +encode: single +description: | + Returns an array of values that result from applying an expression to each document. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/push/#use-in--group-stage' + pipeline: + - + $sort: + date: 1 + item: 1 + - + $group: + _id: + day: + # Example uses the short form, the builder always generates the verbose form + # $dayOfYear: '$date' + $dayOfYear: + date: '$date' + year: + # Example uses the short form, the builder always generates the verbose form + # $year: '$date' + $year: + date: '$date' + itemsSold: + $push: + item: '$item' + quantity: '$quantity' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/push/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + quantitiesForState: + $push: '$quantity' + window: + documents: ['unbounded', 'current'] diff --git a/generator/config/accumulator/rank.yaml b/generator/config/accumulator/rank.yaml new file mode 100644 index 000000000..8b8fd041b --- /dev/null +++ b/generator/config/accumulator/rank.yaml @@ -0,0 +1,34 @@ +# $schema: ../schema.json +name: $rank +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/' +type: + - window +encode: object +description: | + Returns the document position (known as the rank) relative to other documents in the $setWindowFields stage partition. + New in MongoDB 5.0. +tests: + - + name: 'Rank Partitions by an Integer Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-an-integer-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + rankQuantityForState: + $rank: {} + - + name: 'Rank Partitions by a Date Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rank/#rank-partitions-by-a-date-field' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + rankOrderDateForState: + $rank: {} diff --git a/generator/config/accumulator/setUnion.yaml b/generator/config/accumulator/setUnion.yaml new file mode 100644 index 000000000..8c8419f9e --- /dev/null +++ b/generator/config/accumulator/setUnion.yaml @@ -0,0 +1,32 @@ +# $schema: ../schema.json +name: $setUnion +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/' +type: + - accumulator + - window + - resolvesToArray +encode: single +description: | + Takes two or more arrays and returns an array containing the elements that appear in any input array. +arguments: + - + name: array + type: + - resolvesToArray # of arrays + variadic: array + description: | + An array of expressions that resolve to an array. +tests: + - + name: 'Flowers collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/#example' + pipeline: + - + $project: + flowerFieldA: 1 + flowerFieldB: 1 + allValues: + $setUnion: + - "$flowerFieldA" + - "$flowerFieldB" + _id: 0 diff --git a/generator/config/accumulator/shift.yaml b/generator/config/accumulator/shift.yaml new file mode 100644 index 000000000..f4984f056 --- /dev/null +++ b/generator/config/accumulator/shift.yaml @@ -0,0 +1,65 @@ +# $schema: ../schema.json +name: $shift +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/' +type: + - window +encode: object +description: | + Returns the value from an expression applied to a document in a specified position relative to the current document in the $setWindowFields stage partition. + New in MongoDB 5.0. +arguments: + - + name: output + type: + - expression + description: | + Specifies an expression to evaluate and return in the output. + - + name: by + type: + - int + description: | + Specifies an integer with a numeric document position relative to the current document in the output. + For example: + 1 specifies the document position after the current document. + -1 specifies the document position before the current document. + -2 specifies the document position that is two positions before the current document. + - + name: default + type: + - expression + description: | + Specifies an optional default expression to evaluate if the document position is outside of the implicit $setWindowFields stage window. The implicit window contains all the documents in the partition. + The default expression must evaluate to a constant value. + If you do not specify a default expression, $shift returns null for documents whose positions are outside of the implicit $setWindowFields stage window. +tests: + - + name: 'Shift Using a Positive Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-positive-integer' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + shiftQuantityForState: + $shift: + output: '$quantity' + by: 1 + default: 'Not available' + - + name: 'Shift Using a Negative Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shift/#shift-using-a-negative-integer' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + quantity: -1 + output: + shiftQuantityForState: + $shift: + output: '$quantity' + by: -1 + default: 'Not available' diff --git a/generator/config/accumulator/stdDevPop.yaml b/generator/config/accumulator/stdDevPop.yaml new file mode 100644 index 000000000..8916456d4 --- /dev/null +++ b/generator/config/accumulator/stdDevPop.yaml @@ -0,0 +1,40 @@ +# $schema: ../schema.json +name: $stdDevPop +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/' +type: + - accumulator + - window +encode: single +description: | + Calculates the population standard deviation of the input values. Use if the values encompass the entire population of data you want to represent and do not wish to generalize about a larger population. $stdDevPop ignores non-numeric values. + If the values represent only a sample of a population of data from which to generalize about the population, use $stdDevSamp instead. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/#use-in--group-stage' + pipeline: + - + $group: + _id: '$quiz' + stdDev: + $stdDevPop: '$score' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + stdDevPopQuantityForState: + $stdDevPop: '$quantity' + window: + documents: ['unbounded', 'current'] diff --git a/generator/config/accumulator/stdDevSamp.yaml b/generator/config/accumulator/stdDevSamp.yaml new file mode 100644 index 000000000..94ac33d15 --- /dev/null +++ b/generator/config/accumulator/stdDevSamp.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $stdDevSamp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/' +type: + - accumulator + - window +encode: single +description: | + Calculates the sample standard deviation of the input values. Use if the values encompass a sample of a population of data from which to generalize about the population. $stdDevSamp ignores non-numeric values. + If the values represent the entire population of data or you do not wish to generalize about a larger population, use $stdDevPop instead. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/#use-in--group-stage' + pipeline: + - + $sample: + size: 100 + - + $group: + _id: ~ + ageStdDev: + $stdDevSamp: '$age' + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + stdDevSampQuantityForState: + $stdDevSamp: '$quantity' + window: + documents: ['unbounded', 'current'] diff --git a/generator/config/accumulator/sum.yaml b/generator/config/accumulator/sum.yaml new file mode 100644 index 000000000..c40417ef4 --- /dev/null +++ b/generator/config/accumulator/sum.yaml @@ -0,0 +1,56 @@ +# $schema: ../schema.json +name: $sum +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/' +type: + - accumulator + - window +encode: single +description: | + Returns a sum of numerical values. Ignores non-numeric values. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Use in $group Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/#use-in--group-stage' + pipeline: + - + $group: + _id: + day: + # Example uses the short form, the builder always generates the verbose form + # $dayOfYear: '$date' + $dayOfYear: + date: '$date' + year: + # Example uses the short form, the builder always generates the verbose form + # $year: '$date' + $year: + date: '$date' + totalAmount: + $sum: + $multiply: + - '$price' + - '$quantity' + count: + $sum: 1 + - + name: 'Use in $setWindowFields Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/#use-in--setwindowfields-stage' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + sumQuantityForState: + $sum: '$quantity' + window: + documents: + - 'unbounded' + - 'current' diff --git a/generator/config/accumulator/top.yaml b/generator/config/accumulator/top.yaml new file mode 100644 index 000000000..94923cccd --- /dev/null +++ b/generator/config/accumulator/top.yaml @@ -0,0 +1,56 @@ +# $schema: ../schema.json +name: $top +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/top/' +type: + - accumulator +encode: object +description: | + Returns the top element within a group according to the specified sort order. + New in MongoDB 5.2. + + Available in the $group and $setWindowFields stages. +arguments: + - + name: sortBy + type: + - sortBy + description: | + Specifies the order of results, with syntax similar to $sort. + - + name: output + type: + - expression + description: | + Represents the output for each element in the group and can be any expression. +tests: + - + name: 'Find the Top Score' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/top/#find-the-top-score' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + playerId: + $top: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + - + name: 'Find the Top Score Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/top/#find-the-top-score-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $top: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 diff --git a/generator/config/accumulator/topN.yaml b/generator/config/accumulator/topN.yaml new file mode 100644 index 000000000..c5eff6056 --- /dev/null +++ b/generator/config/accumulator/topN.yaml @@ -0,0 +1,85 @@ +# $schema: ../schema.json +name: $topN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/topN/' +type: + - accumulator +encode: object +description: | + Returns an aggregation of the top n fields within a group, according to the specified sort order. + New in MongoDB 5.2. + + Available in the $group and $setWindowFields stages. +arguments: + - + name: 'n' + type: + - resolvesToInt + description: | + limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. + - + name: sortBy + type: + - sortBy + description: | + Specifies the order of results, with syntax similar to $sort. + - + name: output + type: + - expression + description: | + Represents the output for each element in the group and can be any expression. +tests: + - + name: 'Find the Three Highest Scores' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/topN/#find-the-three-highest-scores' + pipeline: + - + $match: + gameId: 'G1' + - + $group: + _id: '$gameId' + playerId: + $topN: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + n: 3 + - + name: 'Finding the Three Highest Score Documents Across Multiple Games' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/topN/#finding-the-three-highest-score-documents-across-multiple-games' + pipeline: + - + $group: + _id: '$gameId' + playerId: + $topN: + output: + - '$playerId' + - '$score' + sortBy: + score: -1 + n: 3 + - + name: 'Computing n Based on the Group Key for $group' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/topN/#computing-n-based-on-the-group-key-for--group' + pipeline: + - + $group: + _id: + gameId: '$gameId' + gamescores: + $topN: + output: '$score' + n: + $cond: + if: + $eq: + - '$gameId' + - 'G2' + then: 1 + else: 3 + sortBy: + score: -1 diff --git a/generator/config/definitions.php b/generator/config/definitions.php new file mode 100644 index 000000000..06d9b44ed --- /dev/null +++ b/generator/config/definitions.php @@ -0,0 +1,73 @@ + __DIR__ . '/stage', + 'namespace' => 'MongoDB\\Builder\\Stage', + 'classNameSuffix' => 'Stage', + 'generators' => [ + OperatorClassGenerator::class, + OperatorFactoryGenerator::class, + OperatorTestGenerator::class, + FluentStageFactoryGenerator::class, + ], + ], + + // Aggregation Pipeline Accumulator and Window Operators + [ + 'configFiles' => __DIR__ . '/accumulator', + 'namespace' => 'MongoDB\\Builder\\Accumulator', + 'classNameSuffix' => 'Accumulator', + 'generators' => [ + OperatorClassGenerator::class, + OperatorFactoryGenerator::class, + OperatorTestGenerator::class, + ], + ], + + // Aggregation Pipeline Expression + [ + 'configFiles' => __DIR__ . '/expression', + 'namespace' => 'MongoDB\\Builder\\Expression', + 'classNameSuffix' => 'Operator', + 'generators' => [ + OperatorClassGenerator::class, + OperatorFactoryGenerator::class, + OperatorTestGenerator::class, + ], + ], + + // Query Operators + [ + 'configFiles' => __DIR__ . '/query', + 'namespace' => 'MongoDB\\Builder\\Query', + 'classNameSuffix' => 'Operator', + 'generators' => [ + OperatorClassGenerator::class, + OperatorFactoryGenerator::class, + OperatorTestGenerator::class, + ], + ], + + // Search Operators + [ + 'configFiles' => __DIR__ . '/search', + 'namespace' => 'MongoDB\\Builder\\Search', + 'classNameSuffix' => 'Operator', + 'generators' => [ + OperatorClassGenerator::class, + OperatorFactoryGenerator::class, + OperatorTestGenerator::class, + ], + ], +]; diff --git a/generator/config/expression/abs.yaml b/generator/config/expression/abs.yaml new file mode 100644 index 000000000..fe29e44e3 --- /dev/null +++ b/generator/config/expression/abs.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $abs +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/abs/' +type: + - resolvesToNumber +encode: single +description: | + Returns the absolute value of a number. +arguments: + - + name: value + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/abs/#example' + pipeline: + - + $project: + delta: + $abs: + $subtract: + - '$startTemp' + - '$endTemp' diff --git a/generator/config/expression/acos.yaml b/generator/config/expression/acos.yaml new file mode 100644 index 000000000..7deca736d --- /dev/null +++ b/generator/config/expression/acos.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $acos +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/acos/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse cosine (arc cosine) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $acos takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + $acos returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $acos returns values as a double. $acos can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/acos/#example' + pipeline: + - + $addFields: + angle_a: + $radiansToDegrees: + $acos: + $divide: + - '$side_b' + - '$hypotenuse' diff --git a/generator/config/expression/acosh.yaml b/generator/config/expression/acosh.yaml new file mode 100644 index 000000000..ce575e317 --- /dev/null +++ b/generator/config/expression/acosh.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $acosh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/acosh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse hyperbolic cosine (hyperbolic arc cosine) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $acosh takes any valid expression that resolves to a number between 1 and +Infinity, e.g. 1 <= value <= +Infinity. + $acosh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $acosh returns values as a double. $acosh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/acosh/#example' + pipeline: + - + $addFields: + y-coordinate: + $radiansToDegrees: + $acosh: '$x-coordinate' diff --git a/generator/config/expression/add.yaml b/generator/config/expression/add.yaml new file mode 100644 index 000000000..fa23253d0 --- /dev/null +++ b/generator/config/expression/add.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $add +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/add/' +type: + - resolvesToInt + - resolvesToLong + - resolvesToDouble + - resolvesToDecimal + - resolvesToDate +encode: single +description: | + Adds numbers to return the sum, or adds numbers and a date to return a new date. If adding numbers and a date, treats the numbers as milliseconds. Accepts any number of argument expressions, but at most, one expression can resolve to a date. +arguments: + - + name: expression + type: + - resolvesToNumber + - resolvesToDate + variadic: array + description: | + The arguments can be any valid expression as long as they resolve to either all numbers or to numbers and a date. +tests: + - + name: 'Add Numbers' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/add/#add-numbers' + pipeline: + - + $project: + item: 1 + total: + $add: + - '$price' + - '$fee' + - + name: 'Perform Addition on a Date' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/add/#perform-addition-on-a-date' + pipeline: + - + $project: + item: 1 + billing_date: + $add: + - '$date' + - 259200000 diff --git a/generator/config/expression/allElementsTrue.yaml b/generator/config/expression/allElementsTrue.yaml new file mode 100644 index 000000000..7301f8d68 --- /dev/null +++ b/generator/config/expression/allElementsTrue.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $allElementsTrue +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/allElementsTrue/' +type: + - resolvesToBool +encode: array +description: | + Returns true if no element of a set evaluates to false, otherwise, returns false. Accepts a single argument expression. +arguments: + - + name: expression + type: + - resolvesToArray +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/allElementsTrue/#example' + pipeline: + - + $project: + responses: 1 + isAllTrue: + $allElementsTrue: + - '$responses' + _id: 0 diff --git a/generator/config/expression/and.yaml b/generator/config/expression/and.yaml new file mode 100644 index 000000000..96057d249 --- /dev/null +++ b/generator/config/expression/and.yaml @@ -0,0 +1,37 @@ +# $schema: ../schema.json +name: $and +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/and/' +type: + - resolvesToBool +encode: single +description: | + Returns true only when all its expressions evaluate to true. Accepts any number of argument expressions. +arguments: + - + name: expression + type: + - expression + - resolvesToBool + - resolvesToNumber + - resolvesToString + - resolvesToNull + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/and/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + result: + $and: + - + $gt: + - '$qty' + - 100 + - + $lt: + - '$qty' + - 250 diff --git a/generator/config/expression/anyElementTrue.yaml b/generator/config/expression/anyElementTrue.yaml new file mode 100644 index 000000000..50fe665b6 --- /dev/null +++ b/generator/config/expression/anyElementTrue.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $anyElementTrue +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/anyElementTrue/' +type: + - resolvesToBool +encode: array +description: | + Returns true if any elements of a set evaluate to true; otherwise, returns false. Accepts a single argument expression. +arguments: + - + name: expression + type: + - resolvesToArray +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/anyElementTrue/#example' + pipeline: + - + $project: + responses: 1 + isAnyTrue: + $anyElementTrue: + - '$responses' + _id: 0 diff --git a/generator/config/expression/arrayElemAt.yaml b/generator/config/expression/arrayElemAt.yaml new file mode 100644 index 000000000..09fe9dae1 --- /dev/null +++ b/generator/config/expression/arrayElemAt.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $arrayElemAt +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayElemAt/' +type: + - resolvesToAny +encode: array +description: | + Returns the element at the specified array index. +arguments: + - + name: array + type: + - resolvesToArray + - + name: idx + type: + - resolvesToInt +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayElemAt/#example' + pipeline: + - + $project: + name: 1 + first: + $arrayElemAt: + - '$favorites' + - 0 + last: + $arrayElemAt: + - '$favorites' + - -1 diff --git a/generator/config/expression/arrayToObject.yaml b/generator/config/expression/arrayToObject.yaml new file mode 100644 index 000000000..87026f7a7 --- /dev/null +++ b/generator/config/expression/arrayToObject.yaml @@ -0,0 +1,50 @@ +# $schema: ../schema.json +name: $arrayToObject +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayToObject/' +type: + - resolvesToObject +encode: array +description: | + Converts an array of key value pairs to a document. +arguments: + - + name: array + type: + - resolvesToArray +tests: + - + name: '$arrayToObject Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayToObject/#-arraytoobject--example' + pipeline: + - + $project: + item: 1 + dimensions: + # Example uses the short form, the builder always generates the verbose form + # $arrayToObject: '$dimensions' + $arrayToObject: + - '$dimensions' + - + name: '$objectToArray and $arrayToObject Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/arrayToObject/#-objecttoarray----arraytoobject-example' + pipeline: + - + $addFields: + instock: + $objectToArray: '$instock' + - + $addFields: + instock: + $concatArrays: + - '$instock' + - + - + k: 'total' + v: + $sum: + - '$instock.v' + - + $addFields: + instock: + $arrayToObject: + - '$instock' diff --git a/generator/config/expression/asin.yaml b/generator/config/expression/asin.yaml new file mode 100644 index 000000000..43e2832a2 --- /dev/null +++ b/generator/config/expression/asin.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $asin +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/asin/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse sin (arc sine) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $asin takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + $asin returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $asin returns values as a double. $asin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/asin/#example' + pipeline: + - + $addFields: + angle_a: + $radiansToDegrees: + $asin: + $divide: + - '$side_a' + - '$hypotenuse' diff --git a/generator/config/expression/asinh.yaml b/generator/config/expression/asinh.yaml new file mode 100644 index 000000000..6d45c14fa --- /dev/null +++ b/generator/config/expression/asinh.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $asinh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/asinh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse hyperbolic sine (hyperbolic arc sine) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $asinh takes any valid expression that resolves to a number. + $asinh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $asinh returns values as a double. $asinh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/asinh/#example' + pipeline: + - + $addFields: + y-coordinate: + $radiansToDegrees: + $asinh: '$x-coordinate' diff --git a/generator/config/expression/atan.yaml b/generator/config/expression/atan.yaml new file mode 100644 index 000000000..a8bb1674f --- /dev/null +++ b/generator/config/expression/atan.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $atan +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse tangent (arc tangent) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $atan takes any valid expression that resolves to a number. + $atan returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $atan returns values as a double. $atan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan/#example' + pipeline: + - + $addFields: + angle_a: + $radiansToDegrees: + $atan: + $divide: + - '$side_b' + - '$side_a' diff --git a/generator/config/expression/atan2.yaml b/generator/config/expression/atan2.yaml new file mode 100644 index 000000000..1abc55e6a --- /dev/null +++ b/generator/config/expression/atan2.yaml @@ -0,0 +1,34 @@ +# $schema: ../schema.json +name: $atan2 +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan2/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: array +description: | + Returns the inverse tangent (arc tangent) of y / x in radians, where y and x are the first and second values passed to the expression respectively. +arguments: + - + name: 'y' + type: + - resolvesToNumber + description: | + $atan2 takes any valid expression that resolves to a number. + $atan2 returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $atan returns values as a double. $atan2 can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + - + name: x + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atan2/#example' + pipeline: + - + $addFields: + angle_a: + $radiansToDegrees: + $atan2: + - '$side_b' + - '$side_a' diff --git a/generator/config/expression/atanh.yaml b/generator/config/expression/atanh.yaml new file mode 100644 index 000000000..501fba2bf --- /dev/null +++ b/generator/config/expression/atanh.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $atanh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atanh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the inverse hyperbolic tangent (hyperbolic arc tangent) of a value in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $atanh takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + $atanh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + By default $atanh returns values as a double. $atanh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/atanh/#example' + pipeline: + - + $addFields: + y-coordinate: + $radiansToDegrees: + $atanh: '$x-coordinate' diff --git a/generator/config/expression/avg.yaml b/generator/config/expression/avg.yaml new file mode 100644 index 000000000..3bb771936 --- /dev/null +++ b/generator/config/expression/avg.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: $avg +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/' +type: + - resolvesToNumber +encode: single +description: | + Returns an average of numerical values. Ignores non-numeric values. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber + variadic: array +tests: + - + name: 'Use in $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#use-in--project-stage' + pipeline: + - + $project: + quizAvg: + # Example uses the short form, the builder always generates the verbose form + # $avg: '$quizzes' + $avg: + - '$quizzes' + labAvg: + # $avg: '$labs' + $avg: + - '$labs' + examAvg: + $avg: + - '$final' + - '$midterm' diff --git a/generator/config/expression/binarySize.yaml b/generator/config/expression/binarySize.yaml new file mode 100644 index 000000000..eb0146f8c --- /dev/null +++ b/generator/config/expression/binarySize.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $binarySize +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/binarySize/' +type: + - resolvesToInt +encode: single +description: | + Returns the size of a given string or binary data value's content in bytes. +arguments: + - + name: expression + type: + - resolvesToString + - resolvesToBinData + - resolvesToNull +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/binarySize/#example' + pipeline: + - + $project: + name: '$name' + imageSize: + $binarySize: '$binary' diff --git a/generator/config/expression/bitAnd.yaml b/generator/config/expression/bitAnd.yaml new file mode 100644 index 000000000..271cc0973 --- /dev/null +++ b/generator/config/expression/bitAnd.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitAnd +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitAnd/' +type: + - resolvesToInt + - resolvesToLong +encode: single +description: | + Returns the result of a bitwise and operation on an array of int or long values. + New in MongoDB 6.3. +arguments: + - + name: expression + type: + - resolvesToInt + - resolvesToLong + variadic: array +tests: + - + name: 'Bitwise AND with Two Integers' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitAnd/#bitwise-and-with-two-integers' + pipeline: + - + $project: + result: + $bitAnd: + - '$a' + - '$b' + - + name: 'Bitwise AND with a Long and Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitAnd/#bitwise-and-with-a-long-and-integer' + pipeline: + - + $project: + result: + $bitAnd: + - '$a' + - { "$numberLong": "63" } diff --git a/generator/config/expression/bitNot.yaml b/generator/config/expression/bitNot.yaml new file mode 100644 index 000000000..5211fa42a --- /dev/null +++ b/generator/config/expression/bitNot.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $bitNot +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitNot/' +type: + - resolvesToInt + - resolvesToLong +encode: single +description: | + Returns the result of a bitwise not operation on a single argument or an array that contains a single int or long value. + New in MongoDB 6.3. +arguments: + - + name: expression + type: + - resolvesToInt + - resolvesToLong +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitNot/#example' + pipeline: + - + $project: + result: + $bitNot: '$a' diff --git a/generator/config/expression/bitOr.yaml b/generator/config/expression/bitOr.yaml new file mode 100644 index 000000000..084ac224c --- /dev/null +++ b/generator/config/expression/bitOr.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitOr +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitOr/' +type: + - resolvesToInt + - resolvesToLong +encode: single +description: | + Returns the result of a bitwise or operation on an array of int or long values. + New in MongoDB 6.3. +arguments: + - + name: expression + type: + - resolvesToInt + - resolvesToLong + variadic: array +tests: + - + name: 'Bitwise OR with Two Integers' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitOr/#bitwise-or-with-two-integers' + pipeline: + - + $project: + result: + $bitOr: + - '$a' + - '$b' + - + name: 'Bitwise OR with a Long and Integer' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitOr/#bitwise-or-with-a-long-and-integer' + pipeline: + - + $project: + result: + $bitOr: + - '$a' + - { "$numberLong": "63" } diff --git a/generator/config/expression/bitXor.yaml b/generator/config/expression/bitXor.yaml new file mode 100644 index 000000000..f4acc4df4 --- /dev/null +++ b/generator/config/expression/bitXor.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $bitXor +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitXor/' +type: + - resolvesToInt + - resolvesToLong +encode: single +description: | + Returns the result of a bitwise xor (exclusive or) operation on an array of int and long values. + New in MongoDB 6.3. +arguments: + - + name: expression + type: + - resolvesToInt + - resolvesToLong + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bitXor/#example' + pipeline: + - + $project: + result: + $bitXor: + - '$a' + - '$b' diff --git a/generator/config/expression/bsonSize.yaml b/generator/config/expression/bsonSize.yaml new file mode 100644 index 000000000..712188c52 --- /dev/null +++ b/generator/config/expression/bsonSize.yaml @@ -0,0 +1,48 @@ +# $schema: ../schema.json +name: $bsonSize +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bsonSize/' +type: + - resolvesToInt +encode: single +description: | + Returns the size in bytes of a given document (i.e. BSON type Object) when encoded as BSON. +arguments: + - + name: object + type: + - resolvesToObject + - resolvesToNull +tests: + - + name: 'Return Sizes of Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bsonSize/#return-sizes-of-documents' + pipeline: + - + $project: + name: 1 + object_size: + $bsonSize: '$$ROOT' + - + name: 'Return Combined Size of All Documents in a Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bsonSize/#return-combined-size-of-all-documents-in-a-collection' + pipeline: + - + $group: + _id: ~ + combined_object_size: + $sum: + $bsonSize: '$$ROOT' + - + name: 'Return Document with Largest Specified Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bsonSize/#return-document-with-largest-specified-field' + pipeline: + - + $project: + name: '$name' + task_object_size: + $bsonSize: '$current_task' + - + $sort: + task_object_size: -1 + - + $limit: 1 diff --git a/generator/config/expression/case.yaml b/generator/config/expression/case.yaml new file mode 100644 index 000000000..ccf463c90 --- /dev/null +++ b/generator/config/expression/case.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $case +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/switch/' +type: + - switchBranch +encode: object +wrapObject: false +description: | + Represents a single case in a $switch expression +arguments: + - + name: case + type: + - resolvesToBool + description: | + Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + - + name: then + type: + - expression + description: | + Can be any valid expression. diff --git a/generator/config/expression/ceil.yaml b/generator/config/expression/ceil.yaml new file mode 100644 index 000000000..73c31ddb7 --- /dev/null +++ b/generator/config/expression/ceil.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $ceil +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ceil/' +type: + - resolvesToInt +encode: single +description: | + Returns the smallest integer greater than or equal to the specified number. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + If the argument resolves to a value of null or refers to a field that is missing, $ceil returns null. If the argument resolves to NaN, $ceil returns NaN. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ceil/#example' + pipeline: + - + $project: + value: 1 + ceilingValue: + $ceil: '$value' diff --git a/generator/config/expression/cmp.yaml b/generator/config/expression/cmp.yaml new file mode 100644 index 000000000..dd24f9839 --- /dev/null +++ b/generator/config/expression/cmp.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $cmp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cmp/' +type: + - resolvesToInt +encode: array +description: | + Returns 0 if the two values are equivalent, 1 if the first value is greater than the second, and -1 if the first value is less than the second. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cmp/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + cmpTo250: + $cmp: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/concat.yaml b/generator/config/expression/concat.yaml new file mode 100644 index 000000000..e8b218d82 --- /dev/null +++ b/generator/config/expression/concat.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $concat +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concat/' +type: + - resolvesToString +encode: single +description: | + Concatenates any number of strings. +arguments: + - + name: expression + type: + - resolvesToString + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concat/#examples' + pipeline: + - + $project: + itemDescription: + $concat: + - '$item' + - ' - ' + - '$description' diff --git a/generator/config/expression/concatArrays.yaml b/generator/config/expression/concatArrays.yaml new file mode 100644 index 000000000..026541092 --- /dev/null +++ b/generator/config/expression/concatArrays.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $concatArrays +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concatArrays/' +type: + - resolvesToArray +encode: single +description: | + Concatenates arrays to return the concatenated array. +arguments: + - + name: array + type: + - resolvesToArray + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/concatArrays/#example' + pipeline: + - + $project: + items: + $concatArrays: + - '$instock' + - '$ordered' diff --git a/generator/config/expression/cond.yaml b/generator/config/expression/cond.yaml new file mode 100644 index 000000000..e2fd66ad7 --- /dev/null +++ b/generator/config/expression/cond.yaml @@ -0,0 +1,37 @@ +# $schema: ../schema.json +name: $cond +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cond/' +type: + - resolvesToAny +encode: object +description: | + A ternary operator that evaluates one expression, and depending on the result, returns the value of one of the other two expressions. Accepts either three expressions in an ordered list or three named parameters. +arguments: + - + name: if + type: + - resolvesToBool + - + name: then + type: + - expression + - + name: else + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cond/#example' + pipeline: + - + $project: + item: 1 + discount: + $cond: + if: + $gte: + - '$qty' + - 250 + then: 30 + else: 20 diff --git a/generator/config/expression/convert.yaml b/generator/config/expression/convert.yaml new file mode 100644 index 000000000..a76311ed5 --- /dev/null +++ b/generator/config/expression/convert.yaml @@ -0,0 +1,82 @@ +# $schema: ../schema.json +name: $convert +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/convert/' +type: + - resolvesToAny +encode: object +description: | + Converts a value to a specified type. + New in MongoDB 4.0. +arguments: + - + name: input + type: + - expression + - + name: to + type: + - resolvesToString + - resolvesToInt + - + name: onError + type: + - expression + optional: true + description: | + The value to return on encountering an error during conversion, including unsupported type conversions. The arguments can be any valid expression. + If unspecified, the operation throws an error upon encountering an error and stops. + - + name: onNull + type: + - expression + optional: true + description: | + The value to return if the input is null or missing. The arguments can be any valid expression. + If unspecified, $convert returns null if the input is null or missing. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/convert/#example' + pipeline: + - + $addFields: + convertedPrice: + $convert: + input: '$price' + to: 'decimal' + onError: 'Error' + onNull: !bson_decimal128 '0' + convertedQty: + $convert: + input: '$qty' + to: 'int' + onError: + $concat: + - 'Could not convert ' + - + $toString: '$qty' + - ' to type integer.' + onNull: 0 + - + $project: + totalPrice: + $switch: + branches: + - + case: + $eq: + - + $type: '$convertedPrice' + - 'string' + then: 'NaN' + - + case: + $eq: + - + $type: '$convertedQty' + - 'string' + then: 'NaN' + default: + $multiply: + - '$convertedPrice' + - '$convertedQty' diff --git a/generator/config/expression/cos.yaml b/generator/config/expression/cos.yaml new file mode 100644 index 000000000..0b47670cf --- /dev/null +++ b/generator/config/expression/cos.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $cos +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cos/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the cosine of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $cos takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + By default $cos returns values as a double. $cos can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cos/#example' + pipeline: + - + $addFields: + side_a: + $multiply: + - + $cos: + $degreesToRadians: '$angle_a' + - '$hypotenuse' diff --git a/generator/config/expression/cosh.yaml b/generator/config/expression/cosh.yaml new file mode 100644 index 000000000..419fa8caa --- /dev/null +++ b/generator/config/expression/cosh.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $cosh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cosh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the hyperbolic cosine of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $cosh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + By default $cosh returns values as a double. $cosh can also return values as a 128-bit decimal if the resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/cosh/#example' + pipeline: + - + $addFields: + cosh_output: + $cosh: + $degreesToRadians: '$angle' diff --git a/generator/config/expression/createObjectId.yaml b/generator/config/expression/createObjectId.yaml new file mode 100644 index 000000000..bab85d7f7 --- /dev/null +++ b/generator/config/expression/createObjectId.yaml @@ -0,0 +1,17 @@ +# $schema: ../schema.json +name: $createObjectId +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/createObjectId/' +type: + - resolvesToObjectId +encode: object +description: | + Returns a random object ID +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/createObjectId/#example' + pipeline: + - + $project: + objectId: + $createObjectId: {} diff --git a/generator/config/expression/dateAdd.yaml b/generator/config/expression/dateAdd.yaml new file mode 100644 index 000000000..c7d85d571 --- /dev/null +++ b/generator/config/expression/dateAdd.yaml @@ -0,0 +1,133 @@ +# $schema: ../schema.json +name: $dateAdd +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/' +type: + - resolvesToDate +encode: object +description: | + Adds a number of time units to a date object. +arguments: + - + name: startDate + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: unit + type: + - timeUnit + description: | + The unit used to measure the amount of time added to the startDate. + - + name: amount + type: + - resolvesToInt + - resolvesToLong + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Add a Future Date' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/#add-a-future-date' + pipeline: + - + $project: + expectedDeliveryDate: + $dateAdd: + startDate: '$purchaseDate' + unit: 'day' + amount: 3 + - + # Example uses the short form, the builder always generates the verbose form + # $merge: 'shipping' + $merge: + into: 'shipping' + - + name: 'Filter on a Date Range' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/#filter-on-a-date-range' + pipeline: + - + $match: + $expr: + $gt: + - '$deliveryDate' + - + $dateAdd: + startDate: '$purchaseDate' + unit: 'day' + amount: 5 + - + $project: + _id: 0 + custId: 1 + purchased: + $dateToString: + format: '%Y-%m-%d' + date: '$purchaseDate' + delivery: + $dateToString: + format: '%Y-%m-%d' + date: '$deliveryDate' + - + name: 'Adjust for Daylight Savings Time' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/#adjust-for-daylight-savings-time' + pipeline: + - + $project: + _id: 0 + location: 1 + start: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: '$login' + days: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateAdd: + startDate: '$login' + unit: 'day' + amount: 1 + timezone: '$location' + hours: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateAdd: + startDate: '$login' + unit: 'hour' + amount: 24 + timezone: '$location' + startTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: '$login' + timezone: '$location' + daysTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateAdd: + startDate: '$login' + unit: 'day' + amount: 1 + timezone: '$location' + timezone: '$location' + hoursTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateAdd: + startDate: '$login' + unit: 'hour' + amount: 24 + timezone: '$location' + timezone: '$location' diff --git a/generator/config/expression/dateDiff.yaml b/generator/config/expression/dateDiff.yaml new file mode 100644 index 000000000..42cb55d15 --- /dev/null +++ b/generator/config/expression/dateDiff.yaml @@ -0,0 +1,114 @@ +# $schema: ../schema.json +name: $dateDiff +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/' +type: + - resolvesToInt +encode: object +description: | + Returns the difference between two dates. +arguments: + - + name: startDate + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The start of the time period. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: endDate + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The end of the time period. The endDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: unit + type: + - timeUnit + description: | + The time measurement unit between the startDate and endDate + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + - + name: startOfWeek + type: + - resolvesToString + optional: true + description: | + Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string +tests: + - + name: 'Elapsed Time' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/#elapsed-time' + pipeline: + - + $group: + _id: ~ + averageTime: + $avg: + $dateDiff: + startDate: '$purchased' + endDate: '$delivered' + unit: 'day' + - + $project: + _id: 0 + numDays: + $trunc: + - '$averageTime' + - 1 + - + name: 'Result Precision' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/#result-precision' + pipeline: + - + $project: + Start: '$start' + End: '$end' + years: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'year' + months: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'month' + days: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'day' + _id: 0 + - + name: 'Weeks Per Month' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/#weeks-per-month' + pipeline: + - + $project: + wks_default: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'week' + wks_monday: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'week' + startOfWeek: 'Monday' + wks_friday: + $dateDiff: + startDate: '$start' + endDate: '$end' + unit: 'week' + startOfWeek: 'fri' + _id: 0 diff --git a/generator/config/expression/dateFromParts.yaml b/generator/config/expression/dateFromParts.yaml new file mode 100644 index 000000000..3ed35004e --- /dev/null +++ b/generator/config/expression/dateFromParts.yaml @@ -0,0 +1,114 @@ +# $schema: ../schema.json +name: $dateFromParts +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromParts/' +type: + - resolvesToDate +encode: object +description: | + Constructs a BSON Date object given the date's constituent parts. +arguments: + - + name: year + type: + - resolvesToNumber + optional: true + description: | + Calendar year. Can be any expression that evaluates to a number. + - + name: isoWeekYear + type: + - resolvesToNumber + optional: true + description: | + ISO Week Date Year. Can be any expression that evaluates to a number. + - + name: month + type: + - resolvesToNumber + optional: true + description: | + Month. Defaults to 1. + - + name: isoWeek + type: + - resolvesToNumber + optional: true + description: | + Week of year. Defaults to 1. + - + name: day + type: + - resolvesToNumber + optional: true + description: | + Day of month. Defaults to 1. + - + name: isoDayOfWeek + type: + - resolvesToNumber + optional: true + description: | + Day of week (Monday 1 - Sunday 7). Defaults to 1. + - + name: hour + type: + - resolvesToNumber + optional: true + description: | + Hour. Defaults to 0. + - + name: minute + type: + - resolvesToNumber + optional: true + description: | + Minute. Defaults to 0. + - + name: second + type: + - resolvesToNumber + optional: true + description: | + Second. Defaults to 0. + - + name: millisecond + type: + - resolvesToNumber + optional: true + description: | + Millisecond. Defaults to 0. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromParts/#example' + pipeline: + - + $project: + date: + $dateFromParts: + year: 2017 + month: 2 + day: 8 + hour: 12 + date_iso: + $dateFromParts: + isoWeekYear: 2017 + isoWeek: 6 + isoDayOfWeek: 3 + hour: 12 + date_timezone: + $dateFromParts: + year: 2016 + month: 12 + day: 31 + hour: 23 + minute: 46 + second: 12 + timezone: 'America/New_York' diff --git a/generator/config/expression/dateFromString.yaml b/generator/config/expression/dateFromString.yaml new file mode 100644 index 000000000..713200f5d --- /dev/null +++ b/generator/config/expression/dateFromString.yaml @@ -0,0 +1,80 @@ +# $schema: ../schema.json +name: $dateFromString +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/' +type: + - resolvesToDate +encode: object +description: | + Converts a date/time string to a date object. +arguments: + - + name: dateString + type: + - resolvesToString + description: | + The date/time string to convert to a date object. + - + name: format + type: + - resolvesToString + optional: true + description: | + The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The time zone to use to format the date. + - + name: onError + type: + - expression + optional: true + description: | + If $dateFromString encounters an error while parsing the given dateString, it outputs the result value of the provided onError expression. This result value can be of any type. + If you do not specify onError, $dateFromString throws an error if it cannot parse dateString. + - + name: onNull + type: + - expression + optional: true + description: | + If the dateString provided to $dateFromString is null or missing, it outputs the result value of the provided onNull expression. This result value can be of any type. + If you do not specify onNull and dateString is null or missing, then $dateFromString outputs null. +tests: + - + name: 'Converting Dates' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/#converting-dates' + pipeline: + - + $project: + date: + $dateFromString: + dateString: '$date' + timezone: 'America/New_York' + - + name: 'onError' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/#onerror' + pipeline: + - + $project: + date: + $dateFromString: + dateString: '$date' + timezone: '$timezone' + onError: '$date' + - + name: 'onNull' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/#onnull' + pipeline: + - + $project: + date: + $dateFromString: + dateString: '$date' + timezone: '$timezone' + onNull: !bson_utcdatetime 0 + diff --git a/generator/config/expression/dateSubtract.yaml b/generator/config/expression/dateSubtract.yaml new file mode 100644 index 000000000..e463fe8f8 --- /dev/null +++ b/generator/config/expression/dateSubtract.yaml @@ -0,0 +1,139 @@ +# $schema: ../schema.json +name: $dateSubtract +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/' +type: + - resolvesToDate +encode: object +description: | + Subtracts a number of time units from a date object. +arguments: + - + name: startDate + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: unit + type: + - timeUnit + description: | + The unit used to measure the amount of time added to the startDate. + - + name: amount + type: + - resolvesToInt + - resolvesToLong + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Subtract A Fixed Amount' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/#subtract-a-fixed-amount' + pipeline: + - + $match: + $expr: + $eq: + - + # Example uses the short form, the builder always generates the verbose form + # $month: '$logout' + $month: + date: '$logout' + - 1 + - + $project: + logoutTime: + $dateSubtract: + startDate: '$logout' + unit: 'hour' + amount: 3 + - + # Example uses the short form, the builder always generates the verbose form + # $merge: 'connectionTime' + $merge: + into: 'connectionTime' + - + name: 'Filter by Relative Dates' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/#filter-by-relative-dates' + pipeline: + - + $match: + $expr: + $gt: + - '$logoutTime' + - + $dateSubtract: + startDate: '$$NOW' + unit: 'week' + amount: 1 + - + $project: + _id: 0 + custId: 1 + loggedOut: + $dateToString: + format: '%Y-%m-%d' + date: '$logoutTime' + - + name: 'Adjust for Daylight Savings Time' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/#adjust-for-daylight-savings-time' + pipeline: + - + $project: + _id: 0 + location: 1 + start: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: '$login' + days: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateSubtract: + startDate: '$login' + unit: 'day' + amount: 1 + timezone: '$location' + hours: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateSubtract: + startDate: '$login' + unit: 'hour' + amount: 24 + timezone: '$location' + startTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: '$login' + timezone: '$location' + daysTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateSubtract: + startDate: '$login' + unit: 'day' + amount: 1 + timezone: '$location' + timezone: '$location' + hoursTZInfo: + $dateToString: + format: '%Y-%m-%d %H:%M' + date: + $dateSubtract: + startDate: '$login' + unit: 'hour' + amount: 24 + timezone: '$location' + timezone: '$location' diff --git a/generator/config/expression/dateToParts.yaml b/generator/config/expression/dateToParts.yaml new file mode 100644 index 000000000..d250e052f --- /dev/null +++ b/generator/config/expression/dateToParts.yaml @@ -0,0 +1,49 @@ +# $schema: ../schema.json +name: $dateToParts +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToParts/' +type: + - resolvesToObject +encode: object +description: | + Returns a document containing the constituent parts of a date. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The input date for which to return parts. date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + - + name: iso8601 + type: + - bool + optional: true + description: | + If set to true, modifies the output document to use ISO week date fields. Defaults to false. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToParts/#example' + pipeline: + - + $project: + date: + $dateToParts: + date: '$date' + date_iso: + $dateToParts: + date: '$date' + iso8601: true + date_timezone: + $dateToParts: + date: '$date' + timezone: 'America/New_York' diff --git a/generator/config/expression/dateToString.yaml b/generator/config/expression/dateToString.yaml new file mode 100644 index 000000000..29e0ea8c8 --- /dev/null +++ b/generator/config/expression/dateToString.yaml @@ -0,0 +1,81 @@ +# $schema: ../schema.json +name: $dateToString +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToString/' +type: + - resolvesToString +encode: object +description: | + Returns the date as a formatted string. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to convert to string. Must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: format + type: + - resolvesToString + optional: true + description: | + The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The time zone to use to format the date. + - + name: onNull + type: + - expression + optional: true + description: | + The value to return if the date is null or missing. + If unspecified, $dateToString returns null if the date is null or missing. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToString/#example' + pipeline: + - + $project: + yearMonthDayUTC: + $dateToString: + format: '%Y-%m-%d' + date: '$date' + timewithOffsetNY: + $dateToString: + format: '%H:%M:%S:%L%z' + date: '$date' + timezone: 'America/New_York' + timewithOffset430: + $dateToString: + format: '%H:%M:%S:%L%z' + date: '$date' + timezone: '+04:30' + minutesOffsetNY: + $dateToString: + format: '%Z' + date: '$date' + timezone: 'America/New_York' + minutesOffset430: + $dateToString: + format: '%Z' + date: '$date' + timezone: '+04:30' + abbreviated_month: + $dateToString: + format: '%b' + date: '$date' + timezone: '+04:30' + full_month: + $dateToString: + format: '%B' + date: '$date' + timezone: '+04:30' diff --git a/generator/config/expression/dateTrunc.yaml b/generator/config/expression/dateTrunc.yaml new file mode 100644 index 000000000..aa3dcd6ca --- /dev/null +++ b/generator/config/expression/dateTrunc.yaml @@ -0,0 +1,77 @@ +# $schema: ../schema.json +name: $dateTrunc +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/' +type: + - resolvesToDate +encode: object +description: | + Truncates a date. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to truncate, specified in UTC. The date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: unit + type: + - timeUnit + description: | + The unit of time, specified as an expression that must resolve to one of these strings: year, quarter, week, month, day, hour, minute, second. + Together, binSize and unit specify the time period used in the $dateTrunc calculation. + - + name: binSize + type: + - resolvesToNumber + optional: true + description: | + The numeric time value, specified as an expression that must resolve to a positive non-zero number. Defaults to 1. + Together, binSize and unit specify the time period used in the $dateTrunc calculation. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + - + name: startOfWeek + type: + - string + optional: true + description: | + The start of the week. Used when + unit is week. Defaults to Sunday. +tests: + - + name: 'Truncate Order Dates in a $project Pipeline Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/#truncate-order-dates-in-a--project-pipeline-stage' + pipeline: + - + $project: + _id: 1 + orderDate: 1 + truncatedOrderDate: + $dateTrunc: + date: '$orderDate' + unit: 'week' + binSize: 2 + timezone: 'America/Los_Angeles' + startOfWeek: 'Monday' + - + name: 'Truncate Order Dates and Obtain Quantity Sum in a $group Pipeline Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/#truncate-order-dates-and-obtain-quantity-sum-in-a--group-pipeline-stage' + pipeline: + - + $group: + _id: + truncatedOrderDate: + $dateTrunc: + date: '$orderDate' + unit: 'month' + binSize: 6 + sumQuantity: + $sum: '$quantity' diff --git a/generator/config/expression/dayOfMonth.yaml b/generator/config/expression/dayOfMonth.yaml new file mode 100644 index 000000000..46a4de0b7 --- /dev/null +++ b/generator/config/expression/dayOfMonth.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $dayOfMonth +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfMonth/' +type: + - resolvesToInt +encode: object +description: | + Returns the day of the month for a date as a number between 1 and 31. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfMonth/#example' + pipeline: + - + $project: + day: + # Example uses the short form, the builder always generates the verbose form + # $dayOfMonth: '$date' + $dayOfMonth: + date: '$date' diff --git a/generator/config/expression/dayOfWeek.yaml b/generator/config/expression/dayOfWeek.yaml new file mode 100644 index 000000000..27a6a809d --- /dev/null +++ b/generator/config/expression/dayOfWeek.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $dayOfWeek +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfWeek/' +type: + - resolvesToInt +encode: object +description: | + Returns the day of the week for a date as a number between 1 (Sunday) and 7 (Saturday). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfWeek/#example' + pipeline: + - + $project: + dayOfWeek: + # Example uses the short form, the builder always generates the verbose form + # $dayOfWeek: '$date' + $dayOfWeek: + date: '$date' diff --git a/generator/config/expression/dayOfYear.yaml b/generator/config/expression/dayOfYear.yaml new file mode 100644 index 000000000..8caa0374d --- /dev/null +++ b/generator/config/expression/dayOfYear.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $dayOfYear +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfYear/' +type: + - resolvesToInt +encode: object +description: | + Returns the day of the year for a date as a number between 1 and 366 (leap year). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfYear/#example' + pipeline: + - + $project: + dayOfYear: + # Example uses the short form, the builder always generates the verbose form + # $dayOfYear: '$date' + $dayOfYear: + date: '$date' diff --git a/generator/config/expression/degreesToRadians.yaml b/generator/config/expression/degreesToRadians.yaml new file mode 100644 index 000000000..59c18d2e3 --- /dev/null +++ b/generator/config/expression/degreesToRadians.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $degreesToRadians +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/degreesToRadians/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Converts a value from degrees to radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $degreesToRadians takes any valid expression that resolves to a number. + By default $degreesToRadians returns values as a double. $degreesToRadians can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/degreesToRadians/#example' + pipeline: + - + $addFields: + angle_a_rad: + $degreesToRadians: '$angle_a' + angle_b_rad: + $degreesToRadians: '$angle_b' + angle_c_rad: + $degreesToRadians: '$angle_c' diff --git a/generator/config/expression/divide.yaml b/generator/config/expression/divide.yaml new file mode 100644 index 000000000..3b69389d8 --- /dev/null +++ b/generator/config/expression/divide.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $divide +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/divide/' +type: + - resolvesToDouble +encode: array +description: | + Returns the result of dividing the first number by the second. Accepts two argument expressions. +arguments: + - + name: dividend + type: + - resolvesToNumber + description: | + The first argument is the dividend, and the second argument is the divisor; i.e. the first argument is divided by the second argument. + - + name: divisor + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/divide/#example' + pipeline: + - + $project: + city: 1 + workdays: + $divide: + - '$hours' + - 8 diff --git a/generator/config/expression/eq.yaml b/generator/config/expression/eq.yaml new file mode 100644 index 000000000..009280ba1 --- /dev/null +++ b/generator/config/expression/eq.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $eq +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/eq/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the values are equivalent. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/eq/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyEq250: + $eq: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/exp.yaml b/generator/config/expression/exp.yaml new file mode 100644 index 000000000..d1f6982a1 --- /dev/null +++ b/generator/config/expression/exp.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $exp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/exp/' +type: + - resolvesToDouble +encode: single +description: | + Raises e to the specified exponent. +arguments: + - + name: exponent + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/exp/#example' + pipeline: + - + $project: + effectiveRate: + $subtract: + - + $exp: '$interestRate' + - 1 diff --git a/generator/config/expression/filter.yaml b/generator/config/expression/filter.yaml new file mode 100644 index 000000000..0b72f6d75 --- /dev/null +++ b/generator/config/expression/filter.yaml @@ -0,0 +1,94 @@ +# $schema: ../schema.json +name: $filter +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/' +type: + - resolvesToArray +encode: object +description: | + Selects a subset of the array to return an array with only the elements that match the filter condition. +arguments: + - + name: input + type: + - resolvesToArray + - + name: cond + type: + - resolvesToBool + description: | + An expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as. + - + name: as + type: + - string + optional: true + description: | + A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + - + name: limit + type: + - resolvesToInt + optional: true + description: | + A number expression that restricts the number of matching array elements that $filter returns. You cannot specify a limit less than 1. The matching array elements are returned in the order they appear in the input array. + If the specified limit is greater than the number of matching array elements, $filter returns all matching array elements. If the limit is null, $filter returns all matching array elements. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#examples' + pipeline: + - + $project: + items: + $filter: + input: '$items' + as: 'item' + cond: + $gte: + - '$$item.price' + - 100 + - + name: 'Using the limit field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#using-the-limit-field' + pipeline: + - + $project: + items: + $filter: + input: '$items' + cond: + $gte: + - '$$item.price' + - 100 + as: 'item' + limit: 1 + - + name: 'limit as a Numeric Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#limit-as-a-numeric-expression' + pipeline: + - + $project: + items: + $filter: + input: '$items' + cond: + $lte: + - '$$item.price' + - 150 + as: 'item' + limit: 2 + - + name: 'limit Greater than Possible Matches' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#limit-greater-than-possible-matches' + pipeline: + - + $project: + items: + $filter: + input: '$items' + cond: + $gte: + - '$$item.price' + - 100 + as: 'item' + limit: 5 diff --git a/generator/config/expression/first.yaml b/generator/config/expression/first.yaml new file mode 100644 index 000000000..262d340c3 --- /dev/null +++ b/generator/config/expression/first.yaml @@ -0,0 +1,21 @@ +# $schema: ../schema.json +name: $first +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/' +type: + - resolvesToAny +encode: single +description: | + Returns the result of an expression for the first document in an array. +arguments: + - + name: expression + type: + - resolvesToArray +tests: + - + name: 'Use in $addFields Stage' + pipeline: + - + $addFields: + firstItem: + $first: '$items' diff --git a/generator/config/expression/firstN.yaml b/generator/config/expression/firstN.yaml new file mode 100644 index 000000000..e914ff88c --- /dev/null +++ b/generator/config/expression/firstN.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $firstN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN-array-element/' +type: + - resolvesToArray +encode: object +description: | + Returns a specified number of elements from the beginning of an array. +arguments: + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return n elements. + +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN-array-element/#example' + pipeline: + - + $addFields: + firstScores: + $firstN: + n: 3 + input: '$score' + - + name: 'Using $firstN as an Aggregation Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN/#using--firstn-as-an-aggregation-expression' + pipeline: + - + $documents: + - + array: [10, 20, 30, 40] + - + $project: + firstThreeElements: + $firstN: + input: '$array' + n: 3 diff --git a/generator/config/expression/floor.yaml b/generator/config/expression/floor.yaml new file mode 100644 index 000000000..4c5856264 --- /dev/null +++ b/generator/config/expression/floor.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $floor +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/floor/' +type: + - resolvesToInt +encode: single +description: | + Returns the largest integer less than or equal to the specified number. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/floor/#example' + pipeline: + - + $project: + value: 1 + floorValue: + $floor: '$value' diff --git a/generator/config/expression/function.yaml b/generator/config/expression/function.yaml new file mode 100644 index 000000000..fa4dba8b5 --- /dev/null +++ b/generator/config/expression/function.yaml @@ -0,0 +1,74 @@ +# $schema: ../schema.json +name: $function +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/function/' +type: + - resolvesToAny +encode: object +description: | + Defines a custom function. + New in MongoDB 4.4. +arguments: + - + name: body + type: + - javascript + description: | + The function definition. You can specify the function definition as either BSON\JavaScript or string. + function(arg1, arg2, ...) { ... } + - + name: args + type: + - array + description: | + Arguments passed to the function body. If the body function does not take an argument, you can specify an empty array [ ]. + default: [] + - + name: lang + type: + - string + default: js +tests: + - + name: 'Usage Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/function/#example-1--usage-example' + pipeline: + - + $addFields: + isFound: + $function: + body: + $code: |- + function(name) { + return hex_md5(name) == "15b0a220baa16331e8d80e15367677ad" + } + args: + - '$name' + lang: 'js' + message: + $function: + body: + $code: |- + function(name, scores) { + let total = Array.sum(scores); + return `Hello ${name}. Your total score is ${total}.` + } + args: + - '$name' + - '$scores' + lang: 'js' + - + name: 'Alternative to $where' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/function/#example-2--alternative-to--where' + pipeline: + - + $match: + $expr: + $function: + body: + $code: |- + function(name) { + return hex_md5(name) == "15b0a220baa16331e8d80e15367677ad"; + } + args: + - '$name' + lang: 'js' diff --git a/generator/config/expression/getField.yaml b/generator/config/expression/getField.yaml new file mode 100644 index 000000000..04b5d4ace --- /dev/null +++ b/generator/config/expression/getField.yaml @@ -0,0 +1,69 @@ +# $schema: ../schema.json +name: $getField +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/' +type: + - resolvesToAny +encode: object +description: | + Returns the value of a specified field from a document. You can use $getField to retrieve the value of fields with names that contain periods (.) or start with dollar signs ($). + New in MongoDB 5.0. +arguments: + - + name: field + type: + - resolvesToString + description: | + Field in the input object for which you want to return a value. field can be any valid expression that resolves to a string constant. + If field begins with a dollar sign ($), place the field name inside of a $literal expression to return its value. + - + name: input + type: + - expression + optional: true + description: | + Default: $$CURRENT + A valid expression that contains the field for which you want to return a value. input must resolve to an object, missing, null, or undefined. If omitted, defaults to the document currently being processed in the pipeline ($$CURRENT). +tests: + - + name: 'Query Fields that Contain Periods' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/#query-fields-that-contain-periods--.-' + pipeline: + - + $match: + $expr: + $gt: + - + # Example uses the short form, the builder always generates the verbose form + # $getField: 'price.usd' + $getField: + field: 'price.usd' + - 200 + - + name: 'Query Fields that Start with a Dollar Sign' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/#query-fields-that-start-with-a-dollar-sign----' + pipeline: + - + $match: + $expr: + $gt: + - + $getField: + # Example uses the short form, the builder always generates the verbose form + # $literal: '$price' + field: + $literal: '$price' + - 200 + - + name: 'Query a Field in a Sub-document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/#query-a-field-in-a-sub-document' + pipeline: + - + $match: + $expr: + $lte: + - + $getField: + field: + $literal: '$small' + input: '$quantity' + - 20 diff --git a/generator/config/expression/gt.yaml b/generator/config/expression/gt.yaml new file mode 100644 index 000000000..ddd4dcdd0 --- /dev/null +++ b/generator/config/expression/gt.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $gt +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/gt/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the first value is greater than the second. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/gt/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyGt250: + $gt: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/gte.yaml b/generator/config/expression/gte.yaml new file mode 100644 index 000000000..6b456daf6 --- /dev/null +++ b/generator/config/expression/gte.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $gte +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/gte/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the first value is greater than or equal to the second. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/gte/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyGte250: + $gte: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/hour.yaml b/generator/config/expression/hour.yaml new file mode 100644 index 000000000..ebd62c3a0 --- /dev/null +++ b/generator/config/expression/hour.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $hour +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/hour/' +type: + - resolvesToInt +encode: object +description: | + Returns the hour for a date as a number between 0 and 23. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/hour/#example' + pipeline: + - + $project: + hour: + # Example uses the short form, the builder always generates the verbose form + # $hour: '$date' + $hour: + date: '$date' diff --git a/generator/config/expression/ifNull.yaml b/generator/config/expression/ifNull.yaml new file mode 100644 index 000000000..9be8e9044 --- /dev/null +++ b/generator/config/expression/ifNull.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $ifNull +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ifNull/' +type: + - resolvesToAny +encode: single +description: | + Returns either the non-null result of the first expression or the result of the second expression if the first expression results in a null result. Null result encompasses instances of undefined values or missing fields. Accepts two expressions as arguments. The result of the second expression can be null. +arguments: + - + name: expression + type: + - expression + variadic: array +tests: + - + name: 'Single Input Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ifNull/#single-input-expression' + pipeline: + - + $project: + item: 1 + description: + $ifNull: + - '$description' + - 'Unspecified' + - + name: 'Multiple Input Expressions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ifNull/#multiple-input-expressions' + pipeline: + - + $project: + item: 1 + value: + $ifNull: + - '$description' + - '$quantity' + - 'Unspecified' diff --git a/generator/config/expression/in.yaml b/generator/config/expression/in.yaml new file mode 100644 index 000000000..7cef8c149 --- /dev/null +++ b/generator/config/expression/in.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $in +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/in/' +type: + - resolvesToBool +encode: array +description: | + Returns a boolean indicating whether a specified value is in an array. +arguments: + - + name: expression + type: + - expression + description: | + Any valid expression expression. + - + name: array + type: + - resolvesToArray + description: | + Any valid expression that resolves to an array. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/in/#example' + pipeline: + - + $project: + store location: '$location' + has bananas: + $in: + - 'bananas' + - '$in_stock' diff --git a/generator/config/expression/indexOfArray.yaml b/generator/config/expression/indexOfArray.yaml new file mode 100644 index 000000000..5840ee0fa --- /dev/null +++ b/generator/config/expression/indexOfArray.yaml @@ -0,0 +1,48 @@ +# $schema: ../schema.json +name: $indexOfArray +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfArray/' +type: + - resolvesToInt +encode: array +description: | + Searches an array for an occurrence of a specified value and returns the array index of the first occurrence. Array indexes start at zero. +arguments: + - + name: array + type: + - resolvesToArray + description: | + Can be any valid expression as long as it resolves to an array. + If the array expression resolves to a value of null or refers to a field that is missing, $indexOfArray returns null. + If the array expression does not resolve to an array or null nor refers to a missing field, $indexOfArray returns an error. + - + name: search + type: + - expression + - + name: start + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + If unspecified, the starting index position for the search is the beginning of the string. + - + name: end + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + If unspecified, the ending index position for the search is the end of the string. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfArray/#example' + pipeline: + - + $project: + index: + $indexOfArray: + - '$items' + - 2 diff --git a/generator/config/expression/indexOfBytes.yaml b/generator/config/expression/indexOfBytes.yaml new file mode 100644 index 000000000..7fe890891 --- /dev/null +++ b/generator/config/expression/indexOfBytes.yaml @@ -0,0 +1,50 @@ +# $schema: ../schema.json +name: $indexOfBytes +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfBytes/' +type: + - resolvesToInt +encode: array +description: | + Searches a string for an occurrence of a substring and returns the UTF-8 byte index of the first occurrence. If the substring is not found, returns -1. +arguments: + - + name: string + type: + - resolvesToString + description: | + Can be any valid expression as long as it resolves to a string. + If the string expression resolves to a value of null or refers to a field that is missing, $indexOfBytes returns null. + If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfBytes returns an error. + - + name: substring + type: + - resolvesToString + description: | + Can be any valid expression as long as it resolves to a string. + - + name: start + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + If unspecified, the starting index position for the search is the beginning of the string. + - + name: end + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + If unspecified, the ending index position for the search is the end of the string. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfBytes/#examples' + pipeline: + - + $project: + byteLocation: + $indexOfBytes: + - '$item' + - 'foo' diff --git a/generator/config/expression/indexOfCP.yaml b/generator/config/expression/indexOfCP.yaml new file mode 100644 index 000000000..88ed55ec6 --- /dev/null +++ b/generator/config/expression/indexOfCP.yaml @@ -0,0 +1,50 @@ +# $schema: ../schema.json +name: $indexOfCP +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfCP/' +type: + - resolvesToInt +encode: array +description: | + Searches a string for an occurrence of a substring and returns the UTF-8 code point index of the first occurrence. If the substring is not found, returns -1 +arguments: + - + name: string + type: + - resolvesToString + description: | + Can be any valid expression as long as it resolves to a string. + If the string expression resolves to a value of null or refers to a field that is missing, $indexOfCP returns null. + If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfCP returns an error. + - + name: substring + type: + - resolvesToString + description: | + Can be any valid expression as long as it resolves to a string. + - + name: start + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + If unspecified, the starting index position for the search is the beginning of the string. + - + name: end + type: + - resolvesToInt + optional: true + description: | + An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + If unspecified, the ending index position for the search is the end of the string. +tests: + - + name: 'Examples' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfCP/#examples' + pipeline: + - + $project: + cpLocation: + $indexOfCP: + - '$item' + - 'foo' diff --git a/generator/config/expression/isArray.yaml b/generator/config/expression/isArray.yaml new file mode 100644 index 000000000..b9a5d5cb5 --- /dev/null +++ b/generator/config/expression/isArray.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $isArray +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isArray/' +type: + - resolvesToBool +encode: array +description: | + Determines if the operand is an array. Returns a boolean. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isArray/#example' + pipeline: + - + $project: + items: + $cond: + if: + $and: + # Example uses the short form, the builder always generates the verbose form + - + $isArray: + - '$instock' + - + $isArray: + - '$ordered' + then: + $concatArrays: + - '$instock' + - '$ordered' + else: 'One or more fields is not an array.' diff --git a/generator/config/expression/isNumber.yaml b/generator/config/expression/isNumber.yaml new file mode 100644 index 000000000..3bce99e99 --- /dev/null +++ b/generator/config/expression/isNumber.yaml @@ -0,0 +1,75 @@ +# $schema: ../schema.json +name: $isNumber +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isNumber/' +type: + - resolvesToBool +encode: single +description: | + Returns boolean true if the specified expression resolves to an integer, decimal, double, or long. + Returns boolean false if the expression resolves to any other BSON type, null, or a missing field. + New in MongoDB 4.4. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Use $isNumber to Check if a Field is Numeric' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isNumber/#use--isnumber-to-check-if-a-field-is-numeric' + pipeline: + - + $addFields: + isNumber: + $isNumber: '$reading' + hasType: + $type: '$reading' + - + name: 'Conditionally Modify Fields using $isNumber' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isNumber/#conditionally-modify-fields-using--isnumber' + pipeline: + - + $addFields: + points: + $cond: + if: + $isNumber: '$grade' + then: '$grade' + else: + $switch: + branches: + - + case: + $eq: + - '$grade' + - 'A' + then: 4 + - + case: + $eq: + - '$grade' + - 'B' + then: 3 + - + case: + $eq: + - '$grade' + - 'C' + then: 2 + - + case: + $eq: + - '$grade' + - 'D' + then: 1 + - + case: + $eq: + - '$grade' + - 'F' + then: 0 + - + $group: + _id: '$student_id' + GPA: + $avg: '$points' diff --git a/generator/config/expression/isoDayOfWeek.yaml b/generator/config/expression/isoDayOfWeek.yaml new file mode 100644 index 000000000..1956ff5c4 --- /dev/null +++ b/generator/config/expression/isoDayOfWeek.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $isoDayOfWeek +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoDayOfWeek/' +type: + - resolvesToInt +encode: object +description: | + Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) to 7 (for Sunday). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoDayOfWeek/#example' + pipeline: + - + $project: + _id: 0 + name: '$name' + dayOfWeek: + # Example uses the short form, the builder always generates the verbose form + # $isoDayOfWeek: '$birthday' + $isoDayOfWeek: + date: '$birthday' diff --git a/generator/config/expression/isoWeek.yaml b/generator/config/expression/isoWeek.yaml new file mode 100644 index 000000000..2958a20c3 --- /dev/null +++ b/generator/config/expression/isoWeek.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $isoWeek +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeek/' +type: + - resolvesToInt +encode: object +description: | + Returns the week number in ISO 8601 format, ranging from 1 to 53. Week numbers start at 1 with the week (Monday through Sunday) that contains the year's first Thursday. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeek/#example' + pipeline: + - + $project: + _id: 0 + city: '$city' + weekNumber: + # Example uses the short form, the builder always generates the verbose form + # $isoWeek: '$date' + $isoWeek: + date: '$date' diff --git a/generator/config/expression/isoWeekYear.yaml b/generator/config/expression/isoWeekYear.yaml new file mode 100644 index 000000000..5dcefa7dd --- /dev/null +++ b/generator/config/expression/isoWeekYear.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $isoWeekYear +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeekYear/' +type: + - resolvesToInt +encode: object +description: | + Returns the year number in ISO 8601 format. The year starts with the Monday of week 1 (ISO 8601) and ends with the Sunday of the last week (ISO 8601). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeekYear/#example' + pipeline: + - + $project: + yearNumber: + # Example uses the short form, the builder always generates the verbose form + # $isoWeekYear: '$date' + $isoWeekYear: + date: '$date' diff --git a/generator/config/expression/last.yaml b/generator/config/expression/last.yaml new file mode 100644 index 000000000..2bf18dc7b --- /dev/null +++ b/generator/config/expression/last.yaml @@ -0,0 +1,21 @@ +# $schema: ../schema.json +name: $last +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/' +type: + - resolvesToAny +encode: single +description: | + Returns the result of an expression for the last document in an array. +arguments: + - + name: expression + type: + - resolvesToArray +tests: + - + name: 'Use in $addFields Stage' + pipeline: + - + $addFields: + lastItem: + $last: '$items' diff --git a/generator/config/expression/lastN.yaml b/generator/config/expression/lastN.yaml new file mode 100644 index 000000000..0c29ad76e --- /dev/null +++ b/generator/config/expression/lastN.yaml @@ -0,0 +1,51 @@ +# $schema: ../schema.json +name: $lastN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN-array-element/' +type: + - resolvesToArray +encode: object +description: | + Returns a specified number of elements from the end of an array. +arguments: + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return n elements. +tests: + - + name: Example + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN-array-element/#example' + pipeline: + - + $addFields: + lastScores: + $lastN: + n: 3 + input: '$score' + + - + name: 'Using $lastN as an Aggregation Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN/#using--lastn-as-an-aggregation-expression' + pipeline: + - + $documents: + - + array: + - 10 + - 20 + - 30 + - 40 + - + $project: + lastThreeElements: + $lastN: + input: '$array' + n: 3 diff --git a/generator/config/expression/let.yaml b/generator/config/expression/let.yaml new file mode 100644 index 000000000..7d3017282 --- /dev/null +++ b/generator/config/expression/let.yaml @@ -0,0 +1,46 @@ +# $schema: ../schema.json +name: $let +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/' +type: + - resolvesToAny +encode: object +description: | + Defines variables for use within the scope of a subexpression and returns the result of the subexpression. Accepts named parameters. + Accepts any number of argument expressions. +arguments: + - + name: vars + type: + - object # of expression + description: | + Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. + The variable assignments have no meaning outside the in expression, not even within the vars block itself. + - + name: in + type: + - expression + description: | + The expression to evaluate. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/#example' + pipeline: + - + $project: + finalTotal: + $let: + vars: + total: + $add: + - '$price' + - '$tax' + discounted: + $cond: + if: '$applyDiscount' + then: 0.9 + else: 1 + in: + $multiply: + - '$$total' + - '$$discounted' diff --git a/generator/config/expression/literal.yaml b/generator/config/expression/literal.yaml new file mode 100644 index 000000000..dfae7bbb3 --- /dev/null +++ b/generator/config/expression/literal.yaml @@ -0,0 +1,15 @@ +# $schema: ../schema.json +name: $literal +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/literal/' +type: + - resolvesToAny +encode: single +description: | + Return a value without parsing. Use for values that the aggregation pipeline may interpret as an expression. For example, use a $literal expression to a string that starts with a dollar sign ($) to avoid parsing as a field path. +arguments: + - + name: value + type: + - any + description: | + If the value is an expression, $literal does not evaluate the expression but instead returns the unparsed expression. diff --git a/generator/config/expression/ln.yaml b/generator/config/expression/ln.yaml new file mode 100644 index 000000000..f7412aeb9 --- /dev/null +++ b/generator/config/expression/ln.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $ln +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ln/' +type: + - resolvesToDouble +encode: single +description: | + Calculates the natural log of a number. + $ln is equivalent to $log: [ , Math.E ] expression, where Math.E is a JavaScript representation for Euler's number e. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + Any valid expression as long as it resolves to a non-negative number. For more information on expressions, see Expressions. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ln/#example' + pipeline: + - + $project: + x: '$year' + y: + $ln: '$sales' diff --git a/generator/config/expression/log.yaml b/generator/config/expression/log.yaml new file mode 100644 index 000000000..462eb9492 --- /dev/null +++ b/generator/config/expression/log.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $log +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/log/' +type: + - resolvesToDouble +encode: array +description: | + Calculates the log of a number in the specified base. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + Any valid expression as long as it resolves to a non-negative number. + - + name: base + type: + - resolvesToNumber + description: | + Any valid expression as long as it resolves to a positive number greater than 1. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/log/#example' + pipeline: + - + $project: + bitsNeeded: + $floor: + $add: + - 1 + - + $log: + - '$int' + - 2 diff --git a/generator/config/expression/log10.yaml b/generator/config/expression/log10.yaml new file mode 100644 index 000000000..77cab075a --- /dev/null +++ b/generator/config/expression/log10.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $log10 +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/log10/' +type: + - resolvesToDouble +encode: single +description: | + Calculates the log base 10 of a number. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + Any valid expression as long as it resolves to a non-negative number. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/log10/#example' + pipeline: + - + $project: + pH: + $multiply: + - -1 + - + $log10: '$H3O' diff --git a/generator/config/expression/lt.yaml b/generator/config/expression/lt.yaml new file mode 100644 index 000000000..4b7319b97 --- /dev/null +++ b/generator/config/expression/lt.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $lt +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lt/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the first value is less than the second. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lt/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyLt250: + $lt: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/lte.yaml b/generator/config/expression/lte.yaml new file mode 100644 index 000000000..91c7b1d88 --- /dev/null +++ b/generator/config/expression/lte.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $lte +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lte/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the first value is less than or equal to the second. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lte/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyLte250: + $lte: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/ltrim.yaml b/generator/config/expression/ltrim.yaml new file mode 100644 index 000000000..992b70616 --- /dev/null +++ b/generator/config/expression/ltrim.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $ltrim +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ltrim/' +type: + - resolvesToString +encode: object +description: | + Removes whitespace or the specified characters from the beginning of a string. + New in MongoDB 4.0. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string to trim. The argument can be any valid expression that resolves to a string. + - + name: chars + type: + - resolvesToString + optional: true + description: | + The character(s) to trim from the beginning of the input. + The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + If unspecified, $ltrim removes whitespace characters, including the null character. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ltrim/#example' + pipeline: + - + $project: + item: 1 + description: + $ltrim: + input: '$description' diff --git a/generator/config/expression/map.yaml b/generator/config/expression/map.yaml new file mode 100644 index 000000000..11db40c24 --- /dev/null +++ b/generator/config/expression/map.yaml @@ -0,0 +1,76 @@ +# $schema: ../schema.json +name: $map +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/' +type: + - resolvesToArray +encode: object +description: | + Applies a subexpression to each element of an array and returns the array of resulting values in order. Accepts named parameters. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to an array. + - + name: as + type: + - resolvesToString + optional: true + description: | + A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + - + name: in + type: + - expression + description: | + An expression that is applied to each element of the input array. The expression references each element individually with the variable name specified in as. +tests: + - + name: 'Add to Each Element of an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/#add-to-each-element-of-an-array' + pipeline: + - + $project: + adjustedGrades: + $map: + input: '$quizzes' + as: 'grade' + in: + $add: + - '$$grade' + - 2 + - + name: 'Truncate Each Array Element' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/#truncate-each-array-element' + pipeline: + - + $project: + city: '$city' + integerValues: + $map: + input: '$distances' + as: 'decimalValue' + in: + # Example uses the short form, the builder always generates the verbose form + # $trunc: '$$decimalValue' + $trunc: + - '$$decimalValue' + - + name: 'Convert Celsius Temperatures to Fahrenheit' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/#convert-celsius-temperatures-to-fahrenheit' + pipeline: + - + $addFields: + tempsF: + $map: + input: '$tempsC' + as: 'tempInCelsius' + in: + $add: + - + $multiply: + - '$$tempInCelsius' + - 1.8 + - 32 diff --git a/generator/config/expression/max.yaml b/generator/config/expression/max.yaml new file mode 100644 index 000000000..b413679c5 --- /dev/null +++ b/generator/config/expression/max.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: $max +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/' +type: + - resolvesToAny +encode: single +description: | + Returns the maximum value that results from applying an expression to each document. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression + variadic: array +tests: + - + name: 'Use in $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/#use-in--project-stage' + pipeline: + - + $project: + quizMax: + # Example uses the short form, the builder always generates the verbose form + # $max: '$quizzes' + $max: + - '$quizzes' + labMax: + # $max: '$labs' + $max: + - '$labs' + examMax: + $max: + - '$final' + - '$midterm' diff --git a/generator/config/expression/maxN.yaml b/generator/config/expression/maxN.yaml new file mode 100644 index 000000000..e0d9d243e --- /dev/null +++ b/generator/config/expression/maxN.yaml @@ -0,0 +1,32 @@ +# $schema: ../schema.json +name: $maxN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN-array-element/' +type: + - resolvesToArray +encode: object +description: | + Returns the n largest values in an array. Distinct from the $maxN accumulator. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return the maximal n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN-array-element/#example' + pipeline: + - + $addFields: + maxScores: + $maxN: + n: 2 + input: '$score' diff --git a/generator/config/expression/median.yaml b/generator/config/expression/median.yaml new file mode 100644 index 000000000..2aac26a1f --- /dev/null +++ b/generator/config/expression/median.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $median +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/' +type: + - resolvesToDouble +encode: object +description: | + Returns an approximation of the median, the 50th percentile, as a scalar value. + New in MongoDB 7.0. + This operator is available as an accumulator in these stages: + $group + $setWindowFields + It is also available as an aggregation expression. +arguments: + - + name: input + type: + - resolvesToNumber + - array # of number + description: | + $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. + - + name: method + type: + - accumulatorPercentile + description: | + The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. +tests: + - + name: 'Use $median in a $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/#use-operatorname-in-a--project-stage' + pipeline: + - + $project: + _id: 0 + studentId: 1 + testMedians: + $median: + input: + - '$test01' + - '$test02' + - '$test03' + method: 'approximate' diff --git a/generator/config/expression/mergeObjects.yaml b/generator/config/expression/mergeObjects.yaml new file mode 100644 index 000000000..928166852 --- /dev/null +++ b/generator/config/expression/mergeObjects.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $mergeObjects +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/' +type: + - resolvesToObject +encode: single +description: | + Combines multiple documents into a single document. +arguments: + - + name: document + type: + - resolvesToObject + variadic: array + description: | + Any valid expression that resolves to a document. +tests: + - + name: '$mergeObjects' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/#-mergeobjects' + pipeline: + - + $lookup: + from: 'items' + localField: 'item' + foreignField: 'item' + as: 'fromItems' + - + $replaceRoot: + newRoot: + $mergeObjects: + - + $arrayElemAt: + - '$fromItems' + - 0 + - '$$ROOT' + - + $project: + fromItems: 0 diff --git a/generator/config/expression/meta.yaml b/generator/config/expression/meta.yaml new file mode 100644 index 000000000..9a96f7aaf --- /dev/null +++ b/generator/config/expression/meta.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $meta +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/meta/' +type: + - resolvesToAny +encode: single +description: | + Access available per-document metadata related to the aggregation operation. +arguments: + - + name: keyword + type: + - string +tests: + - + name: 'textScore' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/meta/#-meta---textscore-' + pipeline: + - + $match: + $text: + $search: 'cake' + - + $group: + _id: + $meta: 'textScore' + count: + $sum: 1 + - + name: 'indexKey' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/meta/#-meta---indexkey-' + pipeline: + - + $match: + type: 'apparel' + - + $addFields: + idxKey: + $meta: 'indexKey' diff --git a/generator/config/expression/millisecond.yaml b/generator/config/expression/millisecond.yaml new file mode 100644 index 000000000..af1d26e75 --- /dev/null +++ b/generator/config/expression/millisecond.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $millisecond +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/millisecond/' +type: + - resolvesToInt +encode: object +description: | + Returns the milliseconds of a date as a number between 0 and 999. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/millisecond/#example' + pipeline: + - + $project: + milliseconds: + # Example uses the short form, the builder always generates the verbose form + # $millisecond: '$date' + $millisecond: + date: '$date' diff --git a/generator/config/expression/min.yaml b/generator/config/expression/min.yaml new file mode 100644 index 000000000..1212fd4f7 --- /dev/null +++ b/generator/config/expression/min.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: $min +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/' +type: + - resolvesToAny +encode: single +description: | + Returns the minimum value that results from applying an expression to each document. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - expression + variadic: array +tests: + - + name: 'Use in $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/#use-in--project-stage' + pipeline: + - + $project: + quizMin: + # Example uses the short form, the builder always generates the verbose form + # $min: '$quizzes' + $min: + - '$quizzes' + labMin: + # $min: '$labs' + $min: + - '$labs' + examMin: + $min: + - '$final' + - '$midterm' diff --git a/generator/config/expression/minN.yaml b/generator/config/expression/minN.yaml new file mode 100644 index 000000000..e7fa8cac1 --- /dev/null +++ b/generator/config/expression/minN.yaml @@ -0,0 +1,32 @@ +# $schema: ../schema.json +name: $minN +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN-array-element/' +type: + - resolvesToArray +encode: object +description: | + Returns the n smallest values in an array. Distinct from the $minN accumulator. +arguments: + - + name: input + type: + - resolvesToArray + description: | + An expression that resolves to the array from which to return the maximal n elements. + - + name: 'n' + type: + - resolvesToInt + description: | + An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN-array-element/#example' + pipeline: + - + $addFields: + minScores: + $minN: + n: 2 + input: '$score' diff --git a/generator/config/expression/minute.yaml b/generator/config/expression/minute.yaml new file mode 100644 index 000000000..109e87f6b --- /dev/null +++ b/generator/config/expression/minute.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $minute +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minute/' +type: + - resolvesToInt +encode: object +description: | + Returns the minute for a date as a number between 0 and 59. +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/minute/#example' + pipeline: + - + $project: + minutes: + # Example uses the short form, the builder always generates the verbose form + # $minute: '$date' + $minute: + date: '$date' diff --git a/generator/config/expression/mod.yaml b/generator/config/expression/mod.yaml new file mode 100644 index 000000000..0ac48bd54 --- /dev/null +++ b/generator/config/expression/mod.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $mod +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mod/' +type: + - resolvesToInt +encode: array +description: | + Returns the remainder of the first number divided by the second. Accepts two argument expressions. +arguments: + - + name: dividend + type: + - resolvesToNumber + description: | + The first argument is the dividend, and the second argument is the divisor; i.e. first argument is divided by the second argument. + - + name: divisor + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/mod/#example' + pipeline: + - + $project: + remainder: + $mod: + - '$hours' + - '$tasks' diff --git a/generator/config/expression/month.yaml b/generator/config/expression/month.yaml new file mode 100644 index 000000000..7bd383be9 --- /dev/null +++ b/generator/config/expression/month.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $month +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/month/' +type: + - resolvesToInt +encode: object +description: | + Returns the month for a date as a number between 1 (January) and 12 (December). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/month/#example' + pipeline: + - + $project: + month: + # Example uses the short form, the builder always generates the verbose form + # $month: '$date' + $month: + date: '$date' diff --git a/generator/config/expression/multiply.yaml b/generator/config/expression/multiply.yaml new file mode 100644 index 000000000..5a069cc8c --- /dev/null +++ b/generator/config/expression/multiply.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $multiply +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/multiply/' +type: + - resolvesToDecimal +encode: single +description: | + Multiplies numbers to return the product. Accepts any number of argument expressions. +arguments: + - + name: expression + type: + - resolvesToNumber + variadic: array + description: | + The arguments can be any valid expression as long as they resolve to numbers. + Starting in MongoDB 6.1 you can optimize the $multiply operation. To improve performance, group references at the end of the argument list. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/multiply/#example' + pipeline: + - + $project: + date: 1 + item: 1 + total: + $multiply: + - '$price' + - '$quantity' diff --git a/generator/config/expression/ne.yaml b/generator/config/expression/ne.yaml new file mode 100644 index 000000000..da92f1014 --- /dev/null +++ b/generator/config/expression/ne.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $ne +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ne/' +type: + - resolvesToBool +encode: array +description: | + Returns true if the values are not equivalent. +arguments: + - + name: expression1 + type: + - expression + - + name: expression2 + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/ne/#example' + pipeline: + - + $project: + item: 1 + qty: 1 + qtyNe250: + $ne: + - '$qty' + - 250 + _id: 0 diff --git a/generator/config/expression/not.yaml b/generator/config/expression/not.yaml new file mode 100644 index 000000000..d4e4c90ea --- /dev/null +++ b/generator/config/expression/not.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $not +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/not/' +type: + - resolvesToBool +encode: array +description: | + Returns the boolean value that is the opposite of its argument expression. Accepts a single argument expression. +arguments: + - + name: expression + type: + - expression + - resolvesToBool +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/not/#example' + pipeline: + - + $project: + item: 1 + result: + $not: + - + $gt: + - '$qty' + - 250 diff --git a/generator/config/expression/objectToArray.yaml b/generator/config/expression/objectToArray.yaml new file mode 100644 index 000000000..460977f33 --- /dev/null +++ b/generator/config/expression/objectToArray.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $objectToArray +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/objectToArray/' +type: + - resolvesToArray +encode: single +description: | + Converts a document to an array of documents representing key-value pairs. +arguments: + - + name: object + type: + - resolvesToObject + description: | + Any valid expression as long as it resolves to a document object. $objectToArray applies to the top-level fields of its argument. If the argument is a document that itself contains embedded document fields, the $objectToArray does not recursively apply to the embedded document fields. +tests: + # "$objectToArray and $arrayToObject Example" omitted as it's already in arrayToObject.yaml + - + name: '$objectToArray Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/objectToArray/#-objecttoarray-example' + pipeline: + - + $project: + item: 1 + dimensions: + $objectToArray: '$dimensions' + - + name: '$objectToArray to Sum Nested Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/objectToArray/#-objecttoarray-to-sum-nested-fields' + pipeline: + - + $project: + warehouses: + $objectToArray: '$instock' + - + # Example uses the short form, the builder always generates the verbose form + # $unwind: '$warehouses' + $unwind: + path: '$warehouses' + - + $group: + _id: '$warehouses.k' + total: + $sum: '$warehouses.v' diff --git a/generator/config/expression/or.yaml b/generator/config/expression/or.yaml new file mode 100644 index 000000000..2bbce1910 --- /dev/null +++ b/generator/config/expression/or.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $or +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/' +type: + - resolvesToBool +encode: single +description: | + Returns true when any of its expressions evaluates to true. Accepts any number of argument expressions. +arguments: + - + name: expression + type: + - expression + - resolvesToBool + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/#example' + pipeline: + - + $project: + item: 1 + result: + $or: + - + $gt: + - '$qty' + - 250 + - + $lt: + - '$qty' + - 200 diff --git a/generator/config/expression/percentile.yaml b/generator/config/expression/percentile.yaml new file mode 100644 index 000000000..b19fc1904 --- /dev/null +++ b/generator/config/expression/percentile.yaml @@ -0,0 +1,51 @@ +# $schema: ../schema.json +name: $percentile +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/' +type: + - resolvesToArray # of scalar +encode: object +description: | + Returns an array of scalar values that correspond to specified percentile values. + New in MongoDB 7.0. + + This operator is available as an accumulator in these stages: + $group + + $setWindowFields + + It is also available as an aggregation expression. +arguments: + - + name: input + type: + - resolvesToNumber + - array # of number + description: | + $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. + - + name: p + type: + - resolvesToArray # of resolvesToNumber + description: | + $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + $percentile returns results in the same order as the elements in p. + - + name: method + type: + - accumulatorPercentile + description: | + The method that mongod uses to calculate the percentile value. The method must be 'approximate'. +tests: + - + name: 'Use $percentile in a $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/#use-operatorname-in-a--project-stage' + pipeline: + - + $project: + _id: 0 + studentId: 1 + testPercentiles: + $percentile: + input: ['$test01', '$test02', '$test03'] + p: [0.5, 0.95] + method: 'approximate' diff --git a/generator/config/expression/pow.yaml b/generator/config/expression/pow.yaml new file mode 100644 index 000000000..afab7f875 --- /dev/null +++ b/generator/config/expression/pow.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $pow +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/pow/' +type: + - resolvesToNumber +encode: array +description: | + Raises a number to the specified exponent. +arguments: + - + name: number + type: + - resolvesToNumber + - + name: exponent + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/pow/#example' + pipeline: + - + $project: + variance: + $pow: + - + # Example uses the short form, the builder always generates the verbose form + # $stdDevPop: '$scores.score' + $stdDevPop: ['$scores.score'] + - 2 diff --git a/generator/config/expression/radiansToDegrees.yaml b/generator/config/expression/radiansToDegrees.yaml new file mode 100644 index 000000000..be4dd1e1d --- /dev/null +++ b/generator/config/expression/radiansToDegrees.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $radiansToDegrees +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/radiansToDegrees/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Converts a value from radians to degrees. +arguments: + - + name: expression + type: + - resolvesToNumber +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/radiansToDegrees/#example' + pipeline: + - + $addFields: + angle_a_deg: + $radiansToDegrees: '$angle_a' + angle_b_deg: + $radiansToDegrees: '$angle_b' + angle_c_deg: + $radiansToDegrees: '$angle_c' diff --git a/generator/config/expression/rand.yaml b/generator/config/expression/rand.yaml new file mode 100644 index 000000000..a19f3163e --- /dev/null +++ b/generator/config/expression/rand.yaml @@ -0,0 +1,48 @@ +# $schema: ../schema.json +name: $rand +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rand/' +type: + - resolvesToDouble +encode: object +description: | + Returns a random float between 0 and 1 +tests: + - + name: 'Generate Random Data Points' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rand/#generate-random-data-points' + pipeline: + - + $set: + amount: + $multiply: + - + $rand: {} + - 100 + - + $set: + amount: + $floor: '$amount' + - + # Example uses the short form, the builder always generates the verbose form + # $merge: 'donors' + $merge: + into: 'donors' + - + name: 'Select Random Items From a Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rand/#select-random-items-from-a-collection' + pipeline: + - + $match: + district: 3 + - + $match: + $expr: + $lt: + - 0.5 + - + $rand: {} + - + $project: + _id: 0 + name: 1 + registered: 1 diff --git a/generator/config/expression/range.yaml b/generator/config/expression/range.yaml new file mode 100644 index 000000000..5c78d8898 --- /dev/null +++ b/generator/config/expression/range.yaml @@ -0,0 +1,42 @@ +# $schema: ../schema.json +name: $range +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/range/' +type: + - resolvesToArray # of int +encode: array +description: | + Outputs an array containing a sequence of integers according to user-defined inputs. +arguments: + - + name: start + type: + - resolvesToInt + description: | + An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. + - + name: end + type: + - resolvesToInt + description: | + An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. + - + name: step + type: + - resolvesToInt + optional: true + description: | + An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/range/#example' + pipeline: + - + $project: + _id: 0 + city: 1 + Rest stops: + $range: + - 0 + - '$distance' + - 25 diff --git a/generator/config/expression/reduce.yaml b/generator/config/expression/reduce.yaml new file mode 100644 index 000000000..bf51eabe3 --- /dev/null +++ b/generator/config/expression/reduce.yaml @@ -0,0 +1,134 @@ +# $schema: ../schema.json +name: $reduce +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/' +type: + - resolvesToAny +encode: object +description: | + Applies an expression to each element in an array and combines them into a single value. +arguments: + - + name: input + type: + - resolvesToArray + description: | + Can be any valid expression that resolves to an array. + If the argument resolves to a value of null or refers to a missing field, $reduce returns null. + If the argument does not resolve to an array or null nor refers to a missing field, $reduce returns an error. + - + name: initialValue + type: + - expression + description: | + The initial cumulative value set before in is applied to the first element of the input array. + - + name: in + type: + - expression + description: | + A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + During evaluation of the in expression, two variables will be available: + - value is the variable that represents the cumulative value of the expression. + - this is the variable that refers to the element being processed. +tests: + - + name: 'Multiplication' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#multiplication' + pipeline: + - + $group: + _id: '$experimentId' + probabilityArr: + $push: '$probability' + - + $project: + description: 1 + results: + $reduce: + input: '$probabilityArr' + initialValue: 1 + in: + $multiply: + - '$$value' + - '$$this' + - + name: 'Discounted Merchandise' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#discounted-merchandise' + pipeline: + - + $project: + discountedPrice: + $reduce: + input: '$discounts' + initialValue: '$price' + in: + $multiply: + - '$$value' + - + $subtract: + - 1 + - '$$this' + - + name: 'String Concatenation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#string-concatenation' + pipeline: + # Filter to return only non-empty arrays + - + $match: + hobbies: + $gt: [] + - + $project: + name: 1 + bio: + $reduce: + input: '$hobbies' + initialValue: 'My hobbies include:' + in: + $concat: + - '$$value' + - + $cond: + if: + $eq: + - '$$value' + - 'My hobbies include:' + then: ' ' + else: ', ' + - '$$this' + - + name: 'Array Concatenation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#array-concatenation' + pipeline: + - + $project: + collapsed: + $reduce: + input: '$arr' + initialValue: [] + in: + $concatArrays: + - '$$value' + - '$$this' + - + name: 'Computing a Multiple Reductions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/#computing-a-multiple-reductions' + pipeline: + - + $project: + results: + $reduce: + input: '$arr' + initialValue: [] + in: + collapsed: + $concatArrays: + - '$$value.collapsed' + - '$$this' + firstValues: + $concatArrays: + - '$$value.firstValues' + - + $slice: + - '$$this' + - 1 diff --git a/generator/config/expression/regexFind.yaml b/generator/config/expression/regexFind.yaml new file mode 100644 index 000000000..d953a4ae6 --- /dev/null +++ b/generator/config/expression/regexFind.yaml @@ -0,0 +1,66 @@ +# $schema: ../schema.json +name: $regexFind +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFind/' +type: + - resolvesToObject +encode: object +description: | + Applies a regular expression (regex) to a string and returns information on the first matched substring. + New in MongoDB 4.2. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + - + name: regex + type: + - resolvesToString + - regex + description: | + The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + - + name: options + type: + - string + optional: true +tests: + - + name: '$regexFind and Its Options' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFind/#-regexfind-and-its-options' + pipeline: + - + $addFields: + returnObject: + $regexFind: + input: '$description' + regex: !bson_regex 'line' + - + name: 'i Option' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFind/#i-option' + pipeline: + - + $addFields: + returnObject: + # Specify i as part of the Regex type + $regexFind: + input: '$description' + regex: !bson_regex ['line', 'i'] + - + $addFields: + returnObject: + # Specify i in the options field + $regexFind: + input: '$description' + regex: 'line' + options: 'i' + - + $addFields: + returnObject: + # Mix Regex type with options field + $regexFind: + input: '$description' + regex: !bson_regex 'line' + options: 'i' diff --git a/generator/config/expression/regexFindAll.yaml b/generator/config/expression/regexFindAll.yaml new file mode 100644 index 000000000..6aea184d7 --- /dev/null +++ b/generator/config/expression/regexFindAll.yaml @@ -0,0 +1,99 @@ +# $schema: ../schema.json +name: $regexFindAll +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/' +type: + - resolvesToArray # of object +encode: object +description: | + Applies a regular expression (regex) to a string and returns information on the all matched substrings. + New in MongoDB 4.2. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + - + name: regex + type: + - resolvesToString + - regex + description: | + The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + - + name: options + type: + - string + optional: true +tests: + - + name: '$regexFindAll and Its Options' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/#-regexfindall-and-its-options' + pipeline: + - + $addFields: + returnObject: + $regexFindAll: + input: '$description' + regex: !bson_regex 'line' + - + name: 'i Option' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/#i-option' + pipeline: + - + $addFields: + returnObject: + # Specify i as part of the regex type + $regexFindAll: + input: '$description' + regex: !bson_regex ['line', 'i'] + - + $addFields: + returnObject: + # Specify i in the options field + $regexFindAll: + input: '$description' + regex: 'line' + options: 'i' + - + $addFields: + returnObject: + # Mix Regex type with options field + $regexFindAll: + input: '$description' + regex: !bson_regex 'line' + options: 'i' + - + name: 'Use $regexFindAll to Parse Email from String' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/#use--regexfindall-to-parse-email-from-string' + pipeline: + - + $addFields: + email: + $regexFindAll: + input: '$comment' + regex: !bson_regex ['[a-z0-9_.+-]+@[a-z0-9_.+-]+\.[a-z0-9_.+-]+', 'i'] + - + $set: + email: '$email.match' + - + name: 'Use Captured Groupings to Parse User Name' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/#use-captured-groupings-to-parse-user-name' + pipeline: + - + $addFields: + names: + $regexFindAll: + input: '$comment' + regex: !bson_regex ['([a-z0-9_.+-]+)@[a-z0-9_.+-]+\.[a-z0-9_.+-]+', 'i'] + - + $set: + names: + $reduce: + input: '$names.captures' + initialValue: [] + in: + $concatArrays: + - '$$value' + - '$$this' diff --git a/generator/config/expression/regexMatch.yaml b/generator/config/expression/regexMatch.yaml new file mode 100644 index 000000000..4ea9f0aab --- /dev/null +++ b/generator/config/expression/regexMatch.yaml @@ -0,0 +1,80 @@ +# $schema: ../schema.json +name: $regexMatch +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/' +type: + - resolvesToBool +encode: object +description: | + Applies a regular expression (regex) to a string and returns a boolean that indicates if a match is found or not. + New in MongoDB 4.2. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + - + name: regex + type: + - resolvesToString + - regex + description: | + The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + - + name: options + type: + - string + optional: true +tests: + - + name: '$regexMatch and Its Options' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/#-regexmatch-and-its-options' + pipeline: + - + $addFields: + result: + $regexMatch: + input: '$description' + regex: !bson_regex 'line' + - + name: 'i Option' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/#i-option' + pipeline: + - + $addFields: + result: + # Specify i as part of the Regex type + $regexMatch: + input: '$description' + regex: !bson_regex ['line', 'i'] + - + $addFields: + result: + # Specify i in the options field + $regexMatch: + input: '$description' + regex: 'line' + options: 'i' + - + $addFields: + result: + # Mix Regex type with options field + $regexMatch: + input: '$description' + regex: !bson_regex 'line' + options: 'i' + - + name: 'Use $regexMatch to Check Email Address' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/#use--regexmatch-to-check-email-address' + pipeline: + - + $addFields: + category: + $cond: + if: + $regexMatch: + input: '$comment' + regex: !bson_regex ['[a-z0-9_.+-]+@mongodb.com', 'i'] + then: 'Employee' + else: 'External' diff --git a/generator/config/expression/replaceAll.yaml b/generator/config/expression/replaceAll.yaml new file mode 100644 index 000000000..b13c58884 --- /dev/null +++ b/generator/config/expression/replaceAll.yaml @@ -0,0 +1,55 @@ +# $schema: ../schema.json +name: $replaceAll +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceAll/' +type: + - resolvesToString +encode: object +description: | + Replaces all instances of a search string in an input string with a replacement string. + $replaceAll is both case-sensitive and diacritic-sensitive, and ignores any collation present on a collection. + New in MongoDB 4.4. +arguments: + - + name: input + type: + - resolvesToString + - resolvesToNull + description: | + The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + - + name: find + type: + - resolvesToString + - resolvesToNull + - resolvesToRegex + description: | + The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + - + name: replacement + type: + - resolvesToString + - resolvesToNull + description: | + The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceAll/#example' + pipeline: + - + $project: + item: + $replaceAll: + input: '$item' + find: 'blue paint' + replacement: 'red paint' + - + name: 'Support regex search string' + pipeline: + - + $project: + item: + $replaceAll: + input: '123-456-7890' + find: !bson_regex '\d{3}' + replacement: 'xxx' diff --git a/generator/config/expression/replaceOne.yaml b/generator/config/expression/replaceOne.yaml new file mode 100644 index 000000000..0962cc6c5 --- /dev/null +++ b/generator/config/expression/replaceOne.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $replaceOne +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceOne/' +type: + - resolvesToString +encode: object +description: | + Replaces the first instance of a matched string in a given input. + New in MongoDB 4.4. +arguments: + - + name: input + type: + - resolvesToString + - resolvesToNull + description: | + The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + - + name: find + type: + - resolvesToString + - resolvesToNull + description: | + The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + - + name: replacement + type: + - resolvesToString + - resolvesToNull + description: | + The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceOne/#example' + pipeline: + - + $project: + item: + $replaceOne: + input: '$item' + find: 'blue paint' + replacement: 'red paint' diff --git a/generator/config/expression/reverseArray.yaml b/generator/config/expression/reverseArray.yaml new file mode 100644 index 000000000..2cbe3f3cd --- /dev/null +++ b/generator/config/expression/reverseArray.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $reverseArray +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reverseArray/' +type: + - resolvesToArray +encode: single +description: | + Returns an array with the elements in reverse order. +arguments: + - + name: expression + type: + - resolvesToArray + description: | + The argument can be any valid expression as long as it resolves to an array. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/reverseArray/#example' + pipeline: + - + $project: + name: 1 + reverseFavorites: + $reverseArray: '$favorites' diff --git a/generator/config/expression/round.yaml b/generator/config/expression/round.yaml new file mode 100644 index 000000000..9bc6961c7 --- /dev/null +++ b/generator/config/expression/round.yaml @@ -0,0 +1,50 @@ +# $schema: ../schema.json +name: $round +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/round/' +type: + - resolvesToInt + - resolvesToDouble + - resolvesToDecimal + - resolvesToLong +encode: array +description: | + Rounds a number to a whole integer or to a specified decimal place. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + $round returns an error if the expression resolves to a non-numeric data type. + - + name: place + type: + - resolvesToInt + optional: true + description: | + Can be any valid expression that resolves to an integer between -20 and 100, exclusive. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/round/#example' + pipeline: + - + $project: + roundedValue: + $round: + - '$value' + - 1 + - + name: 'Round Average Rating' + pipeline: + - + $project: + roundedAverageRating: + $avg: + - + $round: + - + $avg: + - '$averageRating' + - 2 diff --git a/generator/config/expression/rtrim.yaml b/generator/config/expression/rtrim.yaml new file mode 100644 index 000000000..a9d1974db --- /dev/null +++ b/generator/config/expression/rtrim.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: $rtrim +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rtrim/' +type: + - resolvesToString +encode: object +description: | + Removes whitespace characters, including null, or the specified characters from the end of a string. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string to trim. The argument can be any valid expression that resolves to a string. + - + name: chars + type: + - resolvesToString + optional: true + description: | + The character(s) to trim from the beginning of the input. + The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + If unspecified, $ltrim removes whitespace characters, including the null character. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/rtrim/#example' + pipeline: + - + $project: + item: 1 + description: + $rtrim: + input: '$description' diff --git a/generator/config/expression/second.yaml b/generator/config/expression/second.yaml new file mode 100644 index 000000000..83e7fd370 --- /dev/null +++ b/generator/config/expression/second.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $second +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/second/' +type: + - resolvesToInt +encode: object +description: | + Returns the seconds for a date as a number between 0 and 60 (leap seconds). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/second/#example' + pipeline: + - + $project: + seconds: + # Example uses the short form, the builder always generates the verbose form + # $second: '$date' + $second: + date: '$date' diff --git a/generator/config/expression/setDifference.yaml b/generator/config/expression/setDifference.yaml new file mode 100644 index 000000000..69f448cb7 --- /dev/null +++ b/generator/config/expression/setDifference.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: $setDifference +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setDifference/' +type: + - resolvesToArray +encode: array +description: | + Returns a set with elements that appear in the first set but not in the second set; i.e. performs a relative complement of the second set relative to the first. Accepts exactly two argument expressions. +arguments: + - + name: expression1 + type: + - resolvesToArray + description: | + The arguments can be any valid expression as long as they each resolve to an array. + - + name: expression2 + type: + - resolvesToArray + description: | + The arguments can be any valid expression as long as they each resolve to an array. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setDifference/#example' + pipeline: + - + $project: + flowerFieldA: 1 + flowerFieldB: 1 + inBOnly: + $setDifference: + - '$flowerFieldB' + - '$flowerFieldA' + _id: 0 diff --git a/generator/config/expression/setEquals.yaml b/generator/config/expression/setEquals.yaml new file mode 100644 index 000000000..5194c9ae9 --- /dev/null +++ b/generator/config/expression/setEquals.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $setEquals +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setEquals/' +type: + - resolvesToBool +encode: single +description: | + Returns true if the input sets have the same distinct elements. Accepts two or more argument expressions. +arguments: + - + name: expression + type: + - resolvesToArray + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setEquals/#example' + pipeline: + - + $project: + _id: 0 + cakes: 1 + cupcakes: 1 + sameFlavors: + $setEquals: + - '$cakes' + - '$cupcakes' diff --git a/generator/config/expression/setField.yaml b/generator/config/expression/setField.yaml new file mode 100644 index 000000000..b53cf6584 --- /dev/null +++ b/generator/config/expression/setField.yaml @@ -0,0 +1,109 @@ +# $schema: ../schema.json +name: $setField +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/' +type: + - resolvesToObject +encode: object +description: | + Adds, updates, or removes a specified field in a document. You can use $setField to add, update, or remove fields with names that contain periods (.) or start with dollar signs ($). + New in MongoDB 5.0. +arguments: + - + name: field + type: + - resolvesToString + description: | + Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + - + name: input + type: + - resolvesToObject + description: | + Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. + - + name: value + type: + - expression + description: | + The value that you want to assign to field. value can be any valid expression. + Set to $$REMOVE to remove field from the input document. +tests: + - + name: 'Add Fields that Contain Periods' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#add-fields-that-contain-periods--.-' + pipeline: + - + $replaceWith: + $setField: + field: 'price.usd' + input: '$$ROOT' + value: '$price' + - + # Example uses the short form, the builder always generates the verbose form + # $unset: 'price' + $unset: + - 'price' + - + name: 'Add Fields that Start with a Dollar Sign' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#add-fields-that-start-with-a-dollar-sign----' + pipeline: + - + $replaceWith: + $setField: + field: + $literal: '$price' + input: '$$ROOT' + value: '$price' + - + # Example uses the short form, the builder always generates the verbose form + # $unset: 'price' + $unset: + - 'price' + - + name: 'Update Fields that Contain Periods' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#update-fields-that-contain-periods--.-' + pipeline: + - + $match: + _id: 1 + - + $replaceWith: + $setField: + field: 'price.usd' + input: '$$ROOT' + value: 49.99 + - + name: 'Update Fields that Start with a Dollar Sign' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#update-fields-that-start-with-a-dollar-sign----' + pipeline: + - + $match: + _id: 1 + - + $replaceWith: + $setField: + field: + $literal: '$price' + input: '$$ROOT' + value: 49.99 + - + name: 'Remove Fields that Contain Periods' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#remove-fields-that-contain-periods--.-' + pipeline: + - + $replaceWith: + $setField: + field: 'price.usd' + input: '$$ROOT' + value: '$$REMOVE' + - + name: 'Remove Fields that Start with a Dollar Sign' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/#remove-fields-that-start-with-a-dollar-sign----' + pipeline: + - + $replaceWith: + $setField: + field: + $literal: '$price' + input: '$$ROOT' + value: '$$REMOVE' diff --git a/generator/config/expression/setIntersection.yaml b/generator/config/expression/setIntersection.yaml new file mode 100644 index 000000000..8f03651d5 --- /dev/null +++ b/generator/config/expression/setIntersection.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $setIntersection +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIntersection/' +type: + - resolvesToArray +encode: single +description: | + Returns a set with elements that appear in all of the input sets. Accepts any number of argument expressions. +arguments: + - + name: expression + type: + - resolvesToArray + variadic: array +tests: + - + name: 'Elements Array Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIntersection/#elements-array-example' + pipeline: + - + $project: + flowerFieldA: 1 + flowerFieldB: 1 + commonToBoth: + $setIntersection: + - '$flowerFieldA' + - '$flowerFieldB' + _id: 0 + - + name: 'Retrieve Documents for Roles Granted to the Current User' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIntersection/#retrieve-documents-for-roles-granted-to-the-current-user' + pipeline: + - + $match: + $expr: + $not: + # the example doesn't use an array inside $not, but the documentation say it is necessary + - + $eq: + - + $setIntersection: + - '$allowedRoles' + - '$$USER_ROLES.role' + - [] diff --git a/generator/config/expression/setIsSubset.yaml b/generator/config/expression/setIsSubset.yaml new file mode 100644 index 000000000..fe7c9ed02 --- /dev/null +++ b/generator/config/expression/setIsSubset.yaml @@ -0,0 +1,32 @@ +# $schema: ../schema.json +name: $setIsSubset +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIsSubset/' +type: + - resolvesToBool +encode: array +description: | + Returns true if all elements of the first set appear in the second set, including when the first set equals the second set; i.e. not a strict subset. Accepts exactly two argument expressions. +arguments: + - + name: expression1 + type: + - resolvesToArray + - + name: expression2 + type: + - resolvesToArray +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIsSubset/#example' + pipeline: + - + $project: + flowerFieldA: 1 + flowerFieldB: 1 + AisSubset: + $setIsSubset: + - '$flowerFieldA' + - '$flowerFieldB' + _id: 0 + diff --git a/generator/config/expression/setUnion.yaml b/generator/config/expression/setUnion.yaml new file mode 100644 index 000000000..2cfca3e16 --- /dev/null +++ b/generator/config/expression/setUnion.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $setUnion +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/' +type: + - resolvesToArray +encode: single +description: | + Returns a set with elements that appear in any of the input sets. +arguments: + - + name: expression + type: + - resolvesToArray + variadic: array +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/#example' + pipeline: + - + $project: + flowerFieldA: 1 + flowerFieldB: 1 + allValues: + $setUnion: + - '$flowerFieldA' + - '$flowerFieldB' + _id: 0 diff --git a/generator/config/expression/sin.yaml b/generator/config/expression/sin.yaml new file mode 100644 index 000000000..fe02b4f28 --- /dev/null +++ b/generator/config/expression/sin.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $sin +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sin/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the sine of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $sin takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + By default $sin returns values as a double. $sin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sin/#example' + pipeline: + - + $addFields: + side_b: + $multiply: + - + $sin: + $degreesToRadians: '$angle_a' + - '$hypotenuse' diff --git a/generator/config/expression/sinh.yaml b/generator/config/expression/sinh.yaml new file mode 100644 index 000000000..a5b446add --- /dev/null +++ b/generator/config/expression/sinh.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $sinh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sinh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the hyperbolic sine of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $sinh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + By default $sinh returns values as a double. $sinh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sinh/#example' + pipeline: + - + $addFields: + sinh_output: + $sinh: + $degreesToRadians: '$angle' diff --git a/generator/config/expression/size.yaml b/generator/config/expression/size.yaml new file mode 100644 index 000000000..ce4fe775e --- /dev/null +++ b/generator/config/expression/size.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $size +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/size/' +type: + - resolvesToInt +encode: single +description: | + Returns the number of elements in the array. Accepts a single expression as argument. +arguments: + - + name: expression + type: + - resolvesToArray + description: | + The argument for $size can be any expression as long as it resolves to an array. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/size/#example' + pipeline: + - + $project: + item: 1 + numberOfColors: + $cond: + if: + # Example uses the short form, the builder always generates the verbose form + # $isArray: '$colors' + $isArray: + - '$colors' + then: + $size: '$colors' + else: 'NA' diff --git a/generator/config/expression/slice.yaml b/generator/config/expression/slice.yaml new file mode 100644 index 000000000..22cc287e1 --- /dev/null +++ b/generator/config/expression/slice.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $slice +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/' +type: + - resolvesToArray +encode: array +description: | + Returns a subset of an array. +arguments: + - + name: expression + type: + - resolvesToArray + description: | + Any valid expression as long as it resolves to an array. + - + name: position + type: + - resolvesToInt + optional: true + description: | + Any valid expression as long as it resolves to an integer. + If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array. + If negative, $slice determines the starting position from the end of the array. If the absolute value of the is greater than the number of elements, the starting position is the start of the array. + - + name: "n" + type: + - resolvesToInt + description: | + Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer. + If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position. + If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if is specified. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/#example' + pipeline: + - + $project: + name: 1 + threeFavorites: + $slice: + - '$favorites' + - 3 diff --git a/generator/config/expression/sortArray.yaml b/generator/config/expression/sortArray.yaml new file mode 100644 index 000000000..d558f2233 --- /dev/null +++ b/generator/config/expression/sortArray.yaml @@ -0,0 +1,108 @@ +# $schema: ../schema.json +name: $sortArray +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/' +type: + - resolvesToArray +encode: object +description: | + Sorts the elements of an array. +arguments: + - + name: input + type: + - resolvesToArray + description: | + The array to be sorted. + The result is null if the expression: is missing, evaluates to null, or evaluates to undefined + If the expression evaluates to any other non-array value, the document returns an error. + - + name: sortBy + type: + - int + - sortSpec + - sortBy + description: | + The document specifies a sort ordering. +tests: + - + name: 'Sort on a Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/#sort-on-a-field' + pipeline: + - + $project: + _id: 0 + result: + $sortArray: + input: '$team' + sortBy: + name: 1 + - + name: 'Sort on a Subfield' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/#sort-on-a-subfield' + pipeline: + - + $project: + _id: 0 + result: + $sortArray: + input: '$team' + sortBy: + address.city: -1 + - + name: 'Sort on Multiple Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/#sort-on-multiple-fields' + pipeline: + - + $project: + _id: 0 + result: + $sortArray: + input: '$team' + sortBy: + age: -1 + name: 1 + - + name: 'Sort an Array of Integers' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/#sort-an-array-of-integers' + pipeline: + - + $project: + _id: 0 + result: + $sortArray: + input: + - 1 + - 4 + - 1 + - 6 + - 12 + - 5 + sortBy: 1 + - + name: 'Sort on Mixed Type Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/#sort-on-mixed-type-fields' + pipeline: + - + $project: + _id: 0 + result: + $sortArray: + input: + - 20 + - 4 + - + a: 'Free' + - 6 + - 21 + - 5 + - 'Gratis' + - + a: ~ + - + a: + sale: true + price: 19 + - !bson_decimal128 '10.23' + - + a: 'On sale' + sortBy: 1 diff --git a/generator/config/expression/split.yaml b/generator/config/expression/split.yaml new file mode 100644 index 000000000..98739c4ec --- /dev/null +++ b/generator/config/expression/split.yaml @@ -0,0 +1,58 @@ +# $schema: ../schema.json +name: $split +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/split/' +type: + - resolvesToArray # of string +encode: array +description: | + Splits a string into substrings based on a delimiter. Returns an array of substrings. If the delimiter is not found within the string, returns an array containing the original string. +arguments: + - + name: string + type: + - resolvesToString + description: | + The string to be split. string expression can be any valid expression as long as it resolves to a string. + - + name: delimiter + type: + - resolvesToString + - resolvesToRegex + description: | + The delimiter to use when splitting the string expression. delimiter can be any valid expression as long as it resolves to a string. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/split/#example' + pipeline: + - + $project: + city_state: + $split: + - '$city' + - ', ' + qty: 1 + - + $unwind: + path: '$city_state' + - + $match: + city_state: !bson_regex '[A-Z]{2}' + - + $group: + _id: + state: '$city_state' + total_qty: + $sum: '$qty' + - + $sort: + total_qty: -1 + - + name: 'Support regex delimiter' + pipeline: + - + $project: + split: + $split: + - 'abc' + - !bson_regex 'b' diff --git a/generator/config/expression/sqrt.yaml b/generator/config/expression/sqrt.yaml new file mode 100644 index 000000000..52f5bb7c2 --- /dev/null +++ b/generator/config/expression/sqrt.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $sqrt +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sqrt/' +type: + - resolvesToDouble +encode: single +description: | + Calculates the square root. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + The argument can be any valid expression as long as it resolves to a non-negative number. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sqrt/#example' + pipeline: + - + $project: + distance: + $sqrt: + $add: + - + $pow: + - + $subtract: + - '$p2.y' + - '$p1.y' + - 2 + - + $pow: + - + $subtract: + - '$p2.x' + - '$p1.x' + - 2 diff --git a/generator/config/expression/stdDevPop.yaml b/generator/config/expression/stdDevPop.yaml new file mode 100644 index 000000000..46641ebe8 --- /dev/null +++ b/generator/config/expression/stdDevPop.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $stdDevPop +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/' +type: + - resolvesToDouble +encode: single +description: | + Calculates the population standard deviation of the input values. Use if the values encompass the entire population of data you want to represent and do not wish to generalize about a larger population. $stdDevPop ignores non-numeric values. + If the values represent only a sample of a population of data from which to generalize about the population, use $stdDevSamp instead. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber + variadic: array +tests: + - + name: 'Use in $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/#use-in--project-stage' + pipeline: + - + $project: + stdDev: + # Example uses the short form, the builder always generates the verbose form + # $stdDevPop: '$scores.score' + $stdDevPop: ['$scores.score'] diff --git a/generator/config/expression/stdDevSamp.yaml b/generator/config/expression/stdDevSamp.yaml new file mode 100644 index 000000000..84b35f52a --- /dev/null +++ b/generator/config/expression/stdDevSamp.yaml @@ -0,0 +1,15 @@ +# $schema: ../schema.json +name: $stdDevSamp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/' +type: + - resolvesToDouble +encode: single +description: | + Calculates the sample standard deviation of the input values. Use if the values encompass a sample of a population of data from which to generalize about the population. $stdDevSamp ignores non-numeric values. + If the values represent the entire population of data or you do not wish to generalize about a larger population, use $stdDevPop instead. +arguments: + - + name: expression + type: + - resolvesToNumber + variadic: array diff --git a/generator/config/expression/strLenBytes.yaml b/generator/config/expression/strLenBytes.yaml new file mode 100644 index 000000000..301150d19 --- /dev/null +++ b/generator/config/expression/strLenBytes.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $strLenBytes +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenBytes/' +type: + - resolvesToInt +encode: single +description: | + Returns the number of UTF-8 encoded bytes in a string. +arguments: + - + name: expression + type: + - resolvesToString +tests: + - + name: 'Single-Byte and Multibyte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenBytes/#single-byte-and-multibyte-character-set' + pipeline: + - + $project: + name: 1 + length: + $strLenBytes: '$name' diff --git a/generator/config/expression/strLenCP.yaml b/generator/config/expression/strLenCP.yaml new file mode 100644 index 000000000..b852c80ec --- /dev/null +++ b/generator/config/expression/strLenCP.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $strLenCP +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenCP/' +type: + - resolvesToInt +encode: single +description: | + Returns the number of UTF-8 code points in a string. +arguments: + - + name: expression + type: + - resolvesToString +tests: + - + name: 'Single-Byte and Multibyte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenBytes/#single-byte-and-multibyte-character-set' + pipeline: + - + $project: + name: 1 + length: + $strLenCP: '$name' diff --git a/generator/config/expression/strcasecmp.yaml b/generator/config/expression/strcasecmp.yaml new file mode 100644 index 000000000..6775c44b1 --- /dev/null +++ b/generator/config/expression/strcasecmp.yaml @@ -0,0 +1,29 @@ +# $schema: ../schema.json +name: $strcasecmp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strcasecmp/' +type: + - resolvesToInt +encode: array +description: | + Performs case-insensitive string comparison and returns: 0 if two strings are equivalent, 1 if the first string is greater than the second, and -1 if the first string is less than the second. +arguments: + - + name: expression1 + type: + - resolvesToString + - + name: expression2 + type: + - resolvesToString +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/strcasecmp/#example' + pipeline: + - + $project: + item: 1 + comparisonResult: + $strcasecmp: + - '$quarter' + - '13q4' diff --git a/generator/config/expression/substr.yaml b/generator/config/expression/substr.yaml new file mode 100644 index 000000000..6bcf6143f --- /dev/null +++ b/generator/config/expression/substr.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $substr +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substr/' +type: + - resolvesToString +encode: array +description: | + Deprecated. Use $substrBytes or $substrCP. +arguments: + - + name: string + type: + - resolvesToString + - + name: start + type: + - resolvesToInt + description: | + If start is a negative number, $substr returns an empty string "". + - + name: length + type: + - resolvesToInt + description: | + If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substr/#example' + pipeline: + - + $project: + item: 1 + yearSubstring: + $substr: + - '$quarter' + - 0 + - 2 + quarterSubtring: + $substr: + - '$quarter' + - 2 + - -1 diff --git a/generator/config/expression/substrBytes.yaml b/generator/config/expression/substrBytes.yaml new file mode 100644 index 000000000..fed7677c8 --- /dev/null +++ b/generator/config/expression/substrBytes.yaml @@ -0,0 +1,59 @@ +# $schema: ../schema.json +name: $substrBytes +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrBytes/' +type: + - resolvesToString +encode: array +description: | + Returns the substring of a string. Starts with the character at the specified UTF-8 byte index (zero-based) in the string and continues for the specified number of bytes. +arguments: + - + name: string + type: + - resolvesToString + - + name: start + type: + - resolvesToInt + description: | + If start is a negative number, $substr returns an empty string "". + - + name: length + type: + - resolvesToInt + description: | + If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. +tests: + - + name: 'Single-Byte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrBytes/#single-byte-character-set' + pipeline: + - + $project: + item: 1 + yearSubstring: + $substrBytes: + - '$quarter' + - 0 + - 2 + quarterSubtring: + $substrBytes: + - '$quarter' + - 2 + - + $subtract: + - + $strLenBytes: '$quarter' + - 2 + - + name: 'Single-Byte and Multibyte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrBytes/#single-byte-and-multibyte-character-set' + pipeline: + - + $project: + name: 1 + menuCode: + $substrBytes: + - '$name' + - 0 + - 3 diff --git a/generator/config/expression/substrCP.yaml b/generator/config/expression/substrCP.yaml new file mode 100644 index 000000000..843b2ca1a --- /dev/null +++ b/generator/config/expression/substrCP.yaml @@ -0,0 +1,59 @@ +# $schema: ../schema.json +name: $substrCP +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrCP/' +type: + - resolvesToString +encode: array +description: | + Returns the substring of a string. Starts with the character at the specified UTF-8 code point (CP) index (zero-based) in the string and continues for the number of code points specified. +arguments: + - + name: string + type: + - resolvesToString + - + name: start + type: + - resolvesToInt + description: | + If start is a negative number, $substr returns an empty string "". + - + name: length + type: + - resolvesToInt + description: | + If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. +tests: + - + name: 'Single-Byte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrCP/#single-byte-character-set' + pipeline: + - + $project: + item: 1 + yearSubstring: + $substrCP: + - '$quarter' + - 0 + - 2 + quarterSubtring: + $substrCP: + - '$quarter' + - 2 + - + $subtract: + - + $strLenCP: '$quarter' + - 2 + - + name: 'Single-Byte and Multibyte Character Set' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrCP/#single-byte-and-multibyte-character-set' + pipeline: + - + $project: + name: 1 + menuCode: + $substrCP: + - '$name' + - 0 + - 3 diff --git a/generator/config/expression/subtract.yaml b/generator/config/expression/subtract.yaml new file mode 100644 index 000000000..b6db65ac9 --- /dev/null +++ b/generator/config/expression/subtract.yaml @@ -0,0 +1,60 @@ +# $schema: ../schema.json +name: $subtract +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/' +type: + - resolvesToInt + - resolvesToLong + - resolvesToDouble + - resolvesToDecimal + - resolvesToDate +encode: array +description: | + Returns the result of subtracting the second value from the first. If the two values are numbers, return the difference. If the two values are dates, return the difference in milliseconds. If the two values are a date and a number in milliseconds, return the resulting date. Accepts two argument expressions. If the two values are a date and a number, specify the date argument first as it is not meaningful to subtract a date from a number. +arguments: + - + name: expression1 + type: + - resolvesToNumber + - resolvesToDate + - + name: expression2 + type: + - resolvesToNumber + - resolvesToDate +tests: + - + name: 'Subtract Numbers' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/#subtract-numbers' + pipeline: + - + $project: + item: 1 + total: + $subtract: + - + $add: + - '$price' + - '$fee' + - '$discount' + - + name: 'Subtract Two Dates' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/#subtract-two-dates' + pipeline: + - + $project: + item: 1 + dateDifference: + $subtract: + - '$$NOW' + - '$date' + - + name: 'Subtract Milliseconds from a Date' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/#subtract-milliseconds-from-a-date' + pipeline: + - + $project: + item: 1 + dateDifference: + $subtract: + - '$date' + - 300000 diff --git a/generator/config/expression/sum.yaml b/generator/config/expression/sum.yaml new file mode 100644 index 000000000..25b323ab1 --- /dev/null +++ b/generator/config/expression/sum.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $sum +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/' +type: + - resolvesToNumber +encode: single +description: | + Returns a sum of numerical values. Ignores non-numeric values. + Changed in MongoDB 5.0: Available in the $setWindowFields stage. +arguments: + - + name: expression + type: + - resolvesToNumber + - resolvesToArray + variadic: array +tests: + - + name: 'Use in $project Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/#use-in--project-stage' + pipeline: + - + $project: + quizTotal: + # Example uses the short form, the builder always generates the verbose form + # $sum: '$quizzes' + $sum: + - '$quizzes' + labTotal: + # $sum: '$labs' + $sum: + - '$labs' + examTotal: + $sum: + - '$final' + - '$midterm' diff --git a/generator/config/expression/switch.yaml b/generator/config/expression/switch.yaml new file mode 100644 index 000000000..d668e3d94 --- /dev/null +++ b/generator/config/expression/switch.yaml @@ -0,0 +1,70 @@ +# $schema: ../schema.json +name: $switch +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/switch/' +type: + - resolvesToAny +encode: object +description: | + Evaluates a series of case expressions. When it finds an expression which evaluates to true, $switch executes a specified expression and breaks out of the control flow. +arguments: + - + name: branches + type: + - array # of CaseOperator + description: | + An array of control branch documents. Each branch is a document with the following fields: + - case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + - then Can be any valid expression. + The branches array must contain at least one branch document. + - + name: default + type: + - expression + optional: true + description: | + The path to take if no branch case expression evaluates to true. + Although optional, if default is unspecified and no branch case evaluates to true, $switch returns an error. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/switch/#example' + pipeline: + - + $project: + name: 1 + summary: + $switch: + branches: + - + case: + $gte: + - + #$avg: '$scores' + $avg: [ '$scores' ] + - 90 + then: 'Doing great!' + - + case: + $and: + - + $gte: + - + #$avg: '$scores' + $avg: [ '$scores' ] + - 80 + - + $lt: + - + #$avg: '$scores' + $avg: [ '$scores' ] + - 90 + then: 'Doing pretty well.' + - + case: + $lt: + - + #$avg: '$scores' + $avg: [ '$scores' ] + - 80 + then: 'Needs improvement.' + default: 'No scores found.' diff --git a/generator/config/expression/tan.yaml b/generator/config/expression/tan.yaml new file mode 100644 index 000000000..17b11ee63 --- /dev/null +++ b/generator/config/expression/tan.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $tan +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tan/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the tangent of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $tan takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + By default $tan returns values as a double. $tan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tan/#example' + pipeline: + - + $addFields: + side_b: + $multiply: + - + $tan: + $degreesToRadians: '$angle_a' + - '$side_a' diff --git a/generator/config/expression/tanh.yaml b/generator/config/expression/tanh.yaml new file mode 100644 index 000000000..364589452 --- /dev/null +++ b/generator/config/expression/tanh.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $tanh +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tanh/' +type: + - resolvesToDouble + - resolvesToDecimal +encode: single +description: | + Returns the hyperbolic tangent of a value that is measured in radians. +arguments: + - + name: expression + type: + - resolvesToNumber + description: | + $tanh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + By default $tanh returns values as a double. $tanh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tanh/#example' + pipeline: + - + $addFields: + tanh_output: + $tanh: + $degreesToRadians: '$angle' diff --git a/generator/config/expression/toBool.yaml b/generator/config/expression/toBool.yaml new file mode 100644 index 000000000..7f771ec8d --- /dev/null +++ b/generator/config/expression/toBool.yaml @@ -0,0 +1,41 @@ +# $schema: ../schema.json +name: $toBool +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toBool/' +type: + - resolvesToBool +encode: single +description: | + Converts value to a boolean. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toBool/#example' + pipeline: + - + $addFields: + convertedShippedFlag: + $switch: + branches: + - + case: + $eq: + - '$shipped' + - 'false' + then: false + - + case: + $eq: + - '$shipped' + - '' + then: false + default: + $toBool: '$shipped' + - + $match: + convertedShippedFlag: false diff --git a/generator/config/expression/toDate.yaml b/generator/config/expression/toDate.yaml new file mode 100644 index 000000000..d9434a6bd --- /dev/null +++ b/generator/config/expression/toDate.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $toDate +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDate/' +type: + - resolvesToDate +encode: single +description: | + Converts value to a Date. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDate/#example' + pipeline: + - + $addFields: + convertedDate: + $toDate: '$order_date' + - + $sort: + convertedDate: 1 diff --git a/generator/config/expression/toDecimal.yaml b/generator/config/expression/toDecimal.yaml new file mode 100644 index 000000000..2f3588323 --- /dev/null +++ b/generator/config/expression/toDecimal.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $toDecimal +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDecimal/' +type: + - resolvesToDecimal +encode: single +description: | + Converts value to a Decimal128. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDecimal/#example' + pipeline: + - + $addFields: + convertedPrice: + $toDecimal: '$price' diff --git a/generator/config/expression/toDouble.yaml b/generator/config/expression/toDouble.yaml new file mode 100644 index 000000000..f34c36e9a --- /dev/null +++ b/generator/config/expression/toDouble.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $toDouble +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDouble/' +type: + - resolvesToDouble +encode: single +description: | + Converts value to a double. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDouble/#example' + pipeline: + - + $addFields: + degrees: + $toDouble: + $substrBytes: + - '$temp' + - 0 + - 4 diff --git a/generator/config/expression/toHashedIndexKey.yaml b/generator/config/expression/toHashedIndexKey.yaml new file mode 100644 index 000000000..f5811f56d --- /dev/null +++ b/generator/config/expression/toHashedIndexKey.yaml @@ -0,0 +1,28 @@ +# $schema: ../schema.json +name: $toHashedIndexKey +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toHashedIndexKey/' +type: + - resolvesToLong +encode: single +description: | + Computes and returns the hash value of the input expression using the same hash function that MongoDB uses to create a hashed index. A hash function maps a key or string to a fixed-size numeric value. +arguments: + - + name: value + type: + - expression + description: | + key or string to hash +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toHashedIndexKey/#example' + pipeline: + - + $documents: + - + val: 'string to hash' + - + $addFields: + hashedVal: + $toHashedIndexKey: '$val' diff --git a/generator/config/expression/toInt.yaml b/generator/config/expression/toInt.yaml new file mode 100644 index 000000000..2b0239955 --- /dev/null +++ b/generator/config/expression/toInt.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $toInt +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toInt/' +type: + - resolvesToInt +encode: single +description: | + Converts value to an integer. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toInt/#example' + pipeline: + - + $addFields: + convertedQty: + $toInt: '$qty' diff --git a/generator/config/expression/toLong.yaml b/generator/config/expression/toLong.yaml new file mode 100644 index 000000000..3168ad9ff --- /dev/null +++ b/generator/config/expression/toLong.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $toLong +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLong/' +type: + - resolvesToLong +encode: single +description: | + Converts value to a long. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLong/#example' + pipeline: + - + $addFields: + convertedQty: + $toLong: '$qty' + - + $sort: + convertedQty: -1 diff --git a/generator/config/expression/toLower.yaml b/generator/config/expression/toLower.yaml new file mode 100644 index 000000000..0d6176672 --- /dev/null +++ b/generator/config/expression/toLower.yaml @@ -0,0 +1,24 @@ +# $schema: ../schema.json +name: $toLower +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLower/' +type: + - resolvesToString +encode: single +description: | + Converts a string to lowercase. Accepts a single argument expression. +arguments: + - + name: expression + type: + - resolvesToString +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLower/#example' + pipeline: + - + $project: + item: + $toLower: '$item' + description: + $toLower: '$description' diff --git a/generator/config/expression/toObjectId.yaml b/generator/config/expression/toObjectId.yaml new file mode 100644 index 000000000..803f7cafa --- /dev/null +++ b/generator/config/expression/toObjectId.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $toObjectId +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toObjectId/' +type: + - resolvesToObjectId +encode: single +description: | + Converts value to an ObjectId. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toObjectId/#example' + pipeline: + - + $addFields: + convertedId: + $toObjectId: '$_id' + - + $sort: + convertedId: -1 diff --git a/generator/config/expression/toString.yaml b/generator/config/expression/toString.yaml new file mode 100644 index 000000000..0fd068562 --- /dev/null +++ b/generator/config/expression/toString.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $toString +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toString/' +type: + - resolvesToString +encode: single +description: | + Converts value to a string. + New in MongoDB 4.0. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toString/#example' + pipeline: + - + $addFields: + convertedZipCode: + $toString: '$zipcode' + - + $sort: + convertedZipCode: 1 diff --git a/generator/config/expression/toUpper.yaml b/generator/config/expression/toUpper.yaml new file mode 100644 index 000000000..c2c71e1bc --- /dev/null +++ b/generator/config/expression/toUpper.yaml @@ -0,0 +1,24 @@ +# $schema: ../schema.json +name: $toUpper +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toUpper/' +type: + - resolvesToString +encode: single +description: | + Converts a string to uppercase. Accepts a single argument expression. +arguments: + - + name: expression + type: + - resolvesToString +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/toUpper/#example' + pipeline: + - + $project: + item: + $toUpper: '$item' + description: + $toUpper: '$description' diff --git a/generator/config/expression/trim.yaml b/generator/config/expression/trim.yaml new file mode 100644 index 000000000..d63423910 --- /dev/null +++ b/generator/config/expression/trim.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $trim +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/trim/' +type: + - resolvesToString +encode: object +description: | + Removes whitespace or the specified characters from the beginning and end of a string. + New in MongoDB 4.0. +arguments: + - + name: input + type: + - resolvesToString + description: | + The string to trim. The argument can be any valid expression that resolves to a string. + - + name: chars + type: + - resolvesToString + optional: true + description: | + The character(s) to trim from the beginning of the input. + The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + If unspecified, $ltrim removes whitespace characters, including the null character. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/trim/#example' + pipeline: + - + $project: + item: 1 + description: + $trim: + input: '$description' diff --git a/generator/config/expression/trunc.yaml b/generator/config/expression/trunc.yaml new file mode 100644 index 000000000..f930cf027 --- /dev/null +++ b/generator/config/expression/trunc.yaml @@ -0,0 +1,34 @@ +# $schema: ../schema.json +name: $trunc +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/trunc/' +type: + - resolvesToString +encode: array +description: | + Truncates a number to a whole integer or to a specified decimal place. +arguments: + - + name: number + type: + - resolvesToNumber + description: | + Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + $trunc returns an error if the expression resolves to a non-numeric data type. + - + name: place + type: + - resolvesToInt + optional: true + description: | + Can be any valid expression that resolves to an integer between -20 and 100, exclusive. e.g. -20 < place < 100. Defaults to 0. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/trunc/#example' + pipeline: + - + $project: + truncatedValue: + $trunc: + - '$value' + - 1 diff --git a/generator/config/expression/tsIncrement.yaml b/generator/config/expression/tsIncrement.yaml new file mode 100644 index 000000000..9fded2143 --- /dev/null +++ b/generator/config/expression/tsIncrement.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $tsIncrement +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsIncrement/' +type: + - resolvesToLong +encode: single +description: | + Returns the incrementing ordinal from a timestamp as a long. + New in MongoDB 5.1. +arguments: + - + name: expression + type: + - resolvesToTimestamp +tests: + - + name: 'Obtain the Incrementing Ordinal from a Timestamp Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsIncrement/#obtain-the-incrementing-ordinal-from-a-timestamp-field' + pipeline: + - + $project: + _id: 0 + saleTimestamp: 1 + saleIncrement: + $tsIncrement: '$saleTimestamp' + - + name: 'Use $tsSecond in a Change Stream Cursor to Monitor Collection Changes' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsSecond/#use--tssecond-in-a-change-stream-cursor-to-monitor-collection-changes' + pipeline: + - + $match: + $expr: + $eq: + - + $mod: + - + $tsIncrement: '$clusterTime' + - 2 + - 0 diff --git a/generator/config/expression/tsSecond.yaml b/generator/config/expression/tsSecond.yaml new file mode 100644 index 000000000..20a84904b --- /dev/null +++ b/generator/config/expression/tsSecond.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $tsSecond +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsSecond/' +type: + - resolvesToLong +encode: single +description: | + Returns the seconds from a timestamp as a long. + New in MongoDB 5.1. +arguments: + - + name: expression + type: + - resolvesToTimestamp +tests: + - + name: 'Obtain the Number of Seconds from a Timestamp Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsSecond/#obtain-the-number-of-seconds-from-a-timestamp-field' + pipeline: + - + $project: + _id: 0 + saleTimestamp: 1 + saleSeconds: + $tsSecond: '$saleTimestamp' + - + name: 'Use $tsSecond in a Change Stream Cursor to Monitor Collection Changes' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsSecond/#use--tssecond-in-a-change-stream-cursor-to-monitor-collection-changes' + pipeline: + - + $addFields: + clusterTimeSeconds: + $tsSecond: '$clusterTime' diff --git a/generator/config/expression/type.yaml b/generator/config/expression/type.yaml new file mode 100644 index 000000000..c1f63db79 --- /dev/null +++ b/generator/config/expression/type.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $type +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/type/' +type: + - resolvesToString +encode: single +description: | + Return the BSON data type of the field. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/type/#example' + pipeline: + - + $project: + a: + $type: '$a' diff --git a/generator/config/expression/unsetField.yaml b/generator/config/expression/unsetField.yaml new file mode 100644 index 000000000..a4365a646 --- /dev/null +++ b/generator/config/expression/unsetField.yaml @@ -0,0 +1,59 @@ +# $schema: ../schema.json +name: $unsetField +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/' +type: + - resolvesToObject +encode: object +description: | + You can use $unsetField to remove fields with names that contain periods (.) or that start with dollar signs ($). + $unsetField is an alias for $setField using $$REMOVE to remove fields. +arguments: + - + name: field + type: + - resolvesToString + description: | + Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + - + name: input + type: + - resolvesToObject + description: | + Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. +tests: + - + name: 'Remove Fields that Contain Periods' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/#remove-fields-that-contain-periods--.-' + pipeline: + - + $replaceWith: + $unsetField: + field: 'price.usd' + input: '$$ROOT' + - + name: 'Remove Fields that Start with a Dollar Sign' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/#remove-fields-that-start-with-a-dollar-sign----' + pipeline: + - + $replaceWith: + $unsetField: + field: + $literal: '$price' + input: '$$ROOT' + - + name: 'Remove A Subfield' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/#remove-a-subfield' + pipeline: + - + $replaceWith: + $setField: + field: 'price' + input: '$$ROOT' + value: + $unsetField: + field: 'euro' + input: + # Example uses the short form, the builder always generates the verbose form + # $getField: 'price' + $getField: + field: 'price' diff --git a/generator/config/expression/week.yaml b/generator/config/expression/week.yaml new file mode 100644 index 000000000..6086f57ee --- /dev/null +++ b/generator/config/expression/week.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $week +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/week/' +type: + - resolvesToInt +encode: object +description: | + Returns the week number for a date as a number between 0 (the partial week that precedes the first Sunday of the year) and 53 (leap year). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/week/#example' + pipeline: + - + $project: + week: + # Example uses the short form, the builder always generates the verbose form + # $week: '$date' + $week: + date: '$date' diff --git a/generator/config/expression/year.yaml b/generator/config/expression/year.yaml new file mode 100644 index 000000000..3326e3495 --- /dev/null +++ b/generator/config/expression/year.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $year +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/year/' +type: + - resolvesToInt +encode: object +description: | + Returns the year for a date as a number (e.g. 2014). +arguments: + - + name: date + type: + - resolvesToDate + - resolvesToTimestamp + - resolvesToObjectId + description: | + The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + - + name: timezone + type: + - resolvesToString + optional: true + description: | + The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/year/#example' + pipeline: + - + $project: + year: + # Example uses the short form, the builder always generates the verbose form + # $year: '$date' + $year: + date: '$date' diff --git a/generator/config/expression/zip.yaml b/generator/config/expression/zip.yaml new file mode 100644 index 000000000..76402dac5 --- /dev/null +++ b/generator/config/expression/zip.yaml @@ -0,0 +1,87 @@ +# $schema: ../schema.json +name: $zip +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/zip/' +type: + - resolvesToArray # of array +encode: object +description: | + Merge two arrays together. +arguments: + - + name: inputs + type: + - resolvesToArray # of array + description: | + An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + If any of the inputs arrays resolves to a value of null or refers to a missing field, $zip returns null. + If any of the inputs arrays does not resolve to an array or null nor refers to a missing field, $zip returns an error. + - + name: useLongestLength + type: + - bool + optional: true + description: | + A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. + The default value is false: the shortest array length determines the number of arrays in the output array. + - + name: defaults + type: + - array + optional: true + description: | + An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + If useLongestLength: true but defaults is empty or not specified, $zip uses null as the default value. + If specifying a non-empty defaults, you must specify a default for each input array or else $zip will return an error. +tests: + - + name: 'Matrix Transposition' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/zip/#matrix-transposition' + pipeline: + - + $project: + _id: false + transposed: + $zip: + inputs: + - + $arrayElemAt: + - '$matrix' + - 0 + - + $arrayElemAt: + - '$matrix' + - 1 + - + $arrayElemAt: + - '$matrix' + - 2 + - + name: 'Filtering and Preserving Indexes' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/zip/#filtering-and-preserving-indexes' + pipeline: + - + $project: + _id: false + pages: + $filter: + input: + $zip: + inputs: + - '$pages' + - + $range: + - 0 + - + $size: '$pages' + as: 'pageWithIndex' + cond: + $let: + vars: + page: + $arrayElemAt: + - '$$pageWithIndex' + - 0 + in: + $gte: + - '$$page.reviews' + - 1 diff --git a/generator/config/expressions.php b/generator/config/expressions.php new file mode 100644 index 000000000..9d8df47a6 --- /dev/null +++ b/generator/config/expressions.php @@ -0,0 +1,175 @@ + ['int', BSON\Int64::class, 'float'], + 'string' => ['string'], + 'object' => ['array', stdClass::class, BSON\Document::class, BSON\Serializable::class], + 'array' => ['array', BSONArray::class, BSON\PackedArray::class], + 'binData' => ['string', BSON\Binary::class], + 'objectId' => [BSON\ObjectId::class], + 'bool' => ['bool'], + 'date' => [BSON\UTCDateTime::class, DateTimeInterface::class], + 'null' => ['null'], + 'regex' => [BSON\Regex::class], + 'javascript' => ['string', BSON\Javascript::class], + 'int' => ['int'], + 'timestamp' => ['int', BSON\Timestamp::class], + 'long' => ['int', BSON\Int64::class], + 'decimal' => ['int', BSON\Int64::class, 'float', BSON\Decimal128::class], +]; + +// "any" accepts all the BSON types. No generic "object" or "mixed" +$bsonTypes['any'] = ['bool', 'int', 'float', 'string', 'array', 'null', stdClass::class, BSON\Type::class, DateTimeInterface::class]; + +// "number" accepts all the numeric types +$bsonTypes['number'] = ['int', 'float', BSON\Int64::class, BSON\Decimal128::class]; + +$expressions = []; +$resolvesToInterfaces = []; +foreach ($bsonTypes as $name => $acceptedTypes) { + $expressions[$name] = ['acceptedTypes' => $acceptedTypes]; + + $resolvesTo = 'resolvesTo' . ucfirst($name); + $resolvesToInterface = __NAMESPACE__ . '\\' . ucfirst($resolvesTo); + $expressions[$resolvesTo] = [ + 'generate' => PhpObject::PhpInterface, + 'implements' => [Type\ExpressionInterface::class], + 'returnType' => $resolvesToInterface, + 'acceptedTypes' => $acceptedTypes, + ]; + + $fieldPathName = $name . 'FieldPath'; + if ($name === 'any') { + $fieldPathName = 'fieldPath'; + } else { + $resolvesToInterfaces[] = $resolvesToInterface; + } + + $expressions[$fieldPathName] = [ + 'generate' => PhpObject::PhpClass, + 'implements' => [Type\FieldPathInterface::class, $resolvesToInterface], + 'acceptedTypes' => ['string'], + ]; +} + +$expressions['resolvesToLong']['implements'] = [ResolvesToInt::class]; +$expressions['resolvesToInt']['implements'] = [ResolvesToNumber::class]; +$expressions['resolvesToDecimal']['implements'] = [ResolvesToDouble::class]; +$expressions['resolvesToDouble']['implements'] = [ResolvesToNumber::class]; +$expressions['resolvesToAny']['implements'] = $resolvesToInterfaces; + +return $expressions + [ + 'expression' => [ + 'returnType' => Type\ExpressionInterface::class, + 'acceptedTypes' => [Type\ExpressionInterface::class, ...$bsonTypes['any']], + ], + 'fieldQuery' => [ + 'returnType' => Type\FieldQueryInterface::class, + 'acceptedTypes' => [Type\FieldQueryInterface::class, ...$bsonTypes['any']], + ], + 'query' => [ + 'returnType' => Type\QueryInterface::class, + 'acceptedTypes' => [Type\QueryInterface::class, 'array'], + ], + 'accumulator' => [ + 'returnType' => Type\AccumulatorInterface::class, + 'acceptedTypes' => [Type\AccumulatorInterface::class, ...$bsonTypes['object']], + ], + 'window' => [ + 'returnType' => Type\WindowInterface::class, + 'acceptedTypes' => [Type\WindowInterface::class, ...$bsonTypes['object']], + ], + 'stage' => [ + 'returnType' => Type\StageInterface::class, + 'acceptedTypes' => [Type\StageInterface::class, ...$bsonTypes['object']], + ], + 'pipeline' => [ + 'acceptedTypes' => [Pipeline::class, ...$bsonTypes['array']], + ], + 'variable' => [ + 'generate' => PhpObject::PhpClass, + 'implements' => [ResolvesToAny::class], + 'acceptedTypes' => ['string'], + ], + 'searchOperator' => [ + 'returnType' => Type\SearchOperatorInterface::class, + 'acceptedTypes' => [Type\SearchOperatorInterface::class, ...$bsonTypes['object']], + ], + 'geometry' => [ + 'returnType' => Type\GeometryInterface::class, + 'acceptedTypes' => [Type\GeometryInterface::class, ...$bsonTypes['object']], + ], + 'switchBranch' => [ + 'returnType' => Type\SwitchBranchInterface::class, + 'acceptedTypes' => [Type\SwitchBranchInterface::class, ...$bsonTypes['object']], + ], + 'timeUnit' => [ + 'returnType' => Type\TimeUnit::class, + 'acceptedTypes' => [Type\TimeUnit::class, ResolvesToString::class, ...$bsonTypes['string']], + ], + 'sortSpec' => [ + 'returnType' => Type\Sort::class, + 'acceptedTypes' => [Type\Sort::class], + ], + + // @todo add enum values + 'granularity' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + 'fullDocument' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + 'fullDocumentBeforeChange' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + 'accumulatorPercentile' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + 'whenMatched' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + 'whenNotMatched' => [ + 'acceptedTypes' => [...$bsonTypes['string']], + ], + + // @todo create specific model classes factories + 'outCollection' => [ + 'acceptedTypes' => [...$bsonTypes['object']], + ], + 'range' => [ + 'acceptedTypes' => [...$bsonTypes['object']], + ], + 'sortBy' => [ + 'acceptedTypes' => [...$bsonTypes['object']], + ], + 'geoPoint' => [ + 'acceptedTypes' => [...$bsonTypes['object']], + ], + + // Search + 'searchPath' => [ + 'acceptedTypes' => ['string', 'array'], + ], + 'searchScore' => [ + 'acceptedTypes' => [...$bsonTypes['object']], + ], +]; diff --git a/generator/config/query/all.yaml b/generator/config/query/all.yaml new file mode 100644 index 000000000..868e205e2 --- /dev/null +++ b/generator/config/query/all.yaml @@ -0,0 +1,43 @@ +# $schema: ../schema.json +name: $all +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/all/' +type: + - fieldQuery +encode: single +description: | + Matches arrays that contain all elements specified in the query. +arguments: + - + name: value + type: + - fieldQuery + variadic: array +tests: + - + name: 'Use $all to Match Values' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/all/#use--all-to-match-values' + pipeline: + - + $match: + tags: + $all: + - 'appliance' + - 'school' + - 'book' + - + name: 'Use $all with $elemMatch' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/all/#use--all-with--elemmatch' + pipeline: + - + $match: + qty: + $all: + - + $elemMatch: + size: 'M' + num: + $gt: 50 + - + $elemMatch: + num: 100 + color: 'green' diff --git a/generator/config/query/and.yaml b/generator/config/query/and.yaml new file mode 100644 index 000000000..74ebf506e --- /dev/null +++ b/generator/config/query/and.yaml @@ -0,0 +1,51 @@ +# $schema: ../schema.json +name: $and +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/and/' +type: + - query +encode: single +description: | + Joins query clauses with a logical AND returns all documents that match the conditions of both clauses. +arguments: + - + name: queries + type: + - query + variadic: array + variadicMin: 1 +tests: + - + name: 'AND Queries With Multiple Expressions Specifying the Same Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/and/#and-queries-with-multiple-expressions-specifying-the-same-field' + pipeline: + - + $match: + $and: + - + price: + $ne: 1.99 + - + price: + $exists: true + - + name: 'AND Queries With Multiple Expressions Specifying the Same Operator' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/and/#and-queries-with-multiple-expressions-specifying-the-same-operator' + pipeline: + - + $match: + $and: + - + $or: + - + qty: + $lt: 10 + - + qty: + $gt: 50 + - + $or: + - + sale: true + - + price: + $lt: 5 diff --git a/generator/config/query/bitsAllClear.yaml b/generator/config/query/bitsAllClear.yaml new file mode 100644 index 000000000..48e98037d --- /dev/null +++ b/generator/config/query/bitsAllClear.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitsAllClear +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/' +type: + - fieldQuery +encode: single +description: | + Matches numeric or binary values in which a set of bit positions all have a value of 0. +arguments: + - + name: bitmask + type: + - int + - binData + - array # of int +tests: + - + name: 'Bit Position Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/#bit-position-array' + pipeline: + - + $match: + a: + $bitsAllClear: [1, 5] + - + name: 'Integer Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/#integer-bitmask' + pipeline: + - $match: + a: + $bitsAllClear: 35 + - + name: 'BinData Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllClear/#bindata-bitmask' + pipeline: + - $match: + a: + $bitsAllClear: !bson_binary 'IA==' diff --git a/generator/config/query/bitsAllSet.yaml b/generator/config/query/bitsAllSet.yaml new file mode 100644 index 000000000..25e2c6eb8 --- /dev/null +++ b/generator/config/query/bitsAllSet.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitsAllSet +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/' +type: + - fieldQuery +encode: single +description: | + Matches numeric or binary values in which a set of bit positions all have a value of 1. +arguments: + - + name: bitmask + type: + - int + - binData + - array # of int +tests: + - + name: 'Bit Position Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/#bit-position-array' + pipeline: + - + $match: + a: + $bitsAllSet: [1, 5] + - + name: 'Integer Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/#integer-bitmask' + pipeline: + - $match: + a: + $bitsAllSet: 50 + - + name: 'BinData Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAllSet/#bindata-bitmask' + pipeline: + - $match: + a: + $bitsAllSet: !bson_binary 'MA==' diff --git a/generator/config/query/bitsAnyClear.yaml b/generator/config/query/bitsAnyClear.yaml new file mode 100644 index 000000000..a41260998 --- /dev/null +++ b/generator/config/query/bitsAnyClear.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitsAnyClear +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/' +type: + - fieldQuery +encode: single +description: | + Matches numeric or binary values in which any bit from a set of bit positions has a value of 0. +arguments: + - + name: bitmask + type: + - int + - binData + - array # of int +tests: + - + name: 'Bit Position Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/#bit-position-array' + pipeline: + - + $match: + a: + $bitsAnyClear: [1, 5] + - + name: 'Integer Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/#integer-bitmask' + pipeline: + - $match: + a: + $bitsAnyClear: 35 + - + name: 'BinData Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnyClear/#bindata-bitmask' + pipeline: + - $match: + a: + $bitsAnyClear: !bson_binary 'MA==' diff --git a/generator/config/query/bitsAnySet.yaml b/generator/config/query/bitsAnySet.yaml new file mode 100644 index 000000000..95aae908a --- /dev/null +++ b/generator/config/query/bitsAnySet.yaml @@ -0,0 +1,38 @@ +# $schema: ../schema.json +name: $bitsAnySet +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/' +type: + - fieldQuery +encode: single +description: | + Matches numeric or binary values in which any bit from a set of bit positions has a value of 1. +arguments: + - + name: bitmask + type: + - int + - binData + - array # of int +tests: + - + name: 'Bit Position Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/#bit-position-array' + pipeline: + - + $match: + a: + $bitsAnySet: [1, 5] + - + name: 'Integer Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/#integer-bitmask' + pipeline: + - $match: + a: + $bitsAnySet: 35 + - + name: 'BinData Bitmask' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/bitsAnySet/#bindata-bitmask' + pipeline: + - $match: + a: + $bitsAnySet: !bson_binary 'MA==' diff --git a/generator/config/query/box.yaml b/generator/config/query/box.yaml new file mode 100644 index 000000000..14043c4ae --- /dev/null +++ b/generator/config/query/box.yaml @@ -0,0 +1,13 @@ +# $schema: ../schema.json +name: $box +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/box/' +type: + - geometry +encode: single +description: | + Specifies a rectangular box using legacy coordinate pairs for $geoWithin queries. The 2d index supports $box. +arguments: + - + name: value + type: + - array diff --git a/generator/config/query/center.yaml b/generator/config/query/center.yaml new file mode 100644 index 000000000..c86215a52 --- /dev/null +++ b/generator/config/query/center.yaml @@ -0,0 +1,13 @@ +# $schema: ../schema.json +name: $center +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/center/' +type: + - geometry +encode: single +description: | + Specifies a circle using legacy coordinate pairs to $geoWithin queries when using planar geometry. The 2d index supports $center. +arguments: + - + name: value + type: + - array diff --git a/generator/config/query/centerSphere.yaml b/generator/config/query/centerSphere.yaml new file mode 100644 index 000000000..e3677dade --- /dev/null +++ b/generator/config/query/centerSphere.yaml @@ -0,0 +1,13 @@ +# $schema: ../schema.json +name: $centerSphere +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/centerSphere/' +type: + - geometry +encode: single +description: | + Specifies a circle using either legacy coordinate pairs or GeoJSON format for $geoWithin queries when using spherical geometry. The 2dsphere and 2d indexes support $centerSphere. +arguments: + - + name: value + type: + - array diff --git a/generator/config/query/comment.yaml b/generator/config/query/comment.yaml new file mode 100644 index 000000000..13a344613 --- /dev/null +++ b/generator/config/query/comment.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $comment +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/comment/' +type: + - query +encode: single +description: | + Adds a comment to a query predicate. +arguments: + - + name: comment + type: + - string +tests: + - + name: 'Attach a Comment to an Aggregation Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/comment/#attach-a-comment-to-an-aggregation-expression' + pipeline: + - + $match: + x: + $gt: 0 + $comment: 'Don''t allow negative inputs.' + - + $group: + _id: + $mod: + - '$x' + - 2 + total: + $sum: '$x' diff --git a/generator/config/query/elemMatch.yaml b/generator/config/query/elemMatch.yaml new file mode 100644 index 000000000..95db9572e --- /dev/null +++ b/generator/config/query/elemMatch.yaml @@ -0,0 +1,68 @@ +# $schema: ../schema.json +name: $elemMatch +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/' +type: + - fieldQuery +encode: single +description: | + The $elemMatch operator matches documents that contain an array field with at least one element that matches all the specified query criteria. +arguments: + - + name: query + type: + - query + - fieldQuery +tests: + - + name: 'Element Match' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/#element-match' + pipeline: + - + $match: + results: + $elemMatch: + $gte: 80 + $lt: 85 + - + name: 'Array of Embedded Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/#array-of-embedded-documents' + pipeline: + - + $match: + results: + $elemMatch: + product: 'xyz' + score: + $gte: 8 + - + name: 'Single Query Condition' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/elemMatch/#single-query-condition' + pipeline: + - + $match: + results: + $elemMatch: + product: + $ne: 'xyz' + - + name: 'Using $or with $elemMatch' + pipeline: + - + $match: + game: + $elemMatch: + $or: + - + score: + $gt: 10 + - + score: + $lt: 5 + - + name: 'Single field operator' + pipeline: + - + $match: + results: + $elemMatch: + $gt: 10 diff --git a/generator/config/query/eq.yaml b/generator/config/query/eq.yaml new file mode 100644 index 000000000..5629114cc --- /dev/null +++ b/generator/config/query/eq.yaml @@ -0,0 +1,61 @@ +# $schema: ../schema.json +name: $eq +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/eq/' +type: + - fieldQuery +encode: single +description: | + Matches values that are equal to a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Equals a Specified Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/eq/#equals-a-specified-value' + pipeline: + - + $match: + qty: + $eq: 20 + + - + name: 'Field in Embedded Document Equals a Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/eq/#field-in-embedded-document-equals-a-value' + pipeline: + - + $match: + 'item.name': + $eq: 'ab' + + - + name: 'Equals an Array Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/eq/#equals-an-array-value' + pipeline: + - + $match: + tags: + $eq: ['A', 'B'] + + - + name: 'Regex Match Behaviour' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/eq/#regex-match-behaviour' + pipeline: + - + $match: + company: 'MongoDB' + - + $match: + company: + $eq: 'MongoDB' + - + $match: + company: + !bson_regex '^MongoDB' + - + $match: + company: + $eq: + !bson_regex '^MongoDB' diff --git a/generator/config/query/exists.yaml b/generator/config/query/exists.yaml new file mode 100644 index 000000000..00d7ce2cb --- /dev/null +++ b/generator/config/query/exists.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $exists +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/exists/' +type: + - fieldQuery +encode: single +description: | + Matches documents that have the specified field. +arguments: + - + name: exists + type: + - bool + default: true +tests: + - + name: 'Exists and Not Equal To' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/exists/#exists-and-not-equal-to' + pipeline: + - + $match: + qty: + $exists: true + $nin: [5, 15] + - + name: 'Null Values' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/exists/#null-values' + pipeline: + - + $match: + qty: + $exists: true + - + name: 'Missing Field' + pipeline: + - + $match: + qty: + $exists: false diff --git a/generator/config/query/expr.yaml b/generator/config/query/expr.yaml new file mode 100644 index 000000000..320c84507 --- /dev/null +++ b/generator/config/query/expr.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $expr +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/expr/' +type: + - query +encode: single +description: | + Allows use of aggregation expressions within the query language. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Compare Two Fields from A Single Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/expr/#compare-two-fields-from-a-single-document' + pipeline: + - + $match: + $expr: + $gt: + - '$spent' + - '$budget' + - + name: 'Using $expr With Conditional Statements' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/expr/#using--expr-with-conditional-statements' + pipeline: + - + $match: + $expr: + $lt: + - + $cond: + if: + $gte: + - '$qty' + - 100 + then: + $multiply: + - '$price' + - 0.5 + else: + $multiply: + - '$price' + - 0.75 + - 5 diff --git a/generator/config/query/geoIntersects.yaml b/generator/config/query/geoIntersects.yaml new file mode 100644 index 000000000..4df3a43de --- /dev/null +++ b/generator/config/query/geoIntersects.yaml @@ -0,0 +1,53 @@ +# $schema: ../schema.json +name: $geoIntersects +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoIntersects/' +type: + - fieldQuery +encode: object +description: | + Selects geometries that intersect with a GeoJSON geometry. The 2dsphere index supports $geoIntersects. +arguments: + - + name: geometry + mergeObject: true + type: + - geometry +tests: + - + name: 'Intersects a Polygon' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoIntersects/#intersects-a-polygon' + pipeline: + - + $match: + loc: + $geoIntersects: + $geometry: + type: 'Polygon' + coordinates: + - + - [ 0, 0 ] + - [ 3, 6 ] + - [ 6, 1 ] + - [ 0, 0 ] + - + name: 'Intersects a Big Polygon' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoIntersects/#intersects-a--big--polygon' + pipeline: + - + $match: + loc: + $geoIntersects: + $geometry: + type: 'Polygon' + coordinates: + - + - [ -100, 60 ] + - [ -100, 0 ] + - [ -100, -60 ] + - [ 100, -60 ] + - [ 100, 60 ] + - [ -100, 60 ] + crs: + type: 'name' + properties: + name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' diff --git a/generator/config/query/geoWithin.yaml b/generator/config/query/geoWithin.yaml new file mode 100644 index 000000000..f9f6204d0 --- /dev/null +++ b/generator/config/query/geoWithin.yaml @@ -0,0 +1,53 @@ +# $schema: ../schema.json +name: $geoWithin +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/' +type: + - fieldQuery +encode: object +description: | + Selects geometries within a bounding GeoJSON geometry. The 2dsphere and 2d indexes support $geoWithin. +arguments: + - + name: geometry + mergeObject: true + type: + - geometry +tests: + - + name: 'Within a Polygon' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/#within-a-polygon' + pipeline: + - + $match: + loc: + $geoWithin: + $geometry: + type: 'Polygon' + coordinates: + - + - [ 0, 0 ] + - [ 3, 6 ] + - [ 6, 1 ] + - [ 0, 0 ] + - + name: 'Within a Big Polygon' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geoWithin/#within-a--big--polygon' + pipeline: + - + $match: + loc: + $geoWithin: + $geometry: + type: 'Polygon' + coordinates: + - + - [ -100, 60 ] + - [ -100, 0 ] + - [ -100, -60 ] + - [ 100, -60 ] + - [ 100, 60 ] + - [ -100, 60 ] + crs: + type: 'name' + properties: + name: 'urn:x-mongodb:crs:strictwinding:EPSG:4326' diff --git a/generator/config/query/geometry.yaml b/generator/config/query/geometry.yaml new file mode 100644 index 000000000..40b18dc99 --- /dev/null +++ b/generator/config/query/geometry.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $geometry +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/geometry/' +type: + - geometry +encode: object +description: | + Specifies a geometry in GeoJSON format to geospatial query operators. +arguments: + - + name: type + type: + - string + - + name: coordinates + type: + - array + - + name: crs + type: + - object + optional: true diff --git a/generator/config/query/gt.yaml b/generator/config/query/gt.yaml new file mode 100644 index 000000000..9914a5f34 --- /dev/null +++ b/generator/config/query/gt.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $gt +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/gt/' +type: + - fieldQuery +encode: single +description: | + Matches values that are greater than a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Match Document Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/gt/#match-document-fields' + pipeline: + - + $match: + qty: + $gt: 20 diff --git a/generator/config/query/gte.yaml b/generator/config/query/gte.yaml new file mode 100644 index 000000000..d8617a7c6 --- /dev/null +++ b/generator/config/query/gte.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $gte +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/gte/' +type: + - fieldQuery +encode: single +description: | + Matches values that are greater than or equal to a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Match Document Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/gte/#match-document-fields' + pipeline: + - + $match: + qty: + $gte: 20 diff --git a/generator/config/query/in.yaml b/generator/config/query/in.yaml new file mode 100644 index 000000000..67f069416 --- /dev/null +++ b/generator/config/query/in.yaml @@ -0,0 +1,32 @@ +# $schema: ../schema.json +name: $in +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/in/' +type: + - fieldQuery +encode: single +description: | + Matches any of the values specified in an array. +arguments: + - + name: value + type: + - array # of expression +tests: + - + name: 'Use the $in Operator to Match Values in an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/in/#use-the--in-operator-to-match-values' + pipeline: + - + $match: + tags: + $in: ['home', 'school'] + - + name: 'Use the $in Operator with a Regular Expression' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/in/#use-the--in-operator-with-a-regular-expression' + pipeline: + - + $match: + tags: + $in: + - !bson_regex '^be' + - !bson_regex '^st' diff --git a/generator/config/query/jsonSchema.yaml b/generator/config/query/jsonSchema.yaml new file mode 100644 index 000000000..4c1dca6ad --- /dev/null +++ b/generator/config/query/jsonSchema.yaml @@ -0,0 +1,39 @@ +# $schema: ../schema.json +name: $jsonSchema +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/jsonSchema/' +type: + - query +encode: single +description: | + Validate documents against the given JSON Schema. +arguments: + - + name: schema + type: + - object +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/jsonSchema/#syntax' + pipeline: + - + $match: + $jsonSchema: + required: + - 'name' + - 'major' + - 'gpa' + - 'address' + properties: + name: + bsonType: 'string' + description: 'must be a string and is required' + address: + bsonType: 'object' + required: + - 'zipcode' + properties: + street: + bsonType: 'string' + zipcode: + bsonType: 'string' diff --git a/generator/config/query/lt.yaml b/generator/config/query/lt.yaml new file mode 100644 index 000000000..f1c996ded --- /dev/null +++ b/generator/config/query/lt.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $lt +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/lt/' +type: + - fieldQuery +encode: single +description: | + Matches values that are less than a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Match Document Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/lt/#match-document-fields' + pipeline: + - + $match: + qty: + $lt: 20 diff --git a/generator/config/query/lte.yaml b/generator/config/query/lte.yaml new file mode 100644 index 000000000..e61d01b03 --- /dev/null +++ b/generator/config/query/lte.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $lte +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/lte/' +type: + - fieldQuery +encode: single +description: | + Matches values that are less than or equal to a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Match Document Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/lte/#match-document-fields' + pipeline: + - + $match: + qty: + $lte: 20 diff --git a/generator/config/query/maxDistance.yaml b/generator/config/query/maxDistance.yaml new file mode 100644 index 000000000..e95212d23 --- /dev/null +++ b/generator/config/query/maxDistance.yaml @@ -0,0 +1,13 @@ +# $schema: ../schema.json +name: $maxDistance +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/maxDistance/' +type: + - fieldQuery +encode: single +description: | + Specifies a maximum distance to limit the results of $near and $nearSphere queries. The 2dsphere and 2d indexes support $maxDistance. +arguments: + - + name: value + type: + - number diff --git a/generator/config/query/minDistance.yaml b/generator/config/query/minDistance.yaml new file mode 100644 index 000000000..fd467a96f --- /dev/null +++ b/generator/config/query/minDistance.yaml @@ -0,0 +1,14 @@ +# $schema: ../schema.json +name: $minDistance +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/minDistance/' +type: + - fieldQuery +encode: single +description: | + Specifies a minimum distance to limit the results of $near and $nearSphere queries. For use with 2dsphere index only. +arguments: + - + name: value + type: + - int + - double diff --git a/generator/config/query/mod.yaml b/generator/config/query/mod.yaml new file mode 100644 index 000000000..04a187253 --- /dev/null +++ b/generator/config/query/mod.yaml @@ -0,0 +1,42 @@ +# $schema: ../schema.json +name: $mod +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/mod/' +type: + - fieldQuery +encode: array +description: | + Performs a modulo operation on the value of a field and selects documents with a specified result. +arguments: + - + name: divisor + type: + - number + - + name: remainder + type: + - number +tests: + - + name: 'Use $mod to Select Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/mod/#use--mod-to-select-documents' + pipeline: + - + $match: + qty: + $mod: [4, 0] + - + name: 'Floating Point Arguments' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/mod/#floating-point-arguments' + pipeline: + - + $match: + qty: + $mod: [4.0, 0] + - + $match: + qty: + $mod: [4.5, 0] + - + $match: + qty: + $mod: [4.99, 0] diff --git a/generator/config/query/ne.yaml b/generator/config/query/ne.yaml new file mode 100644 index 000000000..a1f5a046b --- /dev/null +++ b/generator/config/query/ne.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $ne +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/ne/' +type: + - fieldQuery +encode: single +description: | + Matches all values that are not equal to a specified value. +arguments: + - + name: value + type: + - any +tests: + - + name: 'Match Document Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/ne/#match-document-fields' + pipeline: + - + $match: + quantity: + $ne: 20 diff --git a/generator/config/query/near.yaml b/generator/config/query/near.yaml new file mode 100644 index 000000000..89d7f511f --- /dev/null +++ b/generator/config/query/near.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $near +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/near/' +type: + - fieldQuery +encode: object +description: | + Returns geospatial objects in proximity to a point. Requires a geospatial index. The 2dsphere and 2d indexes support $near. +arguments: + - + name: geometry + mergeObject: true + type: + - geometry + - + name: $maxDistance + type: + - number + optional: true + description: | + Distance in meters. Limits the results to those documents that are at most the specified distance from the center point. + - + name: $minDistance + type: + - number + optional: true + description: | + Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. +tests: + - + name: 'Query on GeoJSON Data' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/near/#query-on-geojson-data' + pipeline: + - + $match: + location: + $near: + $geometry: + type: 'Point' + coordinates: + - -73.9667 + - 40.78 + $minDistance: 1000 + $maxDistance: 5000 diff --git a/generator/config/query/nearSphere.yaml b/generator/config/query/nearSphere.yaml new file mode 100644 index 000000000..72e8e18e9 --- /dev/null +++ b/generator/config/query/nearSphere.yaml @@ -0,0 +1,42 @@ +# $schema: ../schema.json +name: $nearSphere +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nearSphere/' +type: + - fieldQuery +encode: object +description: | + Returns geospatial objects in proximity to a point on a sphere. Requires a geospatial index. The 2dsphere and 2d indexes support $nearSphere. +arguments: + - + name: geometry + mergeObject: true + type: + - geometry + - + name: $maxDistance + type: + - number + optional: true + description: | + Distance in meters. + - + name: $minDistance + type: + - number + optional: true + description: | + Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. +tests: + - + name: 'Specify Center Point Using GeoJSON' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nearSphere/#specify-center-point-using-geojson' + pipeline: + - + $match: + location: + $nearSphere: + $geometry: + type: 'Point' + coordinates: [-73.9667, 40.78] + $minDistance: 1000 + $maxDistance: 5000 diff --git a/generator/config/query/nin.yaml b/generator/config/query/nin.yaml new file mode 100644 index 000000000..4285e4fe7 --- /dev/null +++ b/generator/config/query/nin.yaml @@ -0,0 +1,30 @@ +# $schema: ../schema.json +name: $nin +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nin/' +type: + - fieldQuery +encode: single +description: | + Matches none of the values specified in an array. +arguments: + - + name: value + type: + - array # of expression +tests: + - + name: 'Select on Unmatching Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nin/#select-on-unmatching-documents' + pipeline: + - + $match: + quantity: + $nin: [5, 15] + - + name: 'Select on Elements Not in an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nin/#select-on-elements-not-in-an-array' + pipeline: + - + $match: + tags: + $nin: ['school'] diff --git a/generator/config/query/nor.yaml b/generator/config/query/nor.yaml new file mode 100644 index 000000000..d1ad7159a --- /dev/null +++ b/generator/config/query/nor.yaml @@ -0,0 +1,58 @@ +# $schema: ../schema.json +name: $nor +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nor/' +type: + - query +encode: single +description: | + Joins query clauses with a logical NOR returns all documents that fail to match both clauses. +arguments: + - + name: queries + type: + - query + variadic: array + variadicMin: 1 +tests: + - + name: 'Query with Two Expressions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nor/#-nor-query-with-two-expressions' + pipeline: + - + $match: + $nor: + - + price: 1.99 + - + sale: true + - + name: 'Additional Comparisons' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nor/#-nor-and-additional-comparisons' + pipeline: + - + $match: + $nor: + - + price: 1.99 + - + qty: + $lt: 20 + - + sale: true + - + name: '$nor and $exists' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/nor/#-nor-and--exists' + pipeline: + - + $match: + $nor: + - + price: 1.99 + - + price: + $exists: false + - + sale: true + - + sale: + $exists: false diff --git a/generator/config/query/not.yaml b/generator/config/query/not.yaml new file mode 100644 index 000000000..eb2b43cdb --- /dev/null +++ b/generator/config/query/not.yaml @@ -0,0 +1,31 @@ +# $schema: ../schema.json +name: $not +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/not/' +type: + - fieldQuery +encode: single +description: | + Inverts the effect of a query expression and returns documents that do not match the query expression. +arguments: + - + name: expression + type: + - fieldQuery +tests: + - + name: 'Syntax' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/not/#syntax' + pipeline: + - + $match: + price: + $not: + $gt: 1.99 + - + name: 'Regular Expressions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/not/#-not-and-regular-expressions' + pipeline: + - + $match: + price: + $not: !bson_regex '^p.*' diff --git a/generator/config/query/or.yaml b/generator/config/query/or.yaml new file mode 100644 index 000000000..ce2b7603c --- /dev/null +++ b/generator/config/query/or.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $or +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/or/' +type: + - query +encode: single +description: | + Joins query clauses with a logical OR returns all documents that match the conditions of either clause. +arguments: + - + name: queries + type: + - query + variadic: array + variadicMin: 1 +tests: + - + name: '$or Clauses' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/or/#-or-clauses-and-indexes' + pipeline: + - + $match: + $or: + - + quantity: + $lt: 20 + - + price: 10 + + - + name: 'Error Handling' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/or/#error-handling' + pipeline: + - + $match: + $or: + - + x: + $eq: 0 + - + $expr: + $eq: + - + $divide: + - 1 + - '$x' + - 3 diff --git a/generator/config/query/polygon.yaml b/generator/config/query/polygon.yaml new file mode 100644 index 000000000..1a46f2bc4 --- /dev/null +++ b/generator/config/query/polygon.yaml @@ -0,0 +1,13 @@ +# $schema: ../schema.json +name: $polygon +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/polygon/' +type: + - geometry +encode: single +description: | + Specifies a polygon to using legacy coordinate pairs for $geoWithin queries. The 2d index supports $center. +arguments: + - + name: points + type: + - array diff --git a/generator/config/query/rand.yaml b/generator/config/query/rand.yaml new file mode 100644 index 000000000..6773ae0d5 --- /dev/null +++ b/generator/config/query/rand.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $rand +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/rand/' +type: + - resolvesToDouble +encode: object +description: | + Generates a random float between 0 and 1. +tests: + - + name: 'Select Random Items From a Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/rand/#select-random-items-from-a-collection' + pipeline: + - + $match: + district: 3 + $expr: + $lt: + - 0.5 + - + $rand: {} + - + $project: + _id: 0 + name: 1 + registered: 1 diff --git a/generator/config/query/regex.yaml b/generator/config/query/regex.yaml new file mode 100644 index 000000000..c7e378ddd --- /dev/null +++ b/generator/config/query/regex.yaml @@ -0,0 +1,33 @@ +# $schema: ../schema.json +name: $regex +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/regex/' +type: + - fieldQuery +encode: single +description: | + Selects documents where values match a specified regular expression. +arguments: + - + name: regex + type: + - regex + +tests: + - + name: 'Perform a LIKE Match' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/regex/#perform-a-like-match' + pipeline: + - + $match: + sku: + $regex: + !bson_regex '789$' + - + name: 'Perform Case-Insensitive Regular Expression Match' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/regex/#perform-case-insensitive-regular-expression-match' + pipeline: + - + $match: + sku: + $regex: + !bson_regex ['^ABC', 'i'] diff --git a/generator/config/query/sampleRate.yaml b/generator/config/query/sampleRate.yaml new file mode 100644 index 000000000..9995e2d8b --- /dev/null +++ b/generator/config/query/sampleRate.yaml @@ -0,0 +1,26 @@ +# $schema: ../schema.json +name: $sampleRate +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sampleRate/' +type: + - query +encode: single +description: | + Randomly select documents at a given rate. Although the exact number of documents selected varies on each run, the quantity chosen approximates the sample rate expressed as a percentage of the total number of documents. +arguments: + - + name: rate + type: + - resolvesToDouble + description: | + The selection process uses a uniform random distribution. The sample rate is a floating point number between 0 and 1, inclusive, which represents the probability that a given document will be selected as it passes through the pipeline. + For example, a sample rate of 0.33 selects roughly one document in three. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sampleRate/#examples' + pipeline: + - + $match: + $sampleRate: 0.33 + - + $count: 'numMatches' diff --git a/generator/config/query/size.yaml b/generator/config/query/size.yaml new file mode 100644 index 000000000..629de4035 --- /dev/null +++ b/generator/config/query/size.yaml @@ -0,0 +1,22 @@ +# $schema: ../schema.json +name: $size +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/size/' +type: + - fieldQuery +encode: single +description: | + Selects documents if the array field is a specified size. +arguments: + - + name: value + type: + - int +tests: + - + name: 'Query an Array by Array Length' + link: 'https://www.mongodb.com/docs/manual/tutorial/query-arrays/#query-an-array-by-array-length' + pipeline: + - + $match: + tags: + $size: 3 diff --git a/generator/config/query/text.yaml b/generator/config/query/text.yaml new file mode 100644 index 000000000..574ee4508 --- /dev/null +++ b/generator/config/query/text.yaml @@ -0,0 +1,114 @@ +# $schema: ../schema.json +name: $text +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/' +type: + - query +encode: object +description: | + Performs text search. +arguments: + - + name: $search + type: + - string + description: | + A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase. + - + name: $language + type: + - string + optional: true + description: | + The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index. + If you specify a default_language value of none, then the text index parses through each word in the field, including stop words, and ignores suffix stemming. + - + name: $caseSensitive + type: + - bool + optional: true + description: | + A boolean flag to enable or disable case sensitive search. Defaults to false; i.e. the search defers to the case insensitivity of the text index. + - + name: $diacriticSensitive + type: + - bool + optional: true + description: | + A boolean flag to enable or disable diacritic sensitive search against version 3 text indexes. Defaults to false; i.e. the search defers to the diacritic insensitivity of the text index. + Text searches against earlier versions of the text index are inherently diacritic sensitive and cannot be diacritic insensitive. As such, the $diacriticSensitive option has no effect with earlier versions of the text index. +tests: + - + name: 'Search for a Single Word' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#search-for-a-single-word' + pipeline: + - + $match: + $text: + $search: 'coffee' + - + name: 'Match Any of the Search Terms' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#search-for-a-single-word' + pipeline: + - + $match: + $text: + $search: 'bake coffee cake' + - + name: 'Search a Different Language' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#search-a-different-language' + pipeline: + - + $match: + $text: + $search: 'leche' + $language: 'es' + - + name: 'Case and Diacritic Insensitive Search' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#case-and-diacritic-insensitive-search' + pipeline: + - + $match: + $text: + $search: 'сы́рники CAFÉS' + - + name: 'Perform Case Sensitive Search' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#perform-case-sensitive-search' + pipeline: + - + $match: + $text: + $search: 'Coffee' + $caseSensitive: true + - + $match: + $text: + $search: '\"Café Con Leche\"' + $caseSensitive: true + - + name: 'Diacritic Sensitive Search' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#perform-case-sensitive-search' + pipeline: + - + $match: + $text: + $search: 'CAFÉ' + $diacriticSensitive: true + - + name: 'Text Search Score Examples' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/text/#perform-case-sensitive-search' + pipeline: + - + $match: + $text: + $search: 'CAFÉ' + $diacriticSensitive: true + - + $project: + score: + $meta: 'textScore' + - + $sort: + score: + $meta: 'textScore' + - + $limit: 5 diff --git a/generator/config/query/type.yaml b/generator/config/query/type.yaml new file mode 100644 index 000000000..d8cd7bc86 --- /dev/null +++ b/generator/config/query/type.yaml @@ -0,0 +1,88 @@ +# $schema: ../schema.json +name: $type +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/type/' +type: + - fieldQuery +encode: single +description: | + Selects documents if a field is of the specified type. +arguments: + - + name: type + type: + - int + - string + variadic: array +tests: + - + name: 'Querying by Data Type' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/type/#querying-by-data-type' + pipeline: + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 2 + $type: [2] + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'string' + $type: ['string'] + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 1 + $type: [1] + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'double' + $type: ['double'] + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'number' + $type: ['number'] + - + name: 'Querying by Multiple Data Type' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/type/#querying-by-multiple-data-type' + pipeline: + - + $match: + zipCode: + $type: [2, 1] + - + $match: + zipCode: + $type: ['string', 'double'] + - + name: 'Querying by MinKey and MaxKey' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/type/#querying-by-minkey-and-maxkey' + pipeline: + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'minKey' + $type: ['minKey'] + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'maxKey' + $type: ['maxKey'] + - + name: 'Querying by Array Type' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/type/#querying-by-array-type' + pipeline: + - + $match: + zipCode: + # Example uses the short form, the builder always generates the verbose form + # $type: 'array' + $type: ['array'] diff --git a/generator/config/query/where.yaml b/generator/config/query/where.yaml new file mode 100644 index 000000000..5f5c974ab --- /dev/null +++ b/generator/config/query/where.yaml @@ -0,0 +1,36 @@ +# $schema: ../schema.json +name: $where +link: 'https://www.mongodb.com/docs/manual/reference/operator/query/where/' +type: + - query +encode: single +description: | + Matches documents that satisfy a JavaScript expression. +arguments: + - + name: function + type: + - javascript +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/query/where/#example' + pipeline: + - + $match: + $where: + $code: |- + function() { + return hex_md5(this.name) == "9b53e667f30cd329dca1ec9e6a83e994" + } + - + $match: + $expr: + $function: + body: + $code: |- + function(name) { + return hex_md5(name) == "9b53e667f30cd329dca1ec9e6a83e994"; + } + args: ['$name'] + lang: 'js' diff --git a/generator/config/schema.json b/generator/config/schema.json new file mode 100644 index 000000000..63739ebcb --- /dev/null +++ b/generator/config/schema.json @@ -0,0 +1,221 @@ +{ + "$schema": "http://json-schema.org/draft-06/schema#", + "$ref": "#/definitions/Operator", + "definitions": { + "Operator": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "$comment": "The name of the operator. Must start with a $", + "type": "string", + "pattern": "^\\$?[a-z][a-zA-Z0-9]*$" + }, + "link": { + "$comment": "The link to the operator's documentation on MongoDB's website.", + "type": "string", + "format": "uri", + "pattern": "^https://" + }, + "type": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "accumulator", + "stage", + "query", + "fieldQuery", + "filter", + "window", + "geometry", + "switchBranch", + "resolvesToAny", + "resolvesToNumber", + "resolvesToDouble", + "resolvesToString", + "resolvesToObject", + "resolvesToArray", + "resolvesToBinData", + "resolvesToObjectId", + "resolvesToBool", + "resolvesToDate", + "resolvesToNull", + "resolvesToRegex", + "resolvesToJavascript", + "resolvesToInt", + "resolvesToTimestamp", + "resolvesToLong", + "resolvesToDecimal", + "searchOperator" + ] + } + }, + "encode": { + "$comment": "Specifies how operator parameters are encoded.", + "$comment": "array: parameters are encoded as an array of values in the order they are defined by the spec", + "$comment": "object: parameters are encoded as an object with keys matching the parameter names", + "$comment": "single: get the single parameter value", + "$comment": "group: specific for $group stage", + "type": "string", + "enum": [ + "array", + "object", + "single", + "search" + ] + }, + "description": { + "$comment": "The description of the argument from MongoDB's documentation.", + "type": "string" + }, + "wrapObject": { + "$comment": "Wrap the properties in an object with the operator name", + "type": "boolean", + "default": true + }, + "arguments": { + "$comment": "An optional list of arguments for the operator.", + "type": "array", + "items": { + "$ref": "#/definitions/Argument" + } + }, + "tests": { + "$comment": "An optional list of examples for the operator.", + "type": "array", + "items": { + "$ref": "#/definitions/Test" + } + } + }, + "required": [ + "description", + "encode", + "link", + "name", + "type" + ], + "title": "Operator" + }, + "Argument": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string", + "pattern": "^([_$]?[a-z][a-zA-Z0-9]*|N)$" + }, + "type": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "accumulator", + "query", + "fieldQuery", + "pipeline", + "window", + "expression", + "geometry", + "fieldPath", + "timeUnit", + "sortSpec", + "any", + "granularity", + "fullDocument", + "fullDocumentBeforeChange", + "accumulatorPercentile", + "whenMatched", + "whenNotMatched", + "outCollection", + "range", + "sortBy", + "geoPoint", + "resolvesToNumber", "numberFieldPath", "number", + "resolvesToDouble", "doubleFieldPath", "double", + "resolvesToString", "stringFieldPath", "string", + "resolvesToObject", "objectFieldPath", "object", + "resolvesToArray", "arrayFieldPath", "array", + "resolvesToBinData", "binDataFieldPath", "binData", + "resolvesToObjectId", "objectIdFieldPath", "objectId", + "resolvesToBool", "boolFieldPath", "bool", + "resolvesToDate", "dateFieldPath", "date", + "resolvesToNull", "nullFieldPath", "null", + "resolvesToRegex", "regexFieldPath", "regex", + "resolvesToJavascript", "javascriptFieldPath", "javascript", + "resolvesToInt", "intFieldPath", "int", + "resolvesToTimestamp", "timestampFieldPath", "timestamp", + "resolvesToLong", "longFieldPath", "long", + "resolvesToDecimal", "decimalFieldPath", "decimal", + "searchPath", "searchScore", "searchOperator" + ] + } + }, + "description": { + "$comment": "The description of the argument from MongoDB's documentation.", + "type": "string" + }, + "optional": { + "$comment": "Whether the argument is optional or not.", + "type": "boolean" + }, + "valueMin": { + "$comment": "The minimum value for a numeric argument.", + "type": "number" + }, + "valueMax": { + "$comment": "The maximum value for a numeric argument.", + "type": "number" + }, + "variadic": { + "$comment": "Whether the argument is variadic or not.", + "type": "string", + "enum": [ + "array", + "object" + ] + }, + "variadicMin": { + "$comment": "The minimum number of arguments for a variadic parameter.", + "type": "integer", + "minimum": 0 + }, + "default": { + "$comment": "The default value for the argument.", + "type": ["array", "boolean", "number", "string"] + }, + "mergeObject": { + "$comment": "Skip the name in object encoding and merge the properties of the value into the operator", + "type": "boolean", + "default": false + } + }, + "required": [ + "name", + "type" + ], + "title": "Argument" + }, + "Test": { + "type": "object", + "additionalProperties": false, + "properties": { + "name": { + "type": "string" + }, + "link": { + "type": "string", + "format": "uri", + "pattern": "^https://" + }, + "pipeline": { + "type": "array", + "items": { + "type": "object" + } + } + } + } + } +} diff --git a/generator/config/search/autocomplete.yaml b/generator/config/search/autocomplete.yaml new file mode 100644 index 000000000..a984b9a39 --- /dev/null +++ b/generator/config/search/autocomplete.yaml @@ -0,0 +1,152 @@ +# $schema: ../schema.json +name: autocomplete +link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/' +type: + - searchOperator +encode: object +description: | + The autocomplete operator performs a search for a word or phrase that + contains a sequence of characters from an incomplete input string. The + fields that you intend to query with the autocomplete operator must be + indexed with the autocomplete data type in the collection's index definition. +arguments: + - + name: path + type: + - searchPath + - + name: query + type: + - string + - + name: tokenOrder + optional: true + type: + - string # any|sequential + - + name: fuzzy + optional: true + type: + - object + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Basic' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#basic-example' + pipeline: + - + $search: + autocomplete: + query: 'off' + path: 'title' + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + + - + name: 'Fuzzy' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#fuzzy-example' + pipeline: + - + $search: + autocomplete: + query: 'pre' + path: 'title' + fuzzy: + maxEdits: 1 + prefixLength: 1 + maxExpansions: 256 + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + + - + name: 'Token Order any' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#simple-any-example' + pipeline: + - + $search: + autocomplete: + query: 'men with' + path: 'title' + tokenOrder: 'any' + - + $limit: 4 + - + $project: + _id: 0 + title: 1 + + - + name: 'Token Order sequential' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#simple-sequential-example' + pipeline: + - + $search: + autocomplete: + query: 'men with' + path: 'title' + tokenOrder: 'sequential' + - + $limit: 4 + - + $project: + _id: 0 + title: 1 + + - + name: 'Highlighting' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#highlighting-example' + pipeline: + - + $search: + autocomplete: + query: 'ger' + path: 'title' + highlight: + path: 'title' + - + $limit: 5 + - + $project: + score: + $meta: 'searchScore' + _id: 0 + title: 1 + highlights: + $meta: 'searchHighlights' + + - + name: 'Across Multiple Fields' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#search-across-multiple-fields' + pipeline: + - + $search: + compound: + should: + - + autocomplete: + query: 'inter' + path: 'title' + - + autocomplete: + query: 'inter' + path: 'plot' + minimumShouldMatch: 1 + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + plot: 1 diff --git a/generator/config/search/compound.yaml b/generator/config/search/compound.yaml new file mode 100644 index 000000000..7a1d9f419 --- /dev/null +++ b/generator/config/search/compound.yaml @@ -0,0 +1,156 @@ +# $schema: ../schema.json +name: compound +link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/' +type: + - searchOperator +encode: object +description: | + The compound operator combines two or more operators into a single query. + Each element of a compound query is called a clause, and each clause + consists of one or more sub-queries. +arguments: + - + name: must + optional: true + type: + - searchOperator + - array # of searchOperator + - + name: mustNot + optional: true + type: + - searchOperator + - array # of searchOperator + - + name: should + optional: true + type: + - searchOperator + - array # of searchOperator + - + name: filter + optional: true + type: + - searchOperator + - array # of searchOperator + - + name: minimumShouldMatch + optional: true + type: + - int + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'must and mustNot' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#must-and-mustnot-example' + pipeline: + - + $search: + compound: + must: + - + text: + query: 'varieties' + path: 'description' + mustNot: + - + text: + query: 'apples' + path: 'description' + + - + name: 'must and should' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#must-and-should-example' + pipeline: + - + $search: + compound: + must: + - + text: + query: 'varieties' + path: 'description' + should: + - + text: + query: 'Fuji' + path: 'description' + - + $project: + score: + $meta: 'searchScore' + + - + name: 'minimumShouldMatch' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#minimumshouldmatch-example' + pipeline: + - + $search: + compound: + must: + - + text: + query: 'varieties' + path: 'description' + should: + - + text: + query: 'Fuji' + path: 'description' + - + text: + query: 'Golden Delicious' + path: 'description' + minimumShouldMatch: 1 + + - + name: 'Filter' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#filter-examples' + pipeline: + - + $search: + compound: + must: + - + text: + query: 'varieties' + path: 'description' + should: + - + text: + query: 'banana' + path: 'description' + filter: + - + text: + query: 'granny' + path: 'description' + + - + name: 'Nested' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/compound/#nested-example' + pipeline: + - + $search: + compound: + should: + - + text: + query: 'apple' + path: 'type' + - + compound: + must: + - + text: + query: 'organic' + path: 'category' + - + equals: + value: true + path: 'in_stock' + minimumShouldMatch: 1 diff --git a/generator/config/search/embeddedDocument.yaml b/generator/config/search/embeddedDocument.yaml new file mode 100644 index 000000000..19c804625 --- /dev/null +++ b/generator/config/search/embeddedDocument.yaml @@ -0,0 +1,155 @@ +# $schema: ../schema.json +name: embeddedDocument +link: 'https://www.mongodb.com/docs/atlas/atlas-search/embedded-document/' +type: + - searchOperator +encode: object +description: | + The embeddedDocument operator is similar to $elemMatch operator. + It constrains multiple query predicates to be satisfied from a single + element of an array of embedded documents. embeddedDocument can be used only + for queries over fields of the embeddedDocuments +arguments: + - + name: path + type: + - searchPath + - + name: operator + type: + - searchOperator + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Basic' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/embedded-document/#index-definition' + pipeline: + - + $search: + embeddedDocument: + path: 'items' + operator: + compound: + must: + - + text: + path: 'items.tags' + query: 'school' + should: + - + text: + path: 'items.name' + query: 'backpack' + score: + embedded: + aggregate: 'mean' + - + $limit: 5 + - + $project: + _id: 0 + items.name: 1 + items.tags: 1 + score: + $meta: 'searchScore' + + - + name: 'Facet' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/embedded-document/#facet-query' + pipeline: + - + $searchMeta: + facet: + operator: + embeddedDocument: + path: 'items' + operator: + compound: + must: + - + text: + path: 'items.tags' + query: 'school' + should: + - + text: + path: 'items.name' + query: 'backpack' + facets: + purchaseMethodFacet: + type: 'string' + path: 'purchaseMethod' + + - + name: 'Query and Sort' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/embedded-document/#query-and-sort' + pipeline: + - + $search: + embeddedDocument: + path: 'items' + operator: + text: + path: 'items.name' + query: 'laptop' + sort: + items.tags: 1 + - + $limit: 5 + - + $project: + _id: 0 + items.name: 1 + items.tags: 1 + score: + $meta: 'searchScore' + + - + name: 'Query for Matching Embedded Documents Only' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/embedded-document/#query-for-matching-embedded-documents-only' + pipeline: + - + $search: + embeddedDocument: + path: 'items' + operator: + compound: + must: + - + range: + path: 'items.quantity' + gt: 2 + - + exists: + path: 'items.price' + - + text: + path: 'items.tags' + query: 'school' + - + $limit: 2 + - + $project: + _id: 0 + storeLocation: 1 + items: + $filter: + input: '$items' + cond: + $and: + - + $ifNull: + - '$$this.price' + - 'false' + - + $gt: + - '$$this.quantity' + - 2 + - + $in: + - 'office' + - '$$this.tags' diff --git a/generator/config/search/equals.yaml b/generator/config/search/equals.yaml new file mode 100644 index 000000000..b3e50c641 --- /dev/null +++ b/generator/config/search/equals.yaml @@ -0,0 +1,104 @@ +# $schema: ../schema.json +name: equals +link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/' +type: + - searchOperator +encode: object +description: | + The equals operator checks whether a field matches a value you specify. +arguments: + - + name: path + type: + - searchPath + - + name: value + type: + - binData + - bool + - date + - objectId + - 'null' + - number + - string + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Boolean' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#boolean-examples' + pipeline: + - + $search: + equals: + path: 'verified_user' + value: true + - + $project: + name: 1 + _id: 0 + score: + $meta: 'searchScore' + + - + name: 'ObjectId' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#objectid-example' + pipeline: + - + $search: + equals: + path: 'teammates' + value: !bson_objectId '5a9427648b0beebeb69589a1' + + - + name: 'Date' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#date-example' + pipeline: + - + $search: + equals: + path: 'account_created' + value: !bson_utcdatetime '2022-05-04T05:01:08.000+00:00' + + - + name: 'Number' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#number-example' + pipeline: + - + $search: + equals: + path: 'employee_number' + value: 259 + + - + name: 'String' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#string-example' + pipeline: + - + $search: + equals: + path: 'name' + value: 'jim hall' + + - + name: 'UUID' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#uuid-example' + pipeline: + - + $search: + equals: + path: 'uuid' + value: !bson_uuid 'fac32260-b511-4c69-8485-a2be5b7dda9e' + + - + name: 'Null' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/equals/#null-example' + pipeline: + - + $search: + equals: + path: 'job_title' + value: ~ diff --git a/generator/config/search/exists.yaml b/generator/config/search/exists.yaml new file mode 100644 index 000000000..062e8ba59 --- /dev/null +++ b/generator/config/search/exists.yaml @@ -0,0 +1,56 @@ +# $schema: ../schema.json +name: exists +link: 'https://www.mongodb.com/docs/atlas/atlas-search/exists/' +type: + - searchOperator +encode: object +description: | + The exists operator tests if a path to a specified indexed field name exists in a document. +arguments: + - + name: path + type: + - searchPath + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Basic' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/exists/#basic-example' + pipeline: + - + $search: + exists: + path: 'type' + + - + name: 'Embedded' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/exists/#embedded-example' + pipeline: + - + $search: + exists: + path: 'quantities.lemons' + + - + name: 'Compound' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/exists/#compound-example' + pipeline: + - + $search: + compound: + must: + - + exists: + path: 'type' + - + text: + query: 'apple' + path: 'type' + should: + text: + query: 'fuji' + path: 'description' diff --git a/generator/config/search/facet.yaml b/generator/config/search/facet.yaml new file mode 100644 index 000000000..53dc8cba9 --- /dev/null +++ b/generator/config/search/facet.yaml @@ -0,0 +1,56 @@ +# $schema: ../schema.json +name: facet +link: 'https://www.mongodb.com/docs/atlas/atlas-search/facet/' +type: + - searchOperator # should be searchCollector +encode: object +description: | + The facet collector groups results by values or ranges in the specified + faceted fields and returns the count for each of those groups. +arguments: + - + name: facets + type: + - object # map of facetDefinition + - + name: operator + optional: true + type: + - searchOperator +tests: + - + name: 'Facet' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/facet/#examples' + pipeline: + - + $search: + facet: + operator: + near: + path: 'released' + origin: !bson_utcdatetime '1999-07-01T00:00:00.000+00:00' + pivot: 7776000000 + facets: + genresFacet: + type: 'string' + path: 'genres' + - + $limit: 2 + - + $facet: + docs: + - + $project: + title: 1 + released: 1 + meta: + - + $replaceWith: '$$SEARCH_META' + - + $limit: 1 + - + $set: + meta: + $arrayElemAt: + - '$meta' + - 0 diff --git a/generator/config/search/geoShape.yaml b/generator/config/search/geoShape.yaml new file mode 100644 index 000000000..4da121e45 --- /dev/null +++ b/generator/config/search/geoShape.yaml @@ -0,0 +1,123 @@ +# $schema: ../schema.json +name: geoShape +link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoShape/' +type: + - searchOperator +encode: object +description: | + The geoShape operator supports querying shapes with a relation to a given + geometry if indexShapes is set to true in the index definition. +arguments: + - + name: path + type: + - searchPath + - + name: relation + type: + - string # contains | disjoint | intersects | within + - + name: geometry + type: + - geometry + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Disjoint' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoShape/#disjoint-example' + pipeline: + - + $search: + geoShape: + relation: 'disjoint' + geometry: + type: 'Polygon' + coordinates: + - + - [-161.323242, 22.512557] + - [-152.446289, 22.065278] + - [-156.09375, 17.811456] + - [-161.323242, 22.512557] + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + score: + $meta: 'searchScore' + + - + name: 'Intersect' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoShape/#intersects-example' + pipeline: + - + $search: + geoShape: + relation: 'intersects' + geometry: + type: 'MultiPolygon' + coordinates: + - + - + - [2.16942, 41.40082] + - [2.17963, 41.40087] + - [2.18146, 41.39716] + - [2.15533, 41.40686] + - [2.14596, 41.38475] + - [2.17519, 41.41035] + - [2.16942, 41.40082] + - + - + - [2.16365, 41.39416] + - [2.16963, 41.39726] + - [2.15395, 41.38005] + - [2.17935, 41.43038] + - [2.16365, 41.39416] + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + score: + $meta: 'searchScore' + + - + name: 'Within' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoShape/#within-example' + pipeline: + - + $search: + geoShape: + relation: 'within' + geometry: + type: 'Polygon' + coordinates: + - + - [-74.3994140625, 40.5305017757] + - [-74.7290039063, 40.5805846641] + - [-74.7729492188, 40.9467136651] + - [-74.0698242188, 41.1290213475] + - [-73.65234375, 40.9964840144] + - [-72.6416015625, 40.9467136651] + - [-72.3559570313, 40.7971774152] + - [-74.3994140625, 40.5305017757] + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + score: + $meta: 'searchScore' diff --git a/generator/config/search/geoWithin.yaml b/generator/config/search/geoWithin.yaml new file mode 100644 index 000000000..1739f1997 --- /dev/null +++ b/generator/config/search/geoWithin.yaml @@ -0,0 +1,103 @@ +# $schema: ../schema.json +name: geoWithin +link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoWithin/' +type: + - searchOperator +encode: object +description: | + The geoWithin operator supports querying geographic points within a given + geometry. Only points are returned, even if indexShapes value is true in + the index definition. +arguments: + - + name: path + type: + - searchPath + - + name: box + optional: true + type: + - object + - + name: circle + optional: true + type: + - object + - + name: geometry + optional: true + type: + - geometry + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'box' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoWithin/#box-example' + pipeline: + - + $search: + geoWithin: + path: 'address.location' + box: + bottomLeft: + type: 'Point' + coordinates: [112.467, -55.05] + topRight: + type: 'Point' + coordinates: [168, -9.133] + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + + - + name: 'circle' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoWithin/#circle-example' + pipeline: + - + $search: + geoWithin: + circle: + center: + type: 'Point' + coordinates: [-73.54, 45.54] + radius: 1600 + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + + - + name: 'geometry' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/geoWithin/#geometry-examples' + pipeline: + - + $search: + geoWithin: + geometry: + type: 'Polygon' + coordinates: + - + - [-161.323242, 22.512557] + - [-152.446289, 22.065278] + - [-156.09375, 17.811456] + - [-161.323242, 22.512557] + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 diff --git a/generator/config/search/in.yaml b/generator/config/search/in.yaml new file mode 100644 index 000000000..cc1aa6c33 --- /dev/null +++ b/generator/config/search/in.yaml @@ -0,0 +1,89 @@ +# $schema: ../schema.json +name: in +link: 'https://www.mongodb.com/docs/atlas/atlas-search/in/' +type: + - searchOperator +encode: object +description: | + The in operator performs a search for an array of BSON values in a field. +arguments: + - + name: path + type: + - searchPath + - + name: value + type: + - any + - array # of any + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Single Value Field Match' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/in/#examples' + pipeline: + - + $search: + in: + path: 'birthdate' + value: + - !bson_utcdatetime '1977-03-02T02:20:31.000+00:00' + - !bson_utcdatetime '1977-03-01T00:00:00.000+00:00' + - !bson_utcdatetime '1977-05-06T21:57:35.000+00:00' + - + $project: + _id: 0 + name: 1 + birthdate: 1 + + - + name: 'Array Value Field Match' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/in/#examples' + pipeline: + - + $search: + in: + path: 'accounts' + value: + - 371138 + - 371139 + - 371140 + - + $project: + _id: 0 + name: 1 + accounts: 1 + + - + name: 'Compound Query Match' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/in/#examples' + pipeline: + - + $search: + compound: + must: + - + in: + path: 'name' + value: + - 'james sanchez' + - 'jennifer lawrence' + should: + - + in: + path: '_id' + value: + - !bson_objectId '5ca4bbcea2dd94ee58162a72' + - !bson_objectId '5ca4bbcea2dd94ee58162a91' + - + $limit: 5 + - + $project: + _id: 1 + name: 1 + score: + $meta: 'searchScore' diff --git a/generator/config/search/moreLikeThis.yaml b/generator/config/search/moreLikeThis.yaml new file mode 100644 index 000000000..8c4803bdd --- /dev/null +++ b/generator/config/search/moreLikeThis.yaml @@ -0,0 +1,99 @@ +# $schema: ../schema.json +name: moreLikeThis +link: 'https://www.mongodb.com/docs/atlas/atlas-search/moreLikeThis/' +type: + - searchOperator +encode: object +description: | + The moreLikeThis operator returns documents similar to input documents. + The moreLikeThis operator allows you to build features for your applications + that display similar or alternative results based on one or more given documents. +arguments: + - + name: like + type: + - object + - array # of object + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Single Document with Multiple Fields' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/#example-1--single-document-with-multiple-fields' + pipeline: + - + $search: + moreLikeThis: + like: + title: 'The Godfather' + genres: 'action' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + released: 1 + genres: 1 + + - + name: 'Input Document Excluded in Results' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/#example-2--input-document-excluded-in-results' + pipeline: + - + $search: + compound: + must: + - + moreLikeThis: + like: + _id: !bson_objectId '573a1396f29313caabce4a9a' + genres: + - 'Crime' + - 'Drama' + title: 'The Godfather' + mustNot: + - + equals: + path: '_id' + value: !bson_objectId '573a1396f29313caabce4a9a' + - + $limit: 5 + - + $project: + _id: 1 + title: 1 + released: 1 + genres: 1 + + - + name: 'Multiple Analyzers' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/morelikethis/#example-3--multiple-analyzers' + pipeline: + - + $search: + compound: + should: + - + moreLikeThis: + like: + _id: !bson_objectId '573a1396f29313caabce4a9a' + genres: + - 'Crime' + - 'Drama' + title: 'The Godfather' + mustNot: + - + equals: + path: '_id' + value: !bson_objectId '573a1394f29313caabcde9ef' + - + $limit: 10 + - + $project: + title: 1 + genres: 1 + _id: 1 diff --git a/generator/config/search/near.yaml b/generator/config/search/near.yaml new file mode 100644 index 000000000..bd4119cf9 --- /dev/null +++ b/generator/config/search/near.yaml @@ -0,0 +1,124 @@ +# $schema: ../schema.json +name: near +link: 'https://www.mongodb.com/docs/atlas/atlas-search/near/' +type: + - searchOperator +encode: object +description: | + The near operator supports querying and scoring numeric, date, and GeoJSON point values. +arguments: + - + name: path + type: + - searchPath + - + name: origin + type: + - date + - number + - geometry + - + name: pivot + type: + - number + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Number' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/near/#number-example' + pipeline: + - + $search: + index: 'runtimes' + near: + path: 'runtime' + origin: 279 + pivot: 2 + - + $limit: 7 + - + $project: + _id: 0 + title: 1 + runtime: 1 + score: + $meta: 'searchScore' + + - + name: 'Date' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/near/#date-example' + pipeline: + - + $search: + index: 'releaseddate' + near: + path: 'released' + origin: !bson_utcdatetime '1915-09-13T00:00:00.000+00:00' + pivot: 7776000000 + - + $limit: 3 + - + $project: + _id: 0 + title: 1 + released: 1 + score: + $meta: 'searchScore' + + - + name: 'GeoJSON Point' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/near/#geojson-point-examples' + pipeline: + - + $search: + near: + origin: + type: 'Point' + coordinates: + - -8.61308 + - 41.1413 + pivot: 1000 + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + name: 1 + address: 1 + score: + $meta: 'searchScore' + + - + name: 'Compound' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/near/#compound-example' + pipeline: + - + $search: + compound: + must: + text: + query: 'Apartment' + path: 'property_type' + should: + near: + origin: + type: 'Point' + coordinates: + - 114.15027 + - 22.28158 + pivot: 1000 + path: 'address.location' + - + $limit: 3 + - + $project: + _id: 0 + property_type: 1 + address: 1 + score: + $meta: 'searchScore' diff --git a/generator/config/search/phrase.yaml b/generator/config/search/phrase.yaml new file mode 100644 index 000000000..4d9b75c4e --- /dev/null +++ b/generator/config/search/phrase.yaml @@ -0,0 +1,109 @@ +# $schema: ../schema.json +name: phrase +link: 'https://www.mongodb.com/docs/atlas/atlas-search/phrase/' +type: + - searchOperator +encode: object +description: | + The phrase operator performs search for documents containing an ordered sequence of terms using the analyzer specified in the index configuration. +arguments: + - + name: path + type: + - searchPath + - + name: query + type: + - string + - array # of string + - + name: slop + optional: true + type: + - int + - + name: synonyms + optional: true + type: + - string + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Single Phrase' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/phrase/#single-phrase-example' + pipeline: + - + $search: + phrase: + path: 'title' + query: 'new york' + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Multiple Phrase' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/phrase/#multiple-phrases-example' + pipeline: + - + $search: + phrase: + path: 'title' + query: + - 'the man' + - 'the moon' + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Phrase Slop' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/phrase/#slop-example' + pipeline: + - + $search: + phrase: + path: 'title' + query: 'men women' + slop: 5 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Phrase Synonyms' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/phrase/#synonyms-example' + pipeline: + - + $search: + phrase: + path: 'plot' + query: 'automobile race' + slop: 5 + synonyms: 'my_synonyms' + - + $limit: 5 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'searchScore' diff --git a/generator/config/search/queryString.yaml b/generator/config/search/queryString.yaml new file mode 100644 index 000000000..8202771c9 --- /dev/null +++ b/generator/config/search/queryString.yaml @@ -0,0 +1,35 @@ +# $schema: ../schema.json +name: queryString +link: 'https://www.mongodb.com/docs/atlas/atlas-search/queryString/' +type: + - searchOperator +encode: object +description: | + +arguments: + - + name: defaultPath + type: + - searchPath + - + name: query + type: + - string + +# The various example from the doc are variations of the "query" parameter +# this is not pertinent for testing the aggregation builder, unless we create +# a queryString builder. +tests: + - + name: 'Boolean Operator Queries' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/queryString/#boolean-operator-queries' + pipeline: + - + $search: + queryString: + defaultPath: 'title' + query: 'Rocky AND (IV OR 4 OR Four)' + - + $project: + _id: 0 + title: 1 diff --git a/generator/config/search/range.yaml b/generator/config/search/range.yaml new file mode 100644 index 000000000..f42c69176 --- /dev/null +++ b/generator/config/search/range.yaml @@ -0,0 +1,139 @@ +# $schema: ../schema.json +name: range +link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/' +type: + - searchOperator +encode: object +description: | + The range operator supports querying and scoring numeric, date, and string values. + You can use this operator to find results that are within a given numeric, date, objectId, or letter (from the English alphabet) range. +arguments: + - + name: path + type: + - searchPath + - + name: gt + optional: true + type: + - date + - number + - string + - objectId + - + name: gte + optional: true + type: + - date + - number + - string + - objectId + - + name: lt + optional: true + type: + - date + - number + - string + - objectId + - + name: lte + optional: true + type: + - date + - number + - string + - objectId + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Number gte lte' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/#number-example' + pipeline: + - + $search: + range: + path: 'runtime' + gte: 2 + lte: 3 + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + runtime: 1 + + - + name: 'Number lte' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/#number-example' + pipeline: + - + $search: + range: + path: 'runtime' + lte: 2 + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + runtime: 1 + score: + $meta: 'searchScore' + + - + name: 'Date' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/#date-example' + pipeline: + - + $search: + range: + path: 'released' + gt: !bson_utcdatetime '2010-01-01T00:00:00.000Z' + lt: !bson_utcdatetime '2015-01-01T00:00:00.000Z' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + released: 1 + + - + name: 'ObjectId' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/#objectid-example' + pipeline: + - + $search: + range: + path: '_id' + gte: !bson_objectId '573a1396f29313caabce4a9a' + lte: !bson_objectId '573a1396f29313caabce4ae7' + - + $project: + _id: 1 + title: 1 + released: 1 + + - + name: 'String' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/range/#string-example' + pipeline: + - + $search: + range: + path: 'title' + gt: 'city' + lt: 'country' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 diff --git a/generator/config/search/regex.yaml b/generator/config/search/regex.yaml new file mode 100644 index 000000000..869ffabde --- /dev/null +++ b/generator/config/search/regex.yaml @@ -0,0 +1,42 @@ +# $schema: ../schema.json +name: regex +link: 'https://www.mongodb.com/docs/atlas/atlas-search/regex/' +type: + - searchOperator +encode: object +description: | + regex interprets the query field as a regular expression. + regex is a term-level operator, meaning that the query field isn't analyzed. +arguments: + - + name: path + type: + - searchPath + - + name: query + type: + - string + - + name: allowAnalyzedField + optional: true + type: + - bool + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Regex' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/regex/#examples' + pipeline: + - + $search: + regex: + path: 'title' + query: '[0-9]{2} (.){4}s' + - + $project: + _id: 0 + title: 1 diff --git a/generator/config/search/text.yaml b/generator/config/search/text.yaml new file mode 100644 index 000000000..dbd48cdd0 --- /dev/null +++ b/generator/config/search/text.yaml @@ -0,0 +1,194 @@ +# $schema: ../schema.json +name: text +link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/' +type: + - searchOperator +encode: object +description: | + The text operator performs a full-text search using the analyzer that you specify in the index configuration. + If you omit an analyzer, the text operator uses the default standard analyzer. +arguments: + - + name: path + type: + - searchPath + - + name: query + type: + - string + - + name: fuzzy + optional: true + type: + - object + - + name: matchCriteria + optional: true + type: + - string # "any" | "all" + - + name: synonyms + optional: true + type: + - string + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Basic' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#basic-example' + pipeline: + - + $search: + text: + path: 'title' + query: 'surfer' + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + - + name: 'Fuzzy Default' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#fuzzy-examples' + pipeline: + - + $search: + text: + path: 'title' + query: 'naw yark' + fuzzy: {} + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Fuzzy maxExpansions' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#fuzzy-examples' + pipeline: + - + $search: + text: + path: 'title' + query: 'naw yark' + fuzzy: + maxEdits: 1 + maxExpansions: 100 + - + $limit: 10 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Fuzzy prefixLength' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#fuzzy-examples' + pipeline: + - + $search: + text: + path: 'title' + query: 'naw yark' + fuzzy: + maxEdits: 1 + prefixLength: 2 + - + $limit: 8 + - + $project: + _id: 1 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Match any Using equivalent Mapping' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#match-any-using-equivalent-mapping' + pipeline: + - + $search: + text: + path: 'plot' + query: 'attire' + synonyms: 'my_synonyms' + matchCriteria: 'any' + - + $limit: 5 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Match any Using explicit Mapping' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#match-any-using-explicit-mapping' + pipeline: + - + $search: + text: + path: 'plot' + query: 'boat race' + synonyms: 'my_synonyms' + matchCriteria: 'any' + - + $limit: 10 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Match all Using Synonyms' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/#match-all-using-synonyms' + pipeline: + - + $search: + text: + path: 'plot' + query: 'automobile race' + matchCriteria: 'all' + synonyms: 'my_synonyms' + - + $limit: 20 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'searchScore' + + - + name: 'Wildcard Path' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/text/' + pipeline: + - + $search: + text: + path: + wildcard: '*' + query: 'surfer' + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' diff --git a/generator/config/search/wildcard.yaml b/generator/config/search/wildcard.yaml new file mode 100644 index 000000000..d17fb4803 --- /dev/null +++ b/generator/config/search/wildcard.yaml @@ -0,0 +1,60 @@ +# $schema: ../schema.json +name: wildcard +link: 'https://www.mongodb.com/docs/atlas/atlas-search/wildcard/' +type: + - searchOperator +encode: object +description: | + The wildcard operator enables queries which use special characters in the search string that can match any character. +arguments: + - + name: path + type: + - searchPath + - + name: query + type: + - string + - + name: allowAnalyzedField + optional: true + type: + - bool + - + name: score + optional: true + type: + - searchScore +tests: + - + name: 'Wildcard Path' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/wildcard/#index-definition' + pipeline: + - + $search: + wildcard: + query: 'Wom?n *' + path: + wildcard: '*' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + + - + name: 'Escape Character Example' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/wildcard/#escape-character-example' + pipeline: + - + $search: + wildcard: + query: '*\?' + path: 'title' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 diff --git a/generator/config/stage/addFields.yaml b/generator/config/stage/addFields.yaml new file mode 100644 index 000000000..e98f5de18 --- /dev/null +++ b/generator/config/stage/addFields.yaml @@ -0,0 +1,64 @@ +# $schema: ../schema.json +name: $addFields +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/' +type: + - stage +encode: single +description: | + Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields. +arguments: + - + name: expression + type: + - expression + variadic: object + description: | + Specify the name of each field to add and set its value to an aggregation expression or an empty object. +tests: + - + name: 'Using Two $addFields Stages' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/#using-two--addfields-stages' + pipeline: + - + $addFields: + totalHomework: + # The example renders a single value, but the builder generates an array for consistency + # $sum: '$homework' + $sum: ['$homework'] + totalQuiz: + # $sum: '$quiz' + $sum: ['$quiz'] + - + $addFields: + totalScore: + $add: + - '$totalHomework' + - '$totalQuiz' + - '$extraCredit' + - + name: 'Adding Fields to an Embedded Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/#adding-fields-to-an-embedded-document' + pipeline: + - + $addFields: + specs.fuel_type: 'unleaded' + - + name: 'Overwriting an existing field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/#overwriting-an-existing-field' + pipeline: + - + $addFields: + cats: 20 + - + name: 'Add Element to an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/#add-element-to-an-array' + pipeline: + - + $match: + _id: 1 + - + $addFields: + homework: + $concatArrays: + - '$homework' + - [7] diff --git a/generator/config/stage/bucket.yaml b/generator/config/stage/bucket.yaml new file mode 100644 index 000000000..0cd65feac --- /dev/null +++ b/generator/config/stage/bucket.yaml @@ -0,0 +1,101 @@ +# $schema: ../schema.json +name: $bucket +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucket/' +type: + - stage +encode: object +description: | + Categorizes incoming documents into groups, called buckets, based on a specified expression and bucket boundaries. +arguments: + - + name: groupBy + type: + - expression # mainly fieldPath + description: | + An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + Unless $bucket includes a default specification, each input document must resolve the groupBy field path or expression to a value that falls within one of the ranges specified by the boundaries. + - + name: boundaries + type: + - array # of expression + description: | + An array of values based on the groupBy expression that specify the boundaries for each bucket. Each adjacent pair of values acts as the inclusive lower boundary and the exclusive upper boundary for the bucket. You must specify at least two boundaries. + The specified values must be in ascending order and all of the same type. The exception is if the values are of mixed numeric types, such as: + - + name: default + type: + - expression + optional: true + description: | + A literal that specifies the _id of an additional bucket that contains all documents whose groupBy expression result does not fall into a bucket specified by boundaries. + If unspecified, each input document must resolve the groupBy expression to a value within one of the bucket ranges specified by boundaries or the operation throws an error. + The default value must be less than the lowest boundaries value, or greater than or equal to the highest boundaries value. + The default value can be of a different type than the entries in boundaries. + - + name: output + type: + - object # of Accumulator + optional: true + description: | + A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + If you do not specify an output document, the operation returns a count field containing the number of documents in each bucket. + If you specify an output document, only the fields specified in the document are returned; i.e. the count field is not returned unless it is explicitly included in the output document. +tests: + - + name: 'Bucket by Year and Filter by Bucket Results' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucket/#bucket-by-year-and-filter-by-bucket-results' + pipeline: + - + $bucket: + groupBy: '$year_born' + boundaries: [1840, 1850, 1860, 1870, 1880] + default: 'Other' + output: + count: + $sum: 1 + artists: + $push: + name: + $concat: + - '$first_name' + - ' ' + - '$last_name' + year_born: '$year_born' + - + $match: + count: + $gt: 3 + - + name: 'Use $bucket with $facet to Bucket by Multiple Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucket/#use--bucket-with--facet-to-bucket-by-multiple-fields' + pipeline: + - + $facet: + price: + - + $bucket: + groupBy: '$price' + boundaries: [0, 200, 400] + default: 'Other' + output: + count: + $sum: 1 + artwork: + $push: + title: '$title' + price: '$price' + averagePrice: + $avg: '$price' + year: + - + $bucket: + groupBy: '$year' + boundaries: [1890, 1910, 1920, 1940] + default: 'Unknown' + output: + count: + $sum: 1 + artwork: + $push: + title: '$title' + year: '$year' diff --git a/generator/config/stage/bucketAuto.yaml b/generator/config/stage/bucketAuto.yaml new file mode 100644 index 000000000..73775fde8 --- /dev/null +++ b/generator/config/stage/bucketAuto.yaml @@ -0,0 +1,46 @@ +# $schema: ../schema.json +name: $bucketAuto +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucketAuto/' +type: + - stage +encode: object +description: | + Categorizes incoming documents into a specific number of groups, called buckets, based on a specified expression. Bucket boundaries are automatically determined in an attempt to evenly distribute the documents into the specified number of buckets. +arguments: + - + name: groupBy + type: + - expression + description: | + An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + - + name: buckets + type: + - int + description: | + A positive 32-bit integer that specifies the number of buckets into which input documents are grouped. + - + name: output + type: + - object # of Accumulator + optional: true + description: | + A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + The default count field is not included in the output document when output is specified. Explicitly specify the count expression as part of the output document to include it. + - + name: granularity + type: + - granularity + optional: true + description: | + A string that specifies the preferred number series to use to ensure that the calculated boundary edges end on preferred round numbers or their powers of 10. + Available only if the all groupBy values are numeric and none of them are NaN. +tests: + - + name: 'Single Facet Aggregation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucketAuto/#single-facet-aggregation' + pipeline: + - + $bucketAuto: + groupBy: '$price' + buckets: 4 diff --git a/generator/config/stage/changeStream.yaml b/generator/config/stage/changeStream.yaml new file mode 100644 index 000000000..be413ef5d --- /dev/null +++ b/generator/config/stage/changeStream.yaml @@ -0,0 +1,66 @@ +# $schema: ../schema.json +name: $changeStream +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStream/' +type: + - stage +encode: object +description: | + Returns a Change Stream cursor for the collection or database. This stage can only occur once in an aggregation pipeline and it must occur as the first stage. +arguments: + - + name: allChangesForCluster + type: + - bool + optional: true + description: | + A flag indicating whether the stream should report all changes that occur on the deployment, aside from those on internal databases or collections. + - + name: fullDocument + type: + - fullDocument + optional: true + description: | + Specifies whether change notifications include a copy of the full document when modified by update operations. + - + name: fullDocumentBeforeChange + type: + - fullDocumentBeforeChange + optional: true + description: | + Valid values are "off", "whenAvailable", or "required". If set to "off", the "fullDocumentBeforeChange" field of the output document is always omitted. If set to "whenAvailable", the "fullDocumentBeforeChange" field will be populated with the pre-image of the document modified by the current change event if such a pre-image is available, and will be omitted otherwise. If set to "required", then the "fullDocumentBeforeChange" field is always populated and an exception is thrown if the pre-image is not available. + - + name: resumeAfter + type: + - int + optional: true + description: | + Specifies a resume token as the logical starting point for the change stream. Cannot be used with startAfter or startAtOperationTime fields. + - + name: showExpandedEvents + type: + - bool + optional: true + description: | + Specifies whether to include additional change events, such as such as DDL and index operations. + New in MongoDB 6.0. + - + name: startAfter + type: + - object + optional: true + description: | + Specifies a resume token as the logical starting point for the change stream. Cannot be used with resumeAfter or startAtOperationTime fields. + - + name: startAtOperationTime + type: + - timestamp + optional: true + description: | + Specifies a time as the logical starting point for the change stream. Cannot be used with resumeAfter or startAfter fields. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStream/#examples' + pipeline: + - + $changeStream: {} diff --git a/generator/config/stage/changeStreamSplitLargeEvent.yaml b/generator/config/stage/changeStreamSplitLargeEvent.yaml new file mode 100644 index 000000000..208129346 --- /dev/null +++ b/generator/config/stage/changeStreamSplitLargeEvent.yaml @@ -0,0 +1,16 @@ +# $schema: ../schema.json +name: $changeStreamSplitLargeEvent +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStreamSplitLargeEvent/' +type: + - stage +encode: object +description: | + Splits large change stream events that exceed 16 MB into smaller fragments returned in a change stream cursor. + You can only use $changeStreamSplitLargeEvent in a $changeStream pipeline and it must be the final stage in the pipeline. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStreamSplitLargeEvent/#example' + pipeline: + - + $changeStreamSplitLargeEvent: {} diff --git a/generator/config/stage/collStats.yaml b/generator/config/stage/collStats.yaml new file mode 100644 index 000000000..26cbbc470 --- /dev/null +++ b/generator/config/stage/collStats.yaml @@ -0,0 +1,59 @@ +# $schema: ../schema.json +name: $collStats +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/' +type: + - stage +encode: object +description: | + Returns statistics regarding a collection or view. +arguments: + - + name: latencyStats + type: + - object + optional: true + - + name: storageStats + type: + - object + optional: true + - + name: count + type: + - object # empty object + optional: true + - + name: queryExecStats + type: + - object # empty object + optional: true +tests: + - + name: 'latencyStats Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/#latencystats-document' + pipeline: + - + $collStats: + latencyStats: + histograms: true + - + name: 'storageStats Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/#storagestats-document' + pipeline: + - + $collStats: + storageStats: {} + - + name: 'count Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/#count-field' + pipeline: + - + $collStats: + count: {} + - + name: 'queryExecStats Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/#queryexecstats-document' + pipeline: + - + $collStats: + queryExecStats: {} diff --git a/generator/config/stage/count.yaml b/generator/config/stage/count.yaml new file mode 100644 index 000000000..a0fa3ba57 --- /dev/null +++ b/generator/config/stage/count.yaml @@ -0,0 +1,27 @@ +# $schema: ../schema.json +name: $count +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/count/' +type: + - stage +encode: single +description: | + Returns a count of the number of documents at this stage of the aggregation pipeline. + Distinct from the $count aggregation accumulator. +arguments: + - + name: field + type: + - string + description: | + Name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/count/#example' + pipeline: + - + $match: + score: + $gt: 80 + - + $count: 'passing_scores' diff --git a/generator/config/stage/currentOp.yaml b/generator/config/stage/currentOp.yaml new file mode 100644 index 000000000..024c71318 --- /dev/null +++ b/generator/config/stage/currentOp.yaml @@ -0,0 +1,59 @@ +# $schema: ../schema.json +name: $currentOp +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/currentOp/' +type: + - stage +encode: object +description: | + Returns information on active and/or dormant operations for the MongoDB deployment. To run, use the db.aggregate() method. +arguments: + - + name: allUsers + type: + - bool + optional: true + - + name: idleConnections + type: + - bool + optional: true + - + name: idleCursors + type: + - bool + optional: true + - + name: idleSessions + type: + - bool + optional: true + - + name: localOps + type: + - bool + optional: true +tests: + - + name: 'Inactive Sessions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/currentOp/#inactive-sessions' + pipeline: + - + $currentOp: + allUsers: true + idleSessions: true + - + $match: + active: false + transaction: + $exists: true + - + name: 'Sampled Queries' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/currentOp/#sampled-queries' + pipeline: + - + $currentOp: + allUsers: true + localOps: true + - + $match: + desc: 'query analyzer' diff --git a/generator/config/stage/densify.yaml b/generator/config/stage/densify.yaml new file mode 100644 index 000000000..46ccbd6dd --- /dev/null +++ b/generator/config/stage/densify.yaml @@ -0,0 +1,56 @@ +# $schema: ../schema.json +name: $densify +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/' +type: + - stage +encode: object +description: | + Creates new documents in a sequence of documents where certain values in a field are missing. +arguments: + - + name: field + type: + - string # field name + description: | + The field to densify. The values of the specified field must either be all numeric values or all dates. + Documents that do not contain the specified field continue through the pipeline unmodified. + To specify a in an embedded document or in an array, use dot notation. + - + name: partitionByFields + type: + - array # of string + optional: true + description: | + The field(s) that will be used as the partition keys. + - + name: range + type: + - range + description: | + Specification for range based densification. +tests: + - + name: 'Densify Time Series Data' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/#densify-time-series-data' + pipeline: + - + $densify: + field: 'timestamp' + range: + step: 1 + unit: 'hour' + bounds: + - !bson_utcdatetime '2021-05-18T00:00:00.000Z' + - !bson_utcdatetime '2021-05-18T08:00:00.000Z' + - + name: 'Densifiction with Partitions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/#densifiction-with-partitions' + pipeline: + - + $densify: + field: 'altitude' + partitionByFields: + - 'variety' + range: + bounds: 'full' + step: 200 diff --git a/generator/config/stage/documents.yaml b/generator/config/stage/documents.yaml new file mode 100644 index 000000000..666468da8 --- /dev/null +++ b/generator/config/stage/documents.yaml @@ -0,0 +1,53 @@ +# $schema: ../schema.json +name: $documents +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/' +type: + - stage +encode: single +description: | + Returns literal documents from input values. +arguments: + - + name: documents + type: + - resolvesToArray # of object + description: | + $documents accepts any valid expression that resolves to an array of objects. This includes: + - system variables, such as $$NOW or $$SEARCH_META + - $let expressions + - variables in scope from $lookup expressions + Expressions that do not resolve to a current document, like $myField or $$ROOT, will result in an error. +tests: + - + name: 'Test a Pipeline Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/#test-a-pipeline-stage' + pipeline: + - + $documents: + - { x: 10 } + - { x: 2 } + - { x: 5 } + - + $bucketAuto: + groupBy: '$x' + buckets: 4 + - + name: 'Use a $documents Stage in a $lookup Stage' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/#use-a--documents-stage-in-a--lookup-stage' + pipeline: + - + $match: {} + - + $lookup: + localField: 'zip' + foreignField: 'zip_id' + as: 'city_state' + pipeline: + - + $documents: + - + zip_id: 94301 + name: 'Palo Alto, CA' + - + zip_id: 10019 + name: 'New York, NY' diff --git a/generator/config/stage/facet.yaml b/generator/config/stage/facet.yaml new file mode 100644 index 000000000..013163c2a --- /dev/null +++ b/generator/config/stage/facet.yaml @@ -0,0 +1,51 @@ +# $schema: ../schema.json +name: $facet +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/' +type: + - stage +encode: single +description: | + Processes multiple aggregation pipelines within a single stage on the same set of input documents. Enables the creation of multi-faceted aggregations capable of characterizing data across multiple dimensions, or facets, in a single stage. +arguments: + - + name: facet + type: + - pipeline + variadic: object +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/#example' + pipeline: + - + $facet: + categorizedByTags: + - + # The builder uses the verbose form of the $unwind operator + # $unwind: '$tags' + $unwind: + path: '$tags' + - + $sortByCount: '$tags' + categorizedByPrice: + - + $match: + price: + # The example uses an int, but the builder requires a bool + # $exists: 1 + $exists: true + - + $bucket: + groupBy: '$price' + boundaries: [0, 150, 200, 300, 400] + default: 'Other' + output: + count: + $sum: 1 + titles: + $push: '$title' + categorizedByYears(Auto): + - + $bucketAuto: + groupBy: '$year' + buckets: 4 diff --git a/generator/config/stage/fill.yaml b/generator/config/stage/fill.yaml new file mode 100644 index 000000000..2c98fac5c --- /dev/null +++ b/generator/config/stage/fill.yaml @@ -0,0 +1,110 @@ +# $schema: ../schema.json +name: $fill +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/' +type: + - stage +encode: object +description: | + Populates null and missing field values within documents. +arguments: + - + name: partitionBy + type: + - object # of expression + - string + optional: true + description: | + Specifies an expression to group the documents. In the $fill stage, a group of documents is known as a partition. + If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + partitionBy and partitionByFields are mutually exclusive. + - + name: partitionByFields + type: + - array # of string + optional: true + description: | + Specifies an array of fields as the compound key to group the documents. In the $fill stage, each group of documents is known as a partition. + If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + partitionBy and partitionByFields are mutually exclusive. + - + name: sortBy + type: + - sortBy + optional: true + description: | + Specifies the field or fields to sort the documents within each partition. Uses the same syntax as the $sort stage. + - + name: output + type: + - object # of object{value:expression} or object{method:string}> + description: | + Specifies an object containing each field for which to fill missing values. You can specify multiple fields in the output object. + The object name is the name of the field to fill. The object value specifies how the field is filled. +tests: + - + name: 'Fill Missing Field Values with a Constant Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/#fill-missing-field-values-with-a-constant-value' + pipeline: + - + $fill: + output: + bootsSold: + value: 0 + sandalsSold: + value: 0 + sneakersSold: + value: 0 + - + name: 'Fill Missing Field Values with Linear Interpolation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/#fill-missing-field-values-with-linear-interpolation' + pipeline: + - + $fill: + sortBy: + time: 1 + output: + price: + method: 'linear' + - + name: 'Fill Missing Field Values Based on the Last Observed Value' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/#fill-missing-field-values-based-on-the-last-observed-value' + pipeline: + - + $fill: + sortBy: + date: 1 + output: + score: + method: 'locf' + - + name: 'Fill Data for Distinct Partitions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/#fill-data-for-distinct-partitions' + pipeline: + - + $fill: + sortBy: + date: 1 + partitionBy: + restaurant: '$restaurant' + output: + score: + method: 'locf' + - + name: 'Indicate if a Field was Populated Using $fill' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/#indicate-if-a-field-was-populated-using--fill' + pipeline: + - + $set: + valueExisted: + $ifNull: + - + $toBool: + $toString: '$score' + - false + - + $fill: + sortBy: + date: 1 + output: + score: + method: 'locf' diff --git a/generator/config/stage/geoNear.yaml b/generator/config/stage/geoNear.yaml new file mode 100644 index 000000000..4967a0823 --- /dev/null +++ b/generator/config/stage/geoNear.yaml @@ -0,0 +1,161 @@ +# $schema: ../schema.json +name: $geoNear +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/' +type: + - stage +encode: object +description: | + Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field. +arguments: + - + name: distanceField + type: + - string + optional: true + description: | + The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. + - + name: distanceMultiplier + type: + - number + optional: true + description: | + The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as returned by a spherical query, to kilometers by multiplying by the radius of the Earth. + - + name: includeLocs + type: + - string + optional: true + description: | + This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location field contains multiple locations. To specify a field within an embedded document, use dot notation. + - + name: key + type: + - string + optional: true + description: | + Specify the geospatial indexed field to use when calculating the distance. + - + name: maxDistance + type: + - number + optional: true + description: | + The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point. + Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs. + - + name: minDistance + type: + - number + optional: true + description: | + The minimum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall outside the specified distance from the center point. + Specify the distance in meters for GeoJSON data and in radians for legacy coordinate pairs. + - + name: near + type: + - geoPoint + - resolvesToObject + description: | + The point for which to find the closest documents. + - + name: query + type: + - query + optional: true + description: | + Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax. + You cannot specify a $near predicate in the query field of the $geoNear stage. + - + name: spherical + type: + - bool + optional: true + description: | + Determines how MongoDB calculates the distance between two points: + - When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry. + - When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes. + Default: false. +tests: + - + name: 'Maximum Distance' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/#maximum-distance' + pipeline: + - + $geoNear: + near: + type: 'Point' + coordinates: + - -73.99279 + - 40.719296 + distanceField: 'dist.calculated' + maxDistance: 2 + query: + category: 'Parks' + includeLocs: 'dist.location' + spherical: true + - + name: 'Minimum Distance' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/#minimum-distance' + pipeline: + - + $geoNear: + near: + type: 'Point' + coordinates: + - -73.99279 + - 40.719296 + distanceField: 'dist.calculated' + minDistance: 2 + query: + category: 'Parks' + includeLocs: 'dist.location' + spherical: true + - + name: 'with the let option' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/#-geonear-with-the-let-option' + pipeline: + - + $geoNear: + near: '$$pt' + distanceField: 'distance' + maxDistance: 2 + query: + category: 'Parks' + includeLocs: 'dist.location' + spherical: true + - + name: 'with Bound let Option' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/#-geonear-with-bound-let-option' + pipeline: + - + $lookup: + from: 'places' + let: + pt: '$location' + pipeline: + - + $geoNear: + near: '$$pt' + distanceField: 'distance' + as: 'joinedField' + - + $match: + name: 'Sara D. Roosevelt Park' + - + name: 'Specify Which Geospatial Index to Use' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/#specify-which-geospatial-index-to-use' + pipeline: + - + $geoNear: + near: + type: 'Point' + coordinates: + - -73.98142 + - 40.71782 + key: 'location' + distanceField: 'dist.calculated' + query: + category: 'Parks' + - + $limit: 5 diff --git a/generator/config/stage/graphLookup.yaml b/generator/config/stage/graphLookup.yaml new file mode 100644 index 000000000..ae220620b --- /dev/null +++ b/generator/config/stage/graphLookup.yaml @@ -0,0 +1,108 @@ +# $schema: ../schema.json +name: $graphLookup +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/' +type: + - stage +encode: object +description: | + Performs a recursive search on a collection. To each output document, adds a new array field that contains the traversal results of the recursive search for that document. +arguments: + - + name: from + type: + - string + description: | + Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation. + Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + - + name: startWith + type: + - expression + - array + description: | + Expression that specifies the value of the connectFromField with which to start the recursive search. Optionally, startWith may be array of values, each of which is individually followed through the traversal process. + - + name: connectFromField + type: + - string + description: | + Field name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. If the value is an array, each element is individually followed through the traversal process. + - + name: connectToField + type: + - string + description: | + Field name in other documents against which to match the value of the field specified by the connectFromField parameter. + - + name: as + type: + - string + description: | + Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document. + - + name: maxDepth + type: + - int + optional: true + description: | + Non-negative integral number specifying the maximum recursion depth. + - + name: depthField + type: + - string + optional: true + description: | + Name of the field to add to each traversed document in the search path. The value of this field is the recursion depth for the document, represented as a NumberLong. Recursion depth value starts at zero, so the first lookup corresponds to zero depth. + - + name: restrictSearchWithMatch + type: + - query + optional: true + description: | + A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax. +tests: + - + name: 'Within a Single Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/#within-a-single-collection' + pipeline: + - + $graphLookup: + from: 'employees' + startWith: '$reportsTo' + connectFromField: 'reportsTo' + connectToField: 'name' + as: 'reportingHierarchy' + - + name: 'Across Multiple Collections' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/#across-multiple-collections' + pipeline: + - + $graphLookup: + from: 'airports' + startWith: '$nearestAirport' + connectFromField: 'connects' + connectToField: 'airport' + maxDepth: 2 + depthField: 'numConnections' + as: 'destinations' + - + name: 'With a Query Filter' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/#with-a-query-filter' + pipeline: + - + $match: + name: 'Tanya Jordan' + - + $graphLookup: + from: 'people' + startWith: '$friends' + connectFromField: 'friends' + connectToField: 'name' + as: 'golfers' + restrictSearchWithMatch: + hobbies: 'golf' + - + $project: + name: 1 + friends: 1 + connections who play golf: '$golfers.name' diff --git a/generator/config/stage/group.yaml b/generator/config/stage/group.yaml new file mode 100644 index 000000000..a4bae60c2 --- /dev/null +++ b/generator/config/stage/group.yaml @@ -0,0 +1,123 @@ +# $schema: ../schema.json +name: $group +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/' +type: + - stage +encode: object +description: | + Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields. +arguments: + - + name: _id + type: + - expression + description: | + The _id expression specifies the group key. If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents. + - + name: field + mergeObject: true + type: + - accumulator + variadic: object + variadicMin: 0 + description: | + Computed using the accumulator operators. +tests: + - + name: 'Count the Number of Documents in a Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#count-the-number-of-documents-in-a-collection' + pipeline: + - + $group: + _id: ~ + count: + $count: {} + - + name: 'Retrieve Distinct Values' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#retrieve-distinct-values' + pipeline: + - + $group: + _id: '$item' + - + name: 'Group by Item Having' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#group-by-item-having' + pipeline: + - + $group: + _id: '$item' + totalSaleAmount: + $sum: + $multiply: + - '$price' + - '$quantity' + - + $match: + totalSaleAmount: + $gte: 100 + - + name: 'Calculate Count Sum and Average' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#calculate-count--sum--and-average' + pipeline: + - + $match: + date: + $gte: !bson_utcdatetime '2014-01-01' + $lt: !bson_utcdatetime '2015-01-01' + - + $group: + _id: + $dateToString: + format: '%Y-%m-%d' + date: '$date' + totalSaleAmount: + $sum: + $multiply: + - '$price' + - '$quantity' + averageQuantity: + $avg: '$quantity' + count: + $sum: 1 + - + $sort: + totalSaleAmount: -1 + - + name: 'Group by null' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#group-by-null' + pipeline: + - + $group: + _id: ~ + totalSaleAmount: + $sum: + $multiply: + - '$price' + - '$quantity' + averageQuantity: + $avg: '$quantity' + count: + $sum: 1 + - + name: 'Pivot Data' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#pivot-data' + pipeline: + - + $group: + _id: '$author' + books: + $push: '$title' + - + name: 'Group Documents by author' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/#group-documents-by-author' + pipeline: + - + $group: + _id: '$author' + books: + $push: '$$ROOT' + - + $addFields: + totalCopies: + # $sum: '$books.copies' + $sum: ['$books.copies'] diff --git a/generator/config/stage/indexStats.yaml b/generator/config/stage/indexStats.yaml new file mode 100644 index 000000000..178b209d8 --- /dev/null +++ b/generator/config/stage/indexStats.yaml @@ -0,0 +1,15 @@ +# $schema: ../schema.json +name: $indexStats +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexStats/' +type: + - stage +encode: object +description: | + Returns statistics regarding the use of each index for the collection. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexStats/#example' + pipeline: + - + $indexStats: {} diff --git a/generator/config/stage/limit.yaml b/generator/config/stage/limit.yaml new file mode 100644 index 000000000..fff391a01 --- /dev/null +++ b/generator/config/stage/limit.yaml @@ -0,0 +1,20 @@ +# $schema: ../schema.json +name: $limit +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/' +type: + - stage +encode: single +description: | + Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents). +arguments: + - + name: limit + type: + - int +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/#example' + pipeline: + - + $limit: 5 diff --git a/generator/config/stage/listLocalSessions.yaml b/generator/config/stage/listLocalSessions.yaml new file mode 100644 index 000000000..50dccc30e --- /dev/null +++ b/generator/config/stage/listLocalSessions.yaml @@ -0,0 +1,47 @@ +# $schema: ../schema.json +name: $listLocalSessions +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/' +type: + - stage +encode: object +description: | + Lists all active sessions recently in use on the currently connected mongos or mongod instance. These sessions may have not yet propagated to the system.sessions collection. +arguments: + - + name: users + type: + - array + optional: true + description: | + Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + - + name: allUsers + type: + - bool + optional: true + description: | + Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. +tests: + - + name: 'List All Local Sessions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/#list-all-local-sessions' + pipeline: + - + $listLocalSessions: + allUsers: true + - + name: 'List All Local Sessions for the Specified Users' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/#list-all-local-sessions-for-the-specified-users' + pipeline: + - + $listLocalSessions: + users: + - + user: 'myAppReader' + db: 'test' + - + name: 'List All Local Sessions for the Current User' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/#list-all-local-sessions-for-the-current-user' + pipeline: + - + $listLocalSessions: {} diff --git a/generator/config/stage/listSampledQueries.yaml b/generator/config/stage/listSampledQueries.yaml new file mode 100644 index 000000000..f767f0d04 --- /dev/null +++ b/generator/config/stage/listSampledQueries.yaml @@ -0,0 +1,29 @@ +# $schema: ../schema.json +name: $listSampledQueries +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSampledQueries/' +type: + - stage +encode: object +description: | + Lists sampled queries for all collections or a specific collection. +arguments: + - + name: namespace + type: + - string + optional: true +tests: + - + name: 'List Sampled Queries for All Collections' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSampledQueries/#list-sampled-queries-for-all-collections' + pipeline: + - + $listSampledQueries: {} + + - + name: 'List Sampled Queries for A Specific Collection' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSampledQueries/#list-sampled-queries-for-a-specific-collection' + pipeline: + - + $listSampledQueries: + namespace: 'social.post' diff --git a/generator/config/stage/listSearchIndexes.yaml b/generator/config/stage/listSearchIndexes.yaml new file mode 100644 index 000000000..afc4f6d05 --- /dev/null +++ b/generator/config/stage/listSearchIndexes.yaml @@ -0,0 +1,44 @@ +# $schema: ../schema.json +name: $listSearchIndexes +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/' +type: + - stage +encode: object +description: | + Returns information about existing Atlas Search indexes on a specified collection. +arguments: + - + name: id + type: + - string + optional: true + description: | + The id of the index to return information about. + - + name: name + type: + - string + optional: true + description: | + The name of the index to return information about. +tests: + - + name: 'Return All Search Indexes' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/#return-all-search-indexes' + pipeline: + - + $listSearchIndexes: {} + - + name: 'Return a Single Search Index by Name' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/#return-a-single-search-index-by-name' + pipeline: + - + $listSearchIndexes: + name: 'synonym-mappings' + - + name: 'Return a Single Search Index by id' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/#return-a-single-search-index-by-id' + pipeline: + - + $listSearchIndexes: + id: '6524096020da840844a4c4a7' diff --git a/generator/config/stage/listSessions.yaml b/generator/config/stage/listSessions.yaml new file mode 100644 index 000000000..efb56de05 --- /dev/null +++ b/generator/config/stage/listSessions.yaml @@ -0,0 +1,48 @@ +# $schema: ../schema.json +name: $listSessions +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/' +type: + - stage +encode: object +description: | + Lists all sessions that have been active long enough to propagate to the system.sessions collection. +arguments: + - + name: users + type: + - array + optional: true + description: | + Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + - + name: allUsers + type: + - bool + optional: true + description: | + Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. +tests: + - + name: 'List All Sessions' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/#list-all-sessions' + pipeline: + - + $listSessions: + allUsers: true + - + name: 'List All Sessions for the Specified Users' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/#list-all-sessions-for-the-specified-users' + pipeline: + - + $listSessions: + users: + - + user: 'myAppReader' + db: 'test' + - + name: 'List All Sessions for the Current User' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/#list-all-sessions-for-the-current-user' + pipeline: + - + $listSessions: {} + diff --git a/generator/config/stage/lookup.yaml b/generator/config/stage/lookup.yaml new file mode 100644 index 000000000..b73770e47 --- /dev/null +++ b/generator/config/stage/lookup.yaml @@ -0,0 +1,165 @@ +# $schema: ../schema.json +name: $lookup +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/' +type: + - stage +encode: object +description: | + Performs a left outer join to another collection in the same database to filter in documents from the "joined" collection for processing. +arguments: + - + name: from + type: + - string + optional: true + description: | + Specifies the collection in the same database to perform the join with. + from is optional, you can use a $documents stage in a $lookup stage instead. For an example, see Use a $documents Stage in a $lookup Stage. + Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + - + name: localField + type: + - string + optional: true + description: | + Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes. + - + name: foreignField + type: + - string + optional: true + description: | + Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes. + - + name: let + type: + - object # of expression + optional: true + description: | + Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline. + - + name: pipeline + type: + - pipeline + optional: true + description: | + Specifies the pipeline to run on the joined collection. The pipeline determines the resulting documents from the joined collection. To return all documents, specify an empty pipeline []. + The pipeline cannot include the $out stage or the $merge stage. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + The pipeline cannot directly access the joined document fields. Instead, define variables for the joined document fields using the let option and then reference the variables in the pipeline stages. + - + name: as + type: + - string + description: | + Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten. +tests: + - + name: 'Perform a Single Equality Join with $lookup' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#perform-a-single-equality-join-with--lookup' + pipeline: + - + $lookup: + from: 'inventory' + localField: 'item' + foreignField: 'sku' + as: 'inventory_docs' + - + name: 'Use $lookup with an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#use--lookup-with-an-array' + pipeline: + - + $lookup: + from: 'members' + localField: 'enrollmentlist' + foreignField: 'name' + as: 'enrollee_info' + - + name: 'Use $lookup with $mergeObjects' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#use--lookup-with--mergeobjects' + pipeline: + - + $lookup: + from: 'items' + localField: 'item' + foreignField: 'item' + as: 'fromItems' + - + $replaceRoot: + newRoot: + $mergeObjects: + - + $arrayElemAt: + - '$fromItems' + - 0 + - '$$ROOT' + - + $project: + fromItems: 0 + - + name: 'Perform Multiple Joins and a Correlated Subquery with $lookup' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#perform-multiple-joins-and-a-correlated-subquery-with--lookup' + pipeline: + - + $lookup: + from: 'warehouses' + let: + order_item: '$item' + order_qty: '$ordered' + pipeline: + - + $match: + $expr: + $and: + - + $eq: + - '$stock_item' + - '$$order_item' + - + $gte: + - '$instock' + - '$$order_qty' + - + $project: + stock_item: 0 + _id: 0 + as: 'stockdata' + - + name: 'Perform an Uncorrelated Subquery with $lookup' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#perform-an-uncorrelated-subquery-with--lookup' + pipeline: + - + $lookup: + from: 'holidays' + pipeline: + - + $match: + year: 2018 + - + $project: + _id: 0 + date: + name: '$name' + date: '$date' + - + $replaceRoot: + newRoot: '$date' + as: 'holidays' + - + name: 'Perform a Concise Correlated Subquery with $lookup' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/#perform-a-concise-correlated-subquery-with--lookup' + pipeline: + - + $lookup: + from: 'restaurants' + localField: 'restaurant_name' + foreignField: 'name' + let: + orders_drink: '$drink' + pipeline: + - + $match: + $expr: + $in: + - '$$orders_drink' + - '$beverages' + as: 'matches' diff --git a/generator/config/stage/match.yaml b/generator/config/stage/match.yaml new file mode 100644 index 000000000..ab0081fd0 --- /dev/null +++ b/generator/config/stage/match.yaml @@ -0,0 +1,40 @@ +# $schema: ../schema.json +name: $match +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/' +type: + - stage +encode: single +description: | + Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match). +arguments: + - + name: query + type: + - query +tests: + - + name: 'Equality Match' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/#equality-match' + pipeline: + - + $match: + author: 'dave' + - + name: 'Perform a Count' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/#perform-a-count' + pipeline: + - + $match: + $or: + - + score: + $gt: 70 + $lt: 90 + - + views: + $gte: 1000 + - + $group: + _id: ~ + count: + $sum: 1 diff --git a/generator/config/stage/merge.yaml b/generator/config/stage/merge.yaml new file mode 100644 index 000000000..766092d82 --- /dev/null +++ b/generator/config/stage/merge.yaml @@ -0,0 +1,180 @@ +# $schema: ../schema.json +name: $merge +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/' +type: + - stage +encode: object +description: | + Writes the resulting documents of the aggregation pipeline to a collection. The stage can incorporate (insert new documents, merge documents, replace documents, keep existing documents, fail the operation, process documents with a custom update pipeline) the results into an output collection. To use the $merge stage, it must be the last stage in the pipeline. + New in MongoDB 4.2. +arguments: + - + name: into + type: + - string + - outCollection + description: | + The output collection. + - + name: 'on' + type: + - string + - array # of string + optional: true + description: | + Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. + - + name: let + type: + - object + optional: true + description: | + Specifies variables for use in the whenMatched pipeline. + - + name: whenMatched + type: + - whenMatched + - pipeline + optional: true + description: | + The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s). + - + name: whenNotMatched + type: + - whenNotMatched + optional: true + description: | + The behavior of $merge if a result document does not match an existing document in the out collection. +tests: + - + name: 'On-Demand Materialized View Initial Creation' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#on-demand-materialized-view--initial-creation' + pipeline: + - + $group: + _id: + fiscal_year: '$fiscal_year' + dept: '$dept' + salaries: + $sum: '$salary' + - + $merge: + into: + db: 'reporting' + coll: 'budgets' + on: '_id' + whenMatched: 'replace' + whenNotMatched: 'insert' + - + name: 'On-Demand Materialized View Update Replace Data' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#on-demand-materialized-view--update-replace-data' + pipeline: + - + $match: + fiscal_year: + $gte: 2019 + - + $group: + _id: + fiscal_year: '$fiscal_year' + dept: '$dept' + salaries: + $sum: '$salary' + - + $merge: + into: + db: 'reporting' + coll: 'budgets' + on: '_id' + whenMatched: 'replace' + whenNotMatched: 'insert' + - + name: 'Only Insert New Data' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#only-insert-new-data' + pipeline: + - + $match: + fiscal_year: 2019 + - + $group: + _id: + fiscal_year: '$fiscal_year' + dept: '$dept' + employees: + $push: '$employee' + - + $project: + _id: 0 + dept: '$_id.dept' + fiscal_year: '$_id.fiscal_year' + employees: 1 + - + $merge: + into: + db: 'reporting' + coll: 'orgArchive' + on: + - 'dept' + - 'fiscal_year' + whenMatched: 'fail' + - + name: 'Merge Results from Multiple Collections' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#merge-results-from-multiple-collections' + pipeline: + - + $group: + _id: '$quarter' + purchased: + $sum: '$qty' + - + $merge: + into: 'quarterlyreport' + on: '_id' + whenMatched: 'merge' + whenNotMatched: 'insert' + - + name: 'Use the Pipeline to Customize the Merge' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#use-the-pipeline-to-customize-the-merge' + pipeline: + - + $match: + date: + $gte: !bson_utcdatetime '2019-05-07' + $lt: !bson_utcdatetime '2019-05-08' + - + $project: + _id: + $dateToString: + format: '%Y-%m' + date: '$date' + thumbsup: 1 + thumbsdown: 1 + - + $merge: + into: 'monthlytotals' + on: '_id' + whenMatched: + - + $addFields: + thumbsup: + $add: + - '$thumbsup' + - '$$new.thumbsup' + thumbsdown: + $add: + - '$thumbsdown' + - '$$new.thumbsdown' + whenNotMatched: 'insert' + - + name: 'Use Variables to Customize the Merge' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/#use-variables-to-customize-the-merge' + pipeline: + - + $merge: + into: 'cakeSales' + let: + year: '2020' + whenMatched: + - + $addFields: + salesYear: '$$year' diff --git a/generator/config/stage/out.yaml b/generator/config/stage/out.yaml new file mode 100644 index 000000000..0c3597fdf --- /dev/null +++ b/generator/config/stage/out.yaml @@ -0,0 +1,40 @@ +# $schema: ../schema.json +name: $out +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/' +type: + - stage +encode: single +description: | + Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline. +arguments: + - name: coll + type: + - string + - outCollection + description: | + Target database name to write documents from $out to. +tests: + - + name: 'Output to Same Database' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/#output-to-same-database' + pipeline: + - + $group: + _id: '$author' + books: + $push: '$title' + - + $out: 'authors' + - + name: 'Output to a Different Database' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/#output-to-a-different-database' + pipeline: + - + $group: + _id: '$author' + books: + $push: '$title' + - + $out: + db: 'reporting' + coll: 'authors' diff --git a/generator/config/stage/planCacheStats.yaml b/generator/config/stage/planCacheStats.yaml new file mode 100644 index 000000000..995caa74e --- /dev/null +++ b/generator/config/stage/planCacheStats.yaml @@ -0,0 +1,24 @@ +# $schema: ../schema.json +name: $planCacheStats +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/' +type: + - stage +encode: object +description: | + Returns plan cache information for a collection. +tests: + - + name: 'Return Information for All Entries in the Query Cache' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/#return-information-for-all-entries-in-the-query-cache' + pipeline: + - + $planCacheStats: {} + - + name: 'Find Cache Entry Details for a Query Hash' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/#find-cache-entry-details-for-a-query-hash' + pipeline: + - + $planCacheStats: {} + - + $match: + planCacheKey: 'B1435201' diff --git a/generator/config/stage/project.yaml b/generator/config/stage/project.yaml new file mode 100644 index 000000000..c7b0f7d59 --- /dev/null +++ b/generator/config/stage/project.yaml @@ -0,0 +1,124 @@ +# $schema: ../schema.json +name: $project +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/' +type: + - stage +encode: single +description: | + Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document. +arguments: + - + name: specification + type: + - expression + variadic: object +tests: + - + name: 'Include Specific Fields in Output Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#include-specific-fields-in-output-documents' + pipeline: + - + $project: + title: 1 + author: 1 + - + name: 'Suppress id Field in the Output Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#suppress-_id-field-in-the-output-documents' + pipeline: + - + $project: + _id: 0 + title: 1 + author: 1 + - + name: 'Exclude Fields from Output Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#exclude-fields-from-output-documents' + pipeline: + - + $project: + lastModified: 0 + - + name: 'Exclude Fields from Embedded Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#exclude-fields-from-embedded-documents' + pipeline: + - + $project: + author.first: 0 + lastModified: 0 + - + $project: + author: + first: 0 + lastModified: 0 + - + name: 'Conditionally Exclude Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#conditionally-exclude-fields' + pipeline: + - + $project: + title: 1 + author.first: 1 + author.last: 1 + author.middle: + $cond: + if: + $eq: + - '' + - '$author.middle' + then: '$$REMOVE' + else: '$author.middle' + - + name: 'Include Specific Fields from Embedded Documents' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#include-specific-fields-from-embedded-documents' + pipeline: + - + $project: + stop.title: 1 + - + $project: + stop: + title: 1 + - + name: 'Include Computed Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#include-computed-fields' + pipeline: + - + $project: + title: 1 + isbn: + prefix: + $substr: + - '$isbn' + - 0 + - 3 + group: + $substr: + - '$isbn' + - 3 + - 2 + publisher: + $substr: + - '$isbn' + - 5 + - 4 + title: + $substr: + - '$isbn' + - 9 + - 3 + checkDigit: + $substr: + - '$isbn' + - 12 + - 1 + lastName: '$author.last' + copiesSold: '$copies' + - + name: 'Project New Array Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#project-new-array-fields' + pipeline: + - + $project: + myArray: + - '$x' + - '$y' diff --git a/generator/config/stage/redact.yaml b/generator/config/stage/redact.yaml new file mode 100644 index 000000000..07698119c --- /dev/null +++ b/generator/config/stage/redact.yaml @@ -0,0 +1,52 @@ +# $schema: ../schema.json +name: $redact +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/' +type: + - stage +encode: single +description: | + Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero documents. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Evaluate Access at Every Document Level' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#evaluate-access-at-every-document-level' + pipeline: + - + $match: + year: 2014 + - + $redact: + $cond: + if: + $gt: + - + $size: + $setIntersection: + - '$tags' + - + - 'STLW' + - 'G' + - 0 + then: '$$DESCEND' + else: '$$PRUNE' + - + name: 'Exclude All Fields at a Given Level' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#exclude-all-fields-at-a-given-level' + pipeline: + - + $match: + status: 'A' + - + $redact: + $cond: + if: + $eq: + - '$level' + - 5 + then: '$$PRUNE' + else: '$$DESCEND' diff --git a/generator/config/stage/replaceRoot.yaml b/generator/config/stage/replaceRoot.yaml new file mode 100644 index 000000000..4de474e00 --- /dev/null +++ b/generator/config/stage/replaceRoot.yaml @@ -0,0 +1,71 @@ +# $schema: ../schema.json +name: $replaceRoot +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/' +type: + - stage +encode: object +description: | + Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. +arguments: + - + name: newRoot + type: + - resolvesToObject +tests: + - + name: 'with an Embedded Document Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/#-replaceroot-with-an-embedded-document-field' + pipeline: + - + $replaceRoot: + newRoot: + $mergeObjects: + - + dogs: 0 + cats: 0 + birds: 0 + fish: 0 + - '$pets' + - + name: 'with a Document Nested in an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/#-replaceroot-with-a-document-nested-in-an-array' + pipeline: + - + # The builder uses the verbose form of the $unwind operator + # $unwind: '$grades' + $unwind: + path: '$grades' + - + $match: + grades.grade: + $gte: 90 + - + $replaceRoot: + newRoot: '$grades' + - + name: 'with a newly created document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/#-replaceroot-with-a-newly-created-document' + pipeline: + - + $replaceRoot: + newRoot: + full_name: + $concat: + - '$first_name' + - ' ' + - '$last_name' + - + name: 'with a New Document Created from $$ROOT and a Default Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/#-replaceroot-with-a-new-document-created-from---root-and-a-default-document' + pipeline: + - + $replaceRoot: + newRoot: + $mergeObjects: + - + _id: '' + name: '' + email: '' + cell: '' + home: '' + - '$$ROOT' diff --git a/generator/config/stage/replaceWith.yaml b/generator/config/stage/replaceWith.yaml new file mode 100644 index 000000000..10c5fa3a2 --- /dev/null +++ b/generator/config/stage/replaceWith.yaml @@ -0,0 +1,74 @@ +# $schema: ../schema.json +name: $replaceWith +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/' +type: + - stage +encode: single +description: | + Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. + Alias for $replaceRoot. +arguments: + - + name: expression + type: + - resolvesToObject +tests: + - + name: 'an Embedded Document Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/#-replacewith-an-embedded-document-field' + pipeline: + - + $replaceWith: + $mergeObjects: + - + dogs: 0 + cats: 0 + birds: 0 + fish: 0 + - '$pets' + - + name: 'a Document Nested in an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/#-replacewith-a-document-nested-in-an-array' + pipeline: + - + # The builder uses the verbose form of the $unwind operator + # $unwind: '$grades' + $unwind: + path: '$grades' + - + $match: + grades.grade: + $gte: 90 + - + $replaceWith: '$grades' + - + name: 'a Newly Created Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/#-replacewith-a-newly-created-document' + pipeline: + - + $match: + status: 'C' + - + $replaceWith: + _id: '$_id' + item: '$item' + amount: + $multiply: + - '$price' + - '$quantity' + status: 'Complete' + asofDate: '$$NOW' + - + name: 'a New Document Created from $$ROOT and a Default Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/#-replacewith-a-new-document-created-from---root-and-a-default-document' + pipeline: + - + $replaceWith: + $mergeObjects: + - + _id: '' + name: '' + email: '' + cell: '' + home: '' + - '$$ROOT' diff --git a/generator/config/stage/sample.yaml b/generator/config/stage/sample.yaml new file mode 100644 index 000000000..757382aaf --- /dev/null +++ b/generator/config/stage/sample.yaml @@ -0,0 +1,23 @@ +# $schema: ../schema.json +name: $sample +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/' +type: + - stage +encode: object +description: | + Randomly selects the specified number of documents from its input. +arguments: + - + name: size + type: + - int + description: | + The number of documents to randomly select. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/#example' + pipeline: + - + $sample: + size: 3 diff --git a/generator/config/stage/search.yaml b/generator/config/stage/search.yaml new file mode 100644 index 000000000..44756ce23 --- /dev/null +++ b/generator/config/stage/search.yaml @@ -0,0 +1,265 @@ +# $schema: ../schema.json +name: $search +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/search/' +type: + - stage +encode: object +description: | + Performs a full-text search of the field or fields in an Atlas collection. + NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments. +arguments: + - + name: operator + mergeObject: true + type: + - searchOperator + description: | + Operator to search with. You can provide a specific operator or use + the compound operator to run a compound query with multiple operators. + - + name: index + optional: true + type: + - string + description: | + Name of the Atlas Search index to use. If omitted, defaults to "default". + - + name: highlight + optional: true + type: + # @todo support "highlight" type object + # https://www.mongodb.com/docs/atlas/atlas-search/highlighting/ + - object + description: | + Specifies the highlighting options for displaying search terms in their original context. + - + name: concurrent + optional: true + type: + - bool + description: | + Parallelize search across segments on dedicated search nodes. + If you don't have separate search nodes on your cluster, + Atlas Search ignores this flag. If omitted, defaults to false. + - + name: count + optional: true + type: + - object + description: | + Document that specifies the count options for retrieving a count of the results. + - + name: searchAfter + optional: true + type: + - string + description: | + Reference point for retrieving results. searchAfter returns documents starting immediately following the specified reference point. + - + name: searchBefore + optional: true + type: + - string + description: | + Reference point for retrieving results. searchBefore returns documents starting immediately before the specified reference point. + - + name: scoreDetails + optional: true + type: + - bool + description: | + Flag that specifies whether to retrieve a detailed breakdown of the score for the documents in the results. If omitted, defaults to false. + - + name: sort + optional: true + type: + - object + description: | + Document that specifies the fields to sort the Atlas Search results by in ascending or descending order. + - + name: returnStoredSource + optional: true + type: + - bool + description: | + Flag that specifies whether to perform a full document lookup on the backend database or return only stored source fields directly from Atlas Search. + - + name: tracking + optional: true + type: + - object + description: | + Document that specifies the tracking option to retrieve analytics information on the search terms. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#aggregation-variable' + pipeline: + - + $search: + near: + path: 'released' + origin: !bson_utcdatetime '2011-09-01T00:00:00.000+00:00' + pivot: 7776000000 + - + $project: + _id: 0 + title: 1 + released: 1 + - + $limit: 5 + - + $facet: + docs: [] + meta: + - + $replaceWith: '$$SEARCH_META' + - + $limit: 1 + - + name: 'Date Search and Sort' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/sort/#date-search-and-sort' + pipeline: + - + $search: + range: + path: 'released' + gt: !bson_utcdatetime '2010-01-01T00:00:00.000Z' + lt: !bson_utcdatetime '2015-01-01T00:00:00.000Z' + sort: + released: -1 + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + released: 1 + - + name: 'Number Search and Sort' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/sort/#number-search-and-sort' + pipeline: + - + $search: + range: + path: 'awards.wins' + gt: 3 + sort: + awards.wins: -1 + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + awards.wins: 1 + - + name: 'Sort by score' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/sort/#sort-by-score' + pipeline: + - + $search: + text: + path: 'title' + query: 'story' + sort: + score: + $meta: 'searchScore' + order: 1 + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + score: + $meta: 'searchScore' + - + name: 'Paginate results after a token' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/paginate-results/#search-after-the-reference-point' + pipeline: + - + $search: + text: + path: 'title' + query: 'war' + sort: + score: + $meta: 'searchScore' + released: 1 + searchAfter: 'CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA==' + - + name: 'Paginate results before a token' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/paginate-results/#search-before-the-reference-point' + pipeline: + - + $search: + text: + path: 'title' + query: 'war' + sort: + score: + $meta: 'searchScore' + released: 1 + searchBefore: 'CJ6kARoGELqvp4MIGgkpACDA3U8BAAA=' + - + name: 'Count results' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/counting/#count-results' + pipeline: + - + $search: + near: + path: 'released' + origin: !bson_utcdatetime '2011-09-01T00:00:00.000+00:00' + pivot: 7776000000 + count: + type: 'total' + - + $project: + meta: '$$SEARCH_META' + title: 1 + released: 1 + - + $limit: 2 + - + name: 'Track Search terms' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/tracking/#examples' + pipeline: + - + $search: + text: + query: 'summer' + path: 'title' + tracking: + searchTerms: 'summer' + - + $limit: 5 + - + $project: + _id: 0 + title: 1 + - + name: 'Return Stored Source Fields' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/return-stored-source/#examples' + pipeline: + - + $search: + text: + query: 'baseball' + path: 'title' + returnStoredSource: true + - + $match: + $or: + - + imdb.rating: + $gt: 8.2 + - + imdb.votes: + $gte: 4500 + - + $lookup: + from: 'movies' + localField: '_id' + foreignField: '_id' + as: 'document' diff --git a/generator/config/stage/searchMeta.yaml b/generator/config/stage/searchMeta.yaml new file mode 100644 index 000000000..a7d92c272 --- /dev/null +++ b/generator/config/stage/searchMeta.yaml @@ -0,0 +1,132 @@ +# $schema: ../schema.json +name: $searchMeta +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/searchMeta/' +type: + - stage +encode: object +description: | + Returns different types of metadata result documents for the Atlas Search query against an Atlas collection. + NOTE: $searchMeta is only available for MongoDB Atlas clusters running MongoDB v4.4.9 or higher, and is not available for self-managed deployments. +arguments: + - + name: operator + mergeObject: true + type: + - searchOperator + description: | + Operator to search with. You can provide a specific operator or use + the compound operator to run a compound query with multiple operators. + - + name: index + optional: true + type: + - string + description: | + Name of the Atlas Search index to use. If omitted, defaults to default. + + - + name: count + optional: true + type: + - object + description: | + Document that specifies the count options for retrieving a count of the results. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#example' + pipeline: + - + $searchMeta: + range: + path: 'year' + gte: 1998 + lt: 1999 + count: + type: 'total' + + - + name: 'Year Facet' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/facet/#example-1' + pipeline: + - $searchMeta: + facet: + operator: + range: + path: 'year' + gte: 1980 + lte: 2000 + facets: + yearFacet: + type: 'number' + path: 'year' + boundaries: + - 1980 + - 1990 + - 2000 + default: 'other' + + - + name: 'Date Facet' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/facet/#example-2' + pipeline: + - + $searchMeta: + facet: + operator: + range: + path: 'released' + gte: !bson_utcdatetime '2000-01-01T00:00:00.000Z' + lte: !bson_utcdatetime '2015-01-31T00:00:00.000Z' + facets: + yearFacet: + type: 'date' + path: 'released' + boundaries: + - !bson_utcdatetime '2000-01-01' + - !bson_utcdatetime '2005-01-01' + - !bson_utcdatetime '2010-01-01' + - !bson_utcdatetime '2015-01-01' + default: 'other' + + - + name: 'Metadata Results' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/facet/#examples' + pipeline: + - + $searchMeta: + facet: + operator: + range: + path: 'released' + gte: !bson_utcdatetime '2000-01-01T00:00:00.000Z' + lte: !bson_utcdatetime '2015-01-31T00:00:00.000Z' + facets: + directorsFacet: + type: 'string' + path: 'directors' + numBuckets: 7 + yearFacet: + type: 'number' + path: 'year' + boundaries: + - 2000 + - 2005 + - 2010 + - 2015 + + - + name: 'Autocomplete Bucket Results through Facet Queries' + link: 'https://www.mongodb.com/docs/atlas/atlas-search/autocomplete/#bucket-results-through-facet-queries' + pipeline: + - + $searchMeta: + facet: + operator: + autocomplete: + path: 'title' + query: 'Gravity' + facets: + titleFacet: + type: 'string' + path: 'title' diff --git a/generator/config/stage/set.yaml b/generator/config/stage/set.yaml new file mode 100644 index 000000000..a5861aa29 --- /dev/null +++ b/generator/config/stage/set.yaml @@ -0,0 +1,73 @@ +# $schema: ../schema.json +name: $set +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/' +type: + - stage +encode: single +description: | + Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields. + Alias for $addFields. +arguments: + - + name: field + type: + - expression + variadic: object +tests: + - + name: 'Using Two $set Stages' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/#using-two--set-stages' + pipeline: + - + $set: + totalHomework: + # The $sum expression is always build as an array, even if the value is an array field name + # $sum: '$homework' + $sum: ['$homework'] + totalQuiz: + # $sum: '$quiz' + $sum: ['$quiz'] + - + $set: + totalScore: + $add: + - '$totalHomework' + - '$totalQuiz' + - '$extraCredit' + - + name: 'Adding Fields to an Embedded Document' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/#adding-fields-to-an-embedded-document' + pipeline: + - + $set: + specs.fuel_type: 'unleaded' + - + name: 'Overwriting an existing field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/#overwriting-an-existing-field' + pipeline: + - + $set: + cats: 20 + - + name: 'Add Element to an Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/#add-element-to-an-array' + pipeline: + - + $match: + _id: 1 + - + $set: + homework: + $concatArrays: + - '$homework' + - + - 7 + - + name: 'Creating a New Field with Existing Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/#creating-a-new-field-with-existing-fields' + pipeline: + - + $set: + quizAverage: + # $avg: '$quiz' + $avg: ['$quiz'] diff --git a/generator/config/stage/setWindowFields.yaml b/generator/config/stage/setWindowFields.yaml new file mode 100644 index 000000000..6f86472d7 --- /dev/null +++ b/generator/config/stage/setWindowFields.yaml @@ -0,0 +1,144 @@ +# $schema: ../schema.json +name: $setWindowFields +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/' +type: + - stage +encode: object +description: | + Groups documents into windows and applies one or more operators to the documents in each window. + New in MongoDB 5.0. +arguments: + - + name: sortBy + type: + - sortBy + description: | + Specifies the field(s) to sort the documents by in the partition. Uses the same syntax as the $sort stage. Default is no sorting. + - + name: output + type: + - object + description: | + Specifies the field(s) to append to the documents in the output returned by the $setWindowFields stage. Each field is set to the result returned by the window operator. + A field can contain dots to specify embedded document fields and array fields. The semantics for the embedded document dotted notation in the $setWindowFields stage are the same as the $addFields and $set stages. + - + name: partitionBy + type: + - expression + description: | + Specifies an expression to group the documents. In the $setWindowFields stage, the group of documents is known as a partition. Default is one partition for the entire collection. + optional: true +tests: + - + name: 'Use Documents Window to Obtain Cumulative Quantity for Each State' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-documents-window-to-obtain-cumulative-quantity-for-each-state' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + cumulativeQuantityForState: + $sum: '$quantity' + window: + documents: ['unbounded', 'current'] + - + name: 'Use Documents Window to Obtain Cumulative Quantity for Each Year' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-documents-window-to-obtain-cumulative-quantity-for-each-year' + pipeline: + - + $setWindowFields: + partitionBy: + # $year: '$orderDate' + $year: + date: '$orderDate' + sortBy: + orderDate: 1 + output: + cumulativeQuantityForYear: + $sum: '$quantity' + window: + documents: ['unbounded', 'current'] + - + name: 'Use Documents Window to Obtain Moving Average Quantity for Each Year' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-documents-window-to-obtain-moving-average-quantity-for-each-year' + pipeline: + - + $setWindowFields: + partitionBy: + # $year: '$orderDate' + $year: + date: '$orderDate' + sortBy: + orderDate: 1 + output: + averageQuantity: + $avg: '$quantity' + window: + documents: [-1, 0] + - + name: 'Use Documents Window to Obtain Cumulative and Maximum Quantity for Each Year' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-documents-window-to-obtain-cumulative-and-maximum-quantity-for-each-year' + pipeline: + - + $setWindowFields: + partitionBy: + # $year: '$orderDate' + $year: + date: '$orderDate' + sortBy: + orderDate: 1 + output: + cumulativeQuantityForYear: + $sum: '$quantity' + window: + documents: ['unbounded', 'current'] + maximumQuantityForYear: + $max: '$quantity' + window: + documents: ['unbounded', 'unbounded'] + - + name: 'Range Window Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#range-window-example' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + price: 1 + output: + quantityFromSimilarOrders: + $sum: '$quantity' + window: + range: [-10, 10] + - + name: 'Use a Time Range Window with a Positive Upper Bound' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-a-time-range-window-with-a-positive-upper-bound' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + recentOrders: + $push: '$orderDate' + window: + range: ['unbounded', 10] + unit: 'month' + - + name: 'Use a Time Range Window with a Negative Upper Bound' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-a-time-range-window-with-a-negative-upper-bound' + pipeline: + - + $setWindowFields: + partitionBy: '$state' + sortBy: + orderDate: 1 + output: + recentOrders: + $push: '$orderDate' + window: + range: ['unbounded', -10] + unit: 'month' diff --git a/generator/config/stage/shardedDataDistribution.yaml b/generator/config/stage/shardedDataDistribution.yaml new file mode 100644 index 000000000..2f298ca0f --- /dev/null +++ b/generator/config/stage/shardedDataDistribution.yaml @@ -0,0 +1,16 @@ +# $schema: ../schema.json +name: $shardedDataDistribution +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shardedDataDistribution/' +type: + - stage +encode: object +description: | + Provides data and size distribution information on sharded collections. + New in MongoDB 6.0.3. +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/shardedDataDistribution/#examples' + pipeline: + - + $shardedDataDistribution: {} diff --git a/generator/config/stage/skip.yaml b/generator/config/stage/skip.yaml new file mode 100644 index 000000000..2128fe226 --- /dev/null +++ b/generator/config/stage/skip.yaml @@ -0,0 +1,20 @@ +# $schema: ../schema.json +name: $skip +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/' +type: + - stage +encode: single +description: | + Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents). +arguments: + - + name: skip + type: + - int +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/#example' + pipeline: + - + $skip: 5 diff --git a/generator/config/stage/sort.yaml b/generator/config/stage/sort.yaml new file mode 100644 index 000000000..d35e23b63 --- /dev/null +++ b/generator/config/stage/sort.yaml @@ -0,0 +1,37 @@ +# $schema: ../schema.json +name: $sort +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/' +type: + - stage +encode: single +description: | + Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document. +arguments: + - + name: sort + type: + - expression + - sortSpec + variadic: object +tests: + - + name: 'Ascending Descending Sort' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/#ascending-descending-sort' + pipeline: + - + $sort: + age: -1 + posts: 1 + - + name: 'Text Score Metadata Sort' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/#text-score-metadata-sort' + pipeline: + - + $match: + $text: + $search: 'operating' + - + $sort: + score: + $meta: 'textScore' + posts: -1 diff --git a/generator/config/stage/sortByCount.yaml b/generator/config/stage/sortByCount.yaml new file mode 100644 index 000000000..a32d7aff4 --- /dev/null +++ b/generator/config/stage/sortByCount.yaml @@ -0,0 +1,25 @@ +# $schema: ../schema.json +name: $sortByCount +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/' +type: + - stage +encode: single +description: | + Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group. +arguments: + - + name: expression + type: + - expression +tests: + - + name: 'Example' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/#example' + pipeline: + - + # The builder uses the verbose form of the $unwind operator + # $unwind: '$tags' + $unwind: + path: '$tags' + - + $sortByCount: '$tags' diff --git a/generator/config/stage/unionWith.yaml b/generator/config/stage/unionWith.yaml new file mode 100644 index 000000000..eafa44110 --- /dev/null +++ b/generator/config/stage/unionWith.yaml @@ -0,0 +1,83 @@ +# $schema: ../schema.json +name: $unionWith +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/' +type: + - stage +encode: object +description: | + Performs a union of two collections; i.e. combines pipeline results from two collections into a single result set. + New in MongoDB 4.4. +arguments: + - + name: coll + type: + - string + description: | + The collection or view whose pipeline results you wish to include in the result set. + - + name: pipeline + type: + - pipeline + optional: true + description: | + An aggregation pipeline to apply to the specified coll. + The pipeline cannot include the $out and $merge stages. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. +tests: + - + name: 'Report 1 All Sales by Year and Stores and Items' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/#report-1--all-sales-by-year-and-stores-and-items' + pipeline: + - + $set: + _id: '2017' + - + $unionWith: + coll: 'sales_2018' + pipeline: + - + $set: + _id: '2018' + - + $unionWith: + coll: 'sales_2019' + pipeline: + - + $set: + _id: '2019' + - + $unionWith: + coll: 'sales_2020' + pipeline: + - + $set: + _id: '2020' + - + $sort: + _id: 1 + store: 1 + item: 1 + - + name: 'Report 2 Aggregated Sales by Items' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/#report-2--aggregated-sales-by-items' + pipeline: + - + # Example uses the short form, the builder always generates the verbose form + # $unionWith: 'sales_2018' + $unionWith: + coll: 'sales_2018' + - + # $unionWith: 'sales_2019' + $unionWith: + coll: 'sales_2019' + - + # $unionWith: 'sales_2020' + $unionWith: + coll: 'sales_2020' + - + $group: + _id: '$item' + total: + $sum: '$quantity' + - + $sort: + total: -1 diff --git a/generator/config/stage/unset.yaml b/generator/config/stage/unset.yaml new file mode 100644 index 000000000..cef9cdd6d --- /dev/null +++ b/generator/config/stage/unset.yaml @@ -0,0 +1,42 @@ +# $schema: ../schema.json +name: $unset +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/' +type: + - stage +encode: single +description: | + Removes or excludes fields from documents. + Alias for $project stage that removes or excludes fields. +arguments: + - + name: field + type: + - fieldPath + variadic: array +tests: + - + name: 'Remove a Single Field' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/#remove-a-single-field' + pipeline: + - + # The example in the docs uses the short syntax whereas + # the aggregation builder always uses the equivalent array syntax. + # $unset: 'copies' + $unset: ['copies'] + - + name: 'Remove Top-Level Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/#remove-top-level-fields' + pipeline: + - + $unset: + - 'isbn' + - 'copies' + - + name: 'Remove Embedded Fields' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/#remove-embedded-fields' + pipeline: + - + $unset: + - 'isbn' + - 'author.first' + - 'copies.warehouse' diff --git a/generator/config/stage/unwind.yaml b/generator/config/stage/unwind.yaml new file mode 100644 index 000000000..a1f93edbc --- /dev/null +++ b/generator/config/stage/unwind.yaml @@ -0,0 +1,95 @@ +# $schema: ../schema.json +name: $unwind +link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/' +type: + - stage +encode: object +description: | + Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array. +arguments: + - + name: path + type: + - arrayFieldPath + description: | + Field path to an array field. + - + name: includeArrayIndex + type: + - string + optional: true + description: | + The name of a new field to hold the array index of the element. The name cannot start with a dollar sign $. + - + name: preserveNullAndEmptyArrays + type: + - bool + optional: true + description: | + If true, if the path is null, missing, or an empty array, $unwind outputs the document. + If false, if path is null, missing, or an empty array, $unwind does not output a document. + The default value is false. +tests: + - + name: 'Unwind Array' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#unwind-array' + pipeline: + - + # Example uses the short form, the builder always generates the verbose form + # $unwind: '$sizes' + $unwind: + path: '$sizes' + - + name: 'preserveNullAndEmptyArrays' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#preservenullandemptyarrays' + pipeline: + - + $unwind: + path: '$sizes' + preserveNullAndEmptyArrays: true + - + name: 'includeArrayIndex' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#includearrayindex' + pipeline: + - + $unwind: + path: '$sizes' + includeArrayIndex: 'arrayIndex' + - + name: 'Group by Unwound Values' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#group-by-unwound-values' + pipeline: + - + $unwind: + path: '$sizes' + preserveNullAndEmptyArrays: true + - + $group: + _id: '$sizes' + averagePrice: + $avg: '$price' + - + $sort: + averagePrice: -1 + - + name: 'Unwind Embedded Arrays' + link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/#unwind-embedded-arrays' + pipeline: + - + # Example uses the short form, the builder always generates the verbose form + # $unwind: '$items' + $unwind: + path: '$items' + - + # Example uses the short form, the builder always generates the verbose form + # $unwind: '$items.tags' + $unwind: + path: '$items.tags' + - + $group: + _id: '$items.tags' + totalSalesAmount: + $sum: + $multiply: + - '$items.price' + - '$items.quantity' diff --git a/generator/config/stage/vectorSearch.yaml b/generator/config/stage/vectorSearch.yaml new file mode 100644 index 000000000..bcd3c008a --- /dev/null +++ b/generator/config/stage/vectorSearch.yaml @@ -0,0 +1,119 @@ +# $schema: ../schema.json +name: $vectorSearch +link: 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/' +type: + - stage +encode: object +description: | + The $vectorSearch stage performs an ANN or ENN search on a vector in the specified field. +arguments: + - + name: index + type: + - string + description: | + Name of the Atlas Vector Search index to use. + - + name: limit + type: + - int + description: | + Number of documents to return in the results. This value can't exceed the value of numCandidates if you specify numCandidates. + - + name: path + type: + - string + description: | + Indexed vector type field to search. + - + name: queryVector + type: + - array # of numbers + description: | + Array of numbers that represent the query vector. The number type must match the indexed field value type. + - + name: exact + optional: true + type: + - bool + description: | + This is required if numCandidates is omitted. false to run ANN search. true to run ENN search. + - + name: filter + optional: true + type: + - query + description: | + Any match query that compares an indexed field with a boolean, date, objectId, number (not decimals), string, or UUID to use as a pre-filter. + - + name: numCandidates + optional: true + type: + - int + description: | + This field is required if exact is false or omitted. + Number of nearest neighbors to use during the search. Value must be less than or equal to (<=) 10000. You can't specify a number less than the number of documents to return (limit). + +tests: + - + name: 'ANN Basic' + link: 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/#ann-examples' + pipeline: + - + $vectorSearch: + index: 'vector_index' + path: 'plot_embedding' + queryVector: [-0.0016261312, -0.028070757, -0.011342932] # skip other numbers, not relevant to the test + numCandidates: 150 + limit: 10 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'vectorSearchScore' + + - + name: 'ANN Filter' + link: 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/#ann-examples' + pipeline: + - + $vectorSearch: + index: 'vector_index' + path: 'plot_embedding' + filter: + $and: + - + year: + $lt: 1975 + queryVector: [0.02421053, -0.022372592, -0.006231137] # skip other numbers, not relevant to the test + numCandidates: 150 + limit: 10 + - + $project: + _id: 0 + title: 1 + plot: 1 + year: 1 + score: + $meta: 'vectorSearchScore' + + - + name: 'ENN' + link: 'https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/#enn-examples' + pipeline: + - + $vectorSearch: + index: 'vector_index' + path: 'plot_embedding' + queryVector: [-0.006954097, -0.009932499, -0.001311474] # skip other numbers, not relevant to the test + exact: true + limit: 10 + - + $project: + _id: 0 + plot: 1 + title: 1 + score: + $meta: 'vectorSearchScore' diff --git a/generator/generate b/generator/generate new file mode 100755 index 000000000..d67515b70 --- /dev/null +++ b/generator/generate @@ -0,0 +1,17 @@ +#!/usr/bin/env php +add(new GenerateCommand(__DIR__ . '/../', __DIR__ . '/config')); +$application->setDefaultCommand('generate'); +$application->run(); diff --git a/generator/js2yaml.html b/generator/js2yaml.html new file mode 100644 index 000000000..31241adb1 --- /dev/null +++ b/generator/js2yaml.html @@ -0,0 +1,179 @@ + + +

Convert JS examples into Yaml

+ +
+ + +
+ +
+ + +
+ + + diff --git a/generator/src/AbstractGenerator.php b/generator/src/AbstractGenerator.php new file mode 100644 index 000000000..b0fef69aa --- /dev/null +++ b/generator/src/AbstractGenerator.php @@ -0,0 +1,111 @@ +printer = new PsrPrinter(); + } + + /** + * Split the namespace and class name from a fully qualified class name. + * + * @return array{0: string, 1: string} + */ + final protected function splitNamespaceAndClassName(string $fqcn): array + { + $parts = explode('\\', ltrim($fqcn, '\\')); + $className = array_pop($parts); + + return [implode('\\', $parts), $className]; + } + + final protected function writeFile(PhpNamespace $namespace, bool $autoGeneratedWarning = true): void + { + $classes = $namespace->getClasses(); + assert(count($classes) === 1, sprintf('Expected exactly one class in namespace "%s", got %d.', $namespace->getName(), count($classes))); + + $filename = $this->rootDir . $this->getFileName($namespace->getName(), current($classes)->getName()); + + $dirname = dirname($filename); + if (! is_dir($dirname)) { + mkdir($dirname, 0755, true); + } + + $file = new PhpFile(); + $file->setStrictTypes(); + if ($autoGeneratedWarning) { + $file->setComment('THIS FILE IS AUTO-GENERATED. ANY CHANGES WILL BE LOST!'); + } + + $file->addNamespace($namespace); + + file_put_contents($filename, $this->printer->printFile($file)); + } + + final protected function readFile(string ...$fqcn): PhpFile|null + { + $filename = $this->rootDir . $this->getFileName(...$fqcn); + + if (! is_file($filename)) { + return null; + } + + return PhpFile::fromCode(file_get_contents($filename)); + } + + /** + * Thanks to PSR-4, the file name can be determined from the fully qualified class name. + * + * @param string ...$fqcn Fully qualified class name, merged if multiple parts + * + * @return string File name relative to the root directory + */ + private function getFileName(string ...$fqcn): string + { + $fqcn = implode('\\', $fqcn); + + // Config from composer.json autoload + $config = [ + 'MongoDB\\Tests\\' => 'tests/', + 'MongoDB\\' => 'src/', + ]; + foreach ($config as $namespace => $directory) { + if (str_starts_with($fqcn, $namespace)) { + return $directory . str_replace([$namespace, '\\'], ['', '/'], $fqcn) . '.php'; + } + } + + throw new InvalidArgumentException(sprintf('Could not determine file name for "%s"', $fqcn)); + } +} diff --git a/generator/src/Command/GenerateCommand.php b/generator/src/Command/GenerateCommand.php new file mode 100644 index 000000000..78482963e --- /dev/null +++ b/generator/src/Command/GenerateCommand.php @@ -0,0 +1,90 @@ +setName('generate'); + $this->setDescription('Generate code for mongodb/mongodb library'); + $this->setHelp('Generate code for mongodb/mongodb library'); + } + + public function execute(InputInterface $input, OutputInterface $output): int + { + $output->writeln('Generating code for mongodb/mongodb library'); + + $expressions = $this->generateExpressionClasses($output); + $this->generateOperatorClasses($expressions, $output); + + return Command::SUCCESS; + } + + /** @return array */ + private function generateExpressionClasses(OutputInterface $output): array + { + $output->writeln('Generating expression classes'); + + $config = require $this->configDir . '/expressions.php'; + assert(is_array($config)); + + $definitions = []; + $generator = new ExpressionClassGenerator($this->rootDir); + foreach ($config as $name => $def) { + assert(is_array($def)); + assert(! array_key_exists($name, $definitions), sprintf('Duplicate expression name "%s".', $name)); + $definitions[$name] = $def = new ExpressionDefinition($name, ...$def); + $generator->generate($def); + } + + $generator = new ExpressionFactoryGenerator($this->rootDir); + $generator->generate($definitions); + + return $definitions; + } + + /** @param array $expressions */ + private function generateOperatorClasses(array $expressions, OutputInterface $output): void + { + $config = require $this->configDir . '/definitions.php'; + assert(is_array($config)); + + foreach ($config as $def) { + assert(is_array($def)); + $definition = new GeneratorDefinition(...$def); + + foreach ($definition->generators as $generatorClass) { + $output->writeln(sprintf('Generating classes for %s with %s', basename($definition->configFiles), $generatorClass)); + assert(is_a($generatorClass, OperatorGenerator::class, true)); + $generator = new $generatorClass($this->rootDir, $expressions); + $generator->generate($definition); + } + } + } +} diff --git a/generator/src/Definition/ArgumentDefinition.php b/generator/src/Definition/ArgumentDefinition.php new file mode 100644 index 000000000..7cdd7d321 --- /dev/null +++ b/generator/src/Definition/ArgumentDefinition.php @@ -0,0 +1,54 @@ + */ + public array $type, + public string|null $description = null, + public bool $optional = false, + string|null $variadic = null, + int|null $variadicMin = null, + public mixed $default = null, + public bool $mergeObject = false, + ) { + assert($this->optional === false || $this->default === null, 'Optional arguments cannot have a default value'); + if (is_array($type)) { + assert(array_is_list($type), 'Type must be a list or a single string'); + foreach ($type as $t) { + assert(is_string($t), sprintf('Type must be a list of strings. Got %s', get_debug_type($type))); + } + } + + $this->propertyName = ltrim($this->name, '$'); + + if ($variadic) { + $this->variadic = VariadicType::from($variadic); + if ($variadicMin === null) { + $this->variadicMin = $optional ? 0 : 1; + } else { + $this->variadicMin = $variadicMin; + } + } else { + $this->variadic = null; + $this->variadicMin = null; + } + } +} diff --git a/generator/src/Definition/ExpressionDefinition.php b/generator/src/Definition/ExpressionDefinition.php new file mode 100644 index 000000000..cbe37febc --- /dev/null +++ b/generator/src/Definition/ExpressionDefinition.php @@ -0,0 +1,37 @@ + */ + public array $acceptedTypes, + /** Interface to implement for operators that resolve to this type. Generated class/enum/interface. */ + public string|null $returnType = null, + public string|null $extends = null, + /** @var list */ + public array $implements = [], + public array $values = [], + public PhpObject|null $generate = null, + ) { + assert($generate === PhpObject::PhpClass || ! $extends, $name . ': Cannot specify "extends" when "generate" is not "class"'); + assert($generate === PhpObject::PhpEnum || ! $this->values, $name . ': Cannot specify "values" when "generate" is not "enum"'); + //assert($returnType === null || interface_exists($returnType), $name . ': Return type must be an interface'); + + foreach ($acceptedTypes as $acceptedType) { + assert(is_string($acceptedType), $name . ': AcceptedTypes must be an array of strings.'); + } + + if ($generate) { + $this->returnType = 'MongoDB\\Builder\\Expression\\' . ucfirst($this->name); + } + } +} diff --git a/generator/src/Definition/GeneratorDefinition.php b/generator/src/Definition/GeneratorDefinition.php new file mode 100644 index 000000000..2faa2fac3 --- /dev/null +++ b/generator/src/Definition/GeneratorDefinition.php @@ -0,0 +1,42 @@ +> */ + public array $generators, + public string $namespace, + public string $classNameSuffix = '', + public array $interfaces = [], + public string|null $parentClass = null, + ) { + assert(str_starts_with($namespace, 'MongoDB\\'), sprintf('Namespace must start with "MongoDB\\". Got "%s"', $namespace)); + assert(! str_ends_with($namespace, '\\'), sprintf('Namespace must not end with "\\". Got "%s"', $namespace)); + + assert(array_is_list($interfaces), 'Generators must be a list of class names'); + foreach ($interfaces as $interface) { + assert(is_string($interface) && class_exists($interface), sprintf('Interface "%s" does not exist', $interface)); + } + + assert(array_is_list($generators), 'Generators must be a list of class names'); + foreach ($generators as $class) { + assert(is_string($class) && is_subclass_of($class, OperatorGenerator::class), sprintf('Generator class "%s" must extend "%s"', $class, OperatorGenerator::class)); + } + } +} diff --git a/generator/src/Definition/OperatorDefinition.php b/generator/src/Definition/OperatorDefinition.php new file mode 100644 index 000000000..5468b7917 --- /dev/null +++ b/generator/src/Definition/OperatorDefinition.php @@ -0,0 +1,75 @@ + */ + public readonly array $arguments; + + /** @var list */ + public readonly array $tests; + + public function __construct( + public string $name, + public string $link, + string $encode, + /** @var list */ + public array $type, + public string|null $description = null, + public bool $wrapObject = true, + array $arguments = [], + array $tests = [], + ) { + $this->encode = match ($encode) { + 'single' => Encode::Single, + 'array' => Encode::Array, + 'object' => Encode::Object, + default => throw new UnexpectedValueException(sprintf('Unexpected "encode" value for operator "%s". Got "%s"', $name, $encode)), + }; + + if (! $wrapObject && $this->encode !== Encode::Object) { + throw new UnexpectedValueException(sprintf('Operator "%s" cannot have wrapObject set to false when encode is not "object"', $name)); + } + + // Convert arguments to ArgumentDefinition objects + // Optional arguments must be after required arguments + $requiredArgs = $optionalArgs = []; + foreach ($arguments as $arg) { + $arg = new ArgumentDefinition(...get_object_vars($arg)); + if ($arg->optional) { + $optionalArgs[] = $arg; + } else { + $requiredArgs[] = $arg; + } + } + + // "single" encode operators must have one required argument + if ($this->encode === Encode::Single) { + assert(count($requiredArgs) === 1, sprintf('Single encode operator "%s" must have one argument', $name)); + assert(count($optionalArgs) === 0, sprintf('Single encode operator "%s" argument cannot be optional', $name)); + } + + $this->arguments = array_merge($requiredArgs, $optionalArgs); + + $this->tests = array_map( + static fn (object $test): TestDefinition => new TestDefinition(...get_object_vars($test)), + array_values($tests), + ); + } +} diff --git a/generator/src/Definition/PhpObject.php b/generator/src/Definition/PhpObject.php new file mode 100644 index 000000000..5e815e0b6 --- /dev/null +++ b/generator/src/Definition/PhpObject.php @@ -0,0 +1,15 @@ + */ + public array $pipeline, + public string|null $link = null, + ) { + assert(array_is_list($pipeline), sprintf('Argument "%s" pipeline must be a list', $name)); + } +} diff --git a/generator/src/Definition/VariadicType.php b/generator/src/Definition/VariadicType.php new file mode 100644 index 000000000..091f654c9 --- /dev/null +++ b/generator/src/Definition/VariadicType.php @@ -0,0 +1,11 @@ + */ + public function read(string $dirname): array + { + $finder = new Finder(); + $finder->files()->in($dirname)->name('*.yaml')->sortByName(); + + $definitions = []; + foreach ($finder as $file) { + $operator = Yaml::parseFile( + $file->getPathname(), + Yaml::PARSE_OBJECT | Yaml::PARSE_OBJECT_FOR_MAP | Yaml::PARSE_CUSTOM_TAGS, + ); + $definitions[] = new OperatorDefinition(...get_object_vars($operator)); + } + + return $definitions; + } +} diff --git a/generator/src/ExpressionClassGenerator.php b/generator/src/ExpressionClassGenerator.php new file mode 100644 index 000000000..fc9119364 --- /dev/null +++ b/generator/src/ExpressionClassGenerator.php @@ -0,0 +1,96 @@ +generate) { + return; + } + + try { + $this->writeFile($this->createClassOrInterface($definition)); + } catch (Throwable $e) { + throw new RuntimeException('Failed to generate expression class for ' . $definition->name, 0, $e); + } + } + + public function createClassOrInterface(ExpressionDefinition $definition): PhpNamespace + { + [$namespace, $className] = $this->splitNamespaceAndClassName($definition->returnType); + $namespace = new PhpNamespace($namespace); + foreach ($definition->implements as $interface) { + $namespace->addUse($interface); + } + + $types = array_map( + fn (string $type): string => match ($type) { + 'list' => 'array', + default => $type, + }, + $definition->acceptedTypes, + ); + + if ($definition->generate === PhpObject::PhpClass) { + $class = $namespace->addClass($className); + $class->setImplements($definition->implements); + $class->setExtends($definition->extends); + + // Replace with promoted property in PHP 8.1 + $propertyType = Type::union(...$types); + $class->addProperty('name') + ->setType($propertyType) + ->setReadOnly() + ->setPublic(); + + $constructor = $class->addMethod('__construct'); + $constructor->addParameter('name')->setType($propertyType); + + $namespace->addUse(InvalidArgumentException::class); + $namespace->addUseFunction('sprintf'); + $namespace->addUseFunction('str_starts_with'); + $constructor->addBody(<<addBody('$this->name = $name;'); + } elseif ($definition->generate === PhpObject::PhpInterface) { + $interface = $namespace->addInterface($className); + $interface->setExtends($definition->implements); + } elseif ($definition->generate === PhpObject::PhpEnum) { + $enum = $namespace->addEnum($className); + $enum->setType('string'); + array_map( + fn (string $case) => $enum->addCase(ucfirst($case), $case), + $definition->values, + ); + } else { + throw new LogicException('Unknown generate type: ' . var_export($definition->generate, true)); + } + + return $namespace; + } +} diff --git a/generator/src/ExpressionFactoryGenerator.php b/generator/src/ExpressionFactoryGenerator.php new file mode 100644 index 000000000..b1e84c5dd --- /dev/null +++ b/generator/src/ExpressionFactoryGenerator.php @@ -0,0 +1,49 @@ + $expressions */ + public function generate(array $expressions): void + { + $this->writeFile($this->createFactoryClass($expressions)); + } + + /** @param array $expressions */ + private function createFactoryClass(array $expressions): PhpNamespace + { + $namespace = new PhpNamespace('MongoDB\\Builder\\Expression'); + $trait = $namespace->addTrait('ExpressionFactoryTrait'); + $trait->addComment('@internal'); + + // Pedantry requires methods to be ordered alphabetically + usort($expressions, fn (ExpressionDefinition $a, ExpressionDefinition $b) => $a->name <=> $b->name); + + foreach ($expressions as $expression) { + if ($expression->generate !== PhpObject::PhpClass) { + continue; + } + + $namespace->addUse($expression->returnType); + $expressionShortClassName = $this->splitNamespaceAndClassName($expression->returnType)[1]; + + $method = $trait->addMethod(lcfirst($expressionShortClassName)); + $method->setStatic(); + $method->addParameter('name')->setType('string'); + $method->addBody('return new ' . $expressionShortClassName . '($name);'); + $method->setReturnType($expression->returnType); + } + + return $namespace; + } +} diff --git a/generator/src/FluentStageFactoryGenerator.php b/generator/src/FluentStageFactoryGenerator.php new file mode 100644 index 000000000..ff7010f15 --- /dev/null +++ b/generator/src/FluentStageFactoryGenerator.php @@ -0,0 +1,134 @@ +writeFile($this->createFluentFactoryTrait($definition)); + } + + private function createFluentFactoryTrait(GeneratorDefinition $definition): PhpNamespace + { + $namespace = new PhpNamespace($definition->namespace); + $trait = $namespace->addTrait('FluentFactoryTrait'); + + $namespace->addUse(self::FACTORY_CLASS); + $namespace->addUse(StageInterface::class); + $namespace->addUse(Pipeline::class); + $namespace->addUse(stdClass::class); + + $trait->addProperty('pipeline') + ->setType('array') + ->setComment('@var list|stdClass>') + ->setValue([]); + $trait->addMethod('getPipeline') + ->setReturnType(Pipeline::class) + ->setBody(<<<'PHP' + return new Pipeline(...$this->pipeline); + PHP); + + $this->addUsesFrom(self::FACTORY_CLASS, $namespace); + $staticFactory = ClassType::from(self::FACTORY_CLASS); + assert($staticFactory instanceof ClassType); + + // Import the methods customized in the factory class + foreach ($staticFactory->getMethods() as $method) { + $this->addMethod($method, $trait); + } + + // Import the other methods provided by the generated trait + foreach ($staticFactory->getTraits() as $usedTrait) { + $this->addUsesFrom($usedTrait->getName(), $namespace); + $staticFactory = TraitType::from($usedTrait->getName()); + assert($staticFactory instanceof TraitType); + foreach ($staticFactory->getMethods() as $method) { + $this->addMethod($method, $trait); + } + } + + return $namespace; + } + + private function addMethod(Method $factoryMethod, TraitType $trait): void + { + // Non-public methods are not part of the API + if (! $factoryMethod->isPublic()) { + return; + } + + // Some methods can be overridden in the class, so we skip them + // when importing the methods provided by the trait. + if ($trait->hasMethod($factoryMethod->getName())) { + return; + } + + $method = $trait->addMethod($factoryMethod->getName()); + + $method->setComment($factoryMethod->getComment()); + $method->setParameters($factoryMethod->getParameters()); + + $args = array_map( + fn (Parameter $param): string => '$' . $param->getName(), + $factoryMethod->getParameters(), + ); + + if ($factoryMethod->isVariadic()) { + $method->setVariadic(); + $args[array_key_last($args)] = '...' . $args[array_key_last($args)]; + } + + $method->setReturnType('static'); + $method->setBody(sprintf( + <<<'PHP' + $this->pipeline[] = %s::%s(%s); + + return $this; + PHP, + (new ReflectionClass(self::FACTORY_CLASS))->getShortName(), + $factoryMethod->getName(), + implode(', ', $args), + )); + } + + private static function addUsesFrom(string $classLike, PhpNamespace $namespace): void + { + $file = PhpFile::fromCode(file_get_contents((new ReflectionClass($classLike))->getFileName())); + + foreach ($file->getNamespaces() as $ns) { + foreach ($ns->getUses() as $use) { + $namespace->addUse($use); + } + } + } +} diff --git a/generator/src/OperatorClassGenerator.php b/generator/src/OperatorClassGenerator.php new file mode 100644 index 000000000..6fd80cc41 --- /dev/null +++ b/generator/src/OperatorClassGenerator.php @@ -0,0 +1,213 @@ +getOperators($definition) as $operator) { + try { + $this->writeFile($this->createClass($definition, $operator)); + } catch (Throwable $e) { + throw new RuntimeException(sprintf('Failed to generate class for operator "%s"', $operator->name), 0, $e); + } + } + } + + public function createClass(GeneratorDefinition $definition, OperatorDefinition $operator): PhpNamespace + { + $namespace = new PhpNamespace($definition->namespace); + + $interfaces = $this->getInterfaces($operator); + foreach ($interfaces as $interface) { + $namespace->addUse($interface); + } + + $class = $namespace->addClass($this->getOperatorClassName($definition, $operator)); + $class->setFinal(); + $class->setImplements($interfaces); + $namespace->addUse(OperatorInterface::class); + $class->addImplement(OperatorInterface::class); + + // Expose operator metadata as constants + // @todo move to encoder class + $class->addComment($operator->description); + $class->addComment('@see ' . $operator->link); + $class->addComment('@internal'); + $namespace->addUse(Encode::class); + $class->addConstant('ENCODE', new Literal('Encode::' . $operator->encode->name)); + $class->addConstant('NAME', $operator->wrapObject ? $operator->name : null); + + $encodeNames = []; + $constructor = $class->addMethod('__construct'); + foreach ($operator->arguments as $argument) { + $encodeNames[$argument->propertyName] = $argument->mergeObject ? null : $argument->name; + + $type = $this->getAcceptedTypes($argument); + foreach ($type->use as $use) { + $namespace->addUse($use); + } + + $property = $class->addProperty($argument->propertyName); + $property->setReadOnly(); + $constructorParam = $constructor->addParameter($argument->propertyName); + $constructorParam->setType($type->native); + + if ($argument->variadic) { + $constructor->setVariadic(); + $constructor->addComment('@param ' . $type->doc . ' ...$' . $argument->propertyName . rtrim(' ' . $argument->description)); + + if ($argument->variadicMin > 0) { + $namespace->addUse(InvalidArgumentException::class); + $constructor->addBody(<<propertyName}) < {$argument->variadicMin}) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for \${$argument->propertyName}, got %d.', {$argument->variadicMin}, \count(\${$argument->propertyName}))); + } + + PHP); + } + + if ($argument->variadic === VariadicType::Array) { + $property->setType('array'); + $property->addComment('@var list<' . $type->doc . '> $' . $argument->propertyName . rtrim(' ' . $argument->description)); + // Warn that named arguments are not supported + // @see https://psalm.dev/docs/running_psalm/issues/NamedArgumentNotAllowed/ + $constructor->addComment('@no-named-arguments'); + $namespace->addUseFunction('array_is_list'); + $namespace->addUse(InvalidArgumentException::class); + $constructor->addBody(<<propertyName})) { + throw new InvalidArgumentException('Expected \${$argument->propertyName} arguments to be a list (array), named arguments are not supported'); + } + + PHP); + } elseif ($argument->variadic === VariadicType::Object) { + $namespace->addUse(stdClass::class); + $property->setType(stdClass::class); + $property->addComment('@var stdClass<' . $type->doc . '> $' . $argument->propertyName . rtrim(' ' . $argument->description)); + $namespace->addUseFunction('is_string'); + $namespace->addUse(InvalidArgumentException::class); + $constructor->addBody(<<propertyName} as \$key => \$value) { + if (! is_string(\$key)) { + throw new InvalidArgumentException('Expected \${$argument->propertyName} arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + \${$argument->propertyName} = (object) \${$argument->propertyName}; + PHP); + } + } else { + // Non-variadic arguments + $property->addComment('@var ' . $type->doc . ' $' . $argument->propertyName . rtrim(' ' . $argument->description)); + $property->setType($type->native); + $constructor->addComment('@param ' . $type->doc . ' $' . $argument->propertyName . rtrim(' ' . $argument->description)); + + if ($argument->optional) { + // We use a special Optional::Undefined type to differentiate between null and undefined + $constructorParam->setDefaultValue(new Literal('Optional::Undefined')); + } elseif ($argument->default !== null) { + $constructorParam->setDefaultValue($argument->default); + } + + if ($type->dollarPrefixedString) { + $namespace->addUseFunction('is_string'); + $namespace->addUseFunction('str_starts_with'); + $namespace->addUse(InvalidArgumentException::class); + $constructor->addBody(<<propertyName}) && ! str_starts_with(\${$argument->propertyName}, '$')) { + throw new InvalidArgumentException('Argument \${$argument->propertyName} can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + PHP); + } + + // List type must be validated with array_is_list() + if ($type->list) { + $namespace->addUseFunction('is_array'); + $namespace->addUseFunction('array_is_list'); + $namespace->addUse(InvalidArgumentException::class); + $constructor->addBody(<<propertyName}) && ! array_is_list(\${$argument->propertyName})) { + throw new InvalidArgumentException('Expected \${$argument->propertyName} argument to be a list, got an associative array.'); + } + + PHP); + } + + if ($type->query) { + $namespace->addUseFunction('is_array'); + $namespace->addUse(QueryObject::class); + $constructor->addBody(<<propertyName})) { + \${$argument->propertyName} = QueryObject::create(\${$argument->propertyName}); + } + + PHP); + } + + if ($type->javascript) { + $namespace->addUseFunction('is_string'); + $namespace->addUse(Javascript::class); + $constructor->addBody(<<propertyName})) { + \${$argument->propertyName} = new Javascript(\${$argument->propertyName}); + } + + PHP); + } + } + + // Set property from constructor argument + $constructor->addBody('$this->' . $argument->propertyName . ' = $' . $argument->propertyName . ';'); + } + + if ($encodeNames !== []) { + $class->addConstant('PROPERTIES', $encodeNames); + } + + return $namespace; + } + + /** + * Operator classes interfaces are defined by their return type as a MongoDB expression. + * + * @return list + */ + private function getInterfaces(OperatorDefinition $definition): array + { + $interfaces = []; + + foreach ($definition->type as $type) { + $interfaces[] = $interface = $this->getType($type)->returnType; + assert(interface_exists($interface), sprintf('"%s" is not an interface.', $interface)); + } + + return $interfaces; + } +} diff --git a/generator/src/OperatorFactoryGenerator.php b/generator/src/OperatorFactoryGenerator.php new file mode 100644 index 000000000..e60804ad8 --- /dev/null +++ b/generator/src/OperatorFactoryGenerator.php @@ -0,0 +1,96 @@ +writeFile($this->createFactoryTrait($definition)); + } + + private function createFactoryTrait(GeneratorDefinition $definition): PhpNamespace + { + $namespace = new PhpNamespace($definition->namespace); + $trait = $namespace->addTrait('FactoryTrait'); + $trait->addComment('@internal'); + + // Pedantry requires methods to be ordered alphabetically + $operators = $this->getOperators($definition); + usort($operators, fn (OperatorDefinition $a, OperatorDefinition $b) => strcasecmp($a->name, $b->name)); + + foreach ($operators as $operator) { + try { + $this->addMethod($definition, $operator, $namespace, $trait); + } catch (Throwable $e) { + throw new RuntimeException(sprintf('Failed to generate class for operator "%s"', $operator->name), 0, $e); + } + } + + return $namespace; + } + + private function addMethod(GeneratorDefinition $definition, OperatorDefinition $operator, PhpNamespace $namespace, TraitType $trait): void + { + $operatorClassName = '\\' . $definition->namespace . '\\' . $this->getOperatorClassName($definition, $operator); + $namespace->addUse($operatorClassName); + + $method = $trait->addMethod(ltrim($operator->name, '$')); + $method->setStatic(); + $method->addComment($operator->description); + $method->addComment('@see ' . $operator->link); + $args = []; + foreach ($operator->arguments as $argument) { + $type = $this->getAcceptedTypes($argument); + foreach ($type->use as $use) { + $namespace->addUse($use); + } + + $parameter = $method->addParameter($argument->propertyName); + $parameter->setType($type->native); + if ($argument->variadic) { + if ($argument->variadic === VariadicType::Array) { + // Warn that named arguments are not supported + // @see https://psalm.dev/docs/running_psalm/issues/NamedArgumentNotAllowed/ + $method->addComment('@no-named-arguments'); + } + + $method->setVariadic(); + $method->addComment('@param ' . $type->doc . ' ...$' . $argument->propertyName . rtrim(' ' . $argument->description)); + $args[] = '...$' . $argument->propertyName; + } else { + if ($argument->optional) { + $parameter->setDefaultValue(new Literal('Optional::Undefined')); + } elseif ($argument->default !== null) { + $parameter->setDefaultValue($argument->default); + } + + $method->addComment('@param ' . $type->doc . ' $' . $argument->propertyName . rtrim(' ' . $argument->description)); + $args[] = '$' . $argument->propertyName; + } + } + + $operatorShortClassName = ltrim(str_replace($definition->namespace, '', $operatorClassName), '\\'); + $method->addBody('return new ' . $operatorShortClassName . '(' . implode(', ', $args) . ');'); + $method->setReturnType($operatorClassName); + } +} diff --git a/generator/src/OperatorGenerator.php b/generator/src/OperatorGenerator.php new file mode 100644 index 000000000..ffe0bb369 --- /dev/null +++ b/generator/src/OperatorGenerator.php @@ -0,0 +1,170 @@ + */ + private array $expressions, + ) { + parent::__construct($rootDir); + + $this->yamlReader = new YamlReader(); + } + + abstract public function generate(GeneratorDefinition $definition): void; + + /** @return list */ + final protected function getOperators(GeneratorDefinition $definition): array + { + // Remove unsupported operators + return array_filter( + $this->yamlReader->read($definition->configFiles), + fn (OperatorDefinition $operator): bool => ! in_array($operator->name, ['$'], true), + ); + } + + final protected function getOperatorClassName(GeneratorDefinition $definition, OperatorDefinition $operator): string + { + return ucfirst(ltrim($operator->name, '$')) . $definition->classNameSuffix; + } + + final protected function getType(string $type): ExpressionDefinition + { + assert(array_key_exists($type, $this->expressions), sprintf('Invalid expression type "%s".', $type)); + + return $this->expressions[$type]; + } + + /** + * Expression types can contain class names, interface, native types or "list". + * PHPDoc types are more precise than native types, so we use them systematically even if redundant. + * + * @return object{native:string,doc:string,use:list,list:bool,query:bool,javascript:bool,dollarPrefixedString:bool} + */ + final protected function getAcceptedTypes(ArgumentDefinition $arg): stdClass + { + $nativeTypes = []; + + $dollarPrefixedString = false; + + foreach ($arg->type as $type) { + if (str_starts_with($type, 'resolvesTo')) { + $dollarPrefixedString = true; + } + + $type = $this->getType($type); + $nativeTypes = array_merge($nativeTypes, $type->acceptedTypes); + + if (isset($type->returnType)) { + $nativeTypes[] = $type->returnType; + } + } + + if ($arg->optional) { + $use[] = '\\' . Optional::class; + $nativeTypes[] = Optional::class; + } + + // If the argument accepts an expression, a $-prefixed string is accepted (field path or variable) + // Checked only if the argument does not already accept a string + if (in_array('string', $nativeTypes, true)) { + $dollarPrefixedString = false; + } elseif ($dollarPrefixedString) { + $nativeTypes[] = 'string'; + } + + $docTypes = $nativeTypes = array_unique($nativeTypes); + $use = []; + + foreach ($nativeTypes as $key => $typeName) { + if (interface_exists($typeName) || class_exists($typeName)) { + $use[] = $nativeTypes[$key] = '\\' . $typeName; + $docTypes[$key] = $this->splitNamespaceAndClassName($typeName)[1]; + // A union cannot contain both object and a class type + if (in_array('object', $nativeTypes, true)) { + unset($nativeTypes[$key]); + } + } + } + + // If an array is expected, but not an object, we can check for a list + $listCheck = in_array('\\' . PackedArray::class, $nativeTypes, true) + && ! in_array('\\' . Document::class, $nativeTypes, true); + + // If the argument is a query, we need to convert it to a QueryObject + $isQuery = in_array('query', $arg->type, true); + + // If the argument is code, we need to convert it to a Javascript object + $isJavascript = in_array('javascript', $arg->type, true); + + // mixed can only be used as a standalone type + if (in_array('mixed', $nativeTypes, true)) { + $nativeTypes = ['mixed']; + } + + usort($nativeTypes, self::sortTypesCallback(...)); + usort($docTypes, self::sortTypesCallback(...)); + sort($use); + + return (object) [ + 'native' => Type::union(...array_unique($nativeTypes)), + 'doc' => Type::union(...array_unique($docTypes)), + 'use' => array_unique($use), + 'list' => $listCheck, + 'query' => $isQuery, + 'javascript' => $isJavascript, + 'dollarPrefixedString' => $dollarPrefixedString, + ]; + } + + /** + * usort() callback for sorting types. + * "Optional" is always first, for documentation of optional parameters, + * then types are sorted alphabetically. + */ + private static function sortTypesCallback(string $type1, string $type2): int + { + if ($type1 === 'Optional' || $type1 === '\\' . Optional::class) { + return -1; + } + + if ($type2 === 'Optional' || $type2 === '\\' . Optional::class) { + return 1; + } + + return $type1 <=> $type2; + } +} diff --git a/generator/src/OperatorTestGenerator.php b/generator/src/OperatorTestGenerator.php new file mode 100644 index 000000000..f665b9210 --- /dev/null +++ b/generator/src/OperatorTestGenerator.php @@ -0,0 +1,175 @@ +createExpectedClass($definition); + + foreach ($this->getOperators($definition) as $operator) { + // Skip operators without tests + if (! $operator->tests) { + continue; + } + + try { + $this->writeFile($this->createClass($definition, $operator, $dataNamespace->getClasses()[self::DATA_ENUM]), false); + } catch (Throwable $e) { + throw new RuntimeException(sprintf('Failed to generate class for operator "%s"', $operator->name), 0, $e); + } + } + + $this->writeFile($dataNamespace); + } + + public function createExpectedClass(GeneratorDefinition $definition): PhpNamespace + { + $dataNamespace = str_replace('MongoDB', 'MongoDB\\Tests', $definition->namespace); + + $namespace = new PhpNamespace($dataNamespace); + $enum = $namespace->addEnum(self::DATA_ENUM); + $enum->setType('string'); + + return $namespace; + } + + public function createClass(GeneratorDefinition $definition, OperatorDefinition $operator, EnumType $dataEnum): PhpNamespace + { + $testNamespace = str_replace('MongoDB', 'MongoDB\\Tests', $definition->namespace); + $testClass = $this->getOperatorClassName($definition, $operator) . 'Test'; + + $namespace = $this->readFile($testNamespace, $testClass)?->getNamespaces()[$testNamespace] ?? null; + $namespace ??= new PhpNamespace($testNamespace); + + $class = $namespace->getClasses()[$testClass] ?? null; + $class ??= $namespace->addClass($testClass); + $namespace->addUse(PipelineTestCase::class); + $class->setExtends(PipelineTestCase::class); + $namespace->addUse(Pipeline::class); + $class->setComment('Test ' . $operator->name . ' ' . basename($definition->configFiles)); + + foreach ($operator->tests as $test) { + $testName = 'test' . str_replace([' ', '-'], '', ucwords(str_replace('$', '', $test->name))); + $caseName = str_replace([' ', '-'], '', ucwords(str_replace('$', '', $operator->name . ' ' . $test->name))); + + $pipeline = $this->convertYamlTaggedValues($test->pipeline); + + // Wrap the pipeline array into a document + $json = Document::fromPHP(['pipeline' => $pipeline])->toCanonicalExtendedJSON(); + // Unwrap the pipeline array and reformat for prettier JSON + $json = json_encode(json_decode($json)->pipeline, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES); + $case = $dataEnum->addCase($caseName, new Literal('<<<\'JSON\'' . "\n" . $json . "\n" . 'JSON')); + $case->setComment($test->name); + if ($test->link) { + $case->addComment(''); + $case->addComment('@see ' . $test->link); + } + + $caseName = self::DATA_ENUM . '::' . $caseName; + + if ($class->hasMethod($testName)) { + $testMethod = $class->getMethod($testName); + } else { + $testMethod = $class->addMethod($testName); + $testMethod->setBody(<<assertSamePipeline({$caseName}, \$pipeline); + PHP); + } + + $testMethod->setPublic(); + $testMethod->setReturnType(Type::Void); + } + + $methods = $class->getMethods(); + ksort($methods); + $class->setMethods($methods); + + return $namespace; + } + + private function convertYamlTaggedValues(mixed $object): mixed + { + if ($object instanceof TaggedValue) { + $value = $object->getValue(); + + return match ($object->getTag()) { + 'bson_regex' => new Regex(...(array) $value), + 'bson_int128' => new Int64($value), + 'bson_decimal128' => new Decimal128($value), + 'bson_utcdatetime' => new UTCDateTime(is_numeric($value) ? $value : new DateTimeImmutable($value)), + 'bson_binary' => new Binary(base64_decode($value)), + 'bson_objectId' => new ObjectId($value), + 'bson_uuid' => new Binary(hex2bin(str_replace('-', '', $value)), Binary::TYPE_UUID), + default => throw new InvalidArgumentException(sprintf('Yaml tag "%s" is not supported.', $object->getTag())), + }; + } + + if (is_array($object)) { + foreach ($object as $key => $value) { + $object[$key] = $this->convertYamlTaggedValues($value); + } + + return $object; + } + + if (is_object($object)) { + $object = clone $object; + foreach (get_object_vars($object) as $key => $value) { + $object->{$key} = $this->convertYamlTaggedValues($value); + } + + return $object; + } + + return $object; + } +} diff --git a/mongo-orchestration/replica_sets/replicaset-old.json b/mongo-orchestration/replica_sets/replicaset-old.json deleted file mode 100644 index 87c81637c..000000000 --- a/mongo-orchestration/replica_sets/replicaset-old.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "id": "REPLICASET_OLD", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3500/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3500/mongod.log", - "journal": true, - "nssize": 1, - "port": 3500, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-OLD-one" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3501/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3501/mongod.log", - "journal": true, - "nssize": 1, - "port": 3501, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "tags": { - "ordinal": "two", - "dc": "nyc" - } - }, - "server_id": "RS-OLD-two" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3502/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3502/mongod.log", - "journal": true, - "nssize": 1, - "port": 3502, - "bind_ip": "::,0.0.0.0", - "smallfiles": true - }, - "rsParams": { - "arbiterOnly": true - - }, - "server_id": "RS-OLD-arbiter" - } - ] -} diff --git a/mongo-orchestration/replica_sets/replicaset-one-node.json b/mongo-orchestration/replica_sets/replicaset-one-node.json deleted file mode 100644 index 8b5bbd045..000000000 --- a/mongo-orchestration/replica_sets/replicaset-one-node.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "id": "REPLICASET_SINGLE", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3020/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3020/mongod.log", - "journal": true, - "port": 3020, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-alone" - } - ] -} diff --git a/mongo-orchestration/replica_sets/replicaset.json b/mongo-orchestration/replica_sets/replicaset.json deleted file mode 100644 index a80de8bbb..000000000 --- a/mongo-orchestration/replica_sets/replicaset.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "id": "REPLICASET", - "name": "mongod", - "members": [ - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3000/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3000/mongod.log", - "journal": true, - "port": 3000, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "one", - "dc": "pa" - } - }, - "server_id": "RS-one" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3001/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3001/mongod.log", - "journal": true, - "port": 3001, - "bind_ip_all": true - }, - "rsParams": { - "tags": { - "ordinal": "two", - "dc": "nyc" - } - }, - "server_id": "RS-two" - }, - { - "procParams": { - "dbpath": "/tmp/REPLICASET/3002/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/REPLICASET/3002/mongod.log", - "journal": true, - "port": 3002, - "bind_ip_all": true - }, - "rsParams": { - "arbiterOnly": true - - }, - "server_id": "RS-arbiter" - } - ] -} diff --git a/mongo-orchestration/sharded_clusters/cluster.json b/mongo-orchestration/sharded_clusters/cluster.json deleted file mode 100644 index 094785a7e..000000000 --- a/mongo-orchestration/sharded_clusters/cluster.json +++ /dev/null @@ -1,86 +0,0 @@ -{ - "configsvrs": [ { - "members" : [ - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4000", - "logpath": "/tmp/SHARDED/CFG/4000/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4000, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4001", - "logpath": "/tmp/SHARDED/CFG/4001/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4001, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED/CFG/4002", - "logpath": "/tmp/SHARDED/CFG/4002/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4002, - "bind_ip_all": true - } - } - ] - } ], - "id": "cluster_1", - "shards": [ - { - "id": "sh01", - "shardParams": { - "procParams": { - "dbpath": "/tmp/SHARDED/SHARD1/4100", - "logpath": "/tmp/SHARDED/SHARD1/4100/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4100, - "bind_ip_all": true - } - } - }, - { - "id": "sh02", - "shardParams": { - "procParams": { - "dbpath": "/tmp/SHARDED/SHARD2/4200", - "logpath": "/tmp/SHARDED/SHARD2/4200/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4200, - "bind_ip_all": true - } - } - } - ], - "routers": [ - { - "logpath": "/tmp/SHARDED/ROUTER/4300/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4300, - "bind_ip_all": true - }, - { - "logpath": "/tmp/SHARDED/ROUTER/4301/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4301, - "bind_ip_all": true - } - ] -} diff --git a/mongo-orchestration/sharded_clusters/cluster_replset.json b/mongo-orchestration/sharded_clusters/cluster_replset.json deleted file mode 100644 index 80cd4c2bd..000000000 --- a/mongo-orchestration/sharded_clusters/cluster_replset.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "configsvrs": [ { - "members" : [ - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4490", - "logpath": "/tmp/SHARDED-RS/CFG/4490/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4490, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4491", - "logpath": "/tmp/SHARDED-RS/CFG/4491/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4491, - "bind_ip_all": true - } - }, - { - "procParams": { - "dbpath": "/tmp/SHARDED-RS/CFG/4492", - "logpath": "/tmp/SHARDED-RS/CFG/4492/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4492, - "bind_ip_all": true - } - } - ] - } ], - "id": "cluster_rs", - "shards": [ - { - "id": "cluster-rs-sh01", - "shardParams": { - "id": "sh01-rs", - "members": [ - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD1/4400", - "logpath": "/tmp/SHARDED-RS/SHARD1/4400/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4400, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } }, - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD1/4401", - "logpath": "/tmp/SHARDED-RS/SHARD1/4401/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4401, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } } - ] - } - }, - { - "id": "cluster-rs-sh02", - "shardParams": { - "id": "sh02-rs", - "members": [ - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD2/4410", - "logpath": "/tmp/SHARDED-RS/SHARD2/4410/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4410, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } }, - { "procParams": { - "dbpath": "/tmp/SHARDED-RS/SHARD2/4411", - "logpath": "/tmp/SHARDED-RS/SHARD2/4411/mongod.log", - "ipv6": true, - "journal": true, - "logappend": true, - "port": 4411, - "bind_ip_all": true, - "setParameter": { - "periodicNoopIntervalSecs": 1, - "writePeriodicNoops": true - } - } } - ] - } - } - ], - "routers": [ - { - "logpath": "/tmp/SHARDED-RS/ROUTER/4430/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4430, - "bind_ip_all": true - }, - { - "logpath": "/tmp/SHARDED-RS/ROUTER/4431/mongod.log", - "ipv6": true, - "logappend": true, - "port": 4431, - "bind_ip_all": true - } - ] -} diff --git a/mongo-orchestration/ssl/ca.pem b/mongo-orchestration/ssl/ca.pem deleted file mode 100644 index 6ac86cfcc..000000000 --- a/mongo-orchestration/ssl/ca.pem +++ /dev/null @@ -1,21 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDfzCCAmegAwIBAgIDB1MGMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIwMjMxMVoXDTM5MDUyMjIwMjMxMVoweTEb -MBkGA1UEAxMSRHJpdmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAw -DgYDVQQKEwdNb25nb0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQI -EwhOZXcgWW9yazELMAkGA1UEBhMCVVMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw -ggEKAoIBAQCl7VN+WsQfHlwapcOpTLZVoeMAl1LTbWTFuXSAavIyy0W1Ytky1UP/ -bxCSW0mSWwCgqoJ5aXbAvrNRp6ArWu3LsTQIEcD3pEdrFIVQhYzWUs9fXqPyI9k+ -QNNQ+MRFKeGteTPYwF2eVEtPzUHU5ws3+OKp1m6MCLkwAG3RBFUAfddUnLvGoZiT -pd8/eNabhgHvdrCw+tYFCWvSjz7SluEVievpQehrSEPKe8DxJq/IM3tSl3tdylzT -zeiKNO7c7LuQrgjAfrZl7n2SriHIlNmqiDR/kdd8+TxBuxjFlcf2WyHCO3lIcIgH -KXTlhUCg50KfHaxHu05Qw0x8869yIzqbAgMBAAGjEDAOMAwGA1UdEwQFMAMBAf8w -DQYJKoZIhvcNAQELBQADggEBAEHuhTL8KQZcKCTSJbYA9MgZj7U32arMGBbc1hiq -VBREwvdVz4+9tIyWMzN9R/YCKmUTnCq8z3wTlC8kBtxYn/l4Tj8nJYcgLJjQ0Fwe -gT564CmvkUat8uXPz6olOCdwkMpJ9Sj62i0mpgXJdBfxKQ6TZ9yGz6m3jannjZpN -LchB7xSAEWtqUgvNusq0dApJsf4n7jZ+oBZVaQw2+tzaMfaLqHgMwcu1FzA8UKCD -sxCgIsZUs8DdxaD418Ot6nPfheOTqe24n+TTa+Z6O0W0QtnofJBx7tmAo1aEc57i -77s89pfwIJetpIlhzNSMKurCAocFCJMJLAASJFuu6dyDvPo= ------END CERTIFICATE----- \ No newline at end of file diff --git a/mongo-orchestration/ssl/client.pem b/mongo-orchestration/ssl/client.pem deleted file mode 100644 index 5b0700109..000000000 --- a/mongo-orchestration/ssl/client.pem +++ /dev/null @@ -1,48 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAsNS8UEuin7/K29jXfIOLpIoh1jEyWVqxiie2Onx7uJJKcoKo -khA3XeUnVN0k6X5MwYWcN52xcns7LYtyt06nRpTG2/emoV44w9uKTuHsvUbiOwSV -m/ToKQQ4FUFZoqorXH+ZmJuIpJNfoW+3CkE1vEDCIecIq6BNg5ySsPtvSuSJHGjp -mc7/5ZUDvFE2aJ8QbJU3Ws0HXiEb6ymi048LlzEL2VKX3w6mqqh+7dcZGAy7qYk2 -5FZ9ktKvCeQau7mTyU1hsPrKFiKtMN8Q2ZAItX13asw5/IeSTq2LgLFHlbj5Kpq4 -GmLdNCshzH5X7Ew3IYM8EHmsX8dmD6mhv7vpVwIDAQABAoIBABOdpb4qhcG+3twA -c/cGCKmaASLnljQ/UU6IFTjrsjXJVKTbRaPeVKX/05sgZQXZ0t3s2mV5AsQ2U1w8 -Cd+3w+qaemzQThW8hAOGCROzEDX29QWi/o2sX0ydgTMqaq0Wv3SlWv6I0mGfT45y -/BURIsrdTCvCmz2erLqa1dL4MWJXRFjT9UTs5twlecIOM2IHKoGGagFhymRK4kDe -wTRC9fpfoAgyfus3pCO/wi/F8yKGPDEwY+zgkhrJQ+kSeki7oKdGD1H540vB8gRt -EIqssE0Y6rEYf97WssQlxJgvoJBDSftOijS6mwvoasDUwfFqyyPiirawXWWhHXkc -DjIi/XECgYEA5xfjilw9YyM2UGQNESbNNunPcj7gDZbN347xJwmYmi9AUdPLt9xN -3XaMqqR22k1DUOxC/5hH0uiXir7mDfqmC+XS/ic/VOsa3CDWejkEnyGLiwSHY502 -wD/xWgHwUiGVAG9HY64vnDGm6L3KGXA2oqxanL4V0+0+Ht49pZ16i8sCgYEAw+Ox -CHGtpkzjCP/z8xr+1VTSdpc/4CP2HONnYopcn48KfQnf7Nale69/1kZpypJlvQSG -eeA3jMGigNJEkb8/kaVoRLCisXcwLc0XIfCTeiK6FS0Ka30D/84Qm8UsHxRdpGkM -kYITAa2r64tgRL8as4/ukeXBKE+oOhX43LeEfyUCgYBkf7IX2Ndlhsm3GlvIarxy -NipeP9PGdR/hKlPbq0OvQf9R1q7QrcE7H7Q6/b0mYNV2mtjkOQB7S2WkFDMOP0P5 -BqDEoKLdNkV/F9TOYH+PCNKbyYNrodJOt0Ap6Y/u1+Xpw3sjcXwJDFrO+sKqX2+T -PStG4S+y84jBedsLbDoAEwKBgQCTz7/KC11o2yOFqv09N+WKvBKDgeWlD/2qFr3w -UU9K5viXGVhqshz0k5z25vL09Drowf1nAZVpFMO2SPOMtq8VC6b+Dfr1xmYIaXVH -Gu1tf77CM9Zk/VSDNc66e7GrUgbHBK2DLo+A+Ld9aRIfTcSsMbNnS+LQtCrQibvb -cG7+MQKBgQCY11oMT2dUekoZEyW4no7W5D74lR8ztMjp/fWWTDo/AZGPBY6cZoZF -IICrzYtDT/5BzB0Jh1f4O9ZQkm5+OvlFbmoZoSbMzHL3oJCBOY5K0/kdGXL46WWh -IRJSYakNU6VIS7SjDpKgm9D8befQqZeoSggSjIIULIiAtYgS80vmGA== ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDgzCCAmugAwIBAgIDAxOUMA0GCSqGSIb3DQEBCwUAMHkxGzAZBgNVBAMTEkRy -aXZlcnMgVGVzdGluZyBDQTEQMA4GA1UECxMHRHJpdmVyczEQMA4GA1UEChMHTW9u -Z29EQjEWMBQGA1UEBxMNTmV3IFlvcmsgQ2l0eTERMA8GA1UECBMITmV3IFlvcmsx -CzAJBgNVBAYTAlVTMB4XDTE5MDUyMjIzNTU1NFoXDTM5MDUyMjIzNTU1NFowaTEP -MA0GA1UEAxMGY2xpZW50MRAwDgYDVQQLEwdEcml2ZXJzMQwwCgYDVQQKEwNNREIx -FjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYD -VQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALDUvFBLop+/ -ytvY13yDi6SKIdYxMllasYontjp8e7iSSnKCqJIQN13lJ1TdJOl+TMGFnDedsXJ7 -Oy2LcrdOp0aUxtv3pqFeOMPbik7h7L1G4jsElZv06CkEOBVBWaKqK1x/mZibiKST -X6FvtwpBNbxAwiHnCKugTYOckrD7b0rkiRxo6ZnO/+WVA7xRNmifEGyVN1rNB14h -G+spotOPC5cxC9lSl98Opqqofu3XGRgMu6mJNuRWfZLSrwnkGru5k8lNYbD6yhYi -rTDfENmQCLV9d2rMOfyHkk6ti4CxR5W4+SqauBpi3TQrIcx+V+xMNyGDPBB5rF/H -Zg+pob+76VcCAwEAAaMkMCIwCwYDVR0PBAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF -BwMCMA0GCSqGSIb3DQEBCwUAA4IBAQAqRcLAGvYMaGYOV4HJTzNotT2qE0I9THNQ -wOV1fBg69x6SrUQTQLjJEptpOA288Wue6Jt3H+p5qAGV5GbXjzN/yjCoItggSKxG -Xg7279nz6/C5faoIKRjpS9R+MsJGlttP9nUzdSxrHvvqm62OuSVFjjETxD39DupE -YPFQoHOxdFTtBQlc/zIKxVdd20rs1xJeeU2/L7jtRBSPuR/Sk8zot7G2/dQHX49y -kHrq8qz12kj1T6XDXf8KZawFywXaz0/Ur+fUYKmkVk1T0JZaNtF4sKqDeNE4zcns -p3xLVDSl1Q5Gwj7bgph9o4Hxs9izPwiqjmNaSjPimGYZ399zcurY ------END CERTIFICATE----- diff --git a/mongo-orchestration/ssl/crl.pem b/mongo-orchestration/ssl/crl.pem deleted file mode 100644 index 733a0acdc..000000000 --- a/mongo-orchestration/ssl/crl.pem +++ /dev/null @@ -1,13 +0,0 @@ ------BEGIN X509 CRL----- -MIIB6jCB0wIBATANBgkqhkiG9w0BAQsFADB5MRswGQYDVQQDExJEcml2ZXJzIFRl -c3RpbmcgQ0ExEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01vbmdvREIxFjAU -BgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3JrMQswCQYDVQQG -EwJVUxcNMTkwNTIyMjI0NTUzWhcNMTkwNjIxMjI0NTUzWjAVMBMCAncVFw0xOTA1 -MjIyMjQ1MzJaoA8wDTALBgNVHRQEBAICEAAwDQYJKoZIhvcNAQELBQADggEBACwQ -W9OF6ExJSzzYbpCRroznkfdLG7ghNSxIpBQUGtcnYbkP4em6TdtAj5K3yBjcKn4a -hnUoa5EJGr2Xgg0QascV/1GuWEJC9rsYYB9boVi95l1CrkS0pseaunM086iItZ4a -hRVza8qEMBc3rdsracA7hElYMKdFTRLpIGciJehXzv40yT5XFBHGy/HIT0CD50O7 -BDOHzA+rCFCvxX8UY9myDfb1r1zUW7Gzjn241VT7bcIJmhFE9oV0popzDyqr6GvP -qB2t5VmFpbnSwkuc4ie8Jizip1P8Hg73lut3oVAHACFGPpfaNIAp4GcSH61zJmff -9UBe3CJ1INwqyiuqGeA= ------END X509 CRL----- diff --git a/mongo-orchestration/ssl/server.pem b/mongo-orchestration/ssl/server.pem deleted file mode 100644 index 7480f9644..000000000 --- a/mongo-orchestration/ssl/server.pem +++ /dev/null @@ -1,49 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAhNrB0E6GY/kFSd8/vNpu/t952tbnOsD5drV0XPvmuy7SgKDY -a/S+xb/jPnlZKKehdBnH7qP/gYbv34ZykzcDFZscjPLiGc2cRGP+NQCSFK0d2/7d -y15zSD3zhj14G8+MkpAejTU+0/qFNZMc5neDvGanTe0+8aWa0DXssM0MuTxIv7j6 -CtsMWeqLLofN7a1Kw2UvmieCHfHMuA/08pJwRnV/+5T9WONBPJja2ZQRrG1BjpI4 -81zSPUZesIqi8yDlExdvgNaRZIEHi/njREqwVgJOZomUY57zmKypiMzbz48dDTsV -gUStxrEqbaP+BEjQYPX5+QQk4GdMjkLf52LR6QIDAQABAoIBAHSs+hHLJNOf2zkp -S3y8CUblVMsQeTpsR6otaehPgi9Zy50TpX4KD5D0GMrBH8BIl86y5Zd7h+VlcDzK -gs0vPxI2izhuBovKuzaE6rf5rFFkSBjxGDCG3o/PeJOoYFdsS3RcBbjVzju0hFCs -xnDQ/Wz0anJRrTnjyraY5SnQqx/xuhLXkj/lwWoWjP2bUqDprnuLOj16soNu60Um -JziWbmWx9ty0wohkI/8DPBl9FjSniEEUi9pnZXPElFN6kwPkgdfT5rY/TkMH4lsu -ozOUc5xgwlkT6kVjXHcs3fleuT/mOfVXLPgNms85JKLucfd6KiV7jYZkT/bXIjQ+ -7CZEn0ECgYEA5QiKZgsfJjWvZpt21V/i7dPje2xdwHtZ8F9NjX7ZUFA7mUPxUlwe -GiXxmy6RGzNdnLOto4SF0/7ebuF3koO77oLup5a2etL+y/AnNAufbu4S5D72sbiz -wdLzr3d5JQ12xeaEH6kQNk2SD5/ShctdS6GmTgQPiJIgH0MIdi9F3v0CgYEAlH84 -hMWcC+5b4hHUEexeNkT8kCXwHVcUjGRaYFdSHgovvWllApZDHSWZ+vRcMBdlhNPu -09Btxo99cjOZwGYJyt20QQLGc/ZyiOF4ximQzabTeFgLkTH3Ox6Mh2Rx9yIruYoX -nE3UfMDkYELanEJUv0zenKpZHw7tTt5yXXSlEF0CgYBSsEOvVcKYO/eoluZPYQAA -F2jgzZ4HeUFebDoGpM52lZD+463Dq2hezmYtPaG77U6V3bUJ/TWH9VN/Or290vvN -v83ECcC2FWlSXdD5lFyqYx/E8gqE3YdgqfW62uqM+xBvoKsA9zvYLydVpsEN9v8m -6CSvs/2btA4O21e5u5WBTQKBgGtAb6vFpe0gHRDs24SOeYUs0lWycPhf+qFjobrP -lqnHpa9iPeheat7UV6BfeW3qmBIVl/s4IPE2ld4z0qqZiB0Tf6ssu/TpXNPsNXS6 -dLFz+myC+ufFdNEoQUtQitd5wKbjTCZCOGRaVRgJcSdG6Tq55Fa22mOKPm+mTmed -ZdKpAoGAFsTYBAHPxs8nzkCJCl7KLa4/zgbgywO6EcQgA7tfelB8bc8vcAMG5o+8 -YqAfwxrzhVSVbJx0fibTARXROmbh2pn010l2wj3+qUajM8NiskCPFbSjGy7HSUze -P8Kt1uMDJdj55gATzn44au31QBioZY2zXleorxF21cr+BZCJgfA= ------END RSA PRIVATE KEY----- ------BEGIN CERTIFICATE----- -MIIDlTCCAn2gAwIBAgICdxUwDQYJKoZIhvcNAQELBQAweTEbMBkGA1UEAxMSRHJp -dmVycyBUZXN0aW5nIENBMRAwDgYDVQQLEwdEcml2ZXJzMRAwDgYDVQQKEwdNb25n -b0RCMRYwFAYDVQQHEw1OZXcgWW9yayBDaXR5MREwDwYDVQQIEwhOZXcgWW9yazEL -MAkGA1UEBhMCVVMwHhcNMTkwNTIyMjIzMjU2WhcNMzkwNTIyMjIzMjU2WjBwMRIw -EAYDVQQDEwlsb2NhbGhvc3QxEDAOBgNVBAsTB0RyaXZlcnMxEDAOBgNVBAoTB01v -bmdvREIxFjAUBgNVBAcTDU5ldyBZb3JrIENpdHkxETAPBgNVBAgTCE5ldyBZb3Jr -MQswCQYDVQQGEwJVUzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAITa -wdBOhmP5BUnfP7zabv7fedrW5zrA+Xa1dFz75rsu0oCg2Gv0vsW/4z55WSinoXQZ -x+6j/4GG79+GcpM3AxWbHIzy4hnNnERj/jUAkhStHdv+3ctec0g984Y9eBvPjJKQ -Ho01PtP6hTWTHOZ3g7xmp03tPvGlmtA17LDNDLk8SL+4+grbDFnqiy6Hze2tSsNl -L5ongh3xzLgP9PKScEZ1f/uU/VjjQTyY2tmUEaxtQY6SOPNc0j1GXrCKovMg5RMX -b4DWkWSBB4v540RKsFYCTmaJlGOe85isqYjM28+PHQ07FYFErcaxKm2j/gRI0GD1 -+fkEJOBnTI5C3+di0ekCAwEAAaMwMC4wLAYDVR0RBCUwI4IJbG9jYWxob3N0hwR/ -AAABhxAAAAAAAAAAAAAAAAAAAAABMA0GCSqGSIb3DQEBCwUAA4IBAQBol8+YH7MA -HwnIh7KcJ8h87GkCWsjOJCDJWiYBJArQ0MmgDO0qdx+QEtvLMn3XNtP05ZfK0WyX -or4cWllAkMFYaFbyB2hYazlD1UAAG+22Rku0UP6pJMLbWe6pnqzx+RL68FYdbZhN -fCW2xiiKsdPoo2VEY7eeZKrNr/0RFE5EKXgzmobpTBQT1Dl3Ve4aWLoTy9INlQ/g -z40qS7oq1PjjPLgxINhf4ncJqfmRXugYTOnyFiVXLZTys5Pb9SMKdToGl3NTYWLL -2AZdjr6bKtT+WtXyHqO0cQ8CkAW0M6VOlMluACllcJxfrtdlQS2S4lUIj76QKBdZ -khBHXq/b8MFX ------END CERTIFICATE----- diff --git a/mongo-orchestration/standalone/standalone-auth.json b/mongo-orchestration/standalone/standalone-auth.json deleted file mode 100644 index b15a68566..000000000 --- a/mongo-orchestration/standalone/standalone-auth.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE_AUTH", - "auth_key": "secret", - "login": "root", - "password": "toor", - "procParams": { - "dbpath": "/tmp/standalone-auth/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone-auth/m.log", - "journal": true, - "port": 2200, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/mongo-orchestration/standalone/standalone-old.json b/mongo-orchestration/standalone/standalone-old.json deleted file mode 100644 index e89f013b8..000000000 --- a/mongo-orchestration/standalone/standalone-old.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE", - "procParams": { - "dbpath": "/tmp/standalone/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone/mongod.log", - "journal": true, - "nssize": 1, - "port": 2700, - "bind_ip": "::,0.0.0.0", - "smallfiles": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/mongo-orchestration/standalone/standalone-ssl.json b/mongo-orchestration/standalone/standalone-ssl.json deleted file mode 100644 index f843589e6..000000000 --- a/mongo-orchestration/standalone/standalone-ssl.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE_SSL", - "procParams": { - "dbpath": "/tmp/standalone-ssl/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone-ssl/m.log", - "journal": true, - "port": 2100, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - }, - "sslParams": { - "sslMode": "requireSSL", - "sslCAFile": "$TRAVIS_BUILD_DIR/mongo-orchestration/ssl/ca.pem", - "sslPEMKeyFile": "$TRAVIS_BUILD_DIR/mongo-orchestration/ssl/server.pem", - "sslWeakCertificateValidation": true, - "sslAllowInvalidHostnames": true - } -} diff --git a/mongo-orchestration/standalone/standalone.json b/mongo-orchestration/standalone/standalone.json deleted file mode 100644 index 4cb451114..000000000 --- a/mongo-orchestration/standalone/standalone.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "name": "mongod", - "id" : "STANDALONE", - "procParams": { - "dbpath": "/tmp/standalone/", - "ipv6": true, - "logappend": true, - "logpath": "/tmp/standalone/mongod.log", - "journal": true, - "port": 2000, - "bind_ip_all": true, - "setParameter": {"enableTestCommands": 1} - } -} diff --git a/phpcs.xml.dist b/phpcs.xml.dist index a2d56ccce..fe0dc3819 100644 --- a/phpcs.xml.dist +++ b/phpcs.xml.dist @@ -9,26 +9,25 @@ + benchmark/src src examples + generator/src + generator/config tests tools + rector.php + + + src/Builder/(Accumulator|Expression|Query|Projection|Search|Stage)/*\.php + + + - - - - - - - - - - - @@ -46,6 +45,7 @@ + @@ -69,7 +69,6 @@ - @@ -84,9 +83,13 @@ - + + + + + + - @@ -102,43 +105,16 @@ - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -157,17 +133,6 @@ - - - - - src - - - src - - - @@ -175,6 +140,7 @@ /src/GridFS/StreamWrapper /tests/DocumentationExamplesTest.php /tests/GridFS/UnusableStream.php + /tests/SpecTests/ClientSideEncryption/Prose* /examples @@ -183,4 +149,10 @@ /examples + + /tests/SpecTests/*/Prose* + + + src/Builder/Type/OperatorInterface.php + diff --git a/phpunit.evergreen.xml b/phpunit.evergreen.xml index b157e92c4..42a1bfb41 100644 --- a/phpunit.evergreen.xml +++ b/phpunit.evergreen.xml @@ -6,13 +6,15 @@ beStrictAboutOutputDuringTests="true" beStrictAboutChangesToGlobalState="true" colors="true" - bootstrap="vendor/autoload.php" + bootstrap="tests/bootstrap.php" defaultTestSuite="Default Test Suite" > + + @@ -21,13 +23,15 @@ ./tests/ - - - tests/SpecTests/AtlasDataLakeSpecTest.php - + + + src + + + - + diff --git a/phpunit.xml.dist b/phpunit.xml.dist index d5ec557d1..b3b46625b 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -6,12 +6,14 @@ beStrictAboutOutputDuringTests="true" beStrictAboutChangesToGlobalState="true" colors="true" - bootstrap="vendor/autoload.php" + bootstrap="tests/bootstrap.php" defaultTestSuite="Default Test Suite" > + + @@ -20,9 +22,5 @@ ./tests/ - - - tests/SpecTests/AtlasDataLakeSpecTest.php - diff --git a/psalm-baseline.xml b/psalm-baseline.xml index 276f9370e..4d99d5c0c 100644 --- a/psalm-baseline.xml +++ b/psalm-baseline.xml @@ -1,873 +1,887 @@ - - - - string - - - toRelaxedExtendedJSON(fromPHP($document)) - - - - - string - - - toRelaxedExtendedJSON(fromPHP($document)) + + + + + name]]> + + + + + + + + name]]> + name]]> + name]]> + queryable]]> + + + + + + + + + + + $this->id, + 'name' => $this->name, + 'emails' => $this->emails, + ]]]> + $this->type, + 'address' => $this->address, + ]]]> + + + + + + + + + encode($value)]]> - - - string - - - toRelaxedExtendedJSON(fromPHP($document)) - + + + + + - - - string - - - toRelaxedExtendedJSON(fromPHP($document)) - + + + name]]> + - - - $address - $emails - $id - $name - $type - - - - - string - - - toRelaxedExtendedJSON(fromPHP($document)) - + + + + + + + + + + + + + + + + + + + - - - $driverOptions['driver'] ?? [] + + + - - $mergedDriver['platform'] + + + + + - - - $encryptedFields['eccCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecc' - $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecoc' - $encryptedFields['escCollection'] ?? 'enxcol_.' . $this->collectionName . '.esc' + + + recursiveEncode($value)]]> - - $encryptedFields + + + - - - $cmd[$option] - $options['session'] + + + + + - - - $cmd[$option] - $options['session'] - + + + + - - - $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc' - $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc' - $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc' - $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc' - $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc' - $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc' - - - $encryptedFields - $encryptedFields - $options['encryptedFields'] - - - - - new static(sprintf('%s is immutable', $class)) - new static(sprintf('%s should not be called for an unacknowledged write result', $method)) - - - - - new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value))) - - - - - new static('Resume token not found in change document') - new static(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value))) - - - - - new static('Array filters are not supported by the server executing this operation') - new static('Collations are not supported by the server executing this operation') - new static('Explain is not supported by the server executing this operation') - new static('Hint is not supported by the server executing this operation') - new static('Read concern is not supported by the server executing this command') - new static('The "allowDiskUse" option is not supported by the server executing this operation') - new static('The "commitQuorum" option is not supported by the server executing this operation') - new static('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.') - new static('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.') - new static('Write concern is not supported by the server executing this command') - + + + + - - - ! is_resource($destination) - ! is_resource($destination) - ! is_resource($source) - ! is_resource($stream) - - - delete - downloadToStream - downloadToStreamByName - drop - rename - - - $id - $options['revision'] - $options['revision'] - - - $id - + + + + + + + + + + + + + + + + + + + + - - - new static(sprintf('Chunk not found for index "%d"', $expectedIndex)) - new static(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index)) - new static(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size)) - new static(sprintf('Invalid data found for index "%d"', $chunkIndex)) - + + + + - - - $json - - - $json - - - new static(sprintf('File "%s" not found in "%s"', $json, $namespace)) - new static(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace)) - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - $idString + + + + - - $idString + + + + + - - new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $filename)) - new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)) - new static(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename)) - - - - $currentChunk->n - $this->file->length + + + + + + + + + + + + + 0]]> + + + + + + - - - $context[$this->protocol]['collectionWrapper'] - $context[$this->protocol]['collectionWrapper'] - $context[$this->protocol]['file'] - $context[$this->protocol]['filename'] - $context[$this->protocol]['options'] + + + + - - $context[$this->protocol]['collectionWrapper'] - $context[$this->protocol]['collectionWrapper'] - $context[$this->protocol]['file'] - $context[$this->protocol]['filename'] - $context[$this->protocol]['options'] - - - stream_wrapper_unregister - - - - - hash_update - - - - - Traversable - - - call_user_func($this->getIterator) - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - $this[$key] - - - $key - $this[$key] - $value - - - new static() - + + + + + + + + + + + + + + + + + - - - $iteratorClass - - - $this[$key] - - - $key - $this[$key] - $value + + + + - - new static() - - - - $documentLength - $documentLength - $this->options['typeMap'] - - - $this->position + + + + - - $documentLength - - - - $currentItem[self::FIELD_KEY] - $currentItem[self::FIELD_VALUE] - - - $currentItem - $currentItem - + + + + + + + + + + + + + + + + + - - - ! is_array($document) && ! is_object($document) - isset($initialResumeToken) && ! is_array($initialResumeToken) && ! is_object($initialResumeToken) - - - $reply->cursor->nextBatch - $this->current() + + + + - - $this->postBatchResumeToken + + + + + + + - - $reply->cursor->nextBatch - $reply->cursor->postBatchResumeToken - - - addSubscriber - removeSubscriber - - - - - $key - - - $this->info[$key] - - - - $info + + + n]]> + file->length]]> - - $info['idIndex']['ns'] - - - $info + + + + + + + getContext($path, $mode)]]> + getContext($path, $mode)]]> + + + + + + + + + + + + + + + - - $info['name'] - - - - $key - - - $this->info[$key] - + + + >|class-string>]]> + + + getArrayCopy()]]> + getArrayCopy()]]> + + + + + + + + + - - - current($this->databases) - - - int - key($this->databases) - - - - - $key + + + options['typeMap']]]> - - $this->info[$key] - - - - $info - $info - - - $info['ns'] - - - $info - + + + + + + ]]> + + + ]]> + + + cursor->nextBatch]]> + + + + postBatchResumeToken]]> + + + cursor->nextBatch]]> + cursor->postBatchResumeToken]]> + + + current()]]> + - - $fieldName - - - $fieldName - - - $this->index['name'] + + index]]> + + + + + + + + + + + + + + index['name']]]> + + + + + + + index]]> + + + + - - $this->options['typeMap'] - $this->options['typeMap'] - - - $cmdOptions['maxAwaitTimeMS'] - $cmd[$option] - $cmd['hint'] - $options[$option] - $options['writeConcern'] - - - isDefault - isDefault - isInTransaction + + options['codec']]]> + options['typeMap']]]> + + + + + + + + + + - - is_array($operation) - - - $args - $args - $args - $args[0] - $args[0] - $args[0] - $args[1] - $args[1] - $args[1] - $args[1] - $args[2] - - - $args[0] - $args[0] - $args[0] - $args[0] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[1] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2]['upsert'] - $args[2]['upsert'] - $args[2]['upsert'] - $args[2]['upsert'] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - $args[1] - $args[1] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2] - $args[2] - $operations[$i][$type][1] - $operations[$i][$type][2] - $operations[$i][$type][2] + + + + + + + + + + + + + + + + + + + + + + + - - $args - $args - $args[2] - $args[2] - $insertedIds[$i] - $operations[$i][$type][1] - $operations[$i][$type][2] - $operations[$i][$type][2] - $options[$option] - $options['session'] - $options['writeConcern'] - - - isDefault - isInTransaction + + + + + + + + + + + + + + + + + + + + - - $args[2] - $args[2] + + + - - - ! is_array($filter) && ! is_object($filter) - - - $cmd[$option] - $cmd['hint'] - $options['readConcern'] - $options['readPreference'] - $options['session'] - - - isInTransaction + + + + + executeBulkWriteCommand($this->bulkWriteCommand, $options)]]> + - - - ! is_array($filter) && ! is_object($filter) - + + + + + + + + + + + + - - $i - $i - $this->options['typeMap'] - - - $cmd[$option] - $options['session'] - $options['writeConcern'] + + + + + + + options['encryptedFields']]]> + options['encryptedFields']]]> + + - - is_array($index) - - - $cmd[$option] - $cmd['commitQuorum'] - $options['session'] - $options['writeConcern'] - - - isInTransaction + + + + + + + + + + + + + + + + - - ! is_array($command) && ! is_object($command) - - - $this->options['typeMap'] + + options['typeMap']]]> - - $options['readPreference'] - $options['session'] + + + - - ! is_array($filter) && ! is_object($filter) - - - $this->options['writeConcern'] - - - $cmd['writeConcern'] - $deleteOptions['hint'] - $options['comment'] - $options['session'] - $options['writeConcern'] - - - isInTransaction + + options['writeConcern']]]> + + + + + + + + + + - - ! is_array($filter) && ! is_object($filter) - - - $this->options['typeMap'] - - - $cmd[$option] - $options['readConcern'] - $options['readPreference'] - $options['session'] - - - isInTransaction + + options['typeMap']]]> + + + + + + + + + + - - $this->options['typeMap'] - - - $cmd['comment'] - $options['session'] - $options['writeConcern'] + + + + - - isInTransaction + + - - $this->options['typeMap'] - - - $cmd['comment'] - $options['session'] - $options['writeConcern'] + + + + - - - $this->options['typeMap'] + + + - - $cmd[$option] - $options['session'] - $options['writeConcern'] + + + + + + - - isInTransaction + + + + + + + - - $this->options['typeMap'] + + options['typeMap']]]> - - $cmd[$option] - $options['readPreference'] - $options['session'] + + + + - - ! is_array($filter) && ! is_object($filter) - - - $this->options['typeMap'] + + options['codec']]]> + options['typeMap']]]> - - $options['modifiers'][$modifier[1]] - - - $options[$modifier[0]] - $options[$option] - $options['readPreference'] - $options['session'] - - - isInTransaction + + + + + + + - - $this->options['typeMap'] - $this->options['writeConcern'] - - - $cmd[$option] - $cmd['new'] - $cmd['update'] - $cmd['upsert'] - $options['session'] - $options['writeConcern'] - - - array|object|null - - - isDefault - isInTransaction + + options['typeMap']]]> + options['writeConcern']]]> + + + + + + + + + + + + - - is_object($result) ? ($result->value ?? null) : null + + options['codec']->decode($value)]]> + options['codec']->decode($value)]]> + value ?? null) : null]]> + value ?? null) : null]]> - - ! is_array($filter) && ! is_object($filter) - + + + - - ! is_array($filter) && ! is_object($filter) - ! is_array($replacement) && ! is_object($replacement) - - - $options['returnDocument'] - + + + + + + + + + - - ! is_array($filter) && ! is_object($filter) - ! is_array($update) && ! is_object($update) - - - $options['returnDocument'] - + + + - - ! is_array($document) && ! is_object($document) - - - $insertedIds[$i] - $options[$option] - $options['session'] - $options['writeConcern'] - - - isDefault - isInTransaction + + + + + + + + + + + - - ! is_array($document) && ! is_object($document) - - - $insertedId - $options[$option] - $options['session'] - $options['writeConcern'] - - - isDefault - isInTransaction + + + + + + + + - - - - function (array $collectionInfo) { - + + + - - $cmd[$option] - $options['session'] - - - - - ! is_string($out) && ! is_array($out) && ! is_object($out) - - - $result->result->collection - $result->result->db - $this->options['typeMap'] - - - $cmd[$option] - $options['readConcern'] - $options['readPreference'] - $options['session'] - $options['writeConcern'] - - - getScope - isDefault - isDefault - isInTransaction - + + + + - - $this->options['typeMap'] + + options['typeMap']]]> - - $cmd['comment'] - $options['session'] - $options['writeConcern'] + + + + - - $this->options['typeMap'] - - - $cmd[$option] - $options['session'] - $options['writeConcern'] + + + + - - isInTransaction + + - - ! is_array($replacement) && ! is_object($replacement) - + + + - - ! is_array($filter) && ! is_object($filter) - ! is_array($update) && ! is_object($update) - - - $this->options['writeConcern'] - - - $cmd['bypassDocumentValidation'] - $cmd['writeConcern'] - $options[$option] - $options['session'] - $options['writeConcern'] - $updateOptions[$option] - - - isDefault - isInTransaction + + options['writeConcern']]]> + + + + + + + + + + - - - ! is_array($update) && ! is_object($update) - - - - - ! is_array($update) && ! is_object($update) - + + + + - - isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken) - - - $reply->cursor->firstBatch + + cursor->firstBatch]]> - - $this->postBatchResumeToken - - - array|object|null - - - $reply->cursor->firstBatch - $reply->cursor->postBatchResumeToken + + postBatchResumeToken]]> + + + cursor->firstBatch]]> + cursor->postBatchResumeToken]]> - - $this->changeStreamOptions['resumeAfter'] - $this->changeStreamOptions['startAfter'] + + changeStreamOptions['resumeAfter']]]> + changeStreamOptions['startAfter']]]> - - $firstBatchSize - $operationTime - - - $resumeToken === null && $this->operationTime !== null - $this->operationTime !== null - - - addSubscriber - removeSubscriber - - - $id + + - - ! is_array($document) && ! is_object($document) - is_array($document) - is_array($document) - is_array($out) - - - $wireVersionForWriteStageOnSecondary + + + - - $manager->getServers() + + getServers()]]> - - $collectionInfo['options']['encryptedFields'] + + - - $typeMap['fieldPaths'][$fieldPath] - $typeMap['fieldPaths'][substr($fieldPath, 0, -2)] + + + - - $element[$key] - $lastOp - $type - $type - $typeMap['fieldPaths'][$fieldPath . '.' . $existingFieldPath] - $typeMap['fieldPaths'][$fieldPath] - $value - - - array|object - array|object|null - array|object|null - - - $type - - - $collectionInfo['options']['encryptedFields'] ?? null - $encryptedFieldsMap[$databaseName . '.' . $collectionName] ?? null - toPHP(fromPHP($document), $typeMap) + + + + + + + + + + + + + + diff --git a/psalm.xml.dist b/psalm.xml.dist index 5a922b4ca..28efbec86 100644 --- a/psalm.xml.dist +++ b/psalm.xml.dist @@ -2,7 +2,11 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/rector.php b/rector.php new file mode 100644 index 000000000..61fe76b6d --- /dev/null +++ b/rector.php @@ -0,0 +1,50 @@ +withPaths([ + __DIR__ . '/examples', + __DIR__ . '/src', + __DIR__ . '/tests', + __DIR__ . '/tools', + ]) + ->withPhpSets(php80: true) + ->withComposerBased(phpunit: true) + // All classes are public API by default, unless marked with @internal. + ->withConfiguredRule(RemoveAnnotationRector::class, ['api']) + // Fix PHP 8.5 deprecations + ->withConfiguredRule( + RenameCastRector::class, + [ + new RenameCast(Int_::class, Int_::KIND_INTEGER, Int_::KIND_INT), + new RenameCast(Bool_::class, Bool_::KIND_BOOLEAN, Bool_::KIND_BOOL), + new RenameCast(Double::class, Double::KIND_DOUBLE, Double::KIND_FLOAT), + ], + ) + // phpcs:disable Squiz.Arrays.ArrayDeclaration.KeySpecified + ->withSkip([ + RemoveExtraParametersRector::class, + // Do not use ternaries extensively + IfIssetToCoalescingRector::class, + ChangeSwitchToMatchRector::class => [ + __DIR__ . '/tests/SpecTests/Operation.php', + ], + ClassPropertyAssignToConstructorPromotionRector::class, + StringableForToStringRector::class => [ + __DIR__ . '/src/Model/IndexInput.php', + ], + ]) + // phpcs:enable + ->withImportNames(importNames: false, removeUnusedImports: true); diff --git a/sbom.json b/sbom.json new file mode 100644 index 000000000..4dce69b3b --- /dev/null +++ b/sbom.json @@ -0,0 +1,85 @@ +{ + "$schema": "http://cyclonedx.org/schema/bom-1.5.schema.json", + "bomFormat": "CycloneDX", + "specVersion": "1.5", + "serialNumber": "urn:uuid:dc42a43b-4ace-4c42-9a6e-0b9e28fdd100", + "version": 1, + "metadata": { + "timestamp": "2024-05-08T09:51:01Z", + "tools": [ + { + "name": "composer", + "version": "2.7.6" + }, + { + "vendor": "cyclonedx", + "name": "cyclonedx-php-composer", + "version": "v5.2.0", + "externalReferences": [ + { + "type": "distribution", + "url": "https://api.github.com/repos/CycloneDX/cyclonedx-php-composer/zipball/f3a3cdc1a9e34bf1d5748e4279a24569cbf31fed", + "comment": "dist reference: f3a3cdc1a9e34bf1d5748e4279a24569cbf31fed" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-php-composer.git", + "comment": "source reference: f3a3cdc1a9e34bf1d5748e4279a24569cbf31fed" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-php-composer/#readme", + "comment": "as detected from Composer manifest 'homepage'" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-php-composer/issues", + "comment": "as detected from Composer manifest 'support.issues'" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-php-composer/", + "comment": "as detected from Composer manifest 'support.source'" + } + ] + }, + { + "vendor": "cyclonedx", + "name": "cyclonedx-library", + "version": "v3.3.1", + "externalReferences": [ + { + "type": "distribution", + "url": "https://api.github.com/repos/CycloneDX/cyclonedx-php-library/zipball/cad0f92b36c85f36b3d3c11ff96002af5f20cd10", + "comment": "dist reference: cad0f92b36c85f36b3d3c11ff96002af5f20cd10" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-php-library.git", + "comment": "source reference: cad0f92b36c85f36b3d3c11ff96002af5f20cd10" + }, + { + "type": "website", + "url": "https://github.com/CycloneDX/cyclonedx-php-library/#readme", + "comment": "as detected from Composer manifest 'homepage'" + }, + { + "type": "documentation", + "url": "https://cyclonedx-php-library.readthedocs.io", + "comment": "as detected from Composer manifest 'support.docs'" + }, + { + "type": "issue-tracker", + "url": "https://github.com/CycloneDX/cyclonedx-php-library/issues", + "comment": "as detected from Composer manifest 'support.issues'" + }, + { + "type": "vcs", + "url": "https://github.com/CycloneDX/cyclonedx-php-library/", + "comment": "as detected from Composer manifest 'support.source'" + } + ] + } + ] + } +} diff --git a/src/Builder/Accumulator.php b/src/Builder/Accumulator.php new file mode 100644 index 000000000..75d460bd1 --- /dev/null +++ b/src/Builder/Accumulator.php @@ -0,0 +1,44 @@ +|stdClass $operator Window operator to use in the $setWindowFields stage. + * @param Optional|array{string|int,string|int} $documents A window where the lower and upper boundaries are specified relative to the position of the current document read from the collection. + * @param Optional|array{string|numeric,string|numeric} $range Arguments passed to the init function. + * @param Optional|non-empty-string $unit Specifies the units for time range window boundaries. If omitted, default numeric range window boundaries are used. + */ + public static function outputWindow( + Document|Serializable|WindowInterface|stdClass|array $operator, + Optional|array $documents = Optional::Undefined, + Optional|array $range = Optional::Undefined, + Optional|TimeUnit|string $unit = Optional::Undefined, + ): OutputWindow { + return new OutputWindow($operator, $documents, $range, $unit); + } + + private function __construct() + { + // This class cannot be instantiated + } +} diff --git a/src/Builder/Accumulator/AccumulatorAccumulator.php b/src/Builder/Accumulator/AccumulatorAccumulator.php new file mode 100644 index 000000000..f26e5f7da --- /dev/null +++ b/src/Builder/Accumulator/AccumulatorAccumulator.php @@ -0,0 +1,127 @@ + 'init', + 'accumulate' => 'accumulate', + 'accumulateArgs' => 'accumulateArgs', + 'merge' => 'merge', + 'lang' => 'lang', + 'initArgs' => 'initArgs', + 'finalize' => 'finalize', + ]; + + /** @var Javascript|string $init Function used to initialize the state. The init function receives its arguments from the initArgs array expression. You can specify the function definition as either BSON type Code or String. */ + public readonly Javascript|string $init; + + /** @var Javascript|string $accumulate Function used to accumulate documents. The accumulate function receives its arguments from the current state and accumulateArgs array expression. The result of the accumulate function becomes the new state. You can specify the function definition as either BSON type Code or String. */ + public readonly Javascript|string $accumulate; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $accumulateArgs Arguments passed to the accumulate function. You can use accumulateArgs to specify what field value(s) to pass to the accumulate function. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $accumulateArgs; + + /** @var Javascript|string $merge Function used to merge two internal states. merge must be either a String or Code BSON type. merge returns the combined result of the two merged states. For information on when the merge function is called, see Merge Two States with $merge. */ + public readonly Javascript|string $merge; + + /** @var string $lang The language used in the $accumulator code. */ + public readonly string $lang; + + /** @var Optional|BSONArray|PackedArray|ResolvesToArray|array|string $initArgs Arguments passed to the init function. */ + public readonly Optional|PackedArray|ResolvesToArray|BSONArray|array|string $initArgs; + + /** @var Optional|Javascript|string $finalize Function used to update the result of the accumulation. */ + public readonly Optional|Javascript|string $finalize; + + /** + * @param Javascript|string $init Function used to initialize the state. The init function receives its arguments from the initArgs array expression. You can specify the function definition as either BSON type Code or String. + * @param Javascript|string $accumulate Function used to accumulate documents. The accumulate function receives its arguments from the current state and accumulateArgs array expression. The result of the accumulate function becomes the new state. You can specify the function definition as either BSON type Code or String. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $accumulateArgs Arguments passed to the accumulate function. You can use accumulateArgs to specify what field value(s) to pass to the accumulate function. + * @param Javascript|string $merge Function used to merge two internal states. merge must be either a String or Code BSON type. merge returns the combined result of the two merged states. For information on when the merge function is called, see Merge Two States with $merge. + * @param string $lang The language used in the $accumulator code. + * @param Optional|BSONArray|PackedArray|ResolvesToArray|array|string $initArgs Arguments passed to the init function. + * @param Optional|Javascript|string $finalize Function used to update the result of the accumulation. + */ + public function __construct( + Javascript|string $init, + Javascript|string $accumulate, + PackedArray|ResolvesToArray|BSONArray|array|string $accumulateArgs, + Javascript|string $merge, + string $lang, + Optional|PackedArray|ResolvesToArray|BSONArray|array|string $initArgs = Optional::Undefined, + Optional|Javascript|string $finalize = Optional::Undefined, + ) { + if (is_string($init)) { + $init = new Javascript($init); + } + + $this->init = $init; + if (is_string($accumulate)) { + $accumulate = new Javascript($accumulate); + } + + $this->accumulate = $accumulate; + if (is_string($accumulateArgs) && ! str_starts_with($accumulateArgs, '$')) { + throw new InvalidArgumentException('Argument $accumulateArgs can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($accumulateArgs) && ! array_is_list($accumulateArgs)) { + throw new InvalidArgumentException('Expected $accumulateArgs argument to be a list, got an associative array.'); + } + + $this->accumulateArgs = $accumulateArgs; + if (is_string($merge)) { + $merge = new Javascript($merge); + } + + $this->merge = $merge; + $this->lang = $lang; + if (is_string($initArgs) && ! str_starts_with($initArgs, '$')) { + throw new InvalidArgumentException('Argument $initArgs can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($initArgs) && ! array_is_list($initArgs)) { + throw new InvalidArgumentException('Expected $initArgs argument to be a list, got an associative array.'); + } + + $this->initArgs = $initArgs; + if (is_string($finalize)) { + $finalize = new Javascript($finalize); + } + + $this->finalize = $finalize; + } +} diff --git a/src/Builder/Accumulator/AddToSetAccumulator.php b/src/Builder/Accumulator/AddToSetAccumulator.php new file mode 100644 index 000000000..7e29eed62 --- /dev/null +++ b/src/Builder/Accumulator/AddToSetAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/AvgAccumulator.php b/src/Builder/Accumulator/AvgAccumulator.php new file mode 100644 index 000000000..afbb1747b --- /dev/null +++ b/src/Builder/Accumulator/AvgAccumulator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/BottomAccumulator.php b/src/Builder/Accumulator/BottomAccumulator.php new file mode 100644 index 000000000..8d4dd417a --- /dev/null +++ b/src/Builder/Accumulator/BottomAccumulator.php @@ -0,0 +1,52 @@ + 'sortBy', 'output' => 'output']; + + /** @var Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. */ + public readonly Document|Serializable|stdClass|array $sortBy; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output; + + /** + * @param Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. + */ + public function __construct( + Document|Serializable|stdClass|array $sortBy, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output, + ) { + $this->sortBy = $sortBy; + $this->output = $output; + } +} diff --git a/src/Builder/Accumulator/BottomNAccumulator.php b/src/Builder/Accumulator/BottomNAccumulator.php new file mode 100644 index 000000000..95f0d7555 --- /dev/null +++ b/src/Builder/Accumulator/BottomNAccumulator.php @@ -0,0 +1,68 @@ + 'n', 'sortBy' => 'sortBy', 'output' => 'output']; + + /** @var ResolvesToInt|int|string $n Limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. */ + public readonly ResolvesToInt|int|string $n; + + /** @var Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. */ + public readonly Document|Serializable|stdClass|array $sortBy; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output; + + /** + * @param ResolvesToInt|int|string $n Limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. + * @param Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. + */ + public function __construct( + ResolvesToInt|int|string $n, + Document|Serializable|stdClass|array $sortBy, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output, + ) { + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + $this->sortBy = $sortBy; + $this->output = $output; + } +} diff --git a/src/Builder/Accumulator/ConcatArraysAccumulator.php b/src/Builder/Accumulator/ConcatArraysAccumulator.php new file mode 100644 index 000000000..90ab96fa9 --- /dev/null +++ b/src/Builder/Accumulator/ConcatArraysAccumulator.php @@ -0,0 +1,57 @@ + 'array']; + + /** + * @var list $array An array of expressions that resolve to an array. + * If any argument resolves to a value of null or refers to a field that is missing, `$concatArrays` returns `null`. + */ + public readonly array $array; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$array An array of expressions that resolve to an array. + * If any argument resolves to a value of null or refers to a field that is missing, `$concatArrays` returns `null`. + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$array) + { + if (\count($array) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $array, got %d.', 1, \count($array))); + } + + if (! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array arguments to be a list (array), named arguments are not supported'); + } + + $this->array = $array; + } +} diff --git a/src/Builder/Accumulator/CountAccumulator.php b/src/Builder/Accumulator/CountAccumulator.php new file mode 100644 index 000000000..933d86c6d --- /dev/null +++ b/src/Builder/Accumulator/CountAccumulator.php @@ -0,0 +1,32 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression1 */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression1; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression2 */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression2; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression1 + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression2 + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $expression1, + Decimal128|Int64|ResolvesToNumber|float|int|string $expression2, + ) { + if (is_string($expression1) && ! str_starts_with($expression1, '$')) { + throw new InvalidArgumentException('Argument $expression1 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression1 = $expression1; + if (is_string($expression2) && ! str_starts_with($expression2, '$')) { + throw new InvalidArgumentException('Argument $expression2 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Accumulator/CovarianceSampAccumulator.php b/src/Builder/Accumulator/CovarianceSampAccumulator.php new file mode 100644 index 000000000..eacd66315 --- /dev/null +++ b/src/Builder/Accumulator/CovarianceSampAccumulator.php @@ -0,0 +1,60 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression1 */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression1; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression2 */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression2; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression1 + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression2 + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $expression1, + Decimal128|Int64|ResolvesToNumber|float|int|string $expression2, + ) { + if (is_string($expression1) && ! str_starts_with($expression1, '$')) { + throw new InvalidArgumentException('Argument $expression1 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression1 = $expression1; + if (is_string($expression2) && ! str_starts_with($expression2, '$')) { + throw new InvalidArgumentException('Argument $expression2 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Accumulator/DenseRankAccumulator.php b/src/Builder/Accumulator/DenseRankAccumulator.php new file mode 100644 index 000000000..c5bc4bee9 --- /dev/null +++ b/src/Builder/Accumulator/DenseRankAccumulator.php @@ -0,0 +1,30 @@ + 'input', 'unit' => 'unit']; + + /** @var DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $input */ + public readonly DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $input; + + /** + * @var Optional|ResolvesToString|TimeUnit|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. + */ + public readonly Optional|ResolvesToString|TimeUnit|string $unit; + + /** + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $input + * @param Optional|ResolvesToString|TimeUnit|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. + */ + public function __construct( + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $input, + Optional|ResolvesToString|TimeUnit|string $unit = Optional::Undefined, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + $this->unit = $unit; + } +} diff --git a/src/Builder/Accumulator/DocumentNumberAccumulator.php b/src/Builder/Accumulator/DocumentNumberAccumulator.php new file mode 100644 index 000000000..a44f63475 --- /dev/null +++ b/src/Builder/Accumulator/DocumentNumberAccumulator.php @@ -0,0 +1,30 @@ + 'input', 'N' => 'N', 'alpha' => 'alpha']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $input */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $input; + + /** + * @var Optional|int $N An integer that specifies the number of historical documents that have a significant mathematical weight in the exponential moving average calculation, with the most recent documents contributing the most weight. + * You must specify either N or alpha. You cannot specify both. + * The N value is used in this formula to calculate the current result based on the expression value from the current document being read and the previous result of the calculation: + */ + public readonly Optional|int $N; + + /** + * @var Optional|Int64|float|int $alpha A double that specifies the exponential decay value to use in the exponential moving average calculation. A higher alpha value assigns a lower mathematical significance to previous results from the calculation. + * You must specify either N or alpha. You cannot specify both. + */ + public readonly Optional|Int64|float|int $alpha; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $input + * @param Optional|int $N An integer that specifies the number of historical documents that have a significant mathematical weight in the exponential moving average calculation, with the most recent documents contributing the most weight. + * You must specify either N or alpha. You cannot specify both. + * The N value is used in this formula to calculate the current result based on the expression value from the current document being read and the previous result of the calculation: + * @param Optional|Int64|float|int $alpha A double that specifies the exponential decay value to use in the exponential moving average calculation. A higher alpha value assigns a lower mathematical significance to previous results from the calculation. + * You must specify either N or alpha. You cannot specify both. + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $input, + Optional|int $N = Optional::Undefined, + Optional|Int64|float|int $alpha = Optional::Undefined, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + $this->N = $N; + $this->alpha = $alpha; + } +} diff --git a/src/Builder/Accumulator/FactoryTrait.php b/src/Builder/Accumulator/FactoryTrait.php new file mode 100644 index 000000000..486eb43a5 --- /dev/null +++ b/src/Builder/Accumulator/FactoryTrait.php @@ -0,0 +1,580 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/FirstNAccumulator.php b/src/Builder/Accumulator/FirstNAccumulator.php new file mode 100644 index 000000000..03d412fd7 --- /dev/null +++ b/src/Builder/Accumulator/FirstNAccumulator.php @@ -0,0 +1,60 @@ + 'input', 'n' => 'n']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input An expression that resolves to the array from which to return n elements. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input; + + /** @var ResolvesToInt|int|string $n A positive integral expression that is either a constant or depends on the _id value for $group. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input An expression that resolves to the array from which to return n elements. + * @param ResolvesToInt|int|string $n A positive integral expression that is either a constant or depends on the _id value for $group. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input, + ResolvesToInt|int|string $n, + ) { + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Accumulator/IntegralAccumulator.php b/src/Builder/Accumulator/IntegralAccumulator.php new file mode 100644 index 000000000..3abe3ff9e --- /dev/null +++ b/src/Builder/Accumulator/IntegralAccumulator.php @@ -0,0 +1,66 @@ + 'input', 'unit' => 'unit']; + + /** @var DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $input */ + public readonly DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $input; + + /** + * @var Optional|ResolvesToString|TimeUnit|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. + */ + public readonly Optional|ResolvesToString|TimeUnit|string $unit; + + /** + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $input + * @param Optional|ResolvesToString|TimeUnit|string $unit A string that specifies the time unit. Use one of these strings: "week", "day","hour", "minute", "second", "millisecond". + * If the sortBy field is not a date, you must omit a unit. If you specify a unit, you must specify a date in the sortBy field. + */ + public function __construct( + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $input, + Optional|ResolvesToString|TimeUnit|string $unit = Optional::Undefined, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + $this->unit = $unit; + } +} diff --git a/src/Builder/Accumulator/LastAccumulator.php b/src/Builder/Accumulator/LastAccumulator.php new file mode 100644 index 000000000..5d7635d6e --- /dev/null +++ b/src/Builder/Accumulator/LastAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/LastNAccumulator.php b/src/Builder/Accumulator/LastNAccumulator.php new file mode 100644 index 000000000..45a2a0f95 --- /dev/null +++ b/src/Builder/Accumulator/LastNAccumulator.php @@ -0,0 +1,69 @@ + 'input', 'n' => 'n']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Accumulator/LinearFillAccumulator.php b/src/Builder/Accumulator/LinearFillAccumulator.php new file mode 100644 index 000000000..19126adbc --- /dev/null +++ b/src/Builder/Accumulator/LinearFillAccumulator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/LocfAccumulator.php b/src/Builder/Accumulator/LocfAccumulator.php new file mode 100644 index 000000000..effb6e463 --- /dev/null +++ b/src/Builder/Accumulator/LocfAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/MaxAccumulator.php b/src/Builder/Accumulator/MaxAccumulator.php new file mode 100644 index 000000000..7870591d2 --- /dev/null +++ b/src/Builder/Accumulator/MaxAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/MaxNAccumulator.php b/src/Builder/Accumulator/MaxNAccumulator.php new file mode 100644 index 000000000..eafbfbcfe --- /dev/null +++ b/src/Builder/Accumulator/MaxNAccumulator.php @@ -0,0 +1,67 @@ + 'input', 'n' => 'n']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Accumulator/MedianAccumulator.php b/src/Builder/Accumulator/MedianAccumulator.php new file mode 100644 index 000000000..cfdd05e8a --- /dev/null +++ b/src/Builder/Accumulator/MedianAccumulator.php @@ -0,0 +1,59 @@ + 'input', 'method' => 'method']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $input $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $input; + + /** @var string $method The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. */ + public readonly string $method; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $input $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. + * @param string $method The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $input, string $method) + { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + $this->method = $method; + } +} diff --git a/src/Builder/Accumulator/MergeObjectsAccumulator.php b/src/Builder/Accumulator/MergeObjectsAccumulator.php new file mode 100644 index 000000000..0a567b771 --- /dev/null +++ b/src/Builder/Accumulator/MergeObjectsAccumulator.php @@ -0,0 +1,49 @@ + 'document']; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $document Any valid expression that resolves to a document. */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $document; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $document Any valid expression that resolves to a document. + */ + public function __construct(Document|Serializable|ResolvesToObject|stdClass|array|string $document) + { + if (is_string($document) && ! str_starts_with($document, '$')) { + throw new InvalidArgumentException('Argument $document can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->document = $document; + } +} diff --git a/src/Builder/Accumulator/MinAccumulator.php b/src/Builder/Accumulator/MinAccumulator.php new file mode 100644 index 000000000..3b8f5a05b --- /dev/null +++ b/src/Builder/Accumulator/MinAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/MinNAccumulator.php b/src/Builder/Accumulator/MinNAccumulator.php new file mode 100644 index 000000000..2d5eeab7e --- /dev/null +++ b/src/Builder/Accumulator/MinNAccumulator.php @@ -0,0 +1,67 @@ + 'input', 'n' => 'n']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Accumulator/PercentileAccumulator.php b/src/Builder/Accumulator/PercentileAccumulator.php new file mode 100644 index 000000000..030f13aab --- /dev/null +++ b/src/Builder/Accumulator/PercentileAccumulator.php @@ -0,0 +1,87 @@ + 'input', 'p' => 'p', 'method' => 'method']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $input $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $input; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $p $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + * $percentile returns results in the same order as the elements in p. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $p; + + /** @var string $method The method that mongod uses to calculate the percentile value. The method must be 'approximate'. */ + public readonly string $method; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $input $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $p $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + * $percentile returns results in the same order as the elements in p. + * @param string $method The method that mongod uses to calculate the percentile value. The method must be 'approximate'. + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $input, + PackedArray|ResolvesToArray|BSONArray|array|string $p, + string $method, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + if (is_string($p) && ! str_starts_with($p, '$')) { + throw new InvalidArgumentException('Argument $p can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($p) && ! array_is_list($p)) { + throw new InvalidArgumentException('Expected $p argument to be a list, got an associative array.'); + } + + $this->p = $p; + $this->method = $method; + } +} diff --git a/src/Builder/Accumulator/PushAccumulator.php b/src/Builder/Accumulator/PushAccumulator.php new file mode 100644 index 000000000..85576e0ce --- /dev/null +++ b/src/Builder/Accumulator/PushAccumulator.php @@ -0,0 +1,44 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/RankAccumulator.php b/src/Builder/Accumulator/RankAccumulator.php new file mode 100644 index 000000000..ddf2f665c --- /dev/null +++ b/src/Builder/Accumulator/RankAccumulator.php @@ -0,0 +1,30 @@ + 'array']; + + /** @var list $array An array of expressions that resolve to an array. */ + public readonly array $array; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$array An array of expressions that resolve to an array. + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$array) + { + if (\count($array) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $array, got %d.', 1, \count($array))); + } + + if (! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array arguments to be a list (array), named arguments are not supported'); + } + + $this->array = $array; + } +} diff --git a/src/Builder/Accumulator/ShiftAccumulator.php b/src/Builder/Accumulator/ShiftAccumulator.php new file mode 100644 index 000000000..e4674e9a3 --- /dev/null +++ b/src/Builder/Accumulator/ShiftAccumulator.php @@ -0,0 +1,71 @@ + 'output', 'by' => 'by', 'default' => 'default']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Specifies an expression to evaluate and return in the output. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output; + + /** + * @var int $by Specifies an integer with a numeric document position relative to the current document in the output. + * For example: + * 1 specifies the document position after the current document. + * -1 specifies the document position before the current document. + * -2 specifies the document position that is two positions before the current document. + */ + public readonly int $by; + + /** + * @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default Specifies an optional default expression to evaluate if the document position is outside of the implicit $setWindowFields stage window. The implicit window contains all the documents in the partition. + * The default expression must evaluate to a constant value. + * If you do not specify a default expression, $shift returns null for documents whose positions are outside of the implicit $setWindowFields stage window. + */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Specifies an expression to evaluate and return in the output. + * @param int $by Specifies an integer with a numeric document position relative to the current document in the output. + * For example: + * 1 specifies the document position after the current document. + * -1 specifies the document position before the current document. + * -2 specifies the document position that is two positions before the current document. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default Specifies an optional default expression to evaluate if the document position is outside of the implicit $setWindowFields stage window. The implicit window contains all the documents in the partition. + * The default expression must evaluate to a constant value. + * If you do not specify a default expression, $shift returns null for documents whose positions are outside of the implicit $setWindowFields stage window. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output, + int $by, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default, + ) { + $this->output = $output; + $this->by = $by; + $this->default = $default; + } +} diff --git a/src/Builder/Accumulator/StdDevPopAccumulator.php b/src/Builder/Accumulator/StdDevPopAccumulator.php new file mode 100644 index 000000000..dd234dab1 --- /dev/null +++ b/src/Builder/Accumulator/StdDevPopAccumulator.php @@ -0,0 +1,51 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/StdDevSampAccumulator.php b/src/Builder/Accumulator/StdDevSampAccumulator.php new file mode 100644 index 000000000..897e08563 --- /dev/null +++ b/src/Builder/Accumulator/StdDevSampAccumulator.php @@ -0,0 +1,51 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/SumAccumulator.php b/src/Builder/Accumulator/SumAccumulator.php new file mode 100644 index 000000000..fa8cdb642 --- /dev/null +++ b/src/Builder/Accumulator/SumAccumulator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Accumulator/TopAccumulator.php b/src/Builder/Accumulator/TopAccumulator.php new file mode 100644 index 000000000..d7fb258f1 --- /dev/null +++ b/src/Builder/Accumulator/TopAccumulator.php @@ -0,0 +1,53 @@ + 'sortBy', 'output' => 'output']; + + /** @var Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. */ + public readonly Document|Serializable|stdClass|array $sortBy; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output; + + /** + * @param Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. + */ + public function __construct( + Document|Serializable|stdClass|array $sortBy, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output, + ) { + $this->sortBy = $sortBy; + $this->output = $output; + } +} diff --git a/src/Builder/Accumulator/TopNAccumulator.php b/src/Builder/Accumulator/TopNAccumulator.php new file mode 100644 index 000000000..45087d5e0 --- /dev/null +++ b/src/Builder/Accumulator/TopNAccumulator.php @@ -0,0 +1,68 @@ + 'n', 'sortBy' => 'sortBy', 'output' => 'output']; + + /** @var ResolvesToInt|int|string $n limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. */ + public readonly ResolvesToInt|int|string $n; + + /** @var Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. */ + public readonly Document|Serializable|stdClass|array $sortBy; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output; + + /** + * @param ResolvesToInt|int|string $n limits the number of results per group and has to be a positive integral expression that is either a constant or depends on the _id value for $group. + * @param Document|Serializable|array|stdClass $sortBy Specifies the order of results, with syntax similar to $sort. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $output Represents the output for each element in the group and can be any expression. + */ + public function __construct( + ResolvesToInt|int|string $n, + Document|Serializable|stdClass|array $sortBy, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $output, + ) { + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + $this->sortBy = $sortBy; + $this->output = $output; + } +} diff --git a/src/Builder/BuilderEncoder.php b/src/Builder/BuilderEncoder.php new file mode 100644 index 000000000..e9121a7d0 --- /dev/null +++ b/src/Builder/BuilderEncoder.php @@ -0,0 +1,115 @@ + */ +final class BuilderEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + + /** @var array */ + private array $encoders; + + /** @var array */ + private array $cachedEncoders = []; + + /** @param iterable $encoders */ + public function __construct(iterable $encoders = []) + { + $self = WeakReference::create($this); + + if (! is_array($encoders)) { + $encoders = iterator_to_array($encoders); + } + + $this->encoders = $encoders + [ + Pipeline::class => new PipelineEncoder($self), + Variable::class => new VariableEncoder(), + DictionaryInterface::class => new DictionaryEncoder(), + FieldPathInterface::class => new FieldPathEncoder(), + CombinedFieldQuery::class => new CombinedFieldQueryEncoder($self), + QueryObject::class => new QueryEncoder($self), + OutputWindow::class => new OutputWindowEncoder($self), + OperatorInterface::class => new OperatorEncoder($self), + DateTimeInterface::class => new DateTimeEncoder(), + ]; + } + + /** @psalm-assert-if-true object $value */ + public function canEncode(mixed $value): bool + { + if (! is_object($value)) { + return false; + } + + return (bool) $this->getEncoderFor($value)?->canEncode($value); + } + + public function encode(mixed $value): Type|stdClass|array|string|int + { + $encoder = $this->getEncoderFor($value); + + if (! $encoder?->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return $encoder->encode($value); + } + + private function getEncoderFor(object $value): Encoder|null + { + $valueClass = $value::class; + if (array_key_exists($valueClass, $this->cachedEncoders)) { + return $this->cachedEncoders[$valueClass]; + } + + // First attempt: match class name exactly + if (isset($this->encoders[$valueClass])) { + return $this->cachedEncoders[$valueClass] = $this->encoders[$valueClass]; + } + + // Second attempt: catch child classes + foreach ($this->encoders as $className => $encoder) { + if ($value instanceof $className) { + return $this->cachedEncoders[$valueClass] = $encoder; + } + } + + return $this->cachedEncoders[$valueClass] = null; + } +} diff --git a/src/Builder/Encoder/CombinedFieldQueryEncoder.php b/src/Builder/Encoder/CombinedFieldQueryEncoder.php new file mode 100644 index 000000000..118757901 --- /dev/null +++ b/src/Builder/Encoder/CombinedFieldQueryEncoder.php @@ -0,0 +1,57 @@ + + * @internal + */ +final class CombinedFieldQueryEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + use RecursiveEncode; + + public function canEncode(mixed $value): bool + { + return $value instanceof CombinedFieldQuery; + } + + public function encode(mixed $value): stdClass + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + $result = new stdClass(); + foreach ($value->fieldQueries as $filter) { + $filter = $this->recursiveEncode($filter); + if (is_object($filter)) { + $filter = get_object_vars($filter); + } elseif (! is_array($filter)) { + throw new LogicException(sprintf('Query filters must an array or an object. Got "%s"', get_debug_type($filter))); + } + + foreach ($filter as $key => $filterValue) { + $result->{$key} = $filterValue; + } + } + + return $result; + } +} diff --git a/src/Builder/Encoder/DateTimeEncoder.php b/src/Builder/Encoder/DateTimeEncoder.php new file mode 100644 index 000000000..6926bd813 --- /dev/null +++ b/src/Builder/Encoder/DateTimeEncoder.php @@ -0,0 +1,36 @@ + + * @internal + */ +final class DateTimeEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + + /** @psalm-assert-if-true DateTimeInterface $value */ + public function canEncode(mixed $value): bool + { + return $value instanceof DateTimeInterface; + } + + public function encode(mixed $value): UTCDateTime + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return new UTCDateTime($value); + } +} diff --git a/src/Builder/Encoder/DictionaryEncoder.php b/src/Builder/Encoder/DictionaryEncoder.php new file mode 100644 index 000000000..dc89cff11 --- /dev/null +++ b/src/Builder/Encoder/DictionaryEncoder.php @@ -0,0 +1,35 @@ + + * @internal + */ +final class DictionaryEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + + public function canEncode(mixed $value): bool + { + return $value instanceof DictionaryInterface; + } + + public function encode(mixed $value): string|int|array|stdClass + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return $value->getValue(); + } +} diff --git a/src/Builder/Encoder/FieldPathEncoder.php b/src/Builder/Encoder/FieldPathEncoder.php new file mode 100644 index 000000000..3766f9a0e --- /dev/null +++ b/src/Builder/Encoder/FieldPathEncoder.php @@ -0,0 +1,34 @@ + + * @internal + */ +final class FieldPathEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + + public function canEncode(mixed $value): bool + { + return $value instanceof FieldPathInterface; + } + + public function encode(mixed $value): string + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return '$' . $value->name; + } +} diff --git a/src/Builder/Encoder/OperatorEncoder.php b/src/Builder/Encoder/OperatorEncoder.php new file mode 100644 index 000000000..8f81edfa2 --- /dev/null +++ b/src/Builder/Encoder/OperatorEncoder.php @@ -0,0 +1,126 @@ + + * @internal + */ +final class OperatorEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + use RecursiveEncode; + + public function canEncode(mixed $value): bool + { + return $value instanceof OperatorInterface; + } + + public function encode(mixed $value): stdClass + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return match ($value::ENCODE) { + Encode::Single => $this->encodeAsSingle($value), + Encode::Array => $this->encodeAsArray($value), + Encode::Object => $this->encodeAsObject($value), + default => throw new LogicException(sprintf('Class "%s" does not have a valid ENCODE constant.', $value::class)), + }; + } + + /** + * Encode the value as an array of properties, in the order they are defined in the class. + */ + private function encodeAsArray(OperatorInterface $value): stdClass + { + $result = []; + foreach ($value::PROPERTIES as $prop => $name) { + $val = $value->$prop; + // Skip optional arguments. For example, the $slice expression operator has an optional argument + // in the middle of the array. + if ($val === Optional::Undefined) { + continue; + } + + $result[] = $this->recursiveEncode($val); + } + + return $this->wrap($value, $result); + } + + /** + * Encode the value as an object with properties. Property names are + * mapped by the PROPERTIES constant. + */ + private function encodeAsObject(OperatorInterface $value): stdClass + { + $result = new stdClass(); + foreach ($value::PROPERTIES as $prop => $name) { + $val = $value->$prop; + + // Skip optional arguments. If they have a default value, it is resolved by the server. + if ($val === Optional::Undefined) { + continue; + } + + // The name is null for arguments with "mergeObject: true" in the YAML file, + // the value properties are merged into the parent object. + if ($name === null) { + $val = $this->recursiveEncode($val); + foreach ($val as $k => $v) { + $result->{$k} = $v; + } + } else { + $result->{$name} = $this->recursiveEncode($val); + } + } + + if ($value::NAME === null) { + return $result; + } + + return $this->wrap($value, $result); + } + + /** + * Get the unique property of the operator as value + */ + private function encodeAsSingle(OperatorInterface $value): stdClass + { + foreach ($value::PROPERTIES as $prop => $name) { + $result = $this->recursiveEncode($value->$prop); + + return $this->wrap($value, $result); + } + + throw new LogicException(sprintf('Class "%s" does not have a single property.', $value::class)); + } + + private function wrap(OperatorInterface $value, mixed $result): stdClass + { + assert(is_string($value::NAME)); + + $object = new stdClass(); + $object->{$value::NAME} = $result; + + return $object; + } +} diff --git a/src/Builder/Encoder/OutputWindowEncoder.php b/src/Builder/Encoder/OutputWindowEncoder.php new file mode 100644 index 000000000..4e435a987 --- /dev/null +++ b/src/Builder/Encoder/OutputWindowEncoder.php @@ -0,0 +1,65 @@ + + * @internal + */ +final class OutputWindowEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + use RecursiveEncode; + + public function canEncode(mixed $value): bool + { + return $value instanceof OutputWindow; + } + + public function encode(mixed $value): stdClass + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + $result = $this->recursiveEncode($value->operator); + + // Transform the result into an stdClass if a document is provided + if (! $value->operator instanceof WindowInterface) { + if (! is_first_key_operator($result)) { + $firstKey = array_key_first((array) $result); + + throw new LogicException(sprintf('Expected OutputWindow::$operator to be an operator. Got "%s"', $firstKey ?? 'null')); + } + + $result = (object) $result; + } + + if (! $result instanceof stdClass) { + throw new LogicException(sprintf('Expected OutputWindow::$operator to be an stdClass, array or WindowInterface. Got "%s"', get_debug_type($result))); + } + + if ($value->window !== Optional::Undefined) { + $result->window = $this->recursiveEncode($value->window); + } + + return $result; + } +} diff --git a/src/Builder/Encoder/PipelineEncoder.php b/src/Builder/Encoder/PipelineEncoder.php new file mode 100644 index 000000000..c4c0829ce --- /dev/null +++ b/src/Builder/Encoder/PipelineEncoder.php @@ -0,0 +1,42 @@ +, Pipeline> + * @internal + */ +final class PipelineEncoder implements Encoder +{ + /** @template-use EncodeIfSupported, Pipeline> */ + use EncodeIfSupported; + use RecursiveEncode; + + /** @psalm-assert-if-true Pipeline $value */ + public function canEncode(mixed $value): bool + { + return $value instanceof Pipeline; + } + + /** @return list */ + public function encode(mixed $value): array + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + $encoded = []; + foreach ($value->getIterator() as $stage) { + $encoded[] = $this->recursiveEncode($stage); + } + + return $encoded; + } +} diff --git a/src/Builder/Encoder/QueryEncoder.php b/src/Builder/Encoder/QueryEncoder.php new file mode 100644 index 000000000..19f21897c --- /dev/null +++ b/src/Builder/Encoder/QueryEncoder.php @@ -0,0 +1,62 @@ + + * @internal + */ +final class QueryEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + use RecursiveEncode; + + public function canEncode(mixed $value): bool + { + return $value instanceof QueryObject; + } + + public function encode(mixed $value): stdClass + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + $result = new stdClass(); + foreach ($value->queries as $key => $value) { + if ($value instanceof QueryInterface) { + // The sub-objects is merged into the main object, replacing duplicate keys + foreach (get_object_vars($this->recursiveEncode($value)) as $subKey => $subValue) { + if (property_exists($result, $subKey)) { + throw new LogicException(sprintf('Duplicate key "%s" in query object', $subKey)); + } + + $result->{$subKey} = $subValue; + } + } else { + if (property_exists($result, (string) $key)) { + throw new LogicException(sprintf('Duplicate key "%s" in query object', $key)); + } + + $result->{$key} = $this->recursiveEncode($value); + } + } + + return $result; + } +} diff --git a/src/Builder/Encoder/RecursiveEncode.php b/src/Builder/Encoder/RecursiveEncode.php new file mode 100644 index 000000000..e83af8a22 --- /dev/null +++ b/src/Builder/Encoder/RecursiveEncode.php @@ -0,0 +1,59 @@ + $encoder */ + final public function __construct(private readonly WeakReference $encoder) + { + } + + /** + * Nested arrays and objects must be encoded recursively. + * + * @psalm-template T + * @psalm-param T $value + * + * @psalm-return (T is stdClass ? stdClass : (T is array ? array : mixed)) + * + * @template T + */ + private function recursiveEncode(mixed $value): mixed + { + if (is_array($value)) { + foreach ($value as $key => $val) { + $value[$key] = $this->recursiveEncode($val); + } + + return $value; + } + + if ($value instanceof stdClass) { + foreach (get_object_vars($value) as $key => $val) { + $value->{$key} = $this->recursiveEncode($val); + } + + return $value; + } + + /** + * If the BuilderEncoder instance is removed from the memory, the + * instances of the classes using this trait will be removed as well. + * Therefore, the weak reference will never return null. + * + * @psalm-suppress PossiblyNullReference + */ + return $this->encoder->get()->encodeIfSupported($value); + } +} diff --git a/src/Builder/Encoder/VariableEncoder.php b/src/Builder/Encoder/VariableEncoder.php new file mode 100644 index 000000000..051977976 --- /dev/null +++ b/src/Builder/Encoder/VariableEncoder.php @@ -0,0 +1,35 @@ + + * @internal + */ +final class VariableEncoder implements Encoder +{ + /** @template-use EncodeIfSupported */ + use EncodeIfSupported; + + public function canEncode(mixed $value): bool + { + return $value instanceof Variable; + } + + public function encode(mixed $value): string + { + if (! $this->canEncode($value)) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + // TODO: needs method because interfaces can't have properties + return '$$' . $value->name; + } +} diff --git a/src/Builder/Expression.php b/src/Builder/Expression.php new file mode 100644 index 000000000..9e4779145 --- /dev/null +++ b/src/Builder/Expression.php @@ -0,0 +1,21 @@ + 'value']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $value */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $value; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $value + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $value) + { + if (is_string($value) && ! str_starts_with($value, '$')) { + throw new InvalidArgumentException('Argument $value can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Expression/AcosOperator.php b/src/Builder/Expression/AcosOperator.php new file mode 100644 index 000000000..4b5017ec1 --- /dev/null +++ b/src/Builder/Expression/AcosOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $acos takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $acos returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $acos returns values as a double. $acos can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $acos takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $acos returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $acos returns values as a double. $acos can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AcoshOperator.php b/src/Builder/Expression/AcoshOperator.php new file mode 100644 index 000000000..1da690261 --- /dev/null +++ b/src/Builder/Expression/AcoshOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $acosh takes any valid expression that resolves to a number between 1 and +Infinity, e.g. 1 <= value <= +Infinity. + * $acosh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $acosh returns values as a double. $acosh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $acosh takes any valid expression that resolves to a number between 1 and +Infinity, e.g. 1 <= value <= +Infinity. + * $acosh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $acosh returns values as a double. $acosh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AddOperator.php b/src/Builder/Expression/AddOperator.php new file mode 100644 index 000000000..9eb139f36 --- /dev/null +++ b/src/Builder/Expression/AddOperator.php @@ -0,0 +1,53 @@ + 'expression']; + + /** @var list $expression The arguments can be any valid expression as long as they resolve to either all numbers or to numbers and a date. */ + public readonly array $expression; + + /** + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string ...$expression The arguments can be any valid expression as long as they resolve to either all numbers or to numbers and a date. + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AllElementsTrueOperator.php b/src/Builder/Expression/AllElementsTrueOperator.php new file mode 100644 index 000000000..282de6dfa --- /dev/null +++ b/src/Builder/Expression/AllElementsTrueOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AndOperator.php b/src/Builder/Expression/AndOperator.php new file mode 100644 index 000000000..5e9121677 --- /dev/null +++ b/src/Builder/Expression/AndOperator.php @@ -0,0 +1,55 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param DateTimeInterface|Decimal128|ExpressionInterface|Int64|ResolvesToBool|ResolvesToNull|ResolvesToNumber|ResolvesToString|Type|array|bool|float|int|null|stdClass|string ...$expression + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Decimal128|Int64|Type|ResolvesToBool|ResolvesToNull|ResolvesToNumber|ResolvesToString|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AnyElementTrueOperator.php b/src/Builder/Expression/AnyElementTrueOperator.php new file mode 100644 index 000000000..c3566ca9e --- /dev/null +++ b/src/Builder/Expression/AnyElementTrueOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ArrayElemAtOperator.php b/src/Builder/Expression/ArrayElemAtOperator.php new file mode 100644 index 000000000..9cbca3cfe --- /dev/null +++ b/src/Builder/Expression/ArrayElemAtOperator.php @@ -0,0 +1,63 @@ + 'array', 'idx' => 'idx']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $array */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $array; + + /** @var ResolvesToInt|int|string $idx */ + public readonly ResolvesToInt|int|string $idx; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array + * @param ResolvesToInt|int|string $idx + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $array, + ResolvesToInt|int|string $idx, + ) { + if (is_string($array) && ! str_starts_with($array, '$')) { + throw new InvalidArgumentException('Argument $array can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($array) && ! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array argument to be a list, got an associative array.'); + } + + $this->array = $array; + if (is_string($idx) && ! str_starts_with($idx, '$')) { + throw new InvalidArgumentException('Argument $idx can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->idx = $idx; + } +} diff --git a/src/Builder/Expression/ArrayFieldPath.php b/src/Builder/Expression/ArrayFieldPath.php new file mode 100644 index 000000000..f475beb1c --- /dev/null +++ b/src/Builder/Expression/ArrayFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/ArrayToObjectOperator.php b/src/Builder/Expression/ArrayToObjectOperator.php new file mode 100644 index 000000000..3a881363e --- /dev/null +++ b/src/Builder/Expression/ArrayToObjectOperator.php @@ -0,0 +1,52 @@ + 'array']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $array */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $array; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $array) + { + if (is_string($array) && ! str_starts_with($array, '$')) { + throw new InvalidArgumentException('Argument $array can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($array) && ! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array argument to be a list, got an associative array.'); + } + + $this->array = $array; + } +} diff --git a/src/Builder/Expression/AsinOperator.php b/src/Builder/Expression/AsinOperator.php new file mode 100644 index 000000000..d814f6cf1 --- /dev/null +++ b/src/Builder/Expression/AsinOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $asin takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $asin returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $asin returns values as a double. $asin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $asin takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $asin returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $asin returns values as a double. $asin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AsinhOperator.php b/src/Builder/Expression/AsinhOperator.php new file mode 100644 index 000000000..edc2f2062 --- /dev/null +++ b/src/Builder/Expression/AsinhOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $asinh takes any valid expression that resolves to a number. + * $asinh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $asinh returns values as a double. $asinh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $asinh takes any valid expression that resolves to a number. + * $asinh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $asinh returns values as a double. $asinh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/Atan2Operator.php b/src/Builder/Expression/Atan2Operator.php new file mode 100644 index 000000000..f0af1fc97 --- /dev/null +++ b/src/Builder/Expression/Atan2Operator.php @@ -0,0 +1,63 @@ + 'y', 'x' => 'x']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $y $atan2 takes any valid expression that resolves to a number. + * $atan2 returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atan returns values as a double. $atan2 can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $y; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $x */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $x; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $y $atan2 takes any valid expression that resolves to a number. + * $atan2 returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atan returns values as a double. $atan2 can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $x + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $y, + Decimal128|Int64|ResolvesToNumber|float|int|string $x, + ) { + if (is_string($y) && ! str_starts_with($y, '$')) { + throw new InvalidArgumentException('Argument $y can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->y = $y; + if (is_string($x) && ! str_starts_with($x, '$')) { + throw new InvalidArgumentException('Argument $x can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->x = $x; + } +} diff --git a/src/Builder/Expression/AtanOperator.php b/src/Builder/Expression/AtanOperator.php new file mode 100644 index 000000000..938a9b007 --- /dev/null +++ b/src/Builder/Expression/AtanOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $atan takes any valid expression that resolves to a number. + * $atan returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atan returns values as a double. $atan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $atan takes any valid expression that resolves to a number. + * $atan returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atan returns values as a double. $atan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AtanhOperator.php b/src/Builder/Expression/AtanhOperator.php new file mode 100644 index 000000000..fde1dae12 --- /dev/null +++ b/src/Builder/Expression/AtanhOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $atanh takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $atanh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atanh returns values as a double. $atanh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $atanh takes any valid expression that resolves to a number between -1 and 1, e.g. -1 <= value <= 1. + * $atanh returns values in radians. Use $radiansToDegrees operator to convert the output value from radians to degrees. + * By default $atanh returns values as a double. $atanh can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/AvgOperator.php b/src/Builder/Expression/AvgOperator.php new file mode 100644 index 000000000..52be761a9 --- /dev/null +++ b/src/Builder/Expression/AvgOperator.php @@ -0,0 +1,51 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BinDataFieldPath.php b/src/Builder/Expression/BinDataFieldPath.php new file mode 100644 index 000000000..77c2d39ea --- /dev/null +++ b/src/Builder/Expression/BinDataFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/BinarySizeOperator.php b/src/Builder/Expression/BinarySizeOperator.php new file mode 100644 index 000000000..792da66b0 --- /dev/null +++ b/src/Builder/Expression/BinarySizeOperator.php @@ -0,0 +1,37 @@ + 'expression']; + + /** @var Binary|ResolvesToBinData|ResolvesToNull|ResolvesToString|null|string $expression */ + public readonly Binary|ResolvesToBinData|ResolvesToNull|ResolvesToString|null|string $expression; + + /** + * @param Binary|ResolvesToBinData|ResolvesToNull|ResolvesToString|null|string $expression + */ + public function __construct(Binary|ResolvesToBinData|ResolvesToNull|ResolvesToString|null|string $expression) + { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BitAndOperator.php b/src/Builder/Expression/BitAndOperator.php new file mode 100644 index 000000000..eb6fffaca --- /dev/null +++ b/src/Builder/Expression/BitAndOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BitNotOperator.php b/src/Builder/Expression/BitNotOperator.php new file mode 100644 index 000000000..adac4fc66 --- /dev/null +++ b/src/Builder/Expression/BitNotOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var Int64|ResolvesToInt|ResolvesToLong|int|string $expression */ + public readonly Int64|ResolvesToInt|ResolvesToLong|int|string $expression; + + /** + * @param Int64|ResolvesToInt|ResolvesToLong|int|string $expression + */ + public function __construct(Int64|ResolvesToInt|ResolvesToLong|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BitOrOperator.php b/src/Builder/Expression/BitOrOperator.php new file mode 100644 index 000000000..bc68838e9 --- /dev/null +++ b/src/Builder/Expression/BitOrOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BitXorOperator.php b/src/Builder/Expression/BitXorOperator.php new file mode 100644 index 000000000..adbf5eb2e --- /dev/null +++ b/src/Builder/Expression/BitXorOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Int64|ResolvesToInt|ResolvesToLong|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/BoolFieldPath.php b/src/Builder/Expression/BoolFieldPath.php new file mode 100644 index 000000000..1dff80de0 --- /dev/null +++ b/src/Builder/Expression/BoolFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/BsonSizeOperator.php b/src/Builder/Expression/BsonSizeOperator.php new file mode 100644 index 000000000..37626d0d9 --- /dev/null +++ b/src/Builder/Expression/BsonSizeOperator.php @@ -0,0 +1,48 @@ + 'object']; + + /** @var Document|ResolvesToNull|ResolvesToObject|Serializable|array|null|stdClass|string $object */ + public readonly Document|Serializable|ResolvesToNull|ResolvesToObject|stdClass|array|null|string $object; + + /** + * @param Document|ResolvesToNull|ResolvesToObject|Serializable|array|null|stdClass|string $object + */ + public function __construct( + Document|Serializable|ResolvesToNull|ResolvesToObject|stdClass|array|null|string $object, + ) { + if (is_string($object) && ! str_starts_with($object, '$')) { + throw new InvalidArgumentException('Argument $object can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->object = $object; + } +} diff --git a/src/Builder/Expression/CaseOperator.php b/src/Builder/Expression/CaseOperator.php new file mode 100644 index 000000000..686d27ec9 --- /dev/null +++ b/src/Builder/Expression/CaseOperator.php @@ -0,0 +1,56 @@ + 'case', 'then' => 'then']; + + /** @var ResolvesToBool|bool|string $case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. */ + public readonly ResolvesToBool|bool|string $case; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $then Can be any valid expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $then; + + /** + * @param ResolvesToBool|bool|string $case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $then Can be any valid expression. + */ + public function __construct( + ResolvesToBool|bool|string $case, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $then, + ) { + if (is_string($case) && ! str_starts_with($case, '$')) { + throw new InvalidArgumentException('Argument $case can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->case = $case; + $this->then = $then; + } +} diff --git a/src/Builder/Expression/CeilOperator.php b/src/Builder/Expression/CeilOperator.php new file mode 100644 index 000000000..9f318c454 --- /dev/null +++ b/src/Builder/Expression/CeilOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression If the argument resolves to a value of null or refers to a field that is missing, $ceil returns null. If the argument resolves to NaN, $ceil returns NaN. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression If the argument resolves to a value of null or refers to a field that is missing, $ceil returns null. If the argument resolves to NaN, $ceil returns NaN. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/CmpOperator.php b/src/Builder/Expression/CmpOperator.php new file mode 100644 index 000000000..7e090f242 --- /dev/null +++ b/src/Builder/Expression/CmpOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/ConcatArraysOperator.php b/src/Builder/Expression/ConcatArraysOperator.php new file mode 100644 index 000000000..12f862086 --- /dev/null +++ b/src/Builder/Expression/ConcatArraysOperator.php @@ -0,0 +1,50 @@ + 'array']; + + /** @var list $array */ + public readonly array $array; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$array + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$array) + { + if (\count($array) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $array, got %d.', 1, \count($array))); + } + + if (! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array arguments to be a list (array), named arguments are not supported'); + } + + $this->array = $array; + } +} diff --git a/src/Builder/Expression/ConcatOperator.php b/src/Builder/Expression/ConcatOperator.php new file mode 100644 index 000000000..c8497be08 --- /dev/null +++ b/src/Builder/Expression/ConcatOperator.php @@ -0,0 +1,48 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param ResolvesToString|string ...$expression + * @no-named-arguments + */ + public function __construct(ResolvesToString|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/CondOperator.php b/src/Builder/Expression/CondOperator.php new file mode 100644 index 000000000..ba34c910a --- /dev/null +++ b/src/Builder/Expression/CondOperator.php @@ -0,0 +1,61 @@ + 'if', 'then' => 'then', 'else' => 'else']; + + /** @var ResolvesToBool|bool|string $if */ + public readonly ResolvesToBool|bool|string $if; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $then */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $then; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $else */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $else; + + /** + * @param ResolvesToBool|bool|string $if + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $then + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $else + */ + public function __construct( + ResolvesToBool|bool|string $if, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $then, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $else, + ) { + if (is_string($if) && ! str_starts_with($if, '$')) { + throw new InvalidArgumentException('Argument $if can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->if = $if; + $this->then = $then; + $this->else = $else; + } +} diff --git a/src/Builder/Expression/ConvertOperator.php b/src/Builder/Expression/ConvertOperator.php new file mode 100644 index 000000000..3a8d3764c --- /dev/null +++ b/src/Builder/Expression/ConvertOperator.php @@ -0,0 +1,69 @@ + 'input', 'to' => 'to', 'onError' => 'onError', 'onNull' => 'onNull']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input; + + /** @var ResolvesToInt|ResolvesToString|int|string $to */ + public readonly ResolvesToInt|ResolvesToString|int|string $to; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onError The value to return on encountering an error during conversion, including unsupported type conversions. The arguments can be any valid expression. + * If unspecified, the operation throws an error upon encountering an error and stops. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onError; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull The value to return if the input is null or missing. The arguments can be any valid expression. + * If unspecified, $convert returns null if the input is null or missing. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input + * @param ResolvesToInt|ResolvesToString|int|string $to + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onError The value to return on encountering an error during conversion, including unsupported type conversions. The arguments can be any valid expression. + * If unspecified, the operation throws an error upon encountering an error and stops. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull The value to return if the input is null or missing. The arguments can be any valid expression. + * If unspecified, $convert returns null if the input is null or missing. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input, + ResolvesToInt|ResolvesToString|int|string $to, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onError = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull = Optional::Undefined, + ) { + $this->input = $input; + $this->to = $to; + $this->onError = $onError; + $this->onNull = $onNull; + } +} diff --git a/src/Builder/Expression/CosOperator.php b/src/Builder/Expression/CosOperator.php new file mode 100644 index 000000000..fdc5fe5f0 --- /dev/null +++ b/src/Builder/Expression/CosOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $cos takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $cos returns values as a double. $cos can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $cos takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $cos returns values as a double. $cos can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/CoshOperator.php b/src/Builder/Expression/CoshOperator.php new file mode 100644 index 000000000..3133153a7 --- /dev/null +++ b/src/Builder/Expression/CoshOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $cosh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $cosh returns values as a double. $cosh can also return values as a 128-bit decimal if the resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $cosh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $cosh returns values as a double. $cosh can also return values as a 128-bit decimal if the resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/CreateObjectIdOperator.php b/src/Builder/Expression/CreateObjectIdOperator.php new file mode 100644 index 000000000..eef57b3ca --- /dev/null +++ b/src/Builder/Expression/CreateObjectIdOperator.php @@ -0,0 +1,28 @@ + 'startDate', 'unit' => 'unit', 'amount' => 'amount', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate; + + /** @var ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. */ + public readonly ResolvesToString|TimeUnit|string $unit; + + /** @var Int64|ResolvesToInt|ResolvesToLong|int|string $amount */ + public readonly Int64|ResolvesToInt|ResolvesToLong|int|string $amount; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. + * @param Int64|ResolvesToInt|ResolvesToLong|int|string $amount + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + ResolvesToString|TimeUnit|string $unit, + Int64|ResolvesToInt|ResolvesToLong|int|string $amount, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($startDate) && ! str_starts_with($startDate, '$')) { + throw new InvalidArgumentException('Argument $startDate can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->startDate = $startDate; + $this->unit = $unit; + if (is_string($amount) && ! str_starts_with($amount, '$')) { + throw new InvalidArgumentException('Argument $amount can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->amount = $amount; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DateDiffOperator.php b/src/Builder/Expression/DateDiffOperator.php new file mode 100644 index 000000000..8c4f5e5b4 --- /dev/null +++ b/src/Builder/Expression/DateDiffOperator.php @@ -0,0 +1,86 @@ + 'startDate', + 'endDate' => 'endDate', + 'unit' => 'unit', + 'timezone' => 'timezone', + 'startOfWeek' => 'startOfWeek', + ]; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The start of the time period. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $endDate The end of the time period. The endDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $endDate; + + /** @var ResolvesToString|TimeUnit|string $unit The time measurement unit between the startDate and endDate */ + public readonly ResolvesToString|TimeUnit|string $unit; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** @var Optional|ResolvesToString|string $startOfWeek Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string */ + public readonly Optional|ResolvesToString|string $startOfWeek; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The start of the time period. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $endDate The end of the time period. The endDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The time measurement unit between the startDate and endDate + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|ResolvesToString|string $startOfWeek Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $endDate, + ResolvesToString|TimeUnit|string $unit, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|ResolvesToString|string $startOfWeek = Optional::Undefined, + ) { + if (is_string($startDate) && ! str_starts_with($startDate, '$')) { + throw new InvalidArgumentException('Argument $startDate can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->startDate = $startDate; + if (is_string($endDate) && ! str_starts_with($endDate, '$')) { + throw new InvalidArgumentException('Argument $endDate can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->endDate = $endDate; + $this->unit = $unit; + $this->timezone = $timezone; + $this->startOfWeek = $startOfWeek; + } +} diff --git a/src/Builder/Expression/DateFieldPath.php b/src/Builder/Expression/DateFieldPath.php new file mode 100644 index 000000000..d32988537 --- /dev/null +++ b/src/Builder/Expression/DateFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/DateFromPartsOperator.php b/src/Builder/Expression/DateFromPartsOperator.php new file mode 100644 index 000000000..c2d32c290 --- /dev/null +++ b/src/Builder/Expression/DateFromPartsOperator.php @@ -0,0 +1,157 @@ + 'year', + 'isoWeekYear' => 'isoWeekYear', + 'month' => 'month', + 'isoWeek' => 'isoWeek', + 'day' => 'day', + 'isoDayOfWeek' => 'isoDayOfWeek', + 'hour' => 'hour', + 'minute' => 'minute', + 'second' => 'second', + 'millisecond' => 'millisecond', + 'timezone' => 'timezone', + ]; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year Calendar year. Can be any expression that evaluates to a number. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear ISO Week Date Year. Can be any expression that evaluates to a number. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month Month. Defaults to 1. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek Week of year. Defaults to 1. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day Day of month. Defaults to 1. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek Day of week (Monday 1 - Sunday 7). Defaults to 1. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour Hour. Defaults to 0. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute Minute. Defaults to 0. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second Second. Defaults to 0. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second; + + /** @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond Millisecond. Defaults to 0. */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year Calendar year. Can be any expression that evaluates to a number. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear ISO Week Date Year. Can be any expression that evaluates to a number. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month Month. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek Week of year. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day Day of month. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek Day of week (Monday 1 - Sunday 7). Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour Hour. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute Minute. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second Second. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond Millisecond. Defaults to 0. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($year) && ! str_starts_with($year, '$')) { + throw new InvalidArgumentException('Argument $year can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->year = $year; + if (is_string($isoWeekYear) && ! str_starts_with($isoWeekYear, '$')) { + throw new InvalidArgumentException('Argument $isoWeekYear can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->isoWeekYear = $isoWeekYear; + if (is_string($month) && ! str_starts_with($month, '$')) { + throw new InvalidArgumentException('Argument $month can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->month = $month; + if (is_string($isoWeek) && ! str_starts_with($isoWeek, '$')) { + throw new InvalidArgumentException('Argument $isoWeek can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->isoWeek = $isoWeek; + if (is_string($day) && ! str_starts_with($day, '$')) { + throw new InvalidArgumentException('Argument $day can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->day = $day; + if (is_string($isoDayOfWeek) && ! str_starts_with($isoDayOfWeek, '$')) { + throw new InvalidArgumentException('Argument $isoDayOfWeek can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->isoDayOfWeek = $isoDayOfWeek; + if (is_string($hour) && ! str_starts_with($hour, '$')) { + throw new InvalidArgumentException('Argument $hour can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->hour = $hour; + if (is_string($minute) && ! str_starts_with($minute, '$')) { + throw new InvalidArgumentException('Argument $minute can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->minute = $minute; + if (is_string($second) && ! str_starts_with($second, '$')) { + throw new InvalidArgumentException('Argument $second can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->second = $second; + if (is_string($millisecond) && ! str_starts_with($millisecond, '$')) { + throw new InvalidArgumentException('Argument $millisecond can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->millisecond = $millisecond; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DateFromStringOperator.php b/src/Builder/Expression/DateFromStringOperator.php new file mode 100644 index 000000000..ad796df98 --- /dev/null +++ b/src/Builder/Expression/DateFromStringOperator.php @@ -0,0 +1,85 @@ + 'dateString', + 'format' => 'format', + 'timezone' => 'timezone', + 'onError' => 'onError', + 'onNull' => 'onNull', + ]; + + /** @var ResolvesToString|string $dateString The date/time string to convert to a date object. */ + public readonly ResolvesToString|string $dateString; + + /** + * @var Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + */ + public readonly Optional|ResolvesToString|string $format; + + /** @var Optional|ResolvesToString|string $timezone The time zone to use to format the date. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onError If $dateFromString encounters an error while parsing the given dateString, it outputs the result value of the provided onError expression. This result value can be of any type. + * If you do not specify onError, $dateFromString throws an error if it cannot parse dateString. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onError; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull If the dateString provided to $dateFromString is null or missing, it outputs the result value of the provided onNull expression. This result value can be of any type. + * If you do not specify onNull and dateString is null or missing, then $dateFromString outputs null. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull; + + /** + * @param ResolvesToString|string $dateString The date/time string to convert to a date object. + * @param Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + * @param Optional|ResolvesToString|string $timezone The time zone to use to format the date. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onError If $dateFromString encounters an error while parsing the given dateString, it outputs the result value of the provided onError expression. This result value can be of any type. + * If you do not specify onError, $dateFromString throws an error if it cannot parse dateString. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull If the dateString provided to $dateFromString is null or missing, it outputs the result value of the provided onNull expression. This result value can be of any type. + * If you do not specify onNull and dateString is null or missing, then $dateFromString outputs null. + */ + public function __construct( + ResolvesToString|string $dateString, + Optional|ResolvesToString|string $format = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onError = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull = Optional::Undefined, + ) { + $this->dateString = $dateString; + $this->format = $format; + $this->timezone = $timezone; + $this->onError = $onError; + $this->onNull = $onNull; + } +} diff --git a/src/Builder/Expression/DateSubtractOperator.php b/src/Builder/Expression/DateSubtractOperator.php new file mode 100644 index 000000000..91c790872 --- /dev/null +++ b/src/Builder/Expression/DateSubtractOperator.php @@ -0,0 +1,74 @@ + 'startDate', 'unit' => 'unit', 'amount' => 'amount', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate; + + /** @var ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. */ + public readonly ResolvesToString|TimeUnit|string $unit; + + /** @var Int64|ResolvesToInt|ResolvesToLong|int|string $amount */ + public readonly Int64|ResolvesToInt|ResolvesToLong|int|string $amount; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. + * @param Int64|ResolvesToInt|ResolvesToLong|int|string $amount + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + ResolvesToString|TimeUnit|string $unit, + Int64|ResolvesToInt|ResolvesToLong|int|string $amount, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($startDate) && ! str_starts_with($startDate, '$')) { + throw new InvalidArgumentException('Argument $startDate can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->startDate = $startDate; + $this->unit = $unit; + if (is_string($amount) && ! str_starts_with($amount, '$')) { + throw new InvalidArgumentException('Argument $amount can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->amount = $amount; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DateToPartsOperator.php b/src/Builder/Expression/DateToPartsOperator.php new file mode 100644 index 000000000..8e23c751c --- /dev/null +++ b/src/Builder/Expression/DateToPartsOperator.php @@ -0,0 +1,62 @@ + 'date', 'timezone' => 'timezone', 'iso8601' => 'iso8601']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The input date for which to return parts. date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** @var Optional|bool $iso8601 If set to true, modifies the output document to use ISO week date fields. Defaults to false. */ + public readonly Optional|bool $iso8601; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The input date for which to return parts. date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|bool $iso8601 If set to true, modifies the output document to use ISO week date fields. Defaults to false. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|bool $iso8601 = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + $this->iso8601 = $iso8601; + } +} diff --git a/src/Builder/Expression/DateToStringOperator.php b/src/Builder/Expression/DateToStringOperator.php new file mode 100644 index 000000000..703ac643e --- /dev/null +++ b/src/Builder/Expression/DateToStringOperator.php @@ -0,0 +1,79 @@ + 'date', 'format' => 'format', 'timezone' => 'timezone', 'onNull' => 'onNull']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to convert to string. Must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** + * @var Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + */ + public readonly Optional|ResolvesToString|string $format; + + /** @var Optional|ResolvesToString|string $timezone The time zone to use to format the date. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull The value to return if the date is null or missing. + * If unspecified, $dateToString returns null if the date is null or missing. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to convert to string. Must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + * @param Optional|ResolvesToString|string $timezone The time zone to use to format the date. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull The value to return if the date is null or missing. + * If unspecified, $dateToString returns null if the date is null or missing. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $format = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->format = $format; + $this->timezone = $timezone; + $this->onNull = $onNull; + } +} diff --git a/src/Builder/Expression/DateTruncOperator.php b/src/Builder/Expression/DateTruncOperator.php new file mode 100644 index 000000000..af550491f --- /dev/null +++ b/src/Builder/Expression/DateTruncOperator.php @@ -0,0 +1,100 @@ + 'date', + 'unit' => 'unit', + 'binSize' => 'binSize', + 'timezone' => 'timezone', + 'startOfWeek' => 'startOfWeek', + ]; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to truncate, specified in UTC. The date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** + * @var ResolvesToString|TimeUnit|string $unit The unit of time, specified as an expression that must resolve to one of these strings: year, quarter, week, month, day, hour, minute, second. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + */ + public readonly ResolvesToString|TimeUnit|string $unit; + + /** + * @var Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize The numeric time value, specified as an expression that must resolve to a positive non-zero number. Defaults to 1. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + */ + public readonly Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize; + + /** @var Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @var Optional|string $startOfWeek The start of the week. Used when + * unit is week. Defaults to Sunday. + */ + public readonly Optional|string $startOfWeek; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to truncate, specified in UTC. The date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit of time, specified as an expression that must resolve to one of these strings: year, quarter, week, month, day, hour, minute, second. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize The numeric time value, specified as an expression that must resolve to a positive non-zero number. Defaults to 1. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|string $startOfWeek The start of the week. Used when + * unit is week. Defaults to Sunday. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + ResolvesToString|TimeUnit|string $unit, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|string $startOfWeek = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->unit = $unit; + if (is_string($binSize) && ! str_starts_with($binSize, '$')) { + throw new InvalidArgumentException('Argument $binSize can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->binSize = $binSize; + $this->timezone = $timezone; + $this->startOfWeek = $startOfWeek; + } +} diff --git a/src/Builder/Expression/DayOfMonthOperator.php b/src/Builder/Expression/DayOfMonthOperator.php new file mode 100644 index 000000000..ce3df4a86 --- /dev/null +++ b/src/Builder/Expression/DayOfMonthOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DayOfWeekOperator.php b/src/Builder/Expression/DayOfWeekOperator.php new file mode 100644 index 000000000..db07c7a2a --- /dev/null +++ b/src/Builder/Expression/DayOfWeekOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DayOfYearOperator.php b/src/Builder/Expression/DayOfYearOperator.php new file mode 100644 index 000000000..530fc619c --- /dev/null +++ b/src/Builder/Expression/DayOfYearOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/DecimalFieldPath.php b/src/Builder/Expression/DecimalFieldPath.php new file mode 100644 index 000000000..2a9136675 --- /dev/null +++ b/src/Builder/Expression/DecimalFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/DegreesToRadiansOperator.php b/src/Builder/Expression/DegreesToRadiansOperator.php new file mode 100644 index 000000000..45482c733 --- /dev/null +++ b/src/Builder/Expression/DegreesToRadiansOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $degreesToRadians takes any valid expression that resolves to a number. + * By default $degreesToRadians returns values as a double. $degreesToRadians can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $degreesToRadians takes any valid expression that resolves to a number. + * By default $degreesToRadians returns values as a double. $degreesToRadians can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/DivideOperator.php b/src/Builder/Expression/DivideOperator.php new file mode 100644 index 000000000..55ba88b21 --- /dev/null +++ b/src/Builder/Expression/DivideOperator.php @@ -0,0 +1,57 @@ + 'dividend', 'divisor' => 'divisor']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. the first argument is divided by the second argument. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $dividend; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $divisor */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $divisor; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. the first argument is divided by the second argument. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $divisor + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $dividend, + Decimal128|Int64|ResolvesToNumber|float|int|string $divisor, + ) { + if (is_string($dividend) && ! str_starts_with($dividend, '$')) { + throw new InvalidArgumentException('Argument $dividend can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->dividend = $dividend; + if (is_string($divisor) && ! str_starts_with($divisor, '$')) { + throw new InvalidArgumentException('Argument $divisor can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->divisor = $divisor; + } +} diff --git a/src/Builder/Expression/DoubleFieldPath.php b/src/Builder/Expression/DoubleFieldPath.php new file mode 100644 index 000000000..2af25b87c --- /dev/null +++ b/src/Builder/Expression/DoubleFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/EqOperator.php b/src/Builder/Expression/EqOperator.php new file mode 100644 index 000000000..5db6ed26a --- /dev/null +++ b/src/Builder/Expression/EqOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/ExpOperator.php b/src/Builder/Expression/ExpOperator.php new file mode 100644 index 000000000..5aab19c1e --- /dev/null +++ b/src/Builder/Expression/ExpOperator.php @@ -0,0 +1,46 @@ + 'exponent']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $exponent */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $exponent; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $exponent + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $exponent) + { + if (is_string($exponent) && ! str_starts_with($exponent, '$')) { + throw new InvalidArgumentException('Argument $exponent can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->exponent = $exponent; + } +} diff --git a/src/Builder/Expression/ExpressionFactoryTrait.php b/src/Builder/Expression/ExpressionFactoryTrait.php new file mode 100644 index 000000000..3f5f04339 --- /dev/null +++ b/src/Builder/Expression/ExpressionFactoryTrait.php @@ -0,0 +1,105 @@ + resolves to a 128-bit decimal value. + */ + public static function cos(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): CosOperator + { + return new CosOperator($expression); + } + + /** + * Returns the hyperbolic cosine of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/cosh/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $cosh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $cosh returns values as a double. $cosh can also return values as a 128-bit decimal if the resolves to a 128-bit decimal value. + */ + public static function cosh(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): CoshOperator + { + return new CoshOperator($expression); + } + + /** + * Returns a random object ID + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/createObjectId/ + */ + public static function createObjectId(): CreateObjectIdOperator + { + return new CreateObjectIdOperator(); + } + + /** + * Adds a number of time units to a date object. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateAdd/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. + * @param Int64|ResolvesToInt|ResolvesToLong|int|string $amount + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dateAdd( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + ResolvesToString|TimeUnit|string $unit, + Int64|ResolvesToInt|ResolvesToLong|int|string $amount, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DateAddOperator { + return new DateAddOperator($startDate, $unit, $amount, $timezone); + } + + /** + * Returns the difference between two dates. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateDiff/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The start of the time period. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $endDate The end of the time period. The endDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The time measurement unit between the startDate and endDate + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|ResolvesToString|string $startOfWeek Used when the unit is equal to week. Defaults to Sunday. The startOfWeek parameter is an expression that resolves to a case insensitive string + */ + public static function dateDiff( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $endDate, + ResolvesToString|TimeUnit|string $unit, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|ResolvesToString|string $startOfWeek = Optional::Undefined, + ): DateDiffOperator { + return new DateDiffOperator($startDate, $endDate, $unit, $timezone, $startOfWeek); + } + + /** + * Constructs a BSON Date object given the date's constituent parts. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromParts/ + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year Calendar year. Can be any expression that evaluates to a number. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear ISO Week Date Year. Can be any expression that evaluates to a number. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month Month. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek Week of year. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day Day of month. Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek Day of week (Monday 1 - Sunday 7). Defaults to 1. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour Hour. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute Minute. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second Second. Defaults to 0. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond Millisecond. Defaults to 0. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dateFromParts( + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $year = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeekYear = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $month = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoWeek = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $day = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $isoDayOfWeek = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $hour = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $minute = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $second = Optional::Undefined, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $millisecond = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DateFromPartsOperator { + return new DateFromPartsOperator($year, $isoWeekYear, $month, $isoWeek, $day, $isoDayOfWeek, $hour, $minute, $second, $millisecond, $timezone); + } + + /** + * Converts a date/time string to a date object. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateFromString/ + * @param ResolvesToString|string $dateString The date/time string to convert to a date object. + * @param Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + * @param Optional|ResolvesToString|string $timezone The time zone to use to format the date. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onError If $dateFromString encounters an error while parsing the given dateString, it outputs the result value of the provided onError expression. This result value can be of any type. + * If you do not specify onError, $dateFromString throws an error if it cannot parse dateString. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull If the dateString provided to $dateFromString is null or missing, it outputs the result value of the provided onNull expression. This result value can be of any type. + * If you do not specify onNull and dateString is null or missing, then $dateFromString outputs null. + */ + public static function dateFromString( + ResolvesToString|string $dateString, + Optional|ResolvesToString|string $format = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onError = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull = Optional::Undefined, + ): DateFromStringOperator { + return new DateFromStringOperator($dateString, $format, $timezone, $onError, $onNull); + } + + /** + * Subtracts a number of time units from a date object. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateSubtract/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $startDate The beginning date, in UTC, for the addition operation. The startDate can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit used to measure the amount of time added to the startDate. + * @param Int64|ResolvesToInt|ResolvesToLong|int|string $amount + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dateSubtract( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $startDate, + ResolvesToString|TimeUnit|string $unit, + Int64|ResolvesToInt|ResolvesToLong|int|string $amount, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DateSubtractOperator { + return new DateSubtractOperator($startDate, $unit, $amount, $timezone); + } + + /** + * Returns a document containing the constituent parts of a date. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToParts/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The input date for which to return parts. date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|bool $iso8601 If set to true, modifies the output document to use ISO week date fields. Defaults to false. + */ + public static function dateToParts( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|bool $iso8601 = Optional::Undefined, + ): DateToPartsOperator { + return new DateToPartsOperator($date, $timezone, $iso8601); + } + + /** + * Returns the date as a formatted string. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateToString/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to convert to string. Must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $format The date format specification of the dateString. The format can be any expression that evaluates to a string literal, containing 0 or more format specifiers. + * If unspecified, $dateFromString uses "%Y-%m-%dT%H:%M:%S.%LZ" as the default format but accepts a variety of formats and attempts to parse the dateString if possible. + * @param Optional|ResolvesToString|string $timezone The time zone to use to format the date. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $onNull The value to return if the date is null or missing. + * If unspecified, $dateToString returns null if the date is null or missing. + */ + public static function dateToString( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $format = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $onNull = Optional::Undefined, + ): DateToStringOperator { + return new DateToStringOperator($date, $format, $timezone, $onNull); + } + + /** + * Truncates a date. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dateTrunc/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to truncate, specified in UTC. The date can be any expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param ResolvesToString|TimeUnit|string $unit The unit of time, specified as an expression that must resolve to one of these strings: year, quarter, week, month, day, hour, minute, second. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + * @param Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize The numeric time value, specified as an expression that must resolve to a positive non-zero number. Defaults to 1. + * Together, binSize and unit specify the time period used in the $dateTrunc calculation. + * @param Optional|ResolvesToString|string $timezone The timezone to carry out the operation. $timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + * @param Optional|string $startOfWeek The start of the week. Used when + * unit is week. Defaults to Sunday. + */ + public static function dateTrunc( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + ResolvesToString|TimeUnit|string $unit, + Optional|Decimal128|Int64|ResolvesToNumber|float|int|string $binSize = Optional::Undefined, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + Optional|string $startOfWeek = Optional::Undefined, + ): DateTruncOperator { + return new DateTruncOperator($date, $unit, $binSize, $timezone, $startOfWeek); + } + + /** + * Returns the day of the month for a date as a number between 1 and 31. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfMonth/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dayOfMonth( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DayOfMonthOperator { + return new DayOfMonthOperator($date, $timezone); + } + + /** + * Returns the day of the week for a date as a number between 1 (Sunday) and 7 (Saturday). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfWeek/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dayOfWeek( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DayOfWeekOperator { + return new DayOfWeekOperator($date, $timezone); + } + + /** + * Returns the day of the year for a date as a number between 1 and 366 (leap year). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/dayOfYear/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function dayOfYear( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): DayOfYearOperator { + return new DayOfYearOperator($date, $timezone); + } + + /** + * Converts a value from degrees to radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/degreesToRadians/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $degreesToRadians takes any valid expression that resolves to a number. + * By default $degreesToRadians returns values as a double. $degreesToRadians can also return values as a 128-bit decimal as long as the resolves to a 128-bit decimal value. + */ + public static function degreesToRadians( + Decimal128|Int64|ResolvesToNumber|float|int|string $expression, + ): DegreesToRadiansOperator { + return new DegreesToRadiansOperator($expression); + } + + /** + * Returns the result of dividing the first number by the second. Accepts two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/divide/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. the first argument is divided by the second argument. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $divisor + */ + public static function divide( + Decimal128|Int64|ResolvesToNumber|float|int|string $dividend, + Decimal128|Int64|ResolvesToNumber|float|int|string $divisor, + ): DivideOperator { + return new DivideOperator($dividend, $divisor); + } + + /** + * Returns true if the values are equivalent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/eq/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function eq( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): EqOperator { + return new EqOperator($expression1, $expression2); + } + + /** + * Raises e to the specified exponent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/exp/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $exponent + */ + public static function exp(Decimal128|Int64|ResolvesToNumber|float|int|string $exponent): ExpOperator + { + return new ExpOperator($exponent); + } + + /** + * Selects a subset of the array to return an array with only the elements that match the filter condition. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input + * @param ResolvesToBool|bool|string $cond An expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as. + * @param Optional|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + * @param Optional|ResolvesToInt|int|string $limit A number expression that restricts the number of matching array elements that $filter returns. You cannot specify a limit less than 1. The matching array elements are returned in the order they appear in the input array. + * If the specified limit is greater than the number of matching array elements, $filter returns all matching array elements. If the limit is null, $filter returns all matching array elements. + */ + public static function filter( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToBool|bool|string $cond, + Optional|string $as = Optional::Undefined, + Optional|ResolvesToInt|int|string $limit = Optional::Undefined, + ): FilterOperator { + return new FilterOperator($input, $cond, $as, $limit); + } + + /** + * Returns the result of an expression for the first document in an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/first/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public static function first(PackedArray|ResolvesToArray|BSONArray|array|string $expression): FirstOperator + { + return new FirstOperator($expression); + } + + /** + * Returns a specified number of elements from the beginning of an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/firstN-array-element/ + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. + */ + public static function firstN( + ResolvesToInt|int|string $n, + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ): FirstNOperator { + return new FirstNOperator($n, $input); + } + + /** + * Returns the largest integer less than or equal to the specified number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/floor/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public static function floor(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): FloorOperator + { + return new FloorOperator($expression); + } + + /** + * Defines a custom function. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/function/ + * @param Javascript|string $body The function definition. You can specify the function definition as either BSON\JavaScript or string. + * function(arg1, arg2, ...) { ... } + * @param BSONArray|PackedArray|array $args Arguments passed to the function body. If the body function does not take an argument, you can specify an empty array [ ]. + * @param string $lang + */ + public static function function( + Javascript|string $body, + PackedArray|BSONArray|array $args = [], + string $lang = 'js', + ): FunctionOperator { + return new FunctionOperator($body, $args, $lang); + } + + /** + * Returns the value of a specified field from a document. You can use $getField to retrieve the value of fields with names that contain periods (.) or start with dollar signs ($). + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/getField/ + * @param ResolvesToString|string $field Field in the input object for which you want to return a value. field can be any valid expression that resolves to a string constant. + * If field begins with a dollar sign ($), place the field name inside of a $literal expression to return its value. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input Default: $$CURRENT + * A valid expression that contains the field for which you want to return a value. input must resolve to an object, missing, null, or undefined. If omitted, defaults to the document currently being processed in the pipeline ($$CURRENT). + */ + public static function getField( + ResolvesToString|string $field, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input = Optional::Undefined, + ): GetFieldOperator { + return new GetFieldOperator($field, $input); + } + + /** + * Returns true if the first value is greater than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/gt/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function gt( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): GtOperator { + return new GtOperator($expression1, $expression2); + } + + /** + * Returns true if the first value is greater than or equal to the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/gte/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function gte( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): GteOperator { + return new GteOperator($expression1, $expression2); + } + + /** + * Returns the hour for a date as a number between 0 and 23. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/hour/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function hour( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): HourOperator { + return new HourOperator($date, $timezone); + } + + /** + * Returns either the non-null result of the first expression or the result of the second expression if the first expression results in a null result. Null result encompasses instances of undefined values or missing fields. Accepts two expressions as arguments. The result of the second expression can be null. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ifNull/ + * @no-named-arguments + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + */ + public static function ifNull( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ): IfNullOperator { + return new IfNullOperator(...$expression); + } + + /** + * Returns a boolean indicating whether a specified value is in an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/in/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression Any valid expression expression. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array Any valid expression that resolves to an array. + */ + public static function in( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + PackedArray|ResolvesToArray|BSONArray|array|string $array, + ): InOperator { + return new InOperator($expression, $array); + } + + /** + * Searches an array for an occurrence of a specified value and returns the array index of the first occurrence. Array indexes start at zero. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfArray/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array Can be any valid expression as long as it resolves to an array. + * If the array expression resolves to a value of null or refers to a field that is missing, $indexOfArray returns null. + * If the array expression does not resolve to an array or null nor refers to a missing field, $indexOfArray returns an error. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $search + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public static function indexOfArray( + PackedArray|ResolvesToArray|BSONArray|array|string $array, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $search, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ): IndexOfArrayOperator { + return new IndexOfArrayOperator($array, $search, $start, $end); + } + + /** + * Searches a string for an occurrence of a substring and returns the UTF-8 byte index of the first occurrence. If the substring is not found, returns -1. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfBytes/ + * @param ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfBytes returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfBytes returns an error. + * @param ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public static function indexOfBytes( + ResolvesToString|string $string, + ResolvesToString|string $substring, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ): IndexOfBytesOperator { + return new IndexOfBytesOperator($string, $substring, $start, $end); + } + + /** + * Searches a string for an occurrence of a substring and returns the UTF-8 code point index of the first occurrence. If the substring is not found, returns -1 + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexOfCP/ + * @param ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfCP returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfCP returns an error. + * @param ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public static function indexOfCP( + ResolvesToString|string $string, + ResolvesToString|string $substring, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ): IndexOfCPOperator { + return new IndexOfCPOperator($string, $substring, $start, $end); + } + + /** + * Determines if the operand is an array. Returns a boolean. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isArray/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function isArray( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): IsArrayOperator { + return new IsArrayOperator($expression); + } + + /** + * Returns boolean true if the specified expression resolves to an integer, decimal, double, or long. + * Returns boolean false if the expression resolves to any other BSON type, null, or a missing field. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isNumber/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function isNumber( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): IsNumberOperator { + return new IsNumberOperator($expression); + } + + /** + * Returns the weekday number in ISO 8601 format, ranging from 1 (for Monday) to 7 (for Sunday). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoDayOfWeek/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function isoDayOfWeek( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): IsoDayOfWeekOperator { + return new IsoDayOfWeekOperator($date, $timezone); + } + + /** + * Returns the week number in ISO 8601 format, ranging from 1 to 53. Week numbers start at 1 with the week (Monday through Sunday) that contains the year's first Thursday. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeek/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function isoWeek( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): IsoWeekOperator { + return new IsoWeekOperator($date, $timezone); + } + + /** + * Returns the year number in ISO 8601 format. The year starts with the Monday of week 1 (ISO 8601) and ends with the Sunday of the last week (ISO 8601). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/isoWeekYear/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function isoWeekYear( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): IsoWeekYearOperator { + return new IsoWeekYearOperator($date, $timezone); + } + + /** + * Returns the result of an expression for the last document in an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/last/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public static function last(PackedArray|ResolvesToArray|BSONArray|array|string $expression): LastOperator + { + return new LastOperator($expression); + } + + /** + * Returns a specified number of elements from the end of an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lastN-array-element/ + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. + */ + public static function lastN( + ResolvesToInt|int|string $n, + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ): LastNOperator { + return new LastNOperator($n, $input); + } + + /** + * Defines variables for use within the scope of a subexpression and returns the result of the subexpression. Accepts named parameters. + * Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/ + * @param Document|Serializable|array|stdClass $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. + * The variable assignments have no meaning outside the in expression, not even within the vars block itself. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in The expression to evaluate. + */ + public static function let( + Document|Serializable|stdClass|array $vars, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + ): LetOperator { + return new LetOperator($vars, $in); + } + + /** + * Return a value without parsing. Use for values that the aggregation pipeline may interpret as an expression. For example, use a $literal expression to a string that starts with a dollar sign ($) to avoid parsing as a field path. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/literal/ + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value If the value is an expression, $literal does not evaluate the expression but instead returns the unparsed expression. + */ + public static function literal( + DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value, + ): LiteralOperator { + return new LiteralOperator($value); + } + + /** + * Calculates the natural log of a number. + * $ln is equivalent to $log: [ , Math.E ] expression, where Math.E is a JavaScript representation for Euler's number e. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ln/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. For more information on expressions, see Expressions. + */ + public static function ln(Decimal128|Int64|ResolvesToNumber|float|int|string $number): LnOperator + { + return new LnOperator($number); + } + + /** + * Calculates the log of a number in the specified base. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/log/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $base Any valid expression as long as it resolves to a positive number greater than 1. + */ + public static function log( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Decimal128|Int64|ResolvesToNumber|float|int|string $base, + ): LogOperator { + return new LogOperator($number, $base); + } + + /** + * Calculates the log base 10 of a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/log10/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. + */ + public static function log10(Decimal128|Int64|ResolvesToNumber|float|int|string $number): Log10Operator + { + return new Log10Operator($number); + } + + /** + * Returns true if the first value is less than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lt/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function lt( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): LtOperator { + return new LtOperator($expression1, $expression2); + } + + /** + * Returns true if the first value is less than or equal to the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lte/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function lte( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): LteOperator { + return new LteOperator($expression1, $expression2); + } + + /** + * Removes whitespace or the specified characters from the beginning of a string. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ltrim/ + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public static function ltrim( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ): LtrimOperator { + return new LtrimOperator($input, $chars); + } + + /** + * Applies a subexpression to each element of an array and returns the array of resulting values in order. Accepts named parameters. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/map/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to an array. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in An expression that is applied to each element of the input array. The expression references each element individually with the variable name specified in as. + * @param Optional|ResolvesToString|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + */ + public static function map( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + Optional|ResolvesToString|string $as = Optional::Undefined, + ): MapOperator { + return new MapOperator($input, $in, $as); + } + + /** + * Returns the maximum value that results from applying an expression to each document. + * Changed in MongoDB 5.0: Available in the $setWindowFields stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/max/ + * @no-named-arguments + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + */ + public static function max( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ): MaxOperator { + return new MaxOperator(...$expression); + } + + /** + * Returns the n largest values in an array. Distinct from the $maxN accumulator. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/maxN-array-element/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public static function maxN( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ): MaxNOperator { + return new MaxNOperator($input, $n); + } + + /** + * Returns an approximation of the median, the 50th percentile, as a scalar value. + * New in MongoDB 7.0. + * This operator is available as an accumulator in these stages: + * $group + * $setWindowFields + * It is also available as an aggregation expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/median/ + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. + * @param string $method The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. + */ + public static function median( + Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input, + string $method, + ): MedianOperator { + return new MedianOperator($input, $method); + } + + /** + * Combines multiple documents into a single document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/mergeObjects/ + * @no-named-arguments + * @param Document|ResolvesToObject|Serializable|array|stdClass|string ...$document Any valid expression that resolves to a document. + */ + public static function mergeObjects( + Document|Serializable|ResolvesToObject|stdClass|array|string ...$document, + ): MergeObjectsOperator { + return new MergeObjectsOperator(...$document); + } + + /** + * Access available per-document metadata related to the aggregation operation. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/meta/ + * @param string $keyword + */ + public static function meta(string $keyword): MetaOperator + { + return new MetaOperator($keyword); + } + + /** + * Returns the milliseconds of a date as a number between 0 and 999. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/millisecond/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function millisecond( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): MillisecondOperator { + return new MillisecondOperator($date, $timezone); + } + + /** + * Returns the minimum value that results from applying an expression to each document. + * Changed in MongoDB 5.0: Available in the $setWindowFields stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/min/ + * @no-named-arguments + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + */ + public static function min( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ): MinOperator { + return new MinOperator(...$expression); + } + + /** + * Returns the n smallest values in an array. Distinct from the $minN accumulator. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minN-array-element/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public static function minN( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ): MinNOperator { + return new MinNOperator($input, $n); + } + + /** + * Returns the minute for a date as a number between 0 and 59. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/minute/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function minute( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): MinuteOperator { + return new MinuteOperator($date, $timezone); + } + + /** + * Returns the remainder of the first number divided by the second. Accepts two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/mod/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. first argument is divided by the second argument. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $divisor + */ + public static function mod( + Decimal128|Int64|ResolvesToNumber|float|int|string $dividend, + Decimal128|Int64|ResolvesToNumber|float|int|string $divisor, + ): ModOperator { + return new ModOperator($dividend, $divisor); + } + + /** + * Returns the month for a date as a number between 1 (January) and 12 (December). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/month/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function month( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): MonthOperator { + return new MonthOperator($date, $timezone); + } + + /** + * Multiplies numbers to return the product. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/multiply/ + * @no-named-arguments + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression The arguments can be any valid expression as long as they resolve to numbers. + * Starting in MongoDB 6.1 you can optimize the $multiply operation. To improve performance, group references at the end of the argument list. + */ + public static function multiply( + Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression, + ): MultiplyOperator { + return new MultiplyOperator(...$expression); + } + + /** + * Returns true if the values are not equivalent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ne/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public static function ne( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ): NeOperator { + return new NeOperator($expression1, $expression2); + } + + /** + * Returns the boolean value that is the opposite of its argument expression. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/not/ + * @param DateTimeInterface|ExpressionInterface|ResolvesToBool|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function not( + DateTimeInterface|Type|ResolvesToBool|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): NotOperator { + return new NotOperator($expression); + } + + /** + * Converts a document to an array of documents representing key-value pairs. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/objectToArray/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $object Any valid expression as long as it resolves to a document object. $objectToArray applies to the top-level fields of its argument. If the argument is a document that itself contains embedded document fields, the $objectToArray does not recursively apply to the embedded document fields. + */ + public static function objectToArray( + Document|Serializable|ResolvesToObject|stdClass|array|string $object, + ): ObjectToArrayOperator { + return new ObjectToArrayOperator($object); + } + + /** + * Returns true when any of its expressions evaluates to true. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/or/ + * @no-named-arguments + * @param DateTimeInterface|ExpressionInterface|ResolvesToBool|Type|array|bool|float|int|null|stdClass|string ...$expression + */ + public static function or( + DateTimeInterface|Type|ResolvesToBool|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ): OrOperator { + return new OrOperator(...$expression); + } + + /** + * Returns an array of scalar values that correspond to specified percentile values. + * New in MongoDB 7.0. + * + * This operator is available as an accumulator in these stages: + * $group + * + * $setWindowFields + * + * It is also available as an aggregation expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/percentile/ + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $p $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + * $percentile returns results in the same order as the elements in p. + * @param string $method The method that mongod uses to calculate the percentile value. The method must be 'approximate'. + */ + public static function percentile( + Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input, + PackedArray|ResolvesToArray|BSONArray|array|string $p, + string $method, + ): PercentileOperator { + return new PercentileOperator($input, $p, $method); + } + + /** + * Raises a number to the specified exponent. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/pow/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $exponent + */ + public static function pow( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Decimal128|Int64|ResolvesToNumber|float|int|string $exponent, + ): PowOperator { + return new PowOperator($number, $exponent); + } + + /** + * Converts a value from radians to degrees. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/radiansToDegrees/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public static function radiansToDegrees( + Decimal128|Int64|ResolvesToNumber|float|int|string $expression, + ): RadiansToDegreesOperator { + return new RadiansToDegreesOperator($expression); + } + + /** + * Returns a random float between 0 and 1 + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rand/ + */ + public static function rand(): RandOperator + { + return new RandOperator(); + } + + /** + * Outputs an array containing a sequence of integers according to user-defined inputs. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/range/ + * @param ResolvesToInt|int|string $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. + * @param ResolvesToInt|int|string $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. + * @param Optional|ResolvesToInt|int|string $step An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. + */ + public static function range( + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $end, + Optional|ResolvesToInt|int|string $step = Optional::Undefined, + ): RangeOperator { + return new RangeOperator($start, $end, $step); + } + + /** + * Applies an expression to each element in an array and combines them into a single value. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/reduce/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input Can be any valid expression that resolves to an array. + * If the argument resolves to a value of null or refers to a missing field, $reduce returns null. + * If the argument does not resolve to an array or null nor refers to a missing field, $reduce returns an error. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $initialValue The initial cumulative value set before in is applied to the first element of the input array. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + * During evaluation of the in expression, two variables will be available: + * - value is the variable that represents the cumulative value of the expression. + * - this is the variable that refers to the element being processed. + */ + public static function reduce( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $initialValue, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + ): ReduceOperator { + return new ReduceOperator($input, $initialValue, $in); + } + + /** + * Applies a regular expression (regex) to a string and returns information on the first matched substring. + * New in MongoDB 4.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFind/ + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public static function regexFind( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ): RegexFindOperator { + return new RegexFindOperator($input, $regex, $options); + } + + /** + * Applies a regular expression (regex) to a string and returns information on the all matched substrings. + * New in MongoDB 4.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexFindAll/ + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public static function regexFindAll( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ): RegexFindAllOperator { + return new RegexFindAllOperator($input, $regex, $options); + } + + /** + * Applies a regular expression (regex) to a string and returns a boolean that indicates if a match is found or not. + * New in MongoDB 4.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/regexMatch/ + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public static function regexMatch( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ): RegexMatchOperator { + return new RegexMatchOperator($input, $regex, $options); + } + + /** + * Replaces all instances of a search string in an input string with a replacement string. + * $replaceAll is both case-sensitive and diacritic-sensitive, and ignores any collation present on a collection. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceAll/ + * @param ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + * @param Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. + */ + public static function replaceAll( + ResolvesToNull|ResolvesToString|null|string $input, + Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find, + ResolvesToNull|ResolvesToString|null|string $replacement, + ): ReplaceAllOperator { + return new ReplaceAllOperator($input, $find, $replacement); + } + + /** + * Replaces the first instance of a matched string in a given input. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceOne/ + * @param ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. + */ + public static function replaceOne( + ResolvesToNull|ResolvesToString|null|string $input, + ResolvesToNull|ResolvesToString|null|string $find, + ResolvesToNull|ResolvesToString|null|string $replacement, + ): ReplaceOneOperator { + return new ReplaceOneOperator($input, $find, $replacement); + } + + /** + * Returns an array with the elements in reverse order. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/reverseArray/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument can be any valid expression as long as it resolves to an array. + */ + public static function reverseArray( + PackedArray|ResolvesToArray|BSONArray|array|string $expression, + ): ReverseArrayOperator { + return new ReverseArrayOperator($expression); + } + + /** + * Rounds a number to a whole integer or to a specified decimal place. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/round/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $round returns an error if the expression resolves to a non-numeric data type. + * @param Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. + */ + public static function round( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Optional|ResolvesToInt|int|string $place = Optional::Undefined, + ): RoundOperator { + return new RoundOperator($number, $place); + } + + /** + * Removes whitespace characters, including null, or the specified characters from the end of a string. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/rtrim/ + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public static function rtrim( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ): RtrimOperator { + return new RtrimOperator($input, $chars); + } + + /** + * Returns the seconds for a date as a number between 0 and 60 (leap seconds). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/second/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function second( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): SecondOperator { + return new SecondOperator($date, $timezone); + } + + /** + * Returns a set with elements that appear in the first set but not in the second set; i.e. performs a relative complement of the second set relative to the first. Accepts exactly two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setDifference/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression1 The arguments can be any valid expression as long as they each resolve to an array. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression2 The arguments can be any valid expression as long as they each resolve to an array. + */ + public static function setDifference( + PackedArray|ResolvesToArray|BSONArray|array|string $expression1, + PackedArray|ResolvesToArray|BSONArray|array|string $expression2, + ): SetDifferenceOperator { + return new SetDifferenceOperator($expression1, $expression2); + } + + /** + * Returns true if the input sets have the same distinct elements. Accepts two or more argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setEquals/ + * @no-named-arguments + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + */ + public static function setEquals( + PackedArray|ResolvesToArray|BSONArray|array|string ...$expression, + ): SetEqualsOperator { + return new SetEqualsOperator(...$expression); + } + + /** + * Adds, updates, or removes a specified field in a document. You can use $setField to add, update, or remove fields with names that contain periods (.) or start with dollar signs ($). + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setField/ + * @param ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value The value that you want to assign to field. value can be any valid expression. + * Set to $$REMOVE to remove field from the input document. + */ + public static function setField( + ResolvesToString|string $field, + Document|Serializable|ResolvesToObject|stdClass|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value, + ): SetFieldOperator { + return new SetFieldOperator($field, $input, $value); + } + + /** + * Returns a set with elements that appear in all of the input sets. Accepts any number of argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIntersection/ + * @no-named-arguments + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + */ + public static function setIntersection( + PackedArray|ResolvesToArray|BSONArray|array|string ...$expression, + ): SetIntersectionOperator { + return new SetIntersectionOperator(...$expression); + } + + /** + * Returns true if all elements of the first set appear in the second set, including when the first set equals the second set; i.e. not a strict subset. Accepts exactly two argument expressions. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setIsSubset/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression1 + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression2 + */ + public static function setIsSubset( + PackedArray|ResolvesToArray|BSONArray|array|string $expression1, + PackedArray|ResolvesToArray|BSONArray|array|string $expression2, + ): SetIsSubsetOperator { + return new SetIsSubsetOperator($expression1, $expression2); + } + + /** + * Returns a set with elements that appear in any of the input sets. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setUnion/ + * @no-named-arguments + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + */ + public static function setUnion( + PackedArray|ResolvesToArray|BSONArray|array|string ...$expression, + ): SetUnionOperator { + return new SetUnionOperator(...$expression); + } + + /** + * Returns the sine of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sin/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sin takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $sin returns values as a double. $sin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public static function sin(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): SinOperator + { + return new SinOperator($expression); + } + + /** + * Returns the hyperbolic sine of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sinh/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sinh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $sinh returns values as a double. $sinh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public static function sinh(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): SinhOperator + { + return new SinhOperator($expression); + } + + /** + * Returns the number of elements in the array. Accepts a single expression as argument. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/size/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument for $size can be any expression as long as it resolves to an array. + */ + public static function size(PackedArray|ResolvesToArray|BSONArray|array|string $expression): SizeOperator + { + return new SizeOperator($expression); + } + + /** + * Returns a subset of an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression Any valid expression as long as it resolves to an array. + * @param ResolvesToInt|int|string $n Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer. + * If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position. + * If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if is specified. + * @param Optional|ResolvesToInt|int|string $position Any valid expression as long as it resolves to an integer. + * If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array. + * If negative, $slice determines the starting position from the end of the array. If the absolute value of the is greater than the number of elements, the starting position is the start of the array. + */ + public static function slice( + PackedArray|ResolvesToArray|BSONArray|array|string $expression, + ResolvesToInt|int|string $n, + Optional|ResolvesToInt|int|string $position = Optional::Undefined, + ): SliceOperator { + return new SliceOperator($expression, $n, $position); + } + + /** + * Sorts the elements of an array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortArray/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input The array to be sorted. + * The result is null if the expression: is missing, evaluates to null, or evaluates to undefined + * If the expression evaluates to any other non-array value, the document returns an error. + * @param Document|Serializable|Sort|array|int|stdClass $sortBy The document specifies a sort ordering. + */ + public static function sortArray( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + Document|Serializable|Sort|stdClass|array|int $sortBy, + ): SortArrayOperator { + return new SortArrayOperator($input, $sortBy); + } + + /** + * Splits a string into substrings based on a delimiter. Returns an array of substrings. If the delimiter is not found within the string, returns an array containing the original string. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/split/ + * @param ResolvesToString|string $string The string to be split. string expression can be any valid expression as long as it resolves to a string. + * @param Regex|ResolvesToRegex|ResolvesToString|string $delimiter The delimiter to use when splitting the string expression. delimiter can be any valid expression as long as it resolves to a string. + */ + public static function split( + ResolvesToString|string $string, + Regex|ResolvesToRegex|ResolvesToString|string $delimiter, + ): SplitOperator { + return new SplitOperator($string, $delimiter); + } + + /** + * Calculates the square root. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sqrt/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number The argument can be any valid expression as long as it resolves to a non-negative number. + */ + public static function sqrt(Decimal128|Int64|ResolvesToNumber|float|int|string $number): SqrtOperator + { + return new SqrtOperator($number); + } + + /** + * Calculates the population standard deviation of the input values. Use if the values encompass the entire population of data you want to represent and do not wish to generalize about a larger population. $stdDevPop ignores non-numeric values. + * If the values represent only a sample of a population of data from which to generalize about the population, use $stdDevSamp instead. + * Changed in MongoDB 5.0: Available in the $setWindowFields stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevPop/ + * @no-named-arguments + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression + */ + public static function stdDevPop( + Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression, + ): StdDevPopOperator { + return new StdDevPopOperator(...$expression); + } + + /** + * Calculates the sample standard deviation of the input values. Use if the values encompass a sample of a population of data from which to generalize about the population. $stdDevSamp ignores non-numeric values. + * If the values represent the entire population of data or you do not wish to generalize about a larger population, use $stdDevPop instead. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/stdDevSamp/ + * @no-named-arguments + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression + */ + public static function stdDevSamp( + Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression, + ): StdDevSampOperator { + return new StdDevSampOperator(...$expression); + } + + /** + * Performs case-insensitive string comparison and returns: 0 if two strings are equivalent, 1 if the first string is greater than the second, and -1 if the first string is less than the second. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strcasecmp/ + * @param ResolvesToString|string $expression1 + * @param ResolvesToString|string $expression2 + */ + public static function strcasecmp( + ResolvesToString|string $expression1, + ResolvesToString|string $expression2, + ): StrcasecmpOperator { + return new StrcasecmpOperator($expression1, $expression2); + } + + /** + * Returns the number of UTF-8 encoded bytes in a string. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenBytes/ + * @param ResolvesToString|string $expression + */ + public static function strLenBytes(ResolvesToString|string $expression): StrLenBytesOperator + { + return new StrLenBytesOperator($expression); + } + + /** + * Returns the number of UTF-8 code points in a string. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/strLenCP/ + * @param ResolvesToString|string $expression + */ + public static function strLenCP(ResolvesToString|string $expression): StrLenCPOperator + { + return new StrLenCPOperator($expression); + } + + /** + * Deprecated. Use $substrBytes or $substrCP. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substr/ + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public static function substr( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ): SubstrOperator { + return new SubstrOperator($string, $start, $length); + } + + /** + * Returns the substring of a string. Starts with the character at the specified UTF-8 byte index (zero-based) in the string and continues for the specified number of bytes. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrBytes/ + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public static function substrBytes( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ): SubstrBytesOperator { + return new SubstrBytesOperator($string, $start, $length); + } + + /** + * Returns the substring of a string. Starts with the character at the specified UTF-8 code point (CP) index (zero-based) in the string and continues for the number of code points specified. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/substrCP/ + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public static function substrCP( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ): SubstrCPOperator { + return new SubstrCPOperator($string, $start, $length); + } + + /** + * Returns the result of subtracting the second value from the first. If the two values are numbers, return the difference. If the two values are dates, return the difference in milliseconds. If the two values are a date and a number in milliseconds, return the resulting date. Accepts two argument expressions. If the two values are a date and a number, specify the date argument first as it is not meaningful to subtract a date from a number. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/subtract/ + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression1 + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression2 + */ + public static function subtract( + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression1, + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression2, + ): SubtractOperator { + return new SubtractOperator($expression1, $expression2); + } + + /** + * Returns a sum of numerical values. Ignores non-numeric values. + * Changed in MongoDB 5.0: Available in the $setWindowFields stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sum/ + * @no-named-arguments + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToArray|ResolvesToNumber|array|float|int|string ...$expression + */ + public static function sum( + Decimal128|Int64|PackedArray|ResolvesToArray|ResolvesToNumber|BSONArray|array|float|int|string ...$expression, + ): SumOperator { + return new SumOperator(...$expression); + } + + /** + * Evaluates a series of case expressions. When it finds an expression which evaluates to true, $switch executes a specified expression and breaks out of the control flow. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/switch/ + * @param BSONArray|PackedArray|array $branches An array of control branch documents. Each branch is a document with the following fields: + * - case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + * - then Can be any valid expression. + * The branches array must contain at least one branch document. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default The path to take if no branch case expression evaluates to true. + * Although optional, if default is unspecified and no branch case evaluates to true, $switch returns an error. + */ + public static function switch( + PackedArray|BSONArray|array $branches, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default = Optional::Undefined, + ): SwitchOperator { + return new SwitchOperator($branches, $default); + } + + /** + * Returns the tangent of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tan/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tan takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $tan returns values as a double. $tan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public static function tan(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): TanOperator + { + return new TanOperator($expression); + } + + /** + * Returns the hyperbolic tangent of a value that is measured in radians. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tanh/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tanh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $tanh returns values as a double. $tanh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public static function tanh(Decimal128|Int64|ResolvesToNumber|float|int|string $expression): TanhOperator + { + return new TanhOperator($expression); + } + + /** + * Converts value to a boolean. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toBool/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toBool( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToBoolOperator { + return new ToBoolOperator($expression); + } + + /** + * Converts value to a Date. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDate/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toDate( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToDateOperator { + return new ToDateOperator($expression); + } + + /** + * Converts value to a Decimal128. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDecimal/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toDecimal( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToDecimalOperator { + return new ToDecimalOperator($expression); + } + + /** + * Converts value to a double. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toDouble/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toDouble( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToDoubleOperator { + return new ToDoubleOperator($expression); + } + + /** + * Computes and returns the hash value of the input expression using the same hash function that MongoDB uses to create a hashed index. A hash function maps a key or string to a fixed-size numeric value. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toHashedIndexKey/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value key or string to hash + */ + public static function toHashedIndexKey( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value, + ): ToHashedIndexKeyOperator { + return new ToHashedIndexKeyOperator($value); + } + + /** + * Converts value to an integer. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toInt/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toInt( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToIntOperator { + return new ToIntOperator($expression); + } + + /** + * Converts value to a long. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLong/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toLong( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToLongOperator { + return new ToLongOperator($expression); + } + + /** + * Converts a string to lowercase. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toLower/ + * @param ResolvesToString|string $expression + */ + public static function toLower(ResolvesToString|string $expression): ToLowerOperator + { + return new ToLowerOperator($expression); + } + + /** + * Converts value to an ObjectId. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toObjectId/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toObjectId( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToObjectIdOperator { + return new ToObjectIdOperator($expression); + } + + /** + * Converts value to a string. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toString/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function toString( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): ToStringOperator { + return new ToStringOperator($expression); + } + + /** + * Converts a string to uppercase. Accepts a single argument expression. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/toUpper/ + * @param ResolvesToString|string $expression + */ + public static function toUpper(ResolvesToString|string $expression): ToUpperOperator + { + return new ToUpperOperator($expression); + } + + /** + * Removes whitespace or the specified characters from the beginning and end of a string. + * New in MongoDB 4.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/trim/ + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public static function trim( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ): TrimOperator { + return new TrimOperator($input, $chars); + } + + /** + * Truncates a number to a whole integer or to a specified decimal place. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/trunc/ + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $trunc returns an error if the expression resolves to a non-numeric data type. + * @param Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. e.g. -20 < place < 100. Defaults to 0. + */ + public static function trunc( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Optional|ResolvesToInt|int|string $place = Optional::Undefined, + ): TruncOperator { + return new TruncOperator($number, $place); + } + + /** + * Returns the incrementing ordinal from a timestamp as a long. + * New in MongoDB 5.1. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsIncrement/ + * @param ResolvesToTimestamp|Timestamp|int|string $expression + */ + public static function tsIncrement(Timestamp|ResolvesToTimestamp|int|string $expression): TsIncrementOperator + { + return new TsIncrementOperator($expression); + } + + /** + * Returns the seconds from a timestamp as a long. + * New in MongoDB 5.1. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/tsSecond/ + * @param ResolvesToTimestamp|Timestamp|int|string $expression + */ + public static function tsSecond(Timestamp|ResolvesToTimestamp|int|string $expression): TsSecondOperator + { + return new TsSecondOperator($expression); + } + + /** + * Return the BSON data type of the field. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/type/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function type( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): TypeOperator { + return new TypeOperator($expression); + } + + /** + * You can use $unsetField to remove fields with names that contain periods (.) or that start with dollar signs ($). + * $unsetField is an alias for $setField using $$REMOVE to remove fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unsetField/ + * @param ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. + */ + public static function unsetField( + ResolvesToString|string $field, + Document|Serializable|ResolvesToObject|stdClass|array|string $input, + ): UnsetFieldOperator { + return new UnsetFieldOperator($field, $input); + } + + /** + * Returns the week number for a date as a number between 0 (the partial week that precedes the first Sunday of the year) and 53 (leap year). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/week/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function week( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): WeekOperator { + return new WeekOperator($date, $timezone); + } + + /** + * Returns the year for a date as a number (e.g. 2014). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/year/ + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public static function year( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ): YearOperator { + return new YearOperator($date, $timezone); + } + + /** + * Merge two arrays together. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/zip/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + * If any of the inputs arrays resolves to a value of null or refers to a missing field, $zip returns null. + * If any of the inputs arrays does not resolve to an array or null nor refers to a missing field, $zip returns an error. + * @param Optional|bool $useLongestLength A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. + * The default value is false: the shortest array length determines the number of arrays in the output array. + * @param Optional|BSONArray|PackedArray|array $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + * If useLongestLength: true but defaults is empty or not specified, $zip uses null as the default value. + * If specifying a non-empty defaults, you must specify a default for each input array or else $zip will return an error. + */ + public static function zip( + PackedArray|ResolvesToArray|BSONArray|array|string $inputs, + Optional|bool $useLongestLength = Optional::Undefined, + Optional|PackedArray|BSONArray|array $defaults = Optional::Undefined, + ): ZipOperator { + return new ZipOperator($inputs, $useLongestLength, $defaults); + } +} diff --git a/src/Builder/Expression/FieldPath.php b/src/Builder/Expression/FieldPath.php new file mode 100644 index 000000000..4563994b9 --- /dev/null +++ b/src/Builder/Expression/FieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/FilterOperator.php b/src/Builder/Expression/FilterOperator.php new file mode 100644 index 000000000..b2f2df99c --- /dev/null +++ b/src/Builder/Expression/FilterOperator.php @@ -0,0 +1,84 @@ + 'input', 'cond' => 'cond', 'as' => 'as', 'limit' => 'limit']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToBool|bool|string $cond An expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as. */ + public readonly ResolvesToBool|bool|string $cond; + + /** @var Optional|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. */ + public readonly Optional|string $as; + + /** + * @var Optional|ResolvesToInt|int|string $limit A number expression that restricts the number of matching array elements that $filter returns. You cannot specify a limit less than 1. The matching array elements are returned in the order they appear in the input array. + * If the specified limit is greater than the number of matching array elements, $filter returns all matching array elements. If the limit is null, $filter returns all matching array elements. + */ + public readonly Optional|ResolvesToInt|int|string $limit; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input + * @param ResolvesToBool|bool|string $cond An expression that resolves to a boolean value used to determine if an element should be included in the output array. The expression references each element of the input array individually with the variable name specified in as. + * @param Optional|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + * @param Optional|ResolvesToInt|int|string $limit A number expression that restricts the number of matching array elements that $filter returns. You cannot specify a limit less than 1. The matching array elements are returned in the order they appear in the input array. + * If the specified limit is greater than the number of matching array elements, $filter returns all matching array elements. If the limit is null, $filter returns all matching array elements. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToBool|bool|string $cond, + Optional|string $as = Optional::Undefined, + Optional|ResolvesToInt|int|string $limit = Optional::Undefined, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($cond) && ! str_starts_with($cond, '$')) { + throw new InvalidArgumentException('Argument $cond can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->cond = $cond; + $this->as = $as; + if (is_string($limit) && ! str_starts_with($limit, '$')) { + throw new InvalidArgumentException('Argument $limit can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->limit = $limit; + } +} diff --git a/src/Builder/Expression/FirstNOperator.php b/src/Builder/Expression/FirstNOperator.php new file mode 100644 index 000000000..9051441dc --- /dev/null +++ b/src/Builder/Expression/FirstNOperator.php @@ -0,0 +1,63 @@ + 'n', 'input' => 'input']; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. + */ + public function __construct( + ResolvesToInt|int|string $n, + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ) { + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + } +} diff --git a/src/Builder/Expression/FirstOperator.php b/src/Builder/Expression/FirstOperator.php new file mode 100644 index 000000000..d967bf7d2 --- /dev/null +++ b/src/Builder/Expression/FirstOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/FloorOperator.php b/src/Builder/Expression/FloorOperator.php new file mode 100644 index 000000000..b81d5e05f --- /dev/null +++ b/src/Builder/Expression/FloorOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/FunctionOperator.php b/src/Builder/Expression/FunctionOperator.php new file mode 100644 index 000000000..516729e3a --- /dev/null +++ b/src/Builder/Expression/FunctionOperator.php @@ -0,0 +1,67 @@ + 'body', 'args' => 'args', 'lang' => 'lang']; + + /** + * @var Javascript|string $body The function definition. You can specify the function definition as either BSON\JavaScript or string. + * function(arg1, arg2, ...) { ... } + */ + public readonly Javascript|string $body; + + /** @var BSONArray|PackedArray|array $args Arguments passed to the function body. If the body function does not take an argument, you can specify an empty array [ ]. */ + public readonly PackedArray|BSONArray|array $args; + + /** @var string $lang */ + public readonly string $lang; + + /** + * @param Javascript|string $body The function definition. You can specify the function definition as either BSON\JavaScript or string. + * function(arg1, arg2, ...) { ... } + * @param BSONArray|PackedArray|array $args Arguments passed to the function body. If the body function does not take an argument, you can specify an empty array [ ]. + * @param string $lang + */ + public function __construct(Javascript|string $body, PackedArray|BSONArray|array $args = [], string $lang = 'js') + { + if (is_string($body)) { + $body = new Javascript($body); + } + + $this->body = $body; + if (is_array($args) && ! array_is_list($args)) { + throw new InvalidArgumentException('Expected $args argument to be a list, got an associative array.'); + } + + $this->args = $args; + $this->lang = $lang; + } +} diff --git a/src/Builder/Expression/GetFieldOperator.php b/src/Builder/Expression/GetFieldOperator.php new file mode 100644 index 000000000..faf2272e8 --- /dev/null +++ b/src/Builder/Expression/GetFieldOperator.php @@ -0,0 +1,57 @@ + 'field', 'input' => 'input']; + + /** + * @var ResolvesToString|string $field Field in the input object for which you want to return a value. field can be any valid expression that resolves to a string constant. + * If field begins with a dollar sign ($), place the field name inside of a $literal expression to return its value. + */ + public readonly ResolvesToString|string $field; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input Default: $$CURRENT + * A valid expression that contains the field for which you want to return a value. input must resolve to an object, missing, null, or undefined. If omitted, defaults to the document currently being processed in the pipeline ($$CURRENT). + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input; + + /** + * @param ResolvesToString|string $field Field in the input object for which you want to return a value. field can be any valid expression that resolves to a string constant. + * If field begins with a dollar sign ($), place the field name inside of a $literal expression to return its value. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $input Default: $$CURRENT + * A valid expression that contains the field for which you want to return a value. input must resolve to an object, missing, null, or undefined. If omitted, defaults to the document currently being processed in the pipeline ($$CURRENT). + */ + public function __construct( + ResolvesToString|string $field, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $input = Optional::Undefined, + ) { + $this->field = $field; + $this->input = $input; + } +} diff --git a/src/Builder/Expression/GtOperator.php b/src/Builder/Expression/GtOperator.php new file mode 100644 index 000000000..9d92a714e --- /dev/null +++ b/src/Builder/Expression/GtOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/GteOperator.php b/src/Builder/Expression/GteOperator.php new file mode 100644 index 000000000..4a6708ca7 --- /dev/null +++ b/src/Builder/Expression/GteOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/HourOperator.php b/src/Builder/Expression/HourOperator.php new file mode 100644 index 000000000..ffbd5ac46 --- /dev/null +++ b/src/Builder/Expression/HourOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/IfNullOperator.php b/src/Builder/Expression/IfNullOperator.php new file mode 100644 index 000000000..878944cdd --- /dev/null +++ b/src/Builder/Expression/IfNullOperator.php @@ -0,0 +1,53 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/InOperator.php b/src/Builder/Expression/InOperator.php new file mode 100644 index 000000000..5c4d408f1 --- /dev/null +++ b/src/Builder/Expression/InOperator.php @@ -0,0 +1,63 @@ + 'expression', 'array' => 'array']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression Any valid expression expression. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $array Any valid expression that resolves to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $array; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression Any valid expression expression. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array Any valid expression that resolves to an array. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + PackedArray|ResolvesToArray|BSONArray|array|string $array, + ) { + $this->expression = $expression; + if (is_string($array) && ! str_starts_with($array, '$')) { + throw new InvalidArgumentException('Argument $array can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($array) && ! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array argument to be a list, got an associative array.'); + } + + $this->array = $array; + } +} diff --git a/src/Builder/Expression/IndexOfArrayOperator.php b/src/Builder/Expression/IndexOfArrayOperator.php new file mode 100644 index 000000000..34c68db41 --- /dev/null +++ b/src/Builder/Expression/IndexOfArrayOperator.php @@ -0,0 +1,98 @@ + 'array', 'search' => 'search', 'start' => 'start', 'end' => 'end']; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $array Can be any valid expression as long as it resolves to an array. + * If the array expression resolves to a value of null or refers to a field that is missing, $indexOfArray returns null. + * If the array expression does not resolve to an array or null nor refers to a missing field, $indexOfArray returns an error. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $array; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $search */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $search; + + /** + * @var Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + */ + public readonly Optional|ResolvesToInt|int|string $start; + + /** + * @var Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public readonly Optional|ResolvesToInt|int|string $end; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $array Can be any valid expression as long as it resolves to an array. + * If the array expression resolves to a value of null or refers to a field that is missing, $indexOfArray returns null. + * If the array expression does not resolve to an array or null nor refers to a missing field, $indexOfArray returns an error. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $search + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $array, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $search, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ) { + if (is_string($array) && ! str_starts_with($array, '$')) { + throw new InvalidArgumentException('Argument $array can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($array) && ! array_is_list($array)) { + throw new InvalidArgumentException('Expected $array argument to be a list, got an associative array.'); + } + + $this->array = $array; + $this->search = $search; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($end) && ! str_starts_with($end, '$')) { + throw new InvalidArgumentException('Argument $end can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->end = $end; + } +} diff --git a/src/Builder/Expression/IndexOfBytesOperator.php b/src/Builder/Expression/IndexOfBytesOperator.php new file mode 100644 index 000000000..e2f2a1cae --- /dev/null +++ b/src/Builder/Expression/IndexOfBytesOperator.php @@ -0,0 +1,82 @@ + 'string', 'substring' => 'substring', 'start' => 'start', 'end' => 'end']; + + /** + * @var ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfBytes returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfBytes returns an error. + */ + public readonly ResolvesToString|string $string; + + /** @var ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. */ + public readonly ResolvesToString|string $substring; + + /** + * @var Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + */ + public readonly Optional|ResolvesToInt|int|string $start; + + /** + * @var Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public readonly Optional|ResolvesToInt|int|string $end; + + /** + * @param ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfBytes returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfBytes returns an error. + * @param ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public function __construct( + ResolvesToString|string $string, + ResolvesToString|string $substring, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ) { + $this->string = $string; + $this->substring = $substring; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($end) && ! str_starts_with($end, '$')) { + throw new InvalidArgumentException('Argument $end can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->end = $end; + } +} diff --git a/src/Builder/Expression/IndexOfCPOperator.php b/src/Builder/Expression/IndexOfCPOperator.php new file mode 100644 index 000000000..67b0c33ea --- /dev/null +++ b/src/Builder/Expression/IndexOfCPOperator.php @@ -0,0 +1,82 @@ + 'string', 'substring' => 'substring', 'start' => 'start', 'end' => 'end']; + + /** + * @var ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfCP returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfCP returns an error. + */ + public readonly ResolvesToString|string $string; + + /** @var ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. */ + public readonly ResolvesToString|string $substring; + + /** + * @var Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + */ + public readonly Optional|ResolvesToInt|int|string $start; + + /** + * @var Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public readonly Optional|ResolvesToInt|int|string $end; + + /** + * @param ResolvesToString|string $string Can be any valid expression as long as it resolves to a string. + * If the string expression resolves to a value of null or refers to a field that is missing, $indexOfCP returns null. + * If the string expression does not resolve to a string or null nor refers to a missing field, $indexOfCP returns an error. + * @param ResolvesToString|string $substring Can be any valid expression as long as it resolves to a string. + * @param Optional|ResolvesToInt|int|string $start An integer, or a number that can be represented as integers (such as 2.0), that specifies the starting index position for the search. Can be any valid expression that resolves to a non-negative integral number. + * If unspecified, the starting index position for the search is the beginning of the string. + * @param Optional|ResolvesToInt|int|string $end An integer, or a number that can be represented as integers (such as 2.0), that specifies the ending index position for the search. Can be any valid expression that resolves to a non-negative integral number. If you specify a index value, you should also specify a index value; otherwise, $indexOfArray uses the value as the index value instead of the value. + * If unspecified, the ending index position for the search is the end of the string. + */ + public function __construct( + ResolvesToString|string $string, + ResolvesToString|string $substring, + Optional|ResolvesToInt|int|string $start = Optional::Undefined, + Optional|ResolvesToInt|int|string $end = Optional::Undefined, + ) { + $this->string = $string; + $this->substring = $substring; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($end) && ! str_starts_with($end, '$')) { + throw new InvalidArgumentException('Argument $end can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->end = $end; + } +} diff --git a/src/Builder/Expression/IntFieldPath.php b/src/Builder/Expression/IntFieldPath.php new file mode 100644 index 000000000..dc9baeecb --- /dev/null +++ b/src/Builder/Expression/IntFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/IsArrayOperator.php b/src/Builder/Expression/IsArrayOperator.php new file mode 100644 index 000000000..136fc42b4 --- /dev/null +++ b/src/Builder/Expression/IsArrayOperator.php @@ -0,0 +1,41 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/IsNumberOperator.php b/src/Builder/Expression/IsNumberOperator.php new file mode 100644 index 000000000..96034de47 --- /dev/null +++ b/src/Builder/Expression/IsNumberOperator.php @@ -0,0 +1,43 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/IsoDayOfWeekOperator.php b/src/Builder/Expression/IsoDayOfWeekOperator.php new file mode 100644 index 000000000..46261538a --- /dev/null +++ b/src/Builder/Expression/IsoDayOfWeekOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/IsoWeekOperator.php b/src/Builder/Expression/IsoWeekOperator.php new file mode 100644 index 000000000..e59d66dc9 --- /dev/null +++ b/src/Builder/Expression/IsoWeekOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/IsoWeekYearOperator.php b/src/Builder/Expression/IsoWeekYearOperator.php new file mode 100644 index 000000000..2fe1440e4 --- /dev/null +++ b/src/Builder/Expression/IsoWeekYearOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/JavascriptFieldPath.php b/src/Builder/Expression/JavascriptFieldPath.php new file mode 100644 index 000000000..e371ab7a9 --- /dev/null +++ b/src/Builder/Expression/JavascriptFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/LastNOperator.php b/src/Builder/Expression/LastNOperator.php new file mode 100644 index 000000000..be714cd9b --- /dev/null +++ b/src/Builder/Expression/LastNOperator.php @@ -0,0 +1,63 @@ + 'n', 'input' => 'input']; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $firstN returns. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return n elements. + */ + public function __construct( + ResolvesToInt|int|string $n, + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ) { + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + } +} diff --git a/src/Builder/Expression/LastOperator.php b/src/Builder/Expression/LastOperator.php new file mode 100644 index 000000000..68043d7d5 --- /dev/null +++ b/src/Builder/Expression/LastOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/LetOperator.php b/src/Builder/Expression/LetOperator.php new file mode 100644 index 000000000..e3dfdf805 --- /dev/null +++ b/src/Builder/Expression/LetOperator.php @@ -0,0 +1,54 @@ + 'vars', 'in' => 'in']; + + /** + * @var Document|Serializable|array|stdClass $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. + * The variable assignments have no meaning outside the in expression, not even within the vars block itself. + */ + public readonly Document|Serializable|stdClass|array $vars; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in The expression to evaluate. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in; + + /** + * @param Document|Serializable|array|stdClass $vars Assignment block for the variables accessible in the in expression. To assign a variable, specify a string for the variable name and assign a valid expression for the value. + * The variable assignments have no meaning outside the in expression, not even within the vars block itself. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in The expression to evaluate. + */ + public function __construct( + Document|Serializable|stdClass|array $vars, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + ) { + $this->vars = $vars; + $this->in = $in; + } +} diff --git a/src/Builder/Expression/LiteralOperator.php b/src/Builder/Expression/LiteralOperator.php new file mode 100644 index 000000000..fe65495d0 --- /dev/null +++ b/src/Builder/Expression/LiteralOperator.php @@ -0,0 +1,39 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value If the value is an expression, $literal does not evaluate the expression but instead returns the unparsed expression. */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value If the value is an expression, $literal does not evaluate the expression but instead returns the unparsed expression. + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Expression/LnOperator.php b/src/Builder/Expression/LnOperator.php new file mode 100644 index 000000000..1848fbec6 --- /dev/null +++ b/src/Builder/Expression/LnOperator.php @@ -0,0 +1,47 @@ +, Math.E ] expression, where Math.E is a JavaScript representation for Euler's number e. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/ln/ + * @internal + */ +final class LnOperator implements ResolvesToDouble, OperatorInterface +{ + public const ENCODE = Encode::Single; + public const NAME = '$ln'; + public const PROPERTIES = ['number' => 'number']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. For more information on expressions, see Expressions. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. For more information on expressions, see Expressions. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $number) + { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + } +} diff --git a/src/Builder/Expression/Log10Operator.php b/src/Builder/Expression/Log10Operator.php new file mode 100644 index 000000000..24ea9824d --- /dev/null +++ b/src/Builder/Expression/Log10Operator.php @@ -0,0 +1,46 @@ + 'number']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $number) + { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + } +} diff --git a/src/Builder/Expression/LogOperator.php b/src/Builder/Expression/LogOperator.php new file mode 100644 index 000000000..b61fa3bef --- /dev/null +++ b/src/Builder/Expression/LogOperator.php @@ -0,0 +1,57 @@ + 'number', 'base' => 'base']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $base Any valid expression as long as it resolves to a positive number greater than 1. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $base; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Any valid expression as long as it resolves to a non-negative number. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $base Any valid expression as long as it resolves to a positive number greater than 1. + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Decimal128|Int64|ResolvesToNumber|float|int|string $base, + ) { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + if (is_string($base) && ! str_starts_with($base, '$')) { + throw new InvalidArgumentException('Argument $base can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->base = $base; + } +} diff --git a/src/Builder/Expression/LongFieldPath.php b/src/Builder/Expression/LongFieldPath.php new file mode 100644 index 000000000..7d18c015b --- /dev/null +++ b/src/Builder/Expression/LongFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/LtOperator.php b/src/Builder/Expression/LtOperator.php new file mode 100644 index 000000000..b29d33ef7 --- /dev/null +++ b/src/Builder/Expression/LtOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/LteOperator.php b/src/Builder/Expression/LteOperator.php new file mode 100644 index 000000000..2bd7ab4fd --- /dev/null +++ b/src/Builder/Expression/LteOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/LtrimOperator.php b/src/Builder/Expression/LtrimOperator.php new file mode 100644 index 000000000..0dd94eaab --- /dev/null +++ b/src/Builder/Expression/LtrimOperator.php @@ -0,0 +1,51 @@ + 'input', 'chars' => 'chars']; + + /** @var ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** + * @var Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public readonly Optional|ResolvesToString|string $chars; + + /** + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public function __construct( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ) { + $this->input = $input; + $this->chars = $chars; + } +} diff --git a/src/Builder/Expression/MapOperator.php b/src/Builder/Expression/MapOperator.php new file mode 100644 index 000000000..a965e69c6 --- /dev/null +++ b/src/Builder/Expression/MapOperator.php @@ -0,0 +1,70 @@ + 'input', 'in' => 'in', 'as' => 'as']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in An expression that is applied to each element of the input array. The expression references each element individually with the variable name specified in as. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in; + + /** @var Optional|ResolvesToString|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. */ + public readonly Optional|ResolvesToString|string $as; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to an array. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in An expression that is applied to each element of the input array. The expression references each element individually with the variable name specified in as. + * @param Optional|ResolvesToString|string $as A name for the variable that represents each individual element of the input array. If no name is specified, the variable name defaults to this. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + Optional|ResolvesToString|string $as = Optional::Undefined, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + $this->in = $in; + $this->as = $as; + } +} diff --git a/src/Builder/Expression/MaxNOperator.php b/src/Builder/Expression/MaxNOperator.php new file mode 100644 index 000000000..b0da2c042 --- /dev/null +++ b/src/Builder/Expression/MaxNOperator.php @@ -0,0 +1,63 @@ + 'input', 'n' => 'n']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Expression/MaxOperator.php b/src/Builder/Expression/MaxOperator.php new file mode 100644 index 000000000..acac1c5d6 --- /dev/null +++ b/src/Builder/Expression/MaxOperator.php @@ -0,0 +1,54 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/MedianOperator.php b/src/Builder/Expression/MedianOperator.php new file mode 100644 index 000000000..ec9afbdcf --- /dev/null +++ b/src/Builder/Expression/MedianOperator.php @@ -0,0 +1,66 @@ + 'input', 'method' => 'method']; + + /** @var BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. */ + public readonly Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input; + + /** @var string $method The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. */ + public readonly string $method; + + /** + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $median calculates the 50th percentile value of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $median calculation ignores it. + * @param string $method The method that mongod uses to calculate the 50th percentile value. The method must be 'approximate'. + */ + public function __construct( + Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input, + string $method, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + $this->method = $method; + } +} diff --git a/src/Builder/Expression/MergeObjectsOperator.php b/src/Builder/Expression/MergeObjectsOperator.php new file mode 100644 index 000000000..e17e1ae1d --- /dev/null +++ b/src/Builder/Expression/MergeObjectsOperator.php @@ -0,0 +1,51 @@ + 'document']; + + /** @var list $document Any valid expression that resolves to a document. */ + public readonly array $document; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string ...$document Any valid expression that resolves to a document. + * @no-named-arguments + */ + public function __construct(Document|Serializable|ResolvesToObject|stdClass|array|string ...$document) + { + if (\count($document) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $document, got %d.', 1, \count($document))); + } + + if (! array_is_list($document)) { + throw new InvalidArgumentException('Expected $document arguments to be a list (array), named arguments are not supported'); + } + + $this->document = $document; + } +} diff --git a/src/Builder/Expression/MetaOperator.php b/src/Builder/Expression/MetaOperator.php new file mode 100644 index 000000000..ec1fd63e6 --- /dev/null +++ b/src/Builder/Expression/MetaOperator.php @@ -0,0 +1,36 @@ + 'keyword']; + + /** @var string $keyword */ + public readonly string $keyword; + + /** + * @param string $keyword + */ + public function __construct(string $keyword) + { + $this->keyword = $keyword; + } +} diff --git a/src/Builder/Expression/MillisecondOperator.php b/src/Builder/Expression/MillisecondOperator.php new file mode 100644 index 000000000..91dc5917c --- /dev/null +++ b/src/Builder/Expression/MillisecondOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/MinNOperator.php b/src/Builder/Expression/MinNOperator.php new file mode 100644 index 000000000..50b22e1fd --- /dev/null +++ b/src/Builder/Expression/MinNOperator.php @@ -0,0 +1,63 @@ + 'input', 'n' => 'n']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. */ + public readonly ResolvesToInt|int|string $n; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input An expression that resolves to the array from which to return the maximal n elements. + * @param ResolvesToInt|int|string $n An expression that resolves to a positive integer. The integer specifies the number of array elements that $maxN returns. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + ResolvesToInt|int|string $n, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + } +} diff --git a/src/Builder/Expression/MinOperator.php b/src/Builder/Expression/MinOperator.php new file mode 100644 index 000000000..28b707d45 --- /dev/null +++ b/src/Builder/Expression/MinOperator.php @@ -0,0 +1,54 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/MinuteOperator.php b/src/Builder/Expression/MinuteOperator.php new file mode 100644 index 000000000..ff86f8857 --- /dev/null +++ b/src/Builder/Expression/MinuteOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/ModOperator.php b/src/Builder/Expression/ModOperator.php new file mode 100644 index 000000000..0e7a44df8 --- /dev/null +++ b/src/Builder/Expression/ModOperator.php @@ -0,0 +1,57 @@ + 'dividend', 'divisor' => 'divisor']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. first argument is divided by the second argument. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $dividend; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $divisor */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $divisor; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $dividend The first argument is the dividend, and the second argument is the divisor; i.e. first argument is divided by the second argument. + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $divisor + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $dividend, + Decimal128|Int64|ResolvesToNumber|float|int|string $divisor, + ) { + if (is_string($dividend) && ! str_starts_with($dividend, '$')) { + throw new InvalidArgumentException('Argument $dividend can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->dividend = $dividend; + if (is_string($divisor) && ! str_starts_with($divisor, '$')) { + throw new InvalidArgumentException('Argument $divisor can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->divisor = $divisor; + } +} diff --git a/src/Builder/Expression/MonthOperator.php b/src/Builder/Expression/MonthOperator.php new file mode 100644 index 000000000..7fd6e1760 --- /dev/null +++ b/src/Builder/Expression/MonthOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/MultiplyOperator.php b/src/Builder/Expression/MultiplyOperator.php new file mode 100644 index 000000000..31d781ad4 --- /dev/null +++ b/src/Builder/Expression/MultiplyOperator.php @@ -0,0 +1,54 @@ + 'expression']; + + /** + * @var list $expression The arguments can be any valid expression as long as they resolve to numbers. + * Starting in MongoDB 6.1 you can optimize the $multiply operation. To improve performance, group references at the end of the argument list. + */ + public readonly array $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression The arguments can be any valid expression as long as they resolve to numbers. + * Starting in MongoDB 6.1 you can optimize the $multiply operation. To improve performance, group references at the end of the argument list. + * @no-named-arguments + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/NeOperator.php b/src/Builder/Expression/NeOperator.php new file mode 100644 index 000000000..b9d60c6e6 --- /dev/null +++ b/src/Builder/Expression/NeOperator.php @@ -0,0 +1,47 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression1 + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression2 + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression1, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression2, + ) { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/NotOperator.php b/src/Builder/Expression/NotOperator.php new file mode 100644 index 000000000..212898961 --- /dev/null +++ b/src/Builder/Expression/NotOperator.php @@ -0,0 +1,41 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|ResolvesToBool|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ResolvesToBool|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|ResolvesToBool|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ResolvesToBool|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/NullFieldPath.php b/src/Builder/Expression/NullFieldPath.php new file mode 100644 index 000000000..63bf171dc --- /dev/null +++ b/src/Builder/Expression/NullFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/NumberFieldPath.php b/src/Builder/Expression/NumberFieldPath.php new file mode 100644 index 000000000..d8ab23a23 --- /dev/null +++ b/src/Builder/Expression/NumberFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/ObjectFieldPath.php b/src/Builder/Expression/ObjectFieldPath.php new file mode 100644 index 000000000..082989831 --- /dev/null +++ b/src/Builder/Expression/ObjectFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/ObjectIdFieldPath.php b/src/Builder/Expression/ObjectIdFieldPath.php new file mode 100644 index 000000000..4428c1a39 --- /dev/null +++ b/src/Builder/Expression/ObjectIdFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/ObjectToArrayOperator.php b/src/Builder/Expression/ObjectToArrayOperator.php new file mode 100644 index 000000000..c8a260970 --- /dev/null +++ b/src/Builder/Expression/ObjectToArrayOperator.php @@ -0,0 +1,47 @@ + 'object']; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $object Any valid expression as long as it resolves to a document object. $objectToArray applies to the top-level fields of its argument. If the argument is a document that itself contains embedded document fields, the $objectToArray does not recursively apply to the embedded document fields. */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $object; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $object Any valid expression as long as it resolves to a document object. $objectToArray applies to the top-level fields of its argument. If the argument is a document that itself contains embedded document fields, the $objectToArray does not recursively apply to the embedded document fields. + */ + public function __construct(Document|Serializable|ResolvesToObject|stdClass|array|string $object) + { + if (is_string($object) && ! str_starts_with($object, '$')) { + throw new InvalidArgumentException('Argument $object can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->object = $object; + } +} diff --git a/src/Builder/Expression/OrOperator.php b/src/Builder/Expression/OrOperator.php new file mode 100644 index 000000000..4289c2b7e --- /dev/null +++ b/src/Builder/Expression/OrOperator.php @@ -0,0 +1,53 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|ResolvesToBool|Type|array|bool|float|int|null|stdClass|string ...$expression + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Type|ResolvesToBool|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/PercentileOperator.php b/src/Builder/Expression/PercentileOperator.php new file mode 100644 index 000000000..c30775ca3 --- /dev/null +++ b/src/Builder/Expression/PercentileOperator.php @@ -0,0 +1,87 @@ + 'input', 'p' => 'p', 'method' => 'method']; + + /** @var BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. */ + public readonly Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $p $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + * $percentile returns results in the same order as the elements in p. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $p; + + /** @var string $method The method that mongod uses to calculate the percentile value. The method must be 'approximate'. */ + public readonly string $method; + + /** + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToNumber|array|float|int|string $input $percentile calculates the percentile values of this data. input must be a field name or an expression that evaluates to a numeric type. If the expression cannot be converted to a numeric type, the $percentile calculation ignores it. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $p $percentile calculates a percentile value for each element in p. The elements represent percentages and must evaluate to numeric values in the range 0.0 to 1.0, inclusive. + * $percentile returns results in the same order as the elements in p. + * @param string $method The method that mongod uses to calculate the percentile value. The method must be 'approximate'. + */ + public function __construct( + Decimal128|Int64|PackedArray|ResolvesToNumber|BSONArray|array|float|int|string $input, + PackedArray|ResolvesToArray|BSONArray|array|string $p, + string $method, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + if (is_string($p) && ! str_starts_with($p, '$')) { + throw new InvalidArgumentException('Argument $p can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($p) && ! array_is_list($p)) { + throw new InvalidArgumentException('Expected $p argument to be a list, got an associative array.'); + } + + $this->p = $p; + $this->method = $method; + } +} diff --git a/src/Builder/Expression/PowOperator.php b/src/Builder/Expression/PowOperator.php new file mode 100644 index 000000000..bf132280f --- /dev/null +++ b/src/Builder/Expression/PowOperator.php @@ -0,0 +1,57 @@ + 'number', 'exponent' => 'exponent']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $number */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $exponent */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $exponent; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $exponent + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Decimal128|Int64|ResolvesToNumber|float|int|string $exponent, + ) { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + if (is_string($exponent) && ! str_starts_with($exponent, '$')) { + throw new InvalidArgumentException('Argument $exponent can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->exponent = $exponent; + } +} diff --git a/src/Builder/Expression/RadiansToDegreesOperator.php b/src/Builder/Expression/RadiansToDegreesOperator.php new file mode 100644 index 000000000..c8e4d5292 --- /dev/null +++ b/src/Builder/Expression/RadiansToDegreesOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/RandOperator.php b/src/Builder/Expression/RandOperator.php new file mode 100644 index 000000000..26da27637 --- /dev/null +++ b/src/Builder/Expression/RandOperator.php @@ -0,0 +1,28 @@ + 'start', 'end' => 'end', 'step' => 'step']; + + /** @var ResolvesToInt|int|string $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. */ + public readonly ResolvesToInt|int|string $start; + + /** @var ResolvesToInt|int|string $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. */ + public readonly ResolvesToInt|int|string $end; + + /** @var Optional|ResolvesToInt|int|string $step An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. */ + public readonly Optional|ResolvesToInt|int|string $step; + + /** + * @param ResolvesToInt|int|string $start An integer that specifies the start of the sequence. Can be any valid expression that resolves to an integer. + * @param ResolvesToInt|int|string $end An integer that specifies the exclusive upper limit of the sequence. Can be any valid expression that resolves to an integer. + * @param Optional|ResolvesToInt|int|string $step An integer that specifies the increment value. Can be any valid expression that resolves to a non-zero integer. Defaults to 1. + */ + public function __construct( + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $end, + Optional|ResolvesToInt|int|string $step = Optional::Undefined, + ) { + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($end) && ! str_starts_with($end, '$')) { + throw new InvalidArgumentException('Argument $end can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->end = $end; + if (is_string($step) && ! str_starts_with($step, '$')) { + throw new InvalidArgumentException('Argument $step can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->step = $step; + } +} diff --git a/src/Builder/Expression/ReduceOperator.php b/src/Builder/Expression/ReduceOperator.php new file mode 100644 index 000000000..d285a4532 --- /dev/null +++ b/src/Builder/Expression/ReduceOperator.php @@ -0,0 +1,83 @@ + 'input', 'initialValue' => 'initialValue', 'in' => 'in']; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $input Can be any valid expression that resolves to an array. + * If the argument resolves to a value of null or refers to a missing field, $reduce returns null. + * If the argument does not resolve to an array or null nor refers to a missing field, $reduce returns an error. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $initialValue The initial cumulative value set before in is applied to the first element of the input array. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $initialValue; + + /** + * @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + * During evaluation of the in expression, two variables will be available: + * - value is the variable that represents the cumulative value of the expression. + * - this is the variable that refers to the element being processed. + */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input Can be any valid expression that resolves to an array. + * If the argument resolves to a value of null or refers to a missing field, $reduce returns null. + * If the argument does not resolve to an array or null nor refers to a missing field, $reduce returns an error. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $initialValue The initial cumulative value set before in is applied to the first element of the input array. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $in A valid expression that $reduce applies to each element in the input array in left-to-right order. Wrap the input value with $reverseArray to yield the equivalent of applying the combining expression from right-to-left. + * During evaluation of the in expression, two variables will be available: + * - value is the variable that represents the cumulative value of the expression. + * - this is the variable that refers to the element being processed. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $initialValue, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $in, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + $this->initialValue = $initialValue; + $this->in = $in; + } +} diff --git a/src/Builder/Expression/RegexFieldPath.php b/src/Builder/Expression/RegexFieldPath.php new file mode 100644 index 000000000..14950bee7 --- /dev/null +++ b/src/Builder/Expression/RegexFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/RegexFindAllOperator.php b/src/Builder/Expression/RegexFindAllOperator.php new file mode 100644 index 000000000..e6e36a9fb --- /dev/null +++ b/src/Builder/Expression/RegexFindAllOperator.php @@ -0,0 +1,52 @@ + 'input', 'regex' => 'regex', 'options' => 'options']; + + /** @var ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** @var Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) */ + public readonly Regex|ResolvesToString|string $regex; + + /** @var Optional|string $options */ + public readonly Optional|string $options; + + /** + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public function __construct( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ) { + $this->input = $input; + $this->regex = $regex; + $this->options = $options; + } +} diff --git a/src/Builder/Expression/RegexFindOperator.php b/src/Builder/Expression/RegexFindOperator.php new file mode 100644 index 000000000..81f713f35 --- /dev/null +++ b/src/Builder/Expression/RegexFindOperator.php @@ -0,0 +1,52 @@ + 'input', 'regex' => 'regex', 'options' => 'options']; + + /** @var ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** @var Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) */ + public readonly Regex|ResolvesToString|string $regex; + + /** @var Optional|string $options */ + public readonly Optional|string $options; + + /** + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public function __construct( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ) { + $this->input = $input; + $this->regex = $regex; + $this->options = $options; + } +} diff --git a/src/Builder/Expression/RegexMatchOperator.php b/src/Builder/Expression/RegexMatchOperator.php new file mode 100644 index 000000000..50fbb1766 --- /dev/null +++ b/src/Builder/Expression/RegexMatchOperator.php @@ -0,0 +1,52 @@ + 'input', 'regex' => 'regex', 'options' => 'options']; + + /** @var ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** @var Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) */ + public readonly Regex|ResolvesToString|string $regex; + + /** @var Optional|string $options */ + public readonly Optional|string $options; + + /** + * @param ResolvesToString|string $input The string on which you wish to apply the regex pattern. Can be a string or any valid expression that resolves to a string. + * @param Regex|ResolvesToString|string $regex The regex pattern to apply. Can be any valid expression that resolves to either a string or regex pattern //. When using the regex //, you can also specify the regex options i and m (but not the s or x options) + * @param Optional|string $options + */ + public function __construct( + ResolvesToString|string $input, + Regex|ResolvesToString|string $regex, + Optional|string $options = Optional::Undefined, + ) { + $this->input = $input; + $this->regex = $regex; + $this->options = $options; + } +} diff --git a/src/Builder/Expression/ReplaceAllOperator.php b/src/Builder/Expression/ReplaceAllOperator.php new file mode 100644 index 000000000..5cede16d4 --- /dev/null +++ b/src/Builder/Expression/ReplaceAllOperator.php @@ -0,0 +1,52 @@ + 'input', 'find' => 'find', 'replacement' => 'replacement']; + + /** @var ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. */ + public readonly ResolvesToNull|ResolvesToString|null|string $input; + + /** @var Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. */ + public readonly Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find; + + /** @var ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. */ + public readonly ResolvesToNull|ResolvesToString|null|string $replacement; + + /** + * @param ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + * @param Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. + */ + public function __construct( + ResolvesToNull|ResolvesToString|null|string $input, + Regex|ResolvesToNull|ResolvesToRegex|ResolvesToString|null|string $find, + ResolvesToNull|ResolvesToString|null|string $replacement, + ) { + $this->input = $input; + $this->find = $find; + $this->replacement = $replacement; + } +} diff --git a/src/Builder/Expression/ReplaceOneOperator.php b/src/Builder/Expression/ReplaceOneOperator.php new file mode 100644 index 000000000..0237aacc0 --- /dev/null +++ b/src/Builder/Expression/ReplaceOneOperator.php @@ -0,0 +1,50 @@ + 'input', 'find' => 'find', 'replacement' => 'replacement']; + + /** @var ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. */ + public readonly ResolvesToNull|ResolvesToString|null|string $input; + + /** @var ResolvesToNull|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. */ + public readonly ResolvesToNull|ResolvesToString|null|string $find; + + /** @var ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. */ + public readonly ResolvesToNull|ResolvesToString|null|string $replacement; + + /** + * @param ResolvesToNull|ResolvesToString|null|string $input The string on which you wish to apply the find. Can be any valid expression that resolves to a string or a null. If input refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $find The string to search for within the given input. Can be any valid expression that resolves to a string or a null. If find refers to a field that is missing, $replaceAll returns null. + * @param ResolvesToNull|ResolvesToString|null|string $replacement The string to use to replace all matched instances of find in input. Can be any valid expression that resolves to a string or a null. + */ + public function __construct( + ResolvesToNull|ResolvesToString|null|string $input, + ResolvesToNull|ResolvesToString|null|string $find, + ResolvesToNull|ResolvesToString|null|string $replacement, + ) { + $this->input = $input; + $this->find = $find; + $this->replacement = $replacement; + } +} diff --git a/src/Builder/Expression/ResolvesToAny.php b/src/Builder/Expression/ResolvesToAny.php new file mode 100644 index 000000000..665564887 --- /dev/null +++ b/src/Builder/Expression/ResolvesToAny.php @@ -0,0 +1,13 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument can be any valid expression as long as it resolves to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument can be any valid expression as long as it resolves to an array. + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/RoundOperator.php b/src/Builder/Expression/RoundOperator.php new file mode 100644 index 000000000..0deb81104 --- /dev/null +++ b/src/Builder/Expression/RoundOperator.php @@ -0,0 +1,62 @@ + 'number', 'place' => 'place']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $round returns an error if the expression resolves to a non-numeric data type. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** @var Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. */ + public readonly Optional|ResolvesToInt|int|string $place; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $round returns an error if the expression resolves to a non-numeric data type. + * @param Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Optional|ResolvesToInt|int|string $place = Optional::Undefined, + ) { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + if (is_string($place) && ! str_starts_with($place, '$')) { + throw new InvalidArgumentException('Argument $place can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->place = $place; + } +} diff --git a/src/Builder/Expression/RtrimOperator.php b/src/Builder/Expression/RtrimOperator.php new file mode 100644 index 000000000..46824f98a --- /dev/null +++ b/src/Builder/Expression/RtrimOperator.php @@ -0,0 +1,50 @@ + 'input', 'chars' => 'chars']; + + /** @var ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** + * @var Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public readonly Optional|ResolvesToString|string $chars; + + /** + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public function __construct( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ) { + $this->input = $input; + $this->chars = $chars; + } +} diff --git a/src/Builder/Expression/SecondOperator.php b/src/Builder/Expression/SecondOperator.php new file mode 100644 index 000000000..48ee9ad85 --- /dev/null +++ b/src/Builder/Expression/SecondOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/SetDifferenceOperator.php b/src/Builder/Expression/SetDifferenceOperator.php new file mode 100644 index 000000000..297897425 --- /dev/null +++ b/src/Builder/Expression/SetDifferenceOperator.php @@ -0,0 +1,67 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression1 The arguments can be any valid expression as long as they each resolve to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression1; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression2 The arguments can be any valid expression as long as they each resolve to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression2; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression1 The arguments can be any valid expression as long as they each resolve to an array. + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression2 The arguments can be any valid expression as long as they each resolve to an array. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $expression1, + PackedArray|ResolvesToArray|BSONArray|array|string $expression2, + ) { + if (is_string($expression1) && ! str_starts_with($expression1, '$')) { + throw new InvalidArgumentException('Argument $expression1 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression1) && ! array_is_list($expression1)) { + throw new InvalidArgumentException('Expected $expression1 argument to be a list, got an associative array.'); + } + + $this->expression1 = $expression1; + if (is_string($expression2) && ! str_starts_with($expression2, '$')) { + throw new InvalidArgumentException('Argument $expression2 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression2) && ! array_is_list($expression2)) { + throw new InvalidArgumentException('Expected $expression2 argument to be a list, got an associative array.'); + } + + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/SetEqualsOperator.php b/src/Builder/Expression/SetEqualsOperator.php new file mode 100644 index 000000000..c743c8cca --- /dev/null +++ b/src/Builder/Expression/SetEqualsOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SetFieldOperator.php b/src/Builder/Expression/SetFieldOperator.php new file mode 100644 index 000000000..660832772 --- /dev/null +++ b/src/Builder/Expression/SetFieldOperator.php @@ -0,0 +1,68 @@ + 'field', 'input' => 'input', 'value' => 'value']; + + /** @var ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. */ + public readonly ResolvesToString|string $field; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $input; + + /** + * @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value The value that you want to assign to field. value can be any valid expression. + * Set to $$REMOVE to remove field from the input document. + */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value; + + /** + * @param ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value The value that you want to assign to field. value can be any valid expression. + * Set to $$REMOVE to remove field from the input document. + */ + public function __construct( + ResolvesToString|string $field, + Document|Serializable|ResolvesToObject|stdClass|array|string $input, + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value, + ) { + $this->field = $field; + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + $this->value = $value; + } +} diff --git a/src/Builder/Expression/SetIntersectionOperator.php b/src/Builder/Expression/SetIntersectionOperator.php new file mode 100644 index 000000000..a27dd4e9b --- /dev/null +++ b/src/Builder/Expression/SetIntersectionOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SetIsSubsetOperator.php b/src/Builder/Expression/SetIsSubsetOperator.php new file mode 100644 index 000000000..8d9a63e45 --- /dev/null +++ b/src/Builder/Expression/SetIsSubsetOperator.php @@ -0,0 +1,67 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression1 */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression1; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression2 */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression2; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression1 + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression2 + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $expression1, + PackedArray|ResolvesToArray|BSONArray|array|string $expression2, + ) { + if (is_string($expression1) && ! str_starts_with($expression1, '$')) { + throw new InvalidArgumentException('Argument $expression1 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression1) && ! array_is_list($expression1)) { + throw new InvalidArgumentException('Expected $expression1 argument to be a list, got an associative array.'); + } + + $this->expression1 = $expression1; + if (is_string($expression2) && ! str_starts_with($expression2, '$')) { + throw new InvalidArgumentException('Argument $expression2 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression2) && ! array_is_list($expression2)) { + throw new InvalidArgumentException('Expected $expression2 argument to be a list, got an associative array.'); + } + + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/SetUnionOperator.php b/src/Builder/Expression/SetUnionOperator.php new file mode 100644 index 000000000..5a7af7651 --- /dev/null +++ b/src/Builder/Expression/SetUnionOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string ...$expression + * @no-named-arguments + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SinOperator.php b/src/Builder/Expression/SinOperator.php new file mode 100644 index 000000000..cc8f82357 --- /dev/null +++ b/src/Builder/Expression/SinOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sin takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $sin returns values as a double. $sin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sin takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $sin returns values as a double. $sin can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SinhOperator.php b/src/Builder/Expression/SinhOperator.php new file mode 100644 index 000000000..763d2f94f --- /dev/null +++ b/src/Builder/Expression/SinhOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sinh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $sinh returns values as a double. $sinh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $sinh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $sinh returns values as a double. $sinh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SizeOperator.php b/src/Builder/Expression/SizeOperator.php new file mode 100644 index 000000000..53b6cdd1a --- /dev/null +++ b/src/Builder/Expression/SizeOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument for $size can be any expression as long as it resolves to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression The argument for $size can be any expression as long as it resolves to an array. + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SliceOperator.php b/src/Builder/Expression/SliceOperator.php new file mode 100644 index 000000000..71cc07415 --- /dev/null +++ b/src/Builder/Expression/SliceOperator.php @@ -0,0 +1,86 @@ + 'expression', 'n' => 'n', 'position' => 'position']; + + /** @var BSONArray|PackedArray|ResolvesToArray|array|string $expression Any valid expression as long as it resolves to an array. */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $expression; + + /** + * @var ResolvesToInt|int|string $n Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer. + * If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position. + * If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if is specified. + */ + public readonly ResolvesToInt|int|string $n; + + /** + * @var Optional|ResolvesToInt|int|string $position Any valid expression as long as it resolves to an integer. + * If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array. + * If negative, $slice determines the starting position from the end of the array. If the absolute value of the is greater than the number of elements, the starting position is the start of the array. + */ + public readonly Optional|ResolvesToInt|int|string $position; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $expression Any valid expression as long as it resolves to an array. + * @param ResolvesToInt|int|string $n Any valid expression as long as it resolves to an integer. If position is specified, n must resolve to a positive integer. + * If positive, $slice returns up to the first n elements in the array. If the position is specified, $slice returns the first n elements starting from the position. + * If negative, $slice returns up to the last n elements in the array. n cannot resolve to a negative number if is specified. + * @param Optional|ResolvesToInt|int|string $position Any valid expression as long as it resolves to an integer. + * If positive, $slice determines the starting position from the start of the array. If position is greater than the number of elements, the $slice returns an empty array. + * If negative, $slice determines the starting position from the end of the array. If the absolute value of the is greater than the number of elements, the starting position is the start of the array. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $expression, + ResolvesToInt|int|string $n, + Optional|ResolvesToInt|int|string $position = Optional::Undefined, + ) { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($expression) && ! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression argument to be a list, got an associative array.'); + } + + $this->expression = $expression; + if (is_string($n) && ! str_starts_with($n, '$')) { + throw new InvalidArgumentException('Argument $n can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->n = $n; + if (is_string($position) && ! str_starts_with($position, '$')) { + throw new InvalidArgumentException('Argument $position can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->position = $position; + } +} diff --git a/src/Builder/Expression/SortArrayOperator.php b/src/Builder/Expression/SortArrayOperator.php new file mode 100644 index 000000000..6b1d26e0f --- /dev/null +++ b/src/Builder/Expression/SortArrayOperator.php @@ -0,0 +1,69 @@ + 'input', 'sortBy' => 'sortBy']; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $input The array to be sorted. + * The result is null if the expression: is missing, evaluates to null, or evaluates to undefined + * If the expression evaluates to any other non-array value, the document returns an error. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $input; + + /** @var Document|Serializable|Sort|array|int|stdClass $sortBy The document specifies a sort ordering. */ + public readonly Document|Serializable|Sort|stdClass|array|int $sortBy; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $input The array to be sorted. + * The result is null if the expression: is missing, evaluates to null, or evaluates to undefined + * If the expression evaluates to any other non-array value, the document returns an error. + * @param Document|Serializable|Sort|array|int|stdClass $sortBy The document specifies a sort ordering. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $input, + Document|Serializable|Sort|stdClass|array|int $sortBy, + ) { + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($input) && ! array_is_list($input)) { + throw new InvalidArgumentException('Expected $input argument to be a list, got an associative array.'); + } + + $this->input = $input; + $this->sortBy = $sortBy; + } +} diff --git a/src/Builder/Expression/SplitOperator.php b/src/Builder/Expression/SplitOperator.php new file mode 100644 index 000000000..30d306259 --- /dev/null +++ b/src/Builder/Expression/SplitOperator.php @@ -0,0 +1,44 @@ + 'string', 'delimiter' => 'delimiter']; + + /** @var ResolvesToString|string $string The string to be split. string expression can be any valid expression as long as it resolves to a string. */ + public readonly ResolvesToString|string $string; + + /** @var Regex|ResolvesToRegex|ResolvesToString|string $delimiter The delimiter to use when splitting the string expression. delimiter can be any valid expression as long as it resolves to a string. */ + public readonly Regex|ResolvesToRegex|ResolvesToString|string $delimiter; + + /** + * @param ResolvesToString|string $string The string to be split. string expression can be any valid expression as long as it resolves to a string. + * @param Regex|ResolvesToRegex|ResolvesToString|string $delimiter The delimiter to use when splitting the string expression. delimiter can be any valid expression as long as it resolves to a string. + */ + public function __construct( + ResolvesToString|string $string, + Regex|ResolvesToRegex|ResolvesToString|string $delimiter, + ) { + $this->string = $string; + $this->delimiter = $delimiter; + } +} diff --git a/src/Builder/Expression/SqrtOperator.php b/src/Builder/Expression/SqrtOperator.php new file mode 100644 index 000000000..9453fb77b --- /dev/null +++ b/src/Builder/Expression/SqrtOperator.php @@ -0,0 +1,46 @@ + 'number']; + + /** @var Decimal128|Int64|ResolvesToNumber|float|int|string $number The argument can be any valid expression as long as it resolves to a non-negative number. */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number The argument can be any valid expression as long as it resolves to a non-negative number. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $number) + { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + } +} diff --git a/src/Builder/Expression/StdDevPopOperator.php b/src/Builder/Expression/StdDevPopOperator.php new file mode 100644 index 000000000..dae7344a1 --- /dev/null +++ b/src/Builder/Expression/StdDevPopOperator.php @@ -0,0 +1,52 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/StdDevSampOperator.php b/src/Builder/Expression/StdDevSampOperator.php new file mode 100644 index 000000000..55af36f53 --- /dev/null +++ b/src/Builder/Expression/StdDevSampOperator.php @@ -0,0 +1,51 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression + * @no-named-arguments + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string ...$expression) + { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/StrLenBytesOperator.php b/src/Builder/Expression/StrLenBytesOperator.php new file mode 100644 index 000000000..39ae8dbe8 --- /dev/null +++ b/src/Builder/Expression/StrLenBytesOperator.php @@ -0,0 +1,36 @@ + 'expression']; + + /** @var ResolvesToString|string $expression */ + public readonly ResolvesToString|string $expression; + + /** + * @param ResolvesToString|string $expression + */ + public function __construct(ResolvesToString|string $expression) + { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/StrLenCPOperator.php b/src/Builder/Expression/StrLenCPOperator.php new file mode 100644 index 000000000..8add7c3f8 --- /dev/null +++ b/src/Builder/Expression/StrLenCPOperator.php @@ -0,0 +1,36 @@ + 'expression']; + + /** @var ResolvesToString|string $expression */ + public readonly ResolvesToString|string $expression; + + /** + * @param ResolvesToString|string $expression + */ + public function __construct(ResolvesToString|string $expression) + { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/StrcasecmpOperator.php b/src/Builder/Expression/StrcasecmpOperator.php new file mode 100644 index 000000000..efcb0e41b --- /dev/null +++ b/src/Builder/Expression/StrcasecmpOperator.php @@ -0,0 +1,41 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var ResolvesToString|string $expression1 */ + public readonly ResolvesToString|string $expression1; + + /** @var ResolvesToString|string $expression2 */ + public readonly ResolvesToString|string $expression2; + + /** + * @param ResolvesToString|string $expression1 + * @param ResolvesToString|string $expression2 + */ + public function __construct(ResolvesToString|string $expression1, ResolvesToString|string $expression2) + { + $this->expression1 = $expression1; + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/StringFieldPath.php b/src/Builder/Expression/StringFieldPath.php new file mode 100644 index 000000000..487d34782 --- /dev/null +++ b/src/Builder/Expression/StringFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/SubstrBytesOperator.php b/src/Builder/Expression/SubstrBytesOperator.php new file mode 100644 index 000000000..ff11d234b --- /dev/null +++ b/src/Builder/Expression/SubstrBytesOperator.php @@ -0,0 +1,61 @@ + 'string', 'start' => 'start', 'length' => 'length']; + + /** @var ResolvesToString|string $string */ + public readonly ResolvesToString|string $string; + + /** @var ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". */ + public readonly ResolvesToInt|int|string $start; + + /** @var ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. */ + public readonly ResolvesToInt|int|string $length; + + /** + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public function __construct( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ) { + $this->string = $string; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($length) && ! str_starts_with($length, '$')) { + throw new InvalidArgumentException('Argument $length can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->length = $length; + } +} diff --git a/src/Builder/Expression/SubstrCPOperator.php b/src/Builder/Expression/SubstrCPOperator.php new file mode 100644 index 000000000..e2c7e89e0 --- /dev/null +++ b/src/Builder/Expression/SubstrCPOperator.php @@ -0,0 +1,61 @@ + 'string', 'start' => 'start', 'length' => 'length']; + + /** @var ResolvesToString|string $string */ + public readonly ResolvesToString|string $string; + + /** @var ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". */ + public readonly ResolvesToInt|int|string $start; + + /** @var ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. */ + public readonly ResolvesToInt|int|string $length; + + /** + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public function __construct( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ) { + $this->string = $string; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($length) && ! str_starts_with($length, '$')) { + throw new InvalidArgumentException('Argument $length can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->length = $length; + } +} diff --git a/src/Builder/Expression/SubstrOperator.php b/src/Builder/Expression/SubstrOperator.php new file mode 100644 index 000000000..3547f5331 --- /dev/null +++ b/src/Builder/Expression/SubstrOperator.php @@ -0,0 +1,61 @@ + 'string', 'start' => 'start', 'length' => 'length']; + + /** @var ResolvesToString|string $string */ + public readonly ResolvesToString|string $string; + + /** @var ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". */ + public readonly ResolvesToInt|int|string $start; + + /** @var ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. */ + public readonly ResolvesToInt|int|string $length; + + /** + * @param ResolvesToString|string $string + * @param ResolvesToInt|int|string $start If start is a negative number, $substr returns an empty string "". + * @param ResolvesToInt|int|string $length If length is a negative number, $substr returns a substring that starts at the specified index and includes the rest of the string. + */ + public function __construct( + ResolvesToString|string $string, + ResolvesToInt|int|string $start, + ResolvesToInt|int|string $length, + ) { + $this->string = $string; + if (is_string($start) && ! str_starts_with($start, '$')) { + throw new InvalidArgumentException('Argument $start can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->start = $start; + if (is_string($length) && ! str_starts_with($length, '$')) { + throw new InvalidArgumentException('Argument $length can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->length = $length; + } +} diff --git a/src/Builder/Expression/SubtractOperator.php b/src/Builder/Expression/SubtractOperator.php new file mode 100644 index 000000000..351f520cb --- /dev/null +++ b/src/Builder/Expression/SubtractOperator.php @@ -0,0 +1,59 @@ + 'expression1', 'expression2' => 'expression2']; + + /** @var DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression1 */ + public readonly DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression1; + + /** @var DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression2 */ + public readonly DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression2; + + /** + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression1 + * @param DateTimeInterface|Decimal128|Int64|ResolvesToDate|ResolvesToNumber|UTCDateTime|float|int|string $expression2 + */ + public function __construct( + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression1, + DateTimeInterface|Decimal128|Int64|UTCDateTime|ResolvesToDate|ResolvesToNumber|float|int|string $expression2, + ) { + if (is_string($expression1) && ! str_starts_with($expression1, '$')) { + throw new InvalidArgumentException('Argument $expression1 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression1 = $expression1; + if (is_string($expression2) && ! str_starts_with($expression2, '$')) { + throw new InvalidArgumentException('Argument $expression2 can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression2 = $expression2; + } +} diff --git a/src/Builder/Expression/SumOperator.php b/src/Builder/Expression/SumOperator.php new file mode 100644 index 000000000..04bfaf06a --- /dev/null +++ b/src/Builder/Expression/SumOperator.php @@ -0,0 +1,54 @@ + 'expression']; + + /** @var list $expression */ + public readonly array $expression; + + /** + * @param BSONArray|Decimal128|Int64|PackedArray|ResolvesToArray|ResolvesToNumber|array|float|int|string ...$expression + * @no-named-arguments + */ + public function __construct( + Decimal128|Int64|PackedArray|ResolvesToArray|ResolvesToNumber|BSONArray|array|float|int|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + if (! array_is_list($expression)) { + throw new InvalidArgumentException('Expected $expression arguments to be a list (array), named arguments are not supported'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/SwitchOperator.php b/src/Builder/Expression/SwitchOperator.php new file mode 100644 index 000000000..7af40b0f1 --- /dev/null +++ b/src/Builder/Expression/SwitchOperator.php @@ -0,0 +1,70 @@ + 'branches', 'default' => 'default']; + + /** + * @var BSONArray|PackedArray|array $branches An array of control branch documents. Each branch is a document with the following fields: + * - case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + * - then Can be any valid expression. + * The branches array must contain at least one branch document. + */ + public readonly PackedArray|BSONArray|array $branches; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default The path to take if no branch case expression evaluates to true. + * Although optional, if default is unspecified and no branch case evaluates to true, $switch returns an error. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default; + + /** + * @param BSONArray|PackedArray|array $branches An array of control branch documents. Each branch is a document with the following fields: + * - case Can be any valid expression that resolves to a boolean. If the result is not a boolean, it is coerced to a boolean value. More information about how MongoDB evaluates expressions as either true or false can be found here. + * - then Can be any valid expression. + * The branches array must contain at least one branch document. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default The path to take if no branch case expression evaluates to true. + * Although optional, if default is unspecified and no branch case evaluates to true, $switch returns an error. + */ + public function __construct( + PackedArray|BSONArray|array $branches, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default = Optional::Undefined, + ) { + if (is_array($branches) && ! array_is_list($branches)) { + throw new InvalidArgumentException('Expected $branches argument to be a list, got an associative array.'); + } + + $this->branches = $branches; + $this->default = $default; + } +} diff --git a/src/Builder/Expression/TanOperator.php b/src/Builder/Expression/TanOperator.php new file mode 100644 index 000000000..c783b0392 --- /dev/null +++ b/src/Builder/Expression/TanOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tan takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $tan returns values as a double. $tan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tan takes any valid expression that resolves to a number. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the result to radians. + * By default $tan returns values as a double. $tan can also return values as a 128-bit decimal as long as the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/TanhOperator.php b/src/Builder/Expression/TanhOperator.php new file mode 100644 index 000000000..e6f8bb619 --- /dev/null +++ b/src/Builder/Expression/TanhOperator.php @@ -0,0 +1,50 @@ + 'expression']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tanh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $tanh returns values as a double. $tanh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $expression; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $expression $tanh takes any valid expression that resolves to a number, measured in radians. If the expression returns a value in degrees, use the $degreesToRadians operator to convert the value to radians. + * By default $tanh returns values as a double. $tanh can also return values as a 128-bit decimal if the expression resolves to a 128-bit decimal value. + */ + public function __construct(Decimal128|Int64|ResolvesToNumber|float|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/TimestampFieldPath.php b/src/Builder/Expression/TimestampFieldPath.php new file mode 100644 index 000000000..5aac9492f --- /dev/null +++ b/src/Builder/Expression/TimestampFieldPath.php @@ -0,0 +1,29 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/ToBoolOperator.php b/src/Builder/Expression/ToBoolOperator.php new file mode 100644 index 000000000..6affe2ea5 --- /dev/null +++ b/src/Builder/Expression/ToBoolOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToDateOperator.php b/src/Builder/Expression/ToDateOperator.php new file mode 100644 index 000000000..9c5a42b1c --- /dev/null +++ b/src/Builder/Expression/ToDateOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToDecimalOperator.php b/src/Builder/Expression/ToDecimalOperator.php new file mode 100644 index 000000000..b1440e9a0 --- /dev/null +++ b/src/Builder/Expression/ToDecimalOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToDoubleOperator.php b/src/Builder/Expression/ToDoubleOperator.php new file mode 100644 index 000000000..8c0d336f5 --- /dev/null +++ b/src/Builder/Expression/ToDoubleOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToHashedIndexKeyOperator.php b/src/Builder/Expression/ToHashedIndexKeyOperator.php new file mode 100644 index 000000000..15f919544 --- /dev/null +++ b/src/Builder/Expression/ToHashedIndexKeyOperator.php @@ -0,0 +1,41 @@ + 'value']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value key or string to hash */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $value key or string to hash + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $value, + ) { + $this->value = $value; + } +} diff --git a/src/Builder/Expression/ToIntOperator.php b/src/Builder/Expression/ToIntOperator.php new file mode 100644 index 000000000..a769d69ad --- /dev/null +++ b/src/Builder/Expression/ToIntOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToLongOperator.php b/src/Builder/Expression/ToLongOperator.php new file mode 100644 index 000000000..7b68c2f55 --- /dev/null +++ b/src/Builder/Expression/ToLongOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToLowerOperator.php b/src/Builder/Expression/ToLowerOperator.php new file mode 100644 index 000000000..500605889 --- /dev/null +++ b/src/Builder/Expression/ToLowerOperator.php @@ -0,0 +1,36 @@ + 'expression']; + + /** @var ResolvesToString|string $expression */ + public readonly ResolvesToString|string $expression; + + /** + * @param ResolvesToString|string $expression + */ + public function __construct(ResolvesToString|string $expression) + { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToObjectIdOperator.php b/src/Builder/Expression/ToObjectIdOperator.php new file mode 100644 index 000000000..bb70f957e --- /dev/null +++ b/src/Builder/Expression/ToObjectIdOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToStringOperator.php b/src/Builder/Expression/ToStringOperator.php new file mode 100644 index 000000000..4d0f59968 --- /dev/null +++ b/src/Builder/Expression/ToStringOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/ToUpperOperator.php b/src/Builder/Expression/ToUpperOperator.php new file mode 100644 index 000000000..604711964 --- /dev/null +++ b/src/Builder/Expression/ToUpperOperator.php @@ -0,0 +1,36 @@ + 'expression']; + + /** @var ResolvesToString|string $expression */ + public readonly ResolvesToString|string $expression; + + /** + * @param ResolvesToString|string $expression + */ + public function __construct(ResolvesToString|string $expression) + { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/TrimOperator.php b/src/Builder/Expression/TrimOperator.php new file mode 100644 index 000000000..0b210e467 --- /dev/null +++ b/src/Builder/Expression/TrimOperator.php @@ -0,0 +1,51 @@ + 'input', 'chars' => 'chars']; + + /** @var ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. */ + public readonly ResolvesToString|string $input; + + /** + * @var Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public readonly Optional|ResolvesToString|string $chars; + + /** + * @param ResolvesToString|string $input The string to trim. The argument can be any valid expression that resolves to a string. + * @param Optional|ResolvesToString|string $chars The character(s) to trim from the beginning of the input. + * The argument can be any valid expression that resolves to a string. The $ltrim operator breaks down the string into individual UTF code point to trim from input. + * If unspecified, $ltrim removes whitespace characters, including the null character. + */ + public function __construct( + ResolvesToString|string $input, + Optional|ResolvesToString|string $chars = Optional::Undefined, + ) { + $this->input = $input; + $this->chars = $chars; + } +} diff --git a/src/Builder/Expression/TruncOperator.php b/src/Builder/Expression/TruncOperator.php new file mode 100644 index 000000000..b8e98c562 --- /dev/null +++ b/src/Builder/Expression/TruncOperator.php @@ -0,0 +1,62 @@ + 'number', 'place' => 'place']; + + /** + * @var Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $trunc returns an error if the expression resolves to a non-numeric data type. + */ + public readonly Decimal128|Int64|ResolvesToNumber|float|int|string $number; + + /** @var Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. e.g. -20 < place < 100. Defaults to 0. */ + public readonly Optional|ResolvesToInt|int|string $place; + + /** + * @param Decimal128|Int64|ResolvesToNumber|float|int|string $number Can be any valid expression that resolves to a number. Specifically, the expression must resolve to an integer, double, decimal, or long. + * $trunc returns an error if the expression resolves to a non-numeric data type. + * @param Optional|ResolvesToInt|int|string $place Can be any valid expression that resolves to an integer between -20 and 100, exclusive. e.g. -20 < place < 100. Defaults to 0. + */ + public function __construct( + Decimal128|Int64|ResolvesToNumber|float|int|string $number, + Optional|ResolvesToInt|int|string $place = Optional::Undefined, + ) { + if (is_string($number) && ! str_starts_with($number, '$')) { + throw new InvalidArgumentException('Argument $number can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->number = $number; + if (is_string($place) && ! str_starts_with($place, '$')) { + throw new InvalidArgumentException('Argument $place can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->place = $place; + } +} diff --git a/src/Builder/Expression/TsIncrementOperator.php b/src/Builder/Expression/TsIncrementOperator.php new file mode 100644 index 000000000..3e1e404c1 --- /dev/null +++ b/src/Builder/Expression/TsIncrementOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var ResolvesToTimestamp|Timestamp|int|string $expression */ + public readonly Timestamp|ResolvesToTimestamp|int|string $expression; + + /** + * @param ResolvesToTimestamp|Timestamp|int|string $expression + */ + public function __construct(Timestamp|ResolvesToTimestamp|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/TsSecondOperator.php b/src/Builder/Expression/TsSecondOperator.php new file mode 100644 index 000000000..bea14d9e1 --- /dev/null +++ b/src/Builder/Expression/TsSecondOperator.php @@ -0,0 +1,46 @@ + 'expression']; + + /** @var ResolvesToTimestamp|Timestamp|int|string $expression */ + public readonly Timestamp|ResolvesToTimestamp|int|string $expression; + + /** + * @param ResolvesToTimestamp|Timestamp|int|string $expression + */ + public function __construct(Timestamp|ResolvesToTimestamp|int|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/TypeOperator.php b/src/Builder/Expression/TypeOperator.php new file mode 100644 index 000000000..78a25aa6e --- /dev/null +++ b/src/Builder/Expression/TypeOperator.php @@ -0,0 +1,41 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Expression/UnsetFieldOperator.php b/src/Builder/Expression/UnsetFieldOperator.php new file mode 100644 index 000000000..1d0fcf9d2 --- /dev/null +++ b/src/Builder/Expression/UnsetFieldOperator.php @@ -0,0 +1,55 @@ + 'field', 'input' => 'input']; + + /** @var ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. */ + public readonly ResolvesToString|string $field; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $input; + + /** + * @param ResolvesToString|string $field Field in the input object that you want to add, update, or remove. field can be any valid expression that resolves to a string constant. + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $input Document that contains the field that you want to add or update. input must resolve to an object, missing, null, or undefined. + */ + public function __construct( + ResolvesToString|string $field, + Document|Serializable|ResolvesToObject|stdClass|array|string $input, + ) { + $this->field = $field; + if (is_string($input) && ! str_starts_with($input, '$')) { + throw new InvalidArgumentException('Argument $input can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->input = $input; + } +} diff --git a/src/Builder/Expression/Variable.php b/src/Builder/Expression/Variable.php new file mode 100644 index 000000000..99b0750be --- /dev/null +++ b/src/Builder/Expression/Variable.php @@ -0,0 +1,28 @@ +name = $name; + } +} diff --git a/src/Builder/Expression/WeekOperator.php b/src/Builder/Expression/WeekOperator.php new file mode 100644 index 000000000..a895012dc --- /dev/null +++ b/src/Builder/Expression/WeekOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/YearOperator.php b/src/Builder/Expression/YearOperator.php new file mode 100644 index 000000000..314b1de9f --- /dev/null +++ b/src/Builder/Expression/YearOperator.php @@ -0,0 +1,56 @@ + 'date', 'timezone' => 'timezone']; + + /** @var DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. */ + public readonly DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date; + + /** @var Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. */ + public readonly Optional|ResolvesToString|string $timezone; + + /** + * @param DateTimeInterface|ObjectId|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|Timestamp|UTCDateTime|int|string $date The date to which the operator is applied. date must be a valid expression that resolves to a Date, a Timestamp, or an ObjectID. + * @param Optional|ResolvesToString|string $timezone The timezone of the operation result. timezone must be a valid expression that resolves to a string formatted as either an Olson Timezone Identifier or a UTC Offset. If no timezone is provided, the result is displayed in UTC. + */ + public function __construct( + DateTimeInterface|ObjectId|Timestamp|UTCDateTime|ResolvesToDate|ResolvesToObjectId|ResolvesToTimestamp|int|string $date, + Optional|ResolvesToString|string $timezone = Optional::Undefined, + ) { + if (is_string($date) && ! str_starts_with($date, '$')) { + throw new InvalidArgumentException('Argument $date can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->date = $date; + $this->timezone = $timezone; + } +} diff --git a/src/Builder/Expression/ZipOperator.php b/src/Builder/Expression/ZipOperator.php new file mode 100644 index 000000000..cc98f9ecf --- /dev/null +++ b/src/Builder/Expression/ZipOperator.php @@ -0,0 +1,86 @@ + 'inputs', 'useLongestLength' => 'useLongestLength', 'defaults' => 'defaults']; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + * If any of the inputs arrays resolves to a value of null or refers to a missing field, $zip returns null. + * If any of the inputs arrays does not resolve to an array or null nor refers to a missing field, $zip returns an error. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $inputs; + + /** + * @var Optional|bool $useLongestLength A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. + * The default value is false: the shortest array length determines the number of arrays in the output array. + */ + public readonly Optional|bool $useLongestLength; + + /** + * @var Optional|BSONArray|PackedArray|array $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + * If useLongestLength: true but defaults is empty or not specified, $zip uses null as the default value. + * If specifying a non-empty defaults, you must specify a default for each input array or else $zip will return an error. + */ + public readonly Optional|PackedArray|BSONArray|array $defaults; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $inputs An array of expressions that resolve to arrays. The elements of these input arrays combine to form the arrays of the output array. + * If any of the inputs arrays resolves to a value of null or refers to a missing field, $zip returns null. + * If any of the inputs arrays does not resolve to an array or null nor refers to a missing field, $zip returns an error. + * @param Optional|bool $useLongestLength A boolean which specifies whether the length of the longest array determines the number of arrays in the output array. + * The default value is false: the shortest array length determines the number of arrays in the output array. + * @param Optional|BSONArray|PackedArray|array $defaults An array of default element values to use if the input arrays have different lengths. You must specify useLongestLength: true along with this field, or else $zip will return an error. + * If useLongestLength: true but defaults is empty or not specified, $zip uses null as the default value. + * If specifying a non-empty defaults, you must specify a default for each input array or else $zip will return an error. + */ + public function __construct( + PackedArray|ResolvesToArray|BSONArray|array|string $inputs, + Optional|bool $useLongestLength = Optional::Undefined, + Optional|PackedArray|BSONArray|array $defaults = Optional::Undefined, + ) { + if (is_string($inputs) && ! str_starts_with($inputs, '$')) { + throw new InvalidArgumentException('Argument $inputs can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($inputs) && ! array_is_list($inputs)) { + throw new InvalidArgumentException('Expected $inputs argument to be a list, got an associative array.'); + } + + $this->inputs = $inputs; + $this->useLongestLength = $useLongestLength; + if (is_array($defaults) && ! array_is_list($defaults)) { + throw new InvalidArgumentException('Expected $defaults argument to be a list, got an associative array.'); + } + + $this->defaults = $defaults; + } +} diff --git a/src/Builder/Pipeline.php b/src/Builder/Pipeline.php new file mode 100644 index 000000000..e481277a9 --- /dev/null +++ b/src/Builder/Pipeline.php @@ -0,0 +1,60 @@ +|stdClass + * @implements IteratorAggregate + */ +final class Pipeline implements IteratorAggregate +{ + private readonly array $stages; + + /** + * @psalm-param stage|list ...$stagesOrPipelines + * + * @no-named-arguments + */ + public function __construct(StageInterface|Pipeline|array|stdClass ...$stagesOrPipelines) + { + if (! array_is_list($stagesOrPipelines)) { + throw new InvalidArgumentException('Named arguments are not supported for pipelines'); + } + + $stages = []; + + foreach ($stagesOrPipelines as $stageOrPipeline) { + if (is_array($stageOrPipeline) && array_is_list($stageOrPipeline)) { + $stages = array_merge($stages, $stageOrPipeline); + } elseif ($stageOrPipeline instanceof Pipeline) { + $stages = array_merge($stages, $stageOrPipeline->stages); + } else { + $stages[] = $stageOrPipeline; + } + } + + $this->stages = $stages; + } + + public function getIterator(): ArrayIterator + { + return new ArrayIterator($this->stages); + } +} diff --git a/src/Builder/Query.php b/src/Builder/Query.php new file mode 100644 index 000000000..b19b293fc --- /dev/null +++ b/src/Builder/Query.php @@ -0,0 +1,61 @@ + 'value']; + + /** @var list $value */ + public readonly array $value; + + /** + * @param DateTimeInterface|FieldQueryInterface|Type|array|bool|float|int|null|stdClass|string ...$value + * @no-named-arguments + */ + public function __construct( + DateTimeInterface|Type|FieldQueryInterface|stdClass|array|bool|float|int|null|string ...$value, + ) { + if (\count($value) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $value, got %d.', 1, \count($value))); + } + + if (! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value arguments to be a list (array), named arguments are not supported'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/AndOperator.php b/src/Builder/Query/AndOperator.php new file mode 100644 index 000000000..8738591be --- /dev/null +++ b/src/Builder/Query/AndOperator.php @@ -0,0 +1,49 @@ + 'queries']; + + /** @var list $queries */ + public readonly array $queries; + + /** + * @param QueryInterface|array ...$queries + * @no-named-arguments + */ + public function __construct(QueryInterface|array ...$queries) + { + if (\count($queries) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $queries, got %d.', 1, \count($queries))); + } + + if (! array_is_list($queries)) { + throw new InvalidArgumentException('Expected $queries arguments to be a list (array), named arguments are not supported'); + } + + $this->queries = $queries; + } +} diff --git a/src/Builder/Query/BitsAllClearOperator.php b/src/Builder/Query/BitsAllClearOperator.php new file mode 100644 index 000000000..072c031bb --- /dev/null +++ b/src/Builder/Query/BitsAllClearOperator.php @@ -0,0 +1,48 @@ + 'bitmask']; + + /** @var BSONArray|Binary|PackedArray|array|int|string $bitmask */ + public readonly Binary|PackedArray|BSONArray|array|int|string $bitmask; + + /** + * @param BSONArray|Binary|PackedArray|array|int|string $bitmask + */ + public function __construct(Binary|PackedArray|BSONArray|array|int|string $bitmask) + { + if (is_array($bitmask) && ! array_is_list($bitmask)) { + throw new InvalidArgumentException('Expected $bitmask argument to be a list, got an associative array.'); + } + + $this->bitmask = $bitmask; + } +} diff --git a/src/Builder/Query/BitsAllSetOperator.php b/src/Builder/Query/BitsAllSetOperator.php new file mode 100644 index 000000000..a90e12f35 --- /dev/null +++ b/src/Builder/Query/BitsAllSetOperator.php @@ -0,0 +1,48 @@ + 'bitmask']; + + /** @var BSONArray|Binary|PackedArray|array|int|string $bitmask */ + public readonly Binary|PackedArray|BSONArray|array|int|string $bitmask; + + /** + * @param BSONArray|Binary|PackedArray|array|int|string $bitmask + */ + public function __construct(Binary|PackedArray|BSONArray|array|int|string $bitmask) + { + if (is_array($bitmask) && ! array_is_list($bitmask)) { + throw new InvalidArgumentException('Expected $bitmask argument to be a list, got an associative array.'); + } + + $this->bitmask = $bitmask; + } +} diff --git a/src/Builder/Query/BitsAnyClearOperator.php b/src/Builder/Query/BitsAnyClearOperator.php new file mode 100644 index 000000000..0ae56d93a --- /dev/null +++ b/src/Builder/Query/BitsAnyClearOperator.php @@ -0,0 +1,48 @@ + 'bitmask']; + + /** @var BSONArray|Binary|PackedArray|array|int|string $bitmask */ + public readonly Binary|PackedArray|BSONArray|array|int|string $bitmask; + + /** + * @param BSONArray|Binary|PackedArray|array|int|string $bitmask + */ + public function __construct(Binary|PackedArray|BSONArray|array|int|string $bitmask) + { + if (is_array($bitmask) && ! array_is_list($bitmask)) { + throw new InvalidArgumentException('Expected $bitmask argument to be a list, got an associative array.'); + } + + $this->bitmask = $bitmask; + } +} diff --git a/src/Builder/Query/BitsAnySetOperator.php b/src/Builder/Query/BitsAnySetOperator.php new file mode 100644 index 000000000..5a202c9d4 --- /dev/null +++ b/src/Builder/Query/BitsAnySetOperator.php @@ -0,0 +1,48 @@ + 'bitmask']; + + /** @var BSONArray|Binary|PackedArray|array|int|string $bitmask */ + public readonly Binary|PackedArray|BSONArray|array|int|string $bitmask; + + /** + * @param BSONArray|Binary|PackedArray|array|int|string $bitmask + */ + public function __construct(Binary|PackedArray|BSONArray|array|int|string $bitmask) + { + if (is_array($bitmask) && ! array_is_list($bitmask)) { + throw new InvalidArgumentException('Expected $bitmask argument to be a list, got an associative array.'); + } + + $this->bitmask = $bitmask; + } +} diff --git a/src/Builder/Query/BoxOperator.php b/src/Builder/Query/BoxOperator.php new file mode 100644 index 000000000..cce9cf8d8 --- /dev/null +++ b/src/Builder/Query/BoxOperator.php @@ -0,0 +1,47 @@ + 'value']; + + /** @var BSONArray|PackedArray|array $value */ + public readonly PackedArray|BSONArray|array $value; + + /** + * @param BSONArray|PackedArray|array $value + */ + public function __construct(PackedArray|BSONArray|array $value) + { + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/CenterOperator.php b/src/Builder/Query/CenterOperator.php new file mode 100644 index 000000000..f3a0b890a --- /dev/null +++ b/src/Builder/Query/CenterOperator.php @@ -0,0 +1,47 @@ + 'value']; + + /** @var BSONArray|PackedArray|array $value */ + public readonly PackedArray|BSONArray|array $value; + + /** + * @param BSONArray|PackedArray|array $value + */ + public function __construct(PackedArray|BSONArray|array $value) + { + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/CenterSphereOperator.php b/src/Builder/Query/CenterSphereOperator.php new file mode 100644 index 000000000..350f535f0 --- /dev/null +++ b/src/Builder/Query/CenterSphereOperator.php @@ -0,0 +1,47 @@ + 'value']; + + /** @var BSONArray|PackedArray|array $value */ + public readonly PackedArray|BSONArray|array $value; + + /** + * @param BSONArray|PackedArray|array $value + */ + public function __construct(PackedArray|BSONArray|array $value) + { + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/CommentOperator.php b/src/Builder/Query/CommentOperator.php new file mode 100644 index 000000000..938498039 --- /dev/null +++ b/src/Builder/Query/CommentOperator.php @@ -0,0 +1,37 @@ + 'comment']; + + /** @var string $comment */ + public readonly string $comment; + + /** + * @param string $comment + */ + public function __construct(string $comment) + { + $this->comment = $comment; + } +} diff --git a/src/Builder/Query/ElemMatchOperator.php b/src/Builder/Query/ElemMatchOperator.php new file mode 100644 index 000000000..59d59528b --- /dev/null +++ b/src/Builder/Query/ElemMatchOperator.php @@ -0,0 +1,49 @@ + 'query']; + + /** @var DateTimeInterface|FieldQueryInterface|QueryInterface|Type|array|bool|float|int|null|stdClass|string $query */ + public readonly DateTimeInterface|Type|FieldQueryInterface|QueryInterface|stdClass|array|bool|float|int|null|string $query; + + /** + * @param DateTimeInterface|FieldQueryInterface|QueryInterface|Type|array|bool|float|int|null|stdClass|string $query + */ + public function __construct( + DateTimeInterface|Type|FieldQueryInterface|QueryInterface|stdClass|array|bool|float|int|null|string $query, + ) { + if (is_array($query)) { + $query = QueryObject::create($query); + } + + $this->query = $query; + } +} diff --git a/src/Builder/Query/EqOperator.php b/src/Builder/Query/EqOperator.php new file mode 100644 index 000000000..73eaae719 --- /dev/null +++ b/src/Builder/Query/EqOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/ExistsOperator.php b/src/Builder/Query/ExistsOperator.php new file mode 100644 index 000000000..1d0b0a292 --- /dev/null +++ b/src/Builder/Query/ExistsOperator.php @@ -0,0 +1,37 @@ + 'exists']; + + /** @var bool $exists */ + public readonly bool $exists; + + /** + * @param bool $exists + */ + public function __construct(bool $exists = true) + { + $this->exists = $exists; + } +} diff --git a/src/Builder/Query/ExprOperator.php b/src/Builder/Query/ExprOperator.php new file mode 100644 index 000000000..15562f046 --- /dev/null +++ b/src/Builder/Query/ExprOperator.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Query/FactoryTrait.php b/src/Builder/Query/FactoryTrait.php new file mode 100644 index 000000000..cedc2adb7 --- /dev/null +++ b/src/Builder/Query/FactoryTrait.php @@ -0,0 +1,523 @@ + null]; + + /** @var Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** + * @param Document|GeometryInterface|Serializable|array|stdClass $geometry + */ + public function __construct(Document|Serializable|GeometryInterface|stdClass|array $geometry) + { + $this->geometry = $geometry; + } +} diff --git a/src/Builder/Query/GeoWithinOperator.php b/src/Builder/Query/GeoWithinOperator.php new file mode 100644 index 000000000..258592f00 --- /dev/null +++ b/src/Builder/Query/GeoWithinOperator.php @@ -0,0 +1,41 @@ + null]; + + /** @var Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** + * @param Document|GeometryInterface|Serializable|array|stdClass $geometry + */ + public function __construct(Document|Serializable|GeometryInterface|stdClass|array $geometry) + { + $this->geometry = $geometry; + } +} diff --git a/src/Builder/Query/GeometryOperator.php b/src/Builder/Query/GeometryOperator.php new file mode 100644 index 000000000..fe52be973 --- /dev/null +++ b/src/Builder/Query/GeometryOperator.php @@ -0,0 +1,64 @@ + 'type', 'coordinates' => 'coordinates', 'crs' => 'crs']; + + /** @var string $type */ + public readonly string $type; + + /** @var BSONArray|PackedArray|array $coordinates */ + public readonly PackedArray|BSONArray|array $coordinates; + + /** @var Optional|Document|Serializable|array|stdClass $crs */ + public readonly Optional|Document|Serializable|stdClass|array $crs; + + /** + * @param string $type + * @param BSONArray|PackedArray|array $coordinates + * @param Optional|Document|Serializable|array|stdClass $crs + */ + public function __construct( + string $type, + PackedArray|BSONArray|array $coordinates, + Optional|Document|Serializable|stdClass|array $crs = Optional::Undefined, + ) { + $this->type = $type; + if (is_array($coordinates) && ! array_is_list($coordinates)) { + throw new InvalidArgumentException('Expected $coordinates argument to be a list, got an associative array.'); + } + + $this->coordinates = $coordinates; + $this->crs = $crs; + } +} diff --git a/src/Builder/Query/GtOperator.php b/src/Builder/Query/GtOperator.php new file mode 100644 index 000000000..235e26c7b --- /dev/null +++ b/src/Builder/Query/GtOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/GteOperator.php b/src/Builder/Query/GteOperator.php new file mode 100644 index 000000000..05d8e5961 --- /dev/null +++ b/src/Builder/Query/GteOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/InOperator.php b/src/Builder/Query/InOperator.php new file mode 100644 index 000000000..e6f1456a4 --- /dev/null +++ b/src/Builder/Query/InOperator.php @@ -0,0 +1,47 @@ + 'value']; + + /** @var BSONArray|PackedArray|array $value */ + public readonly PackedArray|BSONArray|array $value; + + /** + * @param BSONArray|PackedArray|array $value + */ + public function __construct(PackedArray|BSONArray|array $value) + { + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/JsonSchemaOperator.php b/src/Builder/Query/JsonSchemaOperator.php new file mode 100644 index 000000000..3dc8b918e --- /dev/null +++ b/src/Builder/Query/JsonSchemaOperator.php @@ -0,0 +1,40 @@ + 'schema']; + + /** @var Document|Serializable|array|stdClass $schema */ + public readonly Document|Serializable|stdClass|array $schema; + + /** + * @param Document|Serializable|array|stdClass $schema + */ + public function __construct(Document|Serializable|stdClass|array $schema) + { + $this->schema = $schema; + } +} diff --git a/src/Builder/Query/LtOperator.php b/src/Builder/Query/LtOperator.php new file mode 100644 index 000000000..b4c2dd449 --- /dev/null +++ b/src/Builder/Query/LtOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/LteOperator.php b/src/Builder/Query/LteOperator.php new file mode 100644 index 000000000..d0ca1fc9c --- /dev/null +++ b/src/Builder/Query/LteOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/MaxDistanceOperator.php b/src/Builder/Query/MaxDistanceOperator.php new file mode 100644 index 000000000..2aea7d4c8 --- /dev/null +++ b/src/Builder/Query/MaxDistanceOperator.php @@ -0,0 +1,39 @@ + 'value']; + + /** @var Decimal128|Int64|float|int $value */ + public readonly Decimal128|Int64|float|int $value; + + /** + * @param Decimal128|Int64|float|int $value + */ + public function __construct(Decimal128|Int64|float|int $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/MinDistanceOperator.php b/src/Builder/Query/MinDistanceOperator.php new file mode 100644 index 000000000..74aadb397 --- /dev/null +++ b/src/Builder/Query/MinDistanceOperator.php @@ -0,0 +1,38 @@ + 'value']; + + /** @var Int64|float|int $value */ + public readonly Int64|float|int $value; + + /** + * @param Int64|float|int $value + */ + public function __construct(Int64|float|int $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/ModOperator.php b/src/Builder/Query/ModOperator.php new file mode 100644 index 000000000..bbabdfdc9 --- /dev/null +++ b/src/Builder/Query/ModOperator.php @@ -0,0 +1,44 @@ + 'divisor', 'remainder' => 'remainder']; + + /** @var Decimal128|Int64|float|int $divisor */ + public readonly Decimal128|Int64|float|int $divisor; + + /** @var Decimal128|Int64|float|int $remainder */ + public readonly Decimal128|Int64|float|int $remainder; + + /** + * @param Decimal128|Int64|float|int $divisor + * @param Decimal128|Int64|float|int $remainder + */ + public function __construct(Decimal128|Int64|float|int $divisor, Decimal128|Int64|float|int $remainder) + { + $this->divisor = $divisor; + $this->remainder = $remainder; + } +} diff --git a/src/Builder/Query/NeOperator.php b/src/Builder/Query/NeOperator.php new file mode 100644 index 000000000..e5a8cd31d --- /dev/null +++ b/src/Builder/Query/NeOperator.php @@ -0,0 +1,40 @@ + 'value']; + + /** @var DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value; + + /** + * @param DateTimeInterface|Type|array|bool|float|int|null|stdClass|string $value + */ + public function __construct(DateTimeInterface|Type|stdClass|array|bool|float|int|null|string $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/NearOperator.php b/src/Builder/Query/NearOperator.php new file mode 100644 index 000000000..85be90ac4 --- /dev/null +++ b/src/Builder/Query/NearOperator.php @@ -0,0 +1,57 @@ + null, 'maxDistance' => '$maxDistance', 'minDistance' => '$minDistance']; + + /** @var Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** @var Optional|Decimal128|Int64|float|int $maxDistance Distance in meters. Limits the results to those documents that are at most the specified distance from the center point. */ + public readonly Optional|Decimal128|Int64|float|int $maxDistance; + + /** @var Optional|Decimal128|Int64|float|int $minDistance Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. */ + public readonly Optional|Decimal128|Int64|float|int $minDistance; + + /** + * @param Document|GeometryInterface|Serializable|array|stdClass $geometry + * @param Optional|Decimal128|Int64|float|int $maxDistance Distance in meters. Limits the results to those documents that are at most the specified distance from the center point. + * @param Optional|Decimal128|Int64|float|int $minDistance Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. + */ + public function __construct( + Document|Serializable|GeometryInterface|stdClass|array $geometry, + Optional|Decimal128|Int64|float|int $maxDistance = Optional::Undefined, + Optional|Decimal128|Int64|float|int $minDistance = Optional::Undefined, + ) { + $this->geometry = $geometry; + $this->maxDistance = $maxDistance; + $this->minDistance = $minDistance; + } +} diff --git a/src/Builder/Query/NearSphereOperator.php b/src/Builder/Query/NearSphereOperator.php new file mode 100644 index 000000000..5494048b0 --- /dev/null +++ b/src/Builder/Query/NearSphereOperator.php @@ -0,0 +1,57 @@ + null, 'maxDistance' => '$maxDistance', 'minDistance' => '$minDistance']; + + /** @var Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** @var Optional|Decimal128|Int64|float|int $maxDistance Distance in meters. */ + public readonly Optional|Decimal128|Int64|float|int $maxDistance; + + /** @var Optional|Decimal128|Int64|float|int $minDistance Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. */ + public readonly Optional|Decimal128|Int64|float|int $minDistance; + + /** + * @param Document|GeometryInterface|Serializable|array|stdClass $geometry + * @param Optional|Decimal128|Int64|float|int $maxDistance Distance in meters. + * @param Optional|Decimal128|Int64|float|int $minDistance Distance in meters. Limits the results to those documents that are at least the specified distance from the center point. + */ + public function __construct( + Document|Serializable|GeometryInterface|stdClass|array $geometry, + Optional|Decimal128|Int64|float|int $maxDistance = Optional::Undefined, + Optional|Decimal128|Int64|float|int $minDistance = Optional::Undefined, + ) { + $this->geometry = $geometry; + $this->maxDistance = $maxDistance; + $this->minDistance = $minDistance; + } +} diff --git a/src/Builder/Query/NinOperator.php b/src/Builder/Query/NinOperator.php new file mode 100644 index 000000000..5c82c3fd8 --- /dev/null +++ b/src/Builder/Query/NinOperator.php @@ -0,0 +1,47 @@ + 'value']; + + /** @var BSONArray|PackedArray|array $value */ + public readonly PackedArray|BSONArray|array $value; + + /** + * @param BSONArray|PackedArray|array $value + */ + public function __construct(PackedArray|BSONArray|array $value) + { + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + } +} diff --git a/src/Builder/Query/NorOperator.php b/src/Builder/Query/NorOperator.php new file mode 100644 index 000000000..06bdc7e39 --- /dev/null +++ b/src/Builder/Query/NorOperator.php @@ -0,0 +1,49 @@ + 'queries']; + + /** @var list $queries */ + public readonly array $queries; + + /** + * @param QueryInterface|array ...$queries + * @no-named-arguments + */ + public function __construct(QueryInterface|array ...$queries) + { + if (\count($queries) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $queries, got %d.', 1, \count($queries))); + } + + if (! array_is_list($queries)) { + throw new InvalidArgumentException('Expected $queries arguments to be a list (array), named arguments are not supported'); + } + + $this->queries = $queries; + } +} diff --git a/src/Builder/Query/NotOperator.php b/src/Builder/Query/NotOperator.php new file mode 100644 index 000000000..b2ba0894b --- /dev/null +++ b/src/Builder/Query/NotOperator.php @@ -0,0 +1,41 @@ + 'expression']; + + /** @var DateTimeInterface|FieldQueryInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|FieldQueryInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|FieldQueryInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|FieldQueryInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Query/OrOperator.php b/src/Builder/Query/OrOperator.php new file mode 100644 index 000000000..1b922fdb4 --- /dev/null +++ b/src/Builder/Query/OrOperator.php @@ -0,0 +1,49 @@ + 'queries']; + + /** @var list $queries */ + public readonly array $queries; + + /** + * @param QueryInterface|array ...$queries + * @no-named-arguments + */ + public function __construct(QueryInterface|array ...$queries) + { + if (\count($queries) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $queries, got %d.', 1, \count($queries))); + } + + if (! array_is_list($queries)) { + throw new InvalidArgumentException('Expected $queries arguments to be a list (array), named arguments are not supported'); + } + + $this->queries = $queries; + } +} diff --git a/src/Builder/Query/PolygonOperator.php b/src/Builder/Query/PolygonOperator.php new file mode 100644 index 000000000..bba3aac08 --- /dev/null +++ b/src/Builder/Query/PolygonOperator.php @@ -0,0 +1,47 @@ + 'points']; + + /** @var BSONArray|PackedArray|array $points */ + public readonly PackedArray|BSONArray|array $points; + + /** + * @param BSONArray|PackedArray|array $points + */ + public function __construct(PackedArray|BSONArray|array $points) + { + if (is_array($points) && ! array_is_list($points)) { + throw new InvalidArgumentException('Expected $points argument to be a list, got an associative array.'); + } + + $this->points = $points; + } +} diff --git a/src/Builder/Query/RandOperator.php b/src/Builder/Query/RandOperator.php new file mode 100644 index 000000000..ccf8a8d28 --- /dev/null +++ b/src/Builder/Query/RandOperator.php @@ -0,0 +1,29 @@ + 'regex']; + + /** @var Regex $regex */ + public readonly Regex $regex; + + /** + * @param Regex $regex + */ + public function __construct(Regex $regex) + { + $this->regex = $regex; + } +} diff --git a/src/Builder/Query/SampleRateOperator.php b/src/Builder/Query/SampleRateOperator.php new file mode 100644 index 000000000..a027972da --- /dev/null +++ b/src/Builder/Query/SampleRateOperator.php @@ -0,0 +1,51 @@ + 'rate']; + + /** + * @var Int64|ResolvesToDouble|float|int|string $rate The selection process uses a uniform random distribution. The sample rate is a floating point number between 0 and 1, inclusive, which represents the probability that a given document will be selected as it passes through the pipeline. + * For example, a sample rate of 0.33 selects roughly one document in three. + */ + public readonly Int64|ResolvesToDouble|float|int|string $rate; + + /** + * @param Int64|ResolvesToDouble|float|int|string $rate The selection process uses a uniform random distribution. The sample rate is a floating point number between 0 and 1, inclusive, which represents the probability that a given document will be selected as it passes through the pipeline. + * For example, a sample rate of 0.33 selects roughly one document in three. + */ + public function __construct(Int64|ResolvesToDouble|float|int|string $rate) + { + if (is_string($rate) && ! str_starts_with($rate, '$')) { + throw new InvalidArgumentException('Argument $rate can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->rate = $rate; + } +} diff --git a/src/Builder/Query/SizeOperator.php b/src/Builder/Query/SizeOperator.php new file mode 100644 index 000000000..8ec123b11 --- /dev/null +++ b/src/Builder/Query/SizeOperator.php @@ -0,0 +1,37 @@ + 'value']; + + /** @var int $value */ + public readonly int $value; + + /** + * @param int $value + */ + public function __construct(int $value) + { + $this->value = $value; + } +} diff --git a/src/Builder/Query/TextOperator.php b/src/Builder/Query/TextOperator.php new file mode 100644 index 000000000..7c1c0a7ba --- /dev/null +++ b/src/Builder/Query/TextOperator.php @@ -0,0 +1,71 @@ + '$search', + 'language' => '$language', + 'caseSensitive' => '$caseSensitive', + 'diacriticSensitive' => '$diacriticSensitive', + ]; + + /** @var string $search A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase. */ + public readonly string $search; + + /** + * @var Optional|string $language The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index. + * If you specify a default_language value of none, then the text index parses through each word in the field, including stop words, and ignores suffix stemming. + */ + public readonly Optional|string $language; + + /** @var Optional|bool $caseSensitive A boolean flag to enable or disable case sensitive search. Defaults to false; i.e. the search defers to the case insensitivity of the text index. */ + public readonly Optional|bool $caseSensitive; + + /** + * @var Optional|bool $diacriticSensitive A boolean flag to enable or disable diacritic sensitive search against version 3 text indexes. Defaults to false; i.e. the search defers to the diacritic insensitivity of the text index. + * Text searches against earlier versions of the text index are inherently diacritic sensitive and cannot be diacritic insensitive. As such, the $diacriticSensitive option has no effect with earlier versions of the text index. + */ + public readonly Optional|bool $diacriticSensitive; + + /** + * @param string $search A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase. + * @param Optional|string $language The language that determines the list of stop words for the search and the rules for the stemmer and tokenizer. If not specified, the search uses the default language of the index. + * If you specify a default_language value of none, then the text index parses through each word in the field, including stop words, and ignores suffix stemming. + * @param Optional|bool $caseSensitive A boolean flag to enable or disable case sensitive search. Defaults to false; i.e. the search defers to the case insensitivity of the text index. + * @param Optional|bool $diacriticSensitive A boolean flag to enable or disable diacritic sensitive search against version 3 text indexes. Defaults to false; i.e. the search defers to the diacritic insensitivity of the text index. + * Text searches against earlier versions of the text index are inherently diacritic sensitive and cannot be diacritic insensitive. As such, the $diacriticSensitive option has no effect with earlier versions of the text index. + */ + public function __construct( + string $search, + Optional|string $language = Optional::Undefined, + Optional|bool $caseSensitive = Optional::Undefined, + Optional|bool $diacriticSensitive = Optional::Undefined, + ) { + $this->search = $search; + $this->language = $language; + $this->caseSensitive = $caseSensitive; + $this->diacriticSensitive = $diacriticSensitive; + } +} diff --git a/src/Builder/Query/TypeOperator.php b/src/Builder/Query/TypeOperator.php new file mode 100644 index 000000000..e539ebfc8 --- /dev/null +++ b/src/Builder/Query/TypeOperator.php @@ -0,0 +1,49 @@ + 'type']; + + /** @var list $type */ + public readonly array $type; + + /** + * @param int|string ...$type + * @no-named-arguments + */ + public function __construct(int|string ...$type) + { + if (\count($type) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $type, got %d.', 1, \count($type))); + } + + if (! array_is_list($type)) { + throw new InvalidArgumentException('Expected $type arguments to be a list (array), named arguments are not supported'); + } + + $this->type = $type; + } +} diff --git a/src/Builder/Query/WhereOperator.php b/src/Builder/Query/WhereOperator.php new file mode 100644 index 000000000..e679f7347 --- /dev/null +++ b/src/Builder/Query/WhereOperator.php @@ -0,0 +1,44 @@ + 'function']; + + /** @var Javascript|string $function */ + public readonly Javascript|string $function; + + /** + * @param Javascript|string $function + */ + public function __construct(Javascript|string $function) + { + if (is_string($function)) { + $function = new Javascript($function); + } + + $this->function = $function; + } +} diff --git a/src/Builder/Search.php b/src/Builder/Search.php new file mode 100644 index 000000000..7cab84641 --- /dev/null +++ b/src/Builder/Search.php @@ -0,0 +1,10 @@ + 'path', + 'query' => 'query', + 'tokenOrder' => 'tokenOrder', + 'fuzzy' => 'fuzzy', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var string $query */ + public readonly string $query; + + /** @var Optional|string $tokenOrder */ + public readonly Optional|string $tokenOrder; + + /** @var Optional|Document|Serializable|array|stdClass $fuzzy */ + public readonly Optional|Document|Serializable|stdClass|array $fuzzy; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param string $query + * @param Optional|string $tokenOrder + * @param Optional|Document|Serializable|array|stdClass $fuzzy + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + string $query, + Optional|string $tokenOrder = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $fuzzy = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->query = $query; + $this->tokenOrder = $tokenOrder; + $this->fuzzy = $fuzzy; + $this->score = $score; + } +} diff --git a/src/Builder/Search/CompoundOperator.php b/src/Builder/Search/CompoundOperator.php new file mode 100644 index 000000000..27b8c2aeb --- /dev/null +++ b/src/Builder/Search/CompoundOperator.php @@ -0,0 +1,84 @@ + 'must', + 'mustNot' => 'mustNot', + 'should' => 'should', + 'filter' => 'filter', + 'minimumShouldMatch' => 'minimumShouldMatch', + 'score' => 'score', + ]; + + /** @var Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $must */ + public readonly Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $must; + + /** @var Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $mustNot */ + public readonly Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $mustNot; + + /** @var Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $should */ + public readonly Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $should; + + /** @var Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $filter */ + public readonly Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $filter; + + /** @var Optional|int $minimumShouldMatch */ + public readonly Optional|int $minimumShouldMatch; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $must + * @param Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $mustNot + * @param Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $should + * @param Optional|BSONArray|Document|PackedArray|SearchOperatorInterface|Serializable|array|stdClass $filter + * @param Optional|int $minimumShouldMatch + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $must = Optional::Undefined, + Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $mustNot = Optional::Undefined, + Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $should = Optional::Undefined, + Optional|Document|PackedArray|Serializable|SearchOperatorInterface|BSONArray|stdClass|array $filter = Optional::Undefined, + Optional|int $minimumShouldMatch = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->must = $must; + $this->mustNot = $mustNot; + $this->should = $should; + $this->filter = $filter; + $this->minimumShouldMatch = $minimumShouldMatch; + $this->score = $score; + } +} diff --git a/src/Builder/Search/EmbeddedDocumentOperator.php b/src/Builder/Search/EmbeddedDocumentOperator.php new file mode 100644 index 000000000..91f6c1f96 --- /dev/null +++ b/src/Builder/Search/EmbeddedDocumentOperator.php @@ -0,0 +1,57 @@ + 'path', 'operator' => 'operator', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var Document|SearchOperatorInterface|Serializable|array|stdClass $operator */ + public readonly Document|Serializable|SearchOperatorInterface|stdClass|array $operator; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->operator = $operator; + $this->score = $score; + } +} diff --git a/src/Builder/Search/EqualsOperator.php b/src/Builder/Search/EqualsOperator.php new file mode 100644 index 000000000..092a9ca48 --- /dev/null +++ b/src/Builder/Search/EqualsOperator.php @@ -0,0 +1,60 @@ + 'path', 'value' => 'value', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var Binary|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|bool|float|int|null|string $value */ + public readonly DateTimeInterface|Binary|Decimal128|Int64|ObjectId|UTCDateTime|bool|float|int|null|string $value; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param Binary|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|bool|float|int|null|string $value + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + DateTimeInterface|Binary|Decimal128|Int64|ObjectId|UTCDateTime|bool|float|int|null|string $value, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->value = $value; + $this->score = $score; + } +} diff --git a/src/Builder/Search/ExistsOperator.php b/src/Builder/Search/ExistsOperator.php new file mode 100644 index 000000000..4330f130f --- /dev/null +++ b/src/Builder/Search/ExistsOperator.php @@ -0,0 +1,48 @@ + 'path', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->score = $score; + } +} diff --git a/src/Builder/Search/FacetOperator.php b/src/Builder/Search/FacetOperator.php new file mode 100644 index 000000000..15fc10830 --- /dev/null +++ b/src/Builder/Search/FacetOperator.php @@ -0,0 +1,49 @@ + 'facets', 'operator' => 'operator']; + + /** @var Document|Serializable|array|stdClass $facets */ + public readonly Document|Serializable|stdClass|array $facets; + + /** @var Optional|Document|SearchOperatorInterface|Serializable|array|stdClass $operator */ + public readonly Optional|Document|Serializable|SearchOperatorInterface|stdClass|array $operator; + + /** + * @param Document|Serializable|array|stdClass $facets + * @param Optional|Document|SearchOperatorInterface|Serializable|array|stdClass $operator + */ + public function __construct( + Document|Serializable|stdClass|array $facets, + Optional|Document|Serializable|SearchOperatorInterface|stdClass|array $operator = Optional::Undefined, + ) { + $this->facets = $facets; + $this->operator = $operator; + } +} diff --git a/src/Builder/Search/FactoryTrait.php b/src/Builder/Search/FactoryTrait.php new file mode 100644 index 000000000..c5f3b009c --- /dev/null +++ b/src/Builder/Search/FactoryTrait.php @@ -0,0 +1,346 @@ + 'path', 'relation' => 'relation', 'geometry' => 'geometry', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var string $relation */ + public readonly string $relation; + + /** @var Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param string $relation + * @param Document|GeometryInterface|Serializable|array|stdClass $geometry + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + string $relation, + Document|Serializable|GeometryInterface|stdClass|array $geometry, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->relation = $relation; + $this->geometry = $geometry; + $this->score = $score; + } +} diff --git a/src/Builder/Search/GeoWithinOperator.php b/src/Builder/Search/GeoWithinOperator.php new file mode 100644 index 000000000..dcf605697 --- /dev/null +++ b/src/Builder/Search/GeoWithinOperator.php @@ -0,0 +1,76 @@ + 'path', + 'box' => 'box', + 'circle' => 'circle', + 'geometry' => 'geometry', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var Optional|Document|Serializable|array|stdClass $box */ + public readonly Optional|Document|Serializable|stdClass|array $box; + + /** @var Optional|Document|Serializable|array|stdClass $circle */ + public readonly Optional|Document|Serializable|stdClass|array $circle; + + /** @var Optional|Document|GeometryInterface|Serializable|array|stdClass $geometry */ + public readonly Optional|Document|Serializable|GeometryInterface|stdClass|array $geometry; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param Optional|Document|Serializable|array|stdClass $box + * @param Optional|Document|Serializable|array|stdClass $circle + * @param Optional|Document|GeometryInterface|Serializable|array|stdClass $geometry + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + Optional|Document|Serializable|stdClass|array $box = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $circle = Optional::Undefined, + Optional|Document|Serializable|GeometryInterface|stdClass|array $geometry = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->box = $box; + $this->circle = $circle; + $this->geometry = $geometry; + $this->score = $score; + } +} diff --git a/src/Builder/Search/InOperator.php b/src/Builder/Search/InOperator.php new file mode 100644 index 000000000..b9aaf5875 --- /dev/null +++ b/src/Builder/Search/InOperator.php @@ -0,0 +1,66 @@ + 'path', 'value' => 'value', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var BSONArray|DateTimeInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $value */ + public readonly DateTimeInterface|PackedArray|Type|BSONArray|stdClass|array|bool|float|int|null|string $value; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param BSONArray|DateTimeInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $value + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + DateTimeInterface|PackedArray|Type|BSONArray|stdClass|array|bool|float|int|null|string $value, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + if (is_array($value) && ! array_is_list($value)) { + throw new InvalidArgumentException('Expected $value argument to be a list, got an associative array.'); + } + + $this->value = $value; + $this->score = $score; + } +} diff --git a/src/Builder/Search/MoreLikeThisOperator.php b/src/Builder/Search/MoreLikeThisOperator.php new file mode 100644 index 000000000..689508d53 --- /dev/null +++ b/src/Builder/Search/MoreLikeThisOperator.php @@ -0,0 +1,52 @@ + 'like', 'score' => 'score']; + + /** @var BSONArray|Document|PackedArray|Serializable|array|stdClass $like */ + public readonly Document|PackedArray|Serializable|BSONArray|stdClass|array $like; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param BSONArray|Document|PackedArray|Serializable|array|stdClass $like + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + Document|PackedArray|Serializable|BSONArray|stdClass|array $like, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->like = $like; + $this->score = $score; + } +} diff --git a/src/Builder/Search/NearOperator.php b/src/Builder/Search/NearOperator.php new file mode 100644 index 000000000..b22013ead --- /dev/null +++ b/src/Builder/Search/NearOperator.php @@ -0,0 +1,65 @@ + 'path', 'origin' => 'origin', 'pivot' => 'pivot', 'score' => 'score']; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var DateTimeInterface|Decimal128|Document|GeometryInterface|Int64|Serializable|UTCDateTime|array|float|int|stdClass $origin */ + public readonly DateTimeInterface|Decimal128|Document|Int64|Serializable|UTCDateTime|GeometryInterface|stdClass|array|float|int $origin; + + /** @var Decimal128|Int64|float|int $pivot */ + public readonly Decimal128|Int64|float|int $pivot; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param DateTimeInterface|Decimal128|Document|GeometryInterface|Int64|Serializable|UTCDateTime|array|float|int|stdClass $origin + * @param Decimal128|Int64|float|int $pivot + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + DateTimeInterface|Decimal128|Document|Int64|Serializable|UTCDateTime|GeometryInterface|stdClass|array|float|int $origin, + Decimal128|Int64|float|int $pivot, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->origin = $origin; + $this->pivot = $pivot; + $this->score = $score; + } +} diff --git a/src/Builder/Search/PhraseOperator.php b/src/Builder/Search/PhraseOperator.php new file mode 100644 index 000000000..2bbb707a9 --- /dev/null +++ b/src/Builder/Search/PhraseOperator.php @@ -0,0 +1,83 @@ + 'path', + 'query' => 'query', + 'slop' => 'slop', + 'synonyms' => 'synonyms', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var BSONArray|PackedArray|array|string $query */ + public readonly PackedArray|BSONArray|array|string $query; + + /** @var Optional|int $slop */ + public readonly Optional|int $slop; + + /** @var Optional|string $synonyms */ + public readonly Optional|string $synonyms; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param BSONArray|PackedArray|array|string $query + * @param Optional|int $slop + * @param Optional|string $synonyms + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + PackedArray|BSONArray|array|string $query, + Optional|int $slop = Optional::Undefined, + Optional|string $synonyms = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + if (is_array($query) && ! array_is_list($query)) { + throw new InvalidArgumentException('Expected $query argument to be a list, got an associative array.'); + } + + $this->query = $query; + $this->slop = $slop; + $this->synonyms = $synonyms; + $this->score = $score; + } +} diff --git a/src/Builder/Search/QueryStringOperator.php b/src/Builder/Search/QueryStringOperator.php new file mode 100644 index 000000000..edc4d3471 --- /dev/null +++ b/src/Builder/Search/QueryStringOperator.php @@ -0,0 +1,40 @@ + 'defaultPath', 'query' => 'query']; + + /** @var array|string $defaultPath */ + public readonly array|string $defaultPath; + + /** @var string $query */ + public readonly string $query; + + /** + * @param array|string $defaultPath + * @param string $query + */ + public function __construct(array|string $defaultPath, string $query) + { + $this->defaultPath = $defaultPath; + $this->query = $query; + } +} diff --git a/src/Builder/Search/RangeOperator.php b/src/Builder/Search/RangeOperator.php new file mode 100644 index 000000000..e277f2b06 --- /dev/null +++ b/src/Builder/Search/RangeOperator.php @@ -0,0 +1,86 @@ + 'path', + 'gt' => 'gt', + 'gte' => 'gte', + 'lt' => 'lt', + 'lte' => 'lte', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gt */ + public readonly Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gt; + + /** @var Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gte */ + public readonly Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gte; + + /** @var Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lt */ + public readonly Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lt; + + /** @var Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lte */ + public readonly Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lte; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gt + * @param Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gte + * @param Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lt + * @param Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lte + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gt = Optional::Undefined, + Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $gte = Optional::Undefined, + Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lt = Optional::Undefined, + Optional|DateTimeInterface|Decimal128|Int64|ObjectId|UTCDateTime|float|int|string $lte = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->gt = $gt; + $this->gte = $gte; + $this->lt = $lt; + $this->lte = $lte; + $this->score = $score; + } +} diff --git a/src/Builder/Search/RegexOperator.php b/src/Builder/Search/RegexOperator.php new file mode 100644 index 000000000..6b2c2d63a --- /dev/null +++ b/src/Builder/Search/RegexOperator.php @@ -0,0 +1,67 @@ + 'path', + 'query' => 'query', + 'allowAnalyzedField' => 'allowAnalyzedField', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var string $query */ + public readonly string $query; + + /** @var Optional|bool $allowAnalyzedField */ + public readonly Optional|bool $allowAnalyzedField; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param string $query + * @param Optional|bool $allowAnalyzedField + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + string $query, + Optional|bool $allowAnalyzedField = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->query = $query; + $this->allowAnalyzedField = $allowAnalyzedField; + $this->score = $score; + } +} diff --git a/src/Builder/Search/TextOperator.php b/src/Builder/Search/TextOperator.php new file mode 100644 index 000000000..4ade6b434 --- /dev/null +++ b/src/Builder/Search/TextOperator.php @@ -0,0 +1,81 @@ + 'path', + 'query' => 'query', + 'fuzzy' => 'fuzzy', + 'matchCriteria' => 'matchCriteria', + 'synonyms' => 'synonyms', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var string $query */ + public readonly string $query; + + /** @var Optional|Document|Serializable|array|stdClass $fuzzy */ + public readonly Optional|Document|Serializable|stdClass|array $fuzzy; + + /** @var Optional|string $matchCriteria */ + public readonly Optional|string $matchCriteria; + + /** @var Optional|string $synonyms */ + public readonly Optional|string $synonyms; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param string $query + * @param Optional|Document|Serializable|array|stdClass $fuzzy + * @param Optional|string $matchCriteria + * @param Optional|string $synonyms + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + string $query, + Optional|Document|Serializable|stdClass|array $fuzzy = Optional::Undefined, + Optional|string $matchCriteria = Optional::Undefined, + Optional|string $synonyms = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->query = $query; + $this->fuzzy = $fuzzy; + $this->matchCriteria = $matchCriteria; + $this->synonyms = $synonyms; + $this->score = $score; + } +} diff --git a/src/Builder/Search/WildcardOperator.php b/src/Builder/Search/WildcardOperator.php new file mode 100644 index 000000000..ee481910e --- /dev/null +++ b/src/Builder/Search/WildcardOperator.php @@ -0,0 +1,66 @@ + 'path', + 'query' => 'query', + 'allowAnalyzedField' => 'allowAnalyzedField', + 'score' => 'score', + ]; + + /** @var array|string $path */ + public readonly array|string $path; + + /** @var string $query */ + public readonly string $query; + + /** @var Optional|bool $allowAnalyzedField */ + public readonly Optional|bool $allowAnalyzedField; + + /** @var Optional|Document|Serializable|array|stdClass $score */ + public readonly Optional|Document|Serializable|stdClass|array $score; + + /** + * @param array|string $path + * @param string $query + * @param Optional|bool $allowAnalyzedField + * @param Optional|Document|Serializable|array|stdClass $score + */ + public function __construct( + array|string $path, + string $query, + Optional|bool $allowAnalyzedField = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $score = Optional::Undefined, + ) { + $this->path = $path; + $this->query = $query; + $this->allowAnalyzedField = $allowAnalyzedField; + $this->score = $score; + } +} diff --git a/src/Builder/Stage.php b/src/Builder/Stage.php new file mode 100644 index 000000000..2dbbfd972 --- /dev/null +++ b/src/Builder/Stage.php @@ -0,0 +1,37 @@ +|bool|float|int|string|null ...$queries The query predicates to match + */ + public static function match(DateTimeInterface|QueryInterface|FieldQueryInterface|Type|stdClass|array|bool|float|int|string|null ...$queries): MatchStage + { + // Override the generated method to allow variadic arguments + return self::generatedMatch($queries); + } + + private function __construct() + { + // This class cannot be instantiated + } +} diff --git a/src/Builder/Stage/AddFieldsStage.php b/src/Builder/Stage/AddFieldsStage.php new file mode 100644 index 000000000..f6385a391 --- /dev/null +++ b/src/Builder/Stage/AddFieldsStage.php @@ -0,0 +1,56 @@ + 'expression']; + + /** @var stdClass $expression Specify the name of each field to add and set its value to an aggregation expression or an empty object. */ + public readonly stdClass $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression Specify the name of each field to add and set its value to an aggregation expression or an empty object. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$expression, + ) { + if (\count($expression) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $expression, got %d.', 1, \count($expression))); + } + + foreach($expression as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $expression arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $expression = (object) $expression; + $this->expression = $expression; + } +} diff --git a/src/Builder/Stage/BucketAutoStage.php b/src/Builder/Stage/BucketAutoStage.php new file mode 100644 index 000000000..21975208c --- /dev/null +++ b/src/Builder/Stage/BucketAutoStage.php @@ -0,0 +1,77 @@ + 'groupBy', + 'buckets' => 'buckets', + 'output' => 'output', + 'granularity' => 'granularity', + ]; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $groupBy; + + /** @var int $buckets A positive 32-bit integer that specifies the number of buckets into which input documents are grouped. */ + public readonly int $buckets; + + /** + * @var Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * The default count field is not included in the output document when output is specified. Explicitly specify the count expression as part of the output document to include it. + */ + public readonly Optional|Document|Serializable|stdClass|array $output; + + /** + * @var Optional|string $granularity A string that specifies the preferred number series to use to ensure that the calculated boundary edges end on preferred round numbers or their powers of 10. + * Available only if the all groupBy values are numeric and none of them are NaN. + */ + public readonly Optional|string $granularity; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + * @param int $buckets A positive 32-bit integer that specifies the number of buckets into which input documents are grouped. + * @param Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * The default count field is not included in the output document when output is specified. Explicitly specify the count expression as part of the output document to include it. + * @param Optional|string $granularity A string that specifies the preferred number series to use to ensure that the calculated boundary edges end on preferred round numbers or their powers of 10. + * Available only if the all groupBy values are numeric and none of them are NaN. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $groupBy, + int $buckets, + Optional|Document|Serializable|stdClass|array $output = Optional::Undefined, + Optional|string $granularity = Optional::Undefined, + ) { + $this->groupBy = $groupBy; + $this->buckets = $buckets; + $this->output = $output; + $this->granularity = $granularity; + } +} diff --git a/src/Builder/Stage/BucketStage.php b/src/Builder/Stage/BucketStage.php new file mode 100644 index 000000000..334f73dd7 --- /dev/null +++ b/src/Builder/Stage/BucketStage.php @@ -0,0 +1,101 @@ + 'groupBy', + 'boundaries' => 'boundaries', + 'default' => 'default', + 'output' => 'output', + ]; + + /** + * @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + * Unless $bucket includes a default specification, each input document must resolve the groupBy field path or expression to a value that falls within one of the ranges specified by the boundaries. + */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $groupBy; + + /** + * @var BSONArray|PackedArray|array $boundaries An array of values based on the groupBy expression that specify the boundaries for each bucket. Each adjacent pair of values acts as the inclusive lower boundary and the exclusive upper boundary for the bucket. You must specify at least two boundaries. + * The specified values must be in ascending order and all of the same type. The exception is if the values are of mixed numeric types, such as: + */ + public readonly PackedArray|BSONArray|array $boundaries; + + /** + * @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default A literal that specifies the _id of an additional bucket that contains all documents whose groupBy expression result does not fall into a bucket specified by boundaries. + * If unspecified, each input document must resolve the groupBy expression to a value within one of the bucket ranges specified by boundaries or the operation throws an error. + * The default value must be less than the lowest boundaries value, or greater than or equal to the highest boundaries value. + * The default value can be of a different type than the entries in boundaries. + */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default; + + /** + * @var Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * If you do not specify an output document, the operation returns a count field containing the number of documents in each bucket. + * If you specify an output document, only the fields specified in the document are returned; i.e. the count field is not returned unless it is explicitly included in the output document. + */ + public readonly Optional|Document|Serializable|stdClass|array $output; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + * Unless $bucket includes a default specification, each input document must resolve the groupBy field path or expression to a value that falls within one of the ranges specified by the boundaries. + * @param BSONArray|PackedArray|array $boundaries An array of values based on the groupBy expression that specify the boundaries for each bucket. Each adjacent pair of values acts as the inclusive lower boundary and the exclusive upper boundary for the bucket. You must specify at least two boundaries. + * The specified values must be in ascending order and all of the same type. The exception is if the values are of mixed numeric types, such as: + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default A literal that specifies the _id of an additional bucket that contains all documents whose groupBy expression result does not fall into a bucket specified by boundaries. + * If unspecified, each input document must resolve the groupBy expression to a value within one of the bucket ranges specified by boundaries or the operation throws an error. + * The default value must be less than the lowest boundaries value, or greater than or equal to the highest boundaries value. + * The default value can be of a different type than the entries in boundaries. + * @param Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * If you do not specify an output document, the operation returns a count field containing the number of documents in each bucket. + * If you specify an output document, only the fields specified in the document are returned; i.e. the count field is not returned unless it is explicitly included in the output document. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $groupBy, + PackedArray|BSONArray|array $boundaries, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $default = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $output = Optional::Undefined, + ) { + $this->groupBy = $groupBy; + if (is_array($boundaries) && ! array_is_list($boundaries)) { + throw new InvalidArgumentException('Expected $boundaries argument to be a list, got an associative array.'); + } + + $this->boundaries = $boundaries; + $this->default = $default; + $this->output = $output; + } +} diff --git a/src/Builder/Stage/ChangeStreamSplitLargeEventStage.php b/src/Builder/Stage/ChangeStreamSplitLargeEventStage.php new file mode 100644 index 000000000..2539ddb19 --- /dev/null +++ b/src/Builder/Stage/ChangeStreamSplitLargeEventStage.php @@ -0,0 +1,30 @@ + 'allChangesForCluster', + 'fullDocument' => 'fullDocument', + 'fullDocumentBeforeChange' => 'fullDocumentBeforeChange', + 'resumeAfter' => 'resumeAfter', + 'showExpandedEvents' => 'showExpandedEvents', + 'startAfter' => 'startAfter', + 'startAtOperationTime' => 'startAtOperationTime', + ]; + + /** @var Optional|bool $allChangesForCluster A flag indicating whether the stream should report all changes that occur on the deployment, aside from those on internal databases or collections. */ + public readonly Optional|bool $allChangesForCluster; + + /** @var Optional|string $fullDocument Specifies whether change notifications include a copy of the full document when modified by update operations. */ + public readonly Optional|string $fullDocument; + + /** @var Optional|string $fullDocumentBeforeChange Valid values are "off", "whenAvailable", or "required". If set to "off", the "fullDocumentBeforeChange" field of the output document is always omitted. If set to "whenAvailable", the "fullDocumentBeforeChange" field will be populated with the pre-image of the document modified by the current change event if such a pre-image is available, and will be omitted otherwise. If set to "required", then the "fullDocumentBeforeChange" field is always populated and an exception is thrown if the pre-image is not available. */ + public readonly Optional|string $fullDocumentBeforeChange; + + /** @var Optional|int $resumeAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with startAfter or startAtOperationTime fields. */ + public readonly Optional|int $resumeAfter; + + /** + * @var Optional|bool $showExpandedEvents Specifies whether to include additional change events, such as such as DDL and index operations. + * New in MongoDB 6.0. + */ + public readonly Optional|bool $showExpandedEvents; + + /** @var Optional|Document|Serializable|array|stdClass $startAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with resumeAfter or startAtOperationTime fields. */ + public readonly Optional|Document|Serializable|stdClass|array $startAfter; + + /** @var Optional|Timestamp|int $startAtOperationTime Specifies a time as the logical starting point for the change stream. Cannot be used with resumeAfter or startAfter fields. */ + public readonly Optional|Timestamp|int $startAtOperationTime; + + /** + * @param Optional|bool $allChangesForCluster A flag indicating whether the stream should report all changes that occur on the deployment, aside from those on internal databases or collections. + * @param Optional|string $fullDocument Specifies whether change notifications include a copy of the full document when modified by update operations. + * @param Optional|string $fullDocumentBeforeChange Valid values are "off", "whenAvailable", or "required". If set to "off", the "fullDocumentBeforeChange" field of the output document is always omitted. If set to "whenAvailable", the "fullDocumentBeforeChange" field will be populated with the pre-image of the document modified by the current change event if such a pre-image is available, and will be omitted otherwise. If set to "required", then the "fullDocumentBeforeChange" field is always populated and an exception is thrown if the pre-image is not available. + * @param Optional|int $resumeAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with startAfter or startAtOperationTime fields. + * @param Optional|bool $showExpandedEvents Specifies whether to include additional change events, such as such as DDL and index operations. + * New in MongoDB 6.0. + * @param Optional|Document|Serializable|array|stdClass $startAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with resumeAfter or startAtOperationTime fields. + * @param Optional|Timestamp|int $startAtOperationTime Specifies a time as the logical starting point for the change stream. Cannot be used with resumeAfter or startAfter fields. + */ + public function __construct( + Optional|bool $allChangesForCluster = Optional::Undefined, + Optional|string $fullDocument = Optional::Undefined, + Optional|string $fullDocumentBeforeChange = Optional::Undefined, + Optional|int $resumeAfter = Optional::Undefined, + Optional|bool $showExpandedEvents = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $startAfter = Optional::Undefined, + Optional|Timestamp|int $startAtOperationTime = Optional::Undefined, + ) { + $this->allChangesForCluster = $allChangesForCluster; + $this->fullDocument = $fullDocument; + $this->fullDocumentBeforeChange = $fullDocumentBeforeChange; + $this->resumeAfter = $resumeAfter; + $this->showExpandedEvents = $showExpandedEvents; + $this->startAfter = $startAfter; + $this->startAtOperationTime = $startAtOperationTime; + } +} diff --git a/src/Builder/Stage/CollStatsStage.php b/src/Builder/Stage/CollStatsStage.php new file mode 100644 index 000000000..d84245320 --- /dev/null +++ b/src/Builder/Stage/CollStatsStage.php @@ -0,0 +1,66 @@ + 'latencyStats', + 'storageStats' => 'storageStats', + 'count' => 'count', + 'queryExecStats' => 'queryExecStats', + ]; + + /** @var Optional|Document|Serializable|array|stdClass $latencyStats */ + public readonly Optional|Document|Serializable|stdClass|array $latencyStats; + + /** @var Optional|Document|Serializable|array|stdClass $storageStats */ + public readonly Optional|Document|Serializable|stdClass|array $storageStats; + + /** @var Optional|Document|Serializable|array|stdClass $count */ + public readonly Optional|Document|Serializable|stdClass|array $count; + + /** @var Optional|Document|Serializable|array|stdClass $queryExecStats */ + public readonly Optional|Document|Serializable|stdClass|array $queryExecStats; + + /** + * @param Optional|Document|Serializable|array|stdClass $latencyStats + * @param Optional|Document|Serializable|array|stdClass $storageStats + * @param Optional|Document|Serializable|array|stdClass $count + * @param Optional|Document|Serializable|array|stdClass $queryExecStats + */ + public function __construct( + Optional|Document|Serializable|stdClass|array $latencyStats = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $storageStats = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $queryExecStats = Optional::Undefined, + ) { + $this->latencyStats = $latencyStats; + $this->storageStats = $storageStats; + $this->count = $count; + $this->queryExecStats = $queryExecStats; + } +} diff --git a/src/Builder/Stage/CountStage.php b/src/Builder/Stage/CountStage.php new file mode 100644 index 000000000..387da5efc --- /dev/null +++ b/src/Builder/Stage/CountStage.php @@ -0,0 +1,38 @@ + 'field']; + + /** @var string $field Name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character. */ + public readonly string $field; + + /** + * @param string $field Name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character. + */ + public function __construct(string $field) + { + $this->field = $field; + } +} diff --git a/src/Builder/Stage/CurrentOpStage.php b/src/Builder/Stage/CurrentOpStage.php new file mode 100644 index 000000000..3d0d6abb2 --- /dev/null +++ b/src/Builder/Stage/CurrentOpStage.php @@ -0,0 +1,70 @@ + 'allUsers', + 'idleConnections' => 'idleConnections', + 'idleCursors' => 'idleCursors', + 'idleSessions' => 'idleSessions', + 'localOps' => 'localOps', + ]; + + /** @var Optional|bool $allUsers */ + public readonly Optional|bool $allUsers; + + /** @var Optional|bool $idleConnections */ + public readonly Optional|bool $idleConnections; + + /** @var Optional|bool $idleCursors */ + public readonly Optional|bool $idleCursors; + + /** @var Optional|bool $idleSessions */ + public readonly Optional|bool $idleSessions; + + /** @var Optional|bool $localOps */ + public readonly Optional|bool $localOps; + + /** + * @param Optional|bool $allUsers + * @param Optional|bool $idleConnections + * @param Optional|bool $idleCursors + * @param Optional|bool $idleSessions + * @param Optional|bool $localOps + */ + public function __construct( + Optional|bool $allUsers = Optional::Undefined, + Optional|bool $idleConnections = Optional::Undefined, + Optional|bool $idleCursors = Optional::Undefined, + Optional|bool $idleSessions = Optional::Undefined, + Optional|bool $localOps = Optional::Undefined, + ) { + $this->allUsers = $allUsers; + $this->idleConnections = $idleConnections; + $this->idleCursors = $idleCursors; + $this->idleSessions = $idleSessions; + $this->localOps = $localOps; + } +} diff --git a/src/Builder/Stage/DensifyStage.php b/src/Builder/Stage/DensifyStage.php new file mode 100644 index 000000000..9ecff7bbc --- /dev/null +++ b/src/Builder/Stage/DensifyStage.php @@ -0,0 +1,70 @@ + 'field', 'range' => 'range', 'partitionByFields' => 'partitionByFields']; + + /** + * @var string $field The field to densify. The values of the specified field must either be all numeric values or all dates. + * Documents that do not contain the specified field continue through the pipeline unmodified. + * To specify a in an embedded document or in an array, use dot notation. + */ + public readonly string $field; + + /** @var Document|Serializable|array|stdClass $range Specification for range based densification. */ + public readonly Document|Serializable|stdClass|array $range; + + /** @var Optional|BSONArray|PackedArray|array $partitionByFields The field(s) that will be used as the partition keys. */ + public readonly Optional|PackedArray|BSONArray|array $partitionByFields; + + /** + * @param string $field The field to densify. The values of the specified field must either be all numeric values or all dates. + * Documents that do not contain the specified field continue through the pipeline unmodified. + * To specify a in an embedded document or in an array, use dot notation. + * @param Document|Serializable|array|stdClass $range Specification for range based densification. + * @param Optional|BSONArray|PackedArray|array $partitionByFields The field(s) that will be used as the partition keys. + */ + public function __construct( + string $field, + Document|Serializable|stdClass|array $range, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + ) { + $this->field = $field; + $this->range = $range; + if (is_array($partitionByFields) && ! array_is_list($partitionByFields)) { + throw new InvalidArgumentException('Expected $partitionByFields argument to be a list, got an associative array.'); + } + + $this->partitionByFields = $partitionByFields; + } +} diff --git a/src/Builder/Stage/DocumentsStage.php b/src/Builder/Stage/DocumentsStage.php new file mode 100644 index 000000000..c020a9cb0 --- /dev/null +++ b/src/Builder/Stage/DocumentsStage.php @@ -0,0 +1,64 @@ + 'documents']; + + /** + * @var BSONArray|PackedArray|ResolvesToArray|array|string $documents $documents accepts any valid expression that resolves to an array of objects. This includes: + * - system variables, such as $$NOW or $$SEARCH_META + * - $let expressions + * - variables in scope from $lookup expressions + * Expressions that do not resolve to a current document, like $myField or $$ROOT, will result in an error. + */ + public readonly PackedArray|ResolvesToArray|BSONArray|array|string $documents; + + /** + * @param BSONArray|PackedArray|ResolvesToArray|array|string $documents $documents accepts any valid expression that resolves to an array of objects. This includes: + * - system variables, such as $$NOW or $$SEARCH_META + * - $let expressions + * - variables in scope from $lookup expressions + * Expressions that do not resolve to a current document, like $myField or $$ROOT, will result in an error. + */ + public function __construct(PackedArray|ResolvesToArray|BSONArray|array|string $documents) + { + if (is_string($documents) && ! str_starts_with($documents, '$')) { + throw new InvalidArgumentException('Argument $documents can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + if (is_array($documents) && ! array_is_list($documents)) { + throw new InvalidArgumentException('Expected $documents argument to be a list, got an associative array.'); + } + + $this->documents = $documents; + } +} diff --git a/src/Builder/Stage/FacetStage.php b/src/Builder/Stage/FacetStage.php new file mode 100644 index 000000000..6dc36bebd --- /dev/null +++ b/src/Builder/Stage/FacetStage.php @@ -0,0 +1,55 @@ + 'facet']; + + /** @var stdClass $facet */ + public readonly stdClass $facet; + + /** + * @param BSONArray|PackedArray|Pipeline|array ...$facet + */ + public function __construct(PackedArray|Pipeline|BSONArray|array ...$facet) + { + if (\count($facet) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $facet, got %d.', 1, \count($facet))); + } + + foreach($facet as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $facet arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $facet = (object) $facet; + $this->facet = $facet; + } +} diff --git a/src/Builder/Stage/FactoryTrait.php b/src/Builder/Stage/FactoryTrait.php new file mode 100644 index 000000000..df5860ded --- /dev/null +++ b/src/Builder/Stage/FactoryTrait.php @@ -0,0 +1,739 @@ + in an embedded document or in an array, use dot notation. + * @param Document|Serializable|array|stdClass $range Specification for range based densification. + * @param Optional|BSONArray|PackedArray|array $partitionByFields The field(s) that will be used as the partition keys. + */ + public static function densify( + string $field, + Document|Serializable|stdClass|array $range, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + ): DensifyStage { + return new DensifyStage($field, $range, $partitionByFields); + } + + /** + * Returns literal documents from input values. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $documents $documents accepts any valid expression that resolves to an array of objects. This includes: + * - system variables, such as $$NOW or $$SEARCH_META + * - $let expressions + * - variables in scope from $lookup expressions + * Expressions that do not resolve to a current document, like $myField or $$ROOT, will result in an error. + */ + public static function documents(PackedArray|ResolvesToArray|BSONArray|array|string $documents): DocumentsStage + { + return new DocumentsStage($documents); + } + + /** + * Processes multiple aggregation pipelines within a single stage on the same set of input documents. Enables the creation of multi-faceted aggregations capable of characterizing data across multiple dimensions, or facets, in a single stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/ + * @param BSONArray|PackedArray|Pipeline|array ...$facet + */ + public static function facet(PackedArray|Pipeline|BSONArray|array ...$facet): FacetStage + { + return new FacetStage(...$facet); + } + + /** + * Populates null and missing field values within documents. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/ + * @param Document|Serializable|array|stdClass $output Specifies an object containing each field for which to fill missing values. You can specify multiple fields in the output object. + * The object name is the name of the field to fill. The object value specifies how the field is filled. + * @param Optional|Document|Serializable|array|stdClass|string $partitionBy Specifies an expression to group the documents. In the $fill stage, a group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|BSONArray|PackedArray|array $partitionByFields Specifies an array of fields as the compound key to group the documents. In the $fill stage, each group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|Document|Serializable|array|stdClass $sortBy Specifies the field or fields to sort the documents within each partition. Uses the same syntax as the $sort stage. + */ + public static function fill( + Document|Serializable|stdClass|array $output, + Optional|Document|Serializable|stdClass|array|string $partitionBy = Optional::Undefined, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sortBy = Optional::Undefined, + ): FillStage { + return new FillStage($output, $partitionBy, $partitionByFields, $sortBy); + } + + /** + * Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $near The point for which to find the closest documents. + * @param Optional|string $distanceField The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. + * @param Optional|Decimal128|Int64|float|int $distanceMultiplier The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as returned by a spherical query, to kilometers by multiplying by the radius of the Earth. + * @param Optional|string $includeLocs This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location field contains multiple locations. To specify a field within an embedded document, use dot notation. + * @param Optional|string $key Specify the geospatial indexed field to use when calculating the distance. + * @param Optional|Decimal128|Int64|float|int $maxDistance The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point. + * Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs. + * @param Optional|Decimal128|Int64|float|int $minDistance The minimum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall outside the specified distance from the center point. + * Specify the distance in meters for GeoJSON data and in radians for legacy coordinate pairs. + * @param Optional|QueryInterface|array $query Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax. + * You cannot specify a $near predicate in the query field of the $geoNear stage. + * @param Optional|bool $spherical Determines how MongoDB calculates the distance between two points: + * - When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry. + * - When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes. + * Default: false. + */ + public static function geoNear( + Document|Serializable|ResolvesToObject|stdClass|array|string $near, + Optional|string $distanceField = Optional::Undefined, + Optional|Decimal128|Int64|float|int $distanceMultiplier = Optional::Undefined, + Optional|string $includeLocs = Optional::Undefined, + Optional|string $key = Optional::Undefined, + Optional|Decimal128|Int64|float|int $maxDistance = Optional::Undefined, + Optional|Decimal128|Int64|float|int $minDistance = Optional::Undefined, + Optional|QueryInterface|array $query = Optional::Undefined, + Optional|bool $spherical = Optional::Undefined, + ): GeoNearStage { + return new GeoNearStage($near, $distanceField, $distanceMultiplier, $includeLocs, $key, $maxDistance, $minDistance, $query, $spherical); + } + + /** + * Performs a recursive search on a collection. To each output document, adds a new array field that contains the traversal results of the recursive search for that document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/ + * @param string $from Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param BSONArray|DateTimeInterface|ExpressionInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $startWith Expression that specifies the value of the connectFromField with which to start the recursive search. Optionally, startWith may be array of values, each of which is individually followed through the traversal process. + * @param string $connectFromField Field name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. If the value is an array, each element is individually followed through the traversal process. + * @param string $connectToField Field name in other documents against which to match the value of the field specified by the connectFromField parameter. + * @param string $as Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document. + * @param Optional|int $maxDepth Non-negative integral number specifying the maximum recursion depth. + * @param Optional|string $depthField Name of the field to add to each traversed document in the search path. The value of this field is the recursion depth for the document, represented as a NumberLong. Recursion depth value starts at zero, so the first lookup corresponds to zero depth. + * @param Optional|QueryInterface|array $restrictSearchWithMatch A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax. + */ + public static function graphLookup( + string $from, + DateTimeInterface|PackedArray|Type|ExpressionInterface|BSONArray|stdClass|array|bool|float|int|null|string $startWith, + string $connectFromField, + string $connectToField, + string $as, + Optional|int $maxDepth = Optional::Undefined, + Optional|string $depthField = Optional::Undefined, + Optional|QueryInterface|array $restrictSearchWithMatch = Optional::Undefined, + ): GraphLookupStage { + return new GraphLookupStage($from, $startWith, $connectFromField, $connectToField, $as, $maxDepth, $depthField, $restrictSearchWithMatch); + } + + /** + * Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $_id The _id expression specifies the group key. If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents. + * @param AccumulatorInterface|Document|Serializable|array|stdClass ...$field Computed using the accumulator operators. + */ + public static function group( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $_id, + Document|Serializable|AccumulatorInterface|stdClass|array ...$field, + ): GroupStage { + return new GroupStage($_id, ...$field); + } + + /** + * Returns statistics regarding the use of each index for the collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexStats/ + */ + public static function indexStats(): IndexStatsStage + { + return new IndexStatsStage(); + } + + /** + * Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/ + * @param int $limit + */ + public static function limit(int $limit): LimitStage + { + return new LimitStage($limit); + } + + /** + * Lists all active sessions recently in use on the currently connected mongos or mongod instance. These sessions may have not yet propagated to the system.sessions collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/ + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public static function listLocalSessions( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ): ListLocalSessionsStage { + return new ListLocalSessionsStage($users, $allUsers); + } + + /** + * Lists sampled queries for all collections or a specific collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSampledQueries/ + * @param Optional|string $namespace + */ + public static function listSampledQueries( + Optional|string $namespace = Optional::Undefined, + ): ListSampledQueriesStage { + return new ListSampledQueriesStage($namespace); + } + + /** + * Returns information about existing Atlas Search indexes on a specified collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/ + * @param Optional|string $id The id of the index to return information about. + * @param Optional|string $name The name of the index to return information about. + */ + public static function listSearchIndexes( + Optional|string $id = Optional::Undefined, + Optional|string $name = Optional::Undefined, + ): ListSearchIndexesStage { + return new ListSearchIndexesStage($id, $name); + } + + /** + * Lists all sessions that have been active long enough to propagate to the system.sessions collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/ + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public static function listSessions( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ): ListSessionsStage { + return new ListSessionsStage($users, $allUsers); + } + + /** + * Performs a left outer join to another collection in the same database to filter in documents from the "joined" collection for processing. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/ + * @param string $as Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten. + * @param Optional|string $from Specifies the collection in the same database to perform the join with. + * from is optional, you can use a $documents stage in a $lookup stage instead. For an example, see Use a $documents Stage in a $lookup Stage. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param Optional|string $localField Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes. + * @param Optional|string $foreignField Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline Specifies the pipeline to run on the joined collection. The pipeline determines the resulting documents from the joined collection. To return all documents, specify an empty pipeline []. + * The pipeline cannot include the $out stage or the $merge stage. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + * The pipeline cannot directly access the joined document fields. Instead, define variables for the joined document fields using the let option and then reference the variables in the pipeline stages. + */ + public static function lookup( + string $as, + Optional|string $from = Optional::Undefined, + Optional|string $localField = Optional::Undefined, + Optional|string $foreignField = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ): LookupStage { + return new LookupStage($as, $from, $localField, $foreignField, $let, $pipeline); + } + + /** + * Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/ + * @param QueryInterface|array $query + */ + public static function match(QueryInterface|array $query): MatchStage + { + return new MatchStage($query); + } + + /** + * Writes the resulting documents of the aggregation pipeline to a collection. The stage can incorporate (insert new documents, merge documents, replace documents, keep existing documents, fail the operation, process documents with a custom update pipeline) the results into an output collection. To use the $merge stage, it must be the last stage in the pipeline. + * New in MongoDB 4.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/ + * @param Document|Serializable|array|stdClass|string $into The output collection. + * @param Optional|BSONArray|PackedArray|array|string $on Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables for use in the whenMatched pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array|string $whenMatched The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s). + * @param Optional|string $whenNotMatched The behavior of $merge if a result document does not match an existing document in the out collection. + */ + public static function merge( + Document|Serializable|stdClass|array|string $into, + Optional|PackedArray|BSONArray|array|string $on = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array|string $whenMatched = Optional::Undefined, + Optional|string $whenNotMatched = Optional::Undefined, + ): MergeStage { + return new MergeStage($into, $on, $let, $whenMatched, $whenNotMatched); + } + + /** + * Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/ + * @param Document|Serializable|array|stdClass|string $coll Target database name to write documents from $out to. + */ + public static function out(Document|Serializable|stdClass|array|string $coll): OutStage + { + return new OutStage($coll); + } + + /** + * Returns plan cache information for a collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/ + */ + public static function planCacheStats(): PlanCacheStatsStage + { + return new PlanCacheStatsStage(); + } + + /** + * Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$specification + */ + public static function project( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$specification, + ): ProjectStage { + return new ProjectStage(...$specification); + } + + /** + * Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero documents. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function redact( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): RedactStage { + return new RedactStage($expression); + } + + /** + * Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $newRoot + */ + public static function replaceRoot( + Document|Serializable|ResolvesToObject|stdClass|array|string $newRoot, + ): ReplaceRootStage { + return new ReplaceRootStage($newRoot); + } + + /** + * Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. + * Alias for $replaceRoot. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $expression + */ + public static function replaceWith( + Document|Serializable|ResolvesToObject|stdClass|array|string $expression, + ): ReplaceWithStage { + return new ReplaceWithStage($expression); + } + + /** + * Randomly selects the specified number of documents from its input. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/ + * @param int $size The number of documents to randomly select. + */ + public static function sample(int $size): SampleStage + { + return new SampleStage($size); + } + + /** + * Performs a full-text search of the field or fields in an Atlas collection. + * NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/search/ + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to "default". + * @param Optional|Document|Serializable|array|stdClass $highlight Specifies the highlighting options for displaying search terms in their original context. + * @param Optional|bool $concurrent Parallelize search across segments on dedicated search nodes. + * If you don't have separate search nodes on your cluster, + * Atlas Search ignores this flag. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + * @param Optional|string $searchAfter Reference point for retrieving results. searchAfter returns documents starting immediately following the specified reference point. + * @param Optional|string $searchBefore Reference point for retrieving results. searchBefore returns documents starting immediately before the specified reference point. + * @param Optional|bool $scoreDetails Flag that specifies whether to retrieve a detailed breakdown of the score for the documents in the results. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $sort Document that specifies the fields to sort the Atlas Search results by in ascending or descending order. + * @param Optional|bool $returnStoredSource Flag that specifies whether to perform a full document lookup on the backend database or return only stored source fields directly from Atlas Search. + * @param Optional|Document|Serializable|array|stdClass $tracking Document that specifies the tracking option to retrieve analytics information on the search terms. + */ + public static function search( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $highlight = Optional::Undefined, + Optional|bool $concurrent = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + Optional|string $searchAfter = Optional::Undefined, + Optional|string $searchBefore = Optional::Undefined, + Optional|bool $scoreDetails = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sort = Optional::Undefined, + Optional|bool $returnStoredSource = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $tracking = Optional::Undefined, + ): SearchStage { + return new SearchStage($operator, $index, $highlight, $concurrent, $count, $searchAfter, $searchBefore, $scoreDetails, $sort, $returnStoredSource, $tracking); + } + + /** + * Returns different types of metadata result documents for the Atlas Search query against an Atlas collection. + * NOTE: $searchMeta is only available for MongoDB Atlas clusters running MongoDB v4.4.9 or higher, and is not available for self-managed deployments. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/searchMeta/ + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to default. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + */ + public static function searchMeta( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + ): SearchMetaStage { + return new SearchMetaStage($operator, $index, $count); + } + + /** + * Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields. + * Alias for $addFields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$field + */ + public static function set( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$field, + ): SetStage { + return new SetStage(...$field); + } + + /** + * Groups documents into windows and applies one or more operators to the documents in each window. + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/ + * @param Document|Serializable|array|stdClass $sortBy Specifies the field(s) to sort the documents by in the partition. Uses the same syntax as the $sort stage. Default is no sorting. + * @param Document|Serializable|array|stdClass $output Specifies the field(s) to append to the documents in the output returned by the $setWindowFields stage. Each field is set to the result returned by the window operator. + * A field can contain dots to specify embedded document fields and array fields. The semantics for the embedded document dotted notation in the $setWindowFields stage are the same as the $addFields and $set stages. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $partitionBy Specifies an expression to group the documents. In the $setWindowFields stage, the group of documents is known as a partition. Default is one partition for the entire collection. + */ + public static function setWindowFields( + Document|Serializable|stdClass|array $sortBy, + Document|Serializable|stdClass|array $output, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $partitionBy = Optional::Undefined, + ): SetWindowFieldsStage { + return new SetWindowFieldsStage($sortBy, $output, $partitionBy); + } + + /** + * Provides data and size distribution information on sharded collections. + * New in MongoDB 6.0.3. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/shardedDataDistribution/ + */ + public static function shardedDataDistribution(): ShardedDataDistributionStage + { + return new ShardedDataDistributionStage(); + } + + /** + * Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/ + * @param int $skip + */ + public static function skip(int $skip): SkipStage + { + return new SkipStage($skip); + } + + /** + * Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/ + * @param DateTimeInterface|ExpressionInterface|Sort|Type|array|bool|float|int|null|stdClass|string ...$sort + */ + public static function sort( + DateTimeInterface|Type|ExpressionInterface|Sort|stdClass|array|bool|float|int|null|string ...$sort, + ): SortStage { + return new SortStage(...$sort); + } + + /** + * Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public static function sortByCount( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ): SortByCountStage { + return new SortByCountStage($expression); + } + + /** + * Performs a union of two collections; i.e. combines pipeline results from two collections into a single result set. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/ + * @param string $coll The collection or view whose pipeline results you wish to include in the result set. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline An aggregation pipeline to apply to the specified coll. + * The pipeline cannot include the $out and $merge stages. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + */ + public static function unionWith( + string $coll, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ): UnionWithStage { + return new UnionWithStage($coll, $pipeline); + } + + /** + * Removes or excludes fields from documents. + * Alias for $project stage that removes or excludes fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/ + * @no-named-arguments + * @param FieldPath|string ...$field + */ + public static function unset(FieldPath|string ...$field): UnsetStage + { + return new UnsetStage(...$field); + } + + /** + * Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/ + * @param ArrayFieldPath|string $path Field path to an array field. + * @param Optional|string $includeArrayIndex The name of a new field to hold the array index of the element. The name cannot start with a dollar sign $. + * @param Optional|bool $preserveNullAndEmptyArrays If true, if the path is null, missing, or an empty array, $unwind outputs the document. + * If false, if path is null, missing, or an empty array, $unwind does not output a document. + * The default value is false. + */ + public static function unwind( + ArrayFieldPath|string $path, + Optional|string $includeArrayIndex = Optional::Undefined, + Optional|bool $preserveNullAndEmptyArrays = Optional::Undefined, + ): UnwindStage { + return new UnwindStage($path, $includeArrayIndex, $preserveNullAndEmptyArrays); + } + + /** + * The $vectorSearch stage performs an ANN or ENN search on a vector in the specified field. + * + * @see https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/ + * @param string $index Name of the Atlas Vector Search index to use. + * @param int $limit Number of documents to return in the results. This value can't exceed the value of numCandidates if you specify numCandidates. + * @param string $path Indexed vector type field to search. + * @param BSONArray|PackedArray|array $queryVector Array of numbers that represent the query vector. The number type must match the indexed field value type. + * @param Optional|bool $exact This is required if numCandidates is omitted. false to run ANN search. true to run ENN search. + * @param Optional|QueryInterface|array $filter Any match query that compares an indexed field with a boolean, date, objectId, number (not decimals), string, or UUID to use as a pre-filter. + * @param Optional|int $numCandidates This field is required if exact is false or omitted. + * Number of nearest neighbors to use during the search. Value must be less than or equal to (<=) 10000. You can't specify a number less than the number of documents to return (limit). + */ + public static function vectorSearch( + string $index, + int $limit, + string $path, + PackedArray|BSONArray|array $queryVector, + Optional|bool $exact = Optional::Undefined, + Optional|QueryInterface|array $filter = Optional::Undefined, + Optional|int $numCandidates = Optional::Undefined, + ): VectorSearchStage { + return new VectorSearchStage($index, $limit, $path, $queryVector, $exact, $filter, $numCandidates); + } +} diff --git a/src/Builder/Stage/FillStage.php b/src/Builder/Stage/FillStage.php new file mode 100644 index 000000000..f7b3b35e2 --- /dev/null +++ b/src/Builder/Stage/FillStage.php @@ -0,0 +1,92 @@ + 'output', + 'partitionBy' => 'partitionBy', + 'partitionByFields' => 'partitionByFields', + 'sortBy' => 'sortBy', + ]; + + /** + * @var Document|Serializable|array|stdClass $output Specifies an object containing each field for which to fill missing values. You can specify multiple fields in the output object. + * The object name is the name of the field to fill. The object value specifies how the field is filled. + */ + public readonly Document|Serializable|stdClass|array $output; + + /** + * @var Optional|Document|Serializable|array|stdClass|string $partitionBy Specifies an expression to group the documents. In the $fill stage, a group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + */ + public readonly Optional|Document|Serializable|stdClass|array|string $partitionBy; + + /** + * @var Optional|BSONArray|PackedArray|array $partitionByFields Specifies an array of fields as the compound key to group the documents. In the $fill stage, each group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + */ + public readonly Optional|PackedArray|BSONArray|array $partitionByFields; + + /** @var Optional|Document|Serializable|array|stdClass $sortBy Specifies the field or fields to sort the documents within each partition. Uses the same syntax as the $sort stage. */ + public readonly Optional|Document|Serializable|stdClass|array $sortBy; + + /** + * @param Document|Serializable|array|stdClass $output Specifies an object containing each field for which to fill missing values. You can specify multiple fields in the output object. + * The object name is the name of the field to fill. The object value specifies how the field is filled. + * @param Optional|Document|Serializable|array|stdClass|string $partitionBy Specifies an expression to group the documents. In the $fill stage, a group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|BSONArray|PackedArray|array $partitionByFields Specifies an array of fields as the compound key to group the documents. In the $fill stage, each group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|Document|Serializable|array|stdClass $sortBy Specifies the field or fields to sort the documents within each partition. Uses the same syntax as the $sort stage. + */ + public function __construct( + Document|Serializable|stdClass|array $output, + Optional|Document|Serializable|stdClass|array|string $partitionBy = Optional::Undefined, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sortBy = Optional::Undefined, + ) { + $this->output = $output; + $this->partitionBy = $partitionBy; + if (is_array($partitionByFields) && ! array_is_list($partitionByFields)) { + throw new InvalidArgumentException('Expected $partitionByFields argument to be a list, got an associative array.'); + } + + $this->partitionByFields = $partitionByFields; + $this->sortBy = $sortBy; + } +} diff --git a/src/Builder/Stage/FluentFactoryTrait.php b/src/Builder/Stage/FluentFactoryTrait.php new file mode 100644 index 000000000..1b9e542ac --- /dev/null +++ b/src/Builder/Stage/FluentFactoryTrait.php @@ -0,0 +1,832 @@ +|stdClass> */ + public array $pipeline = []; + + public function getPipeline(): Pipeline + { + return new Pipeline(...$this->pipeline); + } + + /** + * Filters the document stream to allow only matching documents to pass unmodified into the next pipeline stage. $match uses standard MongoDB queries. For each input document, outputs either one document (a match) or zero documents (no match). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/ + * + * @param DateTimeInterface|QueryInterface|FieldQueryInterface|Type|stdClass|array|bool|float|int|string|null ...$queries The query predicates to match + */ + public function match( + DateTimeInterface|QueryInterface|FieldQueryInterface|Type|stdClass|array|string|int|float|bool|null ...$queries, + ): static { + $this->pipeline[] = Stage::match(...$queries); + + return $this; + } + + /** + * Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/addFields/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$expression Specify the name of each field to add and set its value to an aggregation expression or an empty object. + */ + public function addFields( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null ...$expression, + ): static { + $this->pipeline[] = Stage::addFields(...$expression); + + return $this; + } + + /** + * Categorizes incoming documents into groups, called buckets, based on a specified expression and bucket boundaries. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucket/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + * Unless $bucket includes a default specification, each input document must resolve the groupBy field path or expression to a value that falls within one of the ranges specified by the boundaries. + * @param BSONArray|PackedArray|array $boundaries An array of values based on the groupBy expression that specify the boundaries for each bucket. Each adjacent pair of values acts as the inclusive lower boundary and the exclusive upper boundary for the bucket. You must specify at least two boundaries. + * The specified values must be in ascending order and all of the same type. The exception is if the values are of mixed numeric types, such as: + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $default A literal that specifies the _id of an additional bucket that contains all documents whose groupBy expression result does not fall into a bucket specified by boundaries. + * If unspecified, each input document must resolve the groupBy expression to a value within one of the bucket ranges specified by boundaries or the operation throws an error. + * The default value must be less than the lowest boundaries value, or greater than or equal to the highest boundaries value. + * The default value can be of a different type than the entries in boundaries. + * @param Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * If you do not specify an output document, the operation returns a count field containing the number of documents in each bucket. + * If you specify an output document, only the fields specified in the document are returned; i.e. the count field is not returned unless it is explicitly included in the output document. + */ + public function bucket( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $groupBy, + PackedArray|BSONArray|array $boundaries, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $default = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $output = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::bucket($groupBy, $boundaries, $default, $output); + + return $this; + } + + /** + * Categorizes incoming documents into a specific number of groups, called buckets, based on a specified expression. Bucket boundaries are automatically determined in an attempt to evenly distribute the documents into the specified number of buckets. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/bucketAuto/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $groupBy An expression to group documents by. To specify a field path, prefix the field name with a dollar sign $ and enclose it in quotes. + * @param int $buckets A positive 32-bit integer that specifies the number of buckets into which input documents are grouped. + * @param Optional|Document|Serializable|array|stdClass $output A document that specifies the fields to include in the output documents in addition to the _id field. To specify the field to include, you must use accumulator expressions. + * The default count field is not included in the output document when output is specified. Explicitly specify the count expression as part of the output document to include it. + * @param Optional|string $granularity A string that specifies the preferred number series to use to ensure that the calculated boundary edges end on preferred round numbers or their powers of 10. + * Available only if the all groupBy values are numeric and none of them are NaN. + */ + public function bucketAuto( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $groupBy, + int $buckets, + Optional|Document|Serializable|stdClass|array $output = Optional::Undefined, + Optional|string $granularity = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::bucketAuto($groupBy, $buckets, $output, $granularity); + + return $this; + } + + /** + * Returns a Change Stream cursor for the collection or database. This stage can only occur once in an aggregation pipeline and it must occur as the first stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStream/ + * @param Optional|bool $allChangesForCluster A flag indicating whether the stream should report all changes that occur on the deployment, aside from those on internal databases or collections. + * @param Optional|string $fullDocument Specifies whether change notifications include a copy of the full document when modified by update operations. + * @param Optional|string $fullDocumentBeforeChange Valid values are "off", "whenAvailable", or "required". If set to "off", the "fullDocumentBeforeChange" field of the output document is always omitted. If set to "whenAvailable", the "fullDocumentBeforeChange" field will be populated with the pre-image of the document modified by the current change event if such a pre-image is available, and will be omitted otherwise. If set to "required", then the "fullDocumentBeforeChange" field is always populated and an exception is thrown if the pre-image is not available. + * @param Optional|int $resumeAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with startAfter or startAtOperationTime fields. + * @param Optional|bool $showExpandedEvents Specifies whether to include additional change events, such as such as DDL and index operations. + * New in MongoDB 6.0. + * @param Optional|Document|Serializable|array|stdClass $startAfter Specifies a resume token as the logical starting point for the change stream. Cannot be used with resumeAfter or startAtOperationTime fields. + * @param Optional|Timestamp|int $startAtOperationTime Specifies a time as the logical starting point for the change stream. Cannot be used with resumeAfter or startAfter fields. + */ + public function changeStream( + Optional|bool $allChangesForCluster = Optional::Undefined, + Optional|string $fullDocument = Optional::Undefined, + Optional|string $fullDocumentBeforeChange = Optional::Undefined, + Optional|int $resumeAfter = Optional::Undefined, + Optional|bool $showExpandedEvents = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $startAfter = Optional::Undefined, + Optional|Timestamp|int $startAtOperationTime = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::changeStream($allChangesForCluster, $fullDocument, $fullDocumentBeforeChange, $resumeAfter, $showExpandedEvents, $startAfter, $startAtOperationTime); + + return $this; + } + + /** + * Splits large change stream events that exceed 16 MB into smaller fragments returned in a change stream cursor. + * You can only use $changeStreamSplitLargeEvent in a $changeStream pipeline and it must be the final stage in the pipeline. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/changeStreamSplitLargeEvent/ + */ + public function changeStreamSplitLargeEvent(): static + { + $this->pipeline[] = Stage::changeStreamSplitLargeEvent(); + + return $this; + } + + /** + * Returns statistics regarding a collection or view. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/collStats/ + * @param Optional|Document|Serializable|array|stdClass $latencyStats + * @param Optional|Document|Serializable|array|stdClass $storageStats + * @param Optional|Document|Serializable|array|stdClass $count + * @param Optional|Document|Serializable|array|stdClass $queryExecStats + */ + public function collStats( + Optional|Document|Serializable|stdClass|array $latencyStats = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $storageStats = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $queryExecStats = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::collStats($latencyStats, $storageStats, $count, $queryExecStats); + + return $this; + } + + /** + * Returns a count of the number of documents at this stage of the aggregation pipeline. + * Distinct from the $count aggregation accumulator. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/count/ + * @param string $field Name of the output field which has the count as its value. It must be a non-empty string, must not start with $ and must not contain the . character. + */ + public function count(string $field): static + { + $this->pipeline[] = Stage::count($field); + + return $this; + } + + /** + * Returns information on active and/or dormant operations for the MongoDB deployment. To run, use the db.aggregate() method. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/currentOp/ + * @param Optional|bool $allUsers + * @param Optional|bool $idleConnections + * @param Optional|bool $idleCursors + * @param Optional|bool $idleSessions + * @param Optional|bool $localOps + */ + public function currentOp( + Optional|bool $allUsers = Optional::Undefined, + Optional|bool $idleConnections = Optional::Undefined, + Optional|bool $idleCursors = Optional::Undefined, + Optional|bool $idleSessions = Optional::Undefined, + Optional|bool $localOps = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::currentOp($allUsers, $idleConnections, $idleCursors, $idleSessions, $localOps); + + return $this; + } + + /** + * Creates new documents in a sequence of documents where certain values in a field are missing. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/densify/ + * @param string $field The field to densify. The values of the specified field must either be all numeric values or all dates. + * Documents that do not contain the specified field continue through the pipeline unmodified. + * To specify a in an embedded document or in an array, use dot notation. + * @param Document|Serializable|array|stdClass $range Specification for range based densification. + * @param Optional|BSONArray|PackedArray|array $partitionByFields The field(s) that will be used as the partition keys. + */ + public function densify( + string $field, + Document|Serializable|stdClass|array $range, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::densify($field, $range, $partitionByFields); + + return $this; + } + + /** + * Returns literal documents from input values. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/documents/ + * @param BSONArray|PackedArray|ResolvesToArray|array|string $documents $documents accepts any valid expression that resolves to an array of objects. This includes: + * - system variables, such as $$NOW or $$SEARCH_META + * - $let expressions + * - variables in scope from $lookup expressions + * Expressions that do not resolve to a current document, like $myField or $$ROOT, will result in an error. + */ + public function documents(PackedArray|ResolvesToArray|BSONArray|array|string $documents): static + { + $this->pipeline[] = Stage::documents($documents); + + return $this; + } + + /** + * Processes multiple aggregation pipelines within a single stage on the same set of input documents. Enables the creation of multi-faceted aggregations capable of characterizing data across multiple dimensions, or facets, in a single stage. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/facet/ + * @param BSONArray|PackedArray|Pipeline|array ...$facet + */ + public function facet(PackedArray|Pipeline|BSONArray|array ...$facet): static + { + $this->pipeline[] = Stage::facet(...$facet); + + return $this; + } + + /** + * Populates null and missing field values within documents. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/fill/ + * @param Document|Serializable|array|stdClass $output Specifies an object containing each field for which to fill missing values. You can specify multiple fields in the output object. + * The object name is the name of the field to fill. The object value specifies how the field is filled. + * @param Optional|Document|Serializable|array|stdClass|string $partitionBy Specifies an expression to group the documents. In the $fill stage, a group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|BSONArray|PackedArray|array $partitionByFields Specifies an array of fields as the compound key to group the documents. In the $fill stage, each group of documents is known as a partition. + * If you omit partitionBy and partitionByFields, $fill uses one partition for the entire collection. + * partitionBy and partitionByFields are mutually exclusive. + * @param Optional|Document|Serializable|array|stdClass $sortBy Specifies the field or fields to sort the documents within each partition. Uses the same syntax as the $sort stage. + */ + public function fill( + Document|Serializable|stdClass|array $output, + Optional|Document|Serializable|stdClass|array|string $partitionBy = Optional::Undefined, + Optional|PackedArray|BSONArray|array $partitionByFields = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sortBy = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::fill($output, $partitionBy, $partitionByFields, $sortBy); + + return $this; + } + + /** + * Returns an ordered stream of documents based on the proximity to a geospatial point. Incorporates the functionality of $match, $sort, and $limit for geospatial data. The output documents include an additional distance field and can include a location identifier field. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/geoNear/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $near The point for which to find the closest documents. + * @param Optional|string $distanceField The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. + * @param Optional|Decimal128|Int64|float|int $distanceMultiplier The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as returned by a spherical query, to kilometers by multiplying by the radius of the Earth. + * @param Optional|string $includeLocs This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location field contains multiple locations. To specify a field within an embedded document, use dot notation. + * @param Optional|string $key Specify the geospatial indexed field to use when calculating the distance. + * @param Optional|Decimal128|Int64|float|int $maxDistance The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point. + * Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs. + * @param Optional|Decimal128|Int64|float|int $minDistance The minimum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall outside the specified distance from the center point. + * Specify the distance in meters for GeoJSON data and in radians for legacy coordinate pairs. + * @param Optional|QueryInterface|array $query Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax. + * You cannot specify a $near predicate in the query field of the $geoNear stage. + * @param Optional|bool $spherical Determines how MongoDB calculates the distance between two points: + * - When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry. + * - When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes. + * Default: false. + */ + public function geoNear( + Document|Serializable|ResolvesToObject|stdClass|array|string $near, + Optional|string $distanceField = Optional::Undefined, + Optional|Decimal128|Int64|int|float $distanceMultiplier = Optional::Undefined, + Optional|string $includeLocs = Optional::Undefined, + Optional|string $key = Optional::Undefined, + Optional|Decimal128|Int64|int|float $maxDistance = Optional::Undefined, + Optional|Decimal128|Int64|int|float $minDistance = Optional::Undefined, + Optional|QueryInterface|array $query = Optional::Undefined, + Optional|bool $spherical = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::geoNear($near, $distanceField, $distanceMultiplier, $includeLocs, $key, $maxDistance, $minDistance, $query, $spherical); + + return $this; + } + + /** + * Performs a recursive search on a collection. To each output document, adds a new array field that contains the traversal results of the recursive search for that document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/graphLookup/ + * @param string $from Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param BSONArray|DateTimeInterface|ExpressionInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $startWith Expression that specifies the value of the connectFromField with which to start the recursive search. Optionally, startWith may be array of values, each of which is individually followed through the traversal process. + * @param string $connectFromField Field name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. If the value is an array, each element is individually followed through the traversal process. + * @param string $connectToField Field name in other documents against which to match the value of the field specified by the connectFromField parameter. + * @param string $as Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document. + * @param Optional|int $maxDepth Non-negative integral number specifying the maximum recursion depth. + * @param Optional|string $depthField Name of the field to add to each traversed document in the search path. The value of this field is the recursion depth for the document, represented as a NumberLong. Recursion depth value starts at zero, so the first lookup corresponds to zero depth. + * @param Optional|QueryInterface|array $restrictSearchWithMatch A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax. + */ + public function graphLookup( + string $from, + DateTimeInterface|PackedArray|Type|ExpressionInterface|BSONArray|stdClass|array|string|int|float|bool|null $startWith, + string $connectFromField, + string $connectToField, + string $as, + Optional|int $maxDepth = Optional::Undefined, + Optional|string $depthField = Optional::Undefined, + Optional|QueryInterface|array $restrictSearchWithMatch = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::graphLookup($from, $startWith, $connectFromField, $connectToField, $as, $maxDepth, $depthField, $restrictSearchWithMatch); + + return $this; + } + + /** + * Groups input documents by a specified identifier expression and applies the accumulator expression(s), if specified, to each group. Consumes all input documents and outputs one document per each distinct group. The output documents only contain the identifier field and, if specified, accumulated fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/group/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $_id The _id expression specifies the group key. If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents. + * @param AccumulatorInterface|Document|Serializable|array|stdClass ...$field Computed using the accumulator operators. + */ + public function group( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $_id, + Document|Serializable|AccumulatorInterface|stdClass|array ...$field, + ): static { + $this->pipeline[] = Stage::group($_id, ...$field); + + return $this; + } + + /** + * Returns statistics regarding the use of each index for the collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/indexStats/ + */ + public function indexStats(): static + { + $this->pipeline[] = Stage::indexStats(); + + return $this; + } + + /** + * Passes the first n documents unmodified to the pipeline where n is the specified limit. For each input document, outputs either one document (for the first n documents) or zero documents (after the first n documents). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/limit/ + * @param int $limit + */ + public function limit(int $limit): static + { + $this->pipeline[] = Stage::limit($limit); + + return $this; + } + + /** + * Lists all active sessions recently in use on the currently connected mongos or mongod instance. These sessions may have not yet propagated to the system.sessions collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listLocalSessions/ + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public function listLocalSessions( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::listLocalSessions($users, $allUsers); + + return $this; + } + + /** + * Lists sampled queries for all collections or a specific collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSampledQueries/ + * @param Optional|string $namespace + */ + public function listSampledQueries(Optional|string $namespace = Optional::Undefined): static + { + $this->pipeline[] = Stage::listSampledQueries($namespace); + + return $this; + } + + /** + * Returns information about existing Atlas Search indexes on a specified collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSearchIndexes/ + * @param Optional|string $id The id of the index to return information about. + * @param Optional|string $name The name of the index to return information about. + */ + public function listSearchIndexes( + Optional|string $id = Optional::Undefined, + Optional|string $name = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::listSearchIndexes($id, $name); + + return $this; + } + + /** + * Lists all sessions that have been active long enough to propagate to the system.sessions collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/listSessions/ + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public function listSessions( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::listSessions($users, $allUsers); + + return $this; + } + + /** + * Performs a left outer join to another collection in the same database to filter in documents from the "joined" collection for processing. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/lookup/ + * @param string $as Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten. + * @param Optional|string $from Specifies the collection in the same database to perform the join with. + * from is optional, you can use a $documents stage in a $lookup stage instead. For an example, see Use a $documents Stage in a $lookup Stage. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param Optional|string $localField Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes. + * @param Optional|string $foreignField Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline Specifies the pipeline to run on the joined collection. The pipeline determines the resulting documents from the joined collection. To return all documents, specify an empty pipeline []. + * The pipeline cannot include the $out stage or the $merge stage. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + * The pipeline cannot directly access the joined document fields. Instead, define variables for the joined document fields using the let option and then reference the variables in the pipeline stages. + */ + public function lookup( + string $as, + Optional|string $from = Optional::Undefined, + Optional|string $localField = Optional::Undefined, + Optional|string $foreignField = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::lookup($as, $from, $localField, $foreignField, $let, $pipeline); + + return $this; + } + + /** + * Writes the resulting documents of the aggregation pipeline to a collection. The stage can incorporate (insert new documents, merge documents, replace documents, keep existing documents, fail the operation, process documents with a custom update pipeline) the results into an output collection. To use the $merge stage, it must be the last stage in the pipeline. + * New in MongoDB 4.2. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/merge/ + * @param Document|Serializable|array|stdClass|string $into The output collection. + * @param Optional|BSONArray|PackedArray|array|string $on Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables for use in the whenMatched pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array|string $whenMatched The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s). + * @param Optional|string $whenNotMatched The behavior of $merge if a result document does not match an existing document in the out collection. + */ + public function merge( + Document|Serializable|stdClass|array|string $into, + Optional|PackedArray|BSONArray|array|string $on = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array|string $whenMatched = Optional::Undefined, + Optional|string $whenNotMatched = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::merge($into, $on, $let, $whenMatched, $whenNotMatched); + + return $this; + } + + /** + * Writes the resulting documents of the aggregation pipeline to a collection. To use the $out stage, it must be the last stage in the pipeline. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/out/ + * @param Document|Serializable|array|stdClass|string $coll Target database name to write documents from $out to. + */ + public function out(Document|Serializable|stdClass|array|string $coll): static + { + $this->pipeline[] = Stage::out($coll); + + return $this; + } + + /** + * Returns plan cache information for a collection. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/planCacheStats/ + */ + public function planCacheStats(): static + { + $this->pipeline[] = Stage::planCacheStats(); + + return $this; + } + + /** + * Reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$specification + */ + public function project( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null ...$specification, + ): static { + $this->pipeline[] = Stage::project(...$specification); + + return $this; + } + + /** + * Reshapes each document in the stream by restricting the content for each document based on information stored in the documents themselves. Incorporates the functionality of $project and $match. Can be used to implement field level redaction. For each input document, outputs either one or zero documents. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function redact( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $expression, + ): static { + $this->pipeline[] = Stage::redact($expression); + + return $this; + } + + /** + * Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceRoot/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $newRoot + */ + public function replaceRoot(Document|Serializable|ResolvesToObject|stdClass|array|string $newRoot): static + { + $this->pipeline[] = Stage::replaceRoot($newRoot); + + return $this; + } + + /** + * Replaces a document with the specified embedded document. The operation replaces all existing fields in the input document, including the _id field. Specify a document embedded in the input document to promote the embedded document to the top level. + * Alias for $replaceRoot. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/replaceWith/ + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $expression + */ + public function replaceWith(Document|Serializable|ResolvesToObject|stdClass|array|string $expression): static + { + $this->pipeline[] = Stage::replaceWith($expression); + + return $this; + } + + /** + * Randomly selects the specified number of documents from its input. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sample/ + * @param int $size The number of documents to randomly select. + */ + public function sample(int $size): static + { + $this->pipeline[] = Stage::sample($size); + + return $this; + } + + /** + * Performs a full-text search of the field or fields in an Atlas collection. + * NOTE: $search is only available for MongoDB Atlas clusters, and is not available for self-managed deployments. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/search/ + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to "default". + * @param Optional|Document|Serializable|array|stdClass $highlight Specifies the highlighting options for displaying search terms in their original context. + * @param Optional|bool $concurrent Parallelize search across segments on dedicated search nodes. + * If you don't have separate search nodes on your cluster, + * Atlas Search ignores this flag. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + * @param Optional|string $searchAfter Reference point for retrieving results. searchAfter returns documents starting immediately following the specified reference point. + * @param Optional|string $searchBefore Reference point for retrieving results. searchBefore returns documents starting immediately before the specified reference point. + * @param Optional|bool $scoreDetails Flag that specifies whether to retrieve a detailed breakdown of the score for the documents in the results. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $sort Document that specifies the fields to sort the Atlas Search results by in ascending or descending order. + * @param Optional|bool $returnStoredSource Flag that specifies whether to perform a full document lookup on the backend database or return only stored source fields directly from Atlas Search. + * @param Optional|Document|Serializable|array|stdClass $tracking Document that specifies the tracking option to retrieve analytics information on the search terms. + */ + public function search( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $highlight = Optional::Undefined, + Optional|bool $concurrent = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + Optional|string $searchAfter = Optional::Undefined, + Optional|string $searchBefore = Optional::Undefined, + Optional|bool $scoreDetails = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sort = Optional::Undefined, + Optional|bool $returnStoredSource = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $tracking = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::search($operator, $index, $highlight, $concurrent, $count, $searchAfter, $searchBefore, $scoreDetails, $sort, $returnStoredSource, $tracking); + + return $this; + } + + /** + * Returns different types of metadata result documents for the Atlas Search query against an Atlas collection. + * NOTE: $searchMeta is only available for MongoDB Atlas clusters running MongoDB v4.4.9 or higher, and is not available for self-managed deployments. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/searchMeta/ + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to default. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + */ + public function searchMeta( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::searchMeta($operator, $index, $count); + + return $this; + } + + /** + * Adds new fields to documents. Outputs documents that contain all existing fields from the input documents and newly added fields. + * Alias for $addFields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/set/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$field + */ + public function set( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null ...$field, + ): static { + $this->pipeline[] = Stage::set(...$field); + + return $this; + } + + /** + * Groups documents into windows and applies one or more operators to the documents in each window. + * New in MongoDB 5.0. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/ + * @param Document|Serializable|array|stdClass $sortBy Specifies the field(s) to sort the documents by in the partition. Uses the same syntax as the $sort stage. Default is no sorting. + * @param Document|Serializable|array|stdClass $output Specifies the field(s) to append to the documents in the output returned by the $setWindowFields stage. Each field is set to the result returned by the window operator. + * A field can contain dots to specify embedded document fields and array fields. The semantics for the embedded document dotted notation in the $setWindowFields stage are the same as the $addFields and $set stages. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $partitionBy Specifies an expression to group the documents. In the $setWindowFields stage, the group of documents is known as a partition. Default is one partition for the entire collection. + */ + public function setWindowFields( + Document|Serializable|stdClass|array $sortBy, + Document|Serializable|stdClass|array $output, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $partitionBy = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::setWindowFields($sortBy, $output, $partitionBy); + + return $this; + } + + /** + * Provides data and size distribution information on sharded collections. + * New in MongoDB 6.0.3. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/shardedDataDistribution/ + */ + public function shardedDataDistribution(): static + { + $this->pipeline[] = Stage::shardedDataDistribution(); + + return $this; + } + + /** + * Skips the first n documents where n is the specified skip number and passes the remaining documents unmodified to the pipeline. For each input document, outputs either zero documents (for the first n documents) or one document (if after the first n documents). + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/skip/ + * @param int $skip + */ + public function skip(int $skip): static + { + $this->pipeline[] = Stage::skip($skip); + + return $this; + } + + /** + * Reorders the document stream by a specified sort key. Only the order changes; the documents remain unmodified. For each input document, outputs one document. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/ + * @param DateTimeInterface|ExpressionInterface|Sort|Type|array|bool|float|int|null|stdClass|string ...$sort + */ + public function sort( + DateTimeInterface|Type|ExpressionInterface|Sort|stdClass|array|string|int|float|bool|null ...$sort, + ): static { + $this->pipeline[] = Stage::sort(...$sort); + + return $this; + } + + /** + * Groups incoming documents based on the value of a specified expression, then computes the count of documents in each distinct group. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sortByCount/ + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function sortByCount( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|string|int|float|bool|null $expression, + ): static { + $this->pipeline[] = Stage::sortByCount($expression); + + return $this; + } + + /** + * Performs a union of two collections; i.e. combines pipeline results from two collections into a single result set. + * New in MongoDB 4.4. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unionWith/ + * @param string $coll The collection or view whose pipeline results you wish to include in the result set. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline An aggregation pipeline to apply to the specified coll. + * The pipeline cannot include the $out and $merge stages. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + */ + public function unionWith( + string $coll, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::unionWith($coll, $pipeline); + + return $this; + } + + /** + * Removes or excludes fields from documents. + * Alias for $project stage that removes or excludes fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unset/ + * @no-named-arguments + * @param FieldPath|string ...$field + */ + public function unset(FieldPath|string ...$field): static + { + $this->pipeline[] = Stage::unset(...$field); + + return $this; + } + + /** + * Deconstructs an array field from the input documents to output a document for each element. Each output document replaces the array with an element value. For each input document, outputs n documents where n is the number of array elements and can be zero for an empty array. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/unwind/ + * @param ArrayFieldPath|string $path Field path to an array field. + * @param Optional|string $includeArrayIndex The name of a new field to hold the array index of the element. The name cannot start with a dollar sign $. + * @param Optional|bool $preserveNullAndEmptyArrays If true, if the path is null, missing, or an empty array, $unwind outputs the document. + * If false, if path is null, missing, or an empty array, $unwind does not output a document. + * The default value is false. + */ + public function unwind( + ArrayFieldPath|string $path, + Optional|string $includeArrayIndex = Optional::Undefined, + Optional|bool $preserveNullAndEmptyArrays = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::unwind($path, $includeArrayIndex, $preserveNullAndEmptyArrays); + + return $this; + } + + /** + * The $vectorSearch stage performs an ANN or ENN search on a vector in the specified field. + * + * @see https://www.mongodb.com/docs/atlas/atlas-vector-search/vector-search-stage/ + * @param string $index Name of the Atlas Vector Search index to use. + * @param int $limit Number of documents to return in the results. This value can't exceed the value of numCandidates if you specify numCandidates. + * @param string $path Indexed vector type field to search. + * @param BSONArray|PackedArray|array $queryVector Array of numbers that represent the query vector. The number type must match the indexed field value type. + * @param Optional|bool $exact This is required if numCandidates is omitted. false to run ANN search. true to run ENN search. + * @param Optional|QueryInterface|array $filter Any match query that compares an indexed field with a boolean, date, objectId, number (not decimals), string, or UUID to use as a pre-filter. + * @param Optional|int $numCandidates This field is required if exact is false or omitted. + * Number of nearest neighbors to use during the search. Value must be less than or equal to (<=) 10000. You can't specify a number less than the number of documents to return (limit). + */ + public function vectorSearch( + string $index, + int $limit, + string $path, + PackedArray|BSONArray|array $queryVector, + Optional|bool $exact = Optional::Undefined, + Optional|QueryInterface|array $filter = Optional::Undefined, + Optional|int $numCandidates = Optional::Undefined, + ): static { + $this->pipeline[] = Stage::vectorSearch($index, $limit, $path, $queryVector, $exact, $filter, $numCandidates); + + return $this; + } +} diff --git a/src/Builder/Stage/GeoNearStage.php b/src/Builder/Stage/GeoNearStage.php new file mode 100644 index 000000000..21600a5b8 --- /dev/null +++ b/src/Builder/Stage/GeoNearStage.php @@ -0,0 +1,139 @@ + 'near', + 'distanceField' => 'distanceField', + 'distanceMultiplier' => 'distanceMultiplier', + 'includeLocs' => 'includeLocs', + 'key' => 'key', + 'maxDistance' => 'maxDistance', + 'minDistance' => 'minDistance', + 'query' => 'query', + 'spherical' => 'spherical', + ]; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $near The point for which to find the closest documents. */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $near; + + /** @var Optional|string $distanceField The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. */ + public readonly Optional|string $distanceField; + + /** @var Optional|Decimal128|Int64|float|int $distanceMultiplier The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as returned by a spherical query, to kilometers by multiplying by the radius of the Earth. */ + public readonly Optional|Decimal128|Int64|float|int $distanceMultiplier; + + /** @var Optional|string $includeLocs This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location field contains multiple locations. To specify a field within an embedded document, use dot notation. */ + public readonly Optional|string $includeLocs; + + /** @var Optional|string $key Specify the geospatial indexed field to use when calculating the distance. */ + public readonly Optional|string $key; + + /** + * @var Optional|Decimal128|Int64|float|int $maxDistance The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point. + * Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs. + */ + public readonly Optional|Decimal128|Int64|float|int $maxDistance; + + /** + * @var Optional|Decimal128|Int64|float|int $minDistance The minimum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall outside the specified distance from the center point. + * Specify the distance in meters for GeoJSON data and in radians for legacy coordinate pairs. + */ + public readonly Optional|Decimal128|Int64|float|int $minDistance; + + /** + * @var Optional|QueryInterface|array $query Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax. + * You cannot specify a $near predicate in the query field of the $geoNear stage. + */ + public readonly Optional|QueryInterface|array $query; + + /** + * @var Optional|bool $spherical Determines how MongoDB calculates the distance between two points: + * - When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry. + * - When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes. + * Default: false. + */ + public readonly Optional|bool $spherical; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $near The point for which to find the closest documents. + * @param Optional|string $distanceField The output field that contains the calculated distance. To specify a field within an embedded document, use dot notation. + * @param Optional|Decimal128|Int64|float|int $distanceMultiplier The factor to multiply all distances returned by the query. For example, use the distanceMultiplier to convert radians, as returned by a spherical query, to kilometers by multiplying by the radius of the Earth. + * @param Optional|string $includeLocs This specifies the output field that identifies the location used to calculate the distance. This option is useful when a location field contains multiple locations. To specify a field within an embedded document, use dot notation. + * @param Optional|string $key Specify the geospatial indexed field to use when calculating the distance. + * @param Optional|Decimal128|Int64|float|int $maxDistance The maximum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall within the specified distance from the center point. + * Specify the distance in meters if the specified point is GeoJSON and in radians if the specified point is legacy coordinate pairs. + * @param Optional|Decimal128|Int64|float|int $minDistance The minimum distance from the center point that the documents can be. MongoDB limits the results to those documents that fall outside the specified distance from the center point. + * Specify the distance in meters for GeoJSON data and in radians for legacy coordinate pairs. + * @param Optional|QueryInterface|array $query Limits the results to the documents that match the query. The query syntax is the usual MongoDB read operation query syntax. + * You cannot specify a $near predicate in the query field of the $geoNear stage. + * @param Optional|bool $spherical Determines how MongoDB calculates the distance between two points: + * - When true, MongoDB uses $nearSphere semantics and calculates distances using spherical geometry. + * - When false, MongoDB uses $near semantics: spherical geometry for 2dsphere indexes and planar geometry for 2d indexes. + * Default: false. + */ + public function __construct( + Document|Serializable|ResolvesToObject|stdClass|array|string $near, + Optional|string $distanceField = Optional::Undefined, + Optional|Decimal128|Int64|float|int $distanceMultiplier = Optional::Undefined, + Optional|string $includeLocs = Optional::Undefined, + Optional|string $key = Optional::Undefined, + Optional|Decimal128|Int64|float|int $maxDistance = Optional::Undefined, + Optional|Decimal128|Int64|float|int $minDistance = Optional::Undefined, + Optional|QueryInterface|array $query = Optional::Undefined, + Optional|bool $spherical = Optional::Undefined, + ) { + if (is_string($near) && ! str_starts_with($near, '$')) { + throw new InvalidArgumentException('Argument $near can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->near = $near; + $this->distanceField = $distanceField; + $this->distanceMultiplier = $distanceMultiplier; + $this->includeLocs = $includeLocs; + $this->key = $key; + $this->maxDistance = $maxDistance; + $this->minDistance = $minDistance; + if (is_array($query)) { + $query = QueryObject::create($query); + } + + $this->query = $query; + $this->spherical = $spherical; + } +} diff --git a/src/Builder/Stage/GraphLookupStage.php b/src/Builder/Stage/GraphLookupStage.php new file mode 100644 index 000000000..d15b51bdf --- /dev/null +++ b/src/Builder/Stage/GraphLookupStage.php @@ -0,0 +1,115 @@ + 'from', + 'startWith' => 'startWith', + 'connectFromField' => 'connectFromField', + 'connectToField' => 'connectToField', + 'as' => 'as', + 'maxDepth' => 'maxDepth', + 'depthField' => 'depthField', + 'restrictSearchWithMatch' => 'restrictSearchWithMatch', + ]; + + /** + * @var string $from Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + */ + public readonly string $from; + + /** @var BSONArray|DateTimeInterface|ExpressionInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $startWith Expression that specifies the value of the connectFromField with which to start the recursive search. Optionally, startWith may be array of values, each of which is individually followed through the traversal process. */ + public readonly DateTimeInterface|PackedArray|Type|ExpressionInterface|BSONArray|stdClass|array|bool|float|int|null|string $startWith; + + /** @var string $connectFromField Field name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. If the value is an array, each element is individually followed through the traversal process. */ + public readonly string $connectFromField; + + /** @var string $connectToField Field name in other documents against which to match the value of the field specified by the connectFromField parameter. */ + public readonly string $connectToField; + + /** @var string $as Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document. */ + public readonly string $as; + + /** @var Optional|int $maxDepth Non-negative integral number specifying the maximum recursion depth. */ + public readonly Optional|int $maxDepth; + + /** @var Optional|string $depthField Name of the field to add to each traversed document in the search path. The value of this field is the recursion depth for the document, represented as a NumberLong. Recursion depth value starts at zero, so the first lookup corresponds to zero depth. */ + public readonly Optional|string $depthField; + + /** @var Optional|QueryInterface|array $restrictSearchWithMatch A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax. */ + public readonly Optional|QueryInterface|array $restrictSearchWithMatch; + + /** + * @param string $from Target collection for the $graphLookup operation to search, recursively matching the connectFromField to the connectToField. The from collection must be in the same database as any other collections used in the operation. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param BSONArray|DateTimeInterface|ExpressionInterface|PackedArray|Type|array|bool|float|int|null|stdClass|string $startWith Expression that specifies the value of the connectFromField with which to start the recursive search. Optionally, startWith may be array of values, each of which is individually followed through the traversal process. + * @param string $connectFromField Field name whose value $graphLookup uses to recursively match against the connectToField of other documents in the collection. If the value is an array, each element is individually followed through the traversal process. + * @param string $connectToField Field name in other documents against which to match the value of the field specified by the connectFromField parameter. + * @param string $as Name of the array field added to each output document. Contains the documents traversed in the $graphLookup stage to reach the document. + * @param Optional|int $maxDepth Non-negative integral number specifying the maximum recursion depth. + * @param Optional|string $depthField Name of the field to add to each traversed document in the search path. The value of this field is the recursion depth for the document, represented as a NumberLong. Recursion depth value starts at zero, so the first lookup corresponds to zero depth. + * @param Optional|QueryInterface|array $restrictSearchWithMatch A document specifying additional conditions for the recursive search. The syntax is identical to query filter syntax. + */ + public function __construct( + string $from, + DateTimeInterface|PackedArray|Type|ExpressionInterface|BSONArray|stdClass|array|bool|float|int|null|string $startWith, + string $connectFromField, + string $connectToField, + string $as, + Optional|int $maxDepth = Optional::Undefined, + Optional|string $depthField = Optional::Undefined, + Optional|QueryInterface|array $restrictSearchWithMatch = Optional::Undefined, + ) { + $this->from = $from; + if (is_array($startWith) && ! array_is_list($startWith)) { + throw new InvalidArgumentException('Expected $startWith argument to be a list, got an associative array.'); + } + + $this->startWith = $startWith; + $this->connectFromField = $connectFromField; + $this->connectToField = $connectToField; + $this->as = $as; + $this->maxDepth = $maxDepth; + $this->depthField = $depthField; + if (is_array($restrictSearchWithMatch)) { + $restrictSearchWithMatch = QueryObject::create($restrictSearchWithMatch); + } + + $this->restrictSearchWithMatch = $restrictSearchWithMatch; + } +} diff --git a/src/Builder/Stage/GroupStage.php b/src/Builder/Stage/GroupStage.php new file mode 100644 index 000000000..8577b4ad1 --- /dev/null +++ b/src/Builder/Stage/GroupStage.php @@ -0,0 +1,61 @@ + '_id', 'field' => null]; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $_id The _id expression specifies the group key. If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents. */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $_id; + + /** @var stdClass $field Computed using the accumulator operators. */ + public readonly stdClass $field; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $_id The _id expression specifies the group key. If you specify an _id value of null, or any other constant value, the $group stage returns a single document that aggregates values across all of the input documents. + * @param AccumulatorInterface|Document|Serializable|array|stdClass ...$field Computed using the accumulator operators. + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $_id, + Document|Serializable|AccumulatorInterface|stdClass|array ...$field, + ) { + $this->_id = $_id; + foreach($field as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $field arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $field = (object) $field; + $this->field = $field; + } +} diff --git a/src/Builder/Stage/IndexStatsStage.php b/src/Builder/Stage/IndexStatsStage.php new file mode 100644 index 000000000..0baaf8a18 --- /dev/null +++ b/src/Builder/Stage/IndexStatsStage.php @@ -0,0 +1,29 @@ + 'limit']; + + /** @var int $limit */ + public readonly int $limit; + + /** + * @param int $limit + */ + public function __construct(int $limit) + { + $this->limit = $limit; + } +} diff --git a/src/Builder/Stage/ListLocalSessionsStage.php b/src/Builder/Stage/ListLocalSessionsStage.php new file mode 100644 index 000000000..f438b0148 --- /dev/null +++ b/src/Builder/Stage/ListLocalSessionsStage.php @@ -0,0 +1,55 @@ + 'users', 'allUsers' => 'allUsers']; + + /** @var Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. */ + public readonly Optional|PackedArray|BSONArray|array $users; + + /** @var Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. */ + public readonly Optional|bool $allUsers; + + /** + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public function __construct( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ) { + if (is_array($users) && ! array_is_list($users)) { + throw new InvalidArgumentException('Expected $users argument to be a list, got an associative array.'); + } + + $this->users = $users; + $this->allUsers = $allUsers; + } +} diff --git a/src/Builder/Stage/ListSampledQueriesStage.php b/src/Builder/Stage/ListSampledQueriesStage.php new file mode 100644 index 000000000..afa242591 --- /dev/null +++ b/src/Builder/Stage/ListSampledQueriesStage.php @@ -0,0 +1,38 @@ + 'namespace']; + + /** @var Optional|string $namespace */ + public readonly Optional|string $namespace; + + /** + * @param Optional|string $namespace + */ + public function __construct(Optional|string $namespace = Optional::Undefined) + { + $this->namespace = $namespace; + } +} diff --git a/src/Builder/Stage/ListSearchIndexesStage.php b/src/Builder/Stage/ListSearchIndexesStage.php new file mode 100644 index 000000000..c6643ad33 --- /dev/null +++ b/src/Builder/Stage/ListSearchIndexesStage.php @@ -0,0 +1,45 @@ + 'id', 'name' => 'name']; + + /** @var Optional|string $id The id of the index to return information about. */ + public readonly Optional|string $id; + + /** @var Optional|string $name The name of the index to return information about. */ + public readonly Optional|string $name; + + /** + * @param Optional|string $id The id of the index to return information about. + * @param Optional|string $name The name of the index to return information about. + */ + public function __construct( + Optional|string $id = Optional::Undefined, + Optional|string $name = Optional::Undefined, + ) { + $this->id = $id; + $this->name = $name; + } +} diff --git a/src/Builder/Stage/ListSessionsStage.php b/src/Builder/Stage/ListSessionsStage.php new file mode 100644 index 000000000..687425ce1 --- /dev/null +++ b/src/Builder/Stage/ListSessionsStage.php @@ -0,0 +1,55 @@ + 'users', 'allUsers' => 'allUsers']; + + /** @var Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. */ + public readonly Optional|PackedArray|BSONArray|array $users; + + /** @var Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. */ + public readonly Optional|bool $allUsers; + + /** + * @param Optional|BSONArray|PackedArray|array $users Returns all sessions for the specified users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster to list sessions for other users. + * @param Optional|bool $allUsers Returns all sessions for all users. If running with access control, the authenticated user must have privileges with listSessions action on the cluster. + */ + public function __construct( + Optional|PackedArray|BSONArray|array $users = Optional::Undefined, + Optional|bool $allUsers = Optional::Undefined, + ) { + if (is_array($users) && ! array_is_list($users)) { + throw new InvalidArgumentException('Expected $users argument to be a list, got an associative array.'); + } + + $this->users = $users; + $this->allUsers = $allUsers; + } +} diff --git a/src/Builder/Stage/LookupStage.php b/src/Builder/Stage/LookupStage.php new file mode 100644 index 000000000..a9bf0d7ee --- /dev/null +++ b/src/Builder/Stage/LookupStage.php @@ -0,0 +1,103 @@ + 'as', + 'from' => 'from', + 'localField' => 'localField', + 'foreignField' => 'foreignField', + 'let' => 'let', + 'pipeline' => 'pipeline', + ]; + + /** @var string $as Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten. */ + public readonly string $as; + + /** + * @var Optional|string $from Specifies the collection in the same database to perform the join with. + * from is optional, you can use a $documents stage in a $lookup stage instead. For an example, see Use a $documents Stage in a $lookup Stage. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + */ + public readonly Optional|string $from; + + /** @var Optional|string $localField Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes. */ + public readonly Optional|string $localField; + + /** @var Optional|string $foreignField Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes. */ + public readonly Optional|string $foreignField; + + /** @var Optional|Document|Serializable|array|stdClass $let Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline. */ + public readonly Optional|Document|Serializable|stdClass|array $let; + + /** + * @var Optional|BSONArray|PackedArray|Pipeline|array $pipeline Specifies the pipeline to run on the joined collection. The pipeline determines the resulting documents from the joined collection. To return all documents, specify an empty pipeline []. + * The pipeline cannot include the $out stage or the $merge stage. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + * The pipeline cannot directly access the joined document fields. Instead, define variables for the joined document fields using the let option and then reference the variables in the pipeline stages. + */ + public readonly Optional|PackedArray|Pipeline|BSONArray|array $pipeline; + + /** + * @param string $as Specifies the name of the new array field to add to the input documents. The new array field contains the matching documents from the from collection. If the specified name already exists in the input document, the existing field is overwritten. + * @param Optional|string $from Specifies the collection in the same database to perform the join with. + * from is optional, you can use a $documents stage in a $lookup stage instead. For an example, see Use a $documents Stage in a $lookup Stage. + * Starting in MongoDB 5.1, the collection specified in the from parameter can be sharded. + * @param Optional|string $localField Specifies the field from the documents input to the $lookup stage. $lookup performs an equality match on the localField to the foreignField from the documents of the from collection. If an input document does not contain the localField, the $lookup treats the field as having a value of null for matching purposes. + * @param Optional|string $foreignField Specifies the field from the documents in the from collection. $lookup performs an equality match on the foreignField to the localField from the input documents. If a document in the from collection does not contain the foreignField, the $lookup treats the value as null for matching purposes. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables to use in the pipeline stages. Use the variable expressions to access the fields from the joined collection's documents that are input to the pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline Specifies the pipeline to run on the joined collection. The pipeline determines the resulting documents from the joined collection. To return all documents, specify an empty pipeline []. + * The pipeline cannot include the $out stage or the $merge stage. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + * The pipeline cannot directly access the joined document fields. Instead, define variables for the joined document fields using the let option and then reference the variables in the pipeline stages. + */ + public function __construct( + string $as, + Optional|string $from = Optional::Undefined, + Optional|string $localField = Optional::Undefined, + Optional|string $foreignField = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ) { + $this->as = $as; + $this->from = $from; + $this->localField = $localField; + $this->foreignField = $foreignField; + $this->let = $let; + if (is_array($pipeline) && ! array_is_list($pipeline)) { + throw new InvalidArgumentException('Expected $pipeline argument to be a list, got an associative array.'); + } + + $this->pipeline = $pipeline; + } +} diff --git a/src/Builder/Stage/MatchStage.php b/src/Builder/Stage/MatchStage.php new file mode 100644 index 000000000..0c88db2cf --- /dev/null +++ b/src/Builder/Stage/MatchStage.php @@ -0,0 +1,45 @@ + 'query']; + + /** @var QueryInterface|array $query */ + public readonly QueryInterface|array $query; + + /** + * @param QueryInterface|array $query + */ + public function __construct(QueryInterface|array $query) + { + if (is_array($query)) { + $query = QueryObject::create($query); + } + + $this->query = $query; + } +} diff --git a/src/Builder/Stage/MergeStage.php b/src/Builder/Stage/MergeStage.php new file mode 100644 index 000000000..2fc3e94da --- /dev/null +++ b/src/Builder/Stage/MergeStage.php @@ -0,0 +1,89 @@ + 'into', + 'on' => 'on', + 'let' => 'let', + 'whenMatched' => 'whenMatched', + 'whenNotMatched' => 'whenNotMatched', + ]; + + /** @var Document|Serializable|array|stdClass|string $into The output collection. */ + public readonly Document|Serializable|stdClass|array|string $into; + + /** @var Optional|BSONArray|PackedArray|array|string $on Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. */ + public readonly Optional|PackedArray|BSONArray|array|string $on; + + /** @var Optional|Document|Serializable|array|stdClass $let Specifies variables for use in the whenMatched pipeline. */ + public readonly Optional|Document|Serializable|stdClass|array $let; + + /** @var Optional|BSONArray|PackedArray|Pipeline|array|string $whenMatched The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s). */ + public readonly Optional|PackedArray|Pipeline|BSONArray|array|string $whenMatched; + + /** @var Optional|string $whenNotMatched The behavior of $merge if a result document does not match an existing document in the out collection. */ + public readonly Optional|string $whenNotMatched; + + /** + * @param Document|Serializable|array|stdClass|string $into The output collection. + * @param Optional|BSONArray|PackedArray|array|string $on Field or fields that act as a unique identifier for a document. The identifier determines if a results document matches an existing document in the output collection. + * @param Optional|Document|Serializable|array|stdClass $let Specifies variables for use in the whenMatched pipeline. + * @param Optional|BSONArray|PackedArray|Pipeline|array|string $whenMatched The behavior of $merge if a result document and an existing document in the collection have the same value for the specified on field(s). + * @param Optional|string $whenNotMatched The behavior of $merge if a result document does not match an existing document in the out collection. + */ + public function __construct( + Document|Serializable|stdClass|array|string $into, + Optional|PackedArray|BSONArray|array|string $on = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $let = Optional::Undefined, + Optional|PackedArray|Pipeline|BSONArray|array|string $whenMatched = Optional::Undefined, + Optional|string $whenNotMatched = Optional::Undefined, + ) { + $this->into = $into; + if (is_array($on) && ! array_is_list($on)) { + throw new InvalidArgumentException('Expected $on argument to be a list, got an associative array.'); + } + + $this->on = $on; + $this->let = $let; + if (is_array($whenMatched) && ! array_is_list($whenMatched)) { + throw new InvalidArgumentException('Expected $whenMatched argument to be a list, got an associative array.'); + } + + $this->whenMatched = $whenMatched; + $this->whenNotMatched = $whenNotMatched; + } +} diff --git a/src/Builder/Stage/OutStage.php b/src/Builder/Stage/OutStage.php new file mode 100644 index 000000000..77959edc8 --- /dev/null +++ b/src/Builder/Stage/OutStage.php @@ -0,0 +1,40 @@ + 'coll']; + + /** @var Document|Serializable|array|stdClass|string $coll Target database name to write documents from $out to. */ + public readonly Document|Serializable|stdClass|array|string $coll; + + /** + * @param Document|Serializable|array|stdClass|string $coll Target database name to write documents from $out to. + */ + public function __construct(Document|Serializable|stdClass|array|string $coll) + { + $this->coll = $coll; + } +} diff --git a/src/Builder/Stage/PlanCacheStatsStage.php b/src/Builder/Stage/PlanCacheStatsStage.php new file mode 100644 index 000000000..5cfd75674 --- /dev/null +++ b/src/Builder/Stage/PlanCacheStatsStage.php @@ -0,0 +1,29 @@ + 'specification']; + + /** @var stdClass $specification */ + public readonly stdClass $specification; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$specification + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$specification, + ) { + if (\count($specification) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $specification, got %d.', 1, \count($specification))); + } + + foreach($specification as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $specification arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $specification = (object) $specification; + $this->specification = $specification; + } +} diff --git a/src/Builder/Stage/RedactStage.php b/src/Builder/Stage/RedactStage.php new file mode 100644 index 000000000..0da03aba5 --- /dev/null +++ b/src/Builder/Stage/RedactStage.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Stage/ReplaceRootStage.php b/src/Builder/Stage/ReplaceRootStage.php new file mode 100644 index 000000000..2cf5c34b2 --- /dev/null +++ b/src/Builder/Stage/ReplaceRootStage.php @@ -0,0 +1,49 @@ + 'newRoot']; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $newRoot */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $newRoot; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $newRoot + */ + public function __construct(Document|Serializable|ResolvesToObject|stdClass|array|string $newRoot) + { + if (is_string($newRoot) && ! str_starts_with($newRoot, '$')) { + throw new InvalidArgumentException('Argument $newRoot can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->newRoot = $newRoot; + } +} diff --git a/src/Builder/Stage/ReplaceWithStage.php b/src/Builder/Stage/ReplaceWithStage.php new file mode 100644 index 000000000..95746bfa9 --- /dev/null +++ b/src/Builder/Stage/ReplaceWithStage.php @@ -0,0 +1,50 @@ + 'expression']; + + /** @var Document|ResolvesToObject|Serializable|array|stdClass|string $expression */ + public readonly Document|Serializable|ResolvesToObject|stdClass|array|string $expression; + + /** + * @param Document|ResolvesToObject|Serializable|array|stdClass|string $expression + */ + public function __construct(Document|Serializable|ResolvesToObject|stdClass|array|string $expression) + { + if (is_string($expression) && ! str_starts_with($expression, '$')) { + throw new InvalidArgumentException('Argument $expression can be an expression, field paths and variable names must be prefixed by "$" or "$$".'); + } + + $this->expression = $expression; + } +} diff --git a/src/Builder/Stage/SampleStage.php b/src/Builder/Stage/SampleStage.php new file mode 100644 index 000000000..c00b2bb26 --- /dev/null +++ b/src/Builder/Stage/SampleStage.php @@ -0,0 +1,37 @@ + 'size']; + + /** @var int $size The number of documents to randomly select. */ + public readonly int $size; + + /** + * @param int $size The number of documents to randomly select. + */ + public function __construct(int $size) + { + $this->size = $size; + } +} diff --git a/src/Builder/Stage/SearchMetaStage.php b/src/Builder/Stage/SearchMetaStage.php new file mode 100644 index 000000000..14687161b --- /dev/null +++ b/src/Builder/Stage/SearchMetaStage.php @@ -0,0 +1,60 @@ + null, 'index' => 'index', 'count' => 'count']; + + /** + * @var Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + */ + public readonly Document|Serializable|SearchOperatorInterface|stdClass|array $operator; + + /** @var Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to default. */ + public readonly Optional|string $index; + + /** @var Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. */ + public readonly Optional|Document|Serializable|stdClass|array $count; + + /** + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to default. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + */ + public function __construct( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + ) { + $this->operator = $operator; + $this->index = $index; + $this->count = $count; + } +} diff --git a/src/Builder/Stage/SearchStage.php b/src/Builder/Stage/SearchStage.php new file mode 100644 index 000000000..1aac4a423 --- /dev/null +++ b/src/Builder/Stage/SearchStage.php @@ -0,0 +1,127 @@ + null, + 'index' => 'index', + 'highlight' => 'highlight', + 'concurrent' => 'concurrent', + 'count' => 'count', + 'searchAfter' => 'searchAfter', + 'searchBefore' => 'searchBefore', + 'scoreDetails' => 'scoreDetails', + 'sort' => 'sort', + 'returnStoredSource' => 'returnStoredSource', + 'tracking' => 'tracking', + ]; + + /** + * @var Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + */ + public readonly Document|Serializable|SearchOperatorInterface|stdClass|array $operator; + + /** @var Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to "default". */ + public readonly Optional|string $index; + + /** @var Optional|Document|Serializable|array|stdClass $highlight Specifies the highlighting options for displaying search terms in their original context. */ + public readonly Optional|Document|Serializable|stdClass|array $highlight; + + /** + * @var Optional|bool $concurrent Parallelize search across segments on dedicated search nodes. + * If you don't have separate search nodes on your cluster, + * Atlas Search ignores this flag. If omitted, defaults to false. + */ + public readonly Optional|bool $concurrent; + + /** @var Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. */ + public readonly Optional|Document|Serializable|stdClass|array $count; + + /** @var Optional|string $searchAfter Reference point for retrieving results. searchAfter returns documents starting immediately following the specified reference point. */ + public readonly Optional|string $searchAfter; + + /** @var Optional|string $searchBefore Reference point for retrieving results. searchBefore returns documents starting immediately before the specified reference point. */ + public readonly Optional|string $searchBefore; + + /** @var Optional|bool $scoreDetails Flag that specifies whether to retrieve a detailed breakdown of the score for the documents in the results. If omitted, defaults to false. */ + public readonly Optional|bool $scoreDetails; + + /** @var Optional|Document|Serializable|array|stdClass $sort Document that specifies the fields to sort the Atlas Search results by in ascending or descending order. */ + public readonly Optional|Document|Serializable|stdClass|array $sort; + + /** @var Optional|bool $returnStoredSource Flag that specifies whether to perform a full document lookup on the backend database or return only stored source fields directly from Atlas Search. */ + public readonly Optional|bool $returnStoredSource; + + /** @var Optional|Document|Serializable|array|stdClass $tracking Document that specifies the tracking option to retrieve analytics information on the search terms. */ + public readonly Optional|Document|Serializable|stdClass|array $tracking; + + /** + * @param Document|SearchOperatorInterface|Serializable|array|stdClass $operator Operator to search with. You can provide a specific operator or use + * the compound operator to run a compound query with multiple operators. + * @param Optional|string $index Name of the Atlas Search index to use. If omitted, defaults to "default". + * @param Optional|Document|Serializable|array|stdClass $highlight Specifies the highlighting options for displaying search terms in their original context. + * @param Optional|bool $concurrent Parallelize search across segments on dedicated search nodes. + * If you don't have separate search nodes on your cluster, + * Atlas Search ignores this flag. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $count Document that specifies the count options for retrieving a count of the results. + * @param Optional|string $searchAfter Reference point for retrieving results. searchAfter returns documents starting immediately following the specified reference point. + * @param Optional|string $searchBefore Reference point for retrieving results. searchBefore returns documents starting immediately before the specified reference point. + * @param Optional|bool $scoreDetails Flag that specifies whether to retrieve a detailed breakdown of the score for the documents in the results. If omitted, defaults to false. + * @param Optional|Document|Serializable|array|stdClass $sort Document that specifies the fields to sort the Atlas Search results by in ascending or descending order. + * @param Optional|bool $returnStoredSource Flag that specifies whether to perform a full document lookup on the backend database or return only stored source fields directly from Atlas Search. + * @param Optional|Document|Serializable|array|stdClass $tracking Document that specifies the tracking option to retrieve analytics information on the search terms. + */ + public function __construct( + Document|Serializable|SearchOperatorInterface|stdClass|array $operator, + Optional|string $index = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $highlight = Optional::Undefined, + Optional|bool $concurrent = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $count = Optional::Undefined, + Optional|string $searchAfter = Optional::Undefined, + Optional|string $searchBefore = Optional::Undefined, + Optional|bool $scoreDetails = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $sort = Optional::Undefined, + Optional|bool $returnStoredSource = Optional::Undefined, + Optional|Document|Serializable|stdClass|array $tracking = Optional::Undefined, + ) { + $this->operator = $operator; + $this->index = $index; + $this->highlight = $highlight; + $this->concurrent = $concurrent; + $this->count = $count; + $this->searchAfter = $searchAfter; + $this->searchBefore = $searchBefore; + $this->scoreDetails = $scoreDetails; + $this->sort = $sort; + $this->returnStoredSource = $returnStoredSource; + $this->tracking = $tracking; + } +} diff --git a/src/Builder/Stage/SetStage.php b/src/Builder/Stage/SetStage.php new file mode 100644 index 000000000..a9c2a53ec --- /dev/null +++ b/src/Builder/Stage/SetStage.php @@ -0,0 +1,57 @@ + 'field']; + + /** @var stdClass $field */ + public readonly stdClass $field; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string ...$field + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string ...$field, + ) { + if (\count($field) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $field, got %d.', 1, \count($field))); + } + + foreach($field as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $field arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $field = (object) $field; + $this->field = $field; + } +} diff --git a/src/Builder/Stage/SetWindowFieldsStage.php b/src/Builder/Stage/SetWindowFieldsStage.php new file mode 100644 index 000000000..7d45bc5aa --- /dev/null +++ b/src/Builder/Stage/SetWindowFieldsStage.php @@ -0,0 +1,62 @@ + 'sortBy', 'output' => 'output', 'partitionBy' => 'partitionBy']; + + /** @var Document|Serializable|array|stdClass $sortBy Specifies the field(s) to sort the documents by in the partition. Uses the same syntax as the $sort stage. Default is no sorting. */ + public readonly Document|Serializable|stdClass|array $sortBy; + + /** + * @var Document|Serializable|array|stdClass $output Specifies the field(s) to append to the documents in the output returned by the $setWindowFields stage. Each field is set to the result returned by the window operator. + * A field can contain dots to specify embedded document fields and array fields. The semantics for the embedded document dotted notation in the $setWindowFields stage are the same as the $addFields and $set stages. + */ + public readonly Document|Serializable|stdClass|array $output; + + /** @var Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $partitionBy Specifies an expression to group the documents. In the $setWindowFields stage, the group of documents is known as a partition. Default is one partition for the entire collection. */ + public readonly Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $partitionBy; + + /** + * @param Document|Serializable|array|stdClass $sortBy Specifies the field(s) to sort the documents by in the partition. Uses the same syntax as the $sort stage. Default is no sorting. + * @param Document|Serializable|array|stdClass $output Specifies the field(s) to append to the documents in the output returned by the $setWindowFields stage. Each field is set to the result returned by the window operator. + * A field can contain dots to specify embedded document fields and array fields. The semantics for the embedded document dotted notation in the $setWindowFields stage are the same as the $addFields and $set stages. + * @param Optional|DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $partitionBy Specifies an expression to group the documents. In the $setWindowFields stage, the group of documents is known as a partition. Default is one partition for the entire collection. + */ + public function __construct( + Document|Serializable|stdClass|array $sortBy, + Document|Serializable|stdClass|array $output, + Optional|DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $partitionBy = Optional::Undefined, + ) { + $this->sortBy = $sortBy; + $this->output = $output; + $this->partitionBy = $partitionBy; + } +} diff --git a/src/Builder/Stage/ShardedDataDistributionStage.php b/src/Builder/Stage/ShardedDataDistributionStage.php new file mode 100644 index 000000000..fe07d282a --- /dev/null +++ b/src/Builder/Stage/ShardedDataDistributionStage.php @@ -0,0 +1,30 @@ + 'skip']; + + /** @var int $skip */ + public readonly int $skip; + + /** + * @param int $skip + */ + public function __construct(int $skip) + { + $this->skip = $skip; + } +} diff --git a/src/Builder/Stage/SortByCountStage.php b/src/Builder/Stage/SortByCountStage.php new file mode 100644 index 000000000..e616470b9 --- /dev/null +++ b/src/Builder/Stage/SortByCountStage.php @@ -0,0 +1,42 @@ + 'expression']; + + /** @var DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression */ + public readonly DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression; + + /** + * @param DateTimeInterface|ExpressionInterface|Type|array|bool|float|int|null|stdClass|string $expression + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|stdClass|array|bool|float|int|null|string $expression, + ) { + $this->expression = $expression; + } +} diff --git a/src/Builder/Stage/SortStage.php b/src/Builder/Stage/SortStage.php new file mode 100644 index 000000000..fc8899a4c --- /dev/null +++ b/src/Builder/Stage/SortStage.php @@ -0,0 +1,57 @@ + 'sort']; + + /** @var stdClass $sort */ + public readonly stdClass $sort; + + /** + * @param DateTimeInterface|ExpressionInterface|Sort|Type|array|bool|float|int|null|stdClass|string ...$sort + */ + public function __construct( + DateTimeInterface|Type|ExpressionInterface|Sort|stdClass|array|bool|float|int|null|string ...$sort, + ) { + if (\count($sort) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $sort, got %d.', 1, \count($sort))); + } + + foreach($sort as $key => $value) { + if (! is_string($key)) { + throw new InvalidArgumentException('Expected $sort arguments to be a map (object), named arguments (:) or array unpacking ...[\'\' => ] must be used'); + } + } + + $sort = (object) $sort; + $this->sort = $sort; + } +} diff --git a/src/Builder/Stage/UnionWithStage.php b/src/Builder/Stage/UnionWithStage.php new file mode 100644 index 000000000..25ced13de --- /dev/null +++ b/src/Builder/Stage/UnionWithStage.php @@ -0,0 +1,61 @@ + 'coll', 'pipeline' => 'pipeline']; + + /** @var string $coll The collection or view whose pipeline results you wish to include in the result set. */ + public readonly string $coll; + + /** + * @var Optional|BSONArray|PackedArray|Pipeline|array $pipeline An aggregation pipeline to apply to the specified coll. + * The pipeline cannot include the $out and $merge stages. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + */ + public readonly Optional|PackedArray|Pipeline|BSONArray|array $pipeline; + + /** + * @param string $coll The collection or view whose pipeline results you wish to include in the result set. + * @param Optional|BSONArray|PackedArray|Pipeline|array $pipeline An aggregation pipeline to apply to the specified coll. + * The pipeline cannot include the $out and $merge stages. Starting in v6.0, the pipeline can contain the Atlas Search $search stage as the first stage inside the pipeline. + */ + public function __construct( + string $coll, + Optional|PackedArray|Pipeline|BSONArray|array $pipeline = Optional::Undefined, + ) { + $this->coll = $coll; + if (is_array($pipeline) && ! array_is_list($pipeline)) { + throw new InvalidArgumentException('Expected $pipeline argument to be a list, got an associative array.'); + } + + $this->pipeline = $pipeline; + } +} diff --git a/src/Builder/Stage/UnsetStage.php b/src/Builder/Stage/UnsetStage.php new file mode 100644 index 000000000..43575d8d5 --- /dev/null +++ b/src/Builder/Stage/UnsetStage.php @@ -0,0 +1,51 @@ + 'field']; + + /** @var list $field */ + public readonly array $field; + + /** + * @param FieldPath|string ...$field + * @no-named-arguments + */ + public function __construct(FieldPath|string ...$field) + { + if (\count($field) < 1) { + throw new InvalidArgumentException(\sprintf('Expected at least %d values for $field, got %d.', 1, \count($field))); + } + + if (! array_is_list($field)) { + throw new InvalidArgumentException('Expected $field arguments to be a list (array), named arguments are not supported'); + } + + $this->field = $field; + } +} diff --git a/src/Builder/Stage/UnwindStage.php b/src/Builder/Stage/UnwindStage.php new file mode 100644 index 000000000..9959f462b --- /dev/null +++ b/src/Builder/Stage/UnwindStage.php @@ -0,0 +1,63 @@ + 'path', + 'includeArrayIndex' => 'includeArrayIndex', + 'preserveNullAndEmptyArrays' => 'preserveNullAndEmptyArrays', + ]; + + /** @var ArrayFieldPath|string $path Field path to an array field. */ + public readonly ArrayFieldPath|string $path; + + /** @var Optional|string $includeArrayIndex The name of a new field to hold the array index of the element. The name cannot start with a dollar sign $. */ + public readonly Optional|string $includeArrayIndex; + + /** + * @var Optional|bool $preserveNullAndEmptyArrays If true, if the path is null, missing, or an empty array, $unwind outputs the document. + * If false, if path is null, missing, or an empty array, $unwind does not output a document. + * The default value is false. + */ + public readonly Optional|bool $preserveNullAndEmptyArrays; + + /** + * @param ArrayFieldPath|string $path Field path to an array field. + * @param Optional|string $includeArrayIndex The name of a new field to hold the array index of the element. The name cannot start with a dollar sign $. + * @param Optional|bool $preserveNullAndEmptyArrays If true, if the path is null, missing, or an empty array, $unwind outputs the document. + * If false, if path is null, missing, or an empty array, $unwind does not output a document. + * The default value is false. + */ + public function __construct( + ArrayFieldPath|string $path, + Optional|string $includeArrayIndex = Optional::Undefined, + Optional|bool $preserveNullAndEmptyArrays = Optional::Undefined, + ) { + $this->path = $path; + $this->includeArrayIndex = $includeArrayIndex; + $this->preserveNullAndEmptyArrays = $preserveNullAndEmptyArrays; + } +} diff --git a/src/Builder/Stage/VectorSearchStage.php b/src/Builder/Stage/VectorSearchStage.php new file mode 100644 index 000000000..5a974c74e --- /dev/null +++ b/src/Builder/Stage/VectorSearchStage.php @@ -0,0 +1,104 @@ + 'index', + 'limit' => 'limit', + 'path' => 'path', + 'queryVector' => 'queryVector', + 'exact' => 'exact', + 'filter' => 'filter', + 'numCandidates' => 'numCandidates', + ]; + + /** @var string $index Name of the Atlas Vector Search index to use. */ + public readonly string $index; + + /** @var int $limit Number of documents to return in the results. This value can't exceed the value of numCandidates if you specify numCandidates. */ + public readonly int $limit; + + /** @var string $path Indexed vector type field to search. */ + public readonly string $path; + + /** @var BSONArray|PackedArray|array $queryVector Array of numbers that represent the query vector. The number type must match the indexed field value type. */ + public readonly PackedArray|BSONArray|array $queryVector; + + /** @var Optional|bool $exact This is required if numCandidates is omitted. false to run ANN search. true to run ENN search. */ + public readonly Optional|bool $exact; + + /** @var Optional|QueryInterface|array $filter Any match query that compares an indexed field with a boolean, date, objectId, number (not decimals), string, or UUID to use as a pre-filter. */ + public readonly Optional|QueryInterface|array $filter; + + /** + * @var Optional|int $numCandidates This field is required if exact is false or omitted. + * Number of nearest neighbors to use during the search. Value must be less than or equal to (<=) 10000. You can't specify a number less than the number of documents to return (limit). + */ + public readonly Optional|int $numCandidates; + + /** + * @param string $index Name of the Atlas Vector Search index to use. + * @param int $limit Number of documents to return in the results. This value can't exceed the value of numCandidates if you specify numCandidates. + * @param string $path Indexed vector type field to search. + * @param BSONArray|PackedArray|array $queryVector Array of numbers that represent the query vector. The number type must match the indexed field value type. + * @param Optional|bool $exact This is required if numCandidates is omitted. false to run ANN search. true to run ENN search. + * @param Optional|QueryInterface|array $filter Any match query that compares an indexed field with a boolean, date, objectId, number (not decimals), string, or UUID to use as a pre-filter. + * @param Optional|int $numCandidates This field is required if exact is false or omitted. + * Number of nearest neighbors to use during the search. Value must be less than or equal to (<=) 10000. You can't specify a number less than the number of documents to return (limit). + */ + public function __construct( + string $index, + int $limit, + string $path, + PackedArray|BSONArray|array $queryVector, + Optional|bool $exact = Optional::Undefined, + Optional|QueryInterface|array $filter = Optional::Undefined, + Optional|int $numCandidates = Optional::Undefined, + ) { + $this->index = $index; + $this->limit = $limit; + $this->path = $path; + if (is_array($queryVector) && ! array_is_list($queryVector)) { + throw new InvalidArgumentException('Expected $queryVector argument to be a list, got an associative array.'); + } + + $this->queryVector = $queryVector; + $this->exact = $exact; + if (is_array($filter)) { + $filter = QueryObject::create($filter); + } + + $this->filter = $filter; + $this->numCandidates = $numCandidates; + } +} diff --git a/src/Builder/Type/AccumulatorInterface.php b/src/Builder/Type/AccumulatorInterface.php new file mode 100644 index 000000000..c3aaad2af --- /dev/null +++ b/src/Builder/Type/AccumulatorInterface.php @@ -0,0 +1,14 @@ + $fieldQueries */ + public readonly array $fieldQueries; + + /** @param list $fieldQueries */ + public function __construct(array $fieldQueries) + { + if (! array_is_list($fieldQueries)) { + throw new InvalidArgumentException('Expected filters to be a list, invalid array given.'); + } + + // Flatten nested CombinedFieldQuery + $this->fieldQueries = array_reduce( + $fieldQueries, + /** + * @param list $fieldQueries + * + * @return list + */ + static function (array $fieldQueries, QueryInterface|FieldQueryInterface|Type|stdClass|array|bool|float|int|string|null $fieldQuery): array { + if ($fieldQuery instanceof CombinedFieldQuery) { + return array_merge($fieldQueries, $fieldQuery->fieldQueries); + } + + $fieldQueries[] = $fieldQuery; + + return $fieldQueries; + }, + [], + ); + + // Validate FieldQuery types and non-duplicate operators + /** @var array $seenOperators */ + $seenOperators = []; + foreach ($this->fieldQueries as $fieldQuery) { + if ($fieldQuery instanceof stdClass) { + $fieldQuery = get_object_vars($fieldQuery); + } + + if ($fieldQuery instanceof FieldQueryInterface && $fieldQuery instanceof OperatorInterface) { + $operator = $fieldQuery::NAME; + } elseif (is_array($fieldQuery)) { + if (count($fieldQuery) !== 1) { + throw new InvalidArgumentException(sprintf('Operator must contain exactly one key, %d given', count($fieldQuery))); + } + + $operator = array_key_first($fieldQuery); + if (! is_string($operator) || ! str_starts_with($operator, '$')) { + throw new InvalidArgumentException(sprintf('Operator must contain exactly one key starting with $, "%s" given', $operator)); + } + } else { + throw new InvalidArgumentException(sprintf('Expected filters to be a list of field query operators, array or stdClass, %s given', get_debug_type($fieldQuery))); + } + + if (array_key_exists($operator, $seenOperators)) { + throw new InvalidArgumentException(sprintf('Duplicate operator "%s" detected', $operator)); + } + + $seenOperators[$operator] = true; + } + } +} diff --git a/src/Builder/Type/DictionaryInterface.php b/src/Builder/Type/DictionaryInterface.php new file mode 100644 index 000000000..5d88abffa --- /dev/null +++ b/src/Builder/Type/DictionaryInterface.php @@ -0,0 +1,10 @@ + */ + public const PROPERTIES = []; + + /** @var string|null */ + public const NAME = null; +} diff --git a/src/Builder/Type/Optional.php b/src/Builder/Type/Optional.php new file mode 100644 index 000000000..f7a4a878e --- /dev/null +++ b/src/Builder/Type/Optional.php @@ -0,0 +1,17 @@ +|stdClass $operator Window operator to use in the $setWindowFields stage. + * @param Optional|array{string|int,string|int} $documents A window where the lower and upper boundaries are specified relative to the position of the current document read from the collection. + * @param Optional|array{string|numeric,string|numeric} $range Arguments passed to the init function. + * @param Optional|non-empty-string $unit Specifies the units for time range window boundaries. If omitted, default numeric range window boundaries are used. + */ + public function __construct( + Document|Serializable|WindowInterface|stdClass|array $operator, + Optional|array $documents = Optional::Undefined, + Optional|array $range = Optional::Undefined, + Optional|TimeUnit|string $unit = Optional::Undefined, + ) { + $this->operator = $operator; + + $window = null; + if ($documents !== Optional::Undefined) { + if (! array_is_list($documents) || count($documents) !== 2) { + throw new InvalidArgumentException('Expected $documents argument to be a list of 2 string or int'); + } + + if (! is_string($documents[0]) && ! is_int($documents[0]) || ! is_string($documents[1]) && ! is_int($documents[1])) { + throw new InvalidArgumentException(sprintf('Expected $documents argument to be a list of 2 string or int. Got [%s, %s]', get_debug_type($documents[0]), get_debug_type($documents[1]))); + } + + $window = new stdClass(); + $window->documents = $documents; + } + + if ($range !== Optional::Undefined) { + if (! array_is_list($range) || count($range) !== 2) { + throw new InvalidArgumentException('Expected $range argument to be a list of 2 string or numeric'); + } + + if (! is_string($range[0]) && ! is_numeric($range[0]) || ! is_string($range[1]) && ! is_numeric($range[1])) { + throw new InvalidArgumentException(sprintf('Expected $range argument to be a list of 2 string or numeric. Got [%s, %s]', get_debug_type($range[0]), get_debug_type($range[1]))); + } + + $window ??= new stdClass(); + $window->range = $range; + } + + if ($unit !== Optional::Undefined) { + $window ??= new stdClass(); + $window->unit = $unit; + } + + $this->window = $window ?? Optional::Undefined; + } +} diff --git a/src/Builder/Type/QueryInterface.php b/src/Builder/Type/QueryInterface.php new file mode 100644 index 000000000..a396d5339 --- /dev/null +++ b/src/Builder/Type/QueryInterface.php @@ -0,0 +1,14 @@ + $queries */ + public static function create(array $queries): QueryInterface + { + // We don't wrap a single query in a QueryObject + if (count($queries) === 1 && isset($queries[0]) && $queries[0] instanceof QueryInterface) { + return $queries[0]; + } + + return new self($queries); + } + + /** @param array $queriesOrArrayOfQueries */ + private function __construct(array $queriesOrArrayOfQueries) + { + // If the first element is an array and not an operator, we assume variadic arguments were not used + if ( + count($queriesOrArrayOfQueries) === 1 && + isset($queriesOrArrayOfQueries[0]) && + is_array($queriesOrArrayOfQueries[0]) && + count($queriesOrArrayOfQueries[0]) > 0 && + ! str_starts_with((string) array_key_first($queriesOrArrayOfQueries[0]), '$') + ) { + $queriesOrArrayOfQueries = $queriesOrArrayOfQueries[0]; + } + + $seenQueryOperators = []; + $queries = []; + + foreach ($queriesOrArrayOfQueries as $fieldPath => $query) { + if ($query instanceof QueryInterface) { + if ($query instanceof OperatorInterface) { + if (isset($seenQueryOperators[$query::NAME])) { + throw new InvalidArgumentException(sprintf('Query operator "%s" cannot be used multiple times in the same query.', $query::NAME)); + } + + $seenQueryOperators[$query::NAME] = true; + } + + $queries[] = $query; + continue; + } + + // Convert list of filters into CombinedFieldQuery + if (self::isListOfFilters($query)) { + if (count($query) === 1) { + $query = $query[0]; + } else { + $query = new CombinedFieldQuery($query); + } + } + + $queries[$fieldPath] = $query; + } + + $this->queries = $queries; + } + + /** @psalm-assert-if-true list $values */ + private static function isListOfFilters(mixed $values): bool + { + if (! is_array($values) || ! array_is_list($values)) { + return false; + } + + /** @var mixed $value */ + foreach ($values as $value) { + if ($value instanceof FieldQueryInterface) { + return true; + } + } + + return false; + } +} diff --git a/src/Builder/Type/SearchOperatorInterface.php b/src/Builder/Type/SearchOperatorInterface.php new file mode 100644 index 000000000..c144d4ebe --- /dev/null +++ b/src/Builder/Type/SearchOperatorInterface.php @@ -0,0 +1,7 @@ + 1, + self::Desc => -1, + self::TextScore => ['$meta' => 'textScore'], + }; + } +} diff --git a/src/Builder/Type/StageInterface.php b/src/Builder/Type/StageInterface.php new file mode 100644 index 000000000..6d4fcf2e6 --- /dev/null +++ b/src/Builder/Type/StageInterface.php @@ -0,0 +1,14 @@ +value; + } +} diff --git a/src/Builder/Type/WindowInterface.php b/src/Builder/Type/WindowInterface.php new file mode 100644 index 000000000..d3fc8c378 --- /dev/null +++ b/src/Builder/Type/WindowInterface.php @@ -0,0 +1,14 @@ + is equivalent to $$CURRENT., rebinding + * CURRENT changes the meaning of $ accesses. + */ + public static function current(string $fieldPath = ''): ResolvesToAny + { + return new Expression\Variable('CURRENT' . ($fieldPath ? '.' . $fieldPath : '')); + } + + /** + * One of the allowed results of a $redact expression. + * + * $redact returns the fields at the current document level, excluding embedded documents. To include embedded + * documents and embedded documents within arrays, apply the $cond expression to the embedded documents to determine + * access for these embedded documents. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact + */ + public static function descend(): ExpressionInterface + { + return new Expression\Variable('DESCEND'); + } + + /** + * One of the allowed results of a $redact expression. + * + * $redact returns or keeps all fields at this current document/embedded document level, without further inspection + * of the fields at this level. This applies even if the included field contains embedded documents that may have + * different access levels. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact + */ + public static function keep(): ExpressionInterface + { + return new Expression\Variable('KEEP'); + } + + /** + * A variable that returns the current datetime value. + * NOW returns the same value for all members of the deployment and remains the same throughout all stages of the + * aggregation pipeline. + * + * New in MongoDB 4.2. + */ + public static function now(): ResolvesToDate + { + return new Expression\Variable('NOW'); + } + + /** + * One of the allowed results of a $redact expression. + * + * $redact excludes all fields at this current document/embedded document level, without further inspection of any + * of the excluded fields. This applies even if the excluded field contains embedded documents that may have + * different access levels. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/#mongodb-pipeline-pipe.-redact + */ + public static function prune(): ExpressionInterface + { + return new Expression\Variable('PRUNE'); + } + + /** + * A variable which evaluates to the missing value. Allows for the conditional exclusion of fields. In a $project, + * a field set to the variable REMOVE is excluded from the output. + * Can be used with $cond operator for conditionally exclude fields. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/project/#std-label-remove-example + */ + public static function remove(): ResolvesToAny + { + return new Expression\Variable('REMOVE'); + } + + /** + * References the root document, i.e. the top-level document, currently being processed in the aggregation pipeline + * stage. + */ + public static function root(): ResolvesToObject + { + return new Expression\Variable('ROOT'); + } + + /** + * A variable that stores the metadata results of an Atlas Search query. In all supported aggregation pipeline + * stages, a field set to the variable $$SEARCH_META returns the metadata results for the query. + * For an example of its usage, see Atlas Search facet and count. + * + * @see https://www.mongodb.com/docs/atlas/atlas-search/query-syntax/#metadata-result-types + */ + public static function searchMeta(): ResolvesToObject + { + return new Expression\Variable('SEARCH_META'); + } + + /** + * Returns the roles assigned to the current user. + * For use cases that include USER_ROLES, see the find, aggregation, view, updateOne, updateMany, and findAndModify + * examples. + * + * New in MongoDB 7.0. + */ + public static function userRoles(): ResolvesToArray + { + return new Expression\Variable('USER_ROLES'); + } + + /** + * User-defined variable that can be used to store any BSON type. + * + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/let/ + */ + public static function variable(string $name): Expression\Variable + { + return new Expression\Variable($name); + } + + private function __construct() + { + // This class cannot be instantiated + } +} diff --git a/src/BulkWriteResult.php b/src/BulkWriteResult.php index dec8d59de..43c60ea2d 100644 --- a/src/BulkWriteResult.php +++ b/src/BulkWriteResult.php @@ -17,28 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a bulk write operation. */ class BulkWriteResult { - /** @var WriteResult */ - private $writeResult; - - /** @var array */ - private $insertedIds; - - /** @var boolean */ - private $isAcknowledged; - - public function __construct(WriteResult $writeResult, array $insertedIds) + public function __construct(private WriteResult $writeResult, private array $insertedIds) { - $this->writeResult = $writeResult; - $this->insertedIds = $insertedIds; - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -47,16 +35,11 @@ public function __construct(WriteResult $writeResult, array $insertedIds) * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getDeletedCount() + public function getDeletedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getDeletedCount(); } /** @@ -65,16 +48,11 @@ public function getDeletedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -85,10 +63,8 @@ public function getInsertedCount() * the driver did not generate an ID), the index will contain its "_id" * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. - * - * @return array */ - public function getInsertedIds() + public function getInsertedIds(): array { return $this->insertedIds; } @@ -99,16 +75,11 @@ public function getInsertedIds() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getMatchedCount() + public function getMatchedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getMatchedCount(); } /** @@ -120,16 +91,11 @@ public function getMatchedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getModifiedCount() + public function getModifiedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getModifiedCount(); } /** @@ -138,16 +104,11 @@ public function getModifiedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedCount() + public function getUpsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedCount(); } /** @@ -161,16 +122,11 @@ public function getUpsertedCount() * This method should only be called if the write was acknowledged. * * @see BulkWriteResult::isAcknowledged() - * @return array - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedIds() + public function getUpsertedIds(): array { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedIds(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedIds(); } /** @@ -178,11 +134,9 @@ public function getUpsertedIds() * * If the update was not acknowledged, other fields from the WriteResult * (e.g. matchedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/ChangeStream.php b/src/ChangeStream.php index 33c461801..b9f8aed1c 100644 --- a/src/ChangeStream.php +++ b/src/ChangeStream.php @@ -18,40 +18,34 @@ namespace MongoDB; use Iterator; -use MongoDB\Driver\CursorId; +use MongoDB\BSON\Document; +use MongoDB\BSON\Int64; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Exception\ConnectionException; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Exception\ServerException; use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\ResumeTokenException; use MongoDB\Model\ChangeStreamIterator; -use ReturnTypeWillChange; +use function assert; use function call_user_func; use function in_array; /** * Iterator for a change stream. * - * @psalm-type ResumeCallable = callable(array|object|null, bool): ChangeStreamIterator - * - * @api * @see \MongoDB\Collection::watch() * @see https://mongodb.com/docs/manual/reference/method/db.watch/#mongodb-method-db.watch + * + * @psalm-type ResumeCallable = callable(array|object|null, bool): ChangeStreamIterator + * @template-implements Iterator */ class ChangeStream implements Iterator { - /** - * @deprecated 1.4 - * @todo Remove this in 2.0 (see: PHPLIB-360) - */ - public const CURSOR_NOT_FOUND = 43; - - /** @var int */ - private static $cursorNotFound = 43; + private const CURSOR_NOT_FOUND = 43; - /** @var int[] */ - private static $resumableErrorCodes = [ + private const RESUMABLE_ERROR_CODES = [ 6, // HostUnreachable 7, // HostNotFound 89, // NetworkTimeout @@ -71,51 +65,34 @@ class ChangeStream implements Iterator 133, // FailedToSatisfyReadPreference ]; - /** @var int */ - private static $wireVersionForResumableChangeStreamError = 9; + private const WIRE_VERSION_FOR_RESUMABLE_CHANGE_STREAM_ERROR = 9; /** @var ResumeCallable|null */ private $resumeCallable; - /** @var ChangeStreamIterator */ - private $iterator; - - /** @var integer */ - private $key = 0; + private int $key = 0; /** * Whether the change stream has advanced to its first result. This is used * to determine whether $key should be incremented after an iteration event. - * - * @var boolean */ - private $hasAdvanced = false; + private bool $hasAdvanced = false; - /** - * @internal - * - * @param ResumeCallable $resumeCallable - */ - public function __construct(ChangeStreamIterator $iterator, callable $resumeCallable) + /** @see https://php.net/iterator.current */ + public function current(): array|object|null { - $this->iterator = $iterator; - $this->resumeCallable = $resumeCallable; - } + $value = $this->iterator->current(); - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() - { - return $this->iterator->current(); + if (! $this->codec) { + return $value; + } + + assert($value instanceof Document); + + return $this->codec->decode($value); } - /** - * @return CursorId - */ - public function getCursorId() + public function getCursorId(): Int64 { return $this->iterator->getInnerIterator()->getId(); } @@ -126,20 +103,14 @@ public function getCursorId() * Null may be returned if no change documents have been iterated and the * server did not include a postBatchResumeToken in its aggregate or getMore * command response. - * - * @return array|object|null */ - public function getResumeToken() + public function getResumeToken(): array|object|null { return $this->iterator->getResumeToken(); } - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iterator.key */ + public function key(): ?int { if ($this->valid()) { return $this->key; @@ -150,11 +121,9 @@ public function key() /** * @see https://php.net/iterator.next - * @return void * @throws ResumeTokenException */ - #[ReturnTypeWillChange] - public function next() + public function next(): void { try { $this->iterator->next(); @@ -166,11 +135,9 @@ public function next() /** * @see https://php.net/iterator.rewind - * @return void * @throws ResumeTokenException */ - #[ReturnTypeWillChange] - public function rewind() + public function rewind(): void { try { $this->iterator->rewind(); @@ -183,20 +150,30 @@ public function rewind() } } + /** @see https://php.net/iterator.valid */ + public function valid(): bool + { + return $this->iterator->valid(); + } + /** - * @see https://php.net/iterator.valid - * @return boolean + * @internal + * + * @param ResumeCallable $resumeCallable */ - #[ReturnTypeWillChange] - public function valid() + public function __construct(private ChangeStreamIterator $iterator, callable $resumeCallable, private ?DocumentCodec $codec = null) { - return $this->iterator->valid(); + $this->resumeCallable = $resumeCallable; + + if ($codec) { + $this->iterator->getInnerIterator()->setTypeMap(['root' => 'bson']); + } } /** * Determines if an exception is a resumable error. * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resumable-error + * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.md#resumable-error */ private function isResumableError(RuntimeException $exception): bool { @@ -208,15 +185,15 @@ private function isResumableError(RuntimeException $exception): bool return false; } - if ($exception->getCode() === self::$cursorNotFound) { + if ($exception->getCode() === self::CURSOR_NOT_FOUND) { return true; } - if (server_supports_feature($this->iterator->getServer(), self::$wireVersionForResumableChangeStreamError)) { + if (server_supports_feature($this->iterator->getServer(), self::WIRE_VERSION_FOR_RESUMABLE_CHANGE_STREAM_ERROR)) { return $exception->hasErrorLabel('ResumableChangeStreamError'); } - return in_array($exception->getCode(), self::$resumableErrorCodes); + return in_array($exception->getCode(), self::RESUMABLE_ERROR_CODES); } /** @@ -233,7 +210,8 @@ private function onIteration(bool $incrementKey): void * have been received in the last response. Therefore, we can unset the * resumeCallable. This will free any reference to Watch as well as the * only reference to any implicit session created therein. */ - if ((string) $this->getCursorId() === '0') { + // Use a type-unsafe comparison to compare with Int64 instances + if ($this->getCursorId() == 0) { $this->resumeCallable = null; } @@ -255,7 +233,7 @@ private function onIteration(bool $incrementKey): void */ private function resume(): void { - if (! $this->resumeCallable) { + if ($this->resumeCallable === null) { throw new BadMethodCallException('Cannot resume a closed change stream.'); } @@ -263,6 +241,10 @@ private function resume(): void $this->iterator->rewind(); + if ($this->codec) { + $this->iterator->getInnerIterator()->setTypeMap(['root' => 'bson']); + } + $this->onIteration($this->hasAdvanced); } diff --git a/src/Client.php b/src/Client.php index bb12c3776..938da2f1a 100644 --- a/src/Client.php +++ b/src/Client.php @@ -17,12 +17,20 @@ namespace MongoDB; +use Composer\InstalledVersions; use Iterator; -use Jean85\PrettyVersions; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Builder\BuilderEncoder; +use MongoDB\Builder\Pipeline; +use MongoDB\Codec\Encoder; +use MongoDB\Driver\BulkWriteCommand; +use MongoDB\Driver\BulkWriteCommandResult; use MongoDB\Driver\ClientEncryption; use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Manager; +use MongoDB\Driver\Monitoring\Subscriber; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Session; @@ -32,50 +40,50 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; -use MongoDB\Model\DatabaseInfoIterator; +use MongoDB\Model\DatabaseInfo; +use MongoDB\Operation\ClientBulkWriteCommand; use MongoDB\Operation\DropDatabase; use MongoDB\Operation\ListDatabaseNames; use MongoDB\Operation\ListDatabases; use MongoDB\Operation\Watch; +use stdClass; +use Stringable; use Throwable; +use function array_diff_key; use function is_array; use function is_string; -class Client +class Client implements Stringable { public const DEFAULT_URI = 'mongodb://127.0.0.1/'; - /** @var array */ - private static $defaultTypeMap = [ + private const DEFAULT_TYPE_MAP = [ 'array' => BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var string */ - private static $handshakeSeparator = ' / '; + private const HANDSHAKE_SEPARATOR = '/'; - /** @var string|null */ - private static $version; + private static ?string $version = null; - /** @var Manager */ - private $manager; + private Manager $manager; - /** @var ReadConcern */ - private $readConcern; + private ReadConcern $readConcern; - /** @var ReadPreference */ - private $readPreference; + private ReadPreference $readPreference; - /** @var string */ - private $uri; + private string $uri; - /** @var array */ - private $typeMap; + private array $typeMap; - /** @var WriteConcern */ - private $writeConcern; + /** @psalm-var Encoder */ + private readonly Encoder $builderEncoder; + + private WriteConcern $writeConcern; + + private bool $autoEncryptionEnabled; /** * Constructs a new Client instance. @@ -86,6 +94,9 @@ class Client * * Supported driver-specific options: * + * * builderEncoder (MongoDB\Codec\Encoder): Encoder for query and + * aggregation builders. If not given, the default encoder will be used. + * * * typeMap (array): Default type map for cursors and BSON documents. * * Other options are documented in MongoDB\Driver\Manager::__construct(). @@ -102,26 +113,32 @@ class Client */ public function __construct(?string $uri = null, array $uriOptions = [], array $driverOptions = []) { - $driverOptions += ['typeMap' => self::$defaultTypeMap]; + $driverOptions += ['typeMap' => self::DEFAULT_TYPE_MAP]; if (! is_array($driverOptions['typeMap'])) { throw InvalidArgumentException::invalidType('"typeMap" driver option', $driverOptions['typeMap'], 'array'); } - if (isset($driverOptions['autoEncryption']['keyVaultClient'])) { - if ($driverOptions['autoEncryption']['keyVaultClient'] instanceof self) { - $driverOptions['autoEncryption']['keyVaultClient'] = $driverOptions['autoEncryption']['keyVaultClient']->manager; - } elseif (! $driverOptions['autoEncryption']['keyVaultClient'] instanceof Manager) { - throw InvalidArgumentException::invalidType('"keyVaultClient" autoEncryption option', $driverOptions['autoEncryption']['keyVaultClient'], [self::class, Manager::class]); - } + if (isset($driverOptions['autoEncryption']) && is_array($driverOptions['autoEncryption'])) { + $driverOptions['autoEncryption'] = $this->prepareEncryptionOptions($driverOptions['autoEncryption']); + } + + if (isset($driverOptions['builderEncoder']) && ! $driverOptions['builderEncoder'] instanceof Encoder) { + throw InvalidArgumentException::invalidType('"builderEncoder" option', $driverOptions['builderEncoder'], Encoder::class); } $driverOptions['driver'] = $this->mergeDriverInfo($driverOptions['driver'] ?? []); $this->uri = $uri ?? self::DEFAULT_URI; + $this->builderEncoder = $driverOptions['builderEncoder'] ?? new BuilderEncoder(); $this->typeMap = $driverOptions['typeMap']; - unset($driverOptions['typeMap']); + /* Database and Collection objects may need to know whether auto + * encryption is enabled for dropping collections. Track this via an + * internal option until PHPC-2615 is implemented. */ + $this->autoEncryptionEnabled = isset($driverOptions['autoEncryption']['keyVaultNamespace']); + + $driverOptions = array_diff_key($driverOptions, ['builderEncoder' => 1, 'typeMap' => 1]); $this->manager = new Manager($uri, $uriOptions, $driverOptions); $this->readConcern = $this->manager->getReadConcern(); @@ -133,14 +150,14 @@ public function __construct(?string $uri = null, array $uriOptions = [], array $ * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'manager' => $this->manager, 'uri' => $this->uri, 'typeMap' => $this->typeMap, + 'builderEncoder' => $this->builderEncoder, 'writeConcern' => $this->writeConcern, ]; } @@ -155,40 +172,65 @@ public function __debugInfo() * @see https://php.net/oop5.overloading#object.get * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $databaseName Name of the database to select - * @return Database */ - public function __get(string $databaseName) + public function __get(string $databaseName): Database { - return $this->selectDatabase($databaseName); + return $this->getDatabase($databaseName); } /** * Return the connection string (i.e. URI). - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->uri; } /** - * Returns a ClientEncryption instance for explicit encryption and decryption + * Registers a monitoring event subscriber with this Client's Manager * - * @param array $options Encryption options + * @see Manager::addSubscriber() + */ + final public function addSubscriber(Subscriber $subscriber): void + { + $this->manager->addSubscriber($subscriber); + } + + /** + * Executes multiple write operations across multiple namespaces. * - * @return ClientEncryption + * @param BulkWriteCommand|ClientBulkWrite $bulk Assembled bulk write command or builder + * @param array $options Additional options + * @throws UnsupportedException if options are unsupported on the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + * @see ClientBulkWriteCommand::__construct() for supported options */ - public function createClientEncryption(array $options) + public function bulkWrite(BulkWriteCommand|ClientBulkWrite $bulk, array $options = []): BulkWriteCommandResult { - if (isset($options['keyVaultClient'])) { - if ($options['keyVaultClient'] instanceof self) { - $options['keyVaultClient'] = $options['keyVaultClient']->manager; - } elseif (! $options['keyVaultClient'] instanceof Manager) { - throw InvalidArgumentException::invalidType('"keyVaultClient" option', $options['keyVaultClient'], [self::class, Manager::class]); - } + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { + $options['writeConcern'] = $this->writeConcern; + } + + if ($bulk instanceof ClientBulkWrite) { + $bulk = $bulk->bulkWriteCommand; } + $operation = new ClientBulkWriteCommand($bulk, $options); + $server = select_server_for_write($this->manager, $options); + + return $operation->execute($server); + } + + /** + * Returns a ClientEncryption instance for explicit encryption and decryption + * + * @param array $options Encryption options + */ + public function createClientEncryption(array $options): ClientEncryption + { + $options = $this->prepareEncryptionOptions($options); + return $this->manager->createClientEncryption($options); } @@ -198,18 +240,13 @@ public function createClientEncryption(array $options) * @see DropDatabase::__construct() for supported options * @param string $databaseName Database name * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropDatabase(string $databaseName, array $options = []) + public function dropDatabase(string $databaseName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); + $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; @@ -217,15 +254,44 @@ public function dropDatabase(string $databaseName, array $options = []) $operation = new DropDatabase($databaseName, $options); - return $operation->execute($server); + $operation->execute($server); } /** - * Return the Manager. + * Returns a collection instance. + * + * If the collection does not exist in the database, it is not created when + * invoking this method. + * + * @see Collection::__construct() for supported options + * @throws InvalidArgumentException for parameter/option parsing errors + */ + public function getCollection(string $databaseName, string $collectionName, array $options = []): Collection + { + $options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder, 'autoEncryptionEnabled' => $this->autoEncryptionEnabled]; + + return new Collection($this->manager, $databaseName, $collectionName, $options); + } + + /** + * Returns a database instance. * - * @return Manager + * If the database does not exist on the server, it is not created when + * invoking this method. + * + * @see Database::__construct() for supported options + */ + public function getDatabase(string $databaseName, array $options = []): Database + { + $options += ['typeMap' => $this->typeMap, 'builderEncoder' => $this->builderEncoder, 'autoEncryptionEnabled' => $this->autoEncryptionEnabled]; + + return new Database($this->manager, $databaseName, $options); + } + + /** + * Return the Manager. */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -234,29 +300,24 @@ public function getManager() * Return the read concern for this client. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this client. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this client. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -265,9 +326,8 @@ public function getTypeMap() * Return the write concern for this client. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -276,6 +336,7 @@ public function getWriteConcern() * List database names. * * @see ListDatabaseNames::__construct() for supported options + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -292,12 +353,12 @@ public function listDatabaseNames(array $options = []): Iterator * List databases. * * @see ListDatabases::__construct() for supported options - * @return DatabaseInfoIterator + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listDatabases(array $options = []) + public function listDatabases(array $options = []): Iterator { $operation = new ListDatabases($options); $server = select_server($this->manager, $options); @@ -305,6 +366,16 @@ public function listDatabases(array $options = []) return $operation->execute($server); } + /** + * Unregisters a monitoring event subscriber with this Client's Manager + * + * @see Manager::removeSubscriber() + */ + final public function removeSubscriber(Subscriber $subscriber): void + { + $this->manager->removeSubscriber($subscriber); + } + /** * Select a collection. * @@ -312,14 +383,11 @@ public function listDatabases(array $options = []) * @param string $databaseName Name of the database containing the collection * @param string $collectionName Name of the collection to select * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection(string $databaseName, string $collectionName, array $options = []) + public function selectCollection(string $databaseName, string $collectionName, array $options = []): Collection { - $options += ['typeMap' => $this->typeMap]; - - return new Collection($this->manager, $databaseName, $collectionName, $options); + return $this->getCollection($databaseName, $collectionName, $options); } /** @@ -328,14 +396,11 @@ public function selectCollection(string $databaseName, string $collectionName, a * @see Database::__construct() for supported options * @param string $databaseName Name of the database to select * @param array $options Database constructor options - * @return Database * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectDatabase(string $databaseName, array $options = []) + public function selectDatabase(string $databaseName, array $options = []): Database { - $options += ['typeMap' => $this->typeMap]; - - return new Database($this->manager, $databaseName, $options); + return $this->getDatabase($databaseName, $options); } /** @@ -343,9 +408,8 @@ public function selectDatabase(string $databaseName, array $options = []) * * @see https://php.net/manual/en/mongodb-driver-manager.startsession.php * @param array $options Session options - * @return Session */ - public function startSession(array $options = []) + public function startSession(array $options = []): Session { return $this->manager->startSession($options); } @@ -354,13 +418,18 @@ public function startSession(array $options = []) * Create a change stream for watching changes to the cluster. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options - * @return ChangeStream * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array $pipeline = [], array $options = []): ChangeStream { + if (is_builder_pipeline($pipeline)) { + $pipeline = new Pipeline(...$pipeline); + } + + $pipeline = $this->builderEncoder->encodeIfSupported($pipeline); + if (! isset($options['readPreference']) && ! is_in_transaction($options)) { $options['readPreference'] = $this->readPreference; } @@ -384,9 +453,9 @@ private static function getVersion(): string { if (self::$version === null) { try { - self::$version = PrettyVersions::getVersion('mongodb/mongodb')->getPrettyVersion(); - } catch (Throwable $t) { - return 'unknown'; + self::$version = InstalledVersions::getPrettyVersion('mongodb/mongodb') ?? 'unknown'; + } catch (Throwable) { + self::$version = 'error'; } } @@ -405,7 +474,7 @@ private function mergeDriverInfo(array $driver): array throw InvalidArgumentException::invalidType('"name" handshake option', $driver['name'], 'string'); } - $mergedDriver['name'] .= self::$handshakeSeparator . $driver['name']; + $mergedDriver['name'] .= self::HANDSHAKE_SEPARATOR . $driver['name']; } if (isset($driver['version'])) { @@ -413,7 +482,7 @@ private function mergeDriverInfo(array $driver): array throw InvalidArgumentException::invalidType('"version" handshake option', $driver['version'], 'string'); } - $mergedDriver['version'] .= self::$handshakeSeparator . $driver['version']; + $mergedDriver['version'] .= self::HANDSHAKE_SEPARATOR . $driver['version']; } if (isset($driver['platform'])) { @@ -422,4 +491,26 @@ private function mergeDriverInfo(array $driver): array return $mergedDriver; } + + private function prepareEncryptionOptions(array $options): array + { + if (isset($options['keyVaultClient'])) { + if ($options['keyVaultClient'] instanceof self) { + $options['keyVaultClient'] = $options['keyVaultClient']->manager; + } elseif (! $options['keyVaultClient'] instanceof Manager) { + throw InvalidArgumentException::invalidType('"keyVaultClient" option', $options['keyVaultClient'], [self::class, Manager::class]); + } + } + + // The server requires an empty document for automatic credentials. + if (isset($options['kmsProviders']) && is_array($options['kmsProviders'])) { + foreach ($options['kmsProviders'] as $name => $provider) { + if ($provider === []) { + $options['kmsProviders'][$name] = new stdClass(); + } + } + } + + return $options; + } } diff --git a/src/ClientBulkWrite.php b/src/ClientBulkWrite.php new file mode 100644 index 000000000..f11452d3a --- /dev/null +++ b/src/ClientBulkWrite.php @@ -0,0 +1,249 @@ + */ + private readonly Encoder $builderEncoder, + private readonly ?DocumentCodec $codec, + ) { + } + + #[NoDiscard] + public static function createWithCollection(Collection $collection, array $options = []): self + { + $options += ['ordered' => true]; + + if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { + throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); + } + + if (isset($options['let']) && ! is_document($options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); + } + + if (! is_bool($options['ordered'])) { + throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); + } + + if (isset($options['verboseResults']) && ! is_bool($options['verboseResults'])) { + throw InvalidArgumentException::invalidType('"verboseResults" option', $options['verboseResults'], 'boolean'); + } + + return new self( + new BulkWriteCommand($options), + $collection->getManager(), + $collection->getNamespace(), + $collection->getBuilderEncoder(), + $collection->getCodec(), + ); + } + + public function deleteMany(array|object $filter, array $options = []): self + { + $filter = $this->builderEncoder->encodeIfSupported($filter); + + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); + } + + $this->bulkWriteCommand->deleteMany($this->namespace, $filter, $options); + + return $this; + } + + public function deleteOne(array|object $filter, array $options = []): self + { + $filter = $this->builderEncoder->encodeIfSupported($filter); + + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); + } + + $this->bulkWriteCommand->deleteOne($this->namespace, $filter, $options); + + return $this; + } + + public function insertOne(array|object $document, mixed &$id = null): self + { + if ($this->codec) { + $document = $this->codec->encode($document); + } + + // Capture the document's _id, which may have been generated, in an optional output variable + /** @var mixed $id */ + $id = $this->bulkWriteCommand->insertOne($this->namespace, $document); + + return $this; + } + + public function replaceOne(array|object $filter, array|object $replacement, array $options = []): self + { + $filter = $this->builderEncoder->encodeIfSupported($filter); + + if ($this->codec) { + $replacement = $this->codec->encode($replacement); + } + + // Treat empty arrays as replacement documents for BC + if ($replacement === []) { + $replacement = (object) $replacement; + } + + if (is_first_key_operator($replacement)) { + throw new InvalidArgumentException('First key in $replacement is an update operator'); + } + + if (is_pipeline($replacement, true)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } + + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); + } + + if (isset($options['sort']) && ! is_document($options['sort'])) { + throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']); + } + + if (isset($options['upsert']) && ! is_bool($options['upsert'])) { + throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); + } + + $this->bulkWriteCommand->replaceOne($this->namespace, $filter, $replacement, $options); + + return $this; + } + + public function updateMany(array|object $filter, array|object $update, array $options = []): self + { + $filter = $this->builderEncoder->encodeIfSupported($filter); + $update = $this->builderEncoder->encodeIfSupported($update); + + if (! is_first_key_operator($update) && ! is_pipeline($update)) { + throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update'); + } + + if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { + throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); + } + + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); + } + + if (isset($options['upsert']) && ! is_bool($options['upsert'])) { + throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); + } + + $this->bulkWriteCommand->updateMany($this->namespace, $filter, $update, $options); + + return $this; + } + + public function updateOne(array|object $filter, array|object $update, array $options = []): self + { + $filter = $this->builderEncoder->encodeIfSupported($filter); + $update = $this->builderEncoder->encodeIfSupported($update); + + if (! is_first_key_operator($update) && ! is_pipeline($update)) { + throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update'); + } + + if (isset($options['arrayFilters']) && ! is_array($options['arrayFilters'])) { + throw InvalidArgumentException::invalidType('"arrayFilters" option', $options['arrayFilters'], 'array'); + } + + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); + } + + if (isset($options['sort']) && ! is_document($options['sort'])) { + throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']); + } + + if (isset($options['upsert']) && ! is_bool($options['upsert'])) { + throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); + } + + $this->bulkWriteCommand->updateOne($this->namespace, $filter, $update, $options); + + return $this; + } + + #[NoDiscard] + public function withCollection(Collection $collection): self + { + /* Prohibit mixing Collections associated with different Manager + * objects. This is not technically necessary, since the Collection is + * only used to derive a namespace and encoding options; however, it + * may prevent a user from inadvertently mixing writes destined for + * different deployments. */ + if ($this->manager !== $collection->getManager()) { + throw new InvalidArgumentException('$collection is associated with a different MongoDB\Driver\Manager'); + } + + return new self( + $this->bulkWriteCommand, + $this->manager, + $collection->getNamespace(), + $collection->getBuilderEncoder(), + $collection->getCodec(), + ); + } +} diff --git a/src/Codec/Codec.php b/src/Codec/Codec.php new file mode 100644 index 000000000..26fddf2cb --- /dev/null +++ b/src/Codec/Codec.php @@ -0,0 +1,31 @@ + + * @template-extends Encoder + */ +interface Codec extends Decoder, Encoder +{ +} diff --git a/src/Codec/DecodeIfSupported.php b/src/Codec/DecodeIfSupported.php new file mode 100644 index 000000000..ea8dde15c --- /dev/null +++ b/src/Codec/DecodeIfSupported.php @@ -0,0 +1,43 @@ +canDecode($value) ? $this->decode($value) : $value; + } +} diff --git a/src/Codec/Decoder.php b/src/Codec/Decoder.php new file mode 100644 index 000000000..37ff9b263 --- /dev/null +++ b/src/Codec/Decoder.php @@ -0,0 +1,54 @@ + + */ +interface DocumentCodec extends Codec +{ + /** + * @psalm-param Document $value + * @psalm-return ObjectType + * @throws UnsupportedValueException if the decoder does not support the value + */ + public function decode(mixed $value): object; + + /** + * @psalm-param ObjectType $value + * @throws UnsupportedValueException if the encoder does not support the value + */ + public function encode(mixed $value): Document; +} diff --git a/src/Codec/EncodeIfSupported.php b/src/Codec/EncodeIfSupported.php new file mode 100644 index 000000000..2ce1fcf53 --- /dev/null +++ b/src/Codec/EncodeIfSupported.php @@ -0,0 +1,43 @@ +canEncode($value) ? $this->encode($value) : $value; + } +} diff --git a/src/Codec/Encoder.php b/src/Codec/Encoder.php new file mode 100644 index 000000000..c8ee0917b --- /dev/null +++ b/src/Codec/Encoder.php @@ -0,0 +1,54 @@ + BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; + private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8; - /** @var string */ - private $collectionName; + /** @psalm-var Encoder */ + private readonly Encoder $builderEncoder; - /** @var string */ - private $databaseName; + private ?DocumentCodec $codec = null; - /** @var Manager */ - private $manager; + private ReadConcern $readConcern; - /** @var ReadConcern */ - private $readConcern; + private ReadPreference $readPreference; - /** @var ReadPreference */ - private $readPreference; + private array $typeMap; - /** @var array */ - private $typeMap; + private WriteConcern $writeConcern; - /** @var WriteConcern */ - private $writeConcern; + private bool $autoEncryptionEnabled; /** * Constructs new Collection instance. @@ -107,6 +112,12 @@ class Collection * * Supported options: * + * * builderEncoder (MongoDB\Codec\Encoder): Encoder for query and + * aggregation builders. If not given, the default encoder will be used. + * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * readConcern (MongoDB\Driver\ReadConcern): The default read concern to * use for collection operations. Defaults to the Manager's read concern. * @@ -126,7 +137,7 @@ class Collection * @param array $options Collection options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, string $databaseName, string $collectionName, array $options = []) + public function __construct(private Manager $manager, private string $databaseName, private string $collectionName, array $options = []) { if (strlen($databaseName) < 1) { throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); @@ -136,6 +147,14 @@ public function __construct(Manager $manager, string $databaseName, string $coll throw new InvalidArgumentException('$collectionName is invalid: ' . $collectionName); } + if (isset($options['builderEncoder']) && ! $options['builderEncoder'] instanceof Encoder) { + throw InvalidArgumentException::invalidType('"builderEncoder" option', $options['builderEncoder'], Encoder::class); + } + + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); + } + if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); } @@ -152,24 +171,29 @@ public function __construct(Manager $manager, string $databaseName, string $coll throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - $this->manager = $manager; - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; + if (isset($options['autoEncryptionEnabled']) && ! is_bool($options['autoEncryptionEnabled'])) { + throw InvalidArgumentException::invalidType('"autoEncryptionEnabled" option', $options['autoEncryptionEnabled'], 'boolean'); + } + + $this->builderEncoder = $options['builderEncoder'] ?? new BuilderEncoder(); + $this->codec = $options['codec'] ?? null; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); + $this->autoEncryptionEnabled = $options['autoEncryptionEnabled'] ?? false; } /** * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ + 'builderEncoder' => $this->builderEncoder, + 'codec' => $this->codec, 'collectionName' => $this->collectionName, 'databaseName' => $this->databaseName, 'manager' => $this->manager, @@ -184,9 +208,8 @@ public function __debugInfo() * Return the collection namespace (e.g. "db.collection"). * * @see https://mongodb.com/docs/manual/core/databases-and-collections/ - * @return string */ - public function __toString() + public function __toString(): string { return $this->databaseName . '.' . $this->collectionName; } @@ -194,27 +217,25 @@ public function __toString() /** * Executes an aggregation framework pipeline on the collection. * - * Note: this method's return value depends on the MongoDB server version - * and the "useCursor" option. If "useCursor" is true, a Cursor will be - * returned; otherwise, an ArrayIterator is returned, which wraps the - * "result" array from the command response document. - * * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return Traversable + * @param array|Pipeline $pipeline Aggregation pipeline + * @param array $options Command options * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function aggregate(array $pipeline, array $options = []) + public function aggregate(array|Pipeline $pipeline, array $options = []): CursorInterface { + if (is_array($pipeline) && is_builder_pipeline($pipeline)) { + $pipeline = new Pipeline(...$pipeline); + } + + $pipeline = $this->builderEncoder->encodeIfSupported($pipeline); + $hasWriteStage = is_last_pipeline_operator_write($pipeline); - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } + $options = $this->inheritReadPreference($options); $server = $hasWriteStage ? select_server_for_aggregate_write_stage($this->manager, $options) @@ -222,23 +243,15 @@ public function aggregate(array $pipeline, array $options = []) /* MongoDB 4.2 and later supports a read concern when an $out stage is * being used, but earlier versions do not. - * - * A read concern is also not compatible with transactions. */ - if ( - ! isset($options['readConcern']) && - ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) - ) { - $options['readConcern'] = $this->readConcern; + if (! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE)) { + $options = $this->inheritReadConcern($options); } - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $options = $this->inheritCodecOrTypeMap($options); - if ($hasWriteStage && ! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; + if ($hasWriteStage) { + $options = $this->inheritWriteOptions($options); } $operation = new Aggregate($this->databaseName, $this->collectionName, $pipeline, $options); @@ -252,21 +265,19 @@ public function aggregate(array $pipeline, array $options = []) * @see BulkWrite::__construct() for supported options * @param array[] $operations List of write operations * @param array $options Command options - * @return BulkWriteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function bulkWrite(array $operations, array $options = []) + public function bulkWrite(array $operations, array $options = []): BulkWriteResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritBuilderEncoder($options); + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodec($options); $operation = new BulkWrite($this->databaseName, $this->collectionName, $operations, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -275,7 +286,6 @@ public function bulkWrite(array $operations, array $options = []) * @see Count::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors @@ -283,21 +293,14 @@ public function bulkWrite(array $operations, array $options = []) * * @deprecated 1.4 */ - public function count($filter = [], array $options = []) + public function count(array|object $filter = [], array $options = []): int { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritReadOptions($options); $operation = new Count($this->databaseName, $this->collectionName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -306,27 +309,19 @@ public function count($filter = [], array $options = []) * @see CountDocuments::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function countDocuments($filter = [], array $options = []) + public function countDocuments(array|object $filter = [], array $options = []): int { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritReadOptions($options); $operation = new CountDocuments($this->databaseName, $this->collectionName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -342,13 +337,13 @@ public function countDocuments($filter = [], array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createIndex($key, array $options = []) + public function createIndex(array|object $key, array $options = []): string { - $commandOptionKeys = ['commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1]; - $indexOptions = array_diff_key($options, $commandOptionKeys); - $commandOptions = array_intersect_key($options, $commandOptionKeys); + $operationOptionKeys = ['comment' => 1, 'commitQuorum' => 1, 'maxTimeMS' => 1, 'session' => 1, 'writeConcern' => 1]; + $indexOptions = array_diff_key($options, $operationOptionKeys); + $operationOptions = array_intersect_key($options, $operationOptionKeys); - return current($this->createIndexes([['key' => $key] + $indexOptions], $commandOptions)); + return current($this->createIndexes([['key' => $key] + $indexOptions], $operationOptions)); } /** @@ -378,16 +373,70 @@ public function createIndex($key, array $options = []) * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createIndexes(array $indexes, array $options = []) + public function createIndexes(array $indexes, array $options = []): array { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); $operation = new CreateIndexes($this->databaseName, $this->collectionName, $indexes, $options); + return $operation->execute(select_server_for_write($this->manager, $options)); + } + + /** + * Create an Atlas Search index for the collection. + * Only available when used against a 7.0+ Atlas cluster. + * + * @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/ + * @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/ + * @param array|object $definition Atlas Search index mapping definition + * @param array{comment?: mixed, name?: string, type?: string} $options Index and command options + * @return string The name of the created search index + * @throws UnsupportedException if options are not supported by the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function createSearchIndex(array|object $definition, array $options = []): string + { + $indexOptionKeys = ['name' => 1, 'type' => 1]; + /** @psalm-var array{name?: string, type?: string} */ + $indexOptions = array_intersect_key($options, $indexOptionKeys); + /** @psalm-var array{comment?: mixed} */ + $operationOptions = array_diff_key($options, $indexOptionKeys); + + $names = $this->createSearchIndexes([['definition' => $definition] + $indexOptions], $operationOptions); + + return current($names); + } + + /** + * Create one or more Atlas Search indexes for the collection. + * Only available when used against a 7.0+ Atlas cluster. + * + * Each element in the $indexes array must have "definition" document and they may have a "name" string. + * The name can be omitted for a single index, in which case a name will be the default. + * For example: + * + * $indexes = [ + * // Create a search index with the default name on a single field + * ['definition' => ['mappings' => ['dynamic' => false, 'fields' => ['title' => ['type' => 'string']]]]], + * // Create a named search index on all fields + * ['name' => 'search_all', 'definition' => ['mappings' => ['dynamic' => true]]], + * ]; + * + * @see https://www.mongodb.com/docs/manual/reference/command/createSearchIndexes/ + * @see https://mongodb.com/docs/manual/reference/method/db.collection.createSearchIndex/ + * @param list $indexes List of search index specifications + * @param array{comment?: mixed} $options Command options + * @return string[] The names of the created search indexes + * @throws UnsupportedException if options are not supported by the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function createSearchIndexes(array $indexes, array $options = []): array + { + $operation = new CreateSearchIndexes($this->databaseName, $this->collectionName, $indexes, $options); + $server = select_server_for_write($this->manager, $options); + return $operation->execute($server); } @@ -398,21 +447,18 @@ public function createIndexes(array $indexes, array $options = []) * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options - * @return DeleteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function deleteMany($filter, array $options = []) + public function deleteMany(array|object $filter, array $options = []): DeleteResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritWriteOptions($options); $operation = new DeleteMany($this->databaseName, $this->collectionName, $filter, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -422,21 +468,18 @@ public function deleteMany($filter, array $options = []) * @see https://mongodb.com/docs/manual/reference/command/delete/ * @param array|object $filter Query by which to delete documents * @param array $options Command options - * @return DeleteResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function deleteOne($filter, array $options = []) + public function deleteOne(array|object $filter, array $options = []): DeleteResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritWriteOptions($options); $operation = new DeleteOne($this->databaseName, $this->collectionName, $filter, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -446,31 +489,20 @@ public function deleteOne($filter, array $options = []) * @param string $fieldName Field for which to return distinct values * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return array * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function distinct(string $fieldName, $filter = [], array $options = []) + public function distinct(string $fieldName, array|object $filter = [], array $options = []): array { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritReadOptions($options); + $options = $this->inheritTypeMap($options); $operation = new Distinct($this->databaseName, $this->collectionName, $fieldName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -478,41 +510,26 @@ public function distinct(string $fieldName, $filter = [], array $options = []) * * @see DropCollection::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop(array $options = []) + public function drop(array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $this->collectionName, $this->manager) - ?? get_encrypted_fields_from_server($this->databaseName, $this->collectionName, $this->manager, $server) - ?? null; + $server = select_server_for_write($this->manager, $options); - if ($encryptedFields !== null) { - // encryptedFields is not passed to the drop command - unset($options['encryptedFields']); - - $encryptedFields = (array) $encryptedFields; - (new DropCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $this->collectionName . '.esc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $this->collectionName . '.ecoc'))->execute($server); + if ($this->autoEncryptionEnabled && ! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $this->collectionName, $this->manager) + ?? get_encrypted_fields_from_server($this->databaseName, $this->collectionName, $server); } - $operation = new DropCollection($this->databaseName, $this->collectionName, $options); + $operation = isset($options['encryptedFields']) + ? new DropEncryptedCollection($this->databaseName, $this->collectionName, $options) + : new DropCollection($this->databaseName, $this->collectionName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -521,12 +538,11 @@ public function drop(array $options = []) * @see DropIndexes::__construct() for supported options * @param string|IndexInfo $indexName Index name or model object * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropIndex($indexName, array $options = []) + public function dropIndex(string|IndexInfo $indexName, array $options = []): void { $indexName = (string) $indexName; @@ -534,19 +550,11 @@ public function dropIndex($indexName, array $options = []) throw new InvalidArgumentException('dropIndexes() must be used to drop multiple indexes'); } - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); $operation = new DropIndexes($this->databaseName, $this->collectionName, $indexName, $options); - return $operation->execute($server); + $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -554,26 +562,35 @@ public function dropIndex($indexName, array $options = []) * * @see DropIndexes::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropIndexes(array $options = []) + public function dropIndexes(array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $options = $this->inheritWriteOptions($options); - $server = select_server($this->manager, $options); + $operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options); - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $operation->execute(select_server_for_write($this->manager, $options)); + } - $operation = new DropIndexes($this->databaseName, $this->collectionName, '*', $options); + /** + * Drop a single Atlas Search index in the collection. + * Only available when used against a 7.0+ Atlas cluster. + * + * @param string $name Search index name + * @param array{comment?: mixed} $options Additional options + * @throws UnsupportedException if options are not supported by the selected server + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function dropSearchIndex(string $name, array $options = []): void + { + $operation = new DropSearchIndex($this->databaseName, $this->collectionName, $name); + $server = select_server_for_write($this->manager, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -581,27 +598,18 @@ public function dropIndexes(array $options = []) * * @see EstimatedDocumentCount::__construct() for supported options * @param array $options Command options - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function estimatedDocumentCount(array $options = []) + public function estimatedDocumentCount(array $options = []): int { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } + $options = $this->inheritReadOptions($options); $operation = new EstimatedDocumentCount($this->databaseName, $this->collectionName, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -611,26 +619,18 @@ public function estimatedDocumentCount(array $options = []) * @see https://mongodb.com/docs/manual/reference/command/explain/ * @param Explainable $explainable Command on which to run explain * @param array $options Additional options - * @return array|object * @throws UnsupportedException if explainable or options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function explain(Explainable $explainable, array $options = []) + public function explain(Explainable $explainable, array $options = []): array|object { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); + $options = $this->inheritReadPreference($options); + $options = $this->inheritTypeMap($options); $operation = new Explain($this->databaseName, $explainable, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -640,30 +640,19 @@ public function explain(Explainable $explainable, array $options = []) * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return Cursor * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function find($filter = [], array $options = []) + public function find(array|object $filter = [], array $options = []): CursorInterface { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritReadOptions($options); + $options = $this->inheritCodecOrTypeMap($options); $operation = new Find($this->databaseName, $this->collectionName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -673,30 +662,19 @@ public function find($filter = [], array $options = []) * @see https://mongodb.com/docs/manual/crud/#read-operations * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOne($filter = [], array $options = []) + public function findOne(array|object $filter = [], array $options = []): array|object|null { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritReadOptions($options); + $options = $this->inheritCodecOrTypeMap($options); $operation = new FindOne($this->databaseName, $this->collectionName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -708,27 +686,20 @@ public function findOne($filter = [], array $options = []) * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ * @param array|object $filter Query by which to filter documents * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndDelete($filter, array $options = []) + public function findOneAndDelete(array|object $filter, array $options = []): array|object|null { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodecOrTypeMap($options); $operation = new FindOneAndDelete($this->databaseName, $this->collectionName, $filter, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -745,27 +716,20 @@ public function findOneAndDelete($filter, array $options = []) * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndReplace($filter, $replacement, array $options = []) + public function findOneAndReplace(array|object $filter, array|object $replacement, array $options = []): array|object|null { - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodecOrTypeMap($options); $operation = new FindOneAndReplace($this->databaseName, $this->collectionName, $filter, $replacement, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -782,55 +746,54 @@ public function findOneAndReplace($filter, $replacement, array $options = []) * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOneAndUpdate($filter, $update, array $options = []) + public function findOneAndUpdate(array|object $filter, array|object $update, array $options = []): array|object|null { - $server = select_server($this->manager, $options); + $filter = $this->builderEncoder->encodeIfSupported($filter); + $update = $this->builderEncoder->encodeIfSupported($update); + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodecOrTypeMap($options); - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options); - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + return $operation->execute(select_server_for_write($this->manager, $options)); + } - $operation = new FindOneAndUpdate($this->databaseName, $this->collectionName, $filter, $update, $options); + /** @psalm-return Encoder */ + public function getBuilderEncoder(): Encoder + { + return $this->builderEncoder; + } - return $operation->execute($server); + public function getCodec(): ?DocumentCodec + { + return $this->codec; } /** * Return the collection name. - * - * @return string */ - public function getCollectionName() + public function getCollectionName(): string { return $this->collectionName; } /** * Return the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } /** * Return the Manager. - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -839,9 +802,8 @@ public function getManager() * Return the collection namespace. * * @see https://mongodb.com/docs/manual/reference/glossary/#term-namespace - * @return string */ - public function getNamespace() + public function getNamespace(): string { return $this->databaseName . '.' . $this->collectionName; } @@ -850,29 +812,24 @@ public function getNamespace() * Return the read concern for this collection. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this collection. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this collection. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -881,9 +838,8 @@ public function getTypeMap() * Return the write concern for this collection. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -893,22 +849,19 @@ public function getWriteConcern() * * @see InsertMany::__construct() for supported options * @see https://mongodb.com/docs/manual/reference/command/insert/ - * @param array[]|object[] $documents The documents to insert - * @param array $options Command options - * @return InsertManyResult + * @param list $documents The documents to insert + * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function insertMany(array $documents, array $options = []) + public function insertMany(array $documents, array $options = []): InsertManyResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodec($options); $operation = new InsertMany($this->databaseName, $this->collectionName, $documents, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -918,87 +871,51 @@ public function insertMany(array $documents, array $options = []) * @see https://mongodb.com/docs/manual/reference/command/insert/ * @param array|object $document The document to insert * @param array $options Command options - * @return InsertOneResult * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function insertOne($document, array $options = []) + public function insertOne(array|object $document, array $options = []): InsertOneResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodec($options); $operation = new InsertOne($this->databaseName, $this->collectionName, $document, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** * Returns information for all indexes for the collection. * * @see ListIndexes::__construct() for supported options - * @return IndexInfoIterator + * @return Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listIndexes(array $options = []) + public function listIndexes(array $options = []): Iterator { $operation = new ListIndexes($this->databaseName, $this->collectionName, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** - * Executes a map-reduce aggregation on the collection. + * Returns information for all Atlas Search indexes for the collection. + * Only available when used against a 7.0+ Atlas cluster. * - * @see MapReduce::__construct() for supported options - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/ - * @param JavascriptInterface $map Map function - * @param JavascriptInterface $reduce Reduce function - * @param string|array|object $out Output specification - * @param array $options Command options - * @return MapReduceResult - * @throws UnsupportedException if options are not supported by the selected server + * @param array $options Command options + * @return Countable&Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - * @throws UnexpectedValueException if the command response was malformed + * @see ListSearchIndexes::__construct() for supported options */ - public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, $out, array $options = []) + public function listSearchIndexes(array $options = []): Iterator { - $hasOutputCollection = ! is_mapreduce_output_inline($out); - - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; - } - - // Check if the out option is inline because we will want to coerce a primary read preference if not - if ($hasOutputCollection) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); - } + $options = $this->inheritTypeMap($options); + $operation = new ListSearchIndexes($this->databaseName, $this->collectionName, $options); $server = select_server($this->manager, $options); - /* A "majority" read concern is not compatible with inline output, so - * avoid providing the Collection's read concern if it would conflict. - * - * A read concern is also not compatible with transactions. - */ - if (! isset($options['readConcern']) && ! ($hasOutputCollection && $this->readConcern->getLevel() === ReadConcern::MAJORITY) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } - - $operation = new MapReduce($this->databaseName, $this->collectionName, $map, $reduce, $out, $options); - return $operation->execute($server); } @@ -1009,30 +926,21 @@ public function mapReduce(JavascriptInterface $map, JavascriptInterface $reduce, * @param string $toCollectionName New name of the collection * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + public function rename(string $toCollectionName, ?string $toDatabaseName = null, array $options = []): void { if (! isset($toDatabaseName)) { $toDatabaseName = $this->databaseName; } - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $options = $this->inheritWriteOptions($options); $operation = new RenameCollection($this->databaseName, $this->collectionName, $toDatabaseName, $toCollectionName, $options); - return $operation->execute($server); + $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -1043,21 +951,19 @@ public function rename(string $toCollectionName, ?string $toDatabaseName = null, * @param array|object $filter Query by which to filter documents * @param array|object $replacement Replacement document * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function replaceOne($filter, $replacement, array $options = []) + public function replaceOne(array|object $filter, array|object $replacement, array $options = []): UpdateResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $options = $this->inheritWriteOptions($options); + $options = $this->inheritCodec($options); $operation = new ReplaceOne($this->databaseName, $this->collectionName, $filter, $replacement, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -1068,21 +974,19 @@ public function replaceOne($filter, $replacement, array $options = []) * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched documents * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function updateMany($filter, $update, array $options = []) + public function updateMany(array|object $filter, array|object $update, array $options = []): UpdateResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $update = $this->builderEncoder->encodeIfSupported($update); + $options = $this->inheritWriteOptions($options); $operation = new UpdateMany($this->databaseName, $this->collectionName, $filter, $update, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); } /** @@ -1093,58 +997,62 @@ public function updateMany($filter, $update, array $options = []) * @param array|object $filter Query by which to filter documents * @param array|object $update Update to apply to the matched document * @param array $options Command options - * @return UpdateResult * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function updateOne($filter, $update, array $options = []) + public function updateOne(array|object $filter, array|object $update, array $options = []): UpdateResult { - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { - $options['writeConcern'] = $this->writeConcern; - } + $filter = $this->builderEncoder->encodeIfSupported($filter); + $update = $this->builderEncoder->encodeIfSupported($update); + $options = $this->inheritWriteOptions($options); $operation = new UpdateOne($this->databaseName, $this->collectionName, $filter, $update, $options); - $server = select_server($this->manager, $options); - return $operation->execute($server); + return $operation->execute(select_server_for_write($this->manager, $options)); + } + + /** + * Update a single Atlas Search index in the collection. + * Only available when used against a 7.0+ Atlas cluster. + * + * @param string $name Search index name + * @param array|object $definition Atlas Search index definition + * @param array{comment?: mixed} $options Command options + * @throws UnsupportedException if options are not supported by the selected server + * @throws InvalidArgumentException for parameter parsing errors + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function updateSearchIndex(string $name, array|object $definition, array $options = []): void + { + $operation = new UpdateSearchIndex($this->databaseName, $this->collectionName, $name, $definition, $options); + $server = select_server_for_write($this->manager, $options); + + $operation->execute($server); } /** * Create a change stream for watching changes to the collection. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return ChangeStream + * @param array|Pipeline $pipeline Aggregation pipeline + * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array|Pipeline $pipeline = [], array $options = []): ChangeStream { - if (! isset($options['readPreference']) && ! is_in_transaction($options)) { - $options['readPreference'] = $this->readPreference; + if (is_array($pipeline) && is_builder_pipeline($pipeline)) { + $pipeline = new Pipeline(...$pipeline); } - $server = select_server($this->manager, $options); - - /* Although change streams require a newer version of the server than - * read concerns, perform the usual wire version check before inheriting - * the collection's read concern. In the event that the server is too - * old, this makes it more likely that users will encounter an error - * related to change streams being unsupported instead of an - * UnsupportedException regarding use of the "readConcern" option from - * the Aggregate operation class. */ - if (! isset($options['readConcern']) && ! is_in_transaction($options)) { - $options['readConcern'] = $this->readConcern; - } + $pipeline = $this->builderEncoder->encodeIfSupported($pipeline); - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } + $options = $this->inheritReadOptions($options); + $options = $this->inheritCodecOrTypeMap($options); $operation = new Watch($this->manager, $this->databaseName, $this->collectionName, $pipeline, $options); - return $operation->execute($server); + return $operation->execute(select_server($this->manager, $options)); } /** @@ -1152,12 +1060,14 @@ public function watch(array $pipeline = [], array $options = []) * * @see Collection::__construct() for supported options * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function withOptions(array $options = []) + public function withOptions(array $options = []): Collection { $options += [ + 'autoEncryptionEnabled' => $this->autoEncryptionEnabled, + 'builderEncoder' => $this->builderEncoder, + 'codec' => $this->codec, 'readConcern' => $this->readConcern, 'readPreference' => $this->readPreference, 'typeMap' => $this->typeMap, @@ -1166,4 +1076,92 @@ public function withOptions(array $options = []) return new Collection($this->manager, $this->databaseName, $this->collectionName, $options); } + + private function inheritBuilderEncoder(array $options): array + { + return ['builderEncoder' => $this->builderEncoder] + $options; + } + + private function inheritCodec(array $options): array + { + // If the options contain a type map, don't inherit anything + if (isset($options['typeMap'])) { + return $options; + } + + if (! array_key_exists('codec', $options)) { + $options['codec'] = $this->codec; + } + + return $options; + } + + private function inheritCodecOrTypeMap(array $options): array + { + // If the options contain a type map, don't inherit anything + if (isset($options['typeMap'])) { + return $options; + } + + // If this collection does not use a codec, or if a codec was explicitly + // defined in the options, only inherit the type map (if possible) + if (! $this->codec || array_key_exists('codec', $options)) { + return $this->inheritTypeMap($options); + } + + // At this point, we know that we use a codec and the options array did + // not explicitly contain a codec, so we can inherit ours + $options['codec'] = $this->codec; + + return $options; + } + + private function inheritReadConcern(array $options): array + { + // ReadConcern and ReadPreference may not change within a transaction + if (! isset($options['readConcern']) && ! is_in_transaction($options)) { + $options['readConcern'] = $this->readConcern; + } + + return $options; + } + + private function inheritReadOptions(array $options): array + { + $options = $this->inheritReadConcern($options); + + return $this->inheritReadPreference($options); + } + + private function inheritReadPreference(array $options): array + { + // ReadConcern and ReadPreference may not change within a transaction + if (! isset($options['readPreference']) && ! is_in_transaction($options)) { + $options['readPreference'] = $this->readPreference; + } + + return $options; + } + + private function inheritTypeMap(array $options): array + { + // Only inherit the type map if no codec is used + if (! isset($options['typeMap']) && ! isset($options['codec'])) { + $options['typeMap'] = $this->typeMap; + } + + return $options; + } + + private function inheritWriteOptions(array $options): array + { + // WriteConcern may not change within a transaction + if (! is_in_transaction($options)) { + if (! isset($options['writeConcern'])) { + $options['writeConcern'] = $this->writeConcern; + } + } + + return $options; + } } diff --git a/src/Command/ListCollections.php b/src/Command/ListCollections.php index a970aeb07..d42250cd3 100644 --- a/src/Command/ListCollections.php +++ b/src/Command/ListCollections.php @@ -18,17 +18,15 @@ namespace MongoDB\Command; use MongoDB\Driver\Command; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\CachingIterator; -use MongoDB\Operation\Executable; -use function is_array; use function is_bool; use function is_integer; -use function is_object; +use function MongoDB\is_document; /** * Wrapper for the listCollections command. @@ -36,14 +34,8 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -class ListCollections implements Executable +final class ListCollections { - /** @var string */ - private $databaseName; - - /** @var array */ - private $options; - /** * Constructs a listCollections command. * @@ -73,14 +65,14 @@ class ListCollections implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, array $options = []) + public function __construct(private string $databaseName, private array $options = []) { if (isset($options['authorizedCollections']) && ! is_bool($options['authorizedCollections'])) { throw InvalidArgumentException::invalidType('"authorizedCollections" option', $options['authorizedCollections'], 'boolean'); } - if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) { - throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], 'array or object'); + if (isset($options['filter']) && ! is_document($options['filter'])) { + throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']); } if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { @@ -94,23 +86,21 @@ public function __construct(string $databaseName, array $options = []) if (isset($options['session']) && ! $options['session'] instanceof Session) { throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); } - - $this->databaseName = $databaseName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() + * @return CursorInterface * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server): CachingIterator + public function execute(Server $server): CursorInterface { + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - return new CachingIterator($cursor); + return $cursor; } /** diff --git a/src/Command/ListDatabases.php b/src/Command/ListDatabases.php index 4dabc6ed2..31ca5f741 100644 --- a/src/Command/ListDatabases.php +++ b/src/Command/ListDatabases.php @@ -23,13 +23,12 @@ use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use MongoDB\Operation\Executable; use function current; use function is_array; use function is_bool; use function is_integer; -use function is_object; +use function MongoDB\is_document; /** * Wrapper for the ListDatabases command. @@ -37,11 +36,8 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/listDatabases/ */ -class ListDatabases implements Executable +final class ListDatabases { - /** @var array */ - private $options; - /** * Constructs a listDatabases command. * @@ -70,14 +66,14 @@ class ListDatabases implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(array $options = []) + public function __construct(private array $options = []) { if (isset($options['authorizedDatabases']) && ! is_bool($options['authorizedDatabases'])) { throw InvalidArgumentException::invalidType('"authorizedDatabases" option', $options['authorizedDatabases'], 'boolean'); } - if (isset($options['filter']) && ! is_array($options['filter']) && ! is_object($options['filter'])) { - throw InvalidArgumentException::invalidType('"filter" option', $options['filter'], ['array', 'object']); + if (isset($options['filter']) && ! is_document($options['filter'])) { + throw InvalidArgumentException::expectedDocumentType('"filter" option', $options['filter']); } if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { @@ -91,14 +87,11 @@ public function __construct(array $options = []) if (isset($options['session']) && ! $options['session'] instanceof Session) { throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); } - - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() * @return array An array of database info structures * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) @@ -107,6 +100,7 @@ public function execute(Server $server): array { $cursor = $server->executeReadCommand('admin', $this->createCommand(), $this->createOptions()); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + $result = current($cursor->toArray()); if (! isset($result['databases']) || ! is_array($result['databases'])) { diff --git a/src/Database.php b/src/Database.php index 9bb2484dd..be5803e34 100644 --- a/src/Database.php +++ b/src/Database.php @@ -18,64 +18,68 @@ namespace MongoDB; use Iterator; -use MongoDB\Driver\Cursor; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Builder\BuilderEncoder; +use MongoDB\Builder\Pipeline; +use MongoDB\Codec\Encoder; +use MongoDB\Driver\ClientEncryption; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; +use MongoDB\Exception\CreateEncryptedCollectionException; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnsupportedException; use MongoDB\GridFS\Bucket; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; -use MongoDB\Model\CollectionInfoIterator; +use MongoDB\Model\CollectionInfo; use MongoDB\Operation\Aggregate; use MongoDB\Operation\CreateCollection; -use MongoDB\Operation\CreateIndexes; +use MongoDB\Operation\CreateEncryptedCollection; use MongoDB\Operation\DatabaseCommand; use MongoDB\Operation\DropCollection; use MongoDB\Operation\DropDatabase; +use MongoDB\Operation\DropEncryptedCollection; use MongoDB\Operation\ListCollectionNames; use MongoDB\Operation\ListCollections; use MongoDB\Operation\ModifyCollection; use MongoDB\Operation\RenameCollection; use MongoDB\Operation\Watch; -use Traversable; +use stdClass; +use Stringable; +use Throwable; use function is_array; +use function is_bool; use function strlen; -class Database +class Database implements Stringable { - /** @var array */ - private static $defaultTypeMap = [ + private const DEFAULT_TYPE_MAP = [ 'array' => BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var integer */ - private static $wireVersionForReadConcernWithWriteStage = 8; + private const WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE = 8; - /** @var string */ - private $databaseName; + /** @psalm-var Encoder */ + private readonly Encoder $builderEncoder; - /** @var Manager */ - private $manager; + private ReadConcern $readConcern; - /** @var ReadConcern */ - private $readConcern; + private ReadPreference $readPreference; - /** @var ReadPreference */ - private $readPreference; + private array $typeMap; - /** @var array */ - private $typeMap; + private WriteConcern $writeConcern; - /** @var WriteConcern */ - private $writeConcern; + private bool $autoEncryptionEnabled; /** * Constructs new Database instance. @@ -85,6 +89,9 @@ class Database * * Supported options: * + * * builderEncoder (MongoDB\Codec\Encoder): Encoder for query and + * aggregation builders. If not given, the default encoder will be used. + * * * readConcern (MongoDB\Driver\ReadConcern): The default read concern to * use for database operations and selected collections. Defaults to the * Manager's read concern. @@ -104,12 +111,16 @@ class Database * @param array $options Database options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, string $databaseName, array $options = []) + public function __construct(private Manager $manager, private string $databaseName, array $options = []) { if (strlen($databaseName) < 1) { throw new InvalidArgumentException('$databaseName is invalid: ' . $databaseName); } + if (isset($options['builderEncoder']) && ! $options['builderEncoder'] instanceof Encoder) { + throw InvalidArgumentException::invalidType('"builderEncoder" option', $options['builderEncoder'], Encoder::class); + } + if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); } @@ -126,23 +137,27 @@ public function __construct(Manager $manager, string $databaseName, array $optio throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - $this->manager = $manager; - $this->databaseName = $databaseName; + if (isset($options['autoEncryptionEnabled']) && ! is_bool($options['autoEncryptionEnabled'])) { + throw InvalidArgumentException::invalidType('"autoEncryptionEnabled" option', $options['autoEncryptionEnabled'], 'boolean'); + } + + $this->builderEncoder = $options['builderEncoder'] ?? new BuilderEncoder(); $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); + $this->autoEncryptionEnabled = $options['autoEncryptionEnabled'] ?? false; } /** * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ + 'builderEncoder' => $this->builderEncoder, 'databaseName' => $this->databaseName, 'manager' => $this->manager, 'readConcern' => $this->readConcern, @@ -162,19 +177,16 @@ public function __debugInfo() * @see https://php.net/oop5.overloading#object.get * @see https://php.net/types.string#language.types.string.parsing.complex * @param string $collectionName Name of the collection to select - * @return Collection */ - public function __get(string $collectionName) + public function __get(string $collectionName): Collection { - return $this->selectCollection($collectionName); + return $this->getCollection($collectionName); } /** * Return the database name. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->databaseName; } @@ -185,16 +197,21 @@ public function __toString() * and $listLocalSessions. Requires MongoDB >= 3.6 * * @see Aggregate::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return Traversable + * @param array|Pipeline $pipeline Aggregation pipeline + * @param array $options Command options * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function aggregate(array $pipeline, array $options = []) + public function aggregate(array|Pipeline $pipeline, array $options = []): CursorInterface { + if (is_array($pipeline) && is_builder_pipeline($pipeline)) { + $pipeline = new Pipeline(...$pipeline); + } + + $pipeline = $this->builderEncoder->encodeIfSupported($pipeline); + $hasWriteStage = is_last_pipeline_operator_write($pipeline); if (! isset($options['readPreference']) && ! is_in_transaction($options)) { @@ -213,7 +230,7 @@ public function aggregate(array $pipeline, array $options = []) if ( ! isset($options['readConcern']) && ! is_in_transaction($options) && - ( ! $hasWriteStage || server_supports_feature($server, self::$wireVersionForReadConcernWithWriteStage)) + ( ! $hasWriteStage || server_supports_feature($server, self::WIRE_VERSION_FOR_READ_CONCERN_WITH_WRITE_STAGE)) ) { $options['readConcern'] = $this->readConcern; } @@ -237,11 +254,10 @@ public function aggregate(array $pipeline, array $options = []) * @see DatabaseCommand::__construct() for supported options * @param array|object $command Command document * @param array $options Options for command execution - * @return Cursor * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function command($command, array $options = []) + public function command(array|object $command, array $options = []): CursorInterface { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; @@ -256,48 +272,73 @@ public function command($command, array $options = []) /** * Create a new collection explicitly. * + * If the "encryptedFields" option is specified, this method additionally + * creates related metadata collections and an index on the encrypted + * collection. + * * @see CreateCollection::__construct() for supported options - * @return array|object Command result document + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#create-collection-helper + * @see https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/manage-collections/ * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function createCollection(string $collectionName, array $options = []) + public function createCollection(string $collectionName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); - if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) - ?? null; + if (! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager); + } + + $operation = isset($options['encryptedFields']) + ? new CreateEncryptedCollection($this->databaseName, $collectionName, $options) + : new CreateCollection($this->databaseName, $collectionName, $options); - if ($encryptedFields !== null) { - // encryptedFields is passed to the create command - $options['encryptedFields'] = $encryptedFields; + $server = select_server_for_write($this->manager, $options); - $encryptedFields = (array) $encryptedFields; - $enxcolOptions = ['clusteredIndex' => ['key' => ['_id' => 1], 'unique' => true]]; - (new CreateCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc', $enxcolOptions))->execute($server); - (new CreateCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc', $enxcolOptions))->execute($server); - (new CreateCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc', $enxcolOptions))->execute($server); + $operation->execute($server); + } + + /** + * Create a new encrypted collection explicitly. + * + * The "encryptedFields" option is required. + * + * This method will automatically create data keys for any encrypted fields + * where "keyId" is null. A copy of the modified "encryptedFields" option + * will be returned in addition to the result from creating the collection. + * + * If any error is encountered creating data keys or the collection, a + * CreateEncryptedCollectionException will be thrown. The original exception + * and modified "encryptedFields" option can be accessed via the + * getPrevious() and getEncryptedFields() methods, respectively. + * + * @see CreateCollection::__construct() for supported options + * @return array The modified "encryptedFields" option + * @throws InvalidArgumentException for parameter/option parsing errors + * @throws CreateEncryptedCollectionException for any errors creating data keys or creating the collection + * @throws UnsupportedException if Queryable Encryption is not supported by the selected server + */ + public function createEncryptedCollection(string $collectionName, ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey, array $options): array + { + if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { + $options['writeConcern'] = $this->writeConcern; } - $operation = new CreateCollection($this->databaseName, $collectionName, $options); + $operation = new CreateEncryptedCollection($this->databaseName, $collectionName, $options); + $server = select_server_for_write($this->manager, $options); - $result = $operation->execute($server); + try { + $encryptedFields = $operation->createDataKeys($clientEncryption, $kmsProvider, $masterKey); + $operation->execute($server); - if ($encryptedFields !== null) { - (new CreateIndexes($this->databaseName, $collectionName, [['key' => ['__safeContent__' => 1]]]))->execute($server); + return $encryptedFields; + } catch (Throwable $e) { + throw new CreateEncryptedCollectionException($e, $encryptedFields ?? []); } - - return $result; } /** @@ -305,18 +346,13 @@ public function createCollection(string $collectionName, array $options = []) * * @see DropDatabase::__construct() for supported options * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop(array $options = []) + public function drop(array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); + $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; @@ -324,7 +360,7 @@ public function drop(array $options = []) $operation = new DropDatabase($this->databaseName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -333,59 +369,65 @@ public function drop(array $options = []) * @see DropCollection::__construct() for supported options * @param string $collectionName Collection name * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function dropCollection(string $collectionName, array $options = []) + public function dropCollection(string $collectionName, array $options = []): void { - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); + $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; } - $encryptedFields = $options['encryptedFields'] - ?? get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) - ?? get_encrypted_fields_from_server($this->databaseName, $collectionName, $this->manager, $server) - ?? null; + if ($this->autoEncryptionEnabled && ! isset($options['encryptedFields'])) { + $options['encryptedFields'] = get_encrypted_fields_from_driver($this->databaseName, $collectionName, $this->manager) + ?? get_encrypted_fields_from_server($this->databaseName, $collectionName, $server); + } - if ($encryptedFields !== null) { - // encryptedFields is not passed to the drop command - unset($options['encryptedFields']); + $operation = isset($options['encryptedFields']) + ? new DropEncryptedCollection($this->databaseName, $collectionName, $options) + : new DropCollection($this->databaseName, $collectionName, $options); - $encryptedFields = (array) $encryptedFields; - (new DropCollection($this->databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['eccCollection'] ?? 'enxcol_.' . $collectionName . '.ecc'))->execute($server); - (new DropCollection($this->databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'))->execute($server); - } + $operation->execute($server); + } - $operation = new DropCollection($this->databaseName, $collectionName, $options); + /** + * Returns a collection instance. + * + * If the collection does not exist in the database, it is not created when + * invoking this method. + * + * @see Collection::__construct() for supported options + * @throws InvalidArgumentException for parameter/option parsing errors + */ + public function getCollection(string $collectionName, array $options = []): Collection + { + $options += [ + 'builderEncoder' => $this->builderEncoder, + 'readConcern' => $this->readConcern, + 'readPreference' => $this->readPreference, + 'typeMap' => $this->typeMap, + 'writeConcern' => $this->writeConcern, + 'autoEncryptionEnabled' => $this->autoEncryptionEnabled, + ]; - return $operation->execute($server); + return new Collection($this->manager, $this->databaseName, $collectionName, $options); } /** * Returns the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } /** * Return the Manager. - * - * @return Manager */ - public function getManager() + public function getManager(): Manager { return $this->manager; } @@ -394,29 +436,24 @@ public function getManager() * Return the read concern for this database. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this database. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this database. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -425,9 +462,8 @@ public function getTypeMap() * Return the write concern for this database. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -436,6 +472,7 @@ public function getWriteConcern() * Returns the names of all collections in this database * * @see ListCollectionNames::__construct() for supported options + * @return Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ @@ -451,11 +488,11 @@ public function listCollectionNames(array $options = []): Iterator * Returns information for all collections in this database. * * @see ListCollections::__construct() for supported options - * @return CollectionInfoIterator + * @return Iterator * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function listCollections(array $options = []) + public function listCollections(array $options = []): Iterator { $operation = new ListCollections($this->databaseName, $options); $server = select_server($this->manager, $options); @@ -470,17 +507,16 @@ public function listCollections(array $options = []) * @param string $collectionName Collection or view to modify * @param array $collectionOptions Collection or view options to assign * @param array $options Command options - * @return array|object * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function modifyCollection(string $collectionName, array $collectionOptions, array $options = []) + public function modifyCollection(string $collectionName, array $collectionOptions, array $options = []): array|object { if (! isset($options['typeMap'])) { $options['typeMap'] = $this->typeMap; } - $server = select_server($this->manager, $options); + $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; @@ -499,22 +535,17 @@ public function modifyCollection(string $collectionName, array $collectionOption * @param string $toCollectionName New name of the collection * @param string|null $toDatabaseName New database name of the collection. Defaults to the original database. * @param array $options Additional options - * @return array|object Command result document * @throws UnsupportedException if options are unsupported on the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []) + public function renameCollection(string $fromCollectionName, string $toCollectionName, ?string $toDatabaseName = null, array $options = []): void { if (! isset($toDatabaseName)) { $toDatabaseName = $this->databaseName; } - if (! isset($options['typeMap'])) { - $options['typeMap'] = $this->typeMap; - } - - $server = select_server($this->manager, $options); + $server = select_server_for_write($this->manager, $options); if (! isset($options['writeConcern']) && ! is_in_transaction($options)) { $options['writeConcern'] = $this->writeConcern; @@ -522,7 +553,7 @@ public function renameCollection(string $fromCollectionName, string $toCollectio $operation = new RenameCollection($this->databaseName, $fromCollectionName, $toDatabaseName, $toCollectionName, $options); - return $operation->execute($server); + $operation->execute($server); } /** @@ -531,19 +562,11 @@ public function renameCollection(string $fromCollectionName, string $toCollectio * @see Collection::__construct() for supported options * @param string $collectionName Name of the collection to select * @param array $options Collection constructor options - * @return Collection * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectCollection(string $collectionName, array $options = []) + public function selectCollection(string $collectionName, array $options = []): Collection { - $options += [ - 'readConcern' => $this->readConcern, - 'readPreference' => $this->readPreference, - 'typeMap' => $this->typeMap, - 'writeConcern' => $this->writeConcern, - ]; - - return new Collection($this->manager, $this->databaseName, $collectionName, $options); + return $this->getCollection($collectionName, $options); } /** @@ -551,10 +574,9 @@ public function selectCollection(string $collectionName, array $options = []) * * @see Bucket::__construct() for supported options * @param array $options Bucket constructor options - * @return Bucket * @throws InvalidArgumentException for parameter/option parsing errors */ - public function selectGridFSBucket(array $options = []) + public function selectGridFSBucket(array $options = []): Bucket { $options += [ 'readConcern' => $this->readConcern, @@ -570,13 +592,18 @@ public function selectGridFSBucket(array $options = []) * Create a change stream for watching changes to the database. * * @see Watch::__construct() for supported options - * @param array $pipeline List of pipeline operations - * @param array $options Command options - * @return ChangeStream + * @param array|Pipeline $pipeline Aggregation pipeline + * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function watch(array $pipeline = [], array $options = []) + public function watch(array|Pipeline $pipeline = [], array $options = []): ChangeStream { + if (is_array($pipeline) && is_builder_pipeline($pipeline)) { + $pipeline = new Pipeline(...$pipeline); + } + + $pipeline = $this->builderEncoder->encodeIfSupported($pipeline); + if (! isset($options['readPreference']) && ! is_in_transaction($options)) { $options['readPreference'] = $this->readPreference; } @@ -601,12 +628,13 @@ public function watch(array $pipeline = [], array $options = []) * * @see Database::__construct() for supported options * @param array $options Database constructor options - * @return Database * @throws InvalidArgumentException for parameter/option parsing errors */ - public function withOptions(array $options = []) + public function withOptions(array $options = []): Database { $options += [ + 'autoEncryptionEnabled' => $this->autoEncryptionEnabled, + 'builderEncoder' => $this->builderEncoder, 'readConcern' => $this->readConcern, 'readPreference' => $this->readPreference, 'typeMap' => $this->typeMap, diff --git a/src/DeleteResult.php b/src/DeleteResult.php index 697ee8692..23b416d0a 100644 --- a/src/DeleteResult.php +++ b/src/DeleteResult.php @@ -17,24 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a delete operation. */ class DeleteResult { - /** @var WriteResult */ - private $writeResult; - - /** @var boolean */ - private $isAcknowledged; - - public function __construct(WriteResult $writeResult) + public function __construct(private WriteResult $writeResult) { - $this->writeResult = $writeResult; - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -43,16 +35,11 @@ public function __construct(WriteResult $writeResult) * This method should only be called if the write was acknowledged. * * @see DeleteResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getDeletedCount() + public function getDeletedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getDeletedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getDeletedCount(); } /** @@ -60,11 +47,9 @@ public function getDeletedCount() * * If the delete was not acknowledged, other fields from the WriteResult * (e.g. deletedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/Exception/BadMethodCallException.php b/src/Exception/BadMethodCallException.php index b140ff3ae..00cece346 100644 --- a/src/Exception/BadMethodCallException.php +++ b/src/Exception/BadMethodCallException.php @@ -27,21 +27,10 @@ class BadMethodCallException extends BaseBadMethodCallException implements Excep * Thrown when a mutable method is invoked on an immutable object. * * @param string $class Class name - * @return self + * @internal */ - public static function classIsImmutable(string $class) + public static function classIsImmutable(string $class): self { - return new static(sprintf('%s is immutable', $class)); - } - - /** - * Thrown when accessing a result field on an unacknowledged write result. - * - * @param string $method Method name - * @return self - */ - public static function unacknowledgedWriteResultAccess(string $method) - { - return new static(sprintf('%s should not be called for an unacknowledged write result', $method)); + return new self(sprintf('%s is immutable', $class)); } } diff --git a/src/Exception/CreateEncryptedCollectionException.php b/src/Exception/CreateEncryptedCollectionException.php new file mode 100644 index 000000000..5f348edd7 --- /dev/null +++ b/src/Exception/CreateEncryptedCollectionException.php @@ -0,0 +1,50 @@ +getMessage()), 0, $previous); + } + + /** + * Returns the encryptedFields option in its last known state before the + * operation was interrupted. + * + * This can be used to infer which data keys were successfully created; + * however, it is possible that additional data keys were successfully + * created and are not present in the returned value. For example, if the + * operation was interrupted by a timeout error when creating a data key, + * the write may actually have succeeded on the server but the key will not + * be present in the returned value. + */ + public function getEncryptedFields(): array + { + return $this->encryptedFields; + } +} diff --git a/src/Exception/InvalidArgumentException.php b/src/Exception/InvalidArgumentException.php index 34f4cc5d9..c92734a0c 100644 --- a/src/Exception/InvalidArgumentException.php +++ b/src/Exception/InvalidArgumentException.php @@ -20,6 +20,7 @@ use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException; use function array_pop; +use function assert; use function count; use function get_debug_type; use function implode; @@ -28,35 +29,65 @@ class InvalidArgumentException extends DriverInvalidArgumentException implements Exception { + /** @internal */ + public static function cannotCombineCodecAndTypeMap(): self + { + return new self('Cannot provide both "codec" and "typeMap" options'); + } + + /** + * Thrown when an argument or option is expected to be a string or a document. + * + * @internal + * + * @param string $name Name of the argument or option + * @param mixed $value Actual value (used to derive the type) + */ + public static function expectedDocumentOrStringType(string $name, mixed $value): self + { + return new self(sprintf('Expected %s to have type "string" or "document" (array or object) but found "%s"', $name, get_debug_type($value))); + } + + /** + * Thrown when an argument or option is expected to be a document. + * + * @param string $name Name of the argument or option + * @param mixed $value Actual value (used to derive the type) + * @internal + */ + public static function expectedDocumentType(string $name, mixed $value): self + { + return new self(sprintf('Expected %s to have type "document" (array or object) but found "%s"', $name, get_debug_type($value))); + } + /** * Thrown when an argument or option has an invalid type. * - * @param string $name Name of the argument or option - * @param mixed $value Actual value (used to derive the type) - * @param string|string[] $expectedType Expected type - * @return self + * @param string $name Name of the argument or option + * @param mixed $value Actual value (used to derive the type) + * @param string|list $expectedType Expected type as a string or an array containing one or more strings + * @internal */ - public static function invalidType(string $name, $value, $expectedType) + public static function invalidType(string $name, mixed $value, string|array $expectedType): self { if (is_array($expectedType)) { - switch (count($expectedType)) { - case 1: - $typeString = array_pop($expectedType); - break; - - case 2: - $typeString = implode('" or "', $expectedType); - break; - - default: - $lastType = array_pop($expectedType); - $typeString = sprintf('%s", or "%s', implode('", "', $expectedType), $lastType); - break; - } - - $expectedType = $typeString; + $expectedType = self::expectedTypesToString($expectedType); } - return new static(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value))); + return new self(sprintf('Expected %s to have type "%s" but found "%s"', $name, $expectedType, get_debug_type($value))); + } + + /** @param list $types */ + private static function expectedTypesToString(array $types): string + { + assert(count($types) > 0); + + if (count($types) < 3) { + return implode('" or "', $types); + } + + $lastType = array_pop($types); + + return sprintf('%s", or "%s', implode('", "', $types), $lastType); } } diff --git a/src/Exception/ResumeTokenException.php b/src/Exception/ResumeTokenException.php index 4d3b23188..9117f700d 100644 --- a/src/Exception/ResumeTokenException.php +++ b/src/Exception/ResumeTokenException.php @@ -26,20 +26,20 @@ class ResumeTokenException extends RuntimeException * Thrown when a resume token has an invalid type. * * @param mixed $value Actual value (used to derive the type) - * @return self + * @internal */ - public static function invalidType($value) + public static function invalidType(mixed $value): self { - return new static(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value))); + return new self(sprintf('Expected resume token to have type "array or object" but found "%s"', get_debug_type($value))); } /** * Thrown when a resume token is not found in a change document. * - * @return self + * @internal */ - public static function notFound() + public static function notFound(): self { - return new static('Resume token not found in change document'); + return new self('Resume token not found in change document'); } } diff --git a/src/Exception/UnsupportedException.php b/src/Exception/UnsupportedException.php index 51d3c84b8..a2204b886 100644 --- a/src/Exception/UnsupportedException.php +++ b/src/Exception/UnsupportedException.php @@ -19,110 +19,44 @@ class UnsupportedException extends RuntimeException { - /** - * Thrown when a command's allowDiskUse option is not supported by a server. - * - * @return self - */ - public static function allowDiskUseNotSupported() - { - return new static('The "allowDiskUse" option is not supported by the server executing this operation'); - } - - /** - * Thrown when array filters are not supported by a server. - * - * @deprecated 1.12 - * @todo Remove this in 2.0 (see: PHPLIB-797) - * - * @return self - */ - public static function arrayFiltersNotSupported() - { - return new static('Array filters are not supported by the server executing this operation'); - } - - /** - * Thrown when collations are not supported by a server. - * - * @deprecated 1.12 - * @todo Remove this in 2.0 (see: PHPLIB-797) - * - * @return self - */ - public static function collationNotSupported() - { - return new static('Collations are not supported by the server executing this operation'); - } - /** * Thrown when the commitQuorum option for createIndexes is not supported * by a server. * - * @return self - */ - public static function commitQuorumNotSupported() - { - return new static('The "commitQuorum" option is not supported by the server executing this operation'); - } - - /** - * Thrown when explain is not supported by a server. - * - * @return self + * @internal */ - public static function explainNotSupported() + public static function commitQuorumNotSupported(): self { - return new static('Explain is not supported by the server executing this operation'); + return new self('The "commitQuorum" option is not supported by the server executing this operation'); } /** * Thrown when a command's hint option is not supported by a server. * - * @return self + * @internal */ - public static function hintNotSupported() + public static function hintNotSupported(): self { - return new static('Hint is not supported by the server executing this operation'); - } - - /** - * Thrown when a command's readConcern option is not supported by a server. - * - * @return self - */ - public static function readConcernNotSupported() - { - return new static('Read concern is not supported by the server executing this command'); + return new self('Hint is not supported by the server executing this operation'); } /** * Thrown when a readConcern is used with a read operation in a transaction. * - * @return self - */ - public static function readConcernNotSupportedInTransaction() - { - return new static('The "readConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.'); - } - - /** - * Thrown when a command's writeConcern option is not supported by a server. - * - * @return self + * @internal */ - public static function writeConcernNotSupported() + public static function readConcernNotSupportedInTransaction(): self { - return new static('Write concern is not supported by the server executing this command'); + return new self('Cannot set read concern after starting a transaction. Instead, specify the "readConcern" option when starting the transaction.'); } /** * Thrown when a writeConcern is used with a write operation in a transaction. * - * @return self + * @internal */ - public static function writeConcernNotSupportedInTransaction() + public static function writeConcernNotSupportedInTransaction(): self { - return new static('The "writeConcern" option cannot be specified within a transaction. Instead, specify it when starting the transaction.'); + return new self('Cannot set write concern after starting a transaction. Instead, specify the "writeConcern" option when starting the transaction.'); } } diff --git a/src/Exception/UnsupportedValueException.php b/src/Exception/UnsupportedValueException.php new file mode 100644 index 000000000..62e089e5e --- /dev/null +++ b/src/Exception/UnsupportedValueException.php @@ -0,0 +1,46 @@ +value; + } + + public static function invalidDecodableValue(mixed $value): self + { + return new self(sprintf('Could not decode value of type "%s".', get_debug_type($value)), $value); + } + + public static function invalidEncodableValue(mixed $value): self + { + return new self(sprintf('Could not encode value of type "%s".', get_debug_type($value)), $value); + } + + private function __construct(string $message, private mixed $value) + { + parent::__construct($message); + } +} diff --git a/src/GridFS/Bucket.php b/src/GridFS/Bucket.php index 67ad9ffd8..3210f8b42 100644 --- a/src/GridFS/Bucket.php +++ b/src/GridFS/Bucket.php @@ -17,8 +17,10 @@ namespace MongoDB\GridFS; +use MongoDB\BSON\Document; +use MongoDB\Codec\DocumentCodec; use MongoDB\Collection; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\ReadConcern; @@ -28,28 +30,28 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\GridFS\Exception\CorruptFileException; use MongoDB\GridFS\Exception\FileNotFoundException; +use MongoDB\GridFS\Exception\LogicException; use MongoDB\GridFS\Exception\StreamException; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; -use MongoDB\Operation\Find; use function array_intersect_key; +use function array_key_exists; use function assert; +use function explode; use function fopen; use function get_resource_type; use function in_array; use function is_array; -use function is_bool; use function is_integer; use function is_object; use function is_resource; use function is_string; use function method_exists; use function MongoDB\apply_type_map_to_document; -use function MongoDB\BSON\fromPHP; -use function MongoDB\BSON\toJSON; use function property_exists; use function sprintf; +use function str_contains; use function stream_context_create; use function stream_copy_to_stream; use function stream_get_meta_data; @@ -59,56 +61,36 @@ /** * Bucket provides a public API for interacting with the GridFS files and chunks * collections. - * - * @api */ class Bucket { - /** @var string */ - private static $defaultBucketName = 'fs'; + private const DEFAULT_BUCKET_NAME = 'fs'; - /** @var integer */ - private static $defaultChunkSizeBytes = 261120; + private const DEFAULT_CHUNK_SIZE_BYTES = 261120; - /** @var array */ - private static $defaultTypeMap = [ + private const DEFAULT_TYPE_MAP = [ 'array' => BSONArray::class, 'document' => BSONDocument::class, 'root' => BSONDocument::class, ]; - /** @var string */ - private static $streamWrapperProtocol = 'gridfs'; - - /** @var CollectionWrapper */ - private $collectionWrapper; - - /** @var string */ - private $databaseName; + private const STREAM_WRAPPER_PROTOCOL = 'gridfs'; - /** @var Manager */ - private $manager; + private ?DocumentCodec $codec = null; - /** @var string */ - private $bucketName; + private CollectionWrapper $collectionWrapper; - /** @var boolean */ - private $disableMD5; + private string $bucketName; - /** @var integer */ - private $chunkSizeBytes; + private int $chunkSizeBytes; - /** @var ReadConcern */ - private $readConcern; + private ReadConcern $readConcern; - /** @var ReadPreference */ - private $readPreference; + private ReadPreference $readPreference; - /** @var array */ - private $typeMap; + private array $typeMap; - /** @var WriteConcern */ - private $writeConcern; + private WriteConcern $writeConcern; /** * Constructs a GridFS bucket. @@ -121,9 +103,6 @@ class Bucket * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to * 261120 (i.e. 255 KiB). * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * each stored file. Defaults to "false". - * * * readConcern (MongoDB\Driver\ReadConcern): Read concern. * * * readPreference (MongoDB\Driver\ReadPreference): Read preference. @@ -137,12 +116,11 @@ class Bucket * @param array $options Bucket options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, string $databaseName, array $options = []) + public function __construct(private Manager $manager, private string $databaseName, array $options = []) { $options += [ - 'bucketName' => self::$defaultBucketName, - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, - 'disableMD5' => false, + 'bucketName' => self::DEFAULT_BUCKET_NAME, + 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, ]; if (! is_string($options['bucketName'])) { @@ -157,8 +135,8 @@ public function __construct(Manager $manager, string $databaseName, array $optio throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes'])); } - if (! is_bool($options['disableMD5'])) { - throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean'); + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { @@ -177,16 +155,23 @@ public function __construct(Manager $manager, string $databaseName, array $optio throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - $this->manager = $manager; - $this->databaseName = $databaseName; + if (isset($options['codec']) && isset($options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); + } + $this->bucketName = $options['bucketName']; $this->chunkSizeBytes = $options['chunkSizeBytes']; - $this->disableMD5 = $options['disableMD5']; + $this->codec = $options['codec'] ?? null; $this->readConcern = $options['readConcern'] ?? $this->manager->getReadConcern(); $this->readPreference = $options['readPreference'] ?? $this->manager->getReadPreference(); - $this->typeMap = $options['typeMap'] ?? self::$defaultTypeMap; + $this->typeMap = $options['typeMap'] ?? self::DEFAULT_TYPE_MAP; $this->writeConcern = $options['writeConcern'] ?? $this->manager->getWriteConcern(); + /* The codec option is intentionally omitted when constructing the files + * and chunks collections so as not to interfere with internal GridFS + * operations. Any codec will be manually applied when querying the + * files collection (i.e. find, findOne, and getFileDocumentForStream). + */ $collectionOptions = array_intersect_key($options, ['readConcern' => 1, 'readPreference' => 1, 'typeMap' => 1, 'writeConcern' => 1]); $this->collectionWrapper = new CollectionWrapper($manager, $databaseName, $options['bucketName'], $collectionOptions); @@ -197,12 +182,12 @@ public function __construct(Manager $manager, string $databaseName, array $optio * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return [ 'bucketName' => $this->bucketName, + 'codec' => $this->codec, 'databaseName' => $this->databaseName, 'manager' => $this->manager, 'chunkSizeBytes' => $this->chunkSizeBytes, @@ -223,7 +208,7 @@ public function __debugInfo() * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function delete($id) + public function delete(mixed $id): void { $file = $this->collectionWrapper->findFileById($id); $this->collectionWrapper->deleteFileAndChunksById($id); @@ -233,6 +218,23 @@ public function delete($id) } } + /** + * Delete all the revisions of a file name from the GridFS bucket. + * + * @param string $filename Filename + * + * @throws FileNotFoundException if no file could be selected + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function deleteByName(string $filename): void + { + $count = $this->collectionWrapper->deleteFileAndChunksByFilename($filename); + + if ($count === 0) { + throw FileNotFoundException::byFilename($filename); + } + } + /** * Writes the contents of a GridFS file to a writable stream. * @@ -243,9 +245,9 @@ public function delete($id) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function downloadToStream($id, $destination) + public function downloadToStream(mixed $id, $destination): void { - if (! is_resource($destination) || get_resource_type($destination) != "stream") { + if (! is_resource($destination) || get_resource_type($destination) != 'stream') { throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); } @@ -282,9 +284,9 @@ public function downloadToStream($id, $destination) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function downloadToStreamByName(string $filename, $destination, array $options = []) + public function downloadToStreamByName(string $filename, $destination, array $options = []): void { - if (! is_resource($destination) || get_resource_type($destination) != "stream") { + if (! is_resource($destination) || get_resource_type($destination) != 'stream') { throw InvalidArgumentException::invalidType('$destination', $destination, 'resource'); } @@ -300,7 +302,7 @@ public function downloadToStreamByName(string $filename, $destination, array $op * * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function drop() + public function drop(): void { $this->collectionWrapper->dropCollections(); } @@ -312,13 +314,16 @@ public function drop() * @see Find::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return Cursor * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function find($filter = [], array $options = []) + public function find(array|object $filter = [], array $options = []): CursorInterface { + if ($this->codec && ! array_key_exists('codec', $options)) { + $options['codec'] = $this->codec; + } + return $this->collectionWrapper->findFiles($filter, $options); } @@ -329,52 +334,47 @@ public function find($filter = [], array $options = []) * @see FindOne::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null * @throws UnsupportedException if options are not supported by the selected server * @throws InvalidArgumentException for parameter/option parsing errors * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function findOne($filter = [], array $options = []) + public function findOne(array|object $filter = [], array $options = []): array|object|null { + if ($this->codec && ! array_key_exists('codec', $options)) { + $options['codec'] = $this->codec; + } + return $this->collectionWrapper->findOneFile($filter, $options); } /** * Return the bucket name. - * - * @return string */ - public function getBucketName() + public function getBucketName(): string { return $this->bucketName; } /** * Return the chunks collection. - * - * @return Collection */ - public function getChunksCollection() + public function getChunksCollection(): Collection { return $this->collectionWrapper->getChunksCollection(); } /** * Return the chunk size in bytes. - * - * @return integer */ - public function getChunkSizeBytes() + public function getChunkSizeBytes(): int { return $this->chunkSizeBytes; } /** * Return the database name. - * - * @return string */ - public function getDatabaseName() + public function getDatabaseName(): string { return $this->databaseName; } @@ -383,14 +383,17 @@ public function getDatabaseName() * Gets the file document of the GridFS file associated with a stream. * * @param resource $stream GridFS stream - * @return array|object * @throws InvalidArgumentException if $stream is not a GridFS stream * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function getFileDocumentForStream($stream) + public function getFileDocumentForStream($stream): array|object { $file = $this->getRawFileDocumentForStream($stream); + if ($this->codec) { + return $this->codec->decode(Document::fromPHP($file)); + } + // Filter the raw document through the specified type map return apply_type_map_to_document($file, $this->typeMap); } @@ -399,12 +402,11 @@ public function getFileDocumentForStream($stream) * Gets the file document's ID of the GridFS file associated with a stream. * * @param resource $stream GridFS stream - * @return mixed * @throws CorruptFileException if the file "_id" field does not exist * @throws InvalidArgumentException if $stream is not a GridFS stream * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function getFileIdForStream($stream) + public function getFileIdForStream($stream): mixed { $file = $this->getRawFileDocumentForStream($stream); @@ -424,10 +426,8 @@ public function getFileIdForStream($stream) /** * Return the files collection. - * - * @return Collection */ - public function getFilesCollection() + public function getFilesCollection(): Collection { return $this->collectionWrapper->getFilesCollection(); } @@ -436,29 +436,24 @@ public function getFilesCollection() * Return the read concern for this GridFS bucket. * * @see https://php.net/manual/en/mongodb-driver-readconcern.isdefault.php - * @return ReadConcern */ - public function getReadConcern() + public function getReadConcern(): ReadConcern { return $this->readConcern; } /** * Return the read preference for this GridFS bucket. - * - * @return ReadPreference */ - public function getReadPreference() + public function getReadPreference(): ReadPreference { return $this->readPreference; } /** * Return the type map for this GridFS bucket. - * - * @return array */ - public function getTypeMap() + public function getTypeMap(): array { return $this->typeMap; } @@ -467,9 +462,8 @@ public function getTypeMap() * Return the write concern for this GridFS bucket. * * @see https://php.net/manual/en/mongodb-driver-writeconcern.isdefault.php - * @return WriteConcern */ - public function getWriteConcern() + public function getWriteConcern(): WriteConcern { return $this->writeConcern; } @@ -482,7 +476,7 @@ public function getWriteConcern() * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function openDownloadStream($id) + public function openDownloadStream(mixed $id) { $file = $this->collectionWrapper->findFileById($id); @@ -494,7 +488,7 @@ public function openDownloadStream($id) } /** - * Opens a readable stream stream to read a GridFS file, which is selected + * Opens a readable stream to read a GridFS file, which is selected * by name and revision. * * Supported options: @@ -541,9 +535,6 @@ public function openDownloadStreamByName(string $filename, array $options = []) * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to the * bucket's chunk size. * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * the stored file. Defaults to "false". - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -553,11 +544,13 @@ public function openDownloadStreamByName(string $filename, array $options = []) */ public function openUploadStream(string $filename, array $options = []) { - $options += ['chunkSizeBytes' => $this->chunkSizeBytes]; + $options += [ + 'chunkSizeBytes' => $this->chunkSizeBytes, + ]; $path = $this->createPathForUpload(); $context = stream_context_create([ - self::$streamWrapperProtocol => [ + self::STREAM_WRAPPER_PROTOCOL => [ 'collectionWrapper' => $this->collectionWrapper, 'filename' => $filename, 'options' => $options, @@ -567,6 +560,29 @@ public function openUploadStream(string $filename, array $options = []) return fopen($path, 'w', false, $context); } + /** + * Register an alias to enable basic filename access for this bucket. + * + * For applications that need to interact with GridFS using only a filename + * string, a bucket can be registered with an alias. Files can then be + * accessed using the following pattern: + * + * gridfs:/// + * + * Read operations will always target the most recent revision of a file. + * + * @param non-empty-string string $alias The alias to use for the bucket + */ + public function registerGlobalStreamWrapperAlias(string $alias): void + { + if ($alias === '' || str_contains($alias, '/')) { + throw new InvalidArgumentException(sprintf('The bucket alias must be a non-empty string without any slash, "%s" given', $alias)); + } + + // Use a closure to expose the private method into another class + StreamWrapper::setContextResolver($alias, fn (string $path, string $mode, array $context) => $this->resolveStreamContext($path, $mode, $context)); + } + /** * Renames the GridFS file with the specified ID. * @@ -575,7 +591,7 @@ public function openUploadStream(string $filename, array $options = []) * @throws FileNotFoundException if no file could be selected * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function rename($id, string $newFilename) + public function rename(mixed $id, string $newFilename): void { $updateResult = $this->collectionWrapper->updateFilenameForId($id, $newFilename); @@ -583,20 +599,31 @@ public function rename($id, string $newFilename) return; } - /* If the update resulted in no modification, it's possible that the - * file did not exist, in which case we must raise an error. Checking - * the write result's matched count will be most efficient, but fall - * back to a findOne operation if necessary (i.e. legacy writes). - */ - $found = $updateResult->getMatchedCount() !== null - ? $updateResult->getMatchedCount() === 1 - : $this->collectionWrapper->findFileById($id) !== null; - - if (! $found) { + // If the update resulted in no modification, it's possible that the + // file did not exist, in which case we must raise an error. + if ($updateResult->getMatchedCount() !== 1) { throw FileNotFoundException::byId($id, $this->getFilesNamespace()); } } + /** + * Renames all the revisions of a file name in the GridFS bucket. + * + * @param string $filename Filename + * @param string $newFilename New filename + * + * @throws FileNotFoundException if no file could be selected + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function renameByName(string $filename, string $newFilename): void + { + $count = $this->collectionWrapper->updateFilenameForFilename($filename, $newFilename); + + if ($count === 0) { + throw FileNotFoundException::byFilename($filename); + } + } + /** * Writes the contents of a readable stream to a GridFS file. * @@ -607,9 +634,6 @@ public function rename($id, string $newFilename) * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to the * bucket's chunk size. * - * * disableMD5 (boolean): When true, no MD5 sum will be generated for - * the stored file. Defaults to "false". - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -621,9 +645,9 @@ public function rename($id, string $newFilename) * @throws StreamException if the file could not be uploaded * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function uploadFromStream(string $filename, $source, array $options = []) + public function uploadFromStream(string $filename, $source, array $options = []): mixed { - if (! is_resource($source) || get_resource_type($source) != "stream") { + if (! is_resource($source) || get_resource_type($source) != 'stream') { throw InvalidArgumentException::invalidType('$source', $source, 'resource'); } @@ -646,17 +670,17 @@ public function uploadFromStream(string $filename, $source, array $options = []) private function createPathForFile(object $file): string { if (is_array($file->_id) || (is_object($file->_id) && ! method_exists($file->_id, '__toString'))) { - $id = toJSON(fromPHP(['_id' => $file->_id])); + $id = Document::fromPHP(['_id' => $file->_id])->toRelaxedExtendedJSON(); } else { $id = (string) $file->_id; } return sprintf( '%s://%s/%s.files/%s', - self::$streamWrapperProtocol, + self::STREAM_WRAPPER_PROTOCOL, urlencode($this->databaseName), urlencode($this->bucketName), - urlencode($id) + urlencode($id), ); } @@ -667,9 +691,9 @@ private function createPathForUpload(): string { return sprintf( '%s://%s/%s.files', - self::$streamWrapperProtocol, + self::STREAM_WRAPPER_PROTOCOL, urlencode($this->databaseName), - urlencode($this->bucketName) + urlencode($this->bucketName), ); } @@ -692,7 +716,7 @@ private function getFilesNamespace(): string */ private function getRawFileDocumentForStream($stream): object { - if (! is_resource($stream) || get_resource_type($stream) != "stream") { + if (! is_resource($stream) || get_resource_type($stream) != 'stream') { throw InvalidArgumentException::invalidType('$stream', $stream, 'resource'); } @@ -715,7 +739,7 @@ private function openDownloadStreamByFile(object $file) { $path = $this->createPathForFile($file); $context = stream_context_create([ - self::$streamWrapperProtocol => [ + self::STREAM_WRAPPER_PROTOCOL => [ 'collectionWrapper' => $this->collectionWrapper, 'file' => $file, ], @@ -729,10 +753,55 @@ private function openDownloadStreamByFile(object $file) */ private function registerStreamWrapper(): void { - if (in_array(self::$streamWrapperProtocol, stream_get_wrappers())) { + if (in_array(self::STREAM_WRAPPER_PROTOCOL, stream_get_wrappers())) { return; } - StreamWrapper::register(self::$streamWrapperProtocol); + StreamWrapper::register(self::STREAM_WRAPPER_PROTOCOL); + } + + /** + * Create a stream context from the path and mode provided to fopen(). + * + * @see StreamWrapper::setContextResolver() + * + * @param string $path The full url provided to fopen(). It contains the filename. + * gridfs://database_name/collection_name.files/file_name + * @param array{revision?: int, chunkSizeBytes?: int} $context The options provided to fopen() + * + * @return array{collectionWrapper: CollectionWrapper, file: object}|array{collectionWrapper: CollectionWrapper, filename: string, options: array} + * + * @throws FileNotFoundException + * @throws LogicException + */ + private function resolveStreamContext(string $path, string $mode, array $context): array + { + // Fallback to an empty filename if the path does not contain one: "gridfs://alias" + $filename = explode('/', $path, 4)[3] ?? ''; + + if ($mode === 'r' || $mode === 'rb') { + $file = $this->collectionWrapper->findFileByFilenameAndRevision($filename, $context['revision'] ?? -1); + + if (! is_object($file)) { + throw FileNotFoundException::byFilenameAndRevision($filename, $context['revision'] ?? -1, $path); + } + + return [ + 'collectionWrapper' => $this->collectionWrapper, + 'file' => $file, + ]; + } + + if ($mode === 'w' || $mode === 'wb') { + return [ + 'collectionWrapper' => $this->collectionWrapper, + 'filename' => $filename, + 'options' => $context + [ + 'chunkSizeBytes' => $this->chunkSizeBytes, + ], + ]; + } + + throw LogicException::openModeNotSupported($mode); } } diff --git a/src/GridFS/CollectionWrapper.php b/src/GridFS/CollectionWrapper.php index 28218707c..62be77fc0 100644 --- a/src/GridFS/CollectionWrapper.php +++ b/src/GridFS/CollectionWrapper.php @@ -19,7 +19,7 @@ use ArrayIterator; use MongoDB\Collection; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Manager; use MongoDB\Driver\ReadPreference; use MongoDB\Exception\InvalidArgumentException; @@ -38,22 +38,13 @@ * * @internal */ -class CollectionWrapper +final class CollectionWrapper { - /** @var string */ - private $bucketName; + private Collection $chunksCollection; - /** @var Collection */ - private $chunksCollection; + private bool $checkedIndexes = false; - /** @var string */ - private $databaseName; - - /** @var boolean */ - private $checkedIndexes = false; - - /** @var Collection */ - private $filesCollection; + private Collection $filesCollection; /** * Constructs a GridFS collection wrapper. @@ -65,31 +56,47 @@ class CollectionWrapper * @param array $collectionOptions Collection options * @throws InvalidArgumentException */ - public function __construct(Manager $manager, string $databaseName, string $bucketName, array $collectionOptions = []) + public function __construct(Manager $manager, private string $databaseName, private string $bucketName, array $collectionOptions = []) { - $this->databaseName = $databaseName; - $this->bucketName = $bucketName; - $this->filesCollection = new Collection($manager, $databaseName, sprintf('%s.files', $bucketName), $collectionOptions); $this->chunksCollection = new Collection($manager, $databaseName, sprintf('%s.chunks', $bucketName), $collectionOptions); } /** * Deletes all GridFS chunks for a given file ID. - * - * @param mixed $id */ - public function deleteChunksByFilesId($id): void + public function deleteChunksByFilesId(mixed $id): void { $this->chunksCollection->deleteMany(['files_id' => $id]); } + /** + * Delete all GridFS files and chunks for a given filename. + */ + public function deleteFileAndChunksByFilename(string $filename): int + { + /** @var iterable $files */ + $files = $this->findFiles(['filename' => $filename], [ + 'typeMap' => ['root' => 'array'], + 'projection' => ['_id' => 1], + ]); + + /** @var list $ids */ + $ids = []; + foreach ($files as $file) { + $ids[] = $file['_id']; + } + + $count = $this->filesCollection->deleteMany(['_id' => ['$in' => $ids]])->getDeletedCount(); + $this->chunksCollection->deleteMany(['files_id' => ['$in' => $ids]]); + + return $count; + } + /** * Deletes a GridFS file and related chunks by ID. - * - * @param mixed $id */ - public function deleteFileAndChunksById($id): void + public function deleteFileAndChunksById(mixed $id): void { $this->filesCollection->deleteOne(['_id' => $id]); $this->chunksCollection->deleteMany(['files_id' => $id]); @@ -110,7 +117,7 @@ public function dropCollections(): void * @param mixed $id File ID * @param integer $fromChunk Starting chunk (inclusive) */ - public function findChunksByFileId($id, int $fromChunk = 0): Cursor + public function findChunksByFileId(mixed $id, int $fromChunk = 0): CursorInterface { return $this->chunksCollection->find( [ @@ -120,7 +127,7 @@ public function findChunksByFileId($id, int $fromChunk = 0): Cursor [ 'sort' => ['n' => 1], 'typeMap' => ['root' => 'stdClass'], - ] + ], ); } @@ -141,9 +148,6 @@ public function findChunksByFileId($id, int $fromChunk = 0): Cursor */ public function findFileByFilenameAndRevision(string $filename, int $revision): ?object { - $filename = $filename; - $revision = $revision; - if ($revision < 0) { $skip = abs($revision) - 1; $sortOrder = -1; @@ -158,7 +162,7 @@ public function findFileByFilenameAndRevision(string $filename, int $revision): 'skip' => $skip, 'sort' => ['uploadDate' => $sortOrder], 'typeMap' => ['root' => 'stdClass'], - ] + ], ); assert(is_object($file) || $file === null); @@ -167,14 +171,12 @@ public function findFileByFilenameAndRevision(string $filename, int $revision): /** * Finds a GridFS file document for a given ID. - * - * @param mixed $id */ - public function findFileById($id): ?object + public function findFileById(mixed $id): ?object { $file = $this->filesCollection->findOne( ['_id' => $id], - ['typeMap' => ['root' => 'stdClass']] + ['typeMap' => ['root' => 'stdClass']], ); assert(is_object($file) || $file === null); @@ -187,9 +189,8 @@ public function findFileById($id): ?object * @see Find::__construct() for supported options * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return Cursor */ - public function findFiles($filter, array $options = []) + public function findFiles(array|object $filter, array $options = []): CursorInterface { return $this->filesCollection->find($filter, $options); } @@ -199,9 +200,8 @@ public function findFiles($filter, array $options = []) * * @param array|object $filter Query by which to filter documents * @param array $options Additional options - * @return array|object|null */ - public function findOneFile($filter, array $options = []) + public function findOneFile(array|object $filter, array $options = []): array|object|null { return $this->filesCollection->findOne($filter, $options); } @@ -231,7 +231,7 @@ public function getFilesCollection(): Collection * * @param array|object $chunk Chunk document */ - public function insertChunk($chunk): void + public function insertChunk(array|object $chunk): void { if (! $this->checkedIndexes) { $this->ensureIndexes(); @@ -247,7 +247,7 @@ public function insertChunk($chunk): void * * @param array|object $file File document */ - public function insertFile($file): void + public function insertFile(array|object $file): void { if (! $this->checkedIndexes) { $this->ensureIndexes(); @@ -256,16 +256,25 @@ public function insertFile($file): void $this->filesCollection->insertOne($file); } + /** + * Updates the filename field in the file document for all the files with a given filename. + */ + public function updateFilenameForFilename(string $filename, string $newFilename): int + { + return $this->filesCollection->updateMany( + ['filename' => $filename], + ['$set' => ['filename' => $newFilename]], + )->getMatchedCount(); + } + /** * Updates the filename field in the file document for a given ID. - * - * @param mixed $id */ - public function updateFilenameForId($id, string $filename): UpdateResult + public function updateFilenameForId(mixed $id, string $filename): UpdateResult { return $this->filesCollection->updateOne( ['_id' => $id], - ['$set' => ['filename' => $filename]] + ['$set' => ['filename' => $filename]], ); } @@ -359,7 +368,7 @@ private function indexKeysMatch(array $expectedKeys, array $actualKeys): bool private function isFilesCollectionEmpty(): bool { return null === $this->filesCollection->findOne([], [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'projection' => ['_id' => 1], 'typeMap' => [], ]); diff --git a/src/GridFS/Exception/CorruptFileException.php b/src/GridFS/Exception/CorruptFileException.php index e31d7daeb..b9feef87d 100644 --- a/src/GridFS/Exception/CorruptFileException.php +++ b/src/GridFS/Exception/CorruptFileException.php @@ -25,21 +25,23 @@ class CorruptFileException extends RuntimeException { /** * Thrown when a chunk doesn't contain valid data. + * + * @internal */ public static function invalidChunkData(int $chunkIndex): self { - return new static(sprintf('Invalid data found for index "%d"', $chunkIndex)); + return new self(sprintf('Invalid data found for index "%d"', $chunkIndex)); } /** * Thrown when a chunk is not found for an expected index. * * @param integer $expectedIndex Expected index number - * @return self + * @internal */ - public static function missingChunk(int $expectedIndex) + public static function missingChunk(int $expectedIndex): self { - return new static(sprintf('Chunk not found for index "%d"', $expectedIndex)); + return new self(sprintf('Chunk not found for index "%d"', $expectedIndex)); } /** @@ -47,11 +49,11 @@ public static function missingChunk(int $expectedIndex) * * @param integer $index Actual index number (i.e. "n" field) * @param integer $expectedIndex Expected index number - * @return self + * @internal */ - public static function unexpectedIndex(int $index, int $expectedIndex) + public static function unexpectedIndex(int $index, int $expectedIndex): self { - return new static(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index)); + return new self(sprintf('Expected chunk to have index "%d" but found "%d"', $expectedIndex, $index)); } /** @@ -59,10 +61,10 @@ public static function unexpectedIndex(int $index, int $expectedIndex) * * @param integer $size Actual size (i.e. "data" field length) * @param integer $expectedSize Expected size - * @return self + * @internal */ - public static function unexpectedSize(int $size, int $expectedSize) + public static function unexpectedSize(int $size, int $expectedSize): self { - return new static(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size)); + return new self(sprintf('Expected chunk to have size "%d" but found "%d"', $expectedSize, $size)); } } diff --git a/src/GridFS/Exception/FileNotFoundException.php b/src/GridFS/Exception/FileNotFoundException.php index 5d0f5d5c5..4cf153c08 100644 --- a/src/GridFS/Exception/FileNotFoundException.php +++ b/src/GridFS/Exception/FileNotFoundException.php @@ -17,25 +17,35 @@ namespace MongoDB\GridFS\Exception; +use MongoDB\BSON\Document; use MongoDB\Exception\RuntimeException; -use function MongoDB\BSON\fromPHP; -use function MongoDB\BSON\toJSON; use function sprintf; class FileNotFoundException extends RuntimeException { + /** + * Thrown when a file cannot be found by its filename. + * + * @param string $filename Filename + * @internal + */ + public static function byFilename(string $filename): self + { + return new self(sprintf('File with name "%s" not found', $filename)); + } + /** * Thrown when a file cannot be found by its filename and revision. * * @param string $filename Filename * @param integer $revision Revision * @param string $namespace Namespace for the files collection - * @return self + * @internal */ - public static function byFilenameAndRevision(string $filename, int $revision, string $namespace) + public static function byFilenameAndRevision(string $filename, int $revision, string $namespace): self { - return new static(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace)); + return new self(sprintf('File with name "%s" and revision "%d" not found in "%s"', $filename, $revision, $namespace)); } /** @@ -43,12 +53,12 @@ public static function byFilenameAndRevision(string $filename, int $revision, st * * @param mixed $id File ID * @param string $namespace Namespace for the files collection - * @return self + * @internal */ - public static function byId($id, string $namespace) + public static function byId(mixed $id, string $namespace): self { - $json = toJSON(fromPHP(['_id' => $id])); + $json = Document::fromPHP(['_id' => $id])->toRelaxedExtendedJSON(); - return new static(sprintf('File "%s" not found in "%s"', $json, $namespace)); + return new self(sprintf('File "%s" not found in "%s"', $json, $namespace)); } } diff --git a/src/GridFS/Exception/LogicException.php b/src/GridFS/Exception/LogicException.php new file mode 100644 index 000000000..a6835423c --- /dev/null +++ b/src/GridFS/Exception/LogicException.php @@ -0,0 +1,78 @@ + $id])); + $idString = Document::fromPHP(['_id' => $id])->toRelaxedExtendedJSON(); $sourceMetadata = stream_get_meta_data($source); $destinationMetadata = stream_get_meta_data($destination); - return new static(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)); + return new self(sprintf('Downloading file from "%s" to "%s" failed. GridFS identifier: "%s"', $sourceMetadata['uri'], $destinationMetadata['uri'], $idString)); } - /** @param resource $source */ + /** + * @param resource $source + * @internal + */ public static function uploadFailed(string $filename, $source, string $destinationUri): self { $sourceMetadata = stream_get_meta_data($source); - return new static(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename)); + return new self(sprintf('Uploading file from "%s" to "%s" failed. GridFS filename: "%s"', $sourceMetadata['uri'], $destinationUri, $filename)); } } diff --git a/src/GridFS/ReadableStream.php b/src/GridFS/ReadableStream.php index d76e06172..cdc5d2d8d 100644 --- a/src/GridFS/ReadableStream.php +++ b/src/GridFS/ReadableStream.php @@ -18,7 +18,7 @@ namespace MongoDB\GridFS; use MongoDB\BSON\Binary; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Exception\InvalidArgumentException; use MongoDB\GridFS\Exception\CorruptFileException; @@ -37,37 +37,23 @@ * * @internal */ -class ReadableStream +final class ReadableStream { - /** @var string|null */ - private $buffer; + private ?string $buffer = null; - /** @var integer */ - private $bufferOffset = 0; + private int $bufferOffset = 0; - /** @var integer */ - private $chunkSize; + private int $chunkSize; - /** @var integer */ - private $chunkOffset = 0; + private int $chunkOffset = 0; - /** @var Cursor|null */ - private $chunksIterator; + private ?CursorInterface $chunksIterator = null; - /** @var CollectionWrapper */ - private $collectionWrapper; + private int $expectedLastChunkSize = 0; - /** @var integer */ - private $expectedLastChunkSize = 0; + private int $length; - /** @var object */ - private $file; - - /** @var integer */ - private $length; - - /** @var integer */ - private $numChunks = 0; + private int $numChunks = 0; /** * Constructs a readable GridFS stream. @@ -76,7 +62,7 @@ class ReadableStream * @param object $file GridFS file document * @throws CorruptFileException */ - public function __construct(CollectionWrapper $collectionWrapper, object $file) + public function __construct(private CollectionWrapper $collectionWrapper, private object $file) { if (! isset($file->chunkSize) || ! is_integer($file->chunkSize) || $file->chunkSize < 1) { throw new CorruptFileException('file.chunkSize is not an integer >= 1'); @@ -90,14 +76,11 @@ public function __construct(CollectionWrapper $collectionWrapper, object $file) throw new CorruptFileException('file._id does not exist'); } - $this->file = $file; $this->chunkSize = $file->chunkSize; $this->length = $file->length; - $this->collectionWrapper = $collectionWrapper; - if ($this->length > 0) { - $this->numChunks = (integer) ceil($this->length / $this->chunkSize); + $this->numChunks = (int) ceil($this->length / $this->chunkSize); $this->expectedLastChunkSize = $this->length - (($this->numChunks - 1) * $this->chunkSize); } } @@ -106,7 +89,6 @@ public function __construct(CollectionWrapper $collectionWrapper, object $file) * Return internal properties for debugging purposes. * * @see https://php.net/manual/en/language.oop5.magic.php#language.oop5.magic.debuginfo - * @return array */ public function __debugInfo(): array { @@ -200,7 +182,7 @@ public function seek(int $offset): void * changed, we'll also need to reset the buffer. */ $lastChunkOffset = $this->chunkOffset; - $this->chunkOffset = (integer) floor($offset / $this->chunkSize); + $this->chunkOffset = (int) floor($offset / $this->chunkSize); $this->bufferOffset = $offset % $this->chunkSize; if ($lastChunkOffset === $this->chunkOffset) { @@ -224,7 +206,7 @@ public function seek(int $offset): void } /* If we are seeking to a subsequent chunk, we do not need to - * reinitalize the chunk iterator. Instead, we can simply move forward + * reinitalize the chunk iterator. Instead, we can move forward * to $this->chunkOffset. */ $numChunks = $this->chunkOffset - $lastChunkOffset; diff --git a/src/GridFS/StreamWrapper.php b/src/GridFS/StreamWrapper.php index 3e04c3d14..e4f57fa85 100644 --- a/src/GridFS/StreamWrapper.php +++ b/src/GridFS/StreamWrapper.php @@ -17,13 +17,20 @@ namespace MongoDB\GridFS; +use Closure; use MongoDB\BSON\UTCDateTime; +use MongoDB\GridFS\Exception\FileNotFoundException; +use MongoDB\GridFS\Exception\LogicException; +use function array_slice; use function assert; use function explode; +use function implode; use function in_array; +use function is_array; use function is_integer; use function is_resource; +use function str_starts_with; use function stream_context_get_options; use function stream_get_wrappers; use function stream_wrapper_register; @@ -40,27 +47,25 @@ * @internal * @see Bucket::openUploadStream() * @see Bucket::openDownloadStream() + * @psalm-type ContextOptions = array{collectionWrapper: CollectionWrapper, file: object}|array{collectionWrapper: CollectionWrapper, filename: string, options: array} */ -class StreamWrapper +final class StreamWrapper { /** @var resource|null Stream context (set by PHP) */ public $context; - /** @var string|null */ - private $mode; + private ReadableStream|WritableStream|null $stream = null; - /** @var string|null */ - private $protocol; - - /** @var ReadableStream|WritableStream|null */ - private $stream; + /** @var array */ + private static array $contextResolvers = []; public function __destruct() { - /* This destructor is a workaround for PHP trying to use the stream well - * after all objects have been destructed. This can cause autoloading - * issues and possibly segmentation faults during PHP shutdown. */ - $this->stream = null; + /* Ensure the stream is closed so the last chunk is written. This is + * necessary because PHP would close the stream after all objects have + * been destructed. This can cause autoloading issues and possibly + * segmentation faults during PHP shutdown. */ + $this->stream_close(); } /** @@ -84,7 +89,47 @@ public static function register(string $protocol = 'gridfs'): void stream_wrapper_unregister($protocol); } - stream_wrapper_register($protocol, static::class, STREAM_IS_URL); + stream_wrapper_register($protocol, self::class, STREAM_IS_URL); + } + + /** + * Rename all revisions of a filename. + * + * @return true + * @throws FileNotFoundException + */ + public function rename(string $fromPath, string $toPath): bool + { + $prefix = implode('/', array_slice(explode('/', $fromPath, 4), 0, 3)) . '/'; + if (! str_starts_with($toPath, $prefix)) { + throw LogicException::renamePathMismatch($fromPath, $toPath); + } + + $context = $this->getContext($fromPath, 'w'); + + $newFilename = explode('/', $toPath, 4)[3] ?? ''; + $count = $context['collectionWrapper']->updateFilenameForFilename($context['filename'], $newFilename); + + if ($count === 0) { + throw FileNotFoundException::byFilename($fromPath); + } + + // If $count is null, the update is unacknowledged, the operation is considered successful. + return true; + } + + /** + * @see Bucket::resolveStreamContext() + * + * @param Closure(string, string, array):ContextOptions|null $resolver + */ + public static function setContextResolver(string $name, ?Closure $resolver): void + { + if ($resolver === null) { + unset(self::$contextResolvers[$name]); + } else { + self::$contextResolvers[$name] = $resolver; + } } /** @@ -126,18 +171,15 @@ public function stream_eof(): bool */ public function stream_open(string $path, string $mode, int $options, ?string &$openedPath): bool { - $this->initProtocol($path); - $this->mode = $mode; - - if ($mode === 'r') { - return $this->initReadableStream(); + if ($mode === 'r' || $mode === 'rb') { + return $this->initReadableStream($this->getContext($path, $mode)); } - if ($mode === 'w') { - return $this->initWritableStream(); + if ($mode === 'w' || $mode === 'wb') { + return $this->initWritableStream($this->getContext($path, $mode)); } - return false; + throw LogicException::openModeNotSupported($mode); } /** @@ -254,6 +296,77 @@ public function stream_write(string $data): int return $this->stream->writeBytes($data); } + /** + * Remove all revisions of a filename. + * + * @return true + * @throws FileNotFoundException + */ + public function unlink(string $path): bool + { + $context = $this->getContext($path, 'w'); + $count = $context['collectionWrapper']->deleteFileAndChunksByFilename($context['filename']); + + if ($count === 0) { + throw FileNotFoundException::byFilename($path); + } + + // If $count is null, the update is unacknowledged, the operation is considered successful. + return true; + } + + public function url_stat(string $path, int $flags): false|array + { + assert($this->stream === null); + + try { + $this->stream_open($path, 'r', 0, $openedPath); + } catch (FileNotFoundException) { + return false; + } + + return $this->stream_stat(); + } + + /** + * @return array{collectionWrapper: CollectionWrapper, file: object}|array{collectionWrapper: CollectionWrapper, filename: string, options: array} + * @psalm-return ($mode == 'r' or $mode == 'rb' ? array{collectionWrapper: CollectionWrapper, file: object} : array{collectionWrapper: CollectionWrapper, filename: string, options: array}) + */ + private function getContext(string $path, string $mode): array + { + $context = []; + + /** + * The Bucket methods { @see Bucket::openUploadStream() } and { @see Bucket::openDownloadStreamByFile() } + * always set an internal context. But the context can also be set by the user. + */ + if (is_resource($this->context)) { + $context = stream_context_get_options($this->context)['gridfs'] ?? []; + + if (! is_array($context)) { + throw LogicException::invalidContext($context); + } + } + + // When the stream is opened using fopen(), the context is not required, it can contain only options. + if (! isset($context['collectionWrapper'])) { + $bucketAlias = explode('/', $path, 4)[2] ?? ''; + + if (! isset(self::$contextResolvers[$bucketAlias])) { + throw LogicException::bucketAliasNotRegistered($bucketAlias); + } + + /** @see Bucket::resolveStreamContext() */ + $context = self::$contextResolvers[$bucketAlias]($path, $mode, $context); + } + + if (! $context['collectionWrapper'] instanceof CollectionWrapper) { + throw LogicException::invalidContextCollectionWrapper($context['collectionWrapper']); + } + + return $context; + } + /** * Returns a stat template with default values. */ @@ -278,31 +391,16 @@ private function getStatTemplate(): array ]; } - /** - * Initialize the protocol from the given path. - * - * @see StreamWrapper::stream_open() - */ - private function initProtocol(string $path): void - { - $parts = explode('://', $path, 2); - $this->protocol = $parts[0] ?: 'gridfs'; - } - /** * Initialize the internal stream for reading. * - * @see StreamWrapper::stream_open() + * @param array{collectionWrapper: CollectionWrapper, file: object} $contextOptions */ - private function initReadableStream(): bool + private function initReadableStream(array $contextOptions): bool { - assert(is_resource($this->context)); - $context = stream_context_get_options($this->context); - - assert($this->protocol !== null); $this->stream = new ReadableStream( - $context[$this->protocol]['collectionWrapper'], - $context[$this->protocol]['file'] + $contextOptions['collectionWrapper'], + $contextOptions['file'], ); return true; @@ -311,18 +409,14 @@ private function initReadableStream(): bool /** * Initialize the internal stream for writing. * - * @see StreamWrapper::stream_open() + * @param array{collectionWrapper: CollectionWrapper, filename: string, options: array} $contextOptions */ - private function initWritableStream(): bool + private function initWritableStream(array $contextOptions): bool { - assert(is_resource($this->context)); - $context = stream_context_get_options($this->context); - - assert($this->protocol !== null); $this->stream = new WritableStream( - $context[$this->protocol]['collectionWrapper'], - $context[$this->protocol]['filename'], - $context[$this->protocol]['options'] + $contextOptions['collectionWrapper'], + $contextOptions['filename'], + $contextOptions['options'], ); return true; diff --git a/src/GridFS/WritableStream.php b/src/GridFS/WritableStream.php index 43519e09b..e7a4e0e63 100644 --- a/src/GridFS/WritableStream.php +++ b/src/GridFS/WritableStream.php @@ -17,7 +17,6 @@ namespace MongoDB\GridFS; -use HashContext; use MongoDB\BSON\Binary; use MongoDB\BSON\ObjectId; use MongoDB\BSON\UTCDateTime; @@ -25,15 +24,8 @@ use MongoDB\Exception\InvalidArgumentException; use function array_intersect_key; -use function hash_final; -use function hash_init; -use function hash_update; -use function is_array; -use function is_bool; use function is_integer; -use function is_object; -use function is_string; -use function MongoDB\is_string_array; +use function MongoDB\is_document; use function sprintf; use function strlen; use function substr; @@ -43,37 +35,21 @@ * * @internal */ -class WritableStream +final class WritableStream { - /** @var integer */ - private static $defaultChunkSizeBytes = 261120; + private const DEFAULT_CHUNK_SIZE_BYTES = 261120; - /** @var string */ - private $buffer = ''; + private string $buffer = ''; - /** @var integer */ - private $chunkOffset = 0; + private int $chunkOffset = 0; - /** @var integer */ - private $chunkSize; + private int $chunkSize; - /** @var boolean */ - private $disableMD5; + private array $file; - /** @var CollectionWrapper */ - private $collectionWrapper; + private bool $isClosed = false; - /** @var array */ - private $file; - - /** @var HashContext|null */ - private $hashCtx; - - /** @var boolean */ - private $isClosed = false; - - /** @var integer */ - private $length = 0; + private int $length = 0; /** * Constructs a writable GridFS stream. @@ -82,19 +58,9 @@ class WritableStream * * * _id (mixed): File document identifier. Defaults to a new ObjectId. * - * * aliases (array of strings): DEPRECATED An array of aliases. - * Applications wishing to store aliases should add an aliases field to - * the metadata document instead. - * * * chunkSizeBytes (integer): The chunk size in bytes. Defaults to * 261120 (i.e. 255 KiB). * - * * disableMD5 (boolean): When true, no MD5 sum will be generated. - * Defaults to "false". - * - * * contentType (string): DEPRECATED content type to be stored with the - * file. This information should now be added to the metadata. - * * * metadata (document): User data for the "metadata" field of the files * collection document. * @@ -103,18 +69,13 @@ class WritableStream * @param array $options Upload options * @throws InvalidArgumentException */ - public function __construct(CollectionWrapper $collectionWrapper, string $filename, array $options = []) + public function __construct(private CollectionWrapper $collectionWrapper, string $filename, array $options = []) { $options += [ '_id' => new ObjectId(), - 'chunkSizeBytes' => self::$defaultChunkSizeBytes, - 'disableMD5' => false, + 'chunkSizeBytes' => self::DEFAULT_CHUNK_SIZE_BYTES, ]; - if (isset($options['aliases']) && ! is_string_array($options['aliases'])) { - throw InvalidArgumentException::invalidType('"aliases" option', $options['aliases'], 'array of strings'); - } - if (! is_integer($options['chunkSizeBytes'])) { throw InvalidArgumentException::invalidType('"chunkSizeBytes" option', $options['chunkSizeBytes'], 'integer'); } @@ -123,31 +84,18 @@ public function __construct(CollectionWrapper $collectionWrapper, string $filena throw new InvalidArgumentException(sprintf('Expected "chunkSizeBytes" option to be >= 1, %d given', $options['chunkSizeBytes'])); } - if (! is_bool($options['disableMD5'])) { - throw InvalidArgumentException::invalidType('"disableMD5" option', $options['disableMD5'], 'boolean'); - } - - if (isset($options['contentType']) && ! is_string($options['contentType'])) { - throw InvalidArgumentException::invalidType('"contentType" option', $options['contentType'], 'string'); - } - - if (isset($options['metadata']) && ! is_array($options['metadata']) && ! is_object($options['metadata'])) { - throw InvalidArgumentException::invalidType('"metadata" option', $options['metadata'], 'array or object'); + if (isset($options['metadata']) && ! is_document($options['metadata'])) { + throw InvalidArgumentException::expectedDocumentType('"metadata" option', $options['metadata']); } $this->chunkSize = $options['chunkSizeBytes']; - $this->collectionWrapper = $collectionWrapper; - $this->disableMD5 = $options['disableMD5']; - - if (! $this->disableMD5) { - $this->hashCtx = hash_init('md5'); - } - $this->file = [ '_id' => $options['_id'], 'chunkSize' => $this->chunkSize, 'filename' => $filename, - ] + array_intersect_key($options, ['aliases' => 1, 'contentType' => 1, 'metadata' => 1]); + 'length' => null, + 'uploadDate' => null, + ] + array_intersect_key($options, ['metadata' => 1]); } /** @@ -248,25 +196,18 @@ private function abort(): void { try { $this->collectionWrapper->deleteChunksByFilesId($this->file['_id']); - } catch (DriverRuntimeException $e) { + } catch (DriverRuntimeException) { // We are already handling an error if abort() is called, so suppress this } $this->isClosed = true; } - /** - * @return mixed - */ - private function fileCollectionInsert() + private function fileCollectionInsert(): void { $this->file['length'] = $this->length; $this->file['uploadDate'] = new UTCDateTime(); - if (! $this->disableMD5 && $this->hashCtx) { - $this->file['md5'] = hash_final($this->hashCtx); - } - try { $this->collectionWrapper->insertFile($this->file); } catch (DriverRuntimeException $e) { @@ -274,8 +215,6 @@ private function fileCollectionInsert() throw $e; } - - return $this->file['_id']; } private function insertChunkFromBuffer(): void @@ -290,13 +229,9 @@ private function insertChunkFromBuffer(): void $chunk = [ 'files_id' => $this->file['_id'], 'n' => $this->chunkOffset, - 'data' => new Binary($data, Binary::TYPE_GENERIC), + 'data' => new Binary($data), ]; - if (! $this->disableMD5 && $this->hashCtx) { - hash_update($this->hashCtx, $data); - } - try { $this->collectionWrapper->insertChunk($chunk); } catch (DriverRuntimeException $e) { diff --git a/src/InsertManyResult.php b/src/InsertManyResult.php index d1aabe558..2f27bd24e 100644 --- a/src/InsertManyResult.php +++ b/src/InsertManyResult.php @@ -17,28 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a multi-document insert operation. */ class InsertManyResult { - /** @var WriteResult */ - private $writeResult; - - /** @var array */ - private $insertedIds; - - /** @var boolean */ - private $isAcknowledged; - - public function __construct(WriteResult $writeResult, array $insertedIds) + public function __construct(private WriteResult $writeResult, private array $insertedIds) { - $this->writeResult = $writeResult; - $this->insertedIds = $insertedIds; - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -47,16 +35,11 @@ public function __construct(WriteResult $writeResult, array $insertedIds) * This method should only be called if the write was acknowledged. * * @see InsertManyResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -67,10 +50,8 @@ public function getInsertedCount() * the driver did not generate an ID), the index will contain its "_id" * field value. Any driver-generated ID will be a MongoDB\BSON\ObjectId * instance. - * - * @return array */ - public function getInsertedIds() + public function getInsertedIds(): array { return $this->insertedIds; } @@ -80,10 +61,8 @@ public function getInsertedIds() * * If the insert was not acknowledged, other fields from the WriteResult * (e.g. insertedCount) will be undefined. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { return $this->writeResult->isAcknowledged(); } diff --git a/src/InsertOneResult.php b/src/InsertOneResult.php index 6a74eb900..47e1d26bc 100644 --- a/src/InsertOneResult.php +++ b/src/InsertOneResult.php @@ -17,31 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for a single-document insert operation. */ class InsertOneResult { - /** @var WriteResult */ - private $writeResult; - - /** @var mixed */ - private $insertedId; - - /** @var boolean */ - private $isAcknowledged; - - /** - * @param mixed $insertedId - */ - public function __construct(WriteResult $writeResult, $insertedId) + public function __construct(private WriteResult $writeResult, private mixed $insertedId) { - $this->writeResult = $writeResult; - $this->insertedId = $insertedId; - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -50,16 +35,11 @@ public function __construct(WriteResult $writeResult, $insertedId) * This method should only be called if the write was acknowledged. * * @see InsertOneResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getInsertedCount() + public function getInsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getInsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getInsertedCount(); } /** @@ -68,10 +48,8 @@ public function getInsertedCount() * If the document had an ID prior to inserting (i.e. the driver did not * need to generate an ID), this will contain its "_id". Any * driver-generated ID will be a MongoDB\BSON\ObjectId instance. - * - * @return mixed */ - public function getInsertedId() + public function getInsertedId(): mixed { return $this->insertedId; } @@ -85,10 +63,8 @@ public function getInsertedId() * If the insert was not acknowledged, other fields from the WriteResult * (e.g. insertedCount) will be undefined and their getter methods should * not be invoked. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { return $this->writeResult->isAcknowledged(); } diff --git a/src/MapReduceResult.php b/src/MapReduceResult.php deleted file mode 100644 index d7ef777a3..000000000 --- a/src/MapReduceResult.php +++ /dev/null @@ -1,109 +0,0 @@ -getIterator = $getIterator; - $this->executionTimeMS = isset($result->timeMillis) ? (integer) $result->timeMillis : 0; - $this->counts = isset($result->counts) ? (array) $result->counts : []; - $this->timing = isset($result->timing) ? (array) $result->timing : []; - } - - /** - * Returns various count statistics from the mapReduce command. - * - * @return array - */ - public function getCounts() - { - return $this->counts; - } - - /** - * Return the command execution time in milliseconds. - * - * @return integer - */ - public function getExecutionTimeMS() - { - return $this->executionTimeMS; - } - - /** - * Return the mapReduce results as a Traversable. - * - * @see https://php.net/iteratoraggregate.getiterator - * @return Traversable - */ - #[ReturnTypeWillChange] - public function getIterator() - { - return call_user_func($this->getIterator); - } - - /** - * Returns various timing statistics from the mapReduce command. - * - * Note: timing statistics are only available if the mapReduce command's - * "verbose" option was true; otherwise, an empty array will be returned. - * - * @return array - */ - public function getTiming() - { - return $this->timing; - } -} diff --git a/src/Model/BSONArray.php b/src/Model/BSONArray.php index a87505f29..040350ce4 100644 --- a/src/Model/BSONArray.php +++ b/src/Model/BSONArray.php @@ -21,7 +21,6 @@ use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; -use ReturnTypeWillChange; use function array_values; use function MongoDB\recursive_copy; @@ -32,7 +31,7 @@ * The internal data will be filtered through array_values() during BSON * serialization to ensure that it becomes a BSON array. * - * @api + * @template-extends ArrayObject */ class BSONArray extends ArrayObject implements JsonSerializable, Serializable, Unserializable { @@ -51,11 +50,10 @@ public function __clone() * * @see https://php.net/oop5.magic#object.set-state * @see https://php.net/var-export - * @return self */ - public static function __set_state(array $properties) + public static function __set_state(array $properties): self { - $array = new static(); + $array = new self(); $array->exchangeArray($properties); return $array; @@ -68,10 +66,8 @@ public static function __set_state(array $properties) * as a BSON array. * * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return array */ - #[ReturnTypeWillChange] - public function bsonSerialize() + public function bsonSerialize(): array { return array_values($this->getArrayCopy()); } @@ -80,12 +76,11 @@ public function bsonSerialize() * Unserialize the document to BSON. * * @see https://php.net/mongodb-bson-unserializable.bsonunserialize - * @param array $data Array data + * @param array $data Array data */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) + public function bsonUnserialize(array $data): void { - self::__construct($data); + parent::__construct($data); } /** @@ -95,10 +90,8 @@ public function bsonUnserialize(array $data) * as a JSON array. * * @see https://php.net/jsonserializable.jsonserialize - * @return array */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): array { return array_values($this->getArrayCopy()); } diff --git a/src/Model/BSONDocument.php b/src/Model/BSONDocument.php index 8fc04e84f..545f08dc0 100644 --- a/src/Model/BSONDocument.php +++ b/src/Model/BSONDocument.php @@ -17,11 +17,12 @@ namespace MongoDB\Model; +use ArrayIterator; use ArrayObject; use JsonSerializable; use MongoDB\BSON\Serializable; use MongoDB\BSON\Unserializable; -use ReturnTypeWillChange; +use stdClass; use function MongoDB\recursive_copy; @@ -31,7 +32,7 @@ * The internal data will be cast to an object during BSON serialization to * ensure that it becomes a BSON document. * - * @api + * @template-extends ArrayObject */ class BSONDocument extends ArrayObject implements JsonSerializable, Serializable, Unserializable { @@ -50,8 +51,10 @@ public function __clone() * by default. * * @see https://php.net/arrayobject.construct + * @param array|stdClass $input + * @psalm-param class-string>|class-string> $iteratorClass */ - public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_AS_PROPS, string $iteratorClass = 'ArrayIterator') + public function __construct(array|stdClass $input = [], int $flags = ArrayObject::ARRAY_AS_PROPS, string $iteratorClass = ArrayIterator::class) { parent::__construct($input, $flags, $iteratorClass); } @@ -61,11 +64,10 @@ public function __construct(array $input = [], int $flags = ArrayObject::ARRAY_A * * @see https://php.net/oop5.magic#object.set-state * @see https://php.net/var-export - * @return self */ - public static function __set_state(array $properties) + public static function __set_state(array $properties): self { - $document = new static(); + $document = new self(); $document->exchangeArray($properties); return $document; @@ -75,10 +77,8 @@ public static function __set_state(array $properties) * Serialize the document to BSON. * * @see https://php.net/mongodb-bson-serializable.bsonserialize - * @return object */ - #[ReturnTypeWillChange] - public function bsonSerialize() + public function bsonSerialize(): stdClass { return (object) $this->getArrayCopy(); } @@ -87,10 +87,9 @@ public function bsonSerialize() * Unserialize the document to BSON. * * @see https://php.net/mongodb-bson-unserializable.bsonunserialize - * @param array $data Array data + * @param array $data Array data */ - #[ReturnTypeWillChange] - public function bsonUnserialize(array $data) + public function bsonUnserialize(array $data): void { parent::__construct($data, ArrayObject::ARRAY_AS_PROPS); } @@ -99,10 +98,8 @@ public function bsonUnserialize(array $data) * Serialize the array to JSON. * * @see https://php.net/jsonserializable.jsonserialize - * @return object */ - #[ReturnTypeWillChange] - public function jsonSerialize() + public function jsonSerialize(): stdClass { return (object) $this->getArrayCopy(); } diff --git a/src/Model/BSONIterator.php b/src/Model/BSONIterator.php index 2bddc264c..e90901173 100644 --- a/src/Model/BSONIterator.php +++ b/src/Model/BSONIterator.php @@ -18,12 +18,13 @@ namespace MongoDB\Model; use Iterator; +use MongoDB\BSON\Document; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use ReturnTypeWillChange; +use function assert; use function is_array; -use function MongoDB\BSON\toPHP; +use function is_int; use function sprintf; use function strlen; use function substr; @@ -31,96 +32,45 @@ /** * Iterator for BSON documents. + * + * @template-implements Iterator */ class BSONIterator implements Iterator { - /** @var integer */ - private static $bsonSize = 4; - - /** @var string */ - private $buffer; + private const BSON_SIZE = 4; - /** @var integer */ - private $bufferLength; + private string $buffer; - /** @var mixed */ - private $current; + private int $bufferLength; - /** @var integer */ - private $key = 0; + private array|object|null $current = null; - /** @var integer */ - private $position = 0; + private int $key = 0; - /** @var array */ - private $options; + private int $position = 0; - /** - * Constructs a BSON Iterator. - * - * Supported options: - * - * * typeMap (array): Type map for BSON deserialization. - * - * @internal - * @see https://php.net/manual/en/function.mongodb.bson-tophp.php - * @param string $data Concatenated, valid, BSON-encoded documents - * @param array $options Iterator options - * @throws InvalidArgumentException for parameter/option parsing errors - */ - public function __construct(string $data, array $options = []) - { - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); - } - - if (! isset($options['typeMap'])) { - $options['typeMap'] = []; - } - - $this->buffer = $data; - $this->bufferLength = strlen($data); - $this->options = $options; - } - - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() + /** @see https://php.net/iterator.current */ + public function current(): mixed { return $this->current; } - /** - * @see https://php.net/iterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iterator.key */ + public function key(): int { return $this->key; } - /** - * @see https://php.net/iterator.next - * @return void - */ - #[ReturnTypeWillChange] - public function next() + /** @see https://php.net/iterator.next */ + public function next(): void { $this->key++; $this->current = null; $this->advance(); } - /** - * @see https://php.net/iterator.rewind - * @return void - */ - #[ReturnTypeWillChange] - public function rewind() + /** @see https://php.net/iterator.rewind */ + public function rewind(): void { $this->key = 0; $this->position = 0; @@ -128,32 +78,57 @@ public function rewind() $this->advance(); } - /** - * @see https://php.net/iterator.valid - */ - #[ReturnTypeWillChange] + /** @see https://php.net/iterator.valid */ public function valid(): bool { return $this->current !== null; } + /** + * Constructs a BSON Iterator. + * + * Supported options: + * + * * typeMap (array): Type map for BSON deserialization. + * + * @internal + * @see https://php.net/manual/en/function.mongodb.bson-tophp.php + * @param string $data Concatenated, valid, BSON-encoded documents + * @param array $options Iterator options + * @throws InvalidArgumentException for parameter/option parsing errors + */ + public function __construct(string $data, private array $options = []) + { + if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + } + + if (! isset($options['typeMap'])) { + $this->options['typeMap'] = []; + } + + $this->buffer = $data; + $this->bufferLength = strlen($data); + } + private function advance(): void { if ($this->position === $this->bufferLength) { return; } - if ($this->bufferLength - $this->position < self::$bsonSize) { - throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::$bsonSize, $this->bufferLength - $this->position)); + if ($this->bufferLength - $this->position < self::BSON_SIZE) { + throw new UnexpectedValueException(sprintf('Expected at least %d bytes; %d remaining', self::BSON_SIZE, $this->bufferLength - $this->position)); } - [, $documentLength] = unpack('V', substr($this->buffer, $this->position, self::$bsonSize)); + [, $documentLength] = unpack('V', substr($this->buffer, $this->position, self::BSON_SIZE)); + assert(is_int($documentLength)); if ($this->bufferLength - $this->position < $documentLength) { throw new UnexpectedValueException(sprintf('Expected %d bytes; %d remaining', $documentLength, $this->bufferLength - $this->position)); } - $this->current = toPHP(substr($this->buffer, $this->position, $documentLength), $this->options['typeMap']); + $this->current = Document::fromBSON(substr($this->buffer, $this->position, $documentLength))->toPHP($this->options['typeMap']); $this->position += $documentLength; } } diff --git a/src/Model/CachingIterator.php b/src/Model/CachingIterator.php index f2a7dd25b..64c61708f 100644 --- a/src/Model/CachingIterator.php +++ b/src/Model/CachingIterator.php @@ -20,7 +20,6 @@ use Countable; use Iterator; use IteratorIterator; -use ReturnTypeWillChange; use Traversable; use function count; @@ -36,23 +35,24 @@ * those operations (e.g. MongoDB\Driver\Cursor). * * @internal + * @template TKey of array-key + * @template TValue + * @template-implements Iterator */ -class CachingIterator implements Countable, Iterator +final class CachingIterator implements Countable, Iterator { private const FIELD_KEY = 0; private const FIELD_VALUE = 1; - /** @var array */ - private $items = []; + /** @var list */ + private array $items = []; - /** @var Iterator */ - private $iterator; + /** @var Iterator */ + private Iterator $iterator; - /** @var boolean */ - private $iteratorAdvanced = false; + private bool $iteratorAdvanced = false; - /** @var boolean */ - private $iteratorExhausted = false; + private bool $iteratorExhausted = false; /** * Initialize the iterator and stores the first item in the cache. This @@ -60,7 +60,7 @@ class CachingIterator implements Countable, Iterator * Additionally, this mimics behavior of the SPL iterators and allows users * to omit an explicit call to rewind() before using the other methods. * - * @param Traversable $traversable + * @param Traversable $traversable */ public function __construct(Traversable $traversable) { @@ -70,9 +70,7 @@ public function __construct(Traversable $traversable) $this->storeCurrentItem(); } - /** - * @see https://php.net/countable.count - */ + /** @see https://php.net/countable.count */ public function count(): int { $this->exhaustIterator(); @@ -80,33 +78,26 @@ public function count(): int return count($this->items); } - /** - * @see https://php.net/iterator.current - * @return mixed - */ - #[ReturnTypeWillChange] - public function current() + /** @see https://php.net/iterator.current */ + public function current(): mixed { $currentItem = current($this->items); - return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : false; + return $currentItem !== false ? $currentItem[self::FIELD_VALUE] : null; } /** * @see https://php.net/iterator.key - * @return mixed + * @psalm-return TKey|null */ - #[ReturnTypeWillChange] - public function key() + public function key(): mixed { $currentItem = current($this->items); return $currentItem !== false ? $currentItem[self::FIELD_KEY] : null; } - /** - * @see https://php.net/iterator.next - */ + /** @see https://php.net/iterator.next */ public function next(): void { if (! $this->iteratorExhausted) { @@ -114,16 +105,12 @@ public function next(): void $this->iterator->next(); $this->storeCurrentItem(); - - $this->iteratorExhausted = ! $this->iterator->valid(); } next($this->items); } - /** - * @see https://php.net/iterator.rewind - */ + /** @see https://php.net/iterator.rewind */ public function rewind(): void { /* If the iterator has advanced, exhaust it now so that future iteration @@ -136,9 +123,7 @@ public function rewind(): void reset($this->items); } - /** - * @see https://php.net/iterator.valid - */ + /** @see https://php.net/iterator.valid */ public function valid(): bool { return $this->key() !== null; @@ -160,6 +145,8 @@ private function exhaustIterator(): void private function storeCurrentItem(): void { if (! $this->iterator->valid()) { + $this->iteratorExhausted = true; + return; } diff --git a/src/Model/CallbackIterator.php b/src/Model/CallbackIterator.php index 8e3c68d0c..4a8e5dda9 100644 --- a/src/Model/CallbackIterator.php +++ b/src/Model/CallbackIterator.php @@ -17,26 +17,35 @@ namespace MongoDB\Model; -use Closure; use Iterator; use IteratorIterator; -use ReturnTypeWillChange; use Traversable; +use function call_user_func; + /** * Iterator to apply a callback before returning an element * * @internal + * + * @template TKey of array-key + * @template TValue + * @template TCallbackValue + * @template-implements Iterator */ -class CallbackIterator implements Iterator +final class CallbackIterator implements Iterator { - /** @var Closure */ + /** @var callable(TValue, TKey): TCallbackValue */ private $callback; - /** @var Iterator */ - private $iterator; + /** @var Iterator */ + private Iterator $iterator; - public function __construct(Traversable $traversable, Closure $callback) + /** + * @param Traversable $traversable + * @param callable(TValue, TKey): TCallbackValue $callback + */ + public function __construct(Traversable $traversable, callable $callback) { $this->iterator = $traversable instanceof Iterator ? $traversable : new IteratorIterator($traversable); $this->callback = $callback; @@ -44,43 +53,35 @@ public function __construct(Traversable $traversable, Closure $callback) /** * @see https://php.net/iterator.current - * @return mixed + * @return TCallbackValue */ - #[ReturnTypeWillChange] - public function current() + public function current(): mixed { - return ($this->callback)($this->iterator->current()); + return call_user_func($this->callback, $this->iterator->current(), $this->iterator->key()); } /** * @see https://php.net/iterator.key - * @return mixed + * @return TKey */ - #[ReturnTypeWillChange] - public function key() + public function key(): mixed { return $this->iterator->key(); } - /** - * @see https://php.net/iterator.next - */ + /** @see https://php.net/iterator.next */ public function next(): void { $this->iterator->next(); } - /** - * @see https://php.net/iterator.rewind - */ + /** @see https://php.net/iterator.rewind */ public function rewind(): void { $this->iterator->rewind(); } - /** - * @see https://php.net/iterator.valid - */ + /** @see https://php.net/iterator.valid */ public function valid(): bool { return $this->iterator->valid(); diff --git a/src/Model/ChangeStreamIterator.php b/src/Model/ChangeStreamIterator.php index ceb31f9fc..4cd7a57af 100644 --- a/src/Model/ChangeStreamIterator.php +++ b/src/Model/ChangeStreamIterator.php @@ -18,8 +18,9 @@ namespace MongoDB\Model; use IteratorIterator; +use MongoDB\BSON\Document; use MongoDB\BSON\Serializable; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Monitoring\CommandFailedEvent; use MongoDB\Driver\Monitoring\CommandStartedEvent; use MongoDB\Driver\Monitoring\CommandSubscriber; @@ -28,7 +29,6 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\ResumeTokenException; use MongoDB\Exception\UnexpectedValueException; -use ReturnTypeWillChange; use function assert; use function count; @@ -36,6 +36,7 @@ use function is_object; use function MongoDB\Driver\Monitoring\addSubscriber; use function MongoDB\Driver\Monitoring\removeSubscriber; +use function MongoDB\is_document; /** * ChangeStreamIterator wraps a change stream's tailable cursor. @@ -45,106 +46,44 @@ * rewind() do not execute getMore commands. * * @internal + * @template TValue of array|object + * @template-extends IteratorIterator> */ -class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber +final class ChangeStreamIterator extends IteratorIterator implements CommandSubscriber { - /** @var integer */ - private $batchPosition = 0; + private int $batchPosition = 0; - /** @var integer */ - private $batchSize; + private int $batchSize; - /** @var boolean */ - private $isRewindNop; + private bool $isRewindNop; - /** @var boolean */ - private $isValid = false; + private bool $isValid = false; - /** @var object|null */ - private $postBatchResumeToken; + private array|object|null $resumeToken = null; - /** @var array|object|null */ - private $resumeToken; - - /** @var Server */ - private $server; - - /** - * @internal - * @param array|object|null $initialResumeToken - */ - public function __construct(Cursor $cursor, int $firstBatchSize, $initialResumeToken, ?object $postBatchResumeToken) - { - if (isset($initialResumeToken) && ! is_array($initialResumeToken) && ! is_object($initialResumeToken)) { - throw InvalidArgumentException::invalidType('$initialResumeToken', $initialResumeToken, 'array or object'); - } - - parent::__construct($cursor); - - $this->batchSize = $firstBatchSize; - $this->isRewindNop = ($firstBatchSize === 0); - $this->postBatchResumeToken = $postBatchResumeToken; - $this->resumeToken = $initialResumeToken; - $this->server = $cursor->getServer(); - } - - /** @internal */ - final public function commandFailed(CommandFailedEvent $event): void - { - } - - /** @internal */ - final public function commandStarted(CommandStartedEvent $event): void - { - if ($event->getCommandName() !== 'getMore') { - return; - } - - $this->batchPosition = 0; - $this->batchSize = 0; - $this->postBatchResumeToken = null; - } - - /** @internal */ - final public function commandSucceeded(CommandSucceededEvent $event): void - { - if ($event->getCommandName() !== 'getMore') { - return; - } - - $reply = $event->getReply(); - - if (! isset($reply->cursor->nextBatch) || ! is_array($reply->cursor->nextBatch)) { - throw new UnexpectedValueException('getMore command did not return a "cursor.nextBatch" array'); - } - - $this->batchSize = count($reply->cursor->nextBatch); - - if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) { - $this->postBatchResumeToken = $reply->cursor->postBatchResumeToken; - } - } + private Server $server; /** * @see https://php.net/iteratoriterator.current - * @return mixed + * @psalm-return TValue|null */ - #[ReturnTypeWillChange] - public function current() + public function current(): array|object|null { - return $this->isValid ? parent::current() : null; + return $this->valid() ? parent::current() : null; } /** - * Necessary to let psalm know that we're always expecting a cursor as inner - * iterator. This could be side-stepped due to the class not being final, - * but it's very much an invalid use-case. This method can be dropped in 2.0 - * once the class is final. + * This method is necessary as psalm does not properly set the return type + * of IteratorIterator::getInnerIterator to the templated iterator + * + * @see https://github.com/vimeo/psalm/pull/11100. + * + * @return CursorInterface */ - final public function getInnerIterator(): Cursor + public function getInnerIterator(): CursorInterface { $cursor = parent::getInnerIterator(); - assert($cursor instanceof Cursor); + assert($cursor instanceof CursorInterface); return $cursor; } @@ -155,10 +94,8 @@ final public function getInnerIterator(): Cursor * Null may be returned if no change documents have been iterated and the * server did not include a postBatchResumeToken in its aggregate or getMore * command response. - * - * @return array|object|null */ - public function getResumeToken() + public function getResumeToken(): array|object|null { return $this->resumeToken; } @@ -171,19 +108,13 @@ public function getServer(): Server return $this->server; } - /** - * @see https://php.net/iteratoriterator.key - * @return mixed - */ - #[ReturnTypeWillChange] - public function key() + /** @see https://php.net/iteratoriterator.key */ + public function key(): ?int { - return $this->isValid ? parent::key() : null; + return $this->valid() ? parent::key() : null; } - /** - * @see https://php.net/iteratoriterator.rewind - */ + /** @see https://php.net/iteratoriterator.rewind */ public function next(): void { /* Determine if advancing the iterator will execute a getMore command @@ -199,6 +130,7 @@ public function next(): void try { parent::next(); + $this->onIteration(! $getMore); } finally { if ($getMore) { @@ -207,9 +139,7 @@ public function next(): void } } - /** - * @see https://php.net/iteratoriterator.rewind - */ + /** @see https://php.net/iteratoriterator.rewind */ public function rewind(): void { if ($this->isRewindNop) { @@ -217,38 +147,102 @@ public function rewind(): void } parent::rewind(); + $this->onIteration(false); } /** * @see https://php.net/iteratoriterator.valid + * @psalm-assert-if-true TValue $this->current() */ public function valid(): bool { return $this->isValid; } + /** + * @internal + * @psalm-param CursorInterface $cursor + */ + public function __construct(CursorInterface $cursor, int $firstBatchSize, array|object|null $initialResumeToken, private ?object $postBatchResumeToken = null) + { + if (isset($initialResumeToken) && ! is_document($initialResumeToken)) { + throw InvalidArgumentException::expectedDocumentType('$initialResumeToken', $initialResumeToken); + } + + parent::__construct($cursor); + + $this->batchSize = $firstBatchSize; + $this->isRewindNop = ($firstBatchSize === 0); + $this->resumeToken = $initialResumeToken; + $this->server = $cursor->getServer(); + } + + /** @internal */ + final public function commandFailed(CommandFailedEvent $event): void + { + } + + /** @internal */ + final public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() !== 'getMore') { + return; + } + + $this->batchPosition = 0; + $this->batchSize = 0; + $this->postBatchResumeToken = null; + } + + /** @internal */ + final public function commandSucceeded(CommandSucceededEvent $event): void + { + if ($event->getCommandName() !== 'getMore') { + return; + } + + $reply = $event->getReply(); + + if (! isset($reply->cursor->nextBatch) || ! is_array($reply->cursor->nextBatch)) { + throw new UnexpectedValueException('getMore command did not return a "cursor.nextBatch" array'); + } + + $this->batchSize = count($reply->cursor->nextBatch); + + if (isset($reply->cursor->postBatchResumeToken) && is_object($reply->cursor->postBatchResumeToken)) { + $this->postBatchResumeToken = $reply->cursor->postBatchResumeToken; + } + } + /** * Extracts the resume token (i.e. "_id" field) from a change document. * * @param array|object $document Change document - * @return array|object * @throws InvalidArgumentException * @throws ResumeTokenException if the resume token is not found or invalid */ - private function extractResumeToken($document) + private function extractResumeToken(array|object $document): array|object { - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType('$document', $document); } if ($document instanceof Serializable) { return $this->extractResumeToken($document->bsonSerialize()); } - $resumeToken = is_array($document) - ? ($document['_id'] ?? null) - : ($document->_id ?? null); + if ($document instanceof Document) { + $resumeToken = $document->get('_id'); + + if ($resumeToken instanceof Document) { + $resumeToken = $resumeToken->toPHP(); + } + } else { + $resumeToken = is_array($document) + ? ($document['_id'] ?? null) + : ($document->_id ?? null); + } if (! isset($resumeToken)) { $this->isValid = false; @@ -276,7 +270,7 @@ private function isAtEndOfBatch(): bool /** * Perform housekeeping after an iteration event. * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#updating-the-cached-resume-token + * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.md#updating-the-cached-resume-token */ private function onIteration(bool $incrementBatchPosition): void { @@ -285,11 +279,11 @@ private function onIteration(bool $incrementBatchPosition): void /* Disable rewind()'s NOP behavior once we advance to a valid position. * This will allow the driver to throw a LogicException if rewind() is * called after the cursor has advanced past its first element. */ - if ($this->isRewindNop && $this->isValid) { + if ($this->isRewindNop && $this->valid()) { $this->isRewindNop = false; } - if ($incrementBatchPosition && $this->isValid) { + if ($incrementBatchPosition && $this->valid()) { $this->batchPosition++; } @@ -301,7 +295,7 @@ private function onIteration(bool $incrementBatchPosition): void * from the current document if possible. */ if ($this->isAtEndOfBatch() && $this->postBatchResumeToken !== null) { $this->resumeToken = $this->postBatchResumeToken; - } elseif ($this->isValid) { + } elseif ($this->valid()) { $this->resumeToken = $this->extractResumeToken($this->current()); } } diff --git a/src/Model/CodecCursor.php b/src/Model/CodecCursor.php new file mode 100644 index 000000000..d7a3776b2 --- /dev/null +++ b/src/Model/CodecCursor.php @@ -0,0 +1,121 @@ + + */ +class CodecCursor implements CursorInterface +{ + private const TYPEMAP = ['root' => 'bson']; + + /** @var TValue|null */ + private ?object $current = null; + + /** @return TValue */ + public function current(): ?object + { + if (! $this->current && $this->valid()) { + $value = $this->cursor->current(); + assert($value instanceof Document); + $this->current = $this->codec->decode($value); + } + + return $this->current; + } + + /** + * @template NativeClass of Object + * @param DocumentCodec $codec + * @return self + */ + public static function fromCursor(CursorInterface $cursor, DocumentCodec $codec): self + { + $cursor->setTypeMap(self::TYPEMAP); + + return new self($cursor, $codec); + } + + public function getId(): Int64 + { + return $this->cursor->getId(); + } + + public function getServer(): Server + { + return $this->cursor->getServer(); + } + + public function isDead(): bool + { + return $this->cursor->isDead(); + } + + public function key(): int + { + return $this->cursor->key(); + } + + public function next(): void + { + $this->current = null; + $this->cursor->next(); + } + + public function rewind(): void + { + $this->current = null; + $this->cursor->rewind(); + } + + public function setTypeMap(array $typemap): void + { + // Not supported + trigger_error(sprintf('Discarding type map for %s', __METHOD__), E_USER_WARNING); + } + + /** @return array */ + public function toArray(): array + { + return iterator_to_array($this); + } + + public function valid(): bool + { + return $this->cursor->valid(); + } + + /** @param DocumentCodec $codec */ + private function __construct(private CursorInterface $cursor, private DocumentCodec $codec) + { + } +} diff --git a/src/Model/CollectionInfo.php b/src/Model/CollectionInfo.php index 6722978bc..3d273e598 100644 --- a/src/Model/CollectionInfo.php +++ b/src/Model/CollectionInfo.php @@ -19,7 +19,6 @@ use ArrayAccess; use MongoDB\Exception\BadMethodCallException; -use ReturnTypeWillChange; use function array_key_exists; @@ -30,30 +29,23 @@ * command or, for legacy servers, queries on the "system.namespaces" * collection. It provides methods to access options for the collection. * - * @api * @see \MongoDB\Database::listCollections() - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.rst + * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-collections.md + * @template-implements ArrayAccess */ class CollectionInfo implements ArrayAccess { - /** @var array */ - private $info; - - /** - * @param array $info Collection info - */ - public function __construct(array $info) + /** @param array $info Collection info */ + public function __construct(private array $info) { - $this->info = $info; } /** * Return the collection info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } @@ -62,26 +54,22 @@ public function __debugInfo() * Return the maximum number of documents to keep in the capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return integer|null */ - public function getCappedMax() + public function getCappedMax(): ?int { /* The MongoDB server might return this number as an integer or float */ - return isset($this->info['options']['max']) ? (integer) $this->info['options']['max'] : null; + return isset($this->info['options']['max']) ? (int) $this->info['options']['max'] : null; } /** * Return the maximum size (in bytes) of the capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return integer|null */ - public function getCappedSize() + public function getCappedSize(): ?int { /* The MongoDB server might return this number as an integer or float */ - return isset($this->info['options']['size']) ? (integer) $this->info['options']['size'] : null; + return isset($this->info['options']['size']) ? (int) $this->info['options']['size'] : null; } /** @@ -106,9 +94,8 @@ public function getInfo(): array * Return the collection name. * * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } @@ -117,9 +104,8 @@ public function getName() * Return the collection options. * * @see https://mongodb.com/docs/manual/reference/command/listCollections/#output - * @return array */ - public function getOptions() + public function getOptions(): array { return (array) ($this->info['options'] ?? []); } @@ -138,51 +124,49 @@ public function getType(): string * Return whether the collection is a capped collection. * * @deprecated 1.0 Deprecated in favor of using getOptions - * - * @return boolean */ - public function isCapped() + public function isCapped(): bool { return ! empty($this->info['options']['capped']); } + /** + * Determines whether the collection is a view. + */ + public function isView(): bool + { + return $this->getType() === 'view'; + } + /** * Check whether a field exists in the collection information. * * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists(mixed $offset): bool { - return array_key_exists($key, $this->info); + return array_key_exists($offset, $this->info); } /** * Return the field's value from the collection information. * * @see https://php.net/arrayaccess.offsetget - * @param mixed $key - * @return mixed + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet(mixed $offset): mixed { - return $this->info[$key]; + return $this->info[$offset]; } /** * Not supported. * * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -191,12 +175,9 @@ public function offsetSet($key, $value) * Not supported. * * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/CollectionInfoCommandIterator.php b/src/Model/CollectionInfoCommandIterator.php deleted file mode 100644 index 96c2d73a9..000000000 --- a/src/Model/CollectionInfoCommandIterator.php +++ /dev/null @@ -1,62 +0,0 @@ -databaseName = $databaseName; - } - - /** - * Return the current element as a CollectionInfo instance. - * - * @see CollectionInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): CollectionInfo - { - $info = parent::current(); - - if ($this->databaseName !== null && isset($info['idIndex']) && ! isset($info['idIndex']['ns'])) { - $info['idIndex']['ns'] = $this->databaseName . '.' . $info['name']; - } - - return new CollectionInfo($info); - } -} diff --git a/src/Model/CollectionInfoIterator.php b/src/Model/CollectionInfoIterator.php deleted file mode 100644 index aa6cb3afb..000000000 --- a/src/Model/CollectionInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ - */ class DatabaseInfo implements ArrayAccess { - /** @var array */ - private $info; - - /** - * @param array $info Database info - */ - public function __construct(array $info) + /** @param array $info Database info */ + public function __construct(private array $info) { - $this->info = $info; } /** * Return the database info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } /** * Return the database name. - * - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } /** * Return the databases size on disk (in bytes). - * - * @return integer */ - public function getSizeOnDisk() + public function getSizeOnDisk(): int { /* The MongoDB server might return this number as an integer or float */ - return (integer) $this->info['sizeOnDisk']; + return (int) $this->info['sizeOnDisk']; } /** * Return whether the database is empty. - * - * @return boolean */ - public function isEmpty() + public function isEmpty(): bool { - return (boolean) $this->info['empty']; + return (bool) $this->info['empty']; } /** * Check whether a field exists in the database information. * * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists(mixed $offset): bool { - return array_key_exists($key, $this->info); + return array_key_exists($offset, $this->info); } /** * Return the field's value from the database information. * * @see https://php.net/arrayaccess.offsetget - * @param mixed $key - * @return mixed + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet(mixed $offset): mixed { - return $this->info[$key]; + return $this->info[$offset]; } /** * Not supported. * * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -133,12 +111,9 @@ public function offsetSet($key, $value) * Not supported. * * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/DatabaseInfoIterator.php b/src/Model/DatabaseInfoIterator.php deleted file mode 100644 index cb6135750..000000000 --- a/src/Model/DatabaseInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ -databases = $databases; - } - - /** - * Return the current element as a DatabaseInfo instance. - * - * @see DatabaseInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): DatabaseInfo - { - return new DatabaseInfo(current($this->databases)); - } - - /** - * Return the key of the current element. - * - * @see https://php.net/iterator.key - */ - public function key(): int - { - return key($this->databases); - } - - /** - * Move forward to next element. - * - * @see https://php.net/iterator.next - */ - public function next(): void - { - next($this->databases); - } - - /** - * Rewind the Iterator to the first element. - * - * @see https://php.net/iterator.rewind - */ - public function rewind(): void - { - reset($this->databases); - } - - /** - * Checks if current position is valid. - * - * @see https://php.net/iterator.valid - */ - public function valid(): bool - { - return key($this->databases) !== null; - } -} diff --git a/src/Model/IndexInfo.php b/src/Model/IndexInfo.php index 5e49568d2..86ae3dc6b 100644 --- a/src/Model/IndexInfo.php +++ b/src/Model/IndexInfo.php @@ -19,7 +19,7 @@ use ArrayAccess; use MongoDB\Exception\BadMethodCallException; -use ReturnTypeWillChange; +use Stringable; use function array_key_exists; use function array_search; @@ -34,122 +34,82 @@ * For information on keys and index options, see the referenced * db.collection.createIndex() documentation. * - * @api * @see \MongoDB\Collection::listIndexes() - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst + * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.md * @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/ + * @template-implements ArrayAccess */ -class IndexInfo implements ArrayAccess +class IndexInfo implements ArrayAccess, Stringable { - /** @var array */ - private $info; - - /** - * @param array $info Index info - */ - public function __construct(array $info) + /** @param array $info Index info */ + public function __construct(private array $info) { - $this->info = $info; } /** * Return the collection info as an array. * * @see https://php.net/oop5.magic#language.oop5.magic.debuginfo - * @return array */ - public function __debugInfo() + public function __debugInfo(): array { return $this->info; } /** * Return the index name to allow casting IndexInfo to string. - * - * @return string */ - public function __toString() + public function __toString(): string { return $this->getName(); } /** * Return the index key. - * - * @return array */ - public function getKey() + public function getKey(): array { return (array) $this->info['key']; } /** * Return the index name. - * - * @return string */ - public function getName() + public function getName(): string { return (string) $this->info['name']; } - /** - * Return the index namespace (e.g. "db.collection"). - * - * @return string - */ - public function getNamespace() - { - return (string) $this->info['ns']; - } - /** * Return the index version. - * - * @return integer */ - public function getVersion() + public function getVersion(): int { - return (integer) $this->info['v']; + return (int) $this->info['v']; } /** * Return whether or not this index is of type 2dsphere. - * - * @return boolean */ - public function is2dSphere() + public function is2dSphere(): bool { return array_search('2dsphere', $this->getKey(), true) !== false; } - /** - * Return whether or not this index is of type geoHaystack. - * - * @return boolean - */ - public function isGeoHaystack() - { - return array_search('geoHaystack', $this->getKey(), true) !== false; - } - /** * Return whether this is a sparse index. * * @see https://mongodb.com/docs/manual/core/index-sparse/ - * @return boolean */ - public function isSparse() + public function isSparse(): bool { return ! empty($this->info['sparse']); } /** * Return whether or not this index is of type text. - * - * @return boolean */ - public function isText() + public function isText(): bool { return array_search('text', $this->getKey(), true) !== false; } @@ -158,9 +118,8 @@ public function isText() * Return whether this is a TTL index. * * @see https://mongodb.com/docs/manual/core/index-ttl/ - * @return boolean */ - public function isTtl() + public function isTtl(): bool { return array_key_exists('expireAfterSeconds', $this->info); } @@ -169,9 +128,8 @@ public function isTtl() * Return whether this is a unique index. * * @see https://mongodb.com/docs/manual/core/index-unique/ - * @return boolean */ - public function isUnique() + public function isUnique(): bool { return ! empty($this->info['unique']); } @@ -180,13 +138,11 @@ public function isUnique() * Check whether a field exists in the index information. * * @see https://php.net/arrayaccess.offsetexists - * @param mixed $key - * @return boolean + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetExists($key) + public function offsetExists(mixed $offset): bool { - return array_key_exists($key, $this->info); + return array_key_exists($offset, $this->info); } /** @@ -197,27 +153,21 @@ public function offsetExists($key) * also be used to access fields that do not have a helper method. * * @see https://php.net/arrayaccess.offsetget - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst#getting-full-index-information - * @param mixed $key - * @return mixed + * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.md#getting-full-index-information + * @psalm-param array-key $offset */ - #[ReturnTypeWillChange] - public function offsetGet($key) + public function offsetGet(mixed $offset): mixed { - return $this->info[$key]; + return $this->info[$offset]; } /** * Not supported. * * @see https://php.net/arrayaccess.offsetset - * @param mixed $key - * @param mixed $value * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetSet($key, $value) + public function offsetSet(mixed $offset, mixed $value): void { throw BadMethodCallException::classIsImmutable(self::class); } @@ -226,12 +176,9 @@ public function offsetSet($key, $value) * Not supported. * * @see https://php.net/arrayaccess.offsetunset - * @param mixed $key * @throws BadMethodCallException - * @return void */ - #[ReturnTypeWillChange] - public function offsetUnset($key) + public function offsetUnset(mixed $offset): void { throw BadMethodCallException::classIsImmutable(self::class); } diff --git a/src/Model/IndexInfoIterator.php b/src/Model/IndexInfoIterator.php deleted file mode 100644 index 342fac6a8..000000000 --- a/src/Model/IndexInfoIterator.php +++ /dev/null @@ -1,40 +0,0 @@ -ns = $ns; - } - - /** - * Return the current element as an IndexInfo instance. - * - * @see IndexInfoIterator::current() - * @see https://php.net/iterator.current - */ - public function current(): IndexInfo - { - $info = parent::current(); - - if (! array_key_exists('ns', $info) && $this->ns !== null) { - $info['ns'] = $this->ns; - } - - return new IndexInfo($info); - } -} diff --git a/src/Model/IndexInput.php b/src/Model/IndexInput.php index 65c3fc2a2..98e47369c 100644 --- a/src/Model/IndexInput.php +++ b/src/Model/IndexInput.php @@ -19,13 +19,14 @@ use MongoDB\BSON\Serializable; use MongoDB\Exception\InvalidArgumentException; +use stdClass; +use Stringable; -use function is_array; use function is_float; use function is_int; -use function is_object; use function is_string; -use function MongoDB\generate_index_name; +use function MongoDB\document_to_array; +use function MongoDB\is_document; use function sprintf; /** @@ -35,26 +36,23 @@ * * @internal * @see \MongoDB\Collection::createIndexes() - * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.rst + * @see https://github.com/mongodb/specifications/blob/master/source/enumerate-indexes.md * @see https://mongodb.com/docs/manual/reference/method/db.collection.createIndex/ */ -class IndexInput implements Serializable +final class IndexInput implements Serializable, Stringable { - /** @var array */ - private $index; - /** * @param array $index Index specification * @throws InvalidArgumentException */ - public function __construct(array $index) + public function __construct(private array $index) { if (! isset($index['key'])) { throw new InvalidArgumentException('Required "key" document is missing from index specification'); } - if (! is_array($index['key']) && ! is_object($index['key'])) { - throw InvalidArgumentException::invalidType('"key" option', $index['key'], 'array or object'); + if (! is_document($index['key'])) { + throw InvalidArgumentException::expectedDocumentType('"key" option', $index['key']); } foreach ($index['key'] as $fieldName => $order) { @@ -64,14 +62,12 @@ public function __construct(array $index) } if (! isset($index['name'])) { - $index['name'] = generate_index_name($index['key']); + $this->index['name'] = $this->generateIndexName($index['key']); } - if (! is_string($index['name'])) { - throw InvalidArgumentException::invalidType('"name" option', $index['name'], 'string'); + if (! is_string($this->index['name'])) { + throw InvalidArgumentException::invalidType('"name" option', $this->index['name'], 'string'); } - - $this->index = $index; } /** @@ -88,8 +84,28 @@ public function __toString(): string * @see \MongoDB\Collection::createIndexes() * @see https://php.net/mongodb-bson-serializable.bsonserialize */ - public function bsonSerialize(): array + public function bsonSerialize(): stdClass + { + return (object) $this->index; + } + + /** + * Generate an index name from a key specification. + * + * @param array|object $document Document containing fields mapped to values, + * which denote order or an index type + * @throws InvalidArgumentException if $document is not an array or object + */ + private function generateIndexName(array|object $document): string { - return $this->index; + $document = document_to_array($document); + + $name = ''; + + foreach ($document as $field => $type) { + $name .= ($name !== '' ? '_' : '') . $field . '_' . $type; + } + + return $name; } } diff --git a/src/Model/SearchIndexInput.php b/src/Model/SearchIndexInput.php new file mode 100644 index 000000000..073deb1a0 --- /dev/null +++ b/src/Model/SearchIndexInput.php @@ -0,0 +1,73 @@ +index; + } +} diff --git a/src/Operation/Aggregate.php b/src/Operation/Aggregate.php index 040e3c9c9..b5da6470c 100644 --- a/src/Operation/Aggregate.php +++ b/src/Operation/Aggregate.php @@ -17,9 +17,9 @@ namespace MongoDB\Operation; -use ArrayIterator; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; @@ -29,44 +29,26 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; use MongoDB\Exception\UnsupportedException; +use MongoDB\Model\CodecCursor; use stdClass; -use function current; use function is_array; use function is_bool; use function is_integer; -use function is_object; use function is_string; -use function MongoDB\create_field_path_type_map; +use function MongoDB\is_document; use function MongoDB\is_last_pipeline_operator_write; -use function sprintf; +use function MongoDB\is_pipeline; /** * Operation for the aggregate command. * - * @api * @see \MongoDB\Collection::aggregate() * @see https://mongodb.com/docs/manual/reference/command/aggregate/ */ -class Aggregate implements Executable, Explainable +final class Aggregate implements Explainable { - /** @var string */ - private $databaseName; - - /** @var string|null */ - private $collectionName; - - /** @var array */ - private $pipeline; - - /** @var array */ - private $options; - - /** @var bool */ - private $isExplain; - - /** @var bool */ - private $isWrite; + private bool $isWrite; /** * Constructs an aggregate command. @@ -83,6 +65,9 @@ class Aggregate implements Executable, Explainable * circumvent document level validation. This only applies when an $out * or $merge stage is specified. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -118,12 +103,6 @@ class Aggregate implements Executable, Explainable * * typeMap (array): Type map for BSON deserialization. This will be * applied to the returned Cursor (it is not sent to the server). * - * * useCursor (boolean): Indicates whether the command will request that - * the server provide results using a cursor. The default is true. - * - * This option allows users to turn off cursors if necessary to aid in - * mongod/mongos upgrades. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. This only * applies when an $out or $merge stage is specified. * @@ -132,135 +111,111 @@ class Aggregate implements Executable, Explainable * * @param string $databaseName Database name * @param string|null $collectionName Collection name - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, ?string $collectionName, array $pipeline, array $options = []) + public function __construct(private string $databaseName, private ?string $collectionName, private array $pipeline, private array $options = []) { - $expectedIndex = 0; - - foreach ($pipeline as $i => $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$pipeline is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation) && ! is_object($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$pipeline[%d]', $i), $operation, 'array or object'); - } - - $expectedIndex += 1; + if (! is_pipeline($pipeline, true /* allowEmpty */)) { + throw new InvalidArgumentException('$pipeline is not a valid aggregation pipeline'); } - $options += ['useCursor' => true]; - - if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) { - throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean'); + if (isset($this->options['allowDiskUse']) && ! is_bool($this->options['allowDiskUse'])) { + throw InvalidArgumentException::invalidType('"allowDiskUse" option', $this->options['allowDiskUse'], 'boolean'); } - if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) { - throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer'); + if (isset($this->options['batchSize']) && ! is_integer($this->options['batchSize'])) { + throw InvalidArgumentException::invalidType('"batchSize" option', $this->options['batchSize'], 'integer'); } - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); + if (isset($this->options['bypassDocumentValidation']) && ! is_bool($this->options['bypassDocumentValidation'])) { + throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $this->options['bypassDocumentValidation'], 'boolean'); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['codec']) && ! $this->options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $this->options['codec'], DocumentCodec::class); } - if (isset($options['explain']) && ! is_bool($options['explain'])) { - throw InvalidArgumentException::invalidType('"explain" option', $options['explain'], 'boolean'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); + if (isset($this->options['explain']) && ! is_bool($this->options['explain'])) { + throw InvalidArgumentException::invalidType('"explain" option', $this->options['explain'], 'boolean'); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], ['array', 'object']); + if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $this->options['hint']); } - if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer'); + if (isset($this->options['let']) && ! is_document($this->options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $this->options['let']); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxAwaitTimeMS']) && ! is_integer($this->options['maxAwaitTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $this->options['maxAwaitTimeMS'], 'integer'); } - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { + throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (! is_bool($options['useCursor'])) { - throw InvalidArgumentException::invalidType('"useCursor" option', $options['useCursor'], 'boolean'); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['batchSize']) && ! $options['useCursor']) { - throw new InvalidArgumentException('"batchSize" option should not be used if "useCursor" is false'); + if (isset($this->options['bypassDocumentValidation']) && ! $this->options['bypassDocumentValidation']) { + unset($this->options['bypassDocumentValidation']); } - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); + if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { + unset($this->options['readConcern']); } - if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { - unset($options['readConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['codec']) && isset($this->options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); } - $this->isExplain = ! empty($options['explain']); - $this->isWrite = is_last_pipeline_operator_write($pipeline) && ! $this->isExplain; + $this->isWrite = is_last_pipeline_operator_write($pipeline) && ! ($this->options['explain'] ?? false); - // Explain does not use a cursor - if ($this->isExplain) { - $options['useCursor'] = false; - unset($options['batchSize']); - } - - /* Ignore batchSize for writes, since no documents are returned and a - * batchSize of zero could prevent the pipeline from executing. */ if ($this->isWrite) { - unset($options['batchSize']); + /* Ignore batchSize for writes, since no documents are returned and + * a batchSize of zero could prevent the pipeline from executing. */ + unset($this->options['batchSize']); + } else { + unset($this->options['writeConcern']); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->pipeline = $pipeline; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return ArrayIterator|Cursor * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction) { @@ -275,41 +230,37 @@ public function execute(Server $server) $command = new Command( $this->createCommandDocument(), - $this->createCommandOptions() + $this->createCommandOptions(), ); $cursor = $this->executeCommand($server, $command); - if ($this->options['useCursor'] || $this->isExplain) { - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return $cursor; + if (isset($this->options['codec'])) { + return CodecCursor::fromCursor($cursor, $this->options['codec']); } if (isset($this->options['typeMap'])) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'result.$')); - } - - $result = current($cursor->toArray()); - - if (! is_object($result) || ! isset($result->result) || ! is_array($result->result)) { - throw new UnexpectedValueException('aggregate command did not return a "result" array'); + $cursor->setTypeMap($this->options['typeMap']); } - return new ArrayIterator($result->result); + return $cursor; } /** * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->createCommandDocument(); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } /** @@ -338,11 +289,9 @@ private function createCommandDocument(): array $cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint']; } - if ($this->options['useCursor']) { - $cmd['cursor'] = isset($this->options["batchSize"]) - ? ['batchSize' => $this->options["batchSize"]] - : new stdClass(); - } + $cmd['cursor'] = isset($this->options['batchSize']) + ? ['batchSize' => $this->options['batchSize']] + : new stdClass(); return $cmd; } @@ -365,20 +314,16 @@ private function createCommandOptions(): array * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php */ - private function executeCommand(Server $server, Command $command): Cursor + private function executeCommand(Server $server, Command $command): CursorInterface { $options = []; - foreach (['readConcern', 'readPreference', 'session'] as $option) { + foreach (['readConcern', 'readPreference', 'session', 'writeConcern'] as $option) { if (isset($this->options[$option])) { $options[$option] = $this->options[$option]; } } - if ($this->isWrite && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - if (! $this->isWrite) { return $server->executeReadCommand($this->databaseName, $command, $options); } diff --git a/src/Operation/BulkWrite.php b/src/Operation/BulkWrite.php index 28568de25..dc6dbb4c7 100644 --- a/src/Operation/BulkWrite.php +++ b/src/Operation/BulkWrite.php @@ -17,7 +17,10 @@ namespace MongoDB\Operation; +use MongoDB\Builder\BuilderEncoder; use MongoDB\BulkWriteResult; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Codec\Encoder; use MongoDB\Driver\BulkWrite as Bulk; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; @@ -26,13 +29,14 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; +use function array_is_list; use function array_key_exists; use function count; use function current; use function is_array; use function is_bool; -use function is_object; use function key; +use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; use function sprintf; @@ -40,10 +44,9 @@ /** * Operation for executing multiple write operations. * - * @api * @see \MongoDB\Collection::bulkWrite() */ -class BulkWrite implements Executable +final class BulkWrite { public const DELETE_MANY = 'deleteMany'; public const DELETE_ONE = 'deleteOne'; @@ -52,17 +55,10 @@ class BulkWrite implements Executable public const UPDATE_MANY = 'updateMany'; public const UPDATE_ONE = 'updateOne'; - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - /** @var array[] */ - private $operations; + private array $operations; - /** @var array */ - private $options; + private array $options; /** * Constructs a bulk write operation. @@ -93,6 +89,14 @@ class BulkWrite implements Executable * * upsert (boolean): When true, a new document is created if no document * matches the query. The default is false. * + * Supported options for replaceOne and updateOne operations: + * + * * sort (document): Determines which document the operation modifies if + * the query selects multiple documents. + * + * This is not supported for server versions < 8.0 and will result in an + * exception at execution time if used. + * * Supported options for updateMany and updateOne operations: * * * arrayFilters (document array): A set of filters specifying to which @@ -100,9 +104,16 @@ class BulkWrite implements Executable * * Supported options for the bulk write operation: * + * * builderEncoder (MongoDB\Codec\Encoder): Encoder for query and + * aggregation builders. If not given, the default encoder will be used. + * * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. The default is false. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. This option is also used to encode PHP + * objects into BSON for insertOne and replaceOne operations. + * * * comment (mixed): BSON value to attach as a comment to this command(s) * associated with this bulk write. * @@ -127,152 +138,30 @@ class BulkWrite implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $operations, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, array $operations, array $options = []) { if (empty($operations)) { throw new InvalidArgumentException('$operations is empty'); } - $expectedIndex = 0; - - foreach ($operations as $i => $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$operations is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array'); - } - - if (count($operation) !== 1) { - throw new InvalidArgumentException(sprintf('Expected one element in $operation[%d], actually: %d', $i, count($operation))); - } - - $type = key($operation); - $args = current($operation); - - if (! isset($args[0]) && ! array_key_exists(0, $args)) { - throw new InvalidArgumentException(sprintf('Missing first argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[0]) && ! is_object($args[0])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][0]', $i, $type), $args[0], 'array or object'); - } - - switch ($type) { - case self::INSERT_ONE: - break; - - case self::DELETE_MANY: - case self::DELETE_ONE: - if (! isset($args[1])) { - $args[1] = []; - } - - if (! is_array($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array'); - } - - $args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0); - - if (isset($args[1]['collation']) && ! is_array($args[1]['collation']) && ! is_object($args[1]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation'], 'array or object'); - } - - $operations[$i][$type][1] = $args[1]; - - break; - - case self::REPLACE_ONE: - if (! isset($args[1]) && ! array_key_exists(1, $args)) { - throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[1]) && ! is_object($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object'); - } - - if (is_first_key_operator($args[1])) { - throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is an update operator', $i, $type)); - } - - if (! isset($args[2])) { - $args[2] = []; - } - - if (! is_array($args[2])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); - } - - $args[2]['multi'] = false; - $args[2] += ['upsert' => false]; - - if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } - - if (! is_bool($args[2]['upsert'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); - } - - $operations[$i][$type][2] = $args[2]; - - break; - - case self::UPDATE_MANY: - case self::UPDATE_ONE: - if (! isset($args[1]) && ! array_key_exists(1, $args)) { - throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); - } - - if (! is_array($args[1]) && ! is_object($args[1])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array or object'); - } - - if (! is_first_key_operator($args[1]) && ! is_pipeline($args[1])) { - throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is neither an update operator nor a pipeline', $i, $type)); - } - - if (! isset($args[2])) { - $args[2] = []; - } - - if (! is_array($args[2])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); - } - - $args[2]['multi'] = ($type === self::UPDATE_MANY); - $args[2] += ['upsert' => false]; - - if (isset($args[2]['arrayFilters']) && ! is_array($args[2]['arrayFilters'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array'); - } - - if (isset($args[2]['collation']) && ! is_array($args[2]['collation']) && ! is_object($args[2]['collation'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation'], 'array or object'); - } - - if (! is_bool($args[2]['upsert'])) { - throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); - } - - $operations[$i][$type][2] = $args[2]; - - break; - - default: - throw new InvalidArgumentException(sprintf('Unknown operation type "%s" in $operations[%d]', $type, $i)); - } - - $expectedIndex += 1; + if (! array_is_list($operations)) { + throw new InvalidArgumentException('$operations is not a list'); } $options += ['ordered' => true]; + if (isset($options['builderEncoder']) && ! $options['builderEncoder'] instanceof Encoder) { + throw InvalidArgumentException::invalidType('"builderEncoder" option', $options['builderEncoder'], Encoder::class); + } + if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); } + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); + } + if (! is_bool($options['ordered'])) { throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); } @@ -285,8 +174,8 @@ public function __construct(string $databaseName, string $collectionName, array throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + if (isset($options['let']) && ! is_document($options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); } if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { @@ -297,21 +186,17 @@ public function __construct(string $databaseName, string $collectionName, array unset($options['writeConcern']); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->operations = $operations; + $this->operations = $this->validateOperations($operations, $options['codec'] ?? null, $options['builderEncoder'] ?? new BuilderEncoder()); $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return BulkWriteResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): BulkWriteResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { @@ -335,10 +220,11 @@ public function execute(Server $server) $insertedIds[$i] = $bulk->insert($args[0]); break; - case self::REPLACE_ONE: case self::UPDATE_MANY: case self::UPDATE_ONE: + case self::REPLACE_ONE: $bulk->update($args[0], $args[1], $args[2]); + break; } } @@ -388,4 +274,174 @@ private function createExecuteOptions(): array return $options; } + + /** + * @param array[] $operations + * @return array[] + */ + private function validateOperations(array $operations, ?DocumentCodec $codec, Encoder $builderEncoder): array + { + foreach ($operations as $i => $operation) { + if (! is_array($operation)) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]', $i), $operation, 'array'); + } + + if (count($operation) !== 1) { + throw new InvalidArgumentException(sprintf('Expected one element in $operation[%d], actually: %d', $i, count($operation))); + } + + $type = key($operation); + $args = current($operation); + + if (! isset($args[0]) && ! array_key_exists(0, $args)) { + throw new InvalidArgumentException(sprintf('Missing first argument for $operations[%d]["%s"]', $i, $type)); + } + + if (! is_document($args[0])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][0]', $i, $type), $args[0]); + } + + switch ($type) { + case self::INSERT_ONE: + // $args[0] was already validated above. Since DocumentCodec::encode will always return a Document + // instance, there is no need to re-validate the returned value here. + if ($codec) { + $operations[$i][$type][0] = $codec->encode($args[0]); + } + + break; + + case self::DELETE_MANY: + case self::DELETE_ONE: + $operations[$i][$type][0] = $builderEncoder->encodeIfSupported($args[0]); + + if (! isset($args[1])) { + $args[1] = []; + } + + if (! is_array($args[1])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1], 'array'); + } + + $args[1]['limit'] = ($type === self::DELETE_ONE ? 1 : 0); + + if (isset($args[1]['collation']) && ! is_document($args[1]['collation'])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][1]["collation"]', $i, $type), $args[1]['collation']); + } + + $operations[$i][$type][1] = $args[1]; + + break; + + case self::REPLACE_ONE: + $operations[$i][$type][0] = $builderEncoder->encodeIfSupported($args[0]); + + if (! isset($args[1]) && ! array_key_exists(1, $args)) { + throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); + } + + if ($codec) { + $operations[$i][$type][1] = $codec->encode($args[1]); + } + + if (! is_document($args[1])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][1]', $i, $type), $args[1]); + } + + // Treat empty arrays as replacement documents for BC + if ($args[1] === []) { + $args[1] = (object) $args[1]; + } + + if (is_first_key_operator($args[1])) { + throw new InvalidArgumentException(sprintf('First key in $operations[%d]["%s"][1] is an update operator', $i, $type)); + } + + if (is_pipeline($args[1], true /* allowEmpty */)) { + throw new InvalidArgumentException(sprintf('$operations[%d]["%s"][1] is an update pipeline', $i, $type)); + } + + if (! isset($args[2])) { + $args[2] = []; + } + + if (! is_array($args[2])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); + } + + $args[2]['multi'] = false; + $args[2] += ['upsert' => false]; + + if (isset($args[2]['collation']) && ! is_document($args[2]['collation'])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation']); + } + + if (isset($args[2]['sort']) && ! is_document($args[2]['sort'])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["sort"]', $i, $type), $args[2]['sort']); + } + + if (! is_bool($args[2]['upsert'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); + } + + $operations[$i][$type][2] = $args[2]; + + break; + + case self::UPDATE_MANY: + case self::UPDATE_ONE: + $operations[$i][$type][0] = $builderEncoder->encodeIfSupported($args[0]); + + if (! isset($args[1]) && ! array_key_exists(1, $args)) { + throw new InvalidArgumentException(sprintf('Missing second argument for $operations[%d]["%s"]', $i, $type)); + } + + $operations[$i][$type][1] = $args[1] = $builderEncoder->encodeIfSupported($args[1]); + + if ((! is_document($args[1]) || ! is_first_key_operator($args[1])) && ! is_pipeline($args[1])) { + throw new InvalidArgumentException(sprintf('Expected update operator(s) or non-empty pipeline for $operations[%d]["%s"][1]', $i, $type)); + } + + if (! isset($args[2])) { + $args[2] = []; + } + + if (! is_array($args[2])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]', $i, $type), $args[2], 'array'); + } + + $args[2]['multi'] = ($type === self::UPDATE_MANY); + $args[2] += ['upsert' => false]; + + if (isset($args[2]['arrayFilters']) && ! is_array($args[2]['arrayFilters'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["arrayFilters"]', $i, $type), $args[2]['arrayFilters'], 'array'); + } + + if (isset($args[2]['collation']) && ! is_document($args[2]['collation'])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["collation"]', $i, $type), $args[2]['collation']); + } + + if (isset($args[2]['sort']) && ! is_document($args[2]['sort'])) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$operations[%d]["%s"][2]["sort"]', $i, $type), $args[2]['sort']); + } + + if (isset($args[2]['sort']) && $args[2]['multi']) { + throw new InvalidArgumentException(sprintf('"sort" option cannot be used with $operations[%d]["%s"]', $i, $type)); + } + + if (! is_bool($args[2]['upsert'])) { + throw InvalidArgumentException::invalidType(sprintf('$operations[%d]["%s"][2]["upsert"]', $i, $type), $args[2]['upsert'], 'boolean'); + } + + $operations[$i][$type][2] = $args[2]; + + break; + + default: + throw new InvalidArgumentException(sprintf('Unknown operation type "%s" in $operations[%d]', $type, $i)); + } + } + + return $operations; + } } diff --git a/src/Operation/ClientBulkWriteCommand.php b/src/Operation/ClientBulkWriteCommand.php new file mode 100644 index 000000000..e7768adcb --- /dev/null +++ b/src/Operation/ClientBulkWriteCommand.php @@ -0,0 +1,95 @@ +options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); + } + + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); + } + + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); + } + } + + /** + * Execute the operation. + * + * @throws UnsupportedException if write concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server): BulkWriteCommandResult + { + $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); + if ($inTransaction && isset($this->options['writeConcern'])) { + throw UnsupportedException::writeConcernNotSupportedInTransaction(); + } + + $options = array_filter($this->options, fn ($value) => $value !== null); + + return $server->executeBulkWriteCommand($this->bulkWriteCommand, $options); + } +} diff --git a/src/Operation/Count.php b/src/Operation/Count.php index 7e427313c..ae40c790f 100644 --- a/src/Operation/Count.php +++ b/src/Operation/Count.php @@ -33,28 +33,16 @@ use function is_integer; use function is_object; use function is_string; +use function MongoDB\is_document; /** * Operation for the count command. * - * @api * @see \MongoDB\Collection::count() * @see https://mongodb.com/docs/manual/reference/command/count/ */ -class Count implements Executable, Explainable +final class Count implements Explainable { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array|object */ - private $filter; - - /** @var array */ - private $options; - /** * Constructs a count command. * @@ -90,64 +78,57 @@ class Count implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter = [], array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array|object $filter = [], private array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); + if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $this->options['hint']); } - if (isset($options['limit']) && ! is_integer($options['limit'])) { - throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer'); + if (isset($this->options['limit']) && ! is_integer($this->options['limit'])) { + throw InvalidArgumentException::invalidType('"limit" option', $this->options['limit'], 'integer'); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); + if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { + throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); } - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['skip']) && ! is_integer($options['skip'])) { - throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer'); + if (isset($this->options['skip']) && ! is_integer($this->options['skip'])) { + throw InvalidArgumentException::invalidType('"skip" option', $this->options['skip'], 'integer'); } - if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { - unset($options['readConcern']); + if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { + unset($this->options['readConcern']); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -162,18 +143,24 @@ public function execute(Server $server) throw new UnexpectedValueException('count command did not return a numeric "n" value'); } - return (integer) $result->n; + return (int) $result->n; } /** * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->createCommandDocument(); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } /** @@ -183,7 +170,7 @@ private function createCommandDocument(): array { $cmd = ['count' => $this->collectionName]; - if (! empty($this->filter)) { + if ($this->filter !== []) { $cmd['query'] = (object) $this->filter; } diff --git a/src/Operation/CountDocuments.php b/src/Operation/CountDocuments.php index 57b04544b..86046f499 100644 --- a/src/Operation/CountDocuments.php +++ b/src/Operation/CountDocuments.php @@ -17,7 +17,6 @@ namespace MongoDB\Operation; -use MongoDB\Driver\Cursor; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; @@ -25,40 +24,26 @@ use MongoDB\Exception\UnsupportedException; use function array_intersect_key; -use function assert; use function count; use function current; -use function is_array; use function is_float; use function is_integer; use function is_object; +use function MongoDB\is_document; /** * Operation for obtaining an exact count of documents in a collection * - * @api * @see \MongoDB\Collection::countDocuments() - * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#countdocuments + * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#countdocuments */ -class CountDocuments implements Executable +final class CountDocuments { - /** @var string */ - private $databaseName; + private array $aggregateOptions; - /** @var string */ - private $collectionName; + private array $countOptions; - /** @var array|object */ - private $filter; - - /** @var array */ - private $aggregateOptions; - - /** @var array */ - private $countOptions; - - /** @var Aggregate */ - private $aggregate; + private Aggregate $aggregate; /** * Constructs an aggregate command for counting documents @@ -95,10 +80,10 @@ class CountDocuments implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array|object $filter, array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } if (isset($options['limit']) && ! is_integer($options['limit'])) { @@ -109,10 +94,6 @@ public function __construct(string $databaseName, string $collectionName, $filte throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer'); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->aggregateOptions = array_intersect_key($options, ['collation' => 1, 'comment' => 1, 'hint' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1]); $this->countOptions = array_intersect_key($options, ['limit' => 1, 'skip' => 1]); @@ -122,16 +103,13 @@ public function __construct(string $databaseName, string $collectionName, $filte /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { $cursor = $this->aggregate->execute($server); - assert($cursor instanceof Cursor); $allResults = $cursor->toArray(); @@ -146,7 +124,7 @@ public function execute(Server $server) throw new UnexpectedValueException('count command did not return a numeric "n" value'); } - return (integer) $result->n; + return (int) $result->n; } private function createAggregate(): Aggregate diff --git a/src/Operation/CreateCollection.php b/src/Operation/CreateCollection.php index 8860a2889..45a4925d3 100644 --- a/src/Operation/CreateCollection.php +++ b/src/Operation/CreateCollection.php @@ -24,51 +24,26 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; -use function current; use function is_array; use function is_bool; use function is_integer; -use function is_object; use function is_string; -use function sprintf; -use function trigger_error; - -use const E_USER_DEPRECATED; +use function MongoDB\is_document; +use function MongoDB\is_pipeline; /** * Operation for the create command. * - * @api * @see \MongoDB\Database::createCollection() * @see https://mongodb.com/docs/manual/reference/command/create/ */ -class CreateCollection implements Executable +final class CreateCollection { - public const USE_POWER_OF_2_SIZES = 1; - public const NO_PADDING = 2; - - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $options = []; - /** * Constructs a create command. * * Supported options: * - * * autoIndexId (boolean): Specify false to disable the automatic creation - * of an index on the _id field. For replica sets, this option cannot be - * false. The default is true. - * - * This option has been deprecated since MongoDB 3.2. As of MongoDB 4.0, - * this option cannot be false when creating a replicated collection - * (i.e. a collection outside of the local database in any mongod mode). - * * * capped (boolean): Specify true to create a capped collection. If set, * the size option must also be specified. The default is false. * @@ -87,17 +62,13 @@ class CreateCollection implements Executable * * * collation (document): Collation specification. * - * * encryptedFields (document): CSFLE specification. + * * encryptedFields (document): Configuration for encrypted fields. + * See: https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/encrypt-and-query/ * * * expireAfterSeconds: The TTL for documents in time series collections. * * This is not supported for servers versions < 5.0. * - * * flags (integer): Options for the MMAPv1 storage engine only. Must be a - * bitwise combination CreateCollection::USE_POWER_OF_2_SIZES and - * CreateCollection::NO_PADDING. The default is - * CreateCollection::USE_POWER_OF_2_SIZES. - * * * indexOptionDefaults (document): Default configuration for indexes when * creating the collection. * @@ -121,9 +92,6 @@ class CreateCollection implements Executable * * This is not supported for servers versions < 5.0. * - * * typeMap (array): Type map for BSON deserialization. This will only be - * used for the returned command result document. - * * * validationAction (string): Validation action. * * * validationLevel (string): Validation level. @@ -142,141 +110,101 @@ class CreateCollection implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array $options = []) { - if (isset($options['autoIndexId']) && ! is_bool($options['autoIndexId'])) { - throw InvalidArgumentException::invalidType('"autoIndexId" option', $options['autoIndexId'], 'boolean'); - } - - if (isset($options['capped']) && ! is_bool($options['capped'])) { - throw InvalidArgumentException::invalidType('"capped" option', $options['capped'], 'boolean'); - } - - if (isset($options['changeStreamPreAndPostImages']) && ! is_array($options['changeStreamPreAndPostImages']) && ! is_object($options['changeStreamPreAndPostImages'])) { - throw InvalidArgumentException::invalidType('"changeStreamPreAndPostImages" option', $options['changeStreamPreAndPostImages'], 'array or object'); + if (isset($this->options['capped']) && ! is_bool($this->options['capped'])) { + throw InvalidArgumentException::invalidType('"capped" option', $this->options['capped'], 'boolean'); } - if (isset($options['clusteredIndex']) && ! is_array($options['clusteredIndex']) && ! is_object($options['clusteredIndex'])) { - throw InvalidArgumentException::invalidType('"clusteredIndex" option', $options['clusteredIndex'], 'array or object'); + if (isset($this->options['changeStreamPreAndPostImages']) && ! is_document($this->options['changeStreamPreAndPostImages'])) { + throw InvalidArgumentException::expectedDocumentType('"changeStreamPreAndPostImages" option', $this->options['changeStreamPreAndPostImages']); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['clusteredIndex']) && ! is_document($this->options['clusteredIndex'])) { + throw InvalidArgumentException::expectedDocumentType('"clusteredIndex" option', $this->options['clusteredIndex']); } - if (isset($options['encryptedFields']) && ! is_array($options['encryptedFields']) && ! is_object($options['encryptedFields'])) { - throw InvalidArgumentException::invalidType('"encryptedFields" option', $options['encryptedFields'], 'array or object'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); } - if (isset($options['expireAfterSeconds']) && ! is_integer($options['expireAfterSeconds'])) { - throw InvalidArgumentException::invalidType('"expireAfterSeconds" option', $options['expireAfterSeconds'], 'integer'); + if (isset($this->options['encryptedFields']) && ! is_document($this->options['encryptedFields'])) { + throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $this->options['encryptedFields']); } - if (isset($options['flags']) && ! is_integer($options['flags'])) { - throw InvalidArgumentException::invalidType('"flags" option', $options['flags'], 'integer'); + if (isset($this->options['expireAfterSeconds']) && ! is_integer($this->options['expireAfterSeconds'])) { + throw InvalidArgumentException::invalidType('"expireAfterSeconds" option', $this->options['expireAfterSeconds'], 'integer'); } - if (isset($options['indexOptionDefaults']) && ! is_array($options['indexOptionDefaults']) && ! is_object($options['indexOptionDefaults'])) { - throw InvalidArgumentException::invalidType('"indexOptionDefaults" option', $options['indexOptionDefaults'], 'array or object'); + if (isset($this->options['indexOptionDefaults']) && ! is_document($this->options['indexOptionDefaults'])) { + throw InvalidArgumentException::expectedDocumentType('"indexOptionDefaults" option', $this->options['indexOptionDefaults']); } - if (isset($options['max']) && ! is_integer($options['max'])) { - throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'integer'); + if (isset($this->options['max']) && ! is_integer($this->options['max'])) { + throw InvalidArgumentException::invalidType('"max" option', $this->options['max'], 'integer'); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['pipeline']) && ! is_array($options['pipeline'])) { - throw InvalidArgumentException::invalidType('"pipeline" option', $options['pipeline'], 'array'); + if (isset($this->options['pipeline']) && ! is_array($this->options['pipeline'])) { + throw InvalidArgumentException::invalidType('"pipeline" option', $this->options['pipeline'], 'array'); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['size']) && ! is_integer($options['size'])) { - throw InvalidArgumentException::invalidType('"size" option', $options['size'], 'integer'); + if (isset($this->options['size']) && ! is_integer($this->options['size'])) { + throw InvalidArgumentException::invalidType('"size" option', $this->options['size'], 'integer'); } - if (isset($options['storageEngine']) && ! is_array($options['storageEngine']) && ! is_object($options['storageEngine'])) { - throw InvalidArgumentException::invalidType('"storageEngine" option', $options['storageEngine'], 'array or object'); + if (isset($this->options['storageEngine']) && ! is_document($this->options['storageEngine'])) { + throw InvalidArgumentException::expectedDocumentType('"storageEngine" option', $this->options['storageEngine']); } - if (isset($options['timeseries']) && ! is_array($options['timeseries']) && ! is_object($options['timeseries'])) { - throw InvalidArgumentException::invalidType('"timeseries" option', $options['timeseries'], ['array', 'object']); + if (isset($this->options['timeseries']) && ! is_document($this->options['timeseries'])) { + throw InvalidArgumentException::expectedDocumentType('"timeseries" option', $this->options['timeseries']); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['validationAction']) && ! is_string($this->options['validationAction'])) { + throw InvalidArgumentException::invalidType('"validationAction" option', $this->options['validationAction'], 'string'); } - if (isset($options['validationAction']) && ! is_string($options['validationAction'])) { - throw InvalidArgumentException::invalidType('"validationAction" option', $options['validationAction'], 'string'); + if (isset($this->options['validationLevel']) && ! is_string($this->options['validationLevel'])) { + throw InvalidArgumentException::invalidType('"validationLevel" option', $this->options['validationLevel'], 'string'); } - if (isset($options['validationLevel']) && ! is_string($options['validationLevel'])) { - throw InvalidArgumentException::invalidType('"validationLevel" option', $options['validationLevel'], 'string'); + if (isset($this->options['validator']) && ! is_document($this->options['validator'])) { + throw InvalidArgumentException::expectedDocumentType('"validator" option', $this->options['validator']); } - if (isset($options['validator']) && ! is_array($options['validator']) && ! is_object($options['validator'])) { - throw InvalidArgumentException::invalidType('"validator" option', $options['validator'], 'array or object'); + if (isset($this->options['viewOn']) && ! is_string($this->options['viewOn'])) { + throw InvalidArgumentException::invalidType('"viewOn" option', $this->options['viewOn'], 'string'); } - if (isset($options['viewOn']) && ! is_string($options['viewOn'])) { - throw InvalidArgumentException::invalidType('"viewOn" option', $options['viewOn'], 'string'); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['pipeline']) && ! is_pipeline($this->options['pipeline'], true /* allowEmpty */)) { + throw new InvalidArgumentException('"pipeline" option is not a valid aggregation pipeline'); } - - if (isset($options['autoIndexId'])) { - trigger_error('The "autoIndexId" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - if (isset($options['pipeline'])) { - $expectedIndex = 0; - - foreach ($options['pipeline'] as $i => $operation) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('The "pipeline" option is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($operation) && ! is_object($operation)) { - throw InvalidArgumentException::invalidType(sprintf('$options["pipeline"][%d]', $i), $operation, 'array or object'); - } - - $expectedIndex += 1; - } - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** @@ -286,7 +214,7 @@ private function createCommand(): Command { $cmd = ['create' => $this->collectionName]; - foreach (['autoIndexId', 'capped', 'comment', 'expireAfterSeconds', 'flags', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { + foreach (['capped', 'comment', 'expireAfterSeconds', 'max', 'maxTimeMS', 'pipeline', 'size', 'validationAction', 'validationLevel', 'viewOn'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; } diff --git a/src/Operation/CreateEncryptedCollection.php b/src/Operation/CreateEncryptedCollection.php new file mode 100644 index 000000000..01729c0c7 --- /dev/null +++ b/src/Operation/CreateEncryptedCollection.php @@ -0,0 +1,176 @@ + */ + private array $createMetadataCollections; + + private CreateIndexes $createSafeContentIndex; + + /** + * @see CreateCollection::__construct() for supported options + * @param string $databaseName Database name + * @param string $collectionName Collection name + * @param array $options CreateCollection options + * @throws InvalidArgumentException for parameter/option parsing errors + */ + public function __construct(private string $databaseName, private string $collectionName, private array $options) + { + if (! isset($this->options['encryptedFields'])) { + throw new InvalidArgumentException('"encryptedFields" option is required'); + } + + if (! is_document($this->options['encryptedFields'])) { + throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $this->options['encryptedFields']); + } + + $this->createCollection = new CreateCollection($databaseName, $collectionName, $this->options); + + /** @psalm-var array{ecocCollection?: ?string, escCollection?: ?string} */ + $encryptedFields = document_to_array($this->options['encryptedFields']); + $enxcolOptions = ['clusteredIndex' => ['key' => ['_id' => 1], 'unique' => true]]; + + $this->createMetadataCollections = [ + new CreateCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc', $enxcolOptions), + new CreateCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc', $enxcolOptions), + ]; + + $this->createSafeContentIndex = new CreateIndexes($databaseName, $collectionName, [['key' => ['__safeContent__' => 1]]]); + } + + /** + * Create data keys for any encrypted fields where "keyId" is null. + * + * This method should be called before execute(), as it may modify the + * "encryptedFields" option and reconstruct the internal CreateCollection + * operation used for creating the encrypted collection. + * + * Returns the data keys that have been created. + * + * @see \MongoDB\Database::createEncryptedCollection() + * @see https://www.php.net/manual/en/mongodb-driver-clientencryption.createdatakey.php + * @throws DriverRuntimeException for errors creating a data key + */ + public function createDataKeys(ClientEncryption $clientEncryption, string $kmsProvider, ?array $masterKey): array + { + /** @psalm-var array{fields: list|Serializable|PackedArray} */ + $encryptedFields = document_to_array($this->options['encryptedFields']); + + // NOP if there are no fields to examine + if (! isset($encryptedFields['fields'])) { + return $encryptedFields; + } + + // Allow PackedArray or Serializable object for the fields array + if ($encryptedFields['fields'] instanceof PackedArray) { + /** @psalm-var array */ + $encryptedFields['fields'] = $encryptedFields['fields']->toPHP([ + 'array' => 'array', + 'document' => 'object', + 'root' => 'array', + ]); + } elseif ($encryptedFields['fields'] instanceof Serializable) { + $encryptedFields['fields'] = $encryptedFields['fields']->bsonSerialize(); + } + + // Skip invalid types and defer to the server to raise an error + if (! is_array($encryptedFields['fields'])) { + return $encryptedFields; + } + + $createDataKeyArgs = [ + $kmsProvider, + $masterKey !== null ? ['masterKey' => $masterKey] : [], + ]; + + foreach ($encryptedFields['fields'] as $i => $field) { + // Skip invalid types and defer to the server to raise an error + if (! is_array($field) && ! is_object($field)) { + continue; + } + + $field = document_to_array($field); + + if (array_key_exists('keyId', $field) && $field['keyId'] === null) { + $field['keyId'] = $clientEncryption->createDataKey(...$createDataKeyArgs); + $encryptedFields['fields'][$i] = $field; + } + } + + $this->options['encryptedFields'] = $encryptedFields; + $this->createCollection = new CreateCollection($this->databaseName, $this->collectionName, $this->options); + + return $encryptedFields; + } + + /** + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + * @throws UnsupportedException if the server does not support Queryable Encryption + */ + public function execute(Server $server): void + { + if (! server_supports_feature($server, self::WIRE_VERSION_FOR_QUERYABLE_ENCRYPTION_V2)) { + throw new UnsupportedException('Driver support of Queryable Encryption is incompatible with server. Upgrade server to use Queryable Encryption.'); + } + + foreach ($this->createMetadataCollections as $createMetadataCollection) { + $createMetadataCollection->execute($server); + } + + $this->createCollection->execute($server); + + $this->createSafeContentIndex->execute($server); + } +} diff --git a/src/Operation/CreateIndexes.php b/src/Operation/CreateIndexes.php index 351f25116..6591270b7 100644 --- a/src/Operation/CreateIndexes.php +++ b/src/Operation/CreateIndexes.php @@ -26,6 +26,7 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\Model\IndexInput; +use function array_is_list; use function array_map; use function is_array; use function is_integer; @@ -36,27 +37,16 @@ /** * Operation for the createIndexes command. * - * @api * @see \MongoDB\Collection::createIndex() * @see \MongoDB\Collection::createIndexes() * @see https://mongodb.com/docs/manual/reference/command/createIndexes/ */ -class CreateIndexes implements Executable +final class CreateIndexes { - /** @var integer */ - private static $wireVersionForCommitQuorum = 9; + private const WIRE_VERSION_FOR_COMMIT_QUORUM = 9; - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $indexes = []; - - /** @var array */ - private $options = []; + /** @var list */ + private array $indexes = []; /** * Constructs a createIndexes command. @@ -84,62 +74,53 @@ class CreateIndexes implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $indexes, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, array $indexes, private array $options = []) { if (empty($indexes)) { throw new InvalidArgumentException('$indexes is empty'); } - $expectedIndex = 0; + if (! array_is_list($indexes)) { + throw new InvalidArgumentException('$indexes is not a list'); + } foreach ($indexes as $i => $index) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$indexes is not a list (unexpected index: "%s")', $i)); - } - if (! is_array($index)) { throw InvalidArgumentException::invalidType(sprintf('$index[%d]', $i), $index, 'array'); } $this->indexes[] = new IndexInput($index); - - $expectedIndex += 1; } - if (isset($options['commitQuorum']) && ! is_string($options['commitQuorum']) && ! is_integer($options['commitQuorum'])) { - throw InvalidArgumentException::invalidType('"commitQuorum" option', $options['commitQuorum'], ['integer', 'string']); + if (isset($this->options['commitQuorum']) && ! is_string($this->options['commitQuorum']) && ! is_integer($this->options['commitQuorum'])) { + throw InvalidArgumentException::invalidType('"commitQuorum" option', $this->options['commitQuorum'], ['integer', 'string']); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() * @return string[] The names of the created indexes * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { @@ -148,9 +129,10 @@ public function execute(Server $server) $this->executeCommand($server); - return array_map(function (IndexInput $index) { - return (string) $index; - }, $this->indexes); + return array_map( + 'strval', + $this->indexes, + ); } /** @@ -189,7 +171,7 @@ private function executeCommand(Server $server): void if (isset($this->options['commitQuorum'])) { /* Drivers MUST manually raise an error if this option is specified * when creating an index on a pre 4.4 server. */ - if (! server_supports_feature($server, self::$wireVersionForCommitQuorum)) { + if (! server_supports_feature($server, self::WIRE_VERSION_FOR_COMMIT_QUORUM)) { throw UnsupportedException::commitQuorumNotSupported(); } diff --git a/src/Operation/CreateSearchIndexes.php b/src/Operation/CreateSearchIndexes.php new file mode 100644 index 000000000..d21ed9428 --- /dev/null +++ b/src/Operation/CreateSearchIndexes.php @@ -0,0 +1,93 @@ + $index) { + if (! is_array($index)) { + throw InvalidArgumentException::invalidType(sprintf('$indexes[%d]', $i), $index, 'array'); + } + + $this->indexes[] = new SearchIndexInput($index); + } + } + + /** + * Execute the operation. + * + * @return string[] The names of the created indexes + * @throws UnsupportedException if write concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server): array + { + $cmd = [ + 'createSearchIndexes' => $this->collectionName, + 'indexes' => $this->indexes, + ]; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + $cursor = $server->executeCommand($this->databaseName, new Command($cmd)); + + /** @var object{indexesCreated: list} $result */ + $result = current($cursor->toArray()); + + return array_column($result->indexesCreated, 'name'); + } +} diff --git a/src/Operation/DatabaseCommand.php b/src/Operation/DatabaseCommand.php index 4e6fac522..567bbff9e 100644 --- a/src/Operation/DatabaseCommand.php +++ b/src/Operation/DatabaseCommand.php @@ -18,31 +18,23 @@ namespace MongoDB\Operation; use MongoDB\Driver\Command; -use MongoDB\Driver\Cursor; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use function is_array; -use function is_object; +use function MongoDB\is_document; /** * Operation for executing a database command. * - * @api * @see \MongoDB\Database::command() */ -class DatabaseCommand implements Executable +final class DatabaseCommand { - /** @var string */ - private $databaseName; - - /** @var Command */ - private $command; - - /** @var array */ - private $options; + private Command $command; /** * Constructs a command. @@ -65,36 +57,28 @@ class DatabaseCommand implements Executable * @param array $options Options for command execution * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, $command, array $options = []) + public function __construct(private string $databaseName, array|object $command, private array $options = []) { - if (! is_array($command) && ! is_object($command)) { - throw InvalidArgumentException::invalidType('$command', $command, 'array or object'); + if (! is_document($command)) { + throw InvalidArgumentException::expectedDocumentType('$command', $command); } - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - $this->databaseName = $databaseName; $this->command = $command instanceof Command ? $command : new Command($command); - $this->options = $options; } - /** - * Execute the operation. - * - * @see Executable::execute() - * @return Cursor - */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $cursor = $server->executeCommand($this->databaseName, $this->command, $this->createOptions()); diff --git a/src/Operation/Delete.php b/src/Operation/Delete.php index 7ca197f7a..84a803c79 100644 --- a/src/Operation/Delete.php +++ b/src/Operation/Delete.php @@ -26,9 +26,8 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function is_array; -use function is_object; use function is_string; +use function MongoDB\is_document; use function MongoDB\is_write_concern_acknowledged; use function MongoDB\server_supports_feature; @@ -41,25 +40,9 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -class Delete implements Executable, Explainable +final class Delete implements Explainable { - /** @var integer */ - private static $wireVersionForHint = 9; - - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array|object */ - private $filter; - - /** @var integer */ - private $limit; - - /** @var array */ - private $options; + private const WIRE_VERSION_FOR_HINT = 9; /** * Constructs a delete command. @@ -97,62 +80,54 @@ class Delete implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, int $limit, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array|object $filter, private int $limit, private array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } if ($limit !== 0 && $limit !== 1) { throw new InvalidArgumentException('$limit must be 0 or 1'); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); + if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $this->options['hint']); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + if (isset($this->options['let']) && ! is_document($this->options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $this->options['let']); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->limit = $limit; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { /* CRUD spec requires a client-side error when using "hint" with an * unacknowledged write concern on an unsupported server. */ if ( isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) ) { throw UnsupportedException::hintNotSupported(); } @@ -174,14 +149,17 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { $cmd = ['delete' => $this->collectionName, 'deletes' => [['q' => $this->filter] + $this->createDeleteOptions()]]; - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + if (isset($this->options['let'])) { + $cmd['let'] = (object) $this->options['let']; } return $cmd; diff --git a/src/Operation/DeleteMany.php b/src/Operation/DeleteMany.php index 33497f3f4..5278ee08a 100644 --- a/src/Operation/DeleteMany.php +++ b/src/Operation/DeleteMany.php @@ -26,14 +26,12 @@ /** * Operation for deleting multiple document with the delete command. * - * @api * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -class DeleteMany implements Executable, Explainable +final class DeleteMany implements Explainable { - /** @var Delete */ - private $delete; + private Delete $delete; /** * Constructs a delete command. @@ -68,7 +66,7 @@ class DeleteMany implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array $options = []) { $this->delete = new Delete($databaseName, $collectionName, $filter, 0, $options); } @@ -76,12 +74,10 @@ public function __construct(string $databaseName, string $collectionName, $filte /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { return $this->delete->execute($server); } @@ -90,10 +86,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->delete->getCommandDocument($server); + return $this->delete->getCommandDocument(); } } diff --git a/src/Operation/DeleteOne.php b/src/Operation/DeleteOne.php index a91d67bb5..44dd47ba5 100644 --- a/src/Operation/DeleteOne.php +++ b/src/Operation/DeleteOne.php @@ -26,14 +26,12 @@ /** * Operation for deleting a single document with the delete command. * - * @api * @see \MongoDB\Collection::deleteOne() * @see https://mongodb.com/docs/manual/reference/command/delete/ */ -class DeleteOne implements Executable, Explainable +final class DeleteOne implements Explainable { - /** @var Delete */ - private $delete; + private Delete $delete; /** * Constructs a delete command. @@ -68,7 +66,7 @@ class DeleteOne implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array $options = []) { $this->delete = new Delete($databaseName, $collectionName, $filter, 1, $options); } @@ -76,12 +74,10 @@ public function __construct(string $databaseName, string $collectionName, $filte /** * Execute the operation. * - * @see Executable::execute() - * @return DeleteResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): DeleteResult { return $this->delete->execute($server); } @@ -90,10 +86,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->delete->getCommandDocument($server); + return $this->delete->getCommandDocument(); } } diff --git a/src/Operation/Distinct.php b/src/Operation/Distinct.php index 3c2f93dbd..c2aa3ef23 100644 --- a/src/Operation/Distinct.php +++ b/src/Operation/Distinct.php @@ -31,32 +31,18 @@ use function is_array; use function is_integer; use function is_object; +use function is_string; use function MongoDB\create_field_path_type_map; +use function MongoDB\is_document; /** * Operation for the distinct command. * - * @api * @see \MongoDB\Collection::distinct() * @see https://mongodb.com/docs/manual/reference/command/distinct/ */ -class Distinct implements Executable, Explainable +final class Distinct implements Explainable { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var string */ - private $fieldName; - - /** @var array|object */ - private $filter; - - /** @var array */ - private $options; - /** * Constructs a distinct command. * @@ -66,7 +52,13 @@ class Distinct implements Executable, Explainable * * * comment (mixed): BSON value to attach as a comment to this command. * - * This is not supported for servers versions < 4.4. + * This is not supported for server versions < 4.4. + * + * * hint (string|document): The index to use. Specify either the index + * name as a string or the index key pattern as a document. If specified, + * then the query system will only consider plans using the hinted index. + * + * This is not supported for server versions < 7.1. * * * maxTimeMS (integer): The maximum amount of time to allow the query to * run. @@ -86,57 +78,53 @@ class Distinct implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, string $fieldName, $filter = [], array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private string $fieldName, private array|object $filter = [], private array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $this->options['hint']); } - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { + throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { - unset($options['readConcern']); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->fieldName = $fieldName; - $this->filter = $filter; - $this->options = $options; + if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { + unset($this->options['readConcern']); + } } /** * Execute the operation. * - * @see Executable::execute() - * @return array * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -162,11 +150,17 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->createCommandDocument(); + $cmd = $this->createCommandDocument(); + + // Read concern can change the query plan + if (isset($this->options['readConcern'])) { + $cmd['readConcern'] = $this->options['readConcern']; + } + + return $cmd; } /** @@ -179,7 +173,7 @@ private function createCommandDocument(): array 'key' => $this->fieldName, ]; - if (! empty($this->filter)) { + if ($this->filter !== []) { $cmd['query'] = (object) $this->filter; } @@ -187,6 +181,11 @@ private function createCommandDocument(): array $cmd['collation'] = (object) $this->options['collation']; } + if (isset($this->options['hint'])) { + /** @psalm-var string|object */ + $cmd['hint'] = is_array($this->options['hint']) ? (object) $this->options['hint'] : $this->options['hint']; + } + foreach (['comment', 'maxTimeMS'] as $option) { if (isset($this->options[$option])) { $cmd[$option] = $this->options[$option]; diff --git a/src/Operation/DropCollection.php b/src/Operation/DropCollection.php index 11e85bdd7..0c520801c 100644 --- a/src/Operation/DropCollection.php +++ b/src/Operation/DropCollection.php @@ -26,30 +26,16 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; - /** * Operation for the drop command. * - * @api * @see \MongoDB\Collection::drop() * @see \MongoDB\Database::dropCollection() * @see https://mongodb.com/docs/manual/reference/command/drop/ */ -class DropCollection implements Executable +final class DropCollection { - /** @var integer */ - private static $errorCodeNamespaceNotFound = 26; - - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $options; + private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; /** * Constructs a drop command. @@ -62,9 +48,6 @@ class DropCollection implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -72,38 +55,28 @@ class DropCollection implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array $options = []) { - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { @@ -111,23 +84,16 @@ public function execute(Server $server) } try { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. - * Check for an error code and return the command reply instead of - * throwing. */ - if ($e->getCode() === self::$errorCodeNamespaceNotFound) { - return $e->getResultDocument(); + * Ignore the exception to make the drop operation idempotent */ + if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND) { + return; } throw $e; } - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); } /** diff --git a/src/Operation/DropDatabase.php b/src/Operation/DropDatabase.php index c909b2436..1cc71a4c0 100644 --- a/src/Operation/DropDatabase.php +++ b/src/Operation/DropDatabase.php @@ -24,25 +24,15 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; -use function current; -use function is_array; - /** * Operation for the dropDatabase command. * - * @api * @see \MongoDB\Client::dropDatabase() * @see \MongoDB\Database::drop() * @see https://mongodb.com/docs/manual/reference/command/dropDatabase/ */ -class DropDatabase implements Executable +final class DropDatabase { - /** @var string */ - private $databaseName; - - /** @var array */ - private $options; - /** * Constructs a dropDatabase command. * @@ -54,53 +44,35 @@ class DropDatabase implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, array $options = []) + public function __construct(private string $databaseName, private array $options = []) { - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - $this->databaseName = $databaseName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/DropEncryptedCollection.php b/src/Operation/DropEncryptedCollection.php new file mode 100644 index 000000000..646844a3a --- /dev/null +++ b/src/Operation/DropEncryptedCollection.php @@ -0,0 +1,96 @@ + */ + private array $dropMetadataCollections; + + /** + * Constructs an operation to drop an encrypted collection and its related + * metadata collections. + * + * The following option is supported in addition to the options for + * DropCollection: + * + * * encryptedFields (document): Configuration for encrypted fields. + * See: https://www.mongodb.com/docs/manual/core/queryable-encryption/fundamentals/encrypt-and-query/ + * + * @see DropCollection::__construct() for supported options + * @param string $databaseName Database name + * @param string $collectionName Collection name + * @param array $options DropCollection options + * @throws InvalidArgumentException for parameter/option parsing errors + */ + public function __construct(string $databaseName, string $collectionName, array $options) + { + if (! isset($options['encryptedFields'])) { + throw new InvalidArgumentException('"encryptedFields" option is required'); + } + + if (! is_document($options['encryptedFields'])) { + throw InvalidArgumentException::expectedDocumentType('"encryptedFields" option', $options['encryptedFields']); + } + + /** @psalm-var array{ecocCollection?: ?string, escCollection?: ?string} */ + $encryptedFields = document_to_array($options['encryptedFields']); + + $this->dropMetadataCollections = [ + new DropCollection($databaseName, $encryptedFields['escCollection'] ?? 'enxcol_.' . $collectionName . '.esc'), + new DropCollection($databaseName, $encryptedFields['ecocCollection'] ?? 'enxcol_.' . $collectionName . '.ecoc'), + ]; + + // DropCollection does not use the "encryptedFields" option + unset($options['encryptedFields']); + + $this->dropCollection = new DropCollection($databaseName, $collectionName, $options); + } + + /** @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ + public function execute(Server $server): void + { + foreach ($this->dropMetadataCollections as $dropMetadataCollection) { + $dropMetadataCollection->execute($server); + } + + $this->dropCollection->execute($server); + } +} diff --git a/src/Operation/DropIndexes.php b/src/Operation/DropIndexes.php index ef0e2f4d3..46a89f22f 100644 --- a/src/Operation/DropIndexes.php +++ b/src/Operation/DropIndexes.php @@ -25,31 +25,16 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; use function is_integer; /** * Operation for the dropIndexes command. * - * @api * @see \MongoDB\Collection::dropIndexes() * @see https://mongodb.com/docs/manual/reference/command/dropIndexes/ */ -class DropIndexes implements Executable +final class DropIndexes { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var string */ - private $indexName; - - /** @var array */ - private $options; - /** * Constructs a dropIndexes command. * @@ -64,9 +49,6 @@ class DropIndexes implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -75,62 +57,43 @@ class DropIndexes implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, string $indexName, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private string $indexName, private array $options = []) { - $indexName = $indexName; - if ($indexName === '') { throw new InvalidArgumentException('$indexName cannot be empty'); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->indexName = $indexName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/DropSearchIndex.php b/src/Operation/DropSearchIndex.php new file mode 100644 index 000000000..c79025fbb --- /dev/null +++ b/src/Operation/DropSearchIndex.php @@ -0,0 +1,79 @@ + $this->collectionName, + 'name' => $this->name, + ]; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + try { + $server->executeCommand($this->databaseName, new Command($cmd)); + } catch (CommandException $e) { + // Drop operations are idempotent. The server may return an error if the collection does not exist. + if ($e->getCode() !== self::ERROR_CODE_NAMESPACE_NOT_FOUND) { + throw $e; + } + } + } +} diff --git a/src/Operation/EstimatedDocumentCount.php b/src/Operation/EstimatedDocumentCount.php index 6deeef3e5..716996541 100644 --- a/src/Operation/EstimatedDocumentCount.php +++ b/src/Operation/EstimatedDocumentCount.php @@ -32,26 +32,12 @@ /** * Operation for obtaining an estimated count of documents in a collection * - * @api * @see \MongoDB\Collection::estimatedDocumentCount() * @see https://mongodb.com/docs/manual/reference/command/count/ */ -class EstimatedDocumentCount implements Executable, Explainable +final class EstimatedDocumentCount implements Explainable { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $options; - - /** @var int */ - private static $errorCodeCollectionNotFound = 26; - - /** @var int */ - private static $wireVersionForCollStats = 12; + private array $options; /** * Constructs a command to get the estimated number of documents in a @@ -77,11 +63,8 @@ class EstimatedDocumentCount implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, array $options = []) { - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); } @@ -104,13 +87,11 @@ public function __construct(string $databaseName, string $collectionName, array /** * Execute the operation. * - * @see Executable::execute() - * @return integer * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): int { return $this->createCount()->execute($server); } @@ -119,11 +100,10 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->createCount()->getCommandDocument($server); + return $this->createCount()->getCommandDocument(); } private function createCount(): Count diff --git a/src/Operation/Executable.php b/src/Operation/Executable.php deleted file mode 100644 index 5bd8c68b3..000000000 --- a/src/Operation/Executable.php +++ /dev/null @@ -1,38 +0,0 @@ -options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - if (isset($options['verbosity']) && ! is_string($options['verbosity'])) { - throw InvalidArgumentException::invalidType('"verbosity" option', $options['verbosity'], 'string'); + if (isset($this->options['verbosity']) && ! is_string($this->options['verbosity'])) { + throw InvalidArgumentException::invalidType('"verbosity" option', $this->options['verbosity'], 'string'); } - - $this->databaseName = $databaseName; - $this->explainable = $explainable; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object * @throws UnsupportedException if the server does not support explaining the operation * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object { - if ($this->explainable instanceof Aggregate && ! server_supports_feature($server, self::$wireVersionForAggregate)) { - throw UnsupportedException::explainNotSupported(); - } - - $cursor = $server->executeCommand($this->databaseName, $this->createCommand($server), $this->createOptions()); + $cursor = $server->executeCommand($this->databaseName, $this->createCommand(), $this->createOptions()); if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); @@ -127,9 +103,9 @@ public function execute(Server $server) /** * Create the explain command. */ - private function createCommand(Server $server): Command + private function createCommand(): Command { - $cmd = ['explain' => $this->explainable->getCommandDocument($server)]; + $cmd = ['explain' => $this->explainable->getCommandDocument()]; foreach (['comment', 'verbosity'] as $option) { if (isset($this->options[$option])) { @@ -159,13 +135,4 @@ private function createOptions(): array return $options; } - - private function isFindAndModify(Explainable $explainable): bool - { - if ($explainable instanceof FindAndModify || $explainable instanceof FindOneAndDelete || $explainable instanceof FindOneAndReplace || $explainable instanceof FindOneAndUpdate) { - return true; - } - - return false; - } } diff --git a/src/Operation/Explainable.php b/src/Operation/Explainable.php index 7647b51ec..0f8717f4b 100644 --- a/src/Operation/Explainable.php +++ b/src/Operation/Explainable.php @@ -17,20 +17,16 @@ namespace MongoDB\Operation; -use MongoDB\Driver\Server; - /** * Explainable interface for explainable operations (aggregate, count, distinct, * find, findAndModify, delete, and update). * * @internal */ -interface Explainable extends Executable +interface Explainable { /** * Returns the command document for this operation. - * - * @return array */ - public function getCommandDocument(Server $server); + public function getCommandDocument(): array; } diff --git a/src/Operation/Find.php b/src/Operation/Find.php index 619e6f5c5..8d84c7bf0 100644 --- a/src/Operation/Find.php +++ b/src/Operation/Find.php @@ -17,7 +17,8 @@ namespace MongoDB\Operation; -use MongoDB\Driver\Cursor; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Query; use MongoDB\Driver\ReadConcern; @@ -26,42 +27,28 @@ use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; +use MongoDB\Model\CodecCursor; +use function assert; use function is_array; use function is_bool; use function is_integer; -use function is_object; use function is_string; -use function trigger_error; - -use const E_USER_DEPRECATED; +use function MongoDB\is_document; /** * Operation for the find command. * - * @api * @see \MongoDB\Collection::find() * @see https://mongodb.com/docs/manual/tutorial/query-documents/ * @see https://mongodb.com/docs/manual/reference/operator/query-modifier/ */ -class Find implements Executable, Explainable +final class Find implements Explainable { public const NON_TAILABLE = 1; public const TAILABLE = 2; public const TAILABLE_AWAIT = 3; - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array|object */ - private $filter; - - /** @var array */ - private $options; - /** * Constructs a find command. * @@ -76,6 +63,9 @@ class Find implements Executable, Explainable * * * batchSize (integer): The number of documents to return per batch. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -97,28 +87,15 @@ class Find implements Executable, Explainable * * maxAwaitTimeMS (integer): The maxium amount of time for the server to wait * on new documents to satisfy a query, if cursorType is TAILABLE_AWAIT. * - * * maxScan (integer): Maximum number of documents or index keys to scan - * when executing the query. - * - * This option has been deprecated since version 1.4. - * * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. If "$maxTimeMS" also exists in the modifiers document, this - * option will take precedence. + * run. * * * min (document): The inclusive upper bound for a specific index. * - * * modifiers (document): Meta operators that modify the output or - * behavior of a query. Use of these operators is deprecated in favor of - * named options. - * * * noCursorTimeout (boolean): The server normally times out idle cursors * after an inactivity period (10 minutes) to prevent excess memory use. * Set this option to prevent that. * - * * oplogReplay (boolean): Internal replication use only. The driver - * should not set this. This option is deprecated as of MongoDB 4.4. - * * * projection (document): Limits the fields to return for the matching * document. * @@ -137,14 +114,7 @@ class Find implements Executable, Explainable * * * skip (integer): The number of documents to skip before returning. * - * * snapshot (boolean): Prevents the cursor from returning a document more - * than once because of an intervening write operation. - * - * This options has been deprecated since version 1.4. - * - * * sort (document): The order in which to return matching documents. If - * "$orderby" also exists in the modifiers document, this option will - * take precedence. + * * sort (document): The order in which to return matching documents. * * * let (document): Map of parameter names and values. Values must be * constant or closed expressions that do not reference document fields. @@ -160,153 +130,130 @@ class Find implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array|object $filter, private array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (isset($options['allowDiskUse']) && ! is_bool($options['allowDiskUse'])) { - throw InvalidArgumentException::invalidType('"allowDiskUse" option', $options['allowDiskUse'], 'boolean'); + if (isset($this->options['allowDiskUse']) && ! is_bool($this->options['allowDiskUse'])) { + throw InvalidArgumentException::invalidType('"allowDiskUse" option', $this->options['allowDiskUse'], 'boolean'); } - if (isset($options['allowPartialResults']) && ! is_bool($options['allowPartialResults'])) { - throw InvalidArgumentException::invalidType('"allowPartialResults" option', $options['allowPartialResults'], 'boolean'); + if (isset($this->options['allowPartialResults']) && ! is_bool($this->options['allowPartialResults'])) { + throw InvalidArgumentException::invalidType('"allowPartialResults" option', $this->options['allowPartialResults'], 'boolean'); } - if (isset($options['batchSize']) && ! is_integer($options['batchSize'])) { - throw InvalidArgumentException::invalidType('"batchSize" option', $options['batchSize'], 'integer'); + if (isset($this->options['batchSize']) && ! is_integer($this->options['batchSize'])) { + throw InvalidArgumentException::invalidType('"batchSize" option', $this->options['batchSize'], 'integer'); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($this->options['codec']) && ! $this->options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $this->options['codec'], DocumentCodec::class); } - if (isset($options['cursorType'])) { - if (! is_integer($options['cursorType'])) { - throw InvalidArgumentException::invalidType('"cursorType" option', $options['cursorType'], 'integer'); + if (isset($this->options['collation']) && ! is_document($this->options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $this->options['collation']); + } + + if (isset($this->options['cursorType'])) { + if (! is_integer($this->options['cursorType'])) { + throw InvalidArgumentException::invalidType('"cursorType" option', $this->options['cursorType'], 'integer'); } if ( - $options['cursorType'] !== self::NON_TAILABLE && - $options['cursorType'] !== self::TAILABLE && - $options['cursorType'] !== self::TAILABLE_AWAIT + $this->options['cursorType'] !== self::NON_TAILABLE && + $this->options['cursorType'] !== self::TAILABLE && + $this->options['cursorType'] !== self::TAILABLE_AWAIT ) { - throw new InvalidArgumentException('Invalid value for "cursorType" option: ' . $options['cursorType']); + throw new InvalidArgumentException('Invalid value for "cursorType" option: ' . $this->options['cursorType']); } } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], 'string or array or object'); - } - - if (isset($options['limit']) && ! is_integer($options['limit'])) { - throw InvalidArgumentException::invalidType('"limit" option', $options['limit'], 'integer'); - } - - if (isset($options['max']) && ! is_array($options['max']) && ! is_object($options['max'])) { - throw InvalidArgumentException::invalidType('"max" option', $options['max'], 'array or object'); + if (isset($this->options['hint']) && ! is_string($this->options['hint']) && ! is_document($this->options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $this->options['hint']); } - if (isset($options['maxAwaitTimeMS']) && ! is_integer($options['maxAwaitTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $options['maxAwaitTimeMS'], 'integer'); + if (isset($this->options['limit']) && ! is_integer($this->options['limit'])) { + throw InvalidArgumentException::invalidType('"limit" option', $this->options['limit'], 'integer'); } - if (isset($options['maxScan']) && ! is_integer($options['maxScan'])) { - throw InvalidArgumentException::invalidType('"maxScan" option', $options['maxScan'], 'integer'); + if (isset($this->options['max']) && ! is_document($this->options['max'])) { + throw InvalidArgumentException::expectedDocumentType('"max" option', $this->options['max']); } - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxAwaitTimeMS']) && ! is_integer($this->options['maxAwaitTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxAwaitTimeMS" option', $this->options['maxAwaitTimeMS'], 'integer'); } - if (isset($options['min']) && ! is_array($options['min']) && ! is_object($options['min'])) { - throw InvalidArgumentException::invalidType('"min" option', $options['min'], 'array or object'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['modifiers']) && ! is_array($options['modifiers']) && ! is_object($options['modifiers'])) { - throw InvalidArgumentException::invalidType('"modifiers" option', $options['modifiers'], 'array or object'); + if (isset($this->options['min']) && ! is_document($this->options['min'])) { + throw InvalidArgumentException::expectedDocumentType('"min" option', $this->options['min']); } - if (isset($options['noCursorTimeout']) && ! is_bool($options['noCursorTimeout'])) { - throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $options['noCursorTimeout'], 'boolean'); + if (isset($this->options['noCursorTimeout']) && ! is_bool($this->options['noCursorTimeout'])) { + throw InvalidArgumentException::invalidType('"noCursorTimeout" option', $this->options['noCursorTimeout'], 'boolean'); } - if (isset($options['oplogReplay']) && ! is_bool($options['oplogReplay'])) { - throw InvalidArgumentException::invalidType('"oplogReplay" option', $options['oplogReplay'], 'boolean'); + if (isset($this->options['projection']) && ! is_document($this->options['projection'])) { + throw InvalidArgumentException::expectedDocumentType('"projection" option', $this->options['projection']); } - if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { - throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); + if (isset($this->options['readConcern']) && ! $this->options['readConcern'] instanceof ReadConcern) { + throw InvalidArgumentException::invalidType('"readConcern" option', $this->options['readConcern'], ReadConcern::class); } - if (isset($options['readConcern']) && ! $options['readConcern'] instanceof ReadConcern) { - throw InvalidArgumentException::invalidType('"readConcern" option', $options['readConcern'], ReadConcern::class); + if (isset($this->options['readPreference']) && ! $this->options['readPreference'] instanceof ReadPreference) { + throw InvalidArgumentException::invalidType('"readPreference" option', $this->options['readPreference'], ReadPreference::class); } - if (isset($options['readPreference']) && ! $options['readPreference'] instanceof ReadPreference) { - throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); + if (isset($this->options['returnKey']) && ! is_bool($this->options['returnKey'])) { + throw InvalidArgumentException::invalidType('"returnKey" option', $this->options['returnKey'], 'boolean'); } - if (isset($options['returnKey']) && ! is_bool($options['returnKey'])) { - throw InvalidArgumentException::invalidType('"returnKey" option', $options['returnKey'], 'boolean'); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['showRecordId']) && ! is_bool($this->options['showRecordId'])) { + throw InvalidArgumentException::invalidType('"showRecordId" option', $this->options['showRecordId'], 'boolean'); } - if (isset($options['showRecordId']) && ! is_bool($options['showRecordId'])) { - throw InvalidArgumentException::invalidType('"showRecordId" option', $options['showRecordId'], 'boolean'); + if (isset($this->options['skip']) && ! is_integer($this->options['skip'])) { + throw InvalidArgumentException::invalidType('"skip" option', $this->options['skip'], 'integer'); } - if (isset($options['skip']) && ! is_integer($options['skip'])) { - throw InvalidArgumentException::invalidType('"skip" option', $options['skip'], 'integer'); + if (isset($this->options['sort']) && ! is_document($this->options['sort'])) { + throw InvalidArgumentException::expectedDocumentType('"sort" option', $this->options['sort']); } - if (isset($options['snapshot']) && ! is_bool($options['snapshot'])) { - throw InvalidArgumentException::invalidType('"snapshot" option', $options['snapshot'], 'boolean'); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) { - throw InvalidArgumentException::invalidType('"sort" option', $options['sort'], 'array or object'); + if (isset($this->options['let']) && ! is_document($this->options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $this->options['let']); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['readConcern']) && $this->options['readConcern']->isDefault()) { + unset($this->options['readConcern']); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + if (isset($this->options['codec']) && isset($this->options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); } - - if (isset($options['readConcern']) && $options['readConcern']->isDefault()) { - unset($options['readConcern']); - } - - if (isset($options['snapshot'])) { - trigger_error('The "snapshot" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - if (isset($options['maxScan'])) { - trigger_error('The "maxScan" option is deprecated and will be removed in a future release', E_USER_DEPRECATED); - } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return Cursor * @throws UnsupportedException if read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): CursorInterface { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['readConcern'])) { @@ -315,6 +262,10 @@ public function execute(Server $server) $cursor = $server->executeQuery($this->databaseName . '.' . $this->collectionName, new Query($this->filter, $this->createQueryOptions()), $this->createExecuteOptions()); + if (isset($this->options['codec'])) { + return CodecCursor::fromCursor($cursor, $this->options['codec']); + } + if (isset($this->options['typeMap'])) { $cursor->setTypeMap($this->options['typeMap']); } @@ -326,17 +277,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) - { - return $this->createCommandDocument(); - } - - /** - * Construct a command document for Find - */ - private function createCommandDocument(): array + public function getCommandDocument(): array { $cmd = ['find' => $this->collectionName, 'filter' => (object) $this->filter]; @@ -349,28 +291,6 @@ private function createCommandDocument(): array // maxAwaitTimeMS is a Query level option so should not be considered here unset($options['maxAwaitTimeMS']); - $modifierFallback = [ - ['allowPartialResults', 'partial'], - ['comment', '$comment'], - ['hint', '$hint'], - ['maxScan', '$maxScan'], - ['max', '$max'], - ['maxTimeMS', '$maxTimeMS'], - ['min', '$min'], - ['returnKey', '$returnKey'], - ['showRecordId', '$showDiskLoc'], - ['sort', '$orderby'], - ['snapshot', '$snapshot'], - ]; - - foreach ($modifierFallback as $modifier) { - if (! isset($options[$modifier[0]]) && isset($options['modifiers'][$modifier[1]])) { - $options[$modifier[0]] = $options['modifiers'][$modifier[1]]; - } - } - - unset($options['modifiers']); - return $cmd + $options; } @@ -415,7 +335,7 @@ private function createQueryOptions(): array } } - foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'noCursorTimeout', 'oplogReplay', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'] as $option) { + foreach (['allowDiskUse', 'allowPartialResults', 'batchSize', 'comment', 'hint', 'limit', 'maxAwaitTimeMS', 'maxTimeMS', 'noCursorTimeout', 'projection', 'readConcern', 'returnKey', 'showRecordId', 'skip', 'sort'] as $option) { if (isset($this->options[$option])) { $options[$option] = $this->options[$option]; } @@ -427,10 +347,14 @@ private function createQueryOptions(): array } } - $modifiers = empty($this->options['modifiers']) ? [] : (array) $this->options['modifiers']; + // Ensure no cursor is left behind when limit == batchSize by increasing batchSize + if (isset($options['limit'], $options['batchSize']) && $options['limit'] === $options['batchSize']) { + assert(is_integer($options['batchSize'])); + $options['batchSize']++; + } - if (! empty($modifiers)) { - $options['modifiers'] = $modifiers; + if (isset($options['limit']) && $options['limit'] === 1) { + $options['singleBatch'] = true; } return $options; diff --git a/src/Operation/FindAndModify.php b/src/Operation/FindAndModify.php index d4fc2b3a4..3d77dd643 100644 --- a/src/Operation/FindAndModify.php +++ b/src/Operation/FindAndModify.php @@ -17,6 +17,8 @@ namespace MongoDB\Operation; +use MongoDB\BSON\Document; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Command; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; @@ -27,6 +29,7 @@ use MongoDB\Exception\UnsupportedException; use function array_key_exists; +use function assert; use function current; use function is_array; use function is_bool; @@ -34,6 +37,7 @@ use function is_object; use function is_string; use function MongoDB\create_field_path_type_map; +use function MongoDB\is_document; use function MongoDB\is_pipeline; use function MongoDB\is_write_concern_acknowledged; use function MongoDB\server_supports_feature; @@ -47,22 +51,13 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -class FindAndModify implements Executable, Explainable +final class FindAndModify implements Explainable { - /** @var integer */ - private static $wireVersionForHint = 9; + private const WIRE_VERSION_FOR_HINT = 9; - /** @var integer */ - private static $wireVersionForUnsupportedOptionServerSideError = 8; + private const WIRE_VERSION_FOR_UNSUPPORTED_OPTION_SERVER_SIDE_ERROR = 8; - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $options; + private array $options; /** * Constructs a findAndModify command. @@ -72,6 +67,9 @@ class FindAndModify implements Executable, Explainable * * arrayFilters (document array): A set of filters specifying to which * array elements an update should apply. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -129,7 +127,7 @@ class FindAndModify implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $options) + public function __construct(private string $databaseName, private string $collectionName, array $options) { $options += ['remove' => false]; @@ -141,16 +139,20 @@ public function __construct(string $databaseName, string $collectionName, array throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } - if (isset($options['fields']) && ! is_array($options['fields']) && ! is_object($options['fields'])) { - throw InvalidArgumentException::invalidType('"fields" option', $options['fields'], 'array or object'); + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); + if (isset($options['fields']) && ! is_document($options['fields'])) { + throw InvalidArgumentException::expectedDocumentType('"fields" option', $options['fields']); + } + + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); } if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { @@ -161,8 +163,8 @@ public function __construct(string $databaseName, string $collectionName, array throw InvalidArgumentException::invalidType('"new" option', $options['new'], 'boolean'); } - if (isset($options['query']) && ! is_array($options['query']) && ! is_object($options['query'])) { - throw InvalidArgumentException::invalidType('"query" option', $options['query'], 'array or object'); + if (isset($options['query']) && ! is_document($options['query'])) { + throw InvalidArgumentException::expectedDocumentType('"query" option', $options['query']); } if (! is_bool($options['remove'])) { @@ -173,8 +175,8 @@ public function __construct(string $databaseName, string $collectionName, array throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); } - if (isset($options['sort']) && ! is_array($options['sort']) && ! is_object($options['sort'])) { - throw InvalidArgumentException::invalidType('"sort" option', $options['sort'], 'array or object'); + if (isset($options['sort']) && ! is_document($options['sort'])) { + throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']); } if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { @@ -193,8 +195,8 @@ public function __construct(string $databaseName, string $collectionName, array throw InvalidArgumentException::invalidType('"upsert" option', $options['upsert'], 'boolean'); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + if (isset($options['let']) && ! is_document($options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); } if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { @@ -209,25 +211,25 @@ public function __construct(string $databaseName, string $collectionName, array unset($options['writeConcern']); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; + if (isset($options['codec']) && isset($options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); + } + $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnexpectedValueException if the command response was malformed * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { /* Server versions >= 4.2.0 raise errors for unsupported update options. * For previous versions, the CRUD spec requires a client-side error. */ - if (isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForUnsupportedOptionServerSideError)) { + if (isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_UNSUPPORTED_OPTION_SERVER_SIDE_ERROR)) { throw UnsupportedException::hintNotSupported(); } @@ -235,7 +237,7 @@ public function execute(Server $server) * unacknowledged write concern on an unsupported server. */ if ( isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) ) { throw UnsupportedException::hintNotSupported(); } @@ -247,6 +249,16 @@ public function execute(Server $server) $cursor = $server->executeWriteCommand($this->databaseName, new Command($this->createCommandDocument()), $this->createOptions()); + if (isset($this->options['codec'])) { + $cursor->setTypeMap(['root' => 'bson']); + $result = current($cursor->toArray()); + assert($result instanceof Document); + + $value = $result->get('value'); + + return $value === null ? $value : $this->options['codec']->decode($value); + } + if (isset($this->options['typeMap'])) { $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'value')); } @@ -260,9 +272,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { return $this->createCommandDocument(); } @@ -293,9 +304,17 @@ private function createCommandDocument(): array } if (isset($this->options['update'])) { - $cmd['update'] = is_pipeline($this->options['update']) - ? $this->options['update'] - : (object) $this->options['update']; + /** @psalm-var array|object */ + $update = $this->options['update']; + /* A non-empty pipeline will encode as a BSON array, so leave it + * as-is. Cast anything else to an object since a BSON document is + * likely expected. This includes empty arrays, which historically + * can be used to represent empty replacement documents. + * + * This also allows an empty pipeline expressed as a PackedArray or + * Serializable to still encode as a BSON array, since the object + * cast will have no effect. */ + $cmd['update'] = is_pipeline($update) ? $update : (object) $update; } foreach (['arrayFilters', 'bypassDocumentValidation', 'comment', 'hint', 'maxTimeMS'] as $option) { diff --git a/src/Operation/FindOne.php b/src/Operation/FindOne.php index 3de70dae6..f685de5ae 100644 --- a/src/Operation/FindOne.php +++ b/src/Operation/FindOne.php @@ -27,21 +27,22 @@ /** * Operation for finding a single document with the find command. * - * @api * @see \MongoDB\Collection::findOne() * @see https://mongodb.com/docs/manual/tutorial/query-documents/ * @see https://mongodb.com/docs/manual/reference/operator/query-modifier/ */ -class FindOne implements Executable, Explainable +final class FindOne implements Explainable { - /** @var Find */ - private $find; + private Find $find; /** * Constructs a find command for finding a single document. * * Supported options: * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -54,20 +55,11 @@ class FindOne implements Executable, Explainable * * * max (document): The exclusive upper bound for a specific index. * - * * maxScan (integer): Maximum number of documents or index keys to scan - * when executing the query. - * - * This option has been deprecated since version 1.4. - * * * maxTimeMS (integer): The maximum amount of time to allow the query to - * run. If "$maxTimeMS" also exists in the modifiers document, this - * option will take precedence. + * run. * * * min (document): The inclusive upper bound for a specific index. * - * * modifiers (document): Meta-operators modifying the output or behavior - * of a query. - * * * projection (document): Limits the fields to return for the matching * document. * @@ -86,9 +78,7 @@ class FindOne implements Executable, Explainable * * * skip (integer): The number of documents to skip before returning. * - * * sort (document): The order in which to return matching documents. If - * "$orderby" also exists in the modifiers document, this option will - * take precedence. + * * sort (document): The order in which to return matching documents. * * * let (document): Map of parameter names and values. Values must be * constant or closed expressions that do not reference document fields. @@ -103,25 +93,23 @@ class FindOne implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array $options = []) { $this->find = new Find( $databaseName, $collectionName, $filter, - ['limit' => 1] + $options + ['limit' => 1] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or read concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { $cursor = $this->find->execute($server); $document = current($cursor->toArray()); @@ -133,10 +121,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->find->getCommandDocument($server); + return $this->find->getCommandDocument(); } } diff --git a/src/Operation/FindOneAndDelete.php b/src/Operation/FindOneAndDelete.php index c4b54dadf..7c0f22ad0 100644 --- a/src/Operation/FindOneAndDelete.php +++ b/src/Operation/FindOneAndDelete.php @@ -22,26 +22,26 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function is_array; -use function is_object; +use function MongoDB\is_document; /** * Operation for deleting a document with the findAndModify command. * - * @api * @see \MongoDB\Collection::findOneAndDelete() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -class FindOneAndDelete implements Executable, Explainable +final class FindOneAndDelete implements Explainable { - /** @var FindAndModify */ - private $findAndModify; + private FindAndModify $findAndModify; /** * Constructs a findAndModify command for deleting a document. * * Supported options: * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -81,14 +81,14 @@ class FindOneAndDelete implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { - throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); + if (isset($options['projection']) && ! is_document($options['projection'])) { + throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']); } if (isset($options['projection'])) { @@ -100,19 +100,17 @@ public function __construct(string $databaseName, string $collectionName, $filte $this->findAndModify = new FindAndModify( $databaseName, $collectionName, - ['query' => $filter, 'remove' => true] + $options + ['query' => $filter, 'remove' => true] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -121,10 +119,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); } } diff --git a/src/Operation/FindOneAndReplace.php b/src/Operation/FindOneAndReplace.php index 0e619b86d..5ea0c5a34 100644 --- a/src/Operation/FindOneAndReplace.php +++ b/src/Operation/FindOneAndReplace.php @@ -17,31 +17,30 @@ namespace MongoDB\Operation; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; use function array_key_exists; -use function is_array; use function is_integer; -use function is_object; +use function MongoDB\is_document; use function MongoDB\is_first_key_operator; +use function MongoDB\is_pipeline; /** * Operation for replacing a document with the findAndModify command. * - * @api * @see \MongoDB\Collection::findOneAndReplace() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -class FindOneAndReplace implements Executable, Explainable +final class FindOneAndReplace implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; - /** @var FindAndModify */ - private $findAndModify; + private FindAndModify $findAndModify; /** * Constructs a findAndModify command for replacing a document. @@ -51,6 +50,9 @@ class FindOneAndReplace implements Executable, Explainable * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -100,22 +102,18 @@ class FindOneAndReplace implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array|object $replacement, array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } - if (! is_array($replacement) && ! is_object($replacement)) { - throw InvalidArgumentException::invalidType('$replacement', $replacement, 'array or object'); + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } - if (is_first_key_operator($replacement)) { - throw new InvalidArgumentException('First key in $replacement argument is an update operator'); - } - - if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { - throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); + if (isset($options['projection']) && ! is_document($options['projection'])) { + throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']); } if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) { @@ -140,22 +138,22 @@ public function __construct(string $databaseName, string $collectionName, $filte unset($options['projection'], $options['returnDocument']); + $replacement = $this->validateReplacement($replacement, $options['codec'] ?? null); + $this->findAndModify = new FindAndModify( $databaseName, $collectionName, - ['query' => $filter, 'update' => $replacement] + $options + ['query' => $filter, 'update' => $replacement] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -164,10 +162,35 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); + } + + private function validateReplacement(array|object $replacement, ?DocumentCodec $codec): array|object + { + if (isset($codec)) { + $replacement = $codec->encode($replacement); + } + + if (! is_document($replacement)) { + throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); + } + + // Treat empty arrays as replacement documents for BC + if ($replacement === []) { + $replacement = (object) $replacement; + } + + if (is_first_key_operator($replacement)) { + throw new InvalidArgumentException('First key in $replacement is an update operator'); + } + + if (is_pipeline($replacement, true /* allowEmpty */)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } + + return $replacement; } } diff --git a/src/Operation/FindOneAndUpdate.php b/src/Operation/FindOneAndUpdate.php index e9a592473..00e97972b 100644 --- a/src/Operation/FindOneAndUpdate.php +++ b/src/Operation/FindOneAndUpdate.php @@ -23,26 +23,23 @@ use MongoDB\Exception\UnsupportedException; use function array_key_exists; -use function is_array; use function is_integer; -use function is_object; +use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; /** * Operation for updating a document with the findAndModify command. * - * @api * @see \MongoDB\Collection::findOneAndUpdate() * @see https://mongodb.com/docs/manual/reference/command/findAndModify/ */ -class FindOneAndUpdate implements Executable, Explainable +final class FindOneAndUpdate implements Explainable { public const RETURN_DOCUMENT_BEFORE = 1; public const RETURN_DOCUMENT_AFTER = 2; - /** @var FindAndModify */ - private $findAndModify; + private FindAndModify $findAndModify; /** * Constructs a findAndModify command for updating a document. @@ -55,6 +52,9 @@ class FindOneAndUpdate implements Executable, Explainable * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -104,22 +104,18 @@ class FindOneAndUpdate implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array|object $update, array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); - } - - if (! is_array($update) && ! is_object($update)) { - throw InvalidArgumentException::invalidType('$update', $update, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } if (! is_first_key_operator($update) && ! is_pipeline($update)) { - throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline'); + throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update'); } - if (isset($options['projection']) && ! is_array($options['projection']) && ! is_object($options['projection'])) { - throw InvalidArgumentException::invalidType('"projection" option', $options['projection'], 'array or object'); + if (isset($options['projection']) && ! is_document($options['projection'])) { + throw InvalidArgumentException::expectedDocumentType('"projection" option', $options['projection']); } if (array_key_exists('returnDocument', $options) && ! is_integer($options['returnDocument'])) { @@ -147,19 +143,17 @@ public function __construct(string $databaseName, string $collectionName, $filte $this->findAndModify = new FindAndModify( $databaseName, $collectionName, - ['query' => $filter, 'update' => $update] + $options + ['query' => $filter, 'update' => $update] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object|null * @throws UnsupportedException if collation or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object|null { return $this->findAndModify->execute($server); } @@ -168,10 +162,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->findAndModify->getCommandDocument($server); + return $this->findAndModify->getCommandDocument(); } } diff --git a/src/Operation/InsertMany.php b/src/Operation/InsertMany.php index b8b3c1255..70e149076 100644 --- a/src/Operation/InsertMany.php +++ b/src/Operation/InsertMany.php @@ -17,6 +17,7 @@ namespace MongoDB\Operation; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\BulkWrite as Bulk; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; @@ -26,31 +27,23 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\InsertManyResult; -use function is_array; +use function array_is_list; use function is_bool; -use function is_object; +use function MongoDB\is_document; use function sprintf; /** * Operation for inserting multiple documents with the insert command. * - * @api * @see \MongoDB\Collection::insertMany() * @see https://mongodb.com/docs/manual/reference/command/insert/ */ -class InsertMany implements Executable +final class InsertMany { - /** @var string */ - private $databaseName; + /** @var list */ + private array $documents; - /** @var string */ - private $collectionName; - - /** @var object[]|array[] */ - private $documents; - - /** @var array */ - private $options; + private array $options; /** * Constructs an insert command. @@ -60,6 +53,9 @@ class InsertMany implements Executable * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to encode PHP objects + * into BSON. + * * * comment (mixed): BSON value to attach as a comment to the command(s) * associated with this insert. * @@ -73,38 +69,24 @@ class InsertMany implements Executable * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * - * @param string $databaseName Database name - * @param string $collectionName Collection name - * @param array[]|object[] $documents List of documents to insert - * @param array $options Command options + * @param string $databaseName Database name + * @param string $collectionName Collection name + * @param list $documents List of documents to insert + * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $documents, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, array $documents, array $options = []) { - if (empty($documents)) { - throw new InvalidArgumentException('$documents is empty'); - } - - $expectedIndex = 0; - - foreach ($documents as $i => $document) { - if ($i !== $expectedIndex) { - throw new InvalidArgumentException(sprintf('$documents is not a list (unexpected index: "%s")', $i)); - } - - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType(sprintf('$documents[%d]', $i), $document, 'array or object'); - } - - $expectedIndex += 1; - } - $options += ['ordered' => true]; if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); } + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); + } + if (! is_bool($options['ordered'])) { throw InvalidArgumentException::invalidType('"ordered" option', $options['ordered'], 'boolean'); } @@ -125,21 +107,17 @@ public function __construct(string $databaseName, string $collectionName, array unset($options['writeConcern']); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->documents = $documents; + $this->documents = $this->validateDocuments($documents, $options['codec'] ?? null); $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return InsertManyResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): InsertManyResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { @@ -195,4 +173,31 @@ private function createExecuteOptions(): array return $options; } + + /** + * @param list $documents + * @return list + */ + private function validateDocuments(array $documents, ?DocumentCodec $codec): array + { + if (empty($documents)) { + throw new InvalidArgumentException('$documents is empty'); + } + + if (! array_is_list($documents)) { + throw new InvalidArgumentException('$documents is not a list'); + } + + foreach ($documents as $i => $document) { + if ($codec) { + $document = $documents[$i] = $codec->encode($document); + } + + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType(sprintf('$documents[%d]', $i), $document); + } + } + + return $documents; + } } diff --git a/src/Operation/InsertOne.php b/src/Operation/InsertOne.php index d0690a5d5..dff9f79f6 100644 --- a/src/Operation/InsertOne.php +++ b/src/Operation/InsertOne.php @@ -17,6 +17,7 @@ namespace MongoDB\Operation; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\BulkWrite as Bulk; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; @@ -26,30 +27,18 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\InsertOneResult; -use function is_array; use function is_bool; -use function is_object; +use function MongoDB\is_document; /** * Operation for inserting a single document with the insert command. * - * @api * @see \MongoDB\Collection::insertOne() * @see https://mongodb.com/docs/manual/reference/command/insert/ */ -class InsertOne implements Executable +final class InsertOne { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array|object */ - private $document; - - /** @var array */ - private $options; + private array|object $document; /** * Constructs an insert command. @@ -59,6 +48,9 @@ class InsertOne implements Executable * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to encode PHP objects + * into BSON. + * * * comment (mixed): BSON value to attach as a comment to this command. * * This is not supported for servers versions < 4.4. @@ -73,47 +65,42 @@ class InsertOne implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $document, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, array|object $document, private array $options = []) { - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); + if (isset($this->options['bypassDocumentValidation']) && ! is_bool($this->options['bypassDocumentValidation'])) { + throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $this->options['bypassDocumentValidation'], 'boolean'); } - if (isset($options['bypassDocumentValidation']) && ! is_bool($options['bypassDocumentValidation'])) { - throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); + if (isset($this->options['codec']) && ! $this->options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $this->options['codec'], DocumentCodec::class); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { - unset($options['bypassDocumentValidation']); + if (isset($this->options['bypassDocumentValidation']) && ! $this->options['bypassDocumentValidation']) { + unset($this->options['bypassDocumentValidation']); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->document = $document; - $this->options = $options; + $this->document = $this->validateDocument($document, $this->options['codec'] ?? null); } /** * Execute the operation. * - * @see Executable::execute() - * @return InsertOneResult * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): InsertOneResult { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if (isset($this->options['writeConcern']) && $inTransaction) { @@ -121,6 +108,7 @@ public function execute(Server $server) } $bulk = new Bulk($this->createBulkWriteOptions()); + $insertedId = $bulk->insert($this->document); $writeResult = $server->executeBulkWrite($this->databaseName . '.' . $this->collectionName, $bulk, $this->createExecuteOptions()); @@ -165,4 +153,17 @@ private function createExecuteOptions(): array return $options; } + + private function validateDocument(array|object $document, ?DocumentCodec $codec): array|object + { + if ($codec) { + $document = $codec->encode($document); + } + + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType('$document', $document); + } + + return $document; + } } diff --git a/src/Operation/ListCollectionNames.php b/src/Operation/ListCollectionNames.php index d0700887b..3b32cd589 100644 --- a/src/Operation/ListCollectionNames.php +++ b/src/Operation/ListCollectionNames.php @@ -22,19 +22,18 @@ use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\CachingIterator; use MongoDB\Model\CallbackIterator; /** * Operation for the listCollectionNames helper. * - * @api * @see \MongoDB\Database::listCollectionNames() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -class ListCollectionNames implements Executable +final class ListCollectionNames { - /** @var ListCollectionsCommand */ - private $listCollections; + private ListCollectionsCommand $listCollections; /** * Constructs a listCollections command. @@ -69,17 +68,16 @@ public function __construct(string $databaseName, array $options = []) /** * Execute the operation. * - * @see Executable::execute() - * @return Iterator + * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ public function execute(Server $server): Iterator { - return new CallbackIterator( - $this->listCollections->execute($server), - function (array $collectionInfo) { - return $collectionInfo['name']; - } + return new CachingIterator( + new CallbackIterator( + $this->listCollections->execute($server), + fn (array $collectionInfo): string => (string) $collectionInfo['name'], + ), ); } } diff --git a/src/Operation/ListCollections.php b/src/Operation/ListCollections.php index e5702509b..a86b0a1e5 100644 --- a/src/Operation/ListCollections.php +++ b/src/Operation/ListCollections.php @@ -17,27 +17,24 @@ namespace MongoDB\Operation; +use Iterator; use MongoDB\Command\ListCollections as ListCollectionsCommand; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\CollectionInfoCommandIterator; -use MongoDB\Model\CollectionInfoIterator; +use MongoDB\Model\CachingIterator; +use MongoDB\Model\CallbackIterator; +use MongoDB\Model\CollectionInfo; /** * Operation for the listCollections command. * - * @api * @see \MongoDB\Database::listCollections() * @see https://mongodb.com/docs/manual/reference/command/listCollections/ */ -class ListCollections implements Executable +final class ListCollections { - /** @var string */ - private $databaseName; - - /** @var ListCollectionsCommand */ - private $listCollections; + private ListCollectionsCommand $listCollections; /** * Constructs a listCollections command. @@ -66,19 +63,25 @@ class ListCollections implements Executable */ public function __construct(string $databaseName, array $options = []) { - $this->databaseName = $databaseName; $this->listCollections = new ListCollectionsCommand($databaseName, ['nameOnly' => false] + $options); } /** * Execute the operation. * - * @see Executable::execute() - * @return CollectionInfoIterator + * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): Iterator { - return new CollectionInfoCommandIterator($this->listCollections->execute($server), $this->databaseName); + /** @var Iterator $collections */ + $collections = $this->listCollections->execute($server); + + return new CachingIterator( + new CallbackIterator( + $collections, + fn (array $collectionInfo, int $key): CollectionInfo => new CollectionInfo($collectionInfo), + ), + ); } } diff --git a/src/Operation/ListDatabaseNames.php b/src/Operation/ListDatabaseNames.php index ccace38f8..74263fb53 100644 --- a/src/Operation/ListDatabaseNames.php +++ b/src/Operation/ListDatabaseNames.php @@ -30,14 +30,12 @@ /** * Operation for the ListDatabases command, returning only database names. * - * @api * @see \MongoDB\Client::listDatabaseNames() * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases */ -class ListDatabaseNames implements Executable +final class ListDatabaseNames { - /** @var ListDatabasesCommand */ - private $listDatabases; + private ListDatabasesCommand $listDatabases; /** * Constructs a listDatabases command. @@ -71,7 +69,7 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ diff --git a/src/Operation/ListDatabases.php b/src/Operation/ListDatabases.php index a19cf07bb..ab32ef7e6 100644 --- a/src/Operation/ListDatabases.php +++ b/src/Operation/ListDatabases.php @@ -17,25 +17,25 @@ namespace MongoDB\Operation; +use ArrayIterator; +use Iterator; use MongoDB\Command\ListDatabases as ListDatabasesCommand; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnexpectedValueException; -use MongoDB\Model\DatabaseInfoIterator; -use MongoDB\Model\DatabaseInfoLegacyIterator; +use MongoDB\Model\CallbackIterator; +use MongoDB\Model\DatabaseInfo; /** * Operation for the ListDatabases command. * - * @api * @see \MongoDB\Client::listDatabases() * @see https://mongodb.com/docs/manual/reference/command/listDatabases/#mongodb-dbcommand-dbcmd.listDatabases` */ -class ListDatabases implements Executable +final class ListDatabases { - /** @var ListDatabasesCommand */ - private $listDatabases; + private ListDatabasesCommand $listDatabases; /** * Constructs a listDatabases command. @@ -69,13 +69,18 @@ public function __construct(array $options = []) /** * Execute the operation. * - * @see Executable::execute() - * @return DatabaseInfoIterator + * @return Iterator * @throws UnexpectedValueException if the command response was malformed * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): Iterator { - return new DatabaseInfoLegacyIterator($this->listDatabases->execute($server)); + /** @var list $databases */ + $databases = $this->listDatabases->execute($server); + + return new CallbackIterator( + new ArrayIterator($databases), + fn (array $databaseInfo): DatabaseInfo => new DatabaseInfo($databaseInfo), + ); } } diff --git a/src/Operation/ListIndexes.php b/src/Operation/ListIndexes.php index e762d9ca2..8fab516fc 100644 --- a/src/Operation/ListIndexes.php +++ b/src/Operation/ListIndexes.php @@ -18,41 +18,30 @@ namespace MongoDB\Operation; use EmptyIterator; +use Iterator; use MongoDB\Driver\Command; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\CachingIterator; -use MongoDB\Model\IndexInfoIterator; -use MongoDB\Model\IndexInfoIteratorIterator; +use MongoDB\Model\CallbackIterator; +use MongoDB\Model\IndexInfo; use function is_integer; /** * Operation for the listIndexes command. * - * @api * @see \MongoDB\Collection::listIndexes() * @see https://mongodb.com/docs/manual/reference/command/listIndexes/ */ -class ListIndexes implements Executable +final class ListIndexes { - /** @var integer */ - private static $errorCodeDatabaseNotFound = 60; - - /** @var integer */ - private static $errorCodeNamespaceNotFound = 26; - - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $options; + private const ERROR_CODE_DATABASE_NOT_FOUND = 60; + private const ERROR_CODE_NAMESPACE_NOT_FOUND = 26; /** * Constructs a listIndexes command. @@ -73,59 +62,24 @@ class ListIndexes implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array $options = []) { - if (isset($options['maxTimeMS']) && ! is_integer($options['maxTimeMS'])) { - throw InvalidArgumentException::invalidType('"maxTimeMS" option', $options['maxTimeMS'], 'integer'); + if (isset($this->options['maxTimeMS']) && ! is_integer($this->options['maxTimeMS'])) { + throw InvalidArgumentException::invalidType('"maxTimeMS" option', $this->options['maxTimeMS'], 'integer'); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return IndexInfoIterator + * @return Iterator * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) - { - return $this->executeCommand($server); - } - - /** - * Create options for executing the command. - * - * Note: read preference is intentionally omitted, as the spec requires that - * the command be executed on the primary. - * - * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php - */ - private function createOptions(): array - { - $options = []; - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - return $options; - } - - /** - * Returns information for all indexes for this collection using the - * listIndexes command. - * - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - private function executeCommand(Server $server): IndexInfoIteratorIterator + public function execute(Server $server): Iterator { $cmd = ['listIndexes' => $this->collectionName]; @@ -136,21 +90,45 @@ private function executeCommand(Server $server): IndexInfoIteratorIterator } try { + /** @var CursorInterface $cursor */ $cursor = $server->executeReadCommand($this->databaseName, new Command($cmd), $this->createOptions()); + $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); } catch (CommandException $e) { /* The server may return an error if the collection does not exist. * Check for possible error codes (see: SERVER-20463) and return an * empty iterator instead of throwing. */ - if ($e->getCode() === self::$errorCodeNamespaceNotFound || $e->getCode() === self::$errorCodeDatabaseNotFound) { - return new IndexInfoIteratorIterator(new EmptyIterator()); + if ($e->getCode() === self::ERROR_CODE_NAMESPACE_NOT_FOUND || $e->getCode() === self::ERROR_CODE_DATABASE_NOT_FOUND) { + return new EmptyIterator(); } throw $e; } - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); + return new CachingIterator( + new CallbackIterator( + $cursor, + fn (array $indexInfo): IndexInfo => new IndexInfo($indexInfo), + ), + ); + } + + /** + * Create options for executing the command. + * + * Note: read preference is intentionally omitted, as the spec requires that + * the command be executed on the primary. + * + * @see https://php.net/manual/en/mongodb-driver-server.executecommand.php + */ + private function createOptions(): array + { + $options = []; + + if (isset($this->options['session'])) { + $options['session'] = $this->options['session']; + } - return new IndexInfoIteratorIterator(new CachingIterator($cursor), $this->databaseName . '.' . $this->collectionName); + return $options; } } diff --git a/src/Operation/ListSearchIndexes.php b/src/Operation/ListSearchIndexes.php new file mode 100644 index 000000000..ae0eb6d6f --- /dev/null +++ b/src/Operation/ListSearchIndexes.php @@ -0,0 +1,90 @@ +listSearchIndexesOptions = array_intersect_key($options, ['name' => 1]); + $this->aggregateOptions = array_intersect_key($options, ['batchSize' => 1, 'codec' => 1, 'collation' => 1, 'comment' => 1, 'maxTimeMS' => 1, 'readConcern' => 1, 'readPreference' => 1, 'session' => 1, 'typeMap' => 1]); + + $this->aggregate = $this->createAggregate(); + } + + /** + * Execute the operation. + * + * @return Iterator&Countable + * @throws UnexpectedValueException if the command response was malformed + * @throws UnsupportedException if collation or read concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server): Iterator + { + $cursor = $this->aggregate->execute($server); + + return new CachingIterator($cursor); + } + + private function createAggregate(): Aggregate + { + $pipeline = [ + ['$listSearchIndexes' => (object) $this->listSearchIndexesOptions], + ]; + + return new Aggregate($this->databaseName, $this->collectionName, $pipeline, $this->aggregateOptions); + } +} diff --git a/src/Operation/MapReduce.php b/src/Operation/MapReduce.php deleted file mode 100644 index 7b97dd0a9..000000000 --- a/src/Operation/MapReduce.php +++ /dev/null @@ -1,418 +0,0 @@ -isDefault()) { - unset($options['readConcern']); - } - - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); - } - - // Handle deprecation of CodeWScope - if ($map->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$map" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if ($reduce->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "$reduce" argument for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - if (isset($options['finalize']) && $options['finalize']->getScope() !== null) { - @trigger_error('Use of Javascript with scope in "finalize" option for MapReduce is deprecated. Put all scope variables in the "scope" option of the MapReduce operation.', E_USER_DEPRECATED); - } - - $this->checkOutDeprecations($out); - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->map = $map; - $this->reduce = $reduce; - $this->out = $out; - $this->options = $options; - } - - /** - * Execute the operation. - * - * @see Executable::execute() - * @return MapReduceResult - * @throws UnexpectedValueException if the command response was malformed - * @throws UnsupportedException if read concern or write concern is used and unsupported - * @throws DriverRuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); - if ($inTransaction) { - if (isset($this->options['readConcern'])) { - throw UnsupportedException::readConcernNotSupportedInTransaction(); - } - - if (isset($this->options['writeConcern'])) { - throw UnsupportedException::writeConcernNotSupportedInTransaction(); - } - } - - $hasOutputCollection = ! is_mapreduce_output_inline($this->out); - - $command = $this->createCommand(); - $options = $this->createOptions($hasOutputCollection); - - /* If the mapReduce operation results in a write, use - * executeReadWriteCommand to ensure we're handling the writeConcern - * option. - * In other cases, we use executeCommand as this will prevent the - * mapReduce operation from being retried when retryReads is enabled. - * See https://github.com/mongodb/specifications/blob/master/source/retryable-reads/retryable-reads.rst#unsupported-read-operations. */ - $cursor = $hasOutputCollection - ? $server->executeReadWriteCommand($this->databaseName, $command, $options) - : $server->executeCommand($this->databaseName, $command, $options); - - if (isset($this->options['typeMap']) && ! $hasOutputCollection) { - $cursor->setTypeMap(create_field_path_type_map($this->options['typeMap'], 'results.$')); - } - - $result = current($cursor->toArray()); - assert($result instanceof stdClass); - - $getIterator = $this->createGetIteratorCallable($result, $server); - - return new MapReduceResult($getIterator, $result); - } - - /** - * @param string|array|object $out - */ - private function checkOutDeprecations($out): void - { - if (is_string($out)) { - return; - } - - $out = (array) $out; - - if (isset($out['nonAtomic']) && ! $out['nonAtomic']) { - @trigger_error('Specifying false for "out.nonAtomic" is deprecated.', E_USER_DEPRECATED); - } - - if (isset($out['sharded']) && ! $out['sharded']) { - @trigger_error('Specifying false for "out.sharded" is deprecated.', E_USER_DEPRECATED); - } - } - - /** - * Create the mapReduce command. - */ - private function createCommand(): Command - { - $cmd = [ - 'mapReduce' => $this->collectionName, - 'map' => $this->map, - 'reduce' => $this->reduce, - 'out' => $this->out, - ]; - - foreach (['bypassDocumentValidation', 'comment', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'verbose'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = $this->options[$option]; - } - } - - foreach (['collation', 'query', 'scope', 'sort'] as $option) { - if (isset($this->options[$option])) { - $cmd[$option] = (object) $this->options[$option]; - } - } - - return new Command($cmd); - } - - /** - * Creates a callable for MapReduceResult::getIterator(). - * - * @throws UnexpectedValueException if the command response was malformed - */ - private function createGetIteratorCallable(stdClass $result, Server $server): callable - { - // Inline results can be wrapped with an ArrayIterator - if (isset($result->results) && is_array($result->results)) { - $results = $result->results; - - return function () use ($results) { - return new ArrayIterator($results); - }; - } - - if (isset($result->result) && (is_string($result->result) || is_object($result->result))) { - $options = isset($this->options['typeMap']) ? ['typeMap' => $this->options['typeMap']] : []; - - $find = is_string($result->result) - ? new Find($this->databaseName, $result->result, [], $options) - : new Find($result->result->db, $result->result->collection, [], $options); - - return function () use ($find, $server) { - return $find->execute($server); - }; - } - - throw new UnexpectedValueException('mapReduce command did not return inline results or an output collection'); - } - - /** - * Create options for executing the command. - * - * @see https://php.net/manual/en/mongodb-driver-server.executereadcommand.php - * @see https://php.net/manual/en/mongodb-driver-server.executereadwritecommand.php - */ - private function createOptions(bool $hasOutputCollection): array - { - $options = []; - - if (isset($this->options['readConcern'])) { - $options['readConcern'] = $this->options['readConcern']; - } - - if (! $hasOutputCollection && isset($this->options['readPreference'])) { - $options['readPreference'] = $this->options['readPreference']; - } - - if (isset($this->options['session'])) { - $options['session'] = $this->options['session']; - } - - if ($hasOutputCollection && isset($this->options['writeConcern'])) { - $options['writeConcern'] = $this->options['writeConcern']; - } - - return $options; - } -} diff --git a/src/Operation/ModifyCollection.php b/src/Operation/ModifyCollection.php index 7933859bf..7b40f2f8c 100644 --- a/src/Operation/ModifyCollection.php +++ b/src/Operation/ModifyCollection.php @@ -30,24 +30,11 @@ /** * Operation for the collMod command. * - * @api * @see \MongoDB\Database::modifyCollection() * @see https://mongodb.com/docs/manual/reference/command/collMod/ */ -class ModifyCollection implements Executable +final class ModifyCollection { - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array */ - private $collectionOptions; - - /** @var array */ - private $options; - /** * Constructs a collMod command. * @@ -70,42 +57,36 @@ class ModifyCollection implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, array $collectionOptions, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array $collectionOptions, private array $options = []) { if (empty($collectionOptions)) { throw new InvalidArgumentException('$collectionOptions is empty'); } - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['typeMap']) && ! is_array($this->options['typeMap'])) { + throw InvalidArgumentException::invalidType('"typeMap" option', $this->options['typeMap'], 'array'); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->collectionOptions = $collectionOptions; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() * @return array|object Command result document * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): array|object { $cursor = $server->executeWriteCommand($this->databaseName, $this->createCommand(), $this->createOptions()); diff --git a/src/Operation/RenameCollection.php b/src/Operation/RenameCollection.php index 64758b0a7..b3848c67e 100644 --- a/src/Operation/RenameCollection.php +++ b/src/Operation/RenameCollection.php @@ -25,28 +25,20 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use function current; -use function is_array; use function is_bool; /** * Operation for the renameCollection command. * - * @api * @see \MongoDB\Collection::rename() * @see \MongoDB\Database::renameCollection() * @see https://mongodb.com/docs/manual/reference/command/renameCollection/ */ -class RenameCollection implements Executable +final class RenameCollection { - /** @var string */ - private $fromNamespace; + private string $fromNamespace; - /** @var string */ - private $toNamespace; - - /** @var array */ - private $options; + private string $toNamespace; /** * Constructs a renameCollection command. @@ -59,9 +51,6 @@ class RenameCollection implements Executable * * * session (MongoDB\Driver\Session): Client session. * - * * typeMap (array): Type map for BSON deserialization. This will be used - * for the returned command result document. - * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * * dropTarget (boolean): If true, MongoDB will drop the target before @@ -74,55 +63,42 @@ class RenameCollection implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $fromDatabaseName, string $fromCollectionName, string $toDatabaseName, string $toCollectionName, array $options = []) + public function __construct(string $fromDatabaseName, string $fromCollectionName, string $toDatabaseName, string $toCollectionName, private array $options = []) { - if (isset($options['session']) && ! $options['session'] instanceof Session) { - throw InvalidArgumentException::invalidType('"session" option', $options['session'], Session::class); - } - - if (isset($options['typeMap']) && ! is_array($options['typeMap'])) { - throw InvalidArgumentException::invalidType('"typeMap" option', $options['typeMap'], 'array'); + if (isset($this->options['session']) && ! $this->options['session'] instanceof Session) { + throw InvalidArgumentException::invalidType('"session" option', $this->options['session'], Session::class); } - if (isset($options['writeConcern']) && ! $options['writeConcern'] instanceof WriteConcern) { - throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); + if (isset($this->options['writeConcern']) && ! $this->options['writeConcern'] instanceof WriteConcern) { + throw InvalidArgumentException::invalidType('"writeConcern" option', $this->options['writeConcern'], WriteConcern::class); } - if (isset($options['writeConcern']) && $options['writeConcern']->isDefault()) { - unset($options['writeConcern']); + if (isset($this->options['writeConcern']) && $this->options['writeConcern']->isDefault()) { + unset($this->options['writeConcern']); } - if (isset($options['dropTarget']) && ! is_bool($options['dropTarget'])) { - throw InvalidArgumentException::invalidType('"dropTarget" option', $options['dropTarget'], 'boolean'); + if (isset($this->options['dropTarget']) && ! is_bool($this->options['dropTarget'])) { + throw InvalidArgumentException::invalidType('"dropTarget" option', $this->options['dropTarget'], 'boolean'); } $this->fromNamespace = $fromDatabaseName . '.' . $fromCollectionName; $this->toNamespace = $toDatabaseName . '.' . $toCollectionName; - $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return array|object Command result document * @throws UnsupportedException if write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): void { $inTransaction = isset($this->options['session']) && $this->options['session']->isInTransaction(); if ($inTransaction && isset($this->options['writeConcern'])) { throw UnsupportedException::writeConcernNotSupportedInTransaction(); } - $cursor = $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions()); - - if (isset($this->options['typeMap'])) { - $cursor->setTypeMap($this->options['typeMap']); - } - - return current($cursor->toArray()); + $server->executeWriteCommand('admin', $this->createCommand(), $this->createOptions()); } /** diff --git a/src/Operation/ReplaceOne.php b/src/Operation/ReplaceOne.php index d5d117b91..70bfd8f77 100644 --- a/src/Operation/ReplaceOne.php +++ b/src/Operation/ReplaceOne.php @@ -17,28 +17,26 @@ namespace MongoDB\Operation; +use MongoDB\Codec\DocumentCodec; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; use MongoDB\UpdateResult; -use function is_array; -use function is_object; +use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; /** * Operation for replacing a single document with the update command. * - * @api * @see \MongoDB\Collection::replaceOne() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -class ReplaceOne implements Executable +final class ReplaceOne { - /** @var Update */ - private $update; + private Update $update; /** * Constructs an update command. @@ -48,6 +46,9 @@ class ReplaceOne implements Executable * * bypassDocumentValidation (boolean): If true, allows the write to * circumvent document level validation. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to encode PHP objects + * into BSON. + * * * collation (document): Collation specification. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -71,6 +72,12 @@ class ReplaceOne implements Executable * Parameters can then be accessed as variables in an aggregate * expression context (e.g. "$$var"). * + * * sort (document): Determines which document the operation modifies if + * the query selects multiple documents. + * + * This is not supported for server versions < 8.0 and will result in an + * exception at execution time if used. + * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -80,39 +87,59 @@ class ReplaceOne implements Executable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $replacement, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array|object $replacement, array $options = []) { - if (! is_array($replacement) && ! is_object($replacement)) { - throw InvalidArgumentException::invalidType('$replacement', $replacement, 'array or object'); - } - - if (is_first_key_operator($replacement)) { - throw new InvalidArgumentException('First key in $replacement argument is an update operator'); + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); } - if (is_pipeline($replacement)) { - throw new InvalidArgumentException('$replacement argument is a pipeline'); + if (isset($options['codec'], $options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); } $this->update = new Update( $databaseName, $collectionName, $filter, - $replacement, - ['multi' => false] + $options + $this->validateReplacement($replacement, $options['codec'] ?? null), + ['multi' => false] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } + + private function validateReplacement(array|object $replacement, ?DocumentCodec $codec): array|object + { + if ($codec) { + $replacement = $codec->encode($replacement); + } + + if (! is_document($replacement)) { + throw InvalidArgumentException::expectedDocumentType('$replacement', $replacement); + } + + // Treat empty arrays as replacement documents for BC + if ($replacement === []) { + $replacement = (object) $replacement; + } + + if (is_first_key_operator($replacement)) { + throw new InvalidArgumentException('First key in $replacement is an update operator'); + } + + if (is_pipeline($replacement, true /* allowEmpty */)) { + throw new InvalidArgumentException('$replacement is an update pipeline'); + } + + return $replacement; + } } diff --git a/src/Operation/Update.php b/src/Operation/Update.php index 96a51c53f..3c8118b81 100644 --- a/src/Operation/Update.php +++ b/src/Operation/Update.php @@ -28,8 +28,8 @@ use function is_array; use function is_bool; -use function is_object; use function is_string; +use function MongoDB\is_document; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; use function MongoDB\is_write_concern_acknowledged; @@ -44,25 +44,11 @@ * @internal * @see https://mongodb.com/docs/manual/reference/command/update/ */ -class Update implements Executable, Explainable +final class Update implements Explainable { - /** @var integer */ - private static $wireVersionForHint = 8; + private const WIRE_VERSION_FOR_HINT = 8; - /** @var string */ - private $databaseName; - - /** @var string */ - private $collectionName; - - /** @var array|object */ - private $filter; - - /** @var array|object */ - private $update; - - /** @var array */ - private $options; + private array $options; /** * Constructs a update command. @@ -112,14 +98,10 @@ class Update implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = []) + public function __construct(private string $databaseName, private string $collectionName, private array|object $filter, private array|object $update, array $options = []) { - if (! is_array($filter) && ! is_object($filter)) { - throw InvalidArgumentException::invalidType('$filter', $filter, 'array or object'); - } - - if (! is_array($update) && ! is_object($update)) { - throw InvalidArgumentException::invalidType('$update', $filter, 'array or object'); + if (! is_document($filter)) { + throw InvalidArgumentException::expectedDocumentType('$filter', $filter); } $options += [ @@ -135,12 +117,12 @@ public function __construct(string $databaseName, string $collectionName, $filte throw InvalidArgumentException::invalidType('"bypassDocumentValidation" option', $options['bypassDocumentValidation'], 'boolean'); } - if (isset($options['collation']) && ! is_array($options['collation']) && ! is_object($options['collation'])) { - throw InvalidArgumentException::invalidType('"collation" option', $options['collation'], 'array or object'); + if (isset($options['collation']) && ! is_document($options['collation'])) { + throw InvalidArgumentException::expectedDocumentType('"collation" option', $options['collation']); } - if (isset($options['hint']) && ! is_string($options['hint']) && ! is_array($options['hint']) && ! is_object($options['hint'])) { - throw InvalidArgumentException::invalidType('"hint" option', $options['hint'], ['string', 'array', 'object']); + if (isset($options['hint']) && ! is_string($options['hint']) && ! is_document($options['hint'])) { + throw InvalidArgumentException::expectedDocumentOrStringType('"hint" option', $options['hint']); } if (! is_bool($options['multi'])) { @@ -148,7 +130,7 @@ public function __construct(string $databaseName, string $collectionName, $filte } if ($options['multi'] && ! is_first_key_operator($update) && ! is_pipeline($update)) { - throw new InvalidArgumentException('"multi" option cannot be true if $update is a replacement document'); + throw new InvalidArgumentException('"multi" option cannot be true unless $update has update operator(s) or non-empty pipeline'); } if (isset($options['session']) && ! $options['session'] instanceof Session) { @@ -163,8 +145,16 @@ public function __construct(string $databaseName, string $collectionName, $filte throw InvalidArgumentException::invalidType('"writeConcern" option', $options['writeConcern'], WriteConcern::class); } - if (isset($options['let']) && ! is_array($options['let']) && ! is_object($options['let'])) { - throw InvalidArgumentException::invalidType('"let" option', $options['let'], 'array or object'); + if (isset($options['let']) && ! is_document($options['let'])) { + throw InvalidArgumentException::expectedDocumentType('"let" option', $options['let']); + } + + if (isset($options['sort']) && ! is_document($options['sort'])) { + throw InvalidArgumentException::expectedDocumentType('"sort" option', $options['sort']); + } + + if (isset($options['sort']) && $options['multi']) { + throw new InvalidArgumentException('"sort" option cannot be used with multi-document updates'); } if (isset($options['bypassDocumentValidation']) && ! $options['bypassDocumentValidation']) { @@ -175,28 +165,22 @@ public function __construct(string $databaseName, string $collectionName, $filte unset($options['writeConcern']); } - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->filter = $filter; - $this->update = $update; $this->options = $options; } /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if hint or write concern is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { /* CRUD spec requires a client-side error when using "hint" with an * unacknowledged write concern on an unsupported server. */ if ( isset($this->options['writeConcern']) && ! is_write_concern_acknowledged($this->options['writeConcern']) && - isset($this->options['hint']) && ! server_supports_feature($server, self::$wireVersionForHint) + isset($this->options['hint']) && ! server_supports_feature($server, self::WIRE_VERSION_FOR_HINT) ) { throw UnsupportedException::hintNotSupported(); } @@ -218,9 +202,8 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { $cmd = ['update' => $this->collectionName, 'updates' => [['q' => $this->filter, 'u' => $this->update] + $this->createUpdateOptions()]]; @@ -228,10 +211,6 @@ public function getCommandDocument(Server $server) $cmd['bypassDocumentValidation'] = $this->options['bypassDocumentValidation']; } - if (isset($this->options['writeConcern'])) { - $cmd['writeConcern'] = $this->options['writeConcern']; - } - return $cmd; } @@ -296,8 +275,10 @@ private function createUpdateOptions(): array } } - if (isset($this->options['collation'])) { - $updateOptions['collation'] = (object) $this->options['collation']; + foreach (['collation', 'sort'] as $option) { + if (isset($this->options[$option])) { + $updateOptions[$option] = (object) $this->options[$option]; + } } return $updateOptions; diff --git a/src/Operation/UpdateMany.php b/src/Operation/UpdateMany.php index e442365a1..97865288c 100644 --- a/src/Operation/UpdateMany.php +++ b/src/Operation/UpdateMany.php @@ -23,22 +23,18 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\UpdateResult; -use function is_array; -use function is_object; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; /** * Operation for updating multiple documents with the update command. * - * @api * @see \MongoDB\Collection::updateMany() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -class UpdateMany implements Executable, Explainable +final class UpdateMany implements Explainable { - /** @var Update */ - private $update; + private Update $update; /** * Constructs an update command. @@ -83,14 +79,10 @@ class UpdateMany implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array|object $update, array $options = []) { - if (! is_array($update) && ! is_object($update)) { - throw InvalidArgumentException::invalidType('$update', $update, 'array or object'); - } - if (! is_first_key_operator($update) && ! is_pipeline($update)) { - throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline'); + throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update'); } $this->update = new Update( @@ -98,19 +90,17 @@ public function __construct(string $databaseName, string $collectionName, $filte $collectionName, $filter, $update, - ['multi' => true] + $options + ['multi' => true] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } @@ -119,10 +109,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->update->getCommandDocument($server); + return $this->update->getCommandDocument(); } } diff --git a/src/Operation/UpdateOne.php b/src/Operation/UpdateOne.php index a7bfab518..7ead97312 100644 --- a/src/Operation/UpdateOne.php +++ b/src/Operation/UpdateOne.php @@ -23,22 +23,18 @@ use MongoDB\Exception\UnsupportedException; use MongoDB\UpdateResult; -use function is_array; -use function is_object; use function MongoDB\is_first_key_operator; use function MongoDB\is_pipeline; /** * Operation for updating a single document with the update command. * - * @api * @see \MongoDB\Collection::updateOne() * @see https://mongodb.com/docs/manual/reference/command/update/ */ -class UpdateOne implements Executable, Explainable +final class UpdateOne implements Explainable { - /** @var Update */ - private $update; + private Update $update; /** * Constructs an update command. @@ -74,6 +70,12 @@ class UpdateOne implements Executable, Explainable * Parameters can then be accessed as variables in an aggregate * expression context (e.g. "$$var"). * + * * sort (document): Determines which document the operation modifies if + * the query selects multiple documents. + * + * This is not supported for server versions < 8.0 and will result in an + * exception at execution time if used. + * * * writeConcern (MongoDB\Driver\WriteConcern): Write concern. * * @param string $databaseName Database name @@ -83,14 +85,10 @@ class UpdateOne implements Executable, Explainable * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(string $databaseName, string $collectionName, $filter, $update, array $options = []) + public function __construct(string $databaseName, string $collectionName, array|object $filter, array|object $update, array $options = []) { - if (! is_array($update) && ! is_object($update)) { - throw InvalidArgumentException::invalidType('$update', $update, 'array or object'); - } - if (! is_first_key_operator($update) && ! is_pipeline($update)) { - throw new InvalidArgumentException('Expected an update document with operator as first key or a pipeline'); + throw new InvalidArgumentException('Expected update operator(s) or non-empty pipeline for $update'); } $this->update = new Update( @@ -98,19 +96,17 @@ public function __construct(string $databaseName, string $collectionName, $filte $collectionName, $filter, $update, - ['multi' => false] + $options + ['multi' => false] + $options, ); } /** * Execute the operation. * - * @see Executable::execute() - * @return UpdateResult * @throws UnsupportedException if collation is used and unsupported * @throws DriverRuntimeException for other driver errors (e.g. connection errors) */ - public function execute(Server $server) + public function execute(Server $server): UpdateResult { return $this->update->execute($server); } @@ -119,10 +115,9 @@ public function execute(Server $server) * Returns the command document for this operation. * * @see Explainable::getCommandDocument() - * @return array */ - public function getCommandDocument(Server $server) + public function getCommandDocument(): array { - return $this->update->getCommandDocument($server); + return $this->update->getCommandDocument(); } } diff --git a/src/Operation/UpdateSearchIndex.php b/src/Operation/UpdateSearchIndex.php new file mode 100644 index 000000000..4543914bb --- /dev/null +++ b/src/Operation/UpdateSearchIndex.php @@ -0,0 +1,81 @@ +definition = (object) $definition; + } + + /** + * Execute the operation. + * + * @throws UnsupportedException if write concern is used and unsupported + * @throws DriverRuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server): void + { + $cmd = [ + 'updateSearchIndex' => $this->collectionName, + 'name' => $this->name, + 'definition' => $this->definition, + ]; + + if (isset($this->options['comment'])) { + $cmd['comment'] = $this->options['comment']; + } + + $server->executeCommand($this->databaseName, new Command($cmd)); + } +} diff --git a/src/Operation/Watch.php b/src/Operation/Watch.php index 9306a5312..a46d5de57 100644 --- a/src/Operation/Watch.php +++ b/src/Operation/Watch.php @@ -19,7 +19,8 @@ use MongoDB\BSON\TimestampInterface; use MongoDB\ChangeStream; -use MongoDB\Driver\Cursor; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Driver\CursorInterface; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\Monitoring\CommandFailedEvent; @@ -36,7 +37,6 @@ use function array_intersect_key; use function array_key_exists; use function array_unshift; -use function assert; use function count; use function is_array; use function is_bool; @@ -44,8 +44,8 @@ use function is_string; use function MongoDB\Driver\Monitoring\addSubscriber; use function MongoDB\Driver\Monitoring\removeSubscriber; +use function MongoDB\is_document; use function MongoDB\select_server; -use function MongoDB\server_supports_feature; /** * Operation for creating a change stream with the aggregate command. @@ -53,13 +53,11 @@ * Note: the implementation of CommandSubscriber is an internal implementation * detail and should not be considered part of the public API. * - * @api * @see \MongoDB\Collection::watch() * @see https://mongodb.com/docs/manual/changeStreams/ */ -class Watch implements Executable, /* @internal */ CommandSubscriber +final class Watch implements /* @internal */ CommandSubscriber { - public const FULL_DOCUMENT_DEFAULT = 'default'; public const FULL_DOCUMENT_UPDATE_LOOKUP = 'updateLookup'; public const FULL_DOCUMENT_WHEN_AVAILABLE = 'whenAvailable'; public const FULL_DOCUMENT_REQUIRED = 'required'; @@ -68,41 +66,23 @@ class Watch implements Executable, /* @internal */ CommandSubscriber public const FULL_DOCUMENT_BEFORE_CHANGE_WHEN_AVAILABLE = 'whenAvailable'; public const FULL_DOCUMENT_BEFORE_CHANGE_REQUIRED = 'required'; - /** @var integer */ - private static $wireVersionForStartAtOperationTime = 7; + private Aggregate $aggregate; - /** @var Aggregate */ - private $aggregate; + private array $aggregateOptions; - /** @var array */ - private $aggregateOptions; + private array $changeStreamOptions; - /** @var array */ - private $changeStreamOptions; + private string $databaseName; - /** @var string|null */ - private $collectionName; + private int $firstBatchSize = 0; - /** @var string */ - private $databaseName; + private bool $hasResumed = false; - /** @var integer */ - private $firstBatchSize; + private ?TimestampInterface $operationTime = null; - /** @var boolean */ - private $hasResumed = false; + private ?object $postBatchResumeToken = null; - /** @var Manager */ - private $manager; - - /** @var TimestampInterface */ - private $operationTime; - - /** @var array */ - private $pipeline; - - /** @var object|null */ - private $postBatchResumeToken; + private ?DocumentCodec $codec; /** * Constructs an aggregate command for creating a change stream. @@ -111,6 +91,9 @@ class Watch implements Executable, /* @internal */ CommandSubscriber * * * batchSize (integer): The number of documents to return per batch. * + * * codec (MongoDB\Codec\DocumentCodec): Codec used to decode documents + * from BSON to PHP objects. + * * * collation (document): Specifies a collation. * * * comment (mixed): BSON value to attach as a comment to this command. @@ -198,20 +181,28 @@ class Watch implements Executable, /* @internal */ CommandSubscriber * @param Manager $manager Manager instance from the driver * @param string|null $databaseName Database name * @param string|null $collectionName Collection name - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline * @param array $options Command options * @throws InvalidArgumentException for parameter/option parsing errors */ - public function __construct(Manager $manager, ?string $databaseName, ?string $collectionName, array $pipeline, array $options = []) + public function __construct(private Manager $manager, ?string $databaseName, private ?string $collectionName, private array $pipeline, array $options = []) { if (isset($collectionName) && ! isset($databaseName)) { throw new InvalidArgumentException('$collectionName should also be null if $databaseName is null'); } $options += [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; + if (isset($options['codec']) && ! $options['codec'] instanceof DocumentCodec) { + throw InvalidArgumentException::invalidType('"codec" option', $options['codec'], DocumentCodec::class); + } + + if (isset($options['codec']) && isset($options['typeMap'])) { + throw InvalidArgumentException::cannotCombineCodecAndTypeMap(); + } + if (array_key_exists('fullDocument', $options) && ! is_string($options['fullDocument'])) { throw InvalidArgumentException::invalidType('"fullDocument" option', $options['fullDocument'], 'string'); } @@ -224,12 +215,12 @@ public function __construct(Manager $manager, ?string $databaseName, ?string $co throw InvalidArgumentException::invalidType('"readPreference" option', $options['readPreference'], ReadPreference::class); } - if (isset($options['resumeAfter']) && ! is_array($options['resumeAfter']) && ! is_object($options['resumeAfter'])) { - throw InvalidArgumentException::invalidType('"resumeAfter" option', $options['resumeAfter'], 'array or object'); + if (isset($options['resumeAfter']) && ! is_document($options['resumeAfter'])) { + throw InvalidArgumentException::expectedDocumentType('"resumeAfter" option', $options['resumeAfter']); } - if (isset($options['startAfter']) && ! is_array($options['startAfter']) && ! is_object($options['startAfter'])) { - throw InvalidArgumentException::invalidType('"startAfter" option', $options['startAfter'], 'array or object'); + if (isset($options['startAfter']) && ! is_document($options['startAfter'])) { + throw InvalidArgumentException::expectedDocumentType('"startAfter" option', $options['startAfter']); } if (isset($options['startAtOperationTime']) && ! $options['startAtOperationTime'] instanceof TimestampInterface) { @@ -248,7 +239,7 @@ public function __construct(Manager $manager, ?string $databaseName, ?string $co if (! isset($options['session'])) { try { $options['session'] = $manager->startSession(['causalConsistency' => false]); - } catch (RuntimeException $e) { + } catch (RuntimeException) { /* We can ignore the exception, as libmongoc likely cannot * create its own session and there is no risk of a mismatch. */ } @@ -263,14 +254,27 @@ public function __construct(Manager $manager, ?string $databaseName, ?string $co $this->changeStreamOptions['allChangesForCluster'] = true; } - $this->manager = $manager; $this->databaseName = $databaseName; - $this->collectionName = $collectionName; - $this->pipeline = $pipeline; + $this->codec = $options['codec'] ?? null; $this->aggregate = $this->createAggregate(); } + /** + * Execute the operation. + * + * @throws UnsupportedException if collation or read concern is used and unsupported + * @throws RuntimeException for other driver errors (e.g. connection errors) + */ + public function execute(Server $server): ChangeStream + { + return new ChangeStream( + $this->createChangeStreamIterator($server), + fn ($resumeToken, $hasAdvanced): ChangeStreamIterator => $this->resume($resumeToken, $hasAdvanced), + $this->codec, + ); + } + /** @internal */ final public function commandFailed(CommandFailedEvent $event): void { @@ -307,31 +311,13 @@ final public function commandSucceeded(CommandSucceededEvent $event): void } if ( - $this->shouldCaptureOperationTime($event->getServer()) && + $this->shouldCaptureOperationTime() && isset($reply->operationTime) && $reply->operationTime instanceof TimestampInterface ) { $this->operationTime = $reply->operationTime; } } - /** - * Execute the operation. - * - * @see Executable::execute() - * @return ChangeStream - * @throws UnsupportedException if collation or read concern is used and unsupported - * @throws RuntimeException for other driver errors (e.g. connection errors) - */ - public function execute(Server $server) - { - return new ChangeStream( - $this->createChangeStreamIterator($server), - function ($resumeToken, $hasAdvanced): ChangeStreamIterator { - return $this->resume($resumeToken, $hasAdvanced); - } - ); - } - /** * Create the aggregate command for a change stream. * @@ -354,7 +340,7 @@ private function createChangeStreamIterator(Server $server): ChangeStreamIterato $this->executeAggregate($server), $this->firstBatchSize, $this->getInitialResumeToken(), - $this->postBatchResumeToken + $this->postBatchResumeToken, ); } @@ -364,15 +350,12 @@ private function createChangeStreamIterator(Server $server): ChangeStreamIterato * The command will be executed using APM so that we can capture data from * its response (e.g. firstBatch size, postBatchResumeToken). */ - private function executeAggregate(Server $server): Cursor + private function executeAggregate(Server $server): CursorInterface { addSubscriber($this); try { - $cursor = $this->aggregate->execute($server); - assert($cursor instanceof Cursor); - - return $cursor; + return $this->aggregate->execute($server); } finally { removeSubscriber($this); } @@ -381,10 +364,9 @@ private function executeAggregate(Server $server): Cursor /** * Return the initial resume token for creating the ChangeStreamIterator. * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#updating-the-cached-resume-token - * @return array|object|null + * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.md#updating-the-cached-resume-token */ - private function getInitialResumeToken() + private function getInitialResumeToken(): array|object|null { if ($this->firstBatchSize === 0 && isset($this->postBatchResumeToken)) { return $this->postBatchResumeToken; @@ -404,16 +386,11 @@ private function getInitialResumeToken() /** * Resumes a change stream. * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#resume-process - * @param array|object|null $resumeToken + * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.md#resume-process * @throws InvalidArgumentException */ - private function resume($resumeToken = null, bool $hasAdvanced = false): ChangeStreamIterator + private function resume(array|object|null $resumeToken = null, bool $hasAdvanced = false): ChangeStreamIterator { - if (isset($resumeToken) && ! is_array($resumeToken) && ! is_object($resumeToken)) { - throw InvalidArgumentException::invalidType('$resumeToken', $resumeToken, 'array or object'); - } - $this->hasResumed = true; /* Select a new server using the original read preference. While watch @@ -445,9 +422,9 @@ private function resume($resumeToken = null, bool $hasAdvanced = false): ChangeS /** * Determine whether to capture operation time from an aggregate response. * - * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.rst#startatoperationtime + * @see https://github.com/mongodb/specifications/blob/master/source/change-streams/change-streams.md#startatoperationtime */ - private function shouldCaptureOperationTime(Server $server): bool + private function shouldCaptureOperationTime(): bool { if ($this->hasResumed) { return false; @@ -469,10 +446,6 @@ private function shouldCaptureOperationTime(Server $server): bool return false; } - if (! server_supports_feature($server, self::$wireVersionForStartAtOperationTime)) { - return false; - } - return true; } } diff --git a/src/Operation/WithTransaction.php b/src/Operation/WithTransaction.php index e31b01b9e..760825946 100644 --- a/src/Operation/WithTransaction.php +++ b/src/Operation/WithTransaction.php @@ -10,27 +10,21 @@ use function call_user_func; use function time; -/** - * @internal - */ -class WithTransaction +/** @internal */ +final class WithTransaction { /** @var callable */ private $callback; - /** @var array */ - private $transactionOptions; - /** * @see Session::startTransaction for supported transaction options * * @param callable $callback A callback that will be invoked within the transaction * @param array $transactionOptions Additional options that are passed to Session::startTransaction */ - public function __construct(callable $callback, array $transactionOptions = []) + public function __construct(callable $callback, private array $transactionOptions = []) { $this->callback = $callback; - $this->transactionOptions = $transactionOptions; } /** diff --git a/src/PsrLogAdapter.php b/src/PsrLogAdapter.php new file mode 100644 index 000000000..d042963d6 --- /dev/null +++ b/src/PsrLogAdapter.php @@ -0,0 +1,161 @@ + */ + private SplObjectStorage $loggers; + + private const SPEC_TO_PSR = [ + self::EMERGENCY => LogLevel::EMERGENCY, + self::ALERT => LogLevel::ALERT, + self::CRITICAL => LogLevel::CRITICAL, + self::ERROR => LogLevel::ERROR, + self::WARN => LogLevel::WARNING, + self::NOTICE => LogLevel::NOTICE, + self::INFO => LogLevel::INFO, + self::DEBUG => LogLevel::DEBUG, + // PSR does not define a "trace" level, so map it to "debug" + self::TRACE => LogLevel::DEBUG, + ]; + + private const MONGOC_TO_PSR = [ + LogSubscriber::LEVEL_ERROR => LogLevel::ERROR, + /* libmongoc considers "critical" less severe than "error" so map it to + * "error" in the PSR logger. */ + LogSubscriber::LEVEL_CRITICAL => LogLevel::ERROR, + LogSubscriber::LEVEL_WARNING => LogLevel::WARNING, + LogSubscriber::LEVEL_MESSAGE => LogLevel::NOTICE, + LogSubscriber::LEVEL_INFO => LogLevel::INFO, + LogSubscriber::LEVEL_DEBUG => LogLevel::DEBUG, + ]; + + public static function addLogger(LoggerInterface $logger): void + { + $instance = self::getInstance(); + + $instance->loggers->attach($logger); + + addSubscriber($instance); + } + + /** + * Forwards a log message from libmongoc/PHPC to all registered PSR loggers. + * + * @see LogSubscriber::log() + */ + public function log(int $level, string $domain, string $message): void + { + if (! isset(self::MONGOC_TO_PSR[$level])) { + throw new UnexpectedValueException(sprintf( + 'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s', + LogSubscriber::LEVEL_ERROR, + LogSubscriber::LEVEL_DEBUG, + $level, + $domain, + $message, + )); + } + + $instance = self::getInstance(); + $psrLevel = self::MONGOC_TO_PSR[$level]; + $context = ['domain' => $domain]; + + foreach ($instance->loggers as $logger) { + $logger->log($psrLevel, $message, $context); + } + } + + public static function removeLogger(LoggerInterface $logger): void + { + $instance = self::getInstance(); + $instance->loggers->detach($logger); + + if ($instance->loggers->count() === 0) { + removeSubscriber($instance); + } + } + + /** + * Writes a log message to all registered PSR loggers. + * + * This function is intended for internal use within the library. + */ + public static function writeLog(int $specLevel, string $domain, string $message): void + { + if (! isset(self::SPEC_TO_PSR[$specLevel])) { + throw new UnexpectedValueException(sprintf( + 'Expected level to be >= %d and <= %d, %d given for domain "%s" and message: %s', + self::EMERGENCY, + self::TRACE, + $specLevel, + $domain, + $message, + )); + } + + $instance = self::getInstance(); + $psrLevel = self::SPEC_TO_PSR[$specLevel]; + $context = ['domain' => $domain]; + + foreach ($instance->loggers as $logger) { + $logger->log($psrLevel, $message, $context); + } + } + + private function __construct() + { + $this->loggers = new SplObjectStorage(); + } + + private static function getInstance(): self + { + return self::$instance ??= new self(); + } +} diff --git a/src/UpdateResult.php b/src/UpdateResult.php index 1fbbab600..a9d50b751 100644 --- a/src/UpdateResult.php +++ b/src/UpdateResult.php @@ -17,24 +17,16 @@ namespace MongoDB; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteResult; -use MongoDB\Exception\BadMethodCallException; /** * Result class for an update operation. */ class UpdateResult { - /** @var WriteResult */ - private $writeResult; - - /** @var boolean */ - private $isAcknowledged; - - public function __construct(WriteResult $writeResult) + public function __construct(private WriteResult $writeResult) { - $this->writeResult = $writeResult; - $this->isAcknowledged = $writeResult->isAcknowledged(); } /** @@ -43,16 +35,11 @@ public function __construct(WriteResult $writeResult) * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getMatchedCount() + public function getMatchedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getMatchedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getMatchedCount(); } /** @@ -64,16 +51,11 @@ public function getMatchedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getModifiedCount() + public function getModifiedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getModifiedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getModifiedCount(); } /** @@ -82,16 +64,11 @@ public function getModifiedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return integer|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedCount() + public function getUpsertedCount(): int { - if ($this->isAcknowledged) { - return $this->writeResult->getUpsertedCount(); - } - - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return $this->writeResult->getUpsertedCount(); } /** @@ -106,20 +83,15 @@ public function getUpsertedCount() * This method should only be called if the write was acknowledged. * * @see UpdateResult::isAcknowledged() - * @return mixed|null - * @throws BadMethodCallException is the write result is unacknowledged + * @throws LogicException if the write result is unacknowledged */ - public function getUpsertedId() + public function getUpsertedId(): mixed { - if ($this->isAcknowledged) { - foreach ($this->writeResult->getUpsertedIds() as $id) { - return $id; - } - - return null; + foreach ($this->writeResult->getUpsertedIds() as $id) { + return $id; } - throw BadMethodCallException::unacknowledgedWriteResultAccess(__METHOD__); + return null; } /** @@ -128,11 +100,9 @@ public function getUpsertedId() * If the update was not acknowledged, other fields from the WriteResult * (e.g. matchedCount) will be undefined and their getter methods should not * be invoked. - * - * @return boolean */ - public function isAcknowledged() + public function isAcknowledged(): bool { - return $this->isAcknowledged; + return $this->writeResult->isAcknowledged(); } } diff --git a/src/functions.php b/src/functions.php index dd2d94021..2e68987c6 100644 --- a/src/functions.php +++ b/src/functions.php @@ -18,7 +18,10 @@ namespace MongoDB; use Exception; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; use MongoDB\BSON\Serializable; +use MongoDB\Builder\Type\StageInterface; use MongoDB\Driver\Exception\RuntimeException as DriverRuntimeException; use MongoDB\Driver\Manager; use MongoDB\Driver\ReadPreference; @@ -29,22 +32,60 @@ use MongoDB\Exception\RuntimeException; use MongoDB\Operation\ListCollections; use MongoDB\Operation\WithTransaction; +use Psr\Log\LoggerInterface; use ReflectionClass; use ReflectionException; +use stdClass; +use function array_is_list; +use function array_key_first; use function assert; use function end; use function get_object_vars; -use function in_array; use function is_array; use function is_object; use function is_string; -use function key; -use function MongoDB\BSON\fromPHP; -use function MongoDB\BSON\toPHP; -use function reset; +use function str_ends_with; use function substr; +/** + * Registers a PSR-3 logger to receive log messages from the driver/library. + * + * Calling this method again with a logger that has already been added will have + * no effect. + */ +function add_logger(LoggerInterface $logger): void +{ + PsrLogAdapter::addLogger($logger); +} + +/** + * Unregisters a PSR-3 logger. + * + * Calling this method with a logger that has not been added will have no + * effect. + */ +function remove_logger(LoggerInterface $logger): void +{ + PsrLogAdapter::removeLogger($logger); +} + +/** + * Create a new stdClass instance with the provided properties. + * Use named arguments to specify the property names. + * object( property1: value1, property2: value2 ) + * + * If property names contain a dot or a dollar characters, use array unpacking syntax. + * object( ...[ 'author.name' => 1, 'array.$' => 1 ] ) + * + * @psalm-suppress MoreSpecificReturnType + * @psalm-suppress LessSpecificReturnStatement + */ +function object(mixed ...$values): stdClass +{ + return (object) $values; +} + /** * Check whether all servers support executing a write stage on a secondary. * @@ -81,47 +122,55 @@ function all_servers_support_write_stage_on_secondary(array $servers): bool * @internal * @param array|object $document Document to which the type map will be applied * @param array $typeMap Type map for BSON deserialization. - * @return array|object * @throws InvalidArgumentException */ -function apply_type_map_to_document($document, array $typeMap) +function apply_type_map_to_document(array|object $document, array $typeMap): array|object { - if (! is_array($document) && ! is_object($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); + if (! is_document($document)) { + throw InvalidArgumentException::expectedDocumentType('$document', $document); } - return toPHP(fromPHP($document), $typeMap); + return Document::fromPHP($document)->toPHP($typeMap); } /** - * Generate an index name from a key specification. + * Converts a document parameter to an array. + * + * This is used to facilitate unified access to document fields. It also handles + * Document, PackedArray, and Serializable objects. + * + * This function is not used for type checking. Therefore, it does not reject + * PackedArray objects or Serializable::bsonSerialize() return values that would + * encode as BSON arrays. * * @internal - * @param array|object $document Document containing fields mapped to values, - * which denote order or an index type - * @throws InvalidArgumentException + * @throws InvalidArgumentException if $document is not an array or object */ -function generate_index_name($document): string +function document_to_array(array|object $document): array { - if ($document instanceof Serializable) { + if ($document instanceof Document || $document instanceof PackedArray) { + /* Nested documents and arrays are intentionally left as BSON. We avoid + * iterator_to_array() since Document and PackedArray iteration returns + * all values as MongoDB\BSON\Value instances. */ + + /** @psalm-var array */ + return $document->toPHP([ + 'array' => 'bson', + 'document' => 'bson', + 'root' => 'array', + ]); + } elseif ($document instanceof Serializable) { $document = $document->bsonSerialize(); } if (is_object($document)) { + /* Note: this omits all uninitialized properties, whereas BSON encoding + * includes untyped, uninitialized properties. This is acceptable given + * document_to_array()'s use cases. */ $document = get_object_vars($document); } - if (! is_array($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } - - $name = ''; - - foreach ($document as $field => $type) { - $name .= ($name != '' ? '_' : '') . $field . '_' . $type; - } - - return $name; + return $document; } /** @@ -129,13 +178,12 @@ function generate_index_name($document): string * autoEncryption driver option (if available). * * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper - * @see Collection::drop - * @see Database::createCollection - * @see Database::dropCollection - * @return array|object|null + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#collection-encryptedfields-lookup-getencryptedfields + * @see Collection::drop() + * @see Database::createCollection() + * @see Database::dropCollection() */ -function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager) +function get_encrypted_fields_from_driver(string $databaseName, string $collectionName, Manager $manager): array|object|null { $encryptedFieldsMap = (array) $manager->getEncryptedFieldsMap(); @@ -146,18 +194,12 @@ function get_encrypted_fields_from_driver(string $databaseName, string $collecti * Return a collection's encryptedFields option from the server (if any). * * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.rst#drop-collection-helper - * @see Collection::drop - * @see Database::dropCollection - * @return array|object|null + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/client-side-encryption.md#collection-encryptedfields-lookup-getencryptedfields + * @see Collection::drop() + * @see Database::dropCollection() */ -function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Manager $manager, Server $server) +function get_encrypted_fields_from_server(string $databaseName, string $collectionName, Server $server): array|object|null { - // No-op if the encryptedFieldsMap autoEncryption driver option was omitted - if ($manager->getEncryptedFieldsMap() === null) { - return null; - } - $collectionInfoIterator = (new ListCollections($databaseName, ['filter' => ['name' => $collectionName]]))->execute($server); foreach ($collectionInfoIterator as $collectionInfo) { @@ -171,73 +213,132 @@ function get_encrypted_fields_from_server(string $databaseName, string $collecti return null; } +/** + * Returns whether a given value is a valid document. + * + * This method returns true for any array or object, but specifically excludes + * BSON PackedArray instances + * + * @internal + */ +function is_document(mixed $document): bool +{ + return is_array($document) || (is_object($document) && ! $document instanceof PackedArray); +} + /** * Return whether the first key in the document starts with a "$" character. * - * This is used for differentiating update and replacement documents. + * This is used for validating aggregation pipeline stages and differentiating + * update and replacement documents. Since true and false return values may be + * expected in different contexts, this function intentionally throws if + * $document has an unexpected type instead of returning false. * * @internal - * @param array|object $document Update or replacement document - * @throws InvalidArgumentException + * @throws InvalidArgumentException if $document is not an array or object */ -function is_first_key_operator($document): bool +function is_first_key_operator(array|object $document): bool { - if ($document instanceof Serializable) { - $document = $document->bsonSerialize(); + if ($document instanceof PackedArray) { + return false; } - if (is_object($document)) { - $document = get_object_vars($document); - } + $document = document_to_array($document); - if (! is_array($document)) { - throw InvalidArgumentException::invalidType('$document', $document, 'array or object'); - } + $firstKey = array_key_first($document); - reset($document); - $firstKey = (string) key($document); + if (! is_string($firstKey)) { + return false; + } - return isset($firstKey[0]) && $firstKey[0] === '$'; + return '$' === ($firstKey[0] ?? null); } /** - * Returns whether an update specification is a valid aggregation pipeline. + * Returns whether the argument is a valid aggregation or update pipeline. + * + * This is primarily used for validating arguments for update and replace + * operations, but can also be used for validating an aggregation pipeline. + * + * The $allowEmpty parameter can be used to control whether an empty array + * should be considered a valid pipeline. Empty arrays are generally valid for + * an aggregation pipeline, but the things are more complicated for update + * pipelines. + * + * Update operations must prohibit empty pipelines, since libmongoc may encode + * an empty pipeline array as an empty replacement document when writing an + * update command (arrays and documents have the same bson_t representation). + * For consistency, findOneAndUpdate should also prohibit empty pipelines. + * Replace operations (e.g. replaceOne, findOneAndReplace) should reject empty + * and non-empty pipelines alike, since neither is a replacement document. + * + * Note: this method may propagate an InvalidArgumentException from + * document_or_array() if a Serializable object within the pipeline array + * returns a non-array, non-object value from its bsonSerialize() method. * * @internal - * @param mixed $pipeline + * @throws InvalidArgumentException */ -function is_pipeline($pipeline): bool +function is_pipeline(array|object $pipeline, bool $allowEmpty = false): bool { + if ($pipeline instanceof PackedArray) { + /* Nested documents and arrays are intentionally left as BSON. We avoid + * iterator_to_array() since PackedArray iteration returns all values as + * MongoDB\BSON\Value instances. */ + /** @psalm-var array */ + $pipeline = $pipeline->toPHP([ + 'array' => 'bson', + 'document' => 'bson', + 'root' => 'array', + ]); + } elseif ($pipeline instanceof Serializable) { + $pipeline = $pipeline->bsonSerialize(); + } + if (! is_array($pipeline)) { return false; } if ($pipeline === []) { - return false; + return $allowEmpty; } - $expectedKey = 0; + if (! array_is_list($pipeline)) { + return false; + } - foreach ($pipeline as $key => $stage) { - if (! is_array($stage) && ! is_object($stage)) { + foreach ($pipeline as $stage) { + if (! is_document($stage)) { return false; } - if ($expectedKey !== $key) { + if (! is_first_key_operator($stage)) { return false; } + } - $expectedKey++; - $stage = (array) $stage; - reset($stage); - $key = key($stage); + return true; +} - if (! is_string($key) || substr($key, 0, 1) !== '$') { - return false; +/** + * Returns whether the argument is a list that contains at least one + * {@see StageInterface} object. + * + * @internal + */ +function is_builder_pipeline(array $pipeline): bool +{ + if (! $pipeline || ! array_is_list($pipeline)) { + return false; + } + + foreach ($pipeline as $stage) { + if (is_object($stage) && $stage instanceof StageInterface) { + return true; } } - return true; + return false; } /** @@ -262,7 +363,7 @@ function is_in_transaction(array $options): bool * executed against a primary server. * * @internal - * @param array $pipeline List of pipeline operations + * @param array $pipeline Aggregation pipeline */ function is_last_pipeline_operator_write(array $pipeline): bool { @@ -272,42 +373,13 @@ function is_last_pipeline_operator_write(array $pipeline): bool return false; } - $lastOp = (array) $lastOp; - - return in_array(key($lastOp), ['$out', '$merge'], true); -} - -/** - * Return whether the "out" option for a mapReduce operation is "inline". - * - * This is used to determine if a mapReduce command requires a primary. - * - * @internal - * @see https://mongodb.com/docs/manual/reference/command/mapReduce/#output-inline - * @param string|array|object $out Output specification - * @throws InvalidArgumentException - */ -function is_mapreduce_output_inline($out): bool -{ - if (! is_array($out) && ! is_object($out)) { + if (! is_array($lastOp) && ! is_object($lastOp)) { return false; } - if ($out instanceof Serializable) { - $out = $out->bsonSerialize(); - } - - if (is_object($out)) { - $out = get_object_vars($out); - } - - if (! is_array($out)) { - throw InvalidArgumentException::invalidType('$out', $out, 'array or object'); - } - - reset($out); + $key = array_key_first(document_to_array($lastOp)); - return key($out) === 'inline'; + return $key === '$merge' || $key === '$out'; } /** @@ -337,8 +409,8 @@ function is_write_concern_acknowledged(WriteConcern $writeConcern): bool function server_supports_feature(Server $server, int $feature): bool { $info = $server->getInfo(); - $maxWireVersion = isset($info['maxWireVersion']) ? (integer) $info['maxWireVersion'] : 0; - $minWireVersion = isset($info['minWireVersion']) ? (integer) $info['minWireVersion'] : 0; + $maxWireVersion = isset($info['maxWireVersion']) ? (int) $info['maxWireVersion'] : 0; + $minWireVersion = isset($info['minWireVersion']) ? (int) $info['minWireVersion'] : 0; return $minWireVersion <= $feature && $maxWireVersion >= $feature; } @@ -347,9 +419,8 @@ function server_supports_feature(Server $server, int $feature): bool * Return whether the input is an array of strings. * * @internal - * @param mixed $input */ -function is_string_array($input): bool +function is_string_array(mixed $input): bool { if (! is_array($input)) { return false; @@ -372,10 +443,9 @@ function is_string_array($input): bool * @internal * @see https://bugs.php.net/bug.php?id=49664 * @param mixed $element Value to be copied - * @return mixed * @throws ReflectionException */ -function recursive_copy($element) +function recursive_copy(mixed $element): mixed { if (is_array($element)) { foreach ($element as $key => $value) { @@ -429,7 +499,7 @@ function create_field_path_type_map(array $typeMap, string $fieldPath): array /* Special case if we want to convert an array, in which case we need to * ensure that the field containing the array is exposed as an array, * instead of the type given in the type map's array key. */ - if (substr($fieldPath, -2, 2) === '.$') { + if (str_ends_with($fieldPath, '.$')) { $typeMap['fieldPaths'][substr($fieldPath, 0, -2)] = 'array'; } @@ -454,8 +524,8 @@ function create_field_path_type_map(array $typeMap, string $fieldPath): array * from the initial call have elapsed. After that, no retries will happen and * the helper will throw the last exception received from the driver. * - * @see Client::startSession - * @see Session::startTransaction for supported transaction options + * @see Client::startSession() + * @see Session::startTransaction() for supported transaction options * * @param Session $session A session object as retrieved by Client::startSession * @param callable $callback A callback that will be invoked within the transaction @@ -476,11 +546,11 @@ function with_transaction(Session $session, callable $callback, array $transacti */ function extract_session_from_options(array $options): ?Session { - if (! isset($options['session']) || ! $options['session'] instanceof Session) { - return null; + if (isset($options['session']) && $options['session'] instanceof Session) { + return $options['session']; } - return $options['session']; + return null; } /** @@ -490,16 +560,19 @@ function extract_session_from_options(array $options): ?Session */ function extract_read_preference_from_options(array $options): ?ReadPreference { - if (! isset($options['readPreference']) || ! $options['readPreference'] instanceof ReadPreference) { - return null; + if (isset($options['readPreference']) && $options['readPreference'] instanceof ReadPreference) { + return $options['readPreference']; } - return $options['readPreference']; + return null; } /** - * Performs server selection, respecting the readPreference and session options - * (if given) + * Performs server selection, respecting the readPreference and session options. + * + * The pinned server for an active transaction takes priority, followed by an + * operation-level read preference, followed by an active transaction's read + * preference, followed by a primary read preference. * * @internal */ @@ -507,16 +580,23 @@ function select_server(Manager $manager, array $options): Server { $session = extract_session_from_options($options); $server = $session instanceof Session ? $session->getServer() : null; + + // Pinned server for an active transaction takes priority if ($server !== null) { return $server; } + // Operation read preference takes priority $readPreference = extract_read_preference_from_options($options); - if (! $readPreference instanceof ReadPreference) { - // TODO: PHPLIB-476: Read transaction read preference once PHPC-1439 is implemented - $readPreference = new ReadPreference(ReadPreference::RP_PRIMARY); + + // Read preference for an active transaction takes priority + if ($readPreference === null && $session instanceof Session && $session->isInTransaction()) { + /* Session::getTransactionOptions() should always return an array if the + * session is in a transaction, but we can be defensive. */ + $readPreference = extract_read_preference_from_options($session->getTransactionOptions() ?? []); } + // Manager::selectServer() defaults to a primary read preference return $manager->selectServer($readPreference); } @@ -526,15 +606,19 @@ function select_server(Manager $manager, array $options): Server * must be forced due to the existence of pre-5.0 servers in the topology. * * @internal - * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.rst#aggregation-pipelines-with-write-stages + * @see https://github.com/mongodb/specifications/blob/master/source/crud/crud.md#aggregation-pipelines-with-write-stages */ function select_server_for_aggregate_write_stage(Manager $manager, array &$options): Server { $readPreference = extract_read_preference_from_options($options); /* If there is either no read preference or a primary read preference, there - * is no special server selection logic to apply. */ - if ($readPreference === null || $readPreference->getMode() === ReadPreference::RP_PRIMARY) { + * is no special server selection logic to apply. + * + * Note: an alternative read preference could still be inherited from an + * active transaction's options, but we can rely on libmongoc to raise a + * "read preference in a transaction must be primary" error if necessary. */ + if ($readPreference === null || $readPreference->getModeString() === ReadPreference::PRIMARY) { return select_server($manager, $options); } @@ -550,7 +634,7 @@ function select_server_for_aggregate_write_stage(Manager $manager, array &$optio * preference and repeat server selection if it previously failed or * selected a secondary. */ if (! all_servers_support_write_stage_on_secondary($manager->getServers())) { - $options['readPreference'] = new ReadPreference(ReadPreference::RP_PRIMARY); + $options['readPreference'] = new ReadPreference(ReadPreference::PRIMARY); if ($server === null || $server->isSecondary()) { return select_server($manager, $options); @@ -567,3 +651,18 @@ function select_server_for_aggregate_write_stage(Manager $manager, array &$optio return $server; } + +/** + * Performs server selection for a write operation. + * + * The pinned server for an active transaction takes priority, followed by an + * operation-level read preference, followed by a primary read preference. This + * is similar to select_server() except that it ignores a read preference from + * an active transaction's options. + * + * @internal + */ +function select_server_for_write(Manager $manager, array $options): Server +{ + return select_server($manager, $options + ['readPreference' => new ReadPreference(ReadPreference::PRIMARY)]); +} diff --git a/stubs/Driver/BulkWriteCommand.stub.php b/stubs/Driver/BulkWriteCommand.stub.php new file mode 100644 index 000000000..464b3f1ae --- /dev/null +++ b/stubs/Driver/BulkWriteCommand.stub.php @@ -0,0 +1,40 @@ + + */ +final class Cursor implements CursorInterface +{ + /** + * @return TValue|null + * @psalm-ignore-nullable-return + */ + public function current(): array|object|null + { + } + + public function next(): void + { + } + + /** @psalm-ignore-nullable-return */ + public function key(): ?int + { + } + + public function valid(): bool + { + } + + public function rewind(): void + { + } + + /** @return array */ + public function toArray(): array + { + } + + public function getId(): Int64 + { + } + + public function getServer(): Server + { + } + + public function isDead(): bool + { + } + + public function setTypeMap(array $typemap): void + { + } +} diff --git a/stubs/Driver/CursorInterface.stub.php b/stubs/Driver/CursorInterface.stub.php new file mode 100644 index 000000000..d2a89737a --- /dev/null +++ b/stubs/Driver/CursorInterface.stub.php @@ -0,0 +1,33 @@ + + */ +interface CursorInterface extends Iterator +{ + /** + * @return TValue|null + * @psalm-ignore-nullable-return + */ + public function current(): array|object|null; + + public function getId(): Int64; + + public function getServer(): Server; + + public function isDead(): bool; + + /** @psalm-ignore-nullable-return */ + public function key(): ?int; + + public function setTypeMap(array $typemap): void; + + /** @return array */ + public function toArray(): array; +} diff --git a/stubs/Driver/WriteResult.stub.php b/stubs/Driver/WriteResult.stub.php new file mode 100644 index 000000000..daa2f4eb1 --- /dev/null +++ b/stubs/Driver/WriteResult.stub.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::AccumulatorUseAccumulatorToImplementTheAvgOperator, $pipeline); + } + + public function testUseInitArgsToVaryTheInitialStateByGroup(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: object(city: Expression::fieldPath('city')), + restaurants: Accumulator::accumulator( + init: <<<'JS' + function(city, userProfileCity) { + return { max: city === userProfileCity ? 3 : 1, restaurants: [] } + } + JS, + accumulate: <<<'JS' + function(state, restaurantName) { + if (state.restaurants.length < state.max) { + state.restaurants.push(restaurantName); + } + return state; + } + JS, + accumulateArgs: [Expression::fieldPath('name')], + merge: <<<'JS' + function(state1, state2) { + return { + max: state1.max, + restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max) + } + } + JS, + lang: 'js', + initArgs: [ + Expression::fieldPath('city'), + 'Bettles', + ], + finalize: <<<'JS' + function(state) { + return state.restaurants + } + JS, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AccumulatorUseInitArgsToVaryTheInitialStateByGroup, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/AddToSetAccumulatorTest.php b/tests/Builder/Accumulator/AddToSetAccumulatorTest.php new file mode 100644 index 000000000..bd3b20912 --- /dev/null +++ b/tests/Builder/Accumulator/AddToSetAccumulatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::AddToSetUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + cakeTypesForState: Accumulator::outputWindow( + Accumulator::addToSet(Expression::fieldPath('type')), + documents: [ + 'unbounded', + 'current', + ], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AddToSetUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/AvgAccumulatorTest.php b/tests/Builder/Accumulator/AvgAccumulatorTest.php new file mode 100644 index 000000000..78e37a905 --- /dev/null +++ b/tests/Builder/Accumulator/AvgAccumulatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::AvgUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + averageQuantityForState: Accumulator::outputWindow( + Accumulator::avg( + Expression::intFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AvgUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/BottomAccumulatorTest.php b/tests/Builder/Accumulator/BottomAccumulatorTest.php new file mode 100644 index 000000000..674c60ddb --- /dev/null +++ b/tests/Builder/Accumulator/BottomAccumulatorTest.php @@ -0,0 +1,63 @@ +assertSamePipeline(Pipelines::BottomFindTheBottomScore, $pipeline); + } + + public function testFindingTheBottomScoreAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::bottom( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BottomFindingTheBottomScoreAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/BottomNAccumulatorTest.php b/tests/Builder/Accumulator/BottomNAccumulatorTest.php new file mode 100644 index 000000000..8ea89f669 --- /dev/null +++ b/tests/Builder/Accumulator/BottomNAccumulatorTest.php @@ -0,0 +1,92 @@ +assertSamePipeline(Pipelines::BottomNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheThreeLowestScores(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::bottomN( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BottomNFindTheThreeLowestScores, $pipeline); + } + + public function testFindingTheThreeLowestScoreDocumentsAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::bottomN( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BottomNFindingTheThreeLowestScoreDocumentsAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/ConcatArraysAccumulatorTest.php b/tests/Builder/Accumulator/ConcatArraysAccumulatorTest.php new file mode 100644 index 000000000..24d811d30 --- /dev/null +++ b/tests/Builder/Accumulator/ConcatArraysAccumulatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ConcatArraysWarehouseCollection, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/CountAccumulatorTest.php b/tests/Builder/Accumulator/CountAccumulatorTest.php new file mode 100644 index 000000000..6c33d7293 --- /dev/null +++ b/tests/Builder/Accumulator/CountAccumulatorTest.php @@ -0,0 +1,52 @@ +assertSamePipeline(Pipelines::CountUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + countNumberOfDocumentsForState: Accumulator::outputWindow( + Accumulator::count(), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::CountUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php b/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php new file mode 100644 index 000000000..516ecca95 --- /dev/null +++ b/tests/Builder/Accumulator/CovariancePopAccumulatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::CovariancePopExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php b/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php new file mode 100644 index 000000000..d2f176006 --- /dev/null +++ b/tests/Builder/Accumulator/CovarianceSampAccumulatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::CovarianceSampExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DenseRankAccumulatorTest.php b/tests/Builder/Accumulator/DenseRankAccumulatorTest.php new file mode 100644 index 000000000..7ba7b8a54 --- /dev/null +++ b/tests/Builder/Accumulator/DenseRankAccumulatorTest.php @@ -0,0 +1,57 @@ +assertSamePipeline(Pipelines::DenseRankDenseRankPartitionsByADateField, $pipeline); + } + + public function testDenseRankPartitionsByAnIntegerField(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: Sort::Desc, + ), + output: object( + // The outputWindow is optional when no window property is set. + denseRankQuantityForState: Accumulator::denseRank(), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DenseRankDenseRankPartitionsByAnIntegerField, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DerivativeAccumulatorTest.php b/tests/Builder/Accumulator/DerivativeAccumulatorTest.php new file mode 100644 index 000000000..7941c4b6a --- /dev/null +++ b/tests/Builder/Accumulator/DerivativeAccumulatorTest.php @@ -0,0 +1,49 @@ +assertSamePipeline(Pipelines::DerivativeExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php b/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php new file mode 100644 index 000000000..fbc13c4cb --- /dev/null +++ b/tests/Builder/Accumulator/DocumentNumberAccumulatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::DocumentNumberDocumentNumberForEachState, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php b/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php new file mode 100644 index 000000000..59a6156d4 --- /dev/null +++ b/tests/Builder/Accumulator/ExpMovingAvgAccumulatorTest.php @@ -0,0 +1,60 @@ +assertSamePipeline(Pipelines::ExpMovingAvgExponentialMovingAverageUsingAlpha, $pipeline); + } + + public function testExponentialMovingAverageUsingN(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('stock'), + sortBy: object( + date: Sort::Asc, + ), + output: object( + expMovingAvgForStock: Accumulator::expMovingAvg( + input: Expression::numberFieldPath('price'), + N: 2, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ExpMovingAvgExponentialMovingAverageUsingN, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/FirstAccumulatorTest.php b/tests/Builder/Accumulator/FirstAccumulatorTest.php new file mode 100644 index 000000000..e78f9c53d --- /dev/null +++ b/tests/Builder/Accumulator/FirstAccumulatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::FirstUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + firstOrderTypeForState: Accumulator::outputWindow( + Accumulator::first(Expression::stringFieldPath('type')), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/FirstNAccumulatorTest.php b/tests/Builder/Accumulator/FirstNAccumulatorTest.php new file mode 100644 index 000000000..fe4f36384 --- /dev/null +++ b/tests/Builder/Accumulator/FirstNAccumulatorTest.php @@ -0,0 +1,126 @@ +assertSamePipeline(Pipelines::FirstNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheFirstThreePlayerScoresForASingleGame(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + firstThreeScores: Accumulator::firstN( + input: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstNFindTheFirstThreePlayerScoresForASingleGame, $pipeline); + } + + public function testFindingTheFirstThreePlayerScoresAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::firstN( + input: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstNFindingTheFirstThreePlayerScoresAcrossMultipleGames, $pipeline); + } + + public function testNullAndMissingValues(): void + { + $pipeline = new Pipeline( + Stage::documents([ + object(playerId: 'PlayerA', gameId: 'G1', score: 1), + object(playerId: 'PlayerB', gameId: 'G1', score: 2), + object(playerId: 'PlayerC', gameId: 'G1', score: 3), + object(playerId: 'PlayerD', gameId: 'G1'), + object(playerId: 'PlayerE', gameId: 'G1', score: null), + ]), + Stage::group( + _id: Expression::stringFieldPath('gameId'), + firstFiveScores: Accumulator::firstN( + input: Expression::fieldPath('score'), + n: 5, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstNNullAndMissingValues, $pipeline); + } + + public function testUsingSortWithFirstN(): void + { + $pipeline = new Pipeline( + Stage::sort( + score: Sort::Desc, + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::firstN( + input: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstNUsingSortWithFirstN, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/IntegralAccumulatorTest.php b/tests/Builder/Accumulator/IntegralAccumulatorTest.php new file mode 100644 index 000000000..636456518 --- /dev/null +++ b/tests/Builder/Accumulator/IntegralAccumulatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::IntegralExample, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LastAccumulatorTest.php b/tests/Builder/Accumulator/LastAccumulatorTest.php new file mode 100644 index 000000000..e0439b662 --- /dev/null +++ b/tests/Builder/Accumulator/LastAccumulatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::LastUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + lastOrderTypeForState: Accumulator::outputWindow( + Accumulator::last(Expression::stringFieldPath('type')), + documents: ['current', 'unbounded'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LastUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LastNAccumulatorTest.php b/tests/Builder/Accumulator/LastNAccumulatorTest.php new file mode 100644 index 000000000..caf957fde --- /dev/null +++ b/tests/Builder/Accumulator/LastNAccumulatorTest.php @@ -0,0 +1,100 @@ + Expression::arrayFieldPath('gameId')], + gamescores: Accumulator::lastN( + input: Expression::arrayFieldPath('score'), + n: Expression::cond( + if: Expression::eq( + Expression::arrayFieldPath('gameId'), + 'G2', + ), + then: 1, + else: 3, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LastNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheLastThreePlayerScoresForASingleGame(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + lastThreeScores: Accumulator::lastN( + input: [ + Expression::arrayFieldPath('playerId'), + Expression::arrayFieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LastNFindTheLastThreePlayerScoresForASingleGame, $pipeline); + } + + public function testFindingTheLastThreePlayerScoresAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::lastN( + input: [ + Expression::arrayFieldPath('playerId'), + Expression::arrayFieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LastNFindingTheLastThreePlayerScoresAcrossMultipleGames, $pipeline); + } + + public function testUsingSortWithLastN(): void + { + $pipeline = new Pipeline( + Stage::sort( + score: Sort::Desc, + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::lastN( + input: [ + Expression::arrayFieldPath('playerId'), + Expression::arrayFieldPath('score'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LastNUsingSortWithLastN, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LinearFillAccumulatorTest.php b/tests/Builder/Accumulator/LinearFillAccumulatorTest.php new file mode 100644 index 000000000..0142d0d68 --- /dev/null +++ b/tests/Builder/Accumulator/LinearFillAccumulatorTest.php @@ -0,0 +1,59 @@ +assertSamePipeline(Pipelines::LinearFillFillMissingValuesWithLinearInterpolation, $pipeline); + } + + public function testUseMultipleFillMethodsInASingleStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + sortBy: object( + time: Sort::Asc, + ), + output: object( + linearFillPrice: Accumulator::linearFill( + Expression::numberFieldPath('price'), + ), + locfPrice: Accumulator::locf( + Expression::numberFieldPath('price'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::LinearFillUseMultipleFillMethodsInASingleStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/LocfAccumulatorTest.php b/tests/Builder/Accumulator/LocfAccumulatorTest.php new file mode 100644 index 000000000..259a92aff --- /dev/null +++ b/tests/Builder/Accumulator/LocfAccumulatorTest.php @@ -0,0 +1,38 @@ +assertSamePipeline(Pipelines::LocfFillMissingValuesWithTheLastObservedValue, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MaxAccumulatorTest.php b/tests/Builder/Accumulator/MaxAccumulatorTest.php new file mode 100644 index 000000000..6a75d85c9 --- /dev/null +++ b/tests/Builder/Accumulator/MaxAccumulatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::MaxUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + maximumQuantityForState: Accumulator::outputWindow( + Accumulator::max( + Expression::intFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MaxUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MaxNAccumulatorTest.php b/tests/Builder/Accumulator/MaxNAccumulatorTest.php new file mode 100644 index 000000000..19617834c --- /dev/null +++ b/tests/Builder/Accumulator/MaxNAccumulatorTest.php @@ -0,0 +1,85 @@ +assertSamePipeline(Pipelines::MaxNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheMaximumThreeScoresForASingleGame(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + maxThreeScores: Accumulator::maxN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MaxNFindTheMaximumThreeScoresForASingleGame, $pipeline); + } + + public function testFindingTheMaximumThreeScoresAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + maxScores: Accumulator::maxN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MaxNFindingTheMaximumThreeScoresAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MedianAccumulatorTest.php b/tests/Builder/Accumulator/MedianAccumulatorTest.php new file mode 100644 index 000000000..c1f453347 --- /dev/null +++ b/tests/Builder/Accumulator/MedianAccumulatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::MedianUseMedianAsAnAccumulator, $pipeline); + } + + public function testUseMedianInASetWindowFieldStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + sortBy: object( + test01: Sort::Asc, + ), + output: object( + test01_median: Accumulator::outputWindow( + Accumulator::median( + input: Expression::intFieldPath('test01'), + method: 'approximate', + ), + range: [-3, 3], + ), + ), + ), + Stage::project( + _id: 0, + studentId: 1, + test01_median: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::MedianUseMedianInASetWindowFieldStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MergeObjectsAccumulatorTest.php b/tests/Builder/Accumulator/MergeObjectsAccumulatorTest.php new file mode 100644 index 000000000..62fabc4f1 --- /dev/null +++ b/tests/Builder/Accumulator/MergeObjectsAccumulatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::MergeObjectsMergeObjectsAsAnAccumulator, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MinAccumulatorTest.php b/tests/Builder/Accumulator/MinAccumulatorTest.php new file mode 100644 index 000000000..442a643a3 --- /dev/null +++ b/tests/Builder/Accumulator/MinAccumulatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::MinUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + minimumQuantityForState: Accumulator::outputWindow( + Accumulator::min( + Expression::intFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MinUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/MinNAccumulatorTest.php b/tests/Builder/Accumulator/MinNAccumulatorTest.php new file mode 100644 index 000000000..c44c1f6d2 --- /dev/null +++ b/tests/Builder/Accumulator/MinNAccumulatorTest.php @@ -0,0 +1,85 @@ +assertSamePipeline(Pipelines::MinNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheMinimumThreeScoresForASingleGame(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + minScores: Accumulator::minN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MinNFindTheMinimumThreeScoresForASingleGame, $pipeline); + } + + public function testFindingTheMinimumThreeDocumentsAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + minScores: Accumulator::minN( + input: [ + Expression::fieldPath('score'), + Expression::fieldPath('playerId'), + ], + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MinNFindingTheMinimumThreeDocumentsAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/PercentileAccumulatorTest.php b/tests/Builder/Accumulator/PercentileAccumulatorTest.php new file mode 100644 index 000000000..a3fc1708d --- /dev/null +++ b/tests/Builder/Accumulator/PercentileAccumulatorTest.php @@ -0,0 +1,95 @@ +assertSamePipeline(Pipelines::PercentileCalculateASingleValueAsAnAccumulator, $pipeline); + } + + public function testCalculateMultipleValuesAsAnAccumulator(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: null, + test01_percentiles: Accumulator::percentile( + input: Expression::numberFieldPath('test01'), + p: [0.5, 0.75, 0.9, 0.95], + method: 'approximate', + ), + test02_percentiles: Accumulator::percentile( + input: Expression::numberFieldPath('test02'), + p: [0.5, 0.75, 0.9, 0.95], + method: 'approximate', + ), + test03_percentiles: Accumulator::percentile( + input: Expression::numberFieldPath('test03'), + p: [0.5, 0.75, 0.9, 0.95], + method: 'approximate', + ), + test03_percent_alt: Accumulator::percentile( + input: Expression::numberFieldPath('test03'), + p: [0.9, 0.5, 0.75, 0.95], + method: 'approximate', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::PercentileCalculateMultipleValuesAsAnAccumulator, $pipeline); + } + + public function testUsePercentileInASetWindowFieldStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + sortBy: object( + test01: Sort::Asc, + ), + output: object( + test01_95percentile: Accumulator::outputWindow( + Accumulator::percentile( + input: Expression::numberFieldPath('test01'), + p: [0.95], + method: 'approximate', + ), + range: [-3, 3], + ), + ), + ), + Stage::project( + _id: 0, + studentId: 1, + test01_95percentile: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::PercentileUsePercentileInASetWindowFieldStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/Pipelines.php b/tests/Builder/Accumulator/Pipelines.php new file mode 100644 index 000000000..23b313828 --- /dev/null +++ b/tests/Builder/Accumulator/Pipelines.php @@ -0,0 +1,2372 @@ +assertSamePipeline(Pipelines::PushUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + quantitiesForState: Accumulator::outputWindow( + Accumulator::push( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::PushUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/RankAccumulatorTest.php b/tests/Builder/Accumulator/RankAccumulatorTest.php new file mode 100644 index 000000000..3be7e63aa --- /dev/null +++ b/tests/Builder/Accumulator/RankAccumulatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::RankRankPartitionsByADateField, $pipeline); + } + + public function testRankPartitionsByAnIntegerField(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: Sort::Desc, + ), + output: object( + rankQuantityForState: Accumulator::rank(), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RankRankPartitionsByAnIntegerField, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/SetUnionAccumulatorTest.php b/tests/Builder/Accumulator/SetUnionAccumulatorTest.php new file mode 100644 index 000000000..a6b83c5c2 --- /dev/null +++ b/tests/Builder/Accumulator/SetUnionAccumulatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::SetUnionFlowersCollection, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/ShiftAccumulatorTest.php b/tests/Builder/Accumulator/ShiftAccumulatorTest.php new file mode 100644 index 000000000..7d8c58856 --- /dev/null +++ b/tests/Builder/Accumulator/ShiftAccumulatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::ShiftShiftUsingANegativeInteger, $pipeline); + } + + public function testShiftUsingAPositiveInteger(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object( + quantity: Sort::Desc, + ), + output: object( + shiftQuantityForState: Accumulator::shift( + output: Expression::fieldPath('quantity'), + by: 1, + default: 'Not available', + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ShiftShiftUsingAPositiveInteger, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/StdDevPopAccumulatorTest.php b/tests/Builder/Accumulator/StdDevPopAccumulatorTest.php new file mode 100644 index 000000000..7d38829e4 --- /dev/null +++ b/tests/Builder/Accumulator/StdDevPopAccumulatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::StdDevPopUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + stdDevPopQuantityForState: Accumulator::outputWindow( + Accumulator::stdDevPop( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::StdDevPopUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/StdDevSampAccumulatorTest.php b/tests/Builder/Accumulator/StdDevSampAccumulatorTest.php new file mode 100644 index 000000000..c1f49e00a --- /dev/null +++ b/tests/Builder/Accumulator/StdDevSampAccumulatorTest.php @@ -0,0 +1,57 @@ +assertSamePipeline(Pipelines::StdDevSampUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + stdDevSampQuantityForState: Accumulator::outputWindow( + Accumulator::stdDevSamp( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::StdDevSampUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/SumAccumulatorTest.php b/tests/Builder/Accumulator/SumAccumulatorTest.php new file mode 100644 index 000000000..21be6da72 --- /dev/null +++ b/tests/Builder/Accumulator/SumAccumulatorTest.php @@ -0,0 +1,67 @@ +assertSamePipeline(Pipelines::SumUseInGroupStage, $pipeline); + } + + public function testUseInSetWindowFieldsStage(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::fieldPath('state'), + sortBy: object( + orderDate: Sort::Asc, + ), + output: object( + sumQuantityForState: Accumulator::outputWindow( + Accumulator::sum( + Expression::intFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SumUseInSetWindowFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/TopAccumulatorTest.php b/tests/Builder/Accumulator/TopAccumulatorTest.php new file mode 100644 index 000000000..c6d32edb5 --- /dev/null +++ b/tests/Builder/Accumulator/TopAccumulatorTest.php @@ -0,0 +1,63 @@ +assertSamePipeline(Pipelines::TopFindTheTopScore, $pipeline); + } + + public function testFindTheTopScoreAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::top( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TopFindTheTopScoreAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/Accumulator/TopNAccumulatorTest.php b/tests/Builder/Accumulator/TopNAccumulatorTest.php new file mode 100644 index 000000000..6d14a9c6f --- /dev/null +++ b/tests/Builder/Accumulator/TopNAccumulatorTest.php @@ -0,0 +1,92 @@ +assertSamePipeline(Pipelines::TopNComputingNBasedOnTheGroupKeyForGroup, $pipeline); + } + + public function testFindTheThreeHighestScores(): void + { + $pipeline = new Pipeline( + Stage::match( + gameId: 'G1', + ), + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::topN( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TopNFindTheThreeHighestScores, $pipeline); + } + + public function testFindingTheThreeHighestScoreDocumentsAcrossMultipleGames(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('gameId'), + playerId: Accumulator::topN( + output: [ + Expression::fieldPath('playerId'), + Expression::fieldPath('score'), + ], + sortBy: object( + score: Sort::Desc, + ), + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TopNFindingTheThreeHighestScoreDocumentsAcrossMultipleGames, $pipeline); + } +} diff --git a/tests/Builder/BuilderEncoderTest.php b/tests/Builder/BuilderEncoderTest.php new file mode 100644 index 000000000..53080ab9e --- /dev/null +++ b/tests/Builder/BuilderEncoderTest.php @@ -0,0 +1,480 @@ + ['author' => 'dave']], + ['$limit' => 1], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + public function testMatchNumericFieldName(): void + { + $pipeline = new Pipeline( + Stage::match(['1' => Query::eq('dave')]), + Stage::match(['1' => Query::not(Query::eq('dave'))]), + Stage::match( + Query::and( + Query::query(['2' => Query::gt(3)]), + Query::query(['2' => Query::lt(4)]), + ), + ), + Stage::match( + Query::or( + Query::query(['2' => Query::gt(3)]), + Query::query(['2' => Query::lt(4)]), + ), + ), + Stage::match( + Query::nor( + Query::query(['2' => Query::gt(3)]), + Query::query(['2' => Query::lt(4)]), + ), + ), + ); + + $expected = [ + ['$match' => ['1' => ['$eq' => 'dave']]], + ['$match' => ['1' => ['$not' => ['$eq' => 'dave']]]], + [ + '$match' => [ + '$and' => [ + ['2' => ['$gt' => 3]], + ['2' => ['$lt' => 4]], + ], + ], + ], + [ + '$match' => [ + '$or' => [ + ['2' => ['$gt' => 3]], + ['2' => ['$lt' => 4]], + ], + ], + ], + [ + '$match' => [ + '$nor' => [ + ['2' => ['$gt' => 3]], + ['2' => ['$lt' => 4]], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + /** @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/sort/#ascending-descending-sort */ + public function testSort(): void + { + $pipeline = new Pipeline( + Stage::sort( + age: Sort::Desc, + posts: Sort::Asc, + ), + ); + + $expected = [ + ['$sort' => ['age' => -1, 'posts' => 1]], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + /** @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/match/#perform-a-count */ + public function testPerformCount(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::or( + Query::query(score: [Query::gt(70), Query::lt(90)]), + Query::query(views: Query::gte(1000)), + ), + ), + Stage::group( + _id: null, + count: Accumulator::sum(1), + ), + ); + + $expected = [ + [ + '$match' => [ + '$or' => [ + ['score' => ['$gt' => 70, '$lt' => 90]], + ['views' => ['$gte' => 1000]], + ], + ], + ], + [ + '$group' => [ + '_id' => null, + 'count' => ['$sum' => 1], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + /** + * @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/filter/#examples + * + * @param list $limit + * @param array $expectedLimit + */ + #[DataProvider('provideExpressionFilterLimit')] + public function testExpressionFilter(array $limit, array $expectedLimit): void + { + $pipeline = new Pipeline( + Stage::project( + items: Expression::filter( + ...$limit, + input: Expression::arrayFieldPath('items'), + cond: Expression::gte(Expression::variable('item.price'), 100), + as:'item', + ), + ), + ); + + $expected = [ + [ + '$project' => [ + 'items' => [ + '$filter' => array_merge([ + 'input' => '$items', + 'as' => 'item', + 'cond' => ['$gte' => ['$$item.price', 100]], + ], $expectedLimit), + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + public static function provideExpressionFilterLimit(): Generator + { + yield 'unspecified limit' => [ + [], + [], + ]; + + yield 'int limit' => [ + ['limit' => 1], + ['limit' => 1], + ]; + } + + /** @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/slice/#example */ + public function testSlice(): void + { + $pipeline = new Pipeline( + Stage::project( + name: 1, + threeFavorites: Expression::slice( + Expression::arrayFieldPath('items'), + n: 3, + ), + ), + ); + + $expected = [ + [ + '$project' => [ + 'name' => 1, + 'threeFavorites' => [ + '$slice' => ['$items', 3], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + /** @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/setWindowFields/#use-documents-window-to-obtain-cumulative-and-maximum-quantity-for-each-year */ + public function testSetWindowFields(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::year(Expression::dateFieldPath('orderDate')), + sortBy: object(orderDate: Sort::Asc), + output: object( + cumulativeQuantityForYear: Accumulator::outputWindow( + Accumulator::sum(Expression::intFieldPath('quantity')), + documents: ['unbounded', 'current'], + ), + maximumQuantityForYear: Accumulator::outputWindow( + Accumulator::max(Expression::intFieldPath('quantity')), + documents: ['unbounded', 'unbounded'], + ), + ), + ), + ); + + $expected = [ + [ + '$setWindowFields' => [ + // "date" key is optional for $year, but we always add it for consistency + 'partitionBy' => ['$year' => ['date' => '$orderDate']], + 'sortBy' => ['orderDate' => 1], + 'output' => [ + 'cumulativeQuantityForYear' => [ + '$sum' => '$quantity', + 'window' => ['documents' => ['unbounded', 'current']], + ], + 'maximumQuantityForYear' => [ + '$max' => '$quantity', + 'window' => ['documents' => ['unbounded', 'unbounded']], + ], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + public function testUnionWith(): void + { + $pipeline = new Pipeline( + Stage::unionWith( + coll: 'orders', + pipeline: new Pipeline( + Stage::match(status: 'A'), + Stage::project( + item: 1, + status: 1, + ), + ), + ), + ); + + $expected = [ + [ + '$unionWith' => [ + 'coll' => 'orders', + 'pipeline' => [ + [ + '$match' => ['status' => 'A'], + ], + [ + '$project' => [ + 'item' => 1, + 'status' => 1, + ], + ], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + /** @see https://www.mongodb.com/docs/manual/reference/operator/aggregation/redact/ */ + public function testRedactStage(): void + { + $pipeline = new Pipeline( + Stage::match(status: 'A'), + Stage::redact( + Expression::cond( + if: Expression::eq(Expression::fieldPath('level'), 5), + then: Variable::prune(), + else: Variable::descend(), + ), + ), + ); + $expected = [ + [ + '$match' => ['status' => 'A'], + ], + [ + '$redact' => [ + '$cond' => [ + 'if' => ['$eq' => ['$level', 5]], + 'then' => '$$PRUNE', + 'else' => '$$DESCEND', + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + public function testDateTimeEncoding(): void + { + $dateTimeImmutable = new DateTimeImmutable(); + $dateTime = DateTime::createFromImmutable($dateTimeImmutable); + $utcDateTime = new UTCDateTime($dateTime); + + $pipeline = new Pipeline( + Stage::match( + utc: $utcDateTime, + mutable: $dateTime, + immutable: $dateTimeImmutable, + ), + Stage::addFields( + utc: Expression::dateToString($utcDateTime), + mutable: Expression::dateToString($dateTime), + immutable: Expression::dateToString($dateTimeImmutable), + ), + ); + + $expected = [ + [ + '$match' => [ + 'utc' => $utcDateTime, + 'mutable' => $utcDateTime, + 'immutable' => $utcDateTime, + ], + ], + [ + '$addFields' => [ + 'utc' => ['$dateToString' => ['date' => $utcDateTime]], + 'mutable' => ['$dateToString' => ['date' => $utcDateTime]], + 'immutable' => ['$dateToString' => ['date' => $utcDateTime]], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline); + } + + public function testCustomEncoder(): void + { + $customEncoders = [ + FieldPathInterface::class => new class implements Encoder { + use EncodeIfSupported; + + public function canEncode(mixed $value): bool + { + return $value instanceof FieldPathInterface; + } + + public function encode(mixed $value): mixed + { + return '$prefix.' . $value->name; + } + }, + ]; + $codec = new BuilderEncoder($customEncoders); + + $pipeline = new Pipeline( + Stage::project( + threeFavorites: Expression::slice( + Expression::arrayFieldPath('items'), + n: 3, + ), + ), + ); + + $expected = [ + [ + '$project' => [ + 'threeFavorites' => [ + '$slice' => ['$prefix.items', 3], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline, $codec); + } + + public function testCustomEncoderIterable(): void + { + $customEncoders = static function (): Generator { + yield FieldPathInterface::class => new class implements Encoder { + use EncodeIfSupported; + + public function canEncode(mixed $value): bool + { + return $value instanceof FieldPathInterface; + } + + public function encode(mixed $value): mixed + { + return '$prefix.' . $value->name; + } + }; + }; + + $codec = new BuilderEncoder($customEncoders()); + + $pipeline = new Pipeline( + Stage::project( + threeFavorites: Expression::slice( + Expression::arrayFieldPath('items'), + n: 3, + ), + ), + ); + + $expected = [ + [ + '$project' => [ + 'threeFavorites' => [ + '$slice' => ['$prefix.items', 3], + ], + ], + ], + ]; + + $this->assertSamePipeline($expected, $pipeline, $codec); + } + + /** @param list> $expected */ + private static function assertSamePipeline(array $expected, Pipeline $pipeline, $codec = new BuilderEncoder()): void + { + $actual = $codec->encode($pipeline); + + // Normalize with BSON round-trip + // BSON Documents doesn't support top-level arrays. + $actual = Document::fromPHP(['root' => $actual])->toCanonicalExtendedJSON(); + $expected = Document::fromPHP(['root' => $expected])->toCanonicalExtendedJSON(); + + self::assertJsonStringEqualsJsonString($expected, $actual, var_export($actual, true)); + } +} diff --git a/tests/Builder/Expression/AbsOperatorTest.php b/tests/Builder/Expression/AbsOperatorTest.php new file mode 100644 index 000000000..829c9c551 --- /dev/null +++ b/tests/Builder/Expression/AbsOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::AbsExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AcosOperatorTest.php b/tests/Builder/Expression/AcosOperatorTest.php new file mode 100644 index 000000000..21b4f4ff0 --- /dev/null +++ b/tests/Builder/Expression/AcosOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::AcosExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AcoshOperatorTest.php b/tests/Builder/Expression/AcoshOperatorTest.php new file mode 100644 index 000000000..4ba41cf59 --- /dev/null +++ b/tests/Builder/Expression/AcoshOperatorTest.php @@ -0,0 +1,33 @@ + Expression::radiansToDegrees( + Expression::acosh( + Expression::numberFieldPath('x-coordinate'), + ), + ), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::AcoshExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AddOperatorTest.php b/tests/Builder/Expression/AddOperatorTest.php new file mode 100644 index 000000000..b6fd43935 --- /dev/null +++ b/tests/Builder/Expression/AddOperatorTest.php @@ -0,0 +1,46 @@ +assertSamePipeline(Pipelines::AddAddNumbers, $pipeline); + } + + public function testPerformAdditionOnADate(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + billing_date: Expression::add( + Expression::fieldPath('date'), + 3 * 24 * 60 * 60000, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AddPerformAdditionOnADate, $pipeline); + } +} diff --git a/tests/Builder/Expression/AllElementsTrueOperatorTest.php b/tests/Builder/Expression/AllElementsTrueOperatorTest.php new file mode 100644 index 000000000..b3591dc3f --- /dev/null +++ b/tests/Builder/Expression/AllElementsTrueOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::AllElementsTrueExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AndOperatorTest.php b/tests/Builder/Expression/AndOperatorTest.php new file mode 100644 index 000000000..3956d29e0 --- /dev/null +++ b/tests/Builder/Expression/AndOperatorTest.php @@ -0,0 +1,38 @@ +assertSamePipeline(Pipelines::AndExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AnyElementTrueOperatorTest.php b/tests/Builder/Expression/AnyElementTrueOperatorTest.php new file mode 100644 index 000000000..a99fd1654 --- /dev/null +++ b/tests/Builder/Expression/AnyElementTrueOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::AnyElementTrueExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ArrayElemAtOperatorTest.php b/tests/Builder/Expression/ArrayElemAtOperatorTest.php new file mode 100644 index 000000000..0dbe768ec --- /dev/null +++ b/tests/Builder/Expression/ArrayElemAtOperatorTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::ArrayElemAtExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ArrayToObjectOperatorTest.php b/tests/Builder/Expression/ArrayToObjectOperatorTest.php new file mode 100644 index 000000000..c18d7bdcb --- /dev/null +++ b/tests/Builder/Expression/ArrayToObjectOperatorTest.php @@ -0,0 +1,60 @@ +assertSamePipeline(Pipelines::ArrayToObjectArrayToObjectExample, $pipeline); + } + + public function testObjectToArrayAndArrayToObjectExample(): void + { + $pipeline = new Pipeline( + Stage::addFields( + instock: Expression::objectToArray( + Expression::objectFieldPath('instock'), + ), + ), + Stage::addFields( + instock: Expression::concatArrays( + Expression::arrayFieldPath('instock'), + [ + object(k: 'total', v: Expression::sum( + Expression::fieldPath('instock.v'), + )), + ], + ), + ), + Stage::addFields( + instock: Expression::arrayToObject( + Expression::arrayFieldPath('instock'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ArrayToObjectObjectToArrayAndArrayToObjectExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AsinOperatorTest.php b/tests/Builder/Expression/AsinOperatorTest.php new file mode 100644 index 000000000..c74dc75c7 --- /dev/null +++ b/tests/Builder/Expression/AsinOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::AsinExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AsinhOperatorTest.php b/tests/Builder/Expression/AsinhOperatorTest.php new file mode 100644 index 000000000..138030e54 --- /dev/null +++ b/tests/Builder/Expression/AsinhOperatorTest.php @@ -0,0 +1,33 @@ + Expression::radiansToDegrees( + Expression::asinh( + Expression::numberFieldPath('x-coordinate'), + ), + ), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::AsinhExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/Atan2OperatorTest.php b/tests/Builder/Expression/Atan2OperatorTest.php new file mode 100644 index 000000000..442e0150f --- /dev/null +++ b/tests/Builder/Expression/Atan2OperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::Atan2Example, $pipeline); + } +} diff --git a/tests/Builder/Expression/AtanOperatorTest.php b/tests/Builder/Expression/AtanOperatorTest.php new file mode 100644 index 000000000..3040823b1 --- /dev/null +++ b/tests/Builder/Expression/AtanOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::AtanExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AtanhOperatorTest.php b/tests/Builder/Expression/AtanhOperatorTest.php new file mode 100644 index 000000000..3194a8e94 --- /dev/null +++ b/tests/Builder/Expression/AtanhOperatorTest.php @@ -0,0 +1,33 @@ + Expression::radiansToDegrees( + Expression::atanh( + Expression::numberFieldPath('x-coordinate'), + ), + ), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::AtanhExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/AvgOperatorTest.php b/tests/Builder/Expression/AvgOperatorTest.php new file mode 100644 index 000000000..595c49f1b --- /dev/null +++ b/tests/Builder/Expression/AvgOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::AvgUseInProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/BinarySizeOperatorTest.php b/tests/Builder/Expression/BinarySizeOperatorTest.php new file mode 100644 index 000000000..8b0653315 --- /dev/null +++ b/tests/Builder/Expression/BinarySizeOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::BinarySizeExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/BitAndOperatorTest.php b/tests/Builder/Expression/BitAndOperatorTest.php new file mode 100644 index 000000000..e46fbb55a --- /dev/null +++ b/tests/Builder/Expression/BitAndOperatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::BitAndBitwiseANDWithALongAndInteger, $pipeline); + } + + public function testBitwiseANDWithTwoIntegers(): void + { + $pipeline = new Pipeline( + Stage::project( + result: Expression::bitAnd( + Expression::intFieldPath('a'), + Expression::intFieldPath('b'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BitAndBitwiseANDWithTwoIntegers, $pipeline); + } +} diff --git a/tests/Builder/Expression/BitNotOperatorTest.php b/tests/Builder/Expression/BitNotOperatorTest.php new file mode 100644 index 000000000..9407a2861 --- /dev/null +++ b/tests/Builder/Expression/BitNotOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::BitNotExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/BitOrOperatorTest.php b/tests/Builder/Expression/BitOrOperatorTest.php new file mode 100644 index 000000000..2558432e9 --- /dev/null +++ b/tests/Builder/Expression/BitOrOperatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::BitOrBitwiseORWithALongAndInteger, $pipeline); + } + + public function testBitwiseORWithTwoIntegers(): void + { + $pipeline = new Pipeline( + Stage::project( + result: Expression::bitOr( + Expression::intFieldPath('a'), + Expression::intFieldPath('b'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BitOrBitwiseORWithTwoIntegers, $pipeline); + } +} diff --git a/tests/Builder/Expression/BitXorOperatorTest.php b/tests/Builder/Expression/BitXorOperatorTest.php new file mode 100644 index 000000000..1fffe2c18 --- /dev/null +++ b/tests/Builder/Expression/BitXorOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::BitXorExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/BsonSizeOperatorTest.php b/tests/Builder/Expression/BsonSizeOperatorTest.php new file mode 100644 index 000000000..49707b120 --- /dev/null +++ b/tests/Builder/Expression/BsonSizeOperatorTest.php @@ -0,0 +1,66 @@ +assertSamePipeline(Pipelines::BsonSizeReturnCombinedSizeOfAllDocumentsInACollection, $pipeline); + } + + public function testReturnDocumentWithLargestSpecifiedField(): void + { + $pipeline = new Pipeline( + Stage::project( + name: Expression::stringFieldPath('name'), + task_object_size: Expression::bsonSize( + Expression::objectFieldPath('current_task'), + ), + ), + Stage::sort( + task_object_size: Sort::Desc, + ), + Stage::limit(1), + ); + + $this->assertSamePipeline(Pipelines::BsonSizeReturnDocumentWithLargestSpecifiedField, $pipeline); + } + + public function testReturnSizesOfDocuments(): void + { + $pipeline = new Pipeline( + Stage::project( + name: 1, + object_size: Expression::bsonSize( + Expression::variable('ROOT'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BsonSizeReturnSizesOfDocuments, $pipeline); + } +} diff --git a/tests/Builder/Expression/CeilOperatorTest.php b/tests/Builder/Expression/CeilOperatorTest.php new file mode 100644 index 000000000..6d78af6a3 --- /dev/null +++ b/tests/Builder/Expression/CeilOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::CeilExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/CmpOperatorTest.php b/tests/Builder/Expression/CmpOperatorTest.php new file mode 100644 index 000000000..e7bb5bc09 --- /dev/null +++ b/tests/Builder/Expression/CmpOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::CmpExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ConcatArraysOperatorTest.php b/tests/Builder/Expression/ConcatArraysOperatorTest.php new file mode 100644 index 000000000..ba7dffb4c --- /dev/null +++ b/tests/Builder/Expression/ConcatArraysOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::ConcatArraysExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ConcatOperatorTest.php b/tests/Builder/Expression/ConcatOperatorTest.php new file mode 100644 index 000000000..d96f17685 --- /dev/null +++ b/tests/Builder/Expression/ConcatOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::ConcatExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/CondOperatorTest.php b/tests/Builder/Expression/CondOperatorTest.php new file mode 100644 index 000000000..725ab1c50 --- /dev/null +++ b/tests/Builder/Expression/CondOperatorTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::CondExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ConvertOperatorTest.php b/tests/Builder/Expression/ConvertOperatorTest.php new file mode 100644 index 000000000..ab559c38a --- /dev/null +++ b/tests/Builder/Expression/ConvertOperatorTest.php @@ -0,0 +1,73 @@ +assertSamePipeline(Pipelines::ConvertExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/CosOperatorTest.php b/tests/Builder/Expression/CosOperatorTest.php new file mode 100644 index 000000000..0124b88c1 --- /dev/null +++ b/tests/Builder/Expression/CosOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::CosExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/CoshOperatorTest.php b/tests/Builder/Expression/CoshOperatorTest.php new file mode 100644 index 000000000..37ec83724 --- /dev/null +++ b/tests/Builder/Expression/CoshOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::CoshExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/CreateObjectIdOperatorTest.php b/tests/Builder/Expression/CreateObjectIdOperatorTest.php new file mode 100644 index 000000000..687d80638 --- /dev/null +++ b/tests/Builder/Expression/CreateObjectIdOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::CreateObjectIdExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateAddOperatorTest.php b/tests/Builder/Expression/DateAddOperatorTest.php new file mode 100644 index 000000000..32ecac36b --- /dev/null +++ b/tests/Builder/Expression/DateAddOperatorTest.php @@ -0,0 +1,125 @@ +assertSamePipeline(Pipelines::DateAddAddAFutureDate, $pipeline); + } + + public function testAdjustForDaylightSavingsTime(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + location: 1, + start: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateFieldPath('login'), + ), + days: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateAdd( + startDate: Expression::dateFieldPath('login'), + unit: TimeUnit::Day, + amount: 1, + timezone: Expression::stringFieldPath('location'), + ), + ), + hours: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateAdd( + startDate: Expression::dateFieldPath('login'), + unit: TimeUnit::Hour, + amount: 24, + timezone: Expression::stringFieldPath('location'), + ), + ), + startTZInfo: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateFieldPath('login'), + timezone: Expression::stringFieldPath('location'), + ), + daysTZInfo: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateAdd( + startDate: Expression::dateFieldPath('login'), + unit: TimeUnit::Day, + amount: 1, + timezone: Expression::stringFieldPath('location'), + ), + timezone: Expression::stringFieldPath('location'), + ), + hoursTZInfo: Expression::dateToString( + format: '%Y-%m-%d %H:%M', + date: Expression::dateAdd( + startDate: Expression::dateFieldPath('login'), + unit: TimeUnit::Hour, + amount: 24, + timezone: Expression::stringFieldPath('location'), + ), + timezone: Expression::stringFieldPath('location'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateAddAdjustForDaylightSavingsTime, $pipeline); + } + + public function testFilterOnADateRange(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::gt( + Expression::dateFieldPath('deliveryDate'), + Expression::dateAdd( + startDate: Expression::dateFieldPath('purchaseDate'), + unit: TimeUnit::Day, + amount: 5, + ), + ), + ), + ), + Stage::project( + _id: 0, + custId: 1, + purchased: Expression::dateToString( + format: '%Y-%m-%d', + date: Expression::dateFieldPath('purchaseDate'), + ), + delivery: Expression::dateToString( + format: '%Y-%m-%d', + date: Expression::dateFieldPath('deliveryDate'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateAddFilterOnADateRange, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateDiffOperatorTest.php b/tests/Builder/Expression/DateDiffOperatorTest.php new file mode 100644 index 000000000..571e0473c --- /dev/null +++ b/tests/Builder/Expression/DateDiffOperatorTest.php @@ -0,0 +1,99 @@ +assertSamePipeline(Pipelines::DateDiffElapsedTime, $pipeline); + } + + public function testResultPrecision(): void + { + $pipeline = new Pipeline( + Stage::project( + Start: Expression::dateFieldPath('start'), + End: Expression::dateFieldPath('end'), + years: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Year, + ), + months: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Month, + ), + days: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Day, + ), + _id: 0, + ), + ); + + $this->assertSamePipeline(Pipelines::DateDiffResultPrecision, $pipeline); + } + + public function testWeeksPerMonth(): void + { + $pipeline = new Pipeline( + Stage::project( + wks_default: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Week, + ), + wks_monday: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Week, + startOfWeek: 'Monday', + ), + wks_friday: Expression::dateDiff( + startDate: Expression::dateFieldPath('start'), + endDate: Expression::dateFieldPath('end'), + unit: TimeUnit::Week, + startOfWeek: 'fri', + ), + _id: 0, + ), + ); + + $this->assertSamePipeline(Pipelines::DateDiffWeeksPerMonth, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateFromPartsOperatorTest.php b/tests/Builder/Expression/DateFromPartsOperatorTest.php new file mode 100644 index 000000000..aa8a471e1 --- /dev/null +++ b/tests/Builder/Expression/DateFromPartsOperatorTest.php @@ -0,0 +1,47 @@ +assertSamePipeline(Pipelines::DateFromPartsExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateFromStringOperatorTest.php b/tests/Builder/Expression/DateFromStringOperatorTest.php new file mode 100644 index 000000000..ce5e53a21 --- /dev/null +++ b/tests/Builder/Expression/DateFromStringOperatorTest.php @@ -0,0 +1,61 @@ +assertSamePipeline(Pipelines::DateFromStringConvertingDates, $pipeline); + } + + public function testOnError(): void + { + $pipeline = new Pipeline( + Stage::project( + date: Expression::dateFromString( + dateString: Expression::stringFieldPath('date'), + timezone: Expression::stringFieldPath('timezone'), + onError: Expression::stringFieldPath('date'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateFromStringOnError, $pipeline); + } + + public function testOnNull(): void + { + $pipeline = new Pipeline( + Stage::project( + date: Expression::dateFromString( + dateString: Expression::stringFieldPath('date'), + timezone: Expression::stringFieldPath('timezone'), + onNull: new UTCDateTime(0), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateFromStringOnNull, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateSubtractOperatorTest.php b/tests/Builder/Expression/DateSubtractOperatorTest.php new file mode 100644 index 000000000..013a3db74 --- /dev/null +++ b/tests/Builder/Expression/DateSubtractOperatorTest.php @@ -0,0 +1,131 @@ +assertSamePipeline(Pipelines::DateSubtractAdjustForDaylightSavingsTime, $pipeline); + } + + public function testFilterByRelativeDates(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::gt( + Expression::dateFieldPath('logoutTime'), + Expression::dateSubtract( + startDate: Expression::variable('NOW'), + unit: TimeUnit::Week, + amount: 1, + ), + ), + ), + ), + Stage::project( + _id: 0, + custId: 1, + loggedOut: Expression::dateToString( + format: '%Y-%m-%d', + date: Expression::dateFieldPath('logoutTime'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateSubtractFilterByRelativeDates, $pipeline); + } + + public function testSubtractAFixedAmount(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::eq( + Expression::month( + Expression::dateFieldPath('logout'), + ), + 1, + ), + ), + ), + Stage::project( + logoutTime: Expression::dateSubtract( + startDate: Expression::dateFieldPath('logout'), + unit: TimeUnit::Hour, + amount: 3, + ), + ), + Stage::merge('connectionTime'), + ); + + $this->assertSamePipeline(Pipelines::DateSubtractSubtractAFixedAmount, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateToPartsOperatorTest.php b/tests/Builder/Expression/DateToPartsOperatorTest.php new file mode 100644 index 000000000..3df3ae827 --- /dev/null +++ b/tests/Builder/Expression/DateToPartsOperatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::DateToPartsExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateToStringOperatorTest.php b/tests/Builder/Expression/DateToStringOperatorTest.php new file mode 100644 index 000000000..a8a24a148 --- /dev/null +++ b/tests/Builder/Expression/DateToStringOperatorTest.php @@ -0,0 +1,60 @@ +assertSamePipeline(Pipelines::DateToStringExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DateTruncOperatorTest.php b/tests/Builder/Expression/DateTruncOperatorTest.php new file mode 100644 index 000000000..37bf8592a --- /dev/null +++ b/tests/Builder/Expression/DateTruncOperatorTest.php @@ -0,0 +1,59 @@ +assertSamePipeline(Pipelines::DateTruncTruncateOrderDatesAndObtainQuantitySumInAGroupPipelineStage, $pipeline); + } + + public function testTruncateOrderDatesInAProjectPipelineStage(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 1, + orderDate: 1, + truncatedOrderDate: Expression::dateTrunc( + date: Expression::dateFieldPath('orderDate'), + unit: TimeUnit::Week, + binSize: 2, + timezone: 'America/Los_Angeles', + startOfWeek: 'Monday', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DateTruncTruncateOrderDatesInAProjectPipelineStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/DayOfMonthOperatorTest.php b/tests/Builder/Expression/DayOfMonthOperatorTest.php new file mode 100644 index 000000000..07b22ae70 --- /dev/null +++ b/tests/Builder/Expression/DayOfMonthOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::DayOfMonthExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DayOfWeekOperatorTest.php b/tests/Builder/Expression/DayOfWeekOperatorTest.php new file mode 100644 index 000000000..54c5d5402 --- /dev/null +++ b/tests/Builder/Expression/DayOfWeekOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::DayOfWeekExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DayOfYearOperatorTest.php b/tests/Builder/Expression/DayOfYearOperatorTest.php new file mode 100644 index 000000000..494ec4365 --- /dev/null +++ b/tests/Builder/Expression/DayOfYearOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::DayOfYearExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DegreesToRadiansOperatorTest.php b/tests/Builder/Expression/DegreesToRadiansOperatorTest.php new file mode 100644 index 000000000..82e9797a2 --- /dev/null +++ b/tests/Builder/Expression/DegreesToRadiansOperatorTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::DegreesToRadiansExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/DivideOperatorTest.php b/tests/Builder/Expression/DivideOperatorTest.php new file mode 100644 index 000000000..dfccd98eb --- /dev/null +++ b/tests/Builder/Expression/DivideOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::DivideExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/EqOperatorTest.php b/tests/Builder/Expression/EqOperatorTest.php new file mode 100644 index 000000000..0c9b72bef --- /dev/null +++ b/tests/Builder/Expression/EqOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::EqExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ExpOperatorTest.php b/tests/Builder/Expression/ExpOperatorTest.php new file mode 100644 index 000000000..daa582518 --- /dev/null +++ b/tests/Builder/Expression/ExpOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::ExpExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/FilterOperatorTest.php b/tests/Builder/Expression/FilterOperatorTest.php new file mode 100644 index 000000000..1cb3af584 --- /dev/null +++ b/tests/Builder/Expression/FilterOperatorTest.php @@ -0,0 +1,91 @@ +assertSamePipeline(Pipelines::FilterExample, $pipeline); + } + + public function testLimitAsANumericExpression(): void + { + $pipeline = new Pipeline( + Stage::project( + items: Expression::filter( + input: Expression::arrayFieldPath('items'), + cond: Expression::lte( + Expression::variable('item.price'), + 150, + ), + as: 'item', + limit: 2, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FilterLimitAsANumericExpression, $pipeline); + } + + public function testLimitGreaterThanPossibleMatches(): void + { + $pipeline = new Pipeline( + Stage::project( + items: Expression::filter( + input: Expression::arrayFieldPath('items'), + cond: Expression::gte( + Expression::variable('item.price'), + 100, + ), + as: 'item', + limit: 5, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FilterLimitGreaterThanPossibleMatches, $pipeline); + } + + public function testUsingTheLimitField(): void + { + $pipeline = new Pipeline( + Stage::project( + items: Expression::filter( + input: Expression::arrayFieldPath('items'), + cond: Expression::gte( + Expression::variable('item.price'), + 100, + ), + as: 'item', + limit: 1, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FilterUsingTheLimitField, $pipeline); + } +} diff --git a/tests/Builder/Expression/FirstNOperatorTest.php b/tests/Builder/Expression/FirstNOperatorTest.php new file mode 100644 index 000000000..a09c356d7 --- /dev/null +++ b/tests/Builder/Expression/FirstNOperatorTest.php @@ -0,0 +1,48 @@ +assertSamePipeline(Pipelines::FirstNExample, $pipeline); + } + + public function testUsingFirstNAsAnAggregationExpression(): void + { + $pipeline = new Pipeline( + Stage::documents([ + object( + array: [10, 20, 30, 40], + ), + ]), + Stage::project( + firstThreeElements: Expression::firstN( + input: Expression::arrayFieldPath('array'), + n: 3, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FirstNUsingFirstNAsAnAggregationExpression, $pipeline); + } +} diff --git a/tests/Builder/Expression/FirstOperatorTest.php b/tests/Builder/Expression/FirstOperatorTest.php new file mode 100644 index 000000000..10fab6cdc --- /dev/null +++ b/tests/Builder/Expression/FirstOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::FirstUseInAddFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/FloorOperatorTest.php b/tests/Builder/Expression/FloorOperatorTest.php new file mode 100644 index 000000000..8aa3c8393 --- /dev/null +++ b/tests/Builder/Expression/FloorOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::FloorExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/FunctionOperatorTest.php b/tests/Builder/Expression/FunctionOperatorTest.php new file mode 100644 index 000000000..97ee7c3c7 --- /dev/null +++ b/tests/Builder/Expression/FunctionOperatorTest.php @@ -0,0 +1,71 @@ +assertSamePipeline(Pipelines::FunctionAlternativeToWhere, $pipeline); + } + + public function testUsageExample(): void + { + $pipeline = new Pipeline( + Stage::addFields( + isFound: Expression::function( + body: <<<'JS' + function(name) { + return hex_md5(name) == "15b0a220baa16331e8d80e15367677ad" + } + JS, + args: [ + Expression::stringFieldPath('name'), + ], + ), + message: Expression::function( + body: <<<'JS' + function(name, scores) { + let total = Array.sum(scores); + return `Hello ${name}. Your total score is ${total}.` + } + JS, + args: [ + Expression::stringFieldPath('name'), + Expression::stringFieldPath('scores'), + ], + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FunctionUsageExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/GetFieldOperatorTest.php b/tests/Builder/Expression/GetFieldOperatorTest.php new file mode 100644 index 000000000..117149ddf --- /dev/null +++ b/tests/Builder/Expression/GetFieldOperatorTest.php @@ -0,0 +1,70 @@ +assertSamePipeline(Pipelines::GetFieldQueryAFieldInASubdocument, $pipeline); + } + + public function testQueryFieldsThatContainPeriods(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::gt( + Expression::getField('price.usd'), + 200, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GetFieldQueryFieldsThatContainPeriods, $pipeline); + } + + public function testQueryFieldsThatStartWithADollarSign(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::gt( + Expression::getField( + Expression::literal('$price'), + ), + 200, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GetFieldQueryFieldsThatStartWithADollarSign, $pipeline); + } +} diff --git a/tests/Builder/Expression/GtOperatorTest.php b/tests/Builder/Expression/GtOperatorTest.php new file mode 100644 index 000000000..a8cecfd84 --- /dev/null +++ b/tests/Builder/Expression/GtOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::GtExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/GteOperatorTest.php b/tests/Builder/Expression/GteOperatorTest.php new file mode 100644 index 000000000..abc89743f --- /dev/null +++ b/tests/Builder/Expression/GteOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::GteExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/HourOperatorTest.php b/tests/Builder/Expression/HourOperatorTest.php new file mode 100644 index 000000000..32234ea91 --- /dev/null +++ b/tests/Builder/Expression/HourOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::HourExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IfNullOperatorTest.php b/tests/Builder/Expression/IfNullOperatorTest.php new file mode 100644 index 000000000..2bd046e8c --- /dev/null +++ b/tests/Builder/Expression/IfNullOperatorTest.php @@ -0,0 +1,47 @@ +assertSamePipeline(Pipelines::IfNullMultipleInputExpressions, $pipeline); + } + + public function testSingleInputExpression(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + description: Expression::ifNull( + Expression::fieldPath('description'), + 'Unspecified', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::IfNullSingleInputExpression, $pipeline); + } +} diff --git a/tests/Builder/Expression/InOperatorTest.php b/tests/Builder/Expression/InOperatorTest.php new file mode 100644 index 000000000..27722f856 --- /dev/null +++ b/tests/Builder/Expression/InOperatorTest.php @@ -0,0 +1,33 @@ + Expression::fieldPath('location'), + 'has bananas' => Expression::in( + 'bananas', + Expression::arrayFieldPath('in_stock'), + ), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::InExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IndexOfArrayOperatorTest.php b/tests/Builder/Expression/IndexOfArrayOperatorTest.php new file mode 100644 index 000000000..a18343ea2 --- /dev/null +++ b/tests/Builder/Expression/IndexOfArrayOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::IndexOfArrayExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IndexOfBytesOperatorTest.php b/tests/Builder/Expression/IndexOfBytesOperatorTest.php new file mode 100644 index 000000000..51c7720e9 --- /dev/null +++ b/tests/Builder/Expression/IndexOfBytesOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::IndexOfBytesExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IndexOfCPOperatorTest.php b/tests/Builder/Expression/IndexOfCPOperatorTest.php new file mode 100644 index 000000000..4d5e3b6da --- /dev/null +++ b/tests/Builder/Expression/IndexOfCPOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::IndexOfCPExamples, $pipeline); + } +} diff --git a/tests/Builder/Expression/IsArrayOperatorTest.php b/tests/Builder/Expression/IsArrayOperatorTest.php new file mode 100644 index 000000000..983f4490d --- /dev/null +++ b/tests/Builder/Expression/IsArrayOperatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::IsArrayExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IsNumberOperatorTest.php b/tests/Builder/Expression/IsNumberOperatorTest.php new file mode 100644 index 000000000..0d7d2d4bc --- /dev/null +++ b/tests/Builder/Expression/IsNumberOperatorTest.php @@ -0,0 +1,94 @@ +assertSamePipeline(Pipelines::IsNumberConditionallyModifyFieldsUsingIsNumber, $pipeline); + } + + public function testUseIsNumberToCheckIfAFieldIsNumeric(): void + { + $pipeline = new Pipeline( + Stage::addFields( + isNumber: Expression::isNumber( + Expression::fieldPath('reading'), + ), + hasType: Expression::type( + Expression::fieldPath('reading'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::IsNumberUseIsNumberToCheckIfAFieldIsNumeric, $pipeline); + } +} diff --git a/tests/Builder/Expression/IsoDayOfWeekOperatorTest.php b/tests/Builder/Expression/IsoDayOfWeekOperatorTest.php new file mode 100644 index 000000000..0169b97dc --- /dev/null +++ b/tests/Builder/Expression/IsoDayOfWeekOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::IsoDayOfWeekExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IsoWeekOperatorTest.php b/tests/Builder/Expression/IsoWeekOperatorTest.php new file mode 100644 index 000000000..adc00f640 --- /dev/null +++ b/tests/Builder/Expression/IsoWeekOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::IsoWeekExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/IsoWeekYearOperatorTest.php b/tests/Builder/Expression/IsoWeekYearOperatorTest.php new file mode 100644 index 000000000..613f32090 --- /dev/null +++ b/tests/Builder/Expression/IsoWeekYearOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::IsoWeekYearExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/LastNOperatorTest.php b/tests/Builder/Expression/LastNOperatorTest.php new file mode 100644 index 000000000..485721135 --- /dev/null +++ b/tests/Builder/Expression/LastNOperatorTest.php @@ -0,0 +1,43 @@ +assertSamePipeline(Pipelines::LastNExample, $pipeline); + } + + public function testUsingLastNAsAnAggregationExpression(): void + { + $pipeline = new Pipeline( + Stage::documents([ + [ + 'array' => [10, 20, 30, 40], + ], + ]), + Stage::project( + lastThreeElements: Expression::lastN(input: Expression::arrayFieldPath('array'), n: 3), + ), + ); + + $this->assertSamePipeline(Pipelines::LastNUsingLastNAsAnAggregationExpression, $pipeline); + } +} diff --git a/tests/Builder/Expression/LastOperatorTest.php b/tests/Builder/Expression/LastOperatorTest.php new file mode 100644 index 000000000..7383819d6 --- /dev/null +++ b/tests/Builder/Expression/LastOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::LastUseInAddFieldsStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/LetOperatorTest.php b/tests/Builder/Expression/LetOperatorTest.php new file mode 100644 index 000000000..602a69adf --- /dev/null +++ b/tests/Builder/Expression/LetOperatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::LetExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/LnOperatorTest.php b/tests/Builder/Expression/LnOperatorTest.php new file mode 100644 index 000000000..cbbb85ce2 --- /dev/null +++ b/tests/Builder/Expression/LnOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::LnExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/Log10OperatorTest.php b/tests/Builder/Expression/Log10OperatorTest.php new file mode 100644 index 000000000..eb7e6b693 --- /dev/null +++ b/tests/Builder/Expression/Log10OperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::Log10Example, $pipeline); + } +} diff --git a/tests/Builder/Expression/LogOperatorTest.php b/tests/Builder/Expression/LogOperatorTest.php new file mode 100644 index 000000000..f1215d53e --- /dev/null +++ b/tests/Builder/Expression/LogOperatorTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::LogExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/LtOperatorTest.php b/tests/Builder/Expression/LtOperatorTest.php new file mode 100644 index 000000000..86a7815aa --- /dev/null +++ b/tests/Builder/Expression/LtOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::LtExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/LteOperatorTest.php b/tests/Builder/Expression/LteOperatorTest.php new file mode 100644 index 000000000..4a65cd593 --- /dev/null +++ b/tests/Builder/Expression/LteOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::LteExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/LtrimOperatorTest.php b/tests/Builder/Expression/LtrimOperatorTest.php new file mode 100644 index 000000000..96045dd32 --- /dev/null +++ b/tests/Builder/Expression/LtrimOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::LtrimExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MapOperatorTest.php b/tests/Builder/Expression/MapOperatorTest.php new file mode 100644 index 000000000..347801c3a --- /dev/null +++ b/tests/Builder/Expression/MapOperatorTest.php @@ -0,0 +1,75 @@ + [ + '$$grade', + 2, + ], + ], + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MapAddToEachElementOfAnArray, $pipeline); + } + + public function testConvertCelsiusTemperaturesToFahrenheit(): void + { + $pipeline = new Pipeline( + Stage::addFields( + tempsF: Expression::map( + input: Expression::arrayFieldPath('tempsC'), + as: 'tempInCelsius', + in: Expression::add( + Expression::multiply( + Expression::variable('tempInCelsius'), + 1.8, + ), + 32, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MapConvertCelsiusTemperaturesToFahrenheit, $pipeline); + } + + public function testTruncateEachArrayElement(): void + { + $pipeline = new Pipeline( + Stage::project( + city: Expression::stringFieldPath('city'), + integerValues: Expression::map( + input: Expression::arrayFieldPath('distances'), + as: 'decimalValue', + in: Expression::trunc( + Expression::variable('decimalValue'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MapTruncateEachArrayElement, $pipeline); + } +} diff --git a/tests/Builder/Expression/MaxNOperatorTest.php b/tests/Builder/Expression/MaxNOperatorTest.php new file mode 100644 index 000000000..1f94cd31f --- /dev/null +++ b/tests/Builder/Expression/MaxNOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::MaxNExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MaxOperatorTest.php b/tests/Builder/Expression/MaxOperatorTest.php new file mode 100644 index 000000000..eb88daa85 --- /dev/null +++ b/tests/Builder/Expression/MaxOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::MaxUseInProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/MedianOperatorTest.php b/tests/Builder/Expression/MedianOperatorTest.php new file mode 100644 index 000000000..f231344f3 --- /dev/null +++ b/tests/Builder/Expression/MedianOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::MedianUseMedianInAProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/MergeObjectsOperatorTest.php b/tests/Builder/Expression/MergeObjectsOperatorTest.php new file mode 100644 index 000000000..0ca4f313f --- /dev/null +++ b/tests/Builder/Expression/MergeObjectsOperatorTest.php @@ -0,0 +1,39 @@ +assertSamePipeline(Pipelines::MergeObjectsMergeObjects, $pipeline); + } +} diff --git a/tests/Builder/Expression/MetaOperatorTest.php b/tests/Builder/Expression/MetaOperatorTest.php new file mode 100644 index 000000000..b8bc3d933 --- /dev/null +++ b/tests/Builder/Expression/MetaOperatorTest.php @@ -0,0 +1,49 @@ +assertSamePipeline(Pipelines::MetaIndexKey, $pipeline); + } + + public function testTextScore(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text( + search: 'cake', + ), + ), + Stage::group( + _id: Expression::meta('textScore'), + count: Accumulator::sum(1), + ), + ); + + $this->assertSamePipeline(Pipelines::MetaTextScore, $pipeline); + } +} diff --git a/tests/Builder/Expression/MillisecondOperatorTest.php b/tests/Builder/Expression/MillisecondOperatorTest.php new file mode 100644 index 000000000..4d6bd1f57 --- /dev/null +++ b/tests/Builder/Expression/MillisecondOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::MillisecondExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MinNOperatorTest.php b/tests/Builder/Expression/MinNOperatorTest.php new file mode 100644 index 000000000..f6fc34eff --- /dev/null +++ b/tests/Builder/Expression/MinNOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::MinNExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MinOperatorTest.php b/tests/Builder/Expression/MinOperatorTest.php new file mode 100644 index 000000000..11e1416a5 --- /dev/null +++ b/tests/Builder/Expression/MinOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::MinUseInProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/MinuteOperatorTest.php b/tests/Builder/Expression/MinuteOperatorTest.php new file mode 100644 index 000000000..adf1d47e6 --- /dev/null +++ b/tests/Builder/Expression/MinuteOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::MinuteExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ModOperatorTest.php b/tests/Builder/Expression/ModOperatorTest.php new file mode 100644 index 000000000..5951779c4 --- /dev/null +++ b/tests/Builder/Expression/ModOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::ModExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MonthOperatorTest.php b/tests/Builder/Expression/MonthOperatorTest.php new file mode 100644 index 000000000..0945b172a --- /dev/null +++ b/tests/Builder/Expression/MonthOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::MonthExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/MultiplyOperatorTest.php b/tests/Builder/Expression/MultiplyOperatorTest.php new file mode 100644 index 000000000..546c4185c --- /dev/null +++ b/tests/Builder/Expression/MultiplyOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::MultiplyExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/NeOperatorTest.php b/tests/Builder/Expression/NeOperatorTest.php new file mode 100644 index 000000000..b052be640 --- /dev/null +++ b/tests/Builder/Expression/NeOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::NeExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/NotOperatorTest.php b/tests/Builder/Expression/NotOperatorTest.php new file mode 100644 index 000000000..cd816a0a7 --- /dev/null +++ b/tests/Builder/Expression/NotOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::NotExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ObjectToArrayOperatorTest.php b/tests/Builder/Expression/ObjectToArrayOperatorTest.php new file mode 100644 index 000000000..0abbde2ef --- /dev/null +++ b/tests/Builder/Expression/ObjectToArrayOperatorTest.php @@ -0,0 +1,53 @@ +assertSamePipeline(Pipelines::ObjectToArrayObjectToArrayExample, $pipeline); + } + + public function testObjectToArrayToSumNestedFields(): void + { + $pipeline = new Pipeline( + Stage::project( + warehouses: Expression::objectToArray( + Expression::objectFieldPath('instock'), + ), + ), + Stage::unwind( + Expression::arrayFieldPath('warehouses'), + ), + Stage::group( + _id: Expression::fieldPath('warehouses.k'), + total: Accumulator::sum( + Expression::fieldPath('warehouses.v'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ObjectToArrayObjectToArrayToSumNestedFields, $pipeline); + } +} diff --git a/tests/Builder/Expression/OrOperatorTest.php b/tests/Builder/Expression/OrOperatorTest.php new file mode 100644 index 000000000..feb544c1c --- /dev/null +++ b/tests/Builder/Expression/OrOperatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::OrExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/PercentileOperatorTest.php b/tests/Builder/Expression/PercentileOperatorTest.php new file mode 100644 index 000000000..9d1538bce --- /dev/null +++ b/tests/Builder/Expression/PercentileOperatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::PercentileUsePercentileInAProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/Pipelines.php b/tests/Builder/Expression/Pipelines.php new file mode 100644 index 000000000..e5bdd225e --- /dev/null +++ b/tests/Builder/Expression/Pipelines.php @@ -0,0 +1,6239 @@ +assertSamePipeline(Pipelines::PowExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/RadiansToDegreesOperatorTest.php b/tests/Builder/Expression/RadiansToDegreesOperatorTest.php new file mode 100644 index 000000000..b52375936 --- /dev/null +++ b/tests/Builder/Expression/RadiansToDegreesOperatorTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::RadiansToDegreesExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/RandOperatorTest.php b/tests/Builder/Expression/RandOperatorTest.php new file mode 100644 index 000000000..b4964df69 --- /dev/null +++ b/tests/Builder/Expression/RandOperatorTest.php @@ -0,0 +1,61 @@ +assertSamePipeline(Pipelines::RandGenerateRandomDataPoints, $pipeline); + } + + public function testSelectRandomItemsFromACollection(): void + { + $pipeline = new Pipeline( + Stage::match( + district: 3, + ), + Stage::match( + Query::expr( + Expression::lt( + 0.5, + Expression::rand(), + ), + ), + ), + Stage::project( + _id: 0, + name: 1, + registered: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::RandSelectRandomItemsFromACollection, $pipeline); + } +} diff --git a/tests/Builder/Expression/RangeOperatorTest.php b/tests/Builder/Expression/RangeOperatorTest.php new file mode 100644 index 000000000..be9a1a3a7 --- /dev/null +++ b/tests/Builder/Expression/RangeOperatorTest.php @@ -0,0 +1,36 @@ + Expression::range( + 0, + Expression::intFieldPath('distance'), + 25, + ), + ], + _id: 0, + city: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::RangeExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ReduceOperatorTest.php b/tests/Builder/Expression/ReduceOperatorTest.php new file mode 100644 index 000000000..ee1f70720 --- /dev/null +++ b/tests/Builder/Expression/ReduceOperatorTest.php @@ -0,0 +1,141 @@ +assertSamePipeline(Pipelines::ReduceArrayConcatenation, $pipeline); + } + + public function testComputingAMultipleReductions(): void + { + $pipeline = new Pipeline( + Stage::project( + results: Expression::reduce( + Expression::arrayFieldPath('arr'), + [], + object( + collapsed: Expression::concatArrays( + Expression::variable('value.collapsed'), + Expression::variable('this'), + ), + firstValues: Expression::concatArrays( + Expression::variable('value.firstValues'), + Expression::slice( + Expression::variable('this'), + 1, + ), + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReduceComputingAMultipleReductions, $pipeline); + } + + public function testDiscountedMerchandise(): void + { + $pipeline = new Pipeline( + Stage::project( + discountedPrice: Expression::reduce( + input: Expression::arrayFieldPath('discounts'), + initialValue: Expression::numberFieldPath('price'), + in: Expression::multiply( + Expression::variable('value'), + Expression::subtract( + 1, + Expression::variable('this'), + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReduceDiscountedMerchandise, $pipeline); + } + + public function testMultiplication(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::objectIdFieldPath('experimentId'), + probabilityArr: Accumulator::push( + Expression::fieldPath('probability'), + ), + ), + Stage::project( + description: 1, + results: Expression::reduce( + input: Expression::arrayFieldPath('probabilityArr'), + initialValue: 1, + in: Expression::multiply( + Expression::variable('value'), + Expression::variable('this'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReduceMultiplication, $pipeline); + } + + public function testStringConcatenation(): void + { + $pipeline = new Pipeline( + Stage::match( + hobbies: Query::gt([]), + ), + Stage::project( + name: 1, + bio: Expression::reduce( + input: Expression::arrayFieldPath('hobbies'), + initialValue: 'My hobbies include:', + in: Expression::concat( + Expression::variable('value'), + Expression::cond( + if: Expression::eq( + Expression::variable('value'), + 'My hobbies include:', + ), + then: ' ', + else: ', ', + ), + Expression::variable('this'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReduceStringConcatenation, $pipeline); + } +} diff --git a/tests/Builder/Expression/RegexFindAllOperatorTest.php b/tests/Builder/Expression/RegexFindAllOperatorTest.php new file mode 100644 index 000000000..df8286c11 --- /dev/null +++ b/tests/Builder/Expression/RegexFindAllOperatorTest.php @@ -0,0 +1,100 @@ +assertSamePipeline(Pipelines::RegexFindAllIOption, $pipeline); + } + + public function testRegexFindAllAndItsOptions(): void + { + $pipeline = new Pipeline( + Stage::addFields( + returnObject: Expression::regexFindAll( + input: Expression::stringFieldPath('description'), + regex: new Regex('line'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexFindAllRegexFindAllAndItsOptions, $pipeline); + } + + public function testUseCapturedGroupingsToParseUserName(): void + { + $pipeline = new Pipeline( + Stage::addFields( + names: Expression::regexFindAll( + input: Expression::stringFieldPath('comment'), + regex: new Regex('([a-z0-9_.+-]+)@[a-z0-9_.+-]+\\.[a-z0-9_.+-]+', 'i'), + ), + ), + Stage::set( + names: Expression::reduce( + input: Expression::arrayFieldPath('names.captures'), + initialValue: [], + in: Expression::concatArrays( + Expression::variable('value'), + Expression::variable('this'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexFindAllUseCapturedGroupingsToParseUserName, $pipeline); + } + + public function testUseRegexFindAllToParseEmailFromString(): void + { + $pipeline = new Pipeline( + Stage::addFields( + email: Expression::regexFindAll( + input: Expression::stringFieldPath('comment'), + regex: new Regex('[a-z0-9_.+-]+@[a-z0-9_.+-]+\\.[a-z0-9_.+-]+', 'i'), + ), + ), + Stage::set( + email: Expression::stringFieldPath('email.match'), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexFindAllUseRegexFindAllToParseEmailFromString, $pipeline); + } +} diff --git a/tests/Builder/Expression/RegexFindOperatorTest.php b/tests/Builder/Expression/RegexFindOperatorTest.php new file mode 100644 index 000000000..d2ed7b453 --- /dev/null +++ b/tests/Builder/Expression/RegexFindOperatorTest.php @@ -0,0 +1,59 @@ +assertSamePipeline(Pipelines::RegexFindIOption, $pipeline); + } + + public function testRegexFindAndItsOptions(): void + { + $pipeline = new Pipeline( + Stage::addFields( + returnObject: Expression::regexFind( + input: Expression::stringFieldPath('description'), + regex: new Regex('line'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexFindRegexFindAndItsOptions, $pipeline); + } +} diff --git a/tests/Builder/Expression/RegexMatchOperatorTest.php b/tests/Builder/Expression/RegexMatchOperatorTest.php new file mode 100644 index 000000000..55f39faf4 --- /dev/null +++ b/tests/Builder/Expression/RegexMatchOperatorTest.php @@ -0,0 +1,77 @@ +assertSamePipeline(Pipelines::RegexMatchIOption, $pipeline); + } + + public function testRegexMatchAndItsOptions(): void + { + $pipeline = new Pipeline( + Stage::addFields( + result: Expression::regexMatch( + input: Expression::stringFieldPath('description'), + regex: new Regex('line', ''), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexMatchRegexMatchAndItsOptions, $pipeline); + } + + public function testUseRegexMatchToCheckEmailAddress(): void + { + $pipeline = new Pipeline( + Stage::addFields( + category: Expression::cond( + if: Expression::regexMatch( + input: Expression::stringFieldPath('comment'), + regex: new Regex('[a-z0-9_.+-]+@mongodb.com', 'i'), + ), + then: 'Employee', + else: 'External', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexMatchUseRegexMatchToCheckEmailAddress, $pipeline); + } +} diff --git a/tests/Builder/Expression/ReplaceAllOperatorTest.php b/tests/Builder/Expression/ReplaceAllOperatorTest.php new file mode 100644 index 000000000..d2247daec --- /dev/null +++ b/tests/Builder/Expression/ReplaceAllOperatorTest.php @@ -0,0 +1,47 @@ +assertSamePipeline(Pipelines::ReplaceAllExample, $pipeline); + } + + public function testSupportRegexSearchString(): void + { + $pipeline = new Pipeline( + Stage::project( + item: Expression::replaceAll( + input: '123-456-7890', + find: new Regex('\d{3}'), + replacement: 'xxx', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceAllSupportRegexSearchString, $pipeline); + } +} diff --git a/tests/Builder/Expression/ReplaceOneOperatorTest.php b/tests/Builder/Expression/ReplaceOneOperatorTest.php new file mode 100644 index 000000000..90bf8082b --- /dev/null +++ b/tests/Builder/Expression/ReplaceOneOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::ReplaceOneExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ReverseArrayOperatorTest.php b/tests/Builder/Expression/ReverseArrayOperatorTest.php new file mode 100644 index 000000000..f620cb4cc --- /dev/null +++ b/tests/Builder/Expression/ReverseArrayOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::ReverseArrayExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/RoundOperatorTest.php b/tests/Builder/Expression/RoundOperatorTest.php new file mode 100644 index 000000000..758668818 --- /dev/null +++ b/tests/Builder/Expression/RoundOperatorTest.php @@ -0,0 +1,48 @@ +assertSamePipeline(Pipelines::RoundExample, $pipeline); + } + + public function testRoundAverageRating(): void + { + $pipeline = new Pipeline( + Stage::project( + roundedAverageRating: Expression::avg( + Expression::round( + Expression::avg( + Expression::doubleFieldPath('averageRating'), + ), + 2, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RoundRoundAverageRating, $pipeline); + } +} diff --git a/tests/Builder/Expression/RtrimOperatorTest.php b/tests/Builder/Expression/RtrimOperatorTest.php new file mode 100644 index 000000000..077fe8dd4 --- /dev/null +++ b/tests/Builder/Expression/RtrimOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::RtrimExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SecondOperatorTest.php b/tests/Builder/Expression/SecondOperatorTest.php new file mode 100644 index 000000000..370686876 --- /dev/null +++ b/tests/Builder/Expression/SecondOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::SecondExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetDifferenceOperatorTest.php b/tests/Builder/Expression/SetDifferenceOperatorTest.php new file mode 100644 index 000000000..1a1fafcc4 --- /dev/null +++ b/tests/Builder/Expression/SetDifferenceOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::SetDifferenceExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetEqualsOperatorTest.php b/tests/Builder/Expression/SetEqualsOperatorTest.php new file mode 100644 index 000000000..e3974dad7 --- /dev/null +++ b/tests/Builder/Expression/SetEqualsOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::SetEqualsExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetFieldOperatorTest.php b/tests/Builder/Expression/SetFieldOperatorTest.php new file mode 100644 index 000000000..eb844334a --- /dev/null +++ b/tests/Builder/Expression/SetFieldOperatorTest.php @@ -0,0 +1,114 @@ +assertSamePipeline(Pipelines::SetFieldAddFieldsThatContainPeriods, $pipeline); + } + + public function testAddFieldsThatStartWithADollarSign(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::setField( + field: Expression::literal('$price'), + input: Expression::variable('ROOT'), + value: Expression::fieldPath('price'), + ), + ), + Stage::unset('price'), + ); + + $this->assertSamePipeline(Pipelines::SetFieldAddFieldsThatStartWithADollarSign, $pipeline); + } + + public function testRemoveFieldsThatContainPeriods(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::setField( + field: 'price.usd', + input: Expression::variable('ROOT'), + value: Expression::variable('REMOVE'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetFieldRemoveFieldsThatContainPeriods, $pipeline); + } + + public function testRemoveFieldsThatStartWithADollarSign(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::setField( + field: Expression::literal('$price'), + input: Expression::variable('ROOT'), + value: Expression::variable('REMOVE'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetFieldRemoveFieldsThatStartWithADollarSign, $pipeline); + } + + public function testUpdateFieldsThatContainPeriods(): void + { + $pipeline = new Pipeline( + Stage::match( + _id: 1, + ), + Stage::replaceWith( + Expression::setField( + field: 'price.usd', + input: Expression::variable('ROOT'), + value: 49.99, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetFieldUpdateFieldsThatContainPeriods, $pipeline); + } + + public function testUpdateFieldsThatStartWithADollarSign(): void + { + $pipeline = new Pipeline( + Stage::match( + _id: 1, + ), + Stage::replaceWith( + Expression::setField( + field: Expression::literal('$price'), + input: Expression::variable('ROOT'), + value: 49.99, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetFieldUpdateFieldsThatStartWithADollarSign, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetIntersectionOperatorTest.php b/tests/Builder/Expression/SetIntersectionOperatorTest.php new file mode 100644 index 000000000..7bce31d1c --- /dev/null +++ b/tests/Builder/Expression/SetIntersectionOperatorTest.php @@ -0,0 +1,55 @@ +assertSamePipeline(Pipelines::SetIntersectionElementsArrayExample, $pipeline); + } + + public function testRetrieveDocumentsForRolesGrantedToTheCurrentUser(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::not( + Expression::eq( + Expression::setIntersection( + Expression::arrayFieldPath('allowedRoles'), + Expression::variable('USER_ROLES.role'), + ), + [], + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetIntersectionRetrieveDocumentsForRolesGrantedToTheCurrentUser, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetIsSubsetOperatorTest.php b/tests/Builder/Expression/SetIsSubsetOperatorTest.php new file mode 100644 index 000000000..b54e6bde6 --- /dev/null +++ b/tests/Builder/Expression/SetIsSubsetOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::SetIsSubsetExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SetUnionOperatorTest.php b/tests/Builder/Expression/SetUnionOperatorTest.php new file mode 100644 index 000000000..56bf8694c --- /dev/null +++ b/tests/Builder/Expression/SetUnionOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::SetUnionExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SinOperatorTest.php b/tests/Builder/Expression/SinOperatorTest.php new file mode 100644 index 000000000..5a585f3c9 --- /dev/null +++ b/tests/Builder/Expression/SinOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::SinExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SinhOperatorTest.php b/tests/Builder/Expression/SinhOperatorTest.php new file mode 100644 index 000000000..5ba2241f3 --- /dev/null +++ b/tests/Builder/Expression/SinhOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::SinhExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SizeOperatorTest.php b/tests/Builder/Expression/SizeOperatorTest.php new file mode 100644 index 000000000..edd2c6cb4 --- /dev/null +++ b/tests/Builder/Expression/SizeOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::SizeExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SliceOperatorTest.php b/tests/Builder/Expression/SliceOperatorTest.php new file mode 100644 index 000000000..b6326b051 --- /dev/null +++ b/tests/Builder/Expression/SliceOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::SliceExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SortArrayOperatorTest.php b/tests/Builder/Expression/SortArrayOperatorTest.php new file mode 100644 index 000000000..a2b738f38 --- /dev/null +++ b/tests/Builder/Expression/SortArrayOperatorTest.php @@ -0,0 +1,116 @@ +assertSamePipeline(Pipelines::SortArraySortAnArrayOfIntegers, $pipeline); + } + + public function testSortOnAField(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + result: Expression::sortArray( + input: Expression::arrayFieldPath('team'), + // @todo This object should be typed as "sort spec" + sortBy: object( + name: Sort::Asc, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SortArraySortOnAField, $pipeline); + } + + public function testSortOnASubfield(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + result: Expression::sortArray( + input: Expression::arrayFieldPath('team'), + sortBy: [ + 'address.city' => Sort::Desc, + ], + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SortArraySortOnASubfield, $pipeline); + } + + public function testSortOnMixedTypeFields(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + result: Expression::sortArray( + input: [ + 20, + 4, + object(a: 'Free'), + 6, + 21, + 5, + 'Gratis', + ['a' => null], + object(a: object(sale: true, price: 19)), + new Decimal128('10.23'), + ['a' => 'On sale'], + ], + sortBy: Sort::Asc, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SortArraySortOnMixedTypeFields, $pipeline); + } + + public function testSortOnMultipleFields(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + result: Expression::sortArray( + input: Expression::arrayFieldPath('team'), + // @todo This array should be typed as "sort spec" + sortBy: object( + age: Sort::Desc, + name: Sort::Asc, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SortArraySortOnMultipleFields, $pipeline); + } +} diff --git a/tests/Builder/Expression/SplitOperatorTest.php b/tests/Builder/Expression/SplitOperatorTest.php new file mode 100644 index 000000000..ee997d539 --- /dev/null +++ b/tests/Builder/Expression/SplitOperatorTest.php @@ -0,0 +1,67 @@ +assertSamePipeline(Pipelines::SplitExample, $pipeline); + } + + public function testSupportRegexDelimiter(): void + { + $pipeline = new Pipeline( + Stage::project( + split: Expression::split( + string: 'abc', + delimiter: new Regex('b'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SplitSupportRegexDelimiter, $pipeline); + } +} diff --git a/tests/Builder/Expression/SqrtOperatorTest.php b/tests/Builder/Expression/SqrtOperatorTest.php new file mode 100644 index 000000000..de33969f5 --- /dev/null +++ b/tests/Builder/Expression/SqrtOperatorTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::SqrtExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/StdDevPopOperatorTest.php b/tests/Builder/Expression/StdDevPopOperatorTest.php new file mode 100644 index 000000000..fdff25f99 --- /dev/null +++ b/tests/Builder/Expression/StdDevPopOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::StdDevPopUseInProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/StrLenBytesOperatorTest.php b/tests/Builder/Expression/StrLenBytesOperatorTest.php new file mode 100644 index 000000000..1c465b672 --- /dev/null +++ b/tests/Builder/Expression/StrLenBytesOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::StrLenBytesSingleByteAndMultibyteCharacterSet, $pipeline); + } +} diff --git a/tests/Builder/Expression/StrLenCPOperatorTest.php b/tests/Builder/Expression/StrLenCPOperatorTest.php new file mode 100644 index 000000000..78e995479 --- /dev/null +++ b/tests/Builder/Expression/StrLenCPOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::StrLenCPSingleByteAndMultibyteCharacterSet, $pipeline); + } +} diff --git a/tests/Builder/Expression/StrcasecmpOperatorTest.php b/tests/Builder/Expression/StrcasecmpOperatorTest.php new file mode 100644 index 000000000..543ac54d0 --- /dev/null +++ b/tests/Builder/Expression/StrcasecmpOperatorTest.php @@ -0,0 +1,31 @@ +assertSamePipeline(Pipelines::StrcasecmpExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SubstrBytesOperatorTest.php b/tests/Builder/Expression/SubstrBytesOperatorTest.php new file mode 100644 index 000000000..34d4d6f61 --- /dev/null +++ b/tests/Builder/Expression/SubstrBytesOperatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::SubstrBytesSingleByteAndMultibyteCharacterSet, $pipeline); + } + + public function testSingleByteCharacterSet(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + yearSubstring: Expression::substrBytes( + Expression::stringFieldPath('quarter'), + 0, + 2, + ), + quarterSubtring: Expression::substrBytes( + Expression::stringFieldPath('quarter'), + 2, + Expression::subtract( + Expression::strLenBytes( + Expression::stringFieldPath('quarter'), + ), + 2, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SubstrBytesSingleByteCharacterSet, $pipeline); + } +} diff --git a/tests/Builder/Expression/SubstrCPOperatorTest.php b/tests/Builder/Expression/SubstrCPOperatorTest.php new file mode 100644 index 000000000..9a24b1fa9 --- /dev/null +++ b/tests/Builder/Expression/SubstrCPOperatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::SubstrCPSingleByteAndMultibyteCharacterSet, $pipeline); + } + + public function testSingleByteCharacterSet(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + yearSubstring: Expression::substrCP( + Expression::stringFieldPath('quarter'), + 0, + 2, + ), + quarterSubtring: Expression::substrCP( + Expression::stringFieldPath('quarter'), + 2, + Expression::subtract( + Expression::strLenCP( + Expression::stringFieldPath('quarter'), + ), + 2, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SubstrCPSingleByteCharacterSet, $pipeline); + } +} diff --git a/tests/Builder/Expression/SubstrOperatorTest.php b/tests/Builder/Expression/SubstrOperatorTest.php new file mode 100644 index 000000000..f2a6f432d --- /dev/null +++ b/tests/Builder/Expression/SubstrOperatorTest.php @@ -0,0 +1,37 @@ +assertSamePipeline(Pipelines::SubstrExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/SubtractOperatorTest.php b/tests/Builder/Expression/SubtractOperatorTest.php new file mode 100644 index 000000000..95b691a95 --- /dev/null +++ b/tests/Builder/Expression/SubtractOperatorTest.php @@ -0,0 +1,64 @@ +assertSamePipeline(Pipelines::SubtractSubtractMillisecondsFromADate, $pipeline); + } + + public function testSubtractNumbers(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + total: Expression::subtract( + Expression::add( + Expression::numberFieldPath('price'), + Expression::numberFieldPath('fee'), + ), + Expression::numberFieldPath('discount'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SubtractSubtractNumbers, $pipeline); + } + + public function testSubtractTwoDates(): void + { + $pipeline = new Pipeline( + Stage::project( + item: 1, + dateDifference: Expression::subtract( + Expression::variable('NOW'), + Expression::dateFieldPath('date'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SubtractSubtractTwoDates, $pipeline); + } +} diff --git a/tests/Builder/Expression/SumOperatorTest.php b/tests/Builder/Expression/SumOperatorTest.php new file mode 100644 index 000000000..923162a20 --- /dev/null +++ b/tests/Builder/Expression/SumOperatorTest.php @@ -0,0 +1,36 @@ +assertSamePipeline(Pipelines::SumUseInProjectStage, $pipeline); + } +} diff --git a/tests/Builder/Expression/SwitchOperatorTest.php b/tests/Builder/Expression/SwitchOperatorTest.php new file mode 100644 index 000000000..1d288ea7a --- /dev/null +++ b/tests/Builder/Expression/SwitchOperatorTest.php @@ -0,0 +1,67 @@ +assertSamePipeline(Pipelines::SwitchExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/TanOperatorTest.php b/tests/Builder/Expression/TanOperatorTest.php new file mode 100644 index 000000000..34b210d0a --- /dev/null +++ b/tests/Builder/Expression/TanOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::TanExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/TanhOperatorTest.php b/tests/Builder/Expression/TanhOperatorTest.php new file mode 100644 index 000000000..4ae799e55 --- /dev/null +++ b/tests/Builder/Expression/TanhOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::TanhExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToBoolOperatorTest.php b/tests/Builder/Expression/ToBoolOperatorTest.php new file mode 100644 index 000000000..2b1aad9f9 --- /dev/null +++ b/tests/Builder/Expression/ToBoolOperatorTest.php @@ -0,0 +1,50 @@ +assertSamePipeline(Pipelines::ToBoolExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToDateOperatorTest.php b/tests/Builder/Expression/ToDateOperatorTest.php new file mode 100644 index 000000000..9b03b1121 --- /dev/null +++ b/tests/Builder/Expression/ToDateOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ToDateExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToDecimalOperatorTest.php b/tests/Builder/Expression/ToDecimalOperatorTest.php new file mode 100644 index 000000000..d12a72755 --- /dev/null +++ b/tests/Builder/Expression/ToDecimalOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::ToDecimalExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToDoubleOperatorTest.php b/tests/Builder/Expression/ToDoubleOperatorTest.php new file mode 100644 index 000000000..d0819e79d --- /dev/null +++ b/tests/Builder/Expression/ToDoubleOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ToDoubleExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToHashedIndexKeyOperatorTest.php b/tests/Builder/Expression/ToHashedIndexKeyOperatorTest.php new file mode 100644 index 000000000..8bb4bb852 --- /dev/null +++ b/tests/Builder/Expression/ToHashedIndexKeyOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::ToHashedIndexKeyExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToIntOperatorTest.php b/tests/Builder/Expression/ToIntOperatorTest.php new file mode 100644 index 000000000..cc88ca63d --- /dev/null +++ b/tests/Builder/Expression/ToIntOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::ToIntExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToLongOperatorTest.php b/tests/Builder/Expression/ToLongOperatorTest.php new file mode 100644 index 000000000..53c6f1f07 --- /dev/null +++ b/tests/Builder/Expression/ToLongOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ToLongExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToLowerOperatorTest.php b/tests/Builder/Expression/ToLowerOperatorTest.php new file mode 100644 index 000000000..6f8cc154d --- /dev/null +++ b/tests/Builder/Expression/ToLowerOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::ToLowerExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToObjectIdOperatorTest.php b/tests/Builder/Expression/ToObjectIdOperatorTest.php new file mode 100644 index 000000000..396507241 --- /dev/null +++ b/tests/Builder/Expression/ToObjectIdOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ToObjectIdExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToStringOperatorTest.php b/tests/Builder/Expression/ToStringOperatorTest.php new file mode 100644 index 000000000..d33cb2e3b --- /dev/null +++ b/tests/Builder/Expression/ToStringOperatorTest.php @@ -0,0 +1,33 @@ +assertSamePipeline(Pipelines::ToStringExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ToUpperOperatorTest.php b/tests/Builder/Expression/ToUpperOperatorTest.php new file mode 100644 index 000000000..529892ea1 --- /dev/null +++ b/tests/Builder/Expression/ToUpperOperatorTest.php @@ -0,0 +1,32 @@ +assertSamePipeline(Pipelines::ToUpperExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/TrimOperatorTest.php b/tests/Builder/Expression/TrimOperatorTest.php new file mode 100644 index 000000000..81c2e2353 --- /dev/null +++ b/tests/Builder/Expression/TrimOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::TrimExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/TruncOperatorTest.php b/tests/Builder/Expression/TruncOperatorTest.php new file mode 100644 index 000000000..45f1826be --- /dev/null +++ b/tests/Builder/Expression/TruncOperatorTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::TruncExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/TsIncrementOperatorTest.php b/tests/Builder/Expression/TsIncrementOperatorTest.php new file mode 100644 index 000000000..092f57660 --- /dev/null +++ b/tests/Builder/Expression/TsIncrementOperatorTest.php @@ -0,0 +1,53 @@ +assertSamePipeline(Pipelines::TsIncrementObtainTheIncrementingOrdinalFromATimestampField, $pipeline); + } + + public function testUseTsSecondInAChangeStreamCursorToMonitorCollectionChanges(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::eq( + Expression::mod( + Expression::tsIncrement( + Expression::timestampFieldPath('clusterTime'), + ), + 2, + ), + 0, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TsIncrementUseTsSecondInAChangeStreamCursorToMonitorCollectionChanges, $pipeline); + } +} diff --git a/tests/Builder/Expression/TsSecondOperatorTest.php b/tests/Builder/Expression/TsSecondOperatorTest.php new file mode 100644 index 000000000..16f5b59e6 --- /dev/null +++ b/tests/Builder/Expression/TsSecondOperatorTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::TsSecondObtainTheNumberOfSecondsFromATimestampField, $pipeline); + } + + public function testUseTsSecondInAChangeStreamCursorToMonitorCollectionChanges(): void + { + $pipeline = new Pipeline( + Stage::addFields( + clusterTimeSeconds: Expression::tsSecond( + Expression::timestampFieldPath('clusterTime'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TsSecondUseTsSecondInAChangeStreamCursorToMonitorCollectionChanges, $pipeline); + } +} diff --git a/tests/Builder/Expression/TypeOperatorTest.php b/tests/Builder/Expression/TypeOperatorTest.php new file mode 100644 index 000000000..797536c27 --- /dev/null +++ b/tests/Builder/Expression/TypeOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::TypeExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/UnsetFieldOperatorTest.php b/tests/Builder/Expression/UnsetFieldOperatorTest.php new file mode 100644 index 000000000..fba80d191 --- /dev/null +++ b/tests/Builder/Expression/UnsetFieldOperatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::UnsetFieldRemoveASubfield, $pipeline); + } + + public function testRemoveFieldsThatContainPeriods(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::unsetField( + field: 'price.usd', + input: Expression::variable('ROOT'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::UnsetFieldRemoveFieldsThatContainPeriods, $pipeline); + } + + public function testRemoveFieldsThatStartWithADollarSign(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::unsetField( + field: Expression::literal('$price'), + input: Expression::variable('ROOT'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::UnsetFieldRemoveFieldsThatStartWithADollarSign, $pipeline); + } +} diff --git a/tests/Builder/Expression/WeekOperatorTest.php b/tests/Builder/Expression/WeekOperatorTest.php new file mode 100644 index 000000000..334cc26ff --- /dev/null +++ b/tests/Builder/Expression/WeekOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::WeekExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/YearOperatorTest.php b/tests/Builder/Expression/YearOperatorTest.php new file mode 100644 index 000000000..a8d02fea6 --- /dev/null +++ b/tests/Builder/Expression/YearOperatorTest.php @@ -0,0 +1,29 @@ +assertSamePipeline(Pipelines::YearExample, $pipeline); + } +} diff --git a/tests/Builder/Expression/ZipOperatorTest.php b/tests/Builder/Expression/ZipOperatorTest.php new file mode 100644 index 000000000..d0039c5d6 --- /dev/null +++ b/tests/Builder/Expression/ZipOperatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::ZipFilteringAndPreservingIndexes, $pipeline); + } + + public function testMatrixTransposition(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: false, + transposed: Expression::zip([ + Expression::arrayElemAt(Expression::arrayFieldPath('matrix'), 0), + Expression::arrayElemAt(Expression::arrayFieldPath('matrix'), 1), + Expression::arrayElemAt(Expression::arrayFieldPath('matrix'), 2), + ]), + ), + ); + + $this->assertSamePipeline(Pipelines::ZipMatrixTransposition, $pipeline); + } +} diff --git a/tests/Builder/FieldPathTest.php b/tests/Builder/FieldPathTest.php new file mode 100644 index 000000000..90ccef804 --- /dev/null +++ b/tests/Builder/FieldPathTest.php @@ -0,0 +1,73 @@ +assertSame('foo', $fieldPath->name); + $this->assertInstanceOf($resolveClass, $fieldPath); + $this->assertInstanceOf(FieldPathInterface::class, $fieldPath); + + // Ensure FieldPath resolves to any type + $this->assertTrue(is_subclass_of(Expression\FieldPath::class, $resolveClass), sprintf('%s instanceof %s', Expression\FieldPath::class, $resolveClass)); + } + + #[DataProvider('provideFieldPath')] + public function testRejectDollarPrefix(string $fieldPathClass): void + { + $this->expectException(InvalidArgumentException::class); + + Expression::{$fieldPathClass}('$foo'); + } + + public static function provideFieldPath(): Generator + { + yield 'double' => ['doubleFieldPath', Expression\ResolvesToDouble::class]; + yield 'string' => ['stringFieldPath', Expression\ResolvesToString::class]; + yield 'object' => ['objectFieldPath', Expression\ResolvesToObject::class]; + yield 'array' => ['arrayFieldPath', Expression\ResolvesToArray::class]; + yield 'binData' => ['binDataFieldPath', Expression\ResolvesToBinData::class]; + yield 'objectId' => ['objectIdFieldPath', Expression\ResolvesToObjectId::class]; + yield 'bool' => ['boolFieldPath', Expression\ResolvesToBool::class]; + yield 'date' => ['dateFieldPath', Expression\ResolvesToDate::class]; + yield 'null' => ['nullFieldPath', Expression\ResolvesToNull::class]; + yield 'regex' => ['regexFieldPath', Expression\ResolvesToRegex::class]; + yield 'javascript' => ['javascriptFieldPath', Expression\ResolvesToJavascript::class]; + yield 'int' => ['intFieldPath', Expression\ResolvesToInt::class]; + yield 'timestamp' => ['timestampFieldPath', Expression\ResolvesToTimestamp::class]; + yield 'long' => ['longFieldPath', Expression\ResolvesToLong::class]; + yield 'decimal' => ['decimalFieldPath', Expression\ResolvesToDecimal::class]; + yield 'number' => ['numberFieldPath', Expression\ResolvesToNumber::class]; + yield 'any' => ['fieldPath', Expression\ResolvesToAny::class]; + } + + public function testStringFieldPathAcceptedAsExpression(): void + { + $operator = Expression::abs('$foo'); + + $this->assertSame('$foo', $operator->value); + } + + public function testNonDollarPrefixedStringRejected(): void + { + self::expectException(InvalidArgumentException::class); + + Expression::abs('foo'); + } +} diff --git a/tests/Builder/FluentPipelineFactoryTest.php b/tests/Builder/FluentPipelineFactoryTest.php new file mode 100644 index 000000000..e1d84a2e4 --- /dev/null +++ b/tests/Builder/FluentPipelineFactoryTest.php @@ -0,0 +1,34 @@ +match(x: Query::eq(1)) + ->project(_id: false, x: true) + ->sort(x: Sort::Asc) + ->getPipeline(); + + $expected = <<<'json' + [ + {"$match": {"x": {"$eq": {"$numberInt": "1"}}}}, + {"$project": {"_id": false, "x": true}}, + {"$sort": {"x": {"$numberInt": "1"}}} + ] + json; + + $this->assertSamePipeline($expected, $pipeline); + } +} diff --git a/tests/Builder/PipelineTest.php b/tests/Builder/PipelineTest.php new file mode 100644 index 000000000..b274fecab --- /dev/null +++ b/tests/Builder/PipelineTest.php @@ -0,0 +1,75 @@ +assertSame([], iterator_to_array($pipeline)); + } + + public function testFromArray(): void + { + $pipeline = new Pipeline( + ['$match' => ['tag' => 'foo']], + [ + ['$sort' => ['_id' => 1]], + ['$skip' => 10], + ], + ['$limit' => 5], + ); + + $expected = [ + ['$match' => ['tag' => 'foo']], + ['$sort' => ['_id' => 1]], + ['$skip' => 10], + ['$limit' => 5], + ]; + + $this->assertSame($expected, iterator_to_array($pipeline)); + } + + public function testMergingPipeline(): void + { + $stages = array_map( + fn (int $i) => $this->createMock(StageInterface::class), + range(0, 7), + ); + + $pipeline = new Pipeline( + $stages[0], + $stages[1], + new Pipeline($stages[2], $stages[3]), + [$stages[4], $stages[5]], + new Pipeline($stages[6]), + [$stages[7]], + ); + + $this->assertSame($stages, iterator_to_array($pipeline)); + } + + public function testRejectNamedArguments(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Named arguments are not supported for pipelines'); + + new Pipeline( + $this->createMock(StageInterface::class), + foo: $this->createMock(StageInterface::class), + ); + } +} diff --git a/tests/Builder/PipelineTestCase.php b/tests/Builder/PipelineTestCase.php new file mode 100644 index 000000000..1975a4ab3 --- /dev/null +++ b/tests/Builder/PipelineTestCase.php @@ -0,0 +1,31 @@ +value; + } + + // BSON Documents doesn't support top-level arrays. + $expected = '{"pipeline":' . $expectedJson . '}'; + + $codec = new BuilderEncoder(); + $actual = $codec->encode($pipeline); + // Normalize with BSON round-trip + $actual = Document::fromPHP(['pipeline' => $actual])->toCanonicalExtendedJSON(); + + self::assertJsonStringEqualsJsonString($expected, $actual); + } +} diff --git a/tests/Builder/Query/AllOperatorTest.php b/tests/Builder/Query/AllOperatorTest.php new file mode 100644 index 000000000..ce0dbe0c8 --- /dev/null +++ b/tests/Builder/Query/AllOperatorTest.php @@ -0,0 +1,51 @@ +assertSamePipeline(Pipelines::AllUseAllToMatchValues, $pipeline); + } + + public function testUseAllWithElemMatch(): void + { + $pipeline = new Pipeline( + Stage::match( + qty: Query::all( + Query::elemMatch( + Query::query( + size: 'M', + num: Query::gt(50), + ), + ), + Query::elemMatch( + Query::query( + num: 100, + color: 'green', + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AllUseAllWithElemMatch, $pipeline); + } +} diff --git a/tests/Builder/Query/AndOperatorTest.php b/tests/Builder/Query/AndOperatorTest.php new file mode 100644 index 000000000..778aa3f97 --- /dev/null +++ b/tests/Builder/Query/AndOperatorTest.php @@ -0,0 +1,62 @@ +assertSamePipeline(Pipelines::AndANDQueriesWithMultipleExpressionsSpecifyingTheSameField, $pipeline); + } + + public function testANDQueriesWithMultipleExpressionsSpecifyingTheSameOperator(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::and( + Query::or( + Query::query( + qty: Query::lt(10), + ), + Query::query( + qty: Query::gt(50), + ), + ), + Query::or( + Query::query( + sale: true, + ), + Query::query( + price: Query::lt(5), + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AndANDQueriesWithMultipleExpressionsSpecifyingTheSameOperator, $pipeline); + } +} diff --git a/tests/Builder/Query/BitsAllClearOperatorTest.php b/tests/Builder/Query/BitsAllClearOperatorTest.php new file mode 100644 index 000000000..ccc670fba --- /dev/null +++ b/tests/Builder/Query/BitsAllClearOperatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::BitsAllClearBinDataBitmask, $pipeline); + } + + public function testBitPositionArray(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAllClear([1, 5]), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAllClearBitPositionArray, $pipeline); + } + + public function testIntegerBitmask(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAllClear(35), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAllClearIntegerBitmask, $pipeline); + } +} diff --git a/tests/Builder/Query/BitsAllSetOperatorTest.php b/tests/Builder/Query/BitsAllSetOperatorTest.php new file mode 100644 index 000000000..c1eaa0e47 --- /dev/null +++ b/tests/Builder/Query/BitsAllSetOperatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::BitsAllSetBinDataBitmask, $pipeline); + } + + public function testBitPositionArray(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAllSet([1, 5]), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAllSetBitPositionArray, $pipeline); + } + + public function testIntegerBitmask(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAllSet(50), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAllSetIntegerBitmask, $pipeline); + } +} diff --git a/tests/Builder/Query/BitsAnyClearOperatorTest.php b/tests/Builder/Query/BitsAnyClearOperatorTest.php new file mode 100644 index 000000000..3f32fdb86 --- /dev/null +++ b/tests/Builder/Query/BitsAnyClearOperatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::BitsAnyClearBinDataBitmask, $pipeline); + } + + public function testBitPositionArray(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAnyClear([1, 5]), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAnyClearBitPositionArray, $pipeline); + } + + public function testIntegerBitmask(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAnyClear(35), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAnyClearIntegerBitmask, $pipeline); + } +} diff --git a/tests/Builder/Query/BitsAnySetOperatorTest.php b/tests/Builder/Query/BitsAnySetOperatorTest.php new file mode 100644 index 000000000..1d90c6893 --- /dev/null +++ b/tests/Builder/Query/BitsAnySetOperatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::BitsAnySetBinDataBitmask, $pipeline); + } + + public function testBitPositionArray(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAnySet([1, 5]), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAnySetBitPositionArray, $pipeline); + } + + public function testIntegerBitmask(): void + { + $pipeline = new Pipeline( + Stage::match( + a: Query::bitsAnySet(35), + ), + ); + + $this->assertSamePipeline(Pipelines::BitsAnySetIntegerBitmask, $pipeline); + } +} diff --git a/tests/Builder/Query/CommentOperatorTest.php b/tests/Builder/Query/CommentOperatorTest.php new file mode 100644 index 000000000..4b94bb56b --- /dev/null +++ b/tests/Builder/Query/CommentOperatorTest.php @@ -0,0 +1,39 @@ +assertSamePipeline(Pipelines::CommentAttachACommentToAnAggregationExpression, $pipeline); + } +} diff --git a/tests/Builder/Query/ElemMatchOperatorTest.php b/tests/Builder/Query/ElemMatchOperatorTest.php new file mode 100644 index 000000000..b179df446 --- /dev/null +++ b/tests/Builder/Query/ElemMatchOperatorTest.php @@ -0,0 +1,92 @@ +assertSamePipeline(Pipelines::ElemMatchArrayOfEmbeddedDocuments, $pipeline); + } + + public function testElementMatch(): void + { + $pipeline = new Pipeline( + Stage::match( + results: Query::elemMatch( + Query::fieldQuery( + Query::gte(80), + Query::lt(85), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ElemMatchElementMatch, $pipeline); + } + + public function testSingleFieldOperator(): void + { + $pipeline = new Pipeline( + Stage::match( + results: Query::elemMatch( + Query::gt(10), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ElemMatchSingleFieldOperator, $pipeline); + } + + public function testSingleQueryCondition(): void + { + $pipeline = new Pipeline( + Stage::match( + results: Query::elemMatch( + Query::query( + product: Query::ne('xyz'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ElemMatchSingleQueryCondition, $pipeline); + } + + public function testUsingOrWithElemMatch(): void + { + $pipeline = new Pipeline( + Stage::match( + game: Query::elemMatch( + Query::or( + Query::query(score: Query::gt(10)), + Query::query(score: Query::lt(5)), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ElemMatchUsingOrWithElemMatch, $pipeline); + } +} diff --git a/tests/Builder/Query/EqOperatorTest.php b/tests/Builder/Query/EqOperatorTest.php new file mode 100644 index 000000000..813c780ac --- /dev/null +++ b/tests/Builder/Query/EqOperatorTest.php @@ -0,0 +1,70 @@ +assertSamePipeline(Pipelines::EqEqualsASpecifiedValue, $pipeline); + } + + public function testEqualsAnArrayValue(): void + { + $pipeline = new Pipeline( + Stage::match( + tags: Query::eq(['A', 'B']), + ), + ); + + $this->assertSamePipeline(Pipelines::EqEqualsAnArrayValue, $pipeline); + } + + public function testFieldInEmbeddedDocumentEqualsAValue(): void + { + $pipeline = new Pipeline( + Stage::match( + ...['item.name' => Query::eq('ab')], + ), + ); + + $this->assertSamePipeline(Pipelines::EqFieldInEmbeddedDocumentEqualsAValue, $pipeline); + } + + public function testRegexMatchBehaviour(): void + { + $pipeline = new Pipeline( + Stage::match( + company: 'MongoDB', + ), + Stage::match( + company: Query::eq('MongoDB'), + ), + Stage::match( + company: new Regex('^MongoDB'), + ), + Stage::match( + company: Query::eq(new Regex('^MongoDB')), + ), + ); + + $this->assertSamePipeline(Pipelines::EqRegexMatchBehaviour, $pipeline); + } +} diff --git a/tests/Builder/Query/ExistsOperatorTest.php b/tests/Builder/Query/ExistsOperatorTest.php new file mode 100644 index 000000000..27ebeec77 --- /dev/null +++ b/tests/Builder/Query/ExistsOperatorTest.php @@ -0,0 +1,52 @@ +assertSamePipeline(Pipelines::ExistsExistsAndNotEqualTo, $pipeline); + } + + public function testMissingField(): void + { + $pipeline = new Pipeline( + Stage::match( + qty: Query::exists(false), + ), + ); + + $this->assertSamePipeline(Pipelines::ExistsMissingField, $pipeline); + } + + public function testNullValues(): void + { + $pipeline = new Pipeline( + Stage::match( + qty: Query::exists(), + ), + ); + + $this->assertSamePipeline(Pipelines::ExistsNullValues, $pipeline); + } +} diff --git a/tests/Builder/Query/ExprOperatorTest.php b/tests/Builder/Query/ExprOperatorTest.php new file mode 100644 index 000000000..3b87fc35b --- /dev/null +++ b/tests/Builder/Query/ExprOperatorTest.php @@ -0,0 +1,58 @@ +assertSamePipeline(Pipelines::ExprCompareTwoFieldsFromASingleDocument, $pipeline); + } + + public function testUsingExprWithConditionalStatements(): void + { + $discountedPrice = Expression::cond( + if: Expression::gte(Expression::fieldPath('qty'), 100), + then: Expression::multiply( + Expression::numberfieldPath('price'), + 0.5, + ), + else: Expression::multiply( + Expression::numberfieldPath('price'), + 0.75, + ), + ); + + $pipeline = new Pipeline( + Stage::match( + Query::expr( + Expression::lt($discountedPrice, 5), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ExprUsingExprWithConditionalStatements, $pipeline); + } +} diff --git a/tests/Builder/Query/GeoIntersectsOperatorTest.php b/tests/Builder/Query/GeoIntersectsOperatorTest.php new file mode 100644 index 000000000..92fc88c83 --- /dev/null +++ b/tests/Builder/Query/GeoIntersectsOperatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::GeoIntersectsIntersectsABigPolygon, $pipeline); + } + + public function testIntersectsAPolygon(): void + { + $pipeline = new Pipeline( + Stage::match( + loc: Query::geoIntersects( + Query::geometry( + type: 'Polygon', + coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GeoIntersectsIntersectsAPolygon, $pipeline); + } +} diff --git a/tests/Builder/Query/GeoWithinOperatorTest.php b/tests/Builder/Query/GeoWithinOperatorTest.php new file mode 100644 index 000000000..d510b6cea --- /dev/null +++ b/tests/Builder/Query/GeoWithinOperatorTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::GeoWithinWithinABigPolygon, $pipeline); + } + + public function testWithinAPolygon(): void + { + $pipeline = new Pipeline( + Stage::match( + loc: Query::geoWithin( + Query::geometry( + type: 'Polygon', + coordinates: [[[0, 0], [3, 6], [6, 1], [0, 0]]], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GeoWithinWithinAPolygon, $pipeline); + } +} diff --git a/tests/Builder/Query/GtOperatorTest.php b/tests/Builder/Query/GtOperatorTest.php new file mode 100644 index 000000000..c2838d00c --- /dev/null +++ b/tests/Builder/Query/GtOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::GtMatchDocumentFields, $pipeline); + } +} diff --git a/tests/Builder/Query/GteOperatorTest.php b/tests/Builder/Query/GteOperatorTest.php new file mode 100644 index 000000000..48198adf8 --- /dev/null +++ b/tests/Builder/Query/GteOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::GteMatchDocumentFields, $pipeline); + } +} diff --git a/tests/Builder/Query/InOperatorTest.php b/tests/Builder/Query/InOperatorTest.php new file mode 100644 index 000000000..25a9001fd --- /dev/null +++ b/tests/Builder/Query/InOperatorTest.php @@ -0,0 +1,39 @@ +assertSamePipeline(Pipelines::InUseTheInOperatorToMatchValuesInAnArray, $pipeline); + } + + public function testUseTheInOperatorWithARegularExpression(): void + { + $pipeline = new Pipeline( + Stage::match( + tags: Query::in([new Regex('^be'), new Regex('^st')]), + ), + ); + + $this->assertSamePipeline(Pipelines::InUseTheInOperatorWithARegularExpression, $pipeline); + } +} diff --git a/tests/Builder/Query/JsonSchemaOperatorTest.php b/tests/Builder/Query/JsonSchemaOperatorTest.php new file mode 100644 index 000000000..0d4b1a92f --- /dev/null +++ b/tests/Builder/Query/JsonSchemaOperatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::JsonSchemaExample, $pipeline); + } +} diff --git a/tests/Builder/Query/LtOperatorTest.php b/tests/Builder/Query/LtOperatorTest.php new file mode 100644 index 000000000..119f08a4b --- /dev/null +++ b/tests/Builder/Query/LtOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::LtMatchDocumentFields, $pipeline); + } +} diff --git a/tests/Builder/Query/LteOperatorTest.php b/tests/Builder/Query/LteOperatorTest.php new file mode 100644 index 000000000..5a0d5da7f --- /dev/null +++ b/tests/Builder/Query/LteOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::LteMatchDocumentFields, $pipeline); + } +} diff --git a/tests/Builder/Query/ModOperatorTest.php b/tests/Builder/Query/ModOperatorTest.php new file mode 100644 index 000000000..e4bbf90dc --- /dev/null +++ b/tests/Builder/Query/ModOperatorTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::ModFloatingPointArguments, $pipeline); + } + + public function testUseModToSelectDocuments(): void + { + $pipeline = new Pipeline( + Stage::match( + qty: Query::mod(4, 0), + ), + ); + + $this->assertSamePipeline(Pipelines::ModUseModToSelectDocuments, $pipeline); + } +} diff --git a/tests/Builder/Query/NeOperatorTest.php b/tests/Builder/Query/NeOperatorTest.php new file mode 100644 index 000000000..dd6196293 --- /dev/null +++ b/tests/Builder/Query/NeOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::NeMatchDocumentFields, $pipeline); + } +} diff --git a/tests/Builder/Query/NearOperatorTest.php b/tests/Builder/Query/NearOperatorTest.php new file mode 100644 index 000000000..3ca7a16c7 --- /dev/null +++ b/tests/Builder/Query/NearOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::NearQueryOnGeoJSONData, $pipeline); + } +} diff --git a/tests/Builder/Query/NearSphereOperatorTest.php b/tests/Builder/Query/NearSphereOperatorTest.php new file mode 100644 index 000000000..40ef97a6c --- /dev/null +++ b/tests/Builder/Query/NearSphereOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::NearSphereSpecifyCenterPointUsingGeoJSON, $pipeline); + } +} diff --git a/tests/Builder/Query/NinOperatorTest.php b/tests/Builder/Query/NinOperatorTest.php new file mode 100644 index 000000000..04799b791 --- /dev/null +++ b/tests/Builder/Query/NinOperatorTest.php @@ -0,0 +1,38 @@ +assertSamePipeline(Pipelines::NinSelectOnElementsNotInAnArray, $pipeline); + } + + public function testSelectOnUnmatchingDocuments(): void + { + $pipeline = new Pipeline( + Stage::match( + quantity: Query::nin([5, 15]), + ), + ); + + $this->assertSamePipeline(Pipelines::NinSelectOnUnmatchingDocuments, $pipeline); + } +} diff --git a/tests/Builder/Query/NorOperatorTest.php b/tests/Builder/Query/NorOperatorTest.php new file mode 100644 index 000000000..db03502dc --- /dev/null +++ b/tests/Builder/Query/NorOperatorTest.php @@ -0,0 +1,79 @@ +assertSamePipeline(Pipelines::NorAdditionalComparisons, $pipeline); + } + + public function testNorAndExists(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::nor( + Query::query( + price: 1.99, + ), + Query::query( + price: Query::exists(false), + ), + Query::query( + sale: true, + ), + Query::query( + sale: Query::exists(false), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::NorNorAndExists, $pipeline); + } + + public function testQueryWithTwoExpressions(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::nor( + Query::query( + price: 1.99, + ), + Query::query( + sale: true, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::NorQueryWithTwoExpressions, $pipeline); + } +} diff --git a/tests/Builder/Query/NotOperatorTest.php b/tests/Builder/Query/NotOperatorTest.php new file mode 100644 index 000000000..e69062f9c --- /dev/null +++ b/tests/Builder/Query/NotOperatorTest.php @@ -0,0 +1,43 @@ +assertSamePipeline(Pipelines::NotRegularExpressions, $pipeline); + } + + public function testSyntax(): void + { + $pipeline = new Pipeline( + Stage::match( + price: Query::not( + Query::gt(1.99), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::NotSyntax, $pipeline); + } +} diff --git a/tests/Builder/Query/OrOperatorTest.php b/tests/Builder/Query/OrOperatorTest.php new file mode 100644 index 000000000..3ad68b20b --- /dev/null +++ b/tests/Builder/Query/OrOperatorTest.php @@ -0,0 +1,59 @@ +assertSamePipeline(Pipelines::OrErrorHandling, $pipeline); + } + + public function testOrClauses(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::or( + Query::query( + quantity: Query::lt(20), + ), + Query::query( + price: 10, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::OrOrClauses, $pipeline); + } +} diff --git a/tests/Builder/Query/Pipelines.php b/tests/Builder/Query/Pipelines.php new file mode 100644 index 000000000..78611649d --- /dev/null +++ b/tests/Builder/Query/Pipelines.php @@ -0,0 +1,2073 @@ +assertSamePipeline(Pipelines::RandSelectRandomItemsFromACollection, $pipeline); + } +} diff --git a/tests/Builder/Query/RegexOperatorTest.php b/tests/Builder/Query/RegexOperatorTest.php new file mode 100644 index 000000000..6f9109387 --- /dev/null +++ b/tests/Builder/Query/RegexOperatorTest.php @@ -0,0 +1,38 @@ +assertSamePipeline(Pipelines::RegexPerformALIKEMatch, $pipeline); + } + + public function testPerformCaseInsensitiveRegularExpressionMatch(): void + { + $pipeline = new Pipeline( + Stage::match( + sku: Query::regex('^ABC', 'i'), + ), + ); + + $this->assertSamePipeline(Pipelines::RegexPerformCaseInsensitiveRegularExpressionMatch, $pipeline); + } +} diff --git a/tests/Builder/Query/SampleRateOperatorTest.php b/tests/Builder/Query/SampleRateOperatorTest.php new file mode 100644 index 000000000..95abec400 --- /dev/null +++ b/tests/Builder/Query/SampleRateOperatorTest.php @@ -0,0 +1,28 @@ +assertSamePipeline(Pipelines::SampleRateExample, $pipeline); + } +} diff --git a/tests/Builder/Query/SizeOperatorTest.php b/tests/Builder/Query/SizeOperatorTest.php new file mode 100644 index 000000000..ea612dbe7 --- /dev/null +++ b/tests/Builder/Query/SizeOperatorTest.php @@ -0,0 +1,27 @@ +assertSamePipeline(Pipelines::SizeQueryAnArrayByArrayLength, $pipeline); + } +} diff --git a/tests/Builder/Query/TextOperatorTest.php b/tests/Builder/Query/TextOperatorTest.php new file mode 100644 index 000000000..813c5460c --- /dev/null +++ b/tests/Builder/Query/TextOperatorTest.php @@ -0,0 +1,120 @@ +assertSamePipeline(Pipelines::TextCaseAndDiacriticInsensitiveSearch, $pipeline); + } + + public function testDiacriticSensitiveSearch(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text( + search: 'CAFÉ', + diacriticSensitive: true, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TextDiacriticSensitiveSearch, $pipeline); + } + + public function testMatchAnyOfTheSearchTerms(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text('bake coffee cake'), + ), + ); + + $this->assertSamePipeline(Pipelines::TextMatchAnyOfTheSearchTerms, $pipeline); + } + + public function testPerformCaseSensitiveSearch(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text( + search: 'Coffee', + caseSensitive: true, + ), + ), + Stage::match( + Query::text( + search: '\"Café Con Leche\"', + caseSensitive: true, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TextPerformCaseSensitiveSearch, $pipeline); + } + + public function testSearchADifferentLanguage(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text( + search: 'leche', + language: 'es', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::TextSearchADifferentLanguage, $pipeline); + } + + public function testSearchForASingleWord(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text('coffee'), + ), + ); + + $this->assertSamePipeline(Pipelines::TextSearchForASingleWord, $pipeline); + } + + public function testTextSearchScoreExamples(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text( + search: 'CAFÉ', + diacriticSensitive: true, + ), + ), + Stage::project( + score: Expression::meta('textScore'), + ), + Stage::sort( + score: Sort::TextScore, + ), + Stage::limit(5), + ); + + $this->assertSamePipeline(Pipelines::TextTextSearchScoreExamples, $pipeline); + } +} diff --git a/tests/Builder/Query/TypeOperatorTest.php b/tests/Builder/Query/TypeOperatorTest.php new file mode 100644 index 000000000..21d727414 --- /dev/null +++ b/tests/Builder/Query/TypeOperatorTest.php @@ -0,0 +1,78 @@ +assertSamePipeline(Pipelines::TypeQueryingByArrayType, $pipeline); + } + + public function testQueryingByDataType(): void + { + $pipeline = new Pipeline( + Stage::match( + zipCode: Query::type(2), + ), + Stage::match( + zipCode: Query::type('string'), + ), + Stage::match( + zipCode: Query::type(1), + ), + Stage::match( + zipCode: Query::type('double'), + ), + Stage::match( + zipCode: Query::type('number'), + ), + ); + + $this->assertSamePipeline(Pipelines::TypeQueryingByDataType, $pipeline); + } + + public function testQueryingByMinKeyAndMaxKey(): void + { + $pipeline = new Pipeline( + Stage::match( + zipCode: Query::type('minKey'), + ), + Stage::match( + zipCode: Query::type('maxKey'), + ), + ); + + $this->assertSamePipeline(Pipelines::TypeQueryingByMinKeyAndMaxKey, $pipeline); + } + + public function testQueryingByMultipleDataType(): void + { + $pipeline = new Pipeline( + Stage::match( + zipCode: Query::type(2, 1), + ), + Stage::match( + zipCode: Query::type('string', 'double'), + ), + ); + + $this->assertSamePipeline(Pipelines::TypeQueryingByMultipleDataType, $pipeline); + } +} diff --git a/tests/Builder/Query/WhereOperatorTest.php b/tests/Builder/Query/WhereOperatorTest.php new file mode 100644 index 000000000..ed87faa4a --- /dev/null +++ b/tests/Builder/Query/WhereOperatorTest.php @@ -0,0 +1,45 @@ +assertSamePipeline(Pipelines::WhereExample, $pipeline); + } +} diff --git a/tests/Builder/Search/AutocompleteOperatorTest.php b/tests/Builder/Search/AutocompleteOperatorTest.php new file mode 100644 index 000000000..46c98f18d --- /dev/null +++ b/tests/Builder/Search/AutocompleteOperatorTest.php @@ -0,0 +1,138 @@ +assertSamePipeline(Pipelines::AutocompleteAcrossMultipleFields, $pipeline); + } + + public function testBasic(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::autocomplete( + query: 'off', + path: 'title', + ), + ), + Stage::limit(10), + Stage::project(_id: 0, title: 1), + ); + + $this->assertSamePipeline(Pipelines::AutocompleteBasic, $pipeline); + } + + public function testFuzzy(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::autocomplete( + query: 'pre', + path: 'title', + fuzzy: object( + maxEdits: 1, + prefixLength: 1, + maxExpansions: 256, + ), + ), + ), + Stage::limit(10), + Stage::project(_id: 0, title: 1), + ); + + $this->assertSamePipeline(Pipelines::AutocompleteFuzzy, $pipeline); + } + + public function testHighlighting(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::autocomplete( + query: 'ger', + path: 'title', + ), + highlight: object( + path: 'title', + ), + ), + Stage::limit(5), + Stage::project( + score: ['$meta' => 'searchScore'], + _id: 0, + title: 1, + highlights: ['$meta' => 'searchHighlights'], + ), + ); + + $this->assertSamePipeline(Pipelines::AutocompleteHighlighting, $pipeline); + } + + public function testTokenOrderAny(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::autocomplete( + query: 'men with', + path: 'title', + tokenOrder: 'any', + ), + ), + Stage::limit(4), + Stage::project(_id: 0, title: 1), + ); + + $this->assertSamePipeline(Pipelines::AutocompleteTokenOrderAny, $pipeline); + } + + public function testTokenOrderSequential(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::autocomplete( + query: 'men with', + path: 'title', + tokenOrder: 'sequential', + ), + ), + Stage::limit(4), + Stage::project(_id: 0, title: 1), + ); + + $this->assertSamePipeline(Pipelines::AutocompleteTokenOrderSequential, $pipeline); + } +} diff --git a/tests/Builder/Search/CompoundOperatorTest.php b/tests/Builder/Search/CompoundOperatorTest.php new file mode 100644 index 000000000..8d5d69aed --- /dev/null +++ b/tests/Builder/Search/CompoundOperatorTest.php @@ -0,0 +1,157 @@ +assertSamePipeline(Pipelines::CompoundFilter, $pipeline); + } + + public function testMinimumShouldMatch(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + must: [ + Search::text( + path: 'description', + query: 'varieties', + ), + ], + should: [ + Search::text( + path: 'description', + query: 'Fuji', + ), + Search::text( + path: 'description', + query: 'Golden Delicious', + ), + ], + minimumShouldMatch: 1, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::CompoundMinimumShouldMatch, $pipeline); + } + + public function testMustAndMustNot(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + must: [ + Search::text( + path: 'description', + query: 'varieties', + ), + ], + mustNot: [ + Search::text( + path: 'description', + query: 'apples', + ), + ], + ), + ), + ); + + $this->assertSamePipeline(Pipelines::CompoundMustAndMustNot, $pipeline); + } + + public function testMustAndShould(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + must: [ + Search::text( + path: 'description', + query: 'varieties', + ), + ], + should: [ + Search::text( + path: 'description', + query: 'Fuji', + ), + ], + ), + ), + Stage::project( + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::CompoundMustAndShould, $pipeline); + } + + public function testNested(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + should: [ + Search::text( + path: 'type', + query: 'apple', + ), + Search::compound( + must: [ + Search::text( + path: 'category', + query: 'organic', + ), + Search::equals( + path: 'in_stock', + value: true, + ), + ], + ), + ], + minimumShouldMatch: 1, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::CompoundNested, $pipeline); + } +} diff --git a/tests/Builder/Search/EmbeddedDocumentOperatorTest.php b/tests/Builder/Search/EmbeddedDocumentOperatorTest.php new file mode 100644 index 000000000..b83790c83 --- /dev/null +++ b/tests/Builder/Search/EmbeddedDocumentOperatorTest.php @@ -0,0 +1,167 @@ + 1, + 'items.tags' => 1, + ], + _id: 0, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::EmbeddedDocumentBasic, $pipeline); + } + + public function testFacet(): void + { + $pipeline = new Pipeline( + Stage::searchMeta( + Search::facet( + facets: object( + purchaseMethodFacet: object( + type: 'string', + path: 'purchaseMethod', + ), + ), + operator: Search::embeddedDocument( + path: 'items', + operator: Search::compound( + must: [ + Search::text( + path: 'items.tags', + query: 'school', + ), + ], + should: [ + Search::text( + path: 'items.name', + query: 'backpack', + ), + ], + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EmbeddedDocumentFacet, $pipeline); + } + + public function testQueryAndSort(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::embeddedDocument( + path: 'items', + operator: + Search::text( + path: 'items.name', + query: 'laptop', + ), + ), + sort: ['items.tags' => Sort::Asc], + ), + Stage::limit(5), + Stage::project( + ...[ + 'items.name' => 1, + 'items.tags' => 1, + ], + _id: 0, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::EmbeddedDocumentQueryAndSort, $pipeline); + } + + public function testQueryForMatchingEmbeddedDocumentsOnly(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::embeddedDocument( + path: 'items', + operator: + Search::compound( + must: [ + Search::range( + path: 'items.quantity', + gt: 2, + ), + Search::exists( + path: 'items.price', + ), + Search::text( + path: 'items.tags', + query: 'school', + ), + ], + ), + ), + ), + Stage::limit(2), + Stage::project( + _id: 0, + storeLocation: 1, + items: Expression::filter( + input: Expression::arrayFieldPath('items'), + cond: Expression::and( + Expression::ifNull('$$this.price', 'false'), + Expression::gt(Expression::variable('this.quantity'), 2), + Expression::in('office', Expression::variable('this.tags')), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EmbeddedDocumentQueryForMatchingEmbeddedDocumentsOnly, $pipeline); + } +} diff --git a/tests/Builder/Search/EqualsOperatorTest.php b/tests/Builder/Search/EqualsOperatorTest.php new file mode 100644 index 000000000..6cd8ae9e1 --- /dev/null +++ b/tests/Builder/Search/EqualsOperatorTest.php @@ -0,0 +1,125 @@ + 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsBoolean, $pipeline); + } + + public function testDate(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'account_created', + value: new UTCDateTime(new DateTimeImmutable('2022-05-04T05:01:08')), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsDate, $pipeline); + } + + public function testNull(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'job_title', + value: null, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsNull, $pipeline); + } + + public function testNumber(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'employee_number', + value: 259, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsNumber, $pipeline); + } + + public function testObjectId(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'teammates', + value: new ObjectId('5a9427648b0beebeb69589a1'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsObjectId, $pipeline); + } + + public function testString(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'name', + value: 'jim hall', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsString, $pipeline); + } + + public function testUUID(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::equals( + path: 'uuid', + value: new Binary(hex2bin('fac32260b5114c698485a2be5b7dda9e'), Binary::TYPE_UUID), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::EqualsUUID, $pipeline); + } +} diff --git a/tests/Builder/Search/ExistsOperatorTest.php b/tests/Builder/Search/ExistsOperatorTest.php new file mode 100644 index 000000000..5666a1099 --- /dev/null +++ b/tests/Builder/Search/ExistsOperatorTest.php @@ -0,0 +1,67 @@ +assertSamePipeline(Pipelines::ExistsBasic, $pipeline); + } + + public function testCompound(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + must: [ + Search::exists( + path: 'type', + ), + Search::text( + path: 'type', + query: 'apple', + ), + ], + should: Search::text( + path: 'description', + query: 'fuji', + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ExistsCompound, $pipeline); + } + + public function testEmbedded(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::exists( + path: 'quantities.lemons', + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ExistsEmbedded, $pipeline); + } +} diff --git a/tests/Builder/Search/FacetOperatorTest.php b/tests/Builder/Search/FacetOperatorTest.php new file mode 100644 index 000000000..3be124bec --- /dev/null +++ b/tests/Builder/Search/FacetOperatorTest.php @@ -0,0 +1,61 @@ +assertSamePipeline(Pipelines::FacetFacet, $pipeline); + } +} diff --git a/tests/Builder/Search/GeoShapeOperatorTest.php b/tests/Builder/Search/GeoShapeOperatorTest.php new file mode 100644 index 000000000..b5e748660 --- /dev/null +++ b/tests/Builder/Search/GeoShapeOperatorTest.php @@ -0,0 +1,204 @@ + 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::GeoShapeDisjoint, $pipeline); + } + + public function testIntersect(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::geoShape( + relation: 'intersects', + geometry: object( + type: 'MultiPolygon', + coordinates: [ + [ + [ + [ + 2.16942, + 41.40082, + ], + [ + 2.17963, + 41.40087, + ], + [ + 2.18146, + 41.39716, + ], + [ + 2.15533, + 41.40686, + ], + [ + 2.14596, + 41.38475, + ], + [ + 2.17519, + 41.41035, + ], + [ + 2.16942, + 41.40082, + ], + ], + ], + [ + [ + [ + 2.16365, + 41.39416, + ], + [ + 2.16963, + 41.39726, + ], + [ + 2.15395, + 41.38005, + ], + [ + 2.17935, + 41.43038, + ], + [ + 2.16365, + 41.39416, + ], + ], + ], + ], + ), + path: 'address.location', + ), + ), + Stage::limit(3), + Stage::project( + _id: 0, + name: 1, + address: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::GeoShapeIntersect, $pipeline); + } + + public function testWithin(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::geoShape( + relation: 'within', + geometry: object( + type: 'Polygon', + coordinates: [ + [ + [ + -74.3994140625, + 40.5305017757, + ], + [ + -74.7290039063, + 40.5805846641, + ], + [ + -74.7729492188, + 40.9467136651, + ], + [ + -74.0698242188, + 41.1290213475, + ], + [ + -73.65234375, + 40.9964840144, + ], + [ + -72.6416015625, + 40.9467136651, + ], + [ + -72.3559570313, + 40.7971774152, + ], + [ + -74.3994140625, + 40.5305017757, + ], + ], + ], + ), + path: 'address.location', + ), + ), + Stage::limit(3), + Stage::project( + _id: 0, + name: 1, + address: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::GeoShapeWithin, $pipeline); + } +} diff --git a/tests/Builder/Search/GeoWithinOperatorTest.php b/tests/Builder/Search/GeoWithinOperatorTest.php new file mode 100644 index 000000000..3a6a6b0af --- /dev/null +++ b/tests/Builder/Search/GeoWithinOperatorTest.php @@ -0,0 +1,124 @@ +assertSamePipeline(Pipelines::GeoWithinBox, $pipeline); + } + + public function testCircle(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::geoWithin( + path: 'address.location', + circle: object( + center: object( + type: 'Point', + coordinates: [ + -73.54, + 45.54, + ], + ), + radius: 1600, + ), + ), + ), + Stage::limit(3), + Stage::project( + _id: 0, + name: 1, + address: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::GeoWithinCircle, $pipeline); + } + + public function testGeometry(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::geoWithin( + path: 'address.location', + geometry: object( + type: 'Polygon', + coordinates: [ + [ + [ + -161.323242, + 22.512557, + ], + [ + -152.446289, + 22.065278, + ], + [ + -156.09375, + 17.811456, + ], + [ + -161.323242, + 22.512557, + ], + ], + ], + ), + ), + ), + Stage::limit(3), + Stage::project( + _id: 0, + name: 1, + address: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::GeoWithinGeometry, $pipeline); + } +} diff --git a/tests/Builder/Search/InOperatorTest.php b/tests/Builder/Search/InOperatorTest.php new file mode 100644 index 000000000..e6d1af57c --- /dev/null +++ b/tests/Builder/Search/InOperatorTest.php @@ -0,0 +1,101 @@ +assertSamePipeline(Pipelines::InArrayValueFieldMatch, $pipeline); + } + + public function testCompoundQueryMatch(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + must: [ + Search::in( + path: 'name', + value: [ + 'james sanchez', + 'jennifer lawrence', + ], + ), + ], + should: [ + Search::in( + path: '_id', + value: [ + new ObjectId('5ca4bbcea2dd94ee58162a72'), + new ObjectId('5ca4bbcea2dd94ee58162a91'), + ], + ), + ], + ), + ), + Stage::limit(5), + Stage::project( + _id: 1, + name: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::InCompoundQueryMatch, $pipeline); + } + + public function testSingleValueFieldMatch(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::in( + path: 'birthdate', + value: [ + new UTCDateTime(new DateTimeImmutable('1977-03-02T02:20:31')), + new UTCDateTime(new DateTimeImmutable('1977-03-01T00:00:00')), + new UTCDateTime(new DateTimeImmutable('1977-05-06T21:57:35')), + ], + ), + ), + Stage::project( + _id: 0, + name: 1, + birthdate: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::InSingleValueFieldMatch, $pipeline); + } +} diff --git a/tests/Builder/Search/MoreLikeThisOperatorTest.php b/tests/Builder/Search/MoreLikeThisOperatorTest.php new file mode 100644 index 000000000..a83c03559 --- /dev/null +++ b/tests/Builder/Search/MoreLikeThisOperatorTest.php @@ -0,0 +1,115 @@ +assertSamePipeline(Pipelines::MoreLikeThisInputDocumentExcludedInResults, $pipeline); + } + + public function testMultipleAnalyzers(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::compound( + mustNot: [ + Search::equals( + path: '_id', + value: new ObjectId('573a1394f29313caabcde9ef'), + ), + ], + should: [ + Search::moreLikeThis( + like: object( + _id: new ObjectId('573a1396f29313caabce4a9a'), + genres: [ + 'Crime', + 'Drama', + ], + title: 'The Godfather', + ), + ), + ], + ), + ), + Stage::limit(10), + Stage::project( + title: 1, + genres: 1, + _id: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::MoreLikeThisMultipleAnalyzers, $pipeline); + } + + public function testSingleDocumentWithMultipleFields(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::moreLikeThis( + like: object( + title: 'The Godfather', + genres: 'action', + ), + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + released: 1, + genres: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::MoreLikeThisSingleDocumentWithMultipleFields, $pipeline); + } +} diff --git a/tests/Builder/Search/NearOperatorTest.php b/tests/Builder/Search/NearOperatorTest.php new file mode 100644 index 000000000..45a48e0d5 --- /dev/null +++ b/tests/Builder/Search/NearOperatorTest.php @@ -0,0 +1,128 @@ + 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::NearCompound, $pipeline); + } + + public function testDate(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::near( + path: 'released', + origin: new UTCDateTime(new DateTimeImmutable('1915-09-13T00:00:00.000+00:00')), + pivot: 7776000000, + ), + index: 'releaseddate', + ), + Stage::limit(3), + Stage::project( + _id: 0, + title: 1, + released: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::NearDate, $pipeline); + } + + public function testGeoJSONPoint(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::near( + path: 'address.location', + origin: object( + type: 'Point', + coordinates: [ + -8.61308, + 41.1413, + ], + ), + pivot: 1000, + ), + ), + Stage::limit(3), + Stage::project( + _id: 0, + name: 1, + address: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::NearGeoJSONPoint, $pipeline); + } + + public function testNumber(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::near( + path: 'runtime', + origin: 279, + pivot: 2, + ), + index: 'runtimes', + ), + Stage::limit(7), + Stage::project( + _id: 0, + title: 1, + runtime: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::NearNumber, $pipeline); + } +} diff --git a/tests/Builder/Search/PhraseOperatorTest.php b/tests/Builder/Search/PhraseOperatorTest.php new file mode 100644 index 000000000..a045e9c12 --- /dev/null +++ b/tests/Builder/Search/PhraseOperatorTest.php @@ -0,0 +1,102 @@ + 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::PhraseMultiplePhrase, $pipeline); + } + + public function testPhraseSlop(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::phrase( + path: 'title', + query: 'men women', + slop: 5, + ), + ), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::PhrasePhraseSlop, $pipeline); + } + + public function testPhraseSynonyms(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::phrase( + path: 'plot', + query: 'automobile race', + slop: 5, + synonyms: 'my_synonyms', + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + plot: 1, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::PhrasePhraseSynonyms, $pipeline); + } + + public function testSinglePhrase(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::phrase( + path: 'title', + query: 'new york', + ), + ), + Stage::limit(10), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::PhraseSinglePhrase, $pipeline); + } +} diff --git a/tests/Builder/Search/Pipelines.php b/tests/Builder/Search/Pipelines.php new file mode 100644 index 000000000..fa94f8c9a --- /dev/null +++ b/tests/Builder/Search/Pipelines.php @@ -0,0 +1,2814 @@ +assertSamePipeline(Pipelines::QueryStringBooleanOperatorQueries, $pipeline); + } +} diff --git a/tests/Builder/Search/RangeOperatorTest.php b/tests/Builder/Search/RangeOperatorTest.php new file mode 100644 index 000000000..9606014ed --- /dev/null +++ b/tests/Builder/Search/RangeOperatorTest.php @@ -0,0 +1,122 @@ +assertSamePipeline(Pipelines::RangeDate, $pipeline); + } + + public function testNumberGteLte(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: 'runtime', + gte: 2, + lte: 3, + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + runtime: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::RangeNumberGteLte, $pipeline); + } + + public function testNumberLte(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: 'runtime', + lte: 2, + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + runtime: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::RangeNumberLte, $pipeline); + } + + public function testObjectId(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: '_id', + gte: new ObjectId('573a1396f29313caabce4a9a'), + lte: new ObjectId('573a1396f29313caabce4ae7'), + ), + ), + Stage::project( + _id: 1, + title: 1, + released: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::RangeObjectId, $pipeline); + } + + public function testString(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: 'title', + gt: 'city', + lt: 'country', + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::RangeString, $pipeline); + } +} diff --git a/tests/Builder/Search/RegexOperatorTest.php b/tests/Builder/Search/RegexOperatorTest.php new file mode 100644 index 000000000..b95f518a0 --- /dev/null +++ b/tests/Builder/Search/RegexOperatorTest.php @@ -0,0 +1,34 @@ +assertSamePipeline(Pipelines::RegexRegex, $pipeline); + } +} diff --git a/tests/Builder/Search/TextOperatorTest.php b/tests/Builder/Search/TextOperatorTest.php new file mode 100644 index 000000000..12709740e --- /dev/null +++ b/tests/Builder/Search/TextOperatorTest.php @@ -0,0 +1,194 @@ + 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextBasic, $pipeline); + } + + public function testFuzzyDefault(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'naw yark', + fuzzy: object(), + ), + ), + Stage::limit(10), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextFuzzyDefault, $pipeline); + } + + public function testFuzzyMaxExpansions(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'naw yark', + fuzzy: object( + maxEdits: 1, + maxExpansions: 100, + ), + ), + ), + Stage::limit(10), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextFuzzyMaxExpansions, $pipeline); + } + + public function testFuzzyPrefixLength(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'naw yark', + fuzzy: object( + maxEdits: 1, + prefixLength: 2, + ), + ), + ), + Stage::limit(8), + Stage::project( + _id: 1, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextFuzzyPrefixLength, $pipeline); + } + + public function testMatchAllUsingSynonyms(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'plot', + query: 'automobile race', + matchCriteria: 'all', + synonyms: 'my_synonyms', + ), + ), + Stage::limit(20), + Stage::project( + _id: 0, + plot: 1, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextMatchAllUsingSynonyms, $pipeline); + } + + public function testMatchAnyUsingEquivalentMapping(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'plot', + query: 'attire', + synonyms: 'my_synonyms', + matchCriteria: 'any', + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + plot: 1, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextMatchAnyUsingEquivalentMapping, $pipeline); + } + + public function testMatchAnyUsingExplicitMapping(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'plot', + query: 'boat race', + synonyms: 'my_synonyms', + matchCriteria: 'any', + ), + ), + Stage::limit(10), + Stage::project( + _id: 0, + plot: 1, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextMatchAnyUsingExplicitMapping, $pipeline); + } + + public function testWildcardPath(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: ['wildcard' => '*'], + query: 'surfer', + ), + ), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::TextWildcardPath, $pipeline); + } +} diff --git a/tests/Builder/Search/WildcardOperatorTest.php b/tests/Builder/Search/WildcardOperatorTest.php new file mode 100644 index 000000000..18103b75d --- /dev/null +++ b/tests/Builder/Search/WildcardOperatorTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::WildcardEscapeCharacterExample, $pipeline); + } + + public function testWildcardPath(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::wildcard( + query: 'Wom?n *', + path: ['wildcard' => '*'], + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::WildcardWildcardPath, $pipeline); + } +} diff --git a/tests/Builder/Stage/AddFieldsStageTest.php b/tests/Builder/Stage/AddFieldsStageTest.php new file mode 100644 index 000000000..970471ba2 --- /dev/null +++ b/tests/Builder/Stage/AddFieldsStageTest.php @@ -0,0 +1,74 @@ +assertSamePipeline(Pipelines::AddFieldsAddElementToAnArray, $pipeline); + } + + public function testAddingFieldsToAnEmbeddedDocument(): void + { + $pipeline = new Pipeline( + Stage::addFields( + ...['specs.fuel_type' => 'unleaded'], + ), + ); + + $this->assertSamePipeline(Pipelines::AddFieldsAddingFieldsToAnEmbeddedDocument, $pipeline); + } + + public function testOverwritingAnExistingField(): void + { + $pipeline = new Pipeline( + Stage::addFields( + cats: 20, + ), + ); + + $this->assertSamePipeline(Pipelines::AddFieldsOverwritingAnExistingField, $pipeline); + } + + public function testUsingTwoAddFieldsStages(): void + { + $pipeline = new Pipeline( + Stage::addFields( + totalHomework: Expression::sum(Expression::fieldPath('homework')), + totalQuiz: Expression::sum(Expression::fieldPath('quiz')), + ), + Stage::addFields( + totalScore: Expression::add( + Expression::fieldPath('totalHomework'), + Expression::fieldPath('totalQuiz'), + Expression::fieldPath('extraCredit'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::AddFieldsUsingTwoAddFieldsStages, $pipeline); + } +} diff --git a/tests/Builder/Stage/BucketAutoStageTest.php b/tests/Builder/Stage/BucketAutoStageTest.php new file mode 100644 index 000000000..ecae48f20 --- /dev/null +++ b/tests/Builder/Stage/BucketAutoStageTest.php @@ -0,0 +1,28 @@ +assertSamePipeline(Pipelines::BucketAutoSingleFacetAggregation, $pipeline); + } +} diff --git a/tests/Builder/Stage/BucketStageTest.php b/tests/Builder/Stage/BucketStageTest.php new file mode 100644 index 000000000..95119a5e3 --- /dev/null +++ b/tests/Builder/Stage/BucketStageTest.php @@ -0,0 +1,94 @@ +assertSamePipeline(Pipelines::BucketBucketByYearAndFilterByBucketResults, $pipeline); + } + + public function testUseBucketWithFacetToBucketByMultipleFields(): void + { + $pipeline = new Pipeline( + Stage::facet( + price: new Pipeline( + Stage::bucket( + groupBy: Expression::numberFieldPath('price'), + boundaries: [0, 200, 400], + default: 'Other', + output: object( + count: Accumulator::sum(1), + artwork: Accumulator::push( + object( + title: Expression::stringFieldPath('title'), + price: Expression::stringFieldPath('price'), + ), + ), + averagePrice: Accumulator::avg( + Expression::numberFieldPath('price'), + ), + ), + ), + ), + year: new Pipeline( + Stage::bucket( + groupBy: Expression::stringFieldPath('year'), + boundaries: [1890, 1910, 1920, 1940], + default: 'Unknown', + output: object( + count: Accumulator::sum(1), + artwork: Accumulator::push( + object( + title: Expression::stringFieldPath('title'), + year: Expression::stringFieldPath('year'), + ), + ), + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::BucketUseBucketWithFacetToBucketByMultipleFields, $pipeline); + } +} diff --git a/tests/Builder/Stage/ChangeStreamSplitLargeEventStageTest.php b/tests/Builder/Stage/ChangeStreamSplitLargeEventStageTest.php new file mode 100644 index 000000000..c7652e2de --- /dev/null +++ b/tests/Builder/Stage/ChangeStreamSplitLargeEventStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::ChangeStreamSplitLargeEventExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/ChangeStreamStageTest.php b/tests/Builder/Stage/ChangeStreamStageTest.php new file mode 100644 index 000000000..03a5bf523 --- /dev/null +++ b/tests/Builder/Stage/ChangeStreamStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::ChangeStreamExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/CollStatsStageTest.php b/tests/Builder/Stage/CollStatsStageTest.php new file mode 100644 index 000000000..5b9277d27 --- /dev/null +++ b/tests/Builder/Stage/CollStatsStageTest.php @@ -0,0 +1,63 @@ +assertSamePipeline(Pipelines::CollStatsCountField, $pipeline); + } + + public function testLatencyStatsDocument(): void + { + $pipeline = new Pipeline( + Stage::collStats( + latencyStats: object( + histograms: true, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::CollStatsLatencyStatsDocument, $pipeline); + } + + public function testQueryExecStatsDocument(): void + { + $pipeline = new Pipeline( + Stage::collStats( + queryExecStats: object(), + ), + ); + + $this->assertSamePipeline(Pipelines::CollStatsQueryExecStatsDocument, $pipeline); + } + + public function testStorageStatsDocument(): void + { + $pipeline = new Pipeline( + Stage::collStats( + storageStats: object(), + ), + ); + + $this->assertSamePipeline(Pipelines::CollStatsStorageStatsDocument, $pipeline); + } +} diff --git a/tests/Builder/Stage/CountStageTest.php b/tests/Builder/Stage/CountStageTest.php new file mode 100644 index 000000000..68c45c52e --- /dev/null +++ b/tests/Builder/Stage/CountStageTest.php @@ -0,0 +1,28 @@ +assertSamePipeline(Pipelines::CountExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/CurrentOpStageTest.php b/tests/Builder/Stage/CurrentOpStageTest.php new file mode 100644 index 000000000..6e50cb0b9 --- /dev/null +++ b/tests/Builder/Stage/CurrentOpStageTest.php @@ -0,0 +1,47 @@ +assertSamePipeline(Pipelines::CurrentOpInactiveSessions, $pipeline); + } + + public function testSampledQueries(): void + { + $pipeline = new Pipeline( + Stage::currentOp( + allUsers: true, + localOps: true, + ), + Stage::match( + desc: 'query analyzer', + ), + ); + + $this->assertSamePipeline(Pipelines::CurrentOpSampledQueries, $pipeline); + } +} diff --git a/tests/Builder/Stage/DensifyStageTest.php b/tests/Builder/Stage/DensifyStageTest.php new file mode 100644 index 000000000..142233fcf --- /dev/null +++ b/tests/Builder/Stage/DensifyStageTest.php @@ -0,0 +1,55 @@ +assertSamePipeline(Pipelines::DensifyDensifictionWithPartitions, $pipeline); + } + + public function testDensifyTimeSeriesData(): void + { + $pipeline = new Pipeline( + Stage::densify( + field: 'timestamp', + range: object( + step: 1, + unit: TimeUnit::Hour, + bounds: [ + new UTCDateTime(new DateTimeImmutable('2021-05-18T00:00:00.000Z')), + new UTCDateTime(new DateTimeImmutable('2021-05-18T08:00:00.000Z')), + ], + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DensifyDensifyTimeSeriesData, $pipeline); + } +} diff --git a/tests/Builder/Stage/DocumentsStageTest.php b/tests/Builder/Stage/DocumentsStageTest.php new file mode 100644 index 000000000..e0b2e4ce0 --- /dev/null +++ b/tests/Builder/Stage/DocumentsStageTest.php @@ -0,0 +1,56 @@ +assertSamePipeline(Pipelines::DocumentsTestAPipelineStage, $pipeline); + } + + public function testUseADocumentsStageInALookupStage(): void + { + $pipeline = new Pipeline( + Stage::match(), + Stage::lookup( + localField: 'zip', + foreignField: 'zip_id', + as: 'city_state', + pipeline: new Pipeline( + Stage::documents([ + Document::fromPHP(object(zip_id: 94301, name: 'Palo Alto, CA')), + Document::fromPHP(object(zip_id: 10019, name: 'New York, NY')), + ]), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::DocumentsUseADocumentsStageInALookupStage, $pipeline); + } +} diff --git a/tests/Builder/Stage/FacetStageTest.php b/tests/Builder/Stage/FacetStageTest.php new file mode 100644 index 000000000..121131617 --- /dev/null +++ b/tests/Builder/Stage/FacetStageTest.php @@ -0,0 +1,62 @@ + new Pipeline( + Stage::bucketAuto( + groupBy: Expression::stringFieldPath('year'), + buckets: 4, + ), + ), + ], + categorizedByTags: new Pipeline( + Stage::unwind( + Expression::arrayFieldPath('tags'), + ), + Stage::sortByCount( + Expression::arrayFieldPath('tags'), + ), + ), + categorizedByPrice: new Pipeline( + Stage::match( + price: Query::exists(), + ), + Stage::bucket( + groupBy: Expression::numberFieldPath('price'), + boundaries: [0, 150, 200, 300, 400], + default: 'Other', + output: object( + count: Accumulator::sum(1), + titles: Accumulator::push( + Expression::stringFieldPath('title'), + ), + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FacetExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/FillStageTest.php b/tests/Builder/Stage/FillStageTest.php new file mode 100644 index 000000000..e464cfaef --- /dev/null +++ b/tests/Builder/Stage/FillStageTest.php @@ -0,0 +1,111 @@ +assertSamePipeline(Pipelines::FillFillDataForDistinctPartitions, $pipeline); + } + + public function testFillMissingFieldValuesBasedOnTheLastObservedValue(): void + { + $pipeline = new Pipeline( + Stage::fill( + sortBy: object( + date: Sort::Asc, + ), + output: object( + score: object(method: 'locf'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FillFillMissingFieldValuesBasedOnTheLastObservedValue, $pipeline); + } + + public function testFillMissingFieldValuesWithAConstantValue(): void + { + $pipeline = new Pipeline( + Stage::fill( + output: object( + bootsSold: object(value: 0), + sandalsSold: object(value: 0), + sneakersSold: object(value: 0), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FillFillMissingFieldValuesWithAConstantValue, $pipeline); + } + + public function testFillMissingFieldValuesWithLinearInterpolation(): void + { + $pipeline = new Pipeline( + Stage::fill( + sortBy: object( + time: Sort::Asc, + ), + output: object( + price: object(method: 'linear'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FillFillMissingFieldValuesWithLinearInterpolation, $pipeline); + } + + public function testIndicateIfAFieldWasPopulatedUsingFill(): void + { + $pipeline = new Pipeline( + Stage::set( + valueExisted: Expression::ifNull( + Expression::toBool( + Expression::toString( + Expression::fieldPath('score'), + ), + ), + false, + ), + ), + Stage::fill( + sortBy: object( + date: Sort::Asc, + ), + output: object( + score: object(method: 'locf'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::FillIndicateIfAFieldWasPopulatedUsingFill, $pipeline); + } +} diff --git a/tests/Builder/Stage/GeoNearStageTest.php b/tests/Builder/Stage/GeoNearStageTest.php new file mode 100644 index 000000000..93acfc277 --- /dev/null +++ b/tests/Builder/Stage/GeoNearStageTest.php @@ -0,0 +1,123 @@ +assertSamePipeline(Pipelines::GeoNearMaximumDistance, $pipeline); + } + + public function testMinimumDistance(): void + { + $pipeline = new Pipeline( + Stage::geoNear( + near: object( + type: 'Point', + coordinates: [-73.99279, 40.719296], + ), + distanceField: 'dist.calculated', + minDistance: 2, + query: Query::query( + category: 'Parks', + ), + includeLocs: 'dist.location', + spherical: true, + ), + ); + + $this->assertSamePipeline(Pipelines::GeoNearMinimumDistance, $pipeline); + } + + public function testSpecifyWhichGeospatialIndexToUse(): void + { + $pipeline = new Pipeline( + Stage::geoNear( + near: object( + type: 'Point', + coordinates: [-73.98142, 40.71782], + ), + key: 'location', + distanceField: 'dist.calculated', + query: Query::query( + category: 'Parks', + ), + ), + Stage::limit(5), + ); + + $this->assertSamePipeline(Pipelines::GeoNearSpecifyWhichGeospatialIndexToUse, $pipeline); + } + + public function testWithBoundLetOption(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'places', + let: object( + pt: Expression::stringFieldPath('location'), + ), + pipeline: new Pipeline( + Stage::geoNear( + near: Expression::variable('pt'), + distanceField: 'distance', + ), + ), + as: 'joinedField', + ), + Stage::match( + name: 'Sara D. Roosevelt Park', + ), + ); + + $this->assertSamePipeline(Pipelines::GeoNearWithBoundLetOption, $pipeline); + } + + public function testWithTheLetOption(): void + { + $pipeline = new Pipeline( + Stage::geoNear( + near: Expression::variable('pt'), + distanceField: 'distance', + maxDistance: 2, + query: Query::query( + category: 'Parks', + ), + includeLocs: 'dist.location', + spherical: true, + ), + ); + + $this->assertSamePipeline(Pipelines::GeoNearWithTheLetOption, $pipeline); + } +} diff --git a/tests/Builder/Stage/GraphLookupStageTest.php b/tests/Builder/Stage/GraphLookupStageTest.php new file mode 100644 index 000000000..fd4a97b4c --- /dev/null +++ b/tests/Builder/Stage/GraphLookupStageTest.php @@ -0,0 +1,77 @@ +assertSamePipeline(Pipelines::GraphLookupAcrossMultipleCollections, $pipeline); + } + + public function testWithAQueryFilter(): void + { + $pipeline = new Pipeline( + Stage::match( + name: 'Tanya Jordan', + ), + Stage::graphLookup( + from: 'people', + startWith: Expression::stringFieldPath('friends'), + connectFromField: 'friends', + connectToField: 'name', + as: 'golfers', + restrictSearchWithMatch: Query::query( + hobbies: 'golf', + ), + ), + Stage::project( + ...[ + 'connections who play golf' => Expression::stringFieldPath('golfers.name'), + ], + name: 1, + friends: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::GraphLookupWithAQueryFilter, $pipeline); + } + + public function testWithinASingleCollection(): void + { + $pipeline = new Pipeline( + Stage::graphLookup( + from: 'employees', + startWith: Expression::stringFieldPath('reportsTo'), + connectFromField: 'reportsTo', + connectToField: 'name', + as: 'reportingHierarchy', + ), + ); + + $this->assertSamePipeline(Pipelines::GraphLookupWithinASingleCollection, $pipeline); + } +} diff --git a/tests/Builder/Stage/GroupStageTest.php b/tests/Builder/Stage/GroupStageTest.php new file mode 100644 index 000000000..ca742312f --- /dev/null +++ b/tests/Builder/Stage/GroupStageTest.php @@ -0,0 +1,148 @@ +assertSamePipeline(Pipelines::GroupCalculateCountSumAndAverage, $pipeline); + } + + public function testCountTheNumberOfDocumentsInACollection(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: null, + count: Accumulator::count(), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupCountTheNumberOfDocumentsInACollection, $pipeline); + } + + public function testGroupByItemHaving(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('item'), + totalSaleAmount: Accumulator::sum( + Expression::multiply( + Expression::numberFieldPath('price'), + Expression::numberFieldPath('quantity'), + ), + ), + ), + Stage::match( + totalSaleAmount: Query::gte(100), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupGroupByItemHaving, $pipeline); + } + + public function testGroupByNull(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: null, + totalSaleAmount: Accumulator::sum( + Expression::multiply( + Expression::numberFieldPath('price'), + Expression::numberFieldPath('quantity'), + ), + ), + averageQuantity: Accumulator::avg( + Expression::numberFieldPath('quantity'), + ), + count: Accumulator::sum(1), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupGroupByNull, $pipeline); + } + + public function testGroupDocumentsByAuthor(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('author'), + books: Accumulator::push( + Expression::variable('ROOT'), + ), + ), + Stage::addFields( + totalCopies: Expression::sum( + Expression::numberFieldPath('books.copies'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupGroupDocumentsByAuthor, $pipeline); + } + + public function testPivotData(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::fieldPath('author'), + books: Accumulator::push( + Expression::stringFieldPath('title'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupPivotData, $pipeline); + } + + public function testRetrieveDistinctValues(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::stringFieldPath('item'), + ), + ); + + $this->assertSamePipeline(Pipelines::GroupRetrieveDistinctValues, $pipeline); + } +} diff --git a/tests/Builder/Stage/IndexStatsStageTest.php b/tests/Builder/Stage/IndexStatsStageTest.php new file mode 100644 index 000000000..9b121417b --- /dev/null +++ b/tests/Builder/Stage/IndexStatsStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::IndexStatsExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/LimitStageTest.php b/tests/Builder/Stage/LimitStageTest.php new file mode 100644 index 000000000..04c9c6bbd --- /dev/null +++ b/tests/Builder/Stage/LimitStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::LimitExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/ListLocalSessionsStageTest.php b/tests/Builder/Stage/ListLocalSessionsStageTest.php new file mode 100644 index 000000000..57d1bf06d --- /dev/null +++ b/tests/Builder/Stage/ListLocalSessionsStageTest.php @@ -0,0 +1,50 @@ +assertSamePipeline(Pipelines::ListLocalSessionsListAllLocalSessions, $pipeline); + } + + public function testListAllLocalSessionsForTheCurrentUser(): void + { + $pipeline = new Pipeline( + Stage::listLocalSessions(), + ); + + $this->assertSamePipeline(Pipelines::ListLocalSessionsListAllLocalSessionsForTheCurrentUser, $pipeline); + } + + public function testListAllLocalSessionsForTheSpecifiedUsers(): void + { + $pipeline = new Pipeline( + Stage::listLocalSessions( + users: [ + object(user: 'myAppReader', db: 'test'), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::ListLocalSessionsListAllLocalSessionsForTheSpecifiedUsers, $pipeline); + } +} diff --git a/tests/Builder/Stage/ListSampledQueriesStageTest.php b/tests/Builder/Stage/ListSampledQueriesStageTest.php new file mode 100644 index 000000000..3d1fccf29 --- /dev/null +++ b/tests/Builder/Stage/ListSampledQueriesStageTest.php @@ -0,0 +1,35 @@ +assertSamePipeline(Pipelines::ListSampledQueriesListSampledQueriesForASpecificCollection, $pipeline); + } + + public function testListSampledQueriesForAllCollections(): void + { + $pipeline = new Pipeline( + Stage::listSampledQueries(), + ); + + $this->assertSamePipeline(Pipelines::ListSampledQueriesListSampledQueriesForAllCollections, $pipeline); + } +} diff --git a/tests/Builder/Stage/ListSearchIndexesStageTest.php b/tests/Builder/Stage/ListSearchIndexesStageTest.php new file mode 100644 index 000000000..e5bdbfd18 --- /dev/null +++ b/tests/Builder/Stage/ListSearchIndexesStageTest.php @@ -0,0 +1,46 @@ +assertSamePipeline(Pipelines::ListSearchIndexesReturnASingleSearchIndexById, $pipeline); + } + + public function testReturnASingleSearchIndexByName(): void + { + $pipeline = new Pipeline( + Stage::listSearchIndexes( + name: 'synonym-mappings', + ), + ); + + $this->assertSamePipeline(Pipelines::ListSearchIndexesReturnASingleSearchIndexByName, $pipeline); + } + + public function testReturnAllSearchIndexes(): void + { + $pipeline = new Pipeline( + Stage::listSearchIndexes(), + ); + + $this->assertSamePipeline(Pipelines::ListSearchIndexesReturnAllSearchIndexes, $pipeline); + } +} diff --git a/tests/Builder/Stage/ListSessionsStageTest.php b/tests/Builder/Stage/ListSessionsStageTest.php new file mode 100644 index 000000000..a6d68e3b1 --- /dev/null +++ b/tests/Builder/Stage/ListSessionsStageTest.php @@ -0,0 +1,50 @@ +assertSamePipeline(Pipelines::ListSessionsListAllSessions, $pipeline); + } + + public function testListAllSessionsForTheCurrentUser(): void + { + $pipeline = new Pipeline( + Stage::listSessions(), + ); + + $this->assertSamePipeline(Pipelines::ListSessionsListAllSessionsForTheCurrentUser, $pipeline); + } + + public function testListAllSessionsForTheSpecifiedUsers(): void + { + $pipeline = new Pipeline( + Stage::listSessions( + users: [ + object(user: 'myAppReader', db: 'test'), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::ListSessionsListAllSessionsForTheSpecifiedUsers, $pipeline); + } +} diff --git a/tests/Builder/Stage/LookupStageTest.php b/tests/Builder/Stage/LookupStageTest.php new file mode 100644 index 000000000..3b541ebb4 --- /dev/null +++ b/tests/Builder/Stage/LookupStageTest.php @@ -0,0 +1,161 @@ +assertSamePipeline(Pipelines::LookupPerformAConciseCorrelatedSubqueryWithLookup, $pipeline); + } + + public function testPerformASingleEqualityJoinWithLookup(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'inventory', + localField: 'item', + foreignField: 'sku', + as: 'inventory_docs', + ), + ); + + $this->assertSamePipeline(Pipelines::LookupPerformASingleEqualityJoinWithLookup, $pipeline); + } + + public function testPerformAnUncorrelatedSubqueryWithLookup(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'holidays', + pipeline: new Pipeline( + Stage::match( + year: 2018, + ), + Stage::project( + _id: 0, + date: object( + name: Expression::stringFieldPath('name'), + date: Expression::dateFieldPath('date'), + ), + ), + Stage::replaceRoot(Expression::objectFieldPath('date')), + ), + as: 'holidays', + ), + ); + + $this->assertSamePipeline(Pipelines::LookupPerformAnUncorrelatedSubqueryWithLookup, $pipeline); + } + + public function testPerformMultipleJoinsAndACorrelatedSubqueryWithLookup(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'warehouses', + let: object( + order_item: Expression::fieldPath('item'), + order_qty: Expression::intFieldPath('ordered'), + ), + pipeline: new Pipeline( + Stage::match( + Query::expr( + Expression::and( + Expression::eq( + Expression::stringFieldPath('stock_item'), + Expression::variable('order_item'), + ), + Expression::gte( + Expression::intFieldPath('instock'), + Expression::variable('order_qty'), + ), + ), + ), + ), + Stage::project( + stock_item: 0, + _id: 0, + ), + ), + as: 'stockdata', + ), + ); + + $this->assertSamePipeline(Pipelines::LookupPerformMultipleJoinsAndACorrelatedSubqueryWithLookup, $pipeline); + } + + public function testUseLookupWithAnArray(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'members', + localField: 'enrollmentlist', + foreignField: 'name', + as: 'enrollee_info', + ), + ); + + $this->assertSamePipeline(Pipelines::LookupUseLookupWithAnArray, $pipeline); + } + + public function testUseLookupWithMergeObjects(): void + { + $pipeline = new Pipeline( + Stage::lookup( + from: 'items', + localField: 'item', + foreignField: 'item', + as: 'fromItems', + ), + Stage::replaceRoot( + Expression::mergeObjects( + Expression::arrayElemAt( + Expression::arrayFieldPath('fromItems'), + 0, + ), + Expression::variable('ROOT'), + ), + ), + Stage::project( + fromItems: 0, + ), + ); + + $this->assertSamePipeline(Pipelines::LookupUseLookupWithMergeObjects, $pipeline); + } +} diff --git a/tests/Builder/Stage/MatchStageTest.php b/tests/Builder/Stage/MatchStageTest.php new file mode 100644 index 000000000..31c2d9554 --- /dev/null +++ b/tests/Builder/Stage/MatchStageTest.php @@ -0,0 +1,53 @@ +assertSamePipeline(Pipelines::MatchEqualityMatch, $pipeline); + } + + public function testPerformACount(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::or( + Query::query( + score: [ + Query::gt(70), + Query::lt(90), + ], + ), + Query::query( + views: Query::gte(1000), + ), + ), + ), + Stage::group( + _id: null, + count: Accumulator::sum(1), + ), + ); + + $this->assertSamePipeline(Pipelines::MatchPerformACount, $pipeline); + } +} diff --git a/tests/Builder/Stage/MergeStageTest.php b/tests/Builder/Stage/MergeStageTest.php new file mode 100644 index 000000000..7c145fc29 --- /dev/null +++ b/tests/Builder/Stage/MergeStageTest.php @@ -0,0 +1,188 @@ +assertSamePipeline(Pipelines::MergeMergeResultsFromMultipleCollections, $pipeline); + } + + public function testOnDemandMaterializedViewInitialCreation(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: object( + fiscal_year: Expression::stringFieldPath('fiscal_year'), + dept: Expression::stringFieldPath('dept'), + ), + salaries: Accumulator::sum( + Expression::numberFieldPath('salary'), + ), + ), + Stage::merge( + into: object( + db: 'reporting', + coll: 'budgets', + ), + on: '_id', + whenMatched: 'replace', + whenNotMatched: 'insert', + ), + ); + + $this->assertSamePipeline(Pipelines::MergeOnDemandMaterializedViewInitialCreation, $pipeline); + } + + public function testOnDemandMaterializedViewUpdateReplaceData(): void + { + $pipeline = new Pipeline( + Stage::match( + fiscal_year: Query::gte(2019), + ), + Stage::group( + _id: object( + fiscal_year: Expression::stringFieldPath('fiscal_year'), + dept: Expression::stringFieldPath('dept'), + ), + salaries: Accumulator::sum( + Expression::numberFieldPath('salary'), + ), + ), + Stage::merge( + into: object( + db: 'reporting', + coll: 'budgets', + ), + on: '_id', + whenMatched: 'replace', + whenNotMatched: 'insert', + ), + ); + + $this->assertSamePipeline(Pipelines::MergeOnDemandMaterializedViewUpdateReplaceData, $pipeline); + } + + public function testOnlyInsertNewData(): void + { + $pipeline = new Pipeline( + Stage::match( + fiscal_year: 2019, + ), + Stage::group( + _id: object( + fiscal_year: Expression::stringFieldPath('fiscal_year'), + dept: Expression::stringFieldPath('dept'), + ), + employees: Accumulator::push( + Expression::numberFieldPath('employee'), + ), + ), + Stage::project( + _id: 0, + dept: Expression::fieldPath('_id.dept'), + fiscal_year: Expression::fieldPath('_id.fiscal_year'), + employees: 1, + ), + Stage::merge( + into: object( + db: 'reporting', + coll: 'orgArchive', + ), + on: ['dept', 'fiscal_year'], + whenMatched: 'fail', + ), + ); + + $this->assertSamePipeline(Pipelines::MergeOnlyInsertNewData, $pipeline); + } + + public function testUseThePipelineToCustomizeTheMerge(): void + { + $pipeline = new Pipeline( + Stage::match( + date: [ + Query::gte(new UTCDateTime(1557187200000)), + Query::lt(new UTCDateTime(1557273600000)), + ], + ), + Stage::project( + _id: Expression::dateToString( + format: '%Y-%m', + date: Expression::dateFieldPath('date'), + ), + thumbsup: 1, + thumbsdown: 1, + ), + Stage::merge( + into: 'monthlytotals', + on: '_id', + whenMatched: new Pipeline( + Stage::addFields( + thumbsup: Expression::add( + Expression::numberFieldPath('thumbsup'), + Expression::variable('new.thumbsup'), + ), + thumbsdown: Expression::add( + Expression::numberFieldPath('thumbsdown'), + Expression::variable('new.thumbsdown'), + ), + ), + ), + whenNotMatched: 'insert', + ), + ); + + $this->assertSamePipeline(Pipelines::MergeUseThePipelineToCustomizeTheMerge, $pipeline); + } + + public function testUseVariablesToCustomizeTheMerge(): void + { + $pipeline = new Pipeline( + Stage::merge( + into: 'cakeSales', + let: object( + year: '2020', + ), + whenMatched: new Pipeline( + Stage::addFields( + salesYear: Expression::variable('year'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::MergeUseVariablesToCustomizeTheMerge, $pipeline); + } +} diff --git a/tests/Builder/Stage/OutStageTest.php b/tests/Builder/Stage/OutStageTest.php new file mode 100644 index 000000000..a8ec9fb77 --- /dev/null +++ b/tests/Builder/Stage/OutStageTest.php @@ -0,0 +1,54 @@ +assertSamePipeline(Pipelines::OutOutputToADifferentDatabase, $pipeline); + } + + public function testOutputToSameDatabase(): void + { + $pipeline = new Pipeline( + Stage::group( + _id: Expression::stringFieldPath('author'), + books: Accumulator::push( + Expression::stringFieldPath('title'), + ), + ), + Stage::out('authors'), + ); + + $this->assertSamePipeline(Pipelines::OutOutputToSameDatabase, $pipeline); + } +} diff --git a/tests/Builder/Stage/Pipelines.php b/tests/Builder/Stage/Pipelines.php new file mode 100644 index 000000000..81f5b8fe3 --- /dev/null +++ b/tests/Builder/Stage/Pipelines.php @@ -0,0 +1,4033 @@ +assertSamePipeline(Pipelines::PlanCacheStatsFindCacheEntryDetailsForAQueryHash, $pipeline); + } + + public function testReturnInformationForAllEntriesInTheQueryCache(): void + { + $pipeline = new Pipeline( + Stage::planCacheStats(), + ); + + $this->assertSamePipeline(Pipelines::PlanCacheStatsReturnInformationForAllEntriesInTheQueryCache, $pipeline); + } +} diff --git a/tests/Builder/Stage/ProjectStageTest.php b/tests/Builder/Stage/ProjectStageTest.php new file mode 100644 index 000000000..9e30d731e --- /dev/null +++ b/tests/Builder/Stage/ProjectStageTest.php @@ -0,0 +1,164 @@ + 1, + 'author.last' => 1, + 'author.middle' => Expression::cond( + if: Expression::eq( + '', + Expression::stringFieldPath('author.middle'), + ), + then: Expression::variable('REMOVE'), + else: Expression::stringFieldPath('author.middle'), + ), + ], + title: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectConditionallyExcludeFields, $pipeline); + } + + public function testExcludeFieldsFromEmbeddedDocuments(): void + { + $pipeline = new Pipeline( + // Both stages are equivalents + Stage::project( + ...['author.first' => 0], + ...['lastModified' => 0], + ), + Stage::project( + author: object(first: 0), + lastModified: 0, + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectExcludeFieldsFromEmbeddedDocuments, $pipeline); + } + + public function testExcludeFieldsFromOutputDocuments(): void + { + $pipeline = new Pipeline( + Stage::project( + lastModified: 0, + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectExcludeFieldsFromOutputDocuments, $pipeline); + } + + public function testIncludeComputedFields(): void + { + $pipeline = new Pipeline( + Stage::project( + title: 1, + isbn: object( + prefix: Expression::substr( + Expression::stringFieldPath('isbn'), + 0, + 3, + ), + group: Expression::substr( + Expression::stringFieldPath('isbn'), + 3, + 2, + ), + publisher: Expression::substr( + Expression::stringFieldPath('isbn'), + 5, + 4, + ), + title: Expression::substr( + Expression::stringFieldPath('isbn'), + 9, + 3, + ), + checkDigit: Expression::substr( + Expression::stringFieldPath('isbn'), + 12, + 1, + ), + ), + lastName: Expression::stringFieldPath('author.last'), + copiesSold: Expression::intFieldPath('copies'), + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectIncludeComputedFields, $pipeline); + } + + public function testIncludeSpecificFieldsFromEmbeddedDocuments(): void + { + $pipeline = new Pipeline( + Stage::project( + ...['stop.title' => 1], + ), + Stage::project( + stop: object(title: 1), + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectIncludeSpecificFieldsFromEmbeddedDocuments, $pipeline); + } + + public function testIncludeSpecificFieldsInOutputDocuments(): void + { + $pipeline = new Pipeline( + Stage::project( + title: 1, + author: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectIncludeSpecificFieldsInOutputDocuments, $pipeline); + } + + public function testProjectNewArrayFields(): void + { + $pipeline = new Pipeline( + Stage::project( + myArray: [ + Expression::fieldPath('x'), + Expression::fieldPath('y'), + ], + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectProjectNewArrayFields, $pipeline); + } + + public function testSuppressIdFieldInTheOutputDocuments(): void + { + $pipeline = new Pipeline( + Stage::project( + _id: 0, + title: 1, + author: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::ProjectSuppressIdFieldInTheOutputDocuments, $pipeline); + } +} diff --git a/tests/Builder/Stage/RedactStageTest.php b/tests/Builder/Stage/RedactStageTest.php new file mode 100644 index 000000000..867ca5919 --- /dev/null +++ b/tests/Builder/Stage/RedactStageTest.php @@ -0,0 +1,63 @@ +assertSamePipeline(Pipelines::RedactEvaluateAccessAtEveryDocumentLevel, $pipeline); + } + + public function testExcludeAllFieldsAtAGivenLevel(): void + { + $pipeline = new Pipeline( + Stage::match( + status: 'A', + ), + Stage::redact( + Expression::cond( + if: Expression::eq( + Expression::intFieldPath('level'), + 5, + ), + then: Expression::variable('PRUNE'), + else: Expression::variable('DESCEND'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::RedactExcludeAllFieldsAtAGivenLevel, $pipeline); + } +} diff --git a/tests/Builder/Stage/ReplaceRootStageTest.php b/tests/Builder/Stage/ReplaceRootStageTest.php new file mode 100644 index 000000000..436bde704 --- /dev/null +++ b/tests/Builder/Stage/ReplaceRootStageTest.php @@ -0,0 +1,77 @@ + Query::gte(90)], + ), + Stage::replaceRoot(Expression::objectFieldPath('grades')), + ); + + $this->assertSamePipeline(Pipelines::ReplaceRootWithADocumentNestedInAnArray, $pipeline); + } + + public function testWithANewDocumentCreatedFromROOTAndADefaultDocument(): void + { + $pipeline = new Pipeline( + Stage::replaceRoot( + Expression::mergeObjects( + object(_id: '', name: '', email: '', cell: '', home: ''), + Expression::variable('ROOT'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceRootWithANewDocumentCreatedFromROOTAndADefaultDocument, $pipeline); + } + + public function testWithANewlyCreatedDocument(): void + { + $pipeline = new Pipeline( + Stage::replaceRoot( + object( + full_name: Expression::concat( + Expression::stringFieldPath('first_name'), + ' ', + Expression::stringFieldPath('last_name'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceRootWithANewlyCreatedDocument, $pipeline); + } + + public function testWithAnEmbeddedDocumentField(): void + { + $pipeline = new Pipeline( + Stage::replaceRoot( + Expression::mergeObjects( + object(dogs: 0, cats: 0, birds: 0, fish: 0), + Expression::objectFieldPath('pets'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceRootWithAnEmbeddedDocumentField, $pipeline); + } +} diff --git a/tests/Builder/Stage/ReplaceWithStageTest.php b/tests/Builder/Stage/ReplaceWithStageTest.php new file mode 100644 index 000000000..ad0edfbc5 --- /dev/null +++ b/tests/Builder/Stage/ReplaceWithStageTest.php @@ -0,0 +1,83 @@ + Query::gte(90)], + ), + Stage::replaceWith(Expression::objectFieldPath('grades')), + ); + + $this->assertSamePipeline(Pipelines::ReplaceWithADocumentNestedInAnArray, $pipeline); + } + + public function testANewDocumentCreatedFromROOTAndADefaultDocument(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::mergeObjects( + object(_id: '', name: '', email: '', cell: '', home: ''), + Expression::variable('ROOT'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceWithANewDocumentCreatedFromROOTAndADefaultDocument, $pipeline); + } + + public function testANewlyCreatedDocument(): void + { + $pipeline = new Pipeline( + Stage::match( + status: 'C', + ), + Stage::replaceWith( + object( + _id: Expression::objectFieldPath('_id'), + item: Expression::fieldPath('item'), + amount: Expression::multiply( + Expression::numberFieldPath('price'), + Expression::numberFieldPath('quantity'), + ), + status: 'Complete', + asofDate: Expression::variable('NOW'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceWithANewlyCreatedDocument, $pipeline); + } + + public function testAnEmbeddedDocumentField(): void + { + $pipeline = new Pipeline( + Stage::replaceWith( + Expression::mergeObjects( + object(dogs: 0, cats: 0, birds: 0, fish: 0), + Expression::objectFieldPath('pets'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::ReplaceWithAnEmbeddedDocumentField, $pipeline); + } +} diff --git a/tests/Builder/Stage/SampleStageTest.php b/tests/Builder/Stage/SampleStageTest.php new file mode 100644 index 000000000..5ac8efd4a --- /dev/null +++ b/tests/Builder/Stage/SampleStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::SampleExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/SearchMetaStageTest.php b/tests/Builder/Stage/SearchMetaStageTest.php new file mode 100644 index 000000000..6ded31935 --- /dev/null +++ b/tests/Builder/Stage/SearchMetaStageTest.php @@ -0,0 +1,151 @@ +assertSamePipeline(Pipelines::SearchMetaAutocompleteBucketResultsThroughFacetQueries, $pipeline); + } + + public function testDateFacet(): void + { + $pipeline = new Pipeline( + Stage::searchMeta( + Search::facet( + operator: Search::range( + path: 'released', + gte: new UTCDateTime(new DateTimeImmutable('2000-01-01')), + lte: new UTCDateTime(new DateTimeImmutable('2015-01-31')), + ), + facets: object( + yearFacet: object( + type: 'date', + path: 'released', + boundaries: [ + new UTCDateTime(new DateTimeImmutable('2000-01-01')), + new UTCDateTime(new DateTimeImmutable('2005-01-01')), + new UTCDateTime(new DateTimeImmutable('2010-01-01')), + new UTCDateTime(new DateTimeImmutable('2015-01-01')), + ], + default: 'other', + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchMetaDateFacet, $pipeline); + } + + public function testExample(): void + { + $pipeline = new Pipeline( + Stage::searchMeta( + Search::range( + path: 'year', + gte: 1998, + lt: 1999, + ), + count: object(type: 'total'), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchMetaExample, $pipeline); + } + + public function testMetadataResults(): void + { + $pipeline = new Pipeline( + Stage::searchMeta( + Search::facet( + operator: Search::range( + path: 'released', + gte: new UTCDateTime(new DateTimeImmutable('2000-01-01')), + lte: new UTCDateTime(new DateTimeImmutable('2015-01-31')), + ), + facets: object( + directorsFacet: object( + type: 'string', + path: 'directors', + numBuckets: 7, + ), + yearFacet: object( + type: 'number', + path: 'year', + boundaries: [ + 2000, + 2005, + 2010, + 2015, + ], + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchMetaMetadataResults, $pipeline); + } + + public function testYearFacet(): void + { + $pipeline = new Pipeline( + Stage::searchMeta( + Search::facet( + operator: Search::range( + path: 'year', + gte: 1980, + lte: 2000, + ), + facets: object( + yearFacet: object( + type: 'number', + path: 'year', + boundaries: [ + 1980, + 1990, + 2000, + ], + default: 'other', + ), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchMetaYearFacet, $pipeline); + } +} diff --git a/tests/Builder/Stage/SearchStageTest.php b/tests/Builder/Stage/SearchStageTest.php new file mode 100644 index 000000000..0af9d7742 --- /dev/null +++ b/tests/Builder/Stage/SearchStageTest.php @@ -0,0 +1,225 @@ +assertSamePipeline(Pipelines::SearchCountResults, $pipeline); + } + + public function testDateSearchAndSort(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: 'released', + gt: new UTCDateTime(new DateTime('2010-01-01')), + lt: new UTCDateTime(new DateTime('2015-01-01')), + ), + sort: object( + released: -1, + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + released: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::SearchDateSearchAndSort, $pipeline); + } + + public function testExample(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::near( + path: 'released', + origin: new UTCDateTime(new DateTime('2011-09-01T00:00:00.000+00:00')), + pivot: 7776000000, + ), + ), + Stage::project(_id: 0, title: 1, released: 1), + Stage::limit(5), + Stage::facet( + docs: [], + meta: new Pipeline( + Stage::replaceWith(Expression::variable('SEARCH_META')), + Stage::limit(1), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchExample, $pipeline); + } + + public function testNumberSearchAndSort(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::range( + path: 'awards.wins', + gt: 3, + ), + sort: ['awards.wins' => -1], + ), + Stage::limit(5), + Stage::project(...[ + '_id' => 0, + 'title' => 1, + 'awards.wins' => 1, + ]), + ); + + $this->assertSamePipeline(Pipelines::SearchNumberSearchAndSort, $pipeline); + } + + public function testPaginateResultsAfterAToken(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'war', + ), + searchAfter: 'CMtJGgYQuq+ngwgaCSkAjBYH7AAAAA==', + sort: object( + score: ['$meta' => 'searchScore'], + released: 1, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchPaginateResultsAfterAToken, $pipeline); + } + + public function testPaginateResultsBeforeAToken(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'war', + ), + searchBefore: 'CJ6kARoGELqvp4MIGgkpACDA3U8BAAA=', + sort: object( + score: ['$meta' => 'searchScore'], + released: 1, + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SearchPaginateResultsBeforeAToken, $pipeline); + } + + public function testReturnStoredSourceFields(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'baseball', + ), + returnStoredSource: true, + ), + Stage::match( + Query::or( + Query::query(...['imdb.rating' => Query::gt(8.2)]), + Query::query(...['imdb.votes' => Query::gte(4500)]), + ), + ), + Stage::lookup( + as: 'document', + from: 'movies', + localField: '_id', + foreignField: '_id', + ), + ); + + $this->assertSamePipeline(Pipelines::SearchReturnStoredSourceFields, $pipeline); + } + + public function testSortByScore(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + path: 'title', + query: 'story', + ), + sort: object( + score: [ + '$meta' => 'searchScore', + 'order' => 1, + ], + ), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + score: ['$meta' => 'searchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::SearchSortByScore, $pipeline); + } + + public function testTrackSearchTerms(): void + { + $pipeline = new Pipeline( + Stage::search( + Search::text( + query: 'summer', + path: 'title', + ), + tracking: object(searchTerms: 'summer'), + ), + Stage::limit(5), + Stage::project( + _id: 0, + title: 1, + ), + ); + + $this->assertSamePipeline(Pipelines::SearchTrackSearchTerms, $pipeline); + } +} diff --git a/tests/Builder/Stage/SetStageTest.php b/tests/Builder/Stage/SetStageTest.php new file mode 100644 index 000000000..64193b8b9 --- /dev/null +++ b/tests/Builder/Stage/SetStageTest.php @@ -0,0 +1,89 @@ +assertSamePipeline(Pipelines::SetAddElementToAnArray, $pipeline); + } + + public function testAddingFieldsToAnEmbeddedDocument(): void + { + $pipeline = new Pipeline( + Stage::set( + ...['specs.fuel_type' => 'unleaded'], + ), + ); + + $this->assertSamePipeline(Pipelines::SetAddingFieldsToAnEmbeddedDocument, $pipeline); + } + + public function testCreatingANewFieldWithExistingFields(): void + { + $pipeline = new Pipeline( + Stage::set( + quizAverage: Expression::avg( + Expression::numberFieldPath('quiz'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetCreatingANewFieldWithExistingFields, $pipeline); + } + + public function testOverwritingAnExistingField(): void + { + $pipeline = new Pipeline( + Stage::set(cats: 20), + ); + + $this->assertSamePipeline(Pipelines::SetOverwritingAnExistingField, $pipeline); + } + + public function testUsingTwoSetStages(): void + { + $pipeline = new Pipeline( + Stage::set( + totalHomework: Expression::sum( + Expression::arrayFieldPath('homework'), + ), + totalQuiz: Expression::sum( + Expression::arrayFieldPath('quiz'), + ), + ), + Stage::set( + totalScore: Expression::add( + Expression::numberFieldPath('totalHomework'), + Expression::numberFieldPath('totalQuiz'), + Expression::numberFieldPath('extraCredit'), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetUsingTwoSetStages, $pipeline); + } +} diff --git a/tests/Builder/Stage/SetWindowFieldsStageTest.php b/tests/Builder/Stage/SetWindowFieldsStageTest.php new file mode 100644 index 000000000..442d959bf --- /dev/null +++ b/tests/Builder/Stage/SetWindowFieldsStageTest.php @@ -0,0 +1,175 @@ +assertSamePipeline(Pipelines::SetWindowFieldsRangeWindowExample, $pipeline); + } + + public function testUseATimeRangeWindowWithANegativeUpperBound(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object(orderDate: Sort::Asc), + output: object( + recentOrders: Accumulator::outputWindow( + Accumulator::push( + Expression::dateFieldPath('orderDate'), + ), + range: ['unbounded', -10], + unit: TimeUnit::Month, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseATimeRangeWindowWithANegativeUpperBound, $pipeline); + } + + public function testUseATimeRangeWindowWithAPositiveUpperBound(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object(orderDate: Sort::Asc), + output: object( + recentOrders: Accumulator::outputWindow( + Accumulator::push( + Expression::dateFieldPath('orderDate'), + ), + range: ['unbounded', 10], + unit: TimeUnit::Month, + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseATimeRangeWindowWithAPositiveUpperBound, $pipeline); + } + + public function testUseDocumentsWindowToObtainCumulativeAndMaximumQuantityForEachYear(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::year( + Expression::dateFieldPath('orderDate'), + ), + sortBy: object(orderDate: Sort::Asc), + output: object( + cumulativeQuantityForYear: Accumulator::outputWindow( + Accumulator::sum( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + maximumQuantityForYear: Accumulator::outputWindow( + Accumulator::max( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'unbounded'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseDocumentsWindowToObtainCumulativeAndMaximumQuantityForEachYear, $pipeline); + } + + public function testUseDocumentsWindowToObtainCumulativeQuantityForEachState(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::stringFieldPath('state'), + sortBy: object(orderDate: Sort::Asc), + output: object( + cumulativeQuantityForState: Accumulator::outputWindow( + Accumulator::sum( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseDocumentsWindowToObtainCumulativeQuantityForEachState, $pipeline); + } + + public function testUseDocumentsWindowToObtainCumulativeQuantityForEachYear(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::year( + Expression::dateFieldPath('orderDate'), + ), + sortBy: object(orderDate: Sort::Asc), + output: object( + cumulativeQuantityForYear: Accumulator::outputWindow( + Accumulator::sum( + Expression::numberFieldPath('quantity'), + ), + documents: ['unbounded', 'current'], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseDocumentsWindowToObtainCumulativeQuantityForEachYear, $pipeline); + } + + public function testUseDocumentsWindowToObtainMovingAverageQuantityForEachYear(): void + { + $pipeline = new Pipeline( + Stage::setWindowFields( + partitionBy: Expression::year( + Expression::dateFieldPath('orderDate'), + ), + sortBy: object(orderDate: Sort::Asc), + output: object( + averageQuantity: Accumulator::outputWindow( + Accumulator::avg( + Expression::numberFieldPath('quantity'), + ), + documents: [-1, 0], + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::SetWindowFieldsUseDocumentsWindowToObtainMovingAverageQuantityForEachYear, $pipeline); + } +} diff --git a/tests/Builder/Stage/ShardedDataDistributionStageTest.php b/tests/Builder/Stage/ShardedDataDistributionStageTest.php new file mode 100644 index 000000000..f9c0db9bc --- /dev/null +++ b/tests/Builder/Stage/ShardedDataDistributionStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::ShardedDataDistributionExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/SkipStageTest.php b/tests/Builder/Stage/SkipStageTest.php new file mode 100644 index 000000000..4a716bea1 --- /dev/null +++ b/tests/Builder/Stage/SkipStageTest.php @@ -0,0 +1,24 @@ +assertSamePipeline(Pipelines::SkipExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/SortByCountStageTest.php b/tests/Builder/Stage/SortByCountStageTest.php new file mode 100644 index 000000000..d1a0393a9 --- /dev/null +++ b/tests/Builder/Stage/SortByCountStageTest.php @@ -0,0 +1,30 @@ +assertSamePipeline(Pipelines::SortByCountExample, $pipeline); + } +} diff --git a/tests/Builder/Stage/SortStageTest.php b/tests/Builder/Stage/SortStageTest.php new file mode 100644 index 000000000..5590454bc --- /dev/null +++ b/tests/Builder/Stage/SortStageTest.php @@ -0,0 +1,44 @@ +assertSamePipeline(Pipelines::SortAscendingDescendingSort, $pipeline); + } + + public function testTextScoreMetadataSort(): void + { + $pipeline = new Pipeline( + Stage::match( + Query::text('operating'), + ), + Stage::sort( + score: Sort::TextScore, + posts: Sort::Desc, + ), + ); + + $this->assertSamePipeline(Pipelines::SortTextScoreMetadataSort, $pipeline); + } +} diff --git a/tests/Builder/Stage/UnionWithStageTest.php b/tests/Builder/Stage/UnionWithStageTest.php new file mode 100644 index 000000000..fcf2edf90 --- /dev/null +++ b/tests/Builder/Stage/UnionWithStageTest.php @@ -0,0 +1,78 @@ +assertSamePipeline(Pipelines::UnionWithReport1AllSalesByYearAndStoresAndItems, $pipeline); + } + + public function testReport2AggregatedSalesByItems(): void + { + $pipeline = new Pipeline( + Stage::unionWith('sales_2018'), + Stage::unionWith('sales_2019'), + Stage::unionWith('sales_2020'), + Stage::group( + _id: Expression::stringFieldPath('item'), + total: Accumulator::sum( + Expression::numberFieldPath('quantity'), + ), + ), + Stage::sort( + total: Sort::Desc, + ), + ); + + $this->assertSamePipeline(Pipelines::UnionWithReport2AggregatedSalesByItems, $pipeline); + } +} diff --git a/tests/Builder/Stage/UnsetStageTest.php b/tests/Builder/Stage/UnsetStageTest.php new file mode 100644 index 000000000..217172743 --- /dev/null +++ b/tests/Builder/Stage/UnsetStageTest.php @@ -0,0 +1,49 @@ +assertSamePipeline(Pipelines::UnsetRemoveASingleField, $pipeline); + } + + public function testRemoveEmbeddedFields(): void + { + $pipeline = new Pipeline( + Stage::unset( + 'isbn', + 'author.first', + 'copies.warehouse', + ), + ); + + $this->assertSamePipeline(Pipelines::UnsetRemoveEmbeddedFields, $pipeline); + } + + public function testRemoveTopLevelFields(): void + { + $pipeline = new Pipeline( + Stage::unset( + 'isbn', + 'copies', + ), + ); + + $this->assertSamePipeline(Pipelines::UnsetRemoveTopLevelFields, $pipeline); + } +} diff --git a/tests/Builder/Stage/UnwindStageTest.php b/tests/Builder/Stage/UnwindStageTest.php new file mode 100644 index 000000000..26b1d377c --- /dev/null +++ b/tests/Builder/Stage/UnwindStageTest.php @@ -0,0 +1,91 @@ +assertSamePipeline(Pipelines::UnwindGroupByUnwoundValues, $pipeline); + } + + public function testIncludeArrayIndex(): void + { + $pipeline = new Pipeline( + Stage::unwind( + path: Expression::arrayFieldPath('sizes'), + includeArrayIndex: 'arrayIndex', + ), + ); + + $this->assertSamePipeline(Pipelines::UnwindIncludeArrayIndex, $pipeline); + } + + public function testPreserveNullAndEmptyArrays(): void + { + $pipeline = new Pipeline( + Stage::unwind( + path: Expression::arrayFieldPath('sizes'), + preserveNullAndEmptyArrays: true, + ), + ); + + $this->assertSamePipeline(Pipelines::UnwindPreserveNullAndEmptyArrays, $pipeline); + } + + public function testUnwindArray(): void + { + $pipeline = new Pipeline( + Stage::unwind(Expression::arrayFieldPath('sizes')), + ); + + $this->assertSamePipeline(Pipelines::UnwindUnwindArray, $pipeline); + } + + public function testUnwindEmbeddedArrays(): void + { + $pipeline = new Pipeline( + Stage::unwind(Expression::arrayFieldPath('items')), + Stage::unwind(Expression::arrayFieldPath('items.tags')), + Stage::group( + _id: Expression::fieldPath('items.tags'), + totalSalesAmount: Accumulator::sum( + Expression::multiply( + Expression::numberFieldPath('items.price'), + Expression::numberFieldPath('items.quantity'), + ), + ), + ), + ); + + $this->assertSamePipeline(Pipelines::UnwindUnwindEmbeddedArrays, $pipeline); + } +} diff --git a/tests/Builder/Stage/VectorSearchStageTest.php b/tests/Builder/Stage/VectorSearchStageTest.php new file mode 100644 index 000000000..f6259510a --- /dev/null +++ b/tests/Builder/Stage/VectorSearchStageTest.php @@ -0,0 +1,85 @@ + 'vectorSearchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::VectorSearchANNBasic, $pipeline); + } + + public function testANNFilter(): void + { + $pipeline = new Pipeline( + Stage::vectorSearch( + index: 'vector_index', + limit: 10, + path: 'plot_embedding', + queryVector: [0.02421053, -0.022372592, -0.006231137], + filter: Query::and( + Query::query( + year: Query::lt(1975), + ), + ), + numCandidates: 150, + ), + Stage::project( + _id: 0, + title: 1, + plot: 1, + year: 1, + score: ['$meta' => 'vectorSearchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::VectorSearchANNFilter, $pipeline); + } + + public function testENN(): void + { + $pipeline = new Pipeline( + Stage::vectorSearch( + index: 'vector_index', + limit: 10, + path: 'plot_embedding', + queryVector: [-0.006954097, -0.009932499, -0.001311474], + exact: true, + ), + Stage::project( + _id: 0, + title: 1, + plot: 1, + score: ['$meta' => 'vectorSearchScore'], + ), + ); + + $this->assertSamePipeline(Pipelines::VectorSearchENN, $pipeline); + } +} diff --git a/tests/Builder/Type/CombinedFieldQueryTest.php b/tests/Builder/Type/CombinedFieldQueryTest.php new file mode 100644 index 000000000..e37f61630 --- /dev/null +++ b/tests/Builder/Type/CombinedFieldQueryTest.php @@ -0,0 +1,111 @@ +assertSame([], $fieldQueries->fieldQueries); + } + + public function testSupportedTypes(): void + { + $fieldQueries = new CombinedFieldQuery([ + new EqOperator(1), + ['$gt' => 1], + (object) ['$lt' => 1], + ]); + + $this->assertCount(3, $fieldQueries->fieldQueries); + } + + public function testFlattenCombinedFieldQueries(): void + { + $fieldQueries = new CombinedFieldQuery([ + new CombinedFieldQuery([ + new CombinedFieldQuery([ + ['$lt' => 1], + new CombinedFieldQuery([]), + ]), + ['$gt' => 1], + ]), + ['$gte' => 1], + ]); + + $this->assertCount(3, $fieldQueries->fieldQueries); + } + + #[DataProvider('provideInvalidFieldQuery')] + public function testRejectInvalidFieldQueries(mixed $invalidQuery, string $message = '-'): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage($message); + + new CombinedFieldQuery([$invalidQuery]); + } + + public static function provideInvalidFieldQuery(): Generator + { + yield 'int' => [1, 'Expected filters to be a list of field query operators, array or stdClass, int given']; + yield 'float' => [1.1, 'Expected filters to be a list of field query operators, array or stdClass, float given']; + yield 'string' => ['foo', 'Expected filters to be a list of field query operators, array or stdClass, string given']; + yield 'bool' => [true, 'Expected filters to be a list of field query operators, array or stdClass, bool given']; + yield 'null' => [null, 'Expected filters to be a list of field query operators, array or stdClass, null given']; + yield 'empty array' => [[], 'Operator must contain exactly one key, 0 given']; + yield 'array with two keys' => [['$eq' => 1, '$ne' => 2], 'Operator must contain exactly one key, 2 given']; + yield 'array key without $' => [['eq' => 1], 'Operator must contain exactly one key starting with $, "eq" given']; + yield 'empty object' => [(object) [], 'Operator must contain exactly one key, 0 given']; + yield 'object with two keys' => [(object) ['$eq' => 1, '$ne' => 2], 'Operator must contain exactly one key, 2 given']; + yield 'object key without $' => [(object) ['eq' => 1], 'Operator must contain exactly one key starting with $, "eq" given']; + } + + /** @param array $fieldQueries */ + #[DataProvider('provideDuplicateOperator')] + public function testRejectDuplicateOperator(array $fieldQueries): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Duplicate operator "$eq" detected'); + + new CombinedFieldQuery([ + ['$eq' => 1], + new EqOperator(2), + ]); + } + + public static function provideDuplicateOperator(): Generator + { + yield 'array and FieldQuery' => [ + [ + ['$eq' => 1], + new EqOperator(2), + ], + ]; + + yield 'object and FieldQuery' => [ + [ + (object) ['$gt' => 1], + new GtOperator(2), + ], + ]; + + yield 'object and array' => [ + [ + (object) ['$ne' => 1], + ['$ne' => 2], + ], + ]; + } +} diff --git a/tests/Builder/Type/OutputWindowTest.php b/tests/Builder/Type/OutputWindowTest.php new file mode 100644 index 000000000..0a60f3635 --- /dev/null +++ b/tests/Builder/Type/OutputWindowTest.php @@ -0,0 +1,103 @@ +createMock(WindowInterface::class), + ); + + $this->assertSame($operator, $outputWindow->operator); + $this->assertSame(Optional::Undefined, $outputWindow->window); + } + + public function testWithDocuments(): void + { + $outputWindow = new OutputWindow( + operator: $operator = $this->createMock(WindowInterface::class), + documents: [1, 5], + ); + + $this->assertSame($operator, $outputWindow->operator); + $this->assertEquals((object) ['documents' => [1, 5]], $outputWindow->window); + } + + public function testWithRange(): void + { + $outputWindow = new OutputWindow( + operator: $operator = $this->createMock(WindowInterface::class), + range: [1.2, 5.8], + ); + + $this->assertSame($operator, $outputWindow->operator); + $this->assertEquals((object) ['range' => [1.2, 5.8]], $outputWindow->window); + } + + public function testWithUnit(): void + { + $outputWindow = new OutputWindow( + operator: $operator = $this->createMock(WindowInterface::class), + unit: TimeUnit::Day, + ); + + $this->assertSame($operator, $outputWindow->operator); + $this->assertEquals((object) ['unit' => TimeUnit::Day], $outputWindow->window); + } + + /** @param array $documents */ + #[DataProvider('provideInvalidDocuments')] + public function testRejectInvalidDocuments(array $documents): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected $documents argument to be a list of 2 string or int'); + + new OutputWindow( + operator: $this->createMock(WindowInterface::class), + documents: $documents, + ); + } + + public static function provideInvalidDocuments(): Generator + { + yield 'too few' => [[1]]; + yield 'too many' => [[1, 2, 3]]; + yield 'invalid boolean' => [[1, true]]; + yield 'invalid float' => [[1, 4.3]]; + yield 'not a list' => [['foo' => 1, 'bar' => 2]]; + } + + /** @param array $range */ + #[DataProvider('provideInvalidRange')] + public function testRejectInvalidRange(array $range): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected $range argument to be a list of 2 string or numeric'); + + new OutputWindow( + operator: $this->createMock(WindowInterface::class), + range: $range, + ); + } + + public static function provideInvalidRange(): Generator + { + yield 'too few' => [[1]]; + yield 'too many' => [[1, 2, 3]]; + yield 'invalid boolean' => [[1, true]]; + yield 'not a list' => [['foo' => 1, 'bar' => 2]]; + } +} diff --git a/tests/Builder/Type/QueryObjectTest.php b/tests/Builder/Type/QueryObjectTest.php new file mode 100644 index 000000000..819495805 --- /dev/null +++ b/tests/Builder/Type/QueryObjectTest.php @@ -0,0 +1,91 @@ +assertSame([], $queryObject->queries); + } + + public function testShortCutQueryObject(): void + { + $query = $this->createMock(QueryInterface::class); + $queryObject = QueryObject::create([$query]); + + $this->assertSame($query, $queryObject); + } + + /** @param array $value */ + #[DataProvider('provideQueryObjectValue')] + public function testCreateQueryObject(array $value, int $expectedCount = 1): void + { + $queryObject = QueryObject::create($value); + + $this->assertCount($expectedCount, $queryObject->queries); + } + + /** @param array $value */ + #[DataProvider('provideQueryObjectValue')] + public function testCreateQueryObjectFromArray(array $value, int $expectedCount = 1): void + { + // $value is wrapped in an array as if the user used an array instead of variadic arguments + $queryObject = QueryObject::create([$value]); + + $this->assertCount($expectedCount, $queryObject->queries); + } + + public static function provideQueryObjectValue(): Generator + { + yield 'int' => [['foo' => 1]]; + yield 'float' => [['foo' => 1.1]]; + yield 'string' => [['foo' => 'bar']]; + yield 'bool' => [['foo' => true]]; + yield 'null' => [['foo' => null]]; + yield 'decimal128' => [['foo' => new BSON\Decimal128('1.1')]]; + yield 'int64' => [[1 => new BSON\Int64(1)]]; + yield 'objectId' => [['foo' => new BSON\ObjectId()]]; + yield 'binary' => [['foo' => new BSON\Binary('foo')]]; + yield 'regex' => [['foo' => new BSON\Regex('foo')]]; + yield 'datetime' => [['foo' => new BSON\UTCDateTime()]]; + yield 'timestamp' => [['foo' => new BSON\Timestamp(1234, 5678)]]; + yield 'bson document' => [['foo' => BSON\Document::fromPHP(['bar' => 'baz'])]]; + yield 'bson array' => [['foo' => BSON\PackedArray::fromPHP(['bar', 'baz'])]]; + yield 'object' => [['foo' => (object) ['bar' => 'baz']]]; + yield 'list' => [['foo' => ['bar', 'baz']]]; + yield 'operator as array' => [['foo' => ['$eq' => 1]]]; + yield 'operator as object' => [['foo' => (object) ['$eq' => 1]]]; + yield 'field query operator' => [['foo' => new EqOperator(1)]]; + yield 'query operator' => [[new CommentOperator('foo'), 'foo' => 1], 2]; + yield 'numeric field with array' => [[1 => [2, 3]]]; + yield 'numeric field with operator' => [[1 => ['$eq' => 2]]]; + } + + public function testFieldQueryList(): void + { + $queryObject = QueryObject::create( + ['foo' => [new GtOperator(1), new LtOperator(5)]], + ); + + $this->assertArrayHasKey('foo', $queryObject->queries); + $this->assertInstanceOf(CombinedFieldQuery::class, $queryObject->queries['foo']); + $this->assertCount(2, $queryObject->queries['foo']->fieldQueries); + } +} diff --git a/tests/Builder/VariableTest.php b/tests/Builder/VariableTest.php new file mode 100644 index 000000000..14137b3aa --- /dev/null +++ b/tests/Builder/VariableTest.php @@ -0,0 +1,68 @@ +assertSame('foo', $variable->name); + $this->assertInstanceOf(Expression\ResolvesToAny::class, $variable); + $this->assertInstanceOf(Expression\Variable::class, $variable); + } + + public function testVariableRejectDollarPrefix(): void + { + $this->expectException(InvalidArgumentException::class); + + new Expression\Variable('$$foo'); + } + + #[DataProvider('provideVariableBuilders')] + public function testSystemVariables($factory): void + { + $variable = $factory(); + $this->assertInstanceOf(Expression\Variable::class, $variable); + $this->assertStringStartsNotWith('$$', $variable->name); + } + + public static function provideVariableBuilders(): Generator + { + yield 'now' => [fn () => Variable::now()]; + yield 'clusterTime' => [fn () => Variable::clusterTime()]; + yield 'root' => [fn () => Variable::root()]; + yield 'current' => [fn () => Variable::current()]; + yield 'remove' => [fn () => Variable::remove()]; + yield 'descend' => [fn () => Variable::descend()]; + yield 'prune' => [fn () => Variable::prune()]; + yield 'keep' => [fn () => Variable::keep()]; + yield 'searchMeta' => [fn () => Variable::searchMeta()]; + yield 'userRoles' => [fn () => Variable::userRoles()]; + } + + public function testCurrent(): void + { + $variable = Variable::current(); + $this->assertInstanceOf(Expression\Variable::class, $variable); + $this->assertSame('CURRENT', $variable->name); + + $variable = Variable::current('foo'); + $this->assertInstanceOf(Expression\Variable::class, $variable); + $this->assertSame('CURRENT.foo', $variable->name); + } + + public function testCustomVariable(): void + { + $this->assertInstanceOf(Expression\Variable::class, Variable::variable('foo')); + } +} diff --git a/tests/ClientFunctionalTest.php b/tests/ClientFunctionalTest.php index 6535c66da..393f23607 100644 --- a/tests/ClientFunctionalTest.php +++ b/tests/ClientFunctionalTest.php @@ -2,15 +2,21 @@ namespace MongoDB\Tests; +use Iterator; +use MongoDB\Builder\Pipeline; +use MongoDB\Builder\Query; +use MongoDB\Builder\Stage; use MongoDB\Client; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Command; use MongoDB\Driver\Manager; +use MongoDB\Driver\Monitoring\CommandSubscriber; use MongoDB\Driver\Session; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use function call_user_func; use function is_callable; +use function iterator_to_array; use function sprintf; /** @@ -18,8 +24,7 @@ */ class ClientFunctionalTest extends FunctionalTestCase { - /** @var Client */ - private $client; + private Client $client; public function setUp(): void { @@ -42,8 +47,7 @@ public function testDropDatabase(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->client->dropDatabase($this->getDatabaseName()); - $this->assertCommandSucceeded($commandResult); + $this->client->dropDatabase($this->getDatabaseName()); $this->assertCollectionCount($this->getNamespace(), 0); } @@ -57,7 +61,7 @@ public function testListDatabases(): void $databases = $this->client->listDatabases(); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); @@ -120,4 +124,41 @@ public function testStartSession(): void { $this->assertInstanceOf(Session::class, $this->client->startSession()); } + + public function testAddAndRemoveSubscriber(): void + { + $client = static::createTestClient(); + + $addedSubscriber = $this->createMock(CommandSubscriber::class); + $addedSubscriber->expects($this->once())->method('commandStarted'); + $client->addSubscriber($addedSubscriber); + + $removedSubscriber = $this->createMock(CommandSubscriber::class); + $removedSubscriber->expects($this->never())->method('commandStarted'); + $client->addSubscriber($removedSubscriber); + $client->removeSubscriber($removedSubscriber); + + $client->getManager()->executeCommand('admin', new Command(['ping' => 1])); + } + + public function testWatchWithBuilderPipeline(): void + { + $this->skipIfChangeStreamIsNotSupported(); + + if ($this->isShardedCluster()) { + $this->markTestSkipped('Test does not apply on sharded clusters: need more than a single getMore call on the change stream.'); + } + + $pipeline = new Pipeline( + Stage::match(operationType: Query::eq('insert')), + ); + // Extract the list of stages for arg type restriction + $pipeline = iterator_to_array($pipeline); + + $changeStream = $this->client->watch($pipeline); + $this->client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->insertOne(['x' => 3]); + $changeStream->next(); + $this->assertTrue($changeStream->valid()); + $this->assertEquals('insert', $changeStream->current()->operationType); + } } diff --git a/tests/ClientTest.php b/tests/ClientTest.php index 192868e75..45d852dc4 100644 --- a/tests/ClientTest.php +++ b/tests/ClientTest.php @@ -3,12 +3,15 @@ namespace MongoDB\Tests; use MongoDB\Client; +use MongoDB\Codec\Encoder; use MongoDB\Driver\ClientEncryption; use MongoDB\Driver\Exception\InvalidArgumentException as DriverInvalidArgumentException; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; /** * Unit tests for the Client class. @@ -22,9 +25,7 @@ public function testConstructorDefaultUri(): void $this->assertEquals('mongodb://127.0.0.1/', (string) $client); } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testConstructorAutoEncryptionOpts(): void { $autoEncryptionOpts = [ @@ -36,34 +37,48 @@ public function testConstructorAutoEncryptionOpts(): void new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); } - /** - * @dataProvider provideInvalidConstructorDriverOptions - */ + #[DoesNotPerformAssertions] + public function testConstructorEmptyKmsProvider(): void + { + $autoEncryptionOpts = [ + 'keyVaultClient' => new Client(static::getUri()), + 'keyVaultNamespace' => 'default.keys', + 'kmsProviders' => ['gcp' => []], + ]; + + new Client(static::getUri(), [], ['autoEncryption' => $autoEncryptionOpts]); + } + + #[DataProvider('provideInvalidConstructorDriverOptions')] public function testConstructorDriverOptionTypeChecks(array $driverOptions, string $exception = InvalidArgumentException::class): void { $this->expectException($exception); new Client(static::getUri(), [], $driverOptions); } - public function provideInvalidConstructorDriverOptions() + public static function provideInvalidConstructorDriverOptions() { $options = []; - foreach ($this->getInvalidArrayValues(true) as $value) { + foreach (self::getInvalidObjectValues() as $value) { + $options[][] = ['builderEncoder' => $value]; + } + + foreach (self::getInvalidArrayValues(true) as $value) { $options[][] = ['typeMap' => $value]; } $options[][] = ['autoEncryption' => ['keyVaultClient' => 'foo']]; - foreach ($this->getInvalidStringValues() as $value) { + foreach (self::getInvalidStringValues() as $value) { $options[][] = ['driver' => ['name' => $value]]; } - foreach ($this->getInvalidStringValues() as $value) { + foreach (self::getInvalidStringValues() as $value) { $options[][] = ['driver' => ['version' => $value]]; } - foreach ($this->getInvalidStringValues() as $value) { + foreach (self::getInvalidStringValues() as $value) { $options[] = [ 'driverOptions' => ['driver' => ['platform' => $value]], 'exception' => DriverInvalidArgumentException::class, @@ -89,6 +104,7 @@ public function testSelectCollectionInheritsOptions(): void ]; $driverOptions = [ + 'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class), 'typeMap' => ['root' => 'array'], ]; @@ -96,10 +112,11 @@ public function testSelectCollectionInheritsOptions(): void $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); $debug = $collection->__debugInfo(); + $this->assertSame($builderEncoder, $debug['builderEncoder']); $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); @@ -109,8 +126,9 @@ public function testSelectCollectionInheritsOptions(): void public function testSelectCollectionPassesOptions(): void { $collectionOptions = [ + 'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -119,10 +137,11 @@ public function testSelectCollectionPassesOptions(): void $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName(), $collectionOptions); $debug = $collection->__debugInfo(); + $this->assertSame($builderEncoder, $debug['builderEncoder']); $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); @@ -133,11 +152,19 @@ public function testGetSelectsDatabaseAndInheritsOptions(): void { $uriOptions = ['w' => WriteConcern::MAJORITY]; - $client = new Client(static::getUri(), $uriOptions); + $driverOptions = [ + 'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class), + 'typeMap' => ['root' => 'array'], + ]; + + $client = new Client(static::getUri(), $uriOptions, $driverOptions); $database = $client->{$this->getDatabaseName()}; $debug = $database->__debugInfo(); + $this->assertSame($builderEncoder, $debug['builderEncoder']); $this->assertSame($this->getDatabaseName(), $debug['databaseName']); + $this->assertIsArray($debug['typeMap']); + $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } @@ -146,11 +173,12 @@ public function testSelectDatabaseInheritsOptions(): void { $uriOptions = [ 'readConcernLevel' => ReadConcern::LOCAL, - 'readPreference' => 'secondaryPreferred', + 'readPreference' => ReadPreference::SECONDARY_PREFERRED, 'w' => WriteConcern::MAJORITY, ]; $driverOptions = [ + 'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class), 'typeMap' => ['root' => 'array'], ]; @@ -158,10 +186,11 @@ public function testSelectDatabaseInheritsOptions(): void $database = $client->selectDatabase($this->getDatabaseName()); $debug = $database->__debugInfo(); + $this->assertSame($builderEncoder, $debug['builderEncoder']); $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); @@ -171,8 +200,9 @@ public function testSelectDatabaseInheritsOptions(): void public function testSelectDatabasePassesOptions(): void { $databaseOptions = [ + 'builderEncoder' => $builderEncoder = $this->createMock(Encoder::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -184,7 +214,7 @@ public function testSelectDatabasePassesOptions(): void $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); diff --git a/tests/Collection/BuilderCollectionFunctionalTest.php b/tests/Collection/BuilderCollectionFunctionalTest.php new file mode 100644 index 000000000..484ad972d --- /dev/null +++ b/tests/Collection/BuilderCollectionFunctionalTest.php @@ -0,0 +1,301 @@ +collection->insertMany([['x' => 1], ['x' => 2], ['x' => 2]]); + } + + #[TestWith([true])] + #[TestWith([false])] + public function testAggregate(bool $pipelineAsArray): void + { + $this->collection->insertMany([['x' => 10], ['x' => 10], ['x' => 10]]); + $pipeline = [ + Stage::bucketAuto( + groupBy: Expression::intFieldPath('x'), + buckets: 2, + ), + ]; + + if (! $pipelineAsArray) { + $pipeline = new Pipeline(...$pipeline); + } + + $results = $this->collection->aggregate($pipeline)->toArray(); + $this->assertCount(2, $results); + } + + public function testBulkWriteDeleteMany(): void + { + $result = $this->collection->bulkWrite([ + [ + 'deleteMany' => [ + Query::query(x: Query::gt(1)), + ], + ], + ]); + $this->assertEquals(2, $result->getDeletedCount()); + } + + public function testBulkWriteDeleteOne(): void + { + $result = $this->collection->bulkWrite([ + [ + 'deleteOne' => [ + Query::query(x: Query::eq(1)), + ], + ], + ]); + $this->assertEquals(1, $result->getDeletedCount()); + } + + public function testBulkWriteReplaceOne(): void + { + $result = $this->collection->bulkWrite([ + [ + 'replaceOne' => [ + Query::query(x: Query::eq(1)), + ['x' => 3], + ], + ], + ]); + $this->assertEquals(1, $result->getModifiedCount()); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testBulkWriteUpdateMany(): void + { + $result = $this->collection->bulkWrite([ + [ + 'updateMany' => [ + Query::query(x: Query::gt(1)), + // @todo Use Builder when update operators are supported by PHPLIB-1507 + ['$set' => ['x' => 3]], + ], + ], + ]); + $this->assertEquals(2, $result->getModifiedCount()); + + $result = $this->collection->find(Query::query(x: Query::eq(3)))->toArray(); + $this->assertCount(2, $result); + $this->assertEquals(3, $result[0]->x); + } + + public function testBulkWriteUpdateOne(): void + { + $result = $this->collection->bulkWrite([ + [ + 'updateOne' => [ + Query::query(x: Query::eq(1)), + // @todo Use Builder when update operators are supported by PHPLIB-1507 + ['$set' => ['x' => 3]], + ], + ], + ]); + + $this->assertEquals(1, $result->getModifiedCount()); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testCountDocuments(): void + { + $result = $this->collection->countDocuments(Query::query(x: Query::gt(1))); + $this->assertEquals(2, $result); + } + + public function testDeleteMany(): void + { + $result = $this->collection->deleteMany(Query::query(x: Query::gt(1))); + $this->assertEquals(2, $result->getDeletedCount()); + } + + public function testDeleteOne(): void + { + $result = $this->collection->deleteOne(Query::query(x: Query::gt(1))); + $this->assertEquals(1, $result->getDeletedCount()); + } + + public function testDistinct(): void + { + $result = $this->collection->distinct('x', Query::query(x: Query::gt(1))); + $this->assertEquals([2], $result); + } + + public function testFind(): void + { + $results = $this->collection->find(Query::query(x: Query::gt(1)))->toArray(); + $this->assertCount(2, $results); + $this->assertEquals(2, $results[0]->x); + } + + public function testFindOne(): void + { + $result = $this->collection->findOne(Query::query(x: Query::eq(1))); + $this->assertEquals(1, $result->x); + } + + public function testFindOneAndDelete(): void + { + $result = $this->collection->findOneAndDelete(Query::query(x: Query::eq(1))); + $this->assertEquals(1, $result->x); + + $result = $this->collection->find()->toArray(); + $this->assertCount(2, $result); + } + + public function testFindOneAndReplace(): void + { + $this->collection->insertOne(['x' => 1]); + + $result = $this->collection->findOneAndReplace( + Query::query(x: Query::lt(2)), + ['x' => 3], + ); + $this->assertEquals(1, $result->x); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testFindOneAndUpdate(): void + { + $result = $this->collection->findOneAndUpdate( + Query::query(x: Query::lt(2)), + // @todo Use Builder when update operators are supported by PHPLIB-1507 + ['$set' => ['x' => 3]], + ); + $this->assertEquals(1, $result->x); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testFindOneAndUpdateWithPipelineUpdate(): void + { + $result = $this->collection->findOneAndUpdate( + Query::query(x: Query::lt(2)), + new Pipeline( + Stage::set(x: 3), + ), + ); + $this->assertEquals(1, $result->x); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testReplaceOne(): void + { + $this->collection->insertOne(['x' => 1]); + + $result = $this->collection->replaceOne( + Query::query(x: Query::lt(2)), + ['x' => 3], + ); + $this->assertEquals(1, $result->getModifiedCount()); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testUpdateOne(): void + { + $this->collection->insertOne(['x' => 1]); + + $result = $this->collection->updateOne( + Query::query(x: Query::lt(2)), + // @todo Use Builder when update operators are supported by PHPLIB-1507 + ['$set' => ['x' => 3]], + ); + $this->assertEquals(1, $result->getModifiedCount()); + + $result = $this->collection->findOne(Query::query(x: Query::eq(3))); + $this->assertEquals(3, $result->x); + } + + public function testUpdateWithPipeline(): void + { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); + + $result = $this->collection->updateOne( + Query::query(x: Query::lt(2)), + new Pipeline( + Stage::set(x: 3), + ), + ); + + $this->assertEquals(1, $result->getModifiedCount()); + } + + public function testUpdateMany(): void + { + $result = $this->collection->updateMany( + Query::query(x: Query::gt(1)), + // @todo Use Builder when update operators are supported by PHPLIB-1507 + ['$set' => ['x' => 3]], + ); + $this->assertEquals(2, $result->getModifiedCount()); + + $result = $this->collection->find(Query::query(x: Query::eq(3)))->toArray(); + $this->assertCount(2, $result); + $this->assertEquals(3, $result[0]->x); + } + + public function testUpdateManyWithPipeline(): void + { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); + + $result = $this->collection->updateMany( + Query::query(x: Query::gt(1)), + new Pipeline( + Stage::set(x: 3), + ), + ); + $this->assertEquals(2, $result->getModifiedCount()); + + $result = $this->collection->find(Query::query(x: Query::eq(3)))->toArray(); + $this->assertCount(2, $result); + $this->assertEquals(3, $result[0]->x); + } + + #[TestWith([true])] + #[TestWith([false])] + public function testWatch(bool $pipelineAsArray): void + { + $this->skipIfChangeStreamIsNotSupported(); + + if ($this->isShardedCluster()) { + $this->markTestSkipped('Test does not apply on sharded clusters: need more than a single getMore call on the change stream.'); + } + + $pipeline = [ + Stage::match(operationType: Query::eq('insert')), + ]; + + if (! $pipelineAsArray) { + $pipeline = new Pipeline(...$pipeline); + } + + $changeStream = $this->collection->watch($pipeline); + $this->collection->insertOne(['x' => 3]); + $changeStream->next(); + $this->assertTrue($changeStream->valid()); + $this->assertEquals('insert', $changeStream->current()->operationType); + } +} diff --git a/tests/Collection/CodecCollectionFunctionalTest.php b/tests/Collection/CodecCollectionFunctionalTest.php new file mode 100644 index 000000000..2447243d0 --- /dev/null +++ b/tests/Collection/CodecCollectionFunctionalTest.php @@ -0,0 +1,535 @@ +collection = new Collection( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + ['codec' => new TestDocumentCodec()], + ); + } + + public static function provideAggregateOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => [ + TestObject::createDecodedForFixture(2), + TestObject::createDecodedForFixture(3), + ], + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => [ + self::createFixtureResult(2), + self::createFixtureResult(3), + ], + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => [ + Document::fromPHP(self::createFixtureResult(2)), + Document::fromPHP(self::createFixtureResult(3)), + ], + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideAggregateOptions')] + public function testAggregate($expected, $options): void + { + $this->createFixtures(3); + + $cursor = $this->collection->aggregate([['$match' => ['_id' => ['$gt' => 1]]]], $options); + + $this->assertEquals($expected, $cursor->toArray()); + } + + public function testAggregateWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->aggregate([['$match' => ['_id' => ['$gt' => 1]]]], $options); + } + + public static function provideBulkWriteOptions(): Generator + { + $replacedObject = TestObject::createDecodedForFixture(3); + $replacedObject->x->foo = 'baz'; + + yield 'Default codec' => [ + 'expected' => [ + TestObject::createDecodedForFixture(1), + TestObject::createDecodedForFixture(2), + $replacedObject, + TestObject::createDecodedForFixture(4), + ], + 'options' => [], + ]; + + $replacedObject = new BSONDocument(['_id' => 3, 'id' => 3, 'x' => new BSONDocument(['foo' => 'baz']), 'decoded' => false]); + $replacedObject->x->foo = 'baz'; + + yield 'No codec' => [ + 'expected' => [ + self::createFixtureResult(1), + self::createFixtureResult(2), + $replacedObject, + self::createObjectFixtureResult(4, true), + ], + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => [ + Document::fromPHP(self::createFixtureResult(1)), + Document::fromPHP(self::createFixtureResult(2)), + Document::fromPHP($replacedObject), + Document::fromPHP(self::createObjectFixtureResult(4, true)), + ], + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideBulkWriteOptions')] + public function testBulkWrite($expected, $options): void + { + $this->createFixtures(3); + + $replaceObject = TestObject::createForFixture(3); + $replaceObject->x->foo = 'baz'; + + $operations = [ + ['insertOne' => [TestObject::createForFixture(4)]], + ['replaceOne' => [['_id' => 3], $replaceObject]], + ]; + + $result = $this->collection->bulkWrite($operations, $options); + + $this->assertInstanceOf(BulkWriteResult::class, $result); + $this->assertSame(1, $result->getInsertedCount()); + $this->assertSame(1, $result->getMatchedCount()); + $this->assertSame(1, $result->getModifiedCount()); + + // Extract inserted ID when not using codec as it's an automatically generated ObjectId + if ($expected[3] instanceof BSONDocument && $expected[3]->_id === null) { + $expected[3]->_id = $result->getInsertedIds()[0]; + } + + if ($expected[3] instanceof Document && $expected[3]->get('_id') === null) { + $inserted = $expected[3]->toPHP(); + $inserted->_id = $result->getInsertedIds()[0]; + $expected[3] = Document::fromPHP($inserted); + } + + $this->assertEquals( + $expected, + $this->collection->find([], $options)->toArray(), + ); + } + + public static function provideFindOneAndModifyOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => TestObject::createDecodedForFixture(1), + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => self::createFixtureResult(1), + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => Document::fromPHP(self::createFixtureResult(1)), + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideFindOneAndModifyOptions')] + public function testFindOneAndDelete($expected, $options): void + { + $this->createFixtures(1); + + $result = $this->collection->findOneAndDelete(['_id' => 1], $options); + + self::assertEquals($expected, $result); + } + + public function testFindOneAndDeleteWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->findOneAndDelete(['_id' => 1], $options); + } + + #[DataProvider('provideFindOneAndModifyOptions')] + public function testFindOneAndUpdate($expected, $options): void + { + $this->createFixtures(1); + + $result = $this->collection->findOneAndUpdate(['_id' => 1], ['$set' => ['x.foo' => 'baz']], $options); + + self::assertEquals($expected, $result); + } + + public function testFindOneAndUpdateWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->findOneAndUpdate(['_id' => 1], ['$set' => ['x.foo' => 'baz']], $options); + } + + public static function provideFindOneAndReplaceOptions(): Generator + { + $replacedObject = TestObject::createDecodedForFixture(1); + $replacedObject->x->foo = 'baz'; + + yield 'Default codec' => [ + 'expected' => $replacedObject, + 'options' => [], + ]; + + $replacedObject = self::createObjectFixtureResult(1); + $replacedObject->x->foo = 'baz'; + + yield 'No codec' => [ + 'expected' => $replacedObject, + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => Document::FromPHP($replacedObject), + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideFindOneAndReplaceOptions')] + public function testFindOneAndReplace($expected, $options): void + { + $this->createFixtures(1); + + $replaceObject = TestObject::createForFixture(1); + $replaceObject->x->foo = 'baz'; + + $result = $this->collection->findOneAndReplace( + ['_id' => 1], + $replaceObject, + $options + ['returnDocument' => FindOneAndReplace::RETURN_DOCUMENT_AFTER], + ); + + self::assertEquals($expected, $result); + } + + public function testFindOneAndReplaceWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->findOneAndReplace(['_id' => 1], TestObject::createForFixture(1), $options); + } + + public static function provideFindOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => [ + TestObject::createDecodedForFixture(1), + TestObject::createDecodedForFixture(2), + TestObject::createDecodedForFixture(3), + ], + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => [ + self::createFixtureResult(1), + self::createFixtureResult(2), + self::createFixtureResult(3), + ], + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => [ + Document::fromPHP(self::createFixtureResult(1)), + Document::fromPHP(self::createFixtureResult(2)), + Document::fromPHP(self::createFixtureResult(3)), + ], + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideFindOptions')] + public function testFind($expected, $options): void + { + $this->createFixtures(3); + + $cursor = $this->collection->find([], $options); + + $this->assertEquals($expected, $cursor->toArray()); + } + + public function testFindWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->find([], $options); + } + + public static function provideFindOneOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => TestObject::createDecodedForFixture(1), + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => self::createFixtureResult(1), + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => Document::fromPHP(self::createFixtureResult(1)), + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideFindOneOptions')] + public function testFindOne($expected, $options): void + { + $this->createFixtures(1); + + $document = $this->collection->findOne([], $options); + + $this->assertEquals($expected, $document); + } + + public function testFindOneWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->findOne([], $options); + } + + public static function provideInsertManyOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => [ + TestObject::createDecodedForFixture(1), + TestObject::createDecodedForFixture(2), + TestObject::createDecodedForFixture(3), + ], + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => [ + self::createObjectFixtureResult(1, true), + self::createObjectFixtureResult(2, true), + self::createObjectFixtureResult(3, true), + ], + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => [ + Document::fromPHP(self::createObjectFixtureResult(1, true)), + Document::fromPHP(self::createObjectFixtureResult(2, true)), + Document::fromPHP(self::createObjectFixtureResult(3, true)), + ], + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideInsertManyOptions')] + public function testInsertMany($expected, $options): void + { + $documents = [ + TestObject::createForFixture(1), + TestObject::createForFixture(2), + TestObject::createForFixture(3), + ]; + + $result = $this->collection->insertMany($documents, $options); + $this->assertSame(3, $result->getInsertedCount()); + + // Add missing identifiers. This is relevant for the "No codec" data set, as the encoded document will not have + // an "_id" field and the driver will automatically generate one. + foreach ($expected as $index => &$expectedDocument) { + if ($expectedDocument instanceof BSONDocument && $expectedDocument->_id === null) { + $expectedDocument->_id = $result->getInsertedIds()[$index]; + } + + if ($expectedDocument instanceof Document && $expectedDocument->get('_id') === null) { + $inserted = $expectedDocument->toPHP(); + $inserted->_id = $result->getInsertedIds()[$index]; + $expectedDocument = Document::fromPHP($inserted); + } + } + + $this->assertEquals($expected, $this->collection->find([], $options)->toArray()); + } + + public static function provideInsertOneOptions(): Generator + { + yield 'Default codec' => [ + 'expected' => TestObject::createDecodedForFixture(1), + 'options' => [], + ]; + + yield 'No codec' => [ + 'expected' => self::createObjectFixtureResult(1, true), + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => Document::fromPHP(self::createObjectFixtureResult(1, true)), + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideInsertOneOptions')] + public function testInsertOne($expected, $options): void + { + $result = $this->collection->insertOne(TestObject::createForFixture(1), $options); + $this->assertSame(1, $result->getInsertedCount()); + + // Add missing identifiers. This is relevant for the "No codec" data set, as the encoded document will have an + // automatically generated identifier, which needs to be used in the expected document. + if ($expected instanceof BSONDocument && $expected->_id === null) { + $expected->_id = $result->getInsertedId(); + } + + if ($expected instanceof Document && $expected->get('_id') === null) { + $inserted = $expected->toPHP(); + $inserted->_id = $result->getInsertedId(); + $expected = Document::fromPHP($inserted); + } + + $this->assertEquals($expected, $this->collection->findOne([], $options)); + } + + public static function provideReplaceOneOptions(): Generator + { + $replacedObject = TestObject::createDecodedForFixture(1); + $replacedObject->x->foo = 'baz'; + + yield 'Default codec' => [ + 'expected' => $replacedObject, + 'options' => [], + ]; + + $replacedObject = self::createObjectFixtureResult(1); + $replacedObject->x->foo = 'baz'; + + yield 'No codec' => [ + 'expected' => $replacedObject, + 'options' => ['codec' => null], + ]; + + yield 'BSON type map' => [ + 'expected' => Document::fromPHP($replacedObject), + 'options' => ['typeMap' => ['root' => 'bson']], + ]; + } + + #[DataProvider('provideReplaceOneOptions')] + public function testReplaceOne($expected, $options): void + { + $this->createFixtures(1); + + $replaceObject = TestObject::createForFixture(1); + $replaceObject->x->foo = 'baz'; + + $result = $this->collection->replaceOne(['_id' => 1], $replaceObject, $options); + $this->assertSame(1, $result->getMatchedCount()); + $this->assertSame(1, $result->getModifiedCount()); + + $this->assertEquals($expected, $this->collection->findOne([], $options)); + } + + public function testReplaceOneWithCodecAndTypemap(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + $this->collection->replaceOne(['_id' => 1], ['foo' => 'bar'], $options); + } + + /** + * Create data fixtures. + */ + private function createFixtures(int $n, array $executeBulkWriteOptions = []): void + { + $bulkWrite = new BulkWrite(['ordered' => true]); + + for ($i = 1; $i <= $n; $i++) { + $bulkWrite->insert(TestObject::createDocument($i)); + } + + $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite, $executeBulkWriteOptions); + + $this->assertEquals($n, $result->getInsertedCount()); + } + + private static function createFixtureResult(int $id): BSONDocument + { + return new BSONDocument(['_id' => $id, 'x' => new BSONDocument(['foo' => 'bar'])]); + } + + private static function createObjectFixtureResult(int $id, bool $isInserted = false): BSONDocument + { + return new BSONDocument([ + '_id' => $isInserted ? null : $id, + 'id' => $id, + 'x' => new BSONDocument(['foo' => 'bar']), + 'decoded' => false, + ]); + } +} diff --git a/tests/Collection/CollectionFunctionalTest.php b/tests/Collection/CollectionFunctionalTest.php index cd687bc0e..95659381c 100644 --- a/tests/Collection/CollectionFunctionalTest.php +++ b/tests/Collection/CollectionFunctionalTest.php @@ -3,36 +3,39 @@ namespace MongoDB\Tests\Collection; use Closure; -use MongoDB\BSON\Javascript; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Codec\Encoder; use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Exception\UnsupportedException; -use MongoDB\MapReduceResult; use MongoDB\Operation\Count; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; +use ReflectionClass; use TypeError; use function array_filter; use function call_user_func; use function is_scalar; +use function iterator_to_array; use function json_encode; -use function strchr; +use function str_contains; use function usort; -use function version_compare; + +use const JSON_THROW_ON_ERROR; /** * Functional tests for the Collection class. */ class CollectionFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideInvalidDatabaseAndCollectionNames - */ + #[DataProvider('provideInvalidDatabaseAndCollectionNames')] public function testConstructorDatabaseNameArgument($databaseName, string $expectedExceptionClass): void { $this->expectException($expectedExceptionClass); @@ -40,9 +43,7 @@ public function testConstructorDatabaseNameArgument($databaseName, string $expec new Collection($this->manager, $databaseName, $this->getCollectionName()); } - /** - * @dataProvider provideInvalidDatabaseAndCollectionNames - */ + #[DataProvider('provideInvalidDatabaseAndCollectionNames')] public function testConstructorCollectionNameArgument($collectionName, string $expectedExceptionClass): void { $this->expectException($expectedExceptionClass); @@ -50,7 +51,7 @@ public function testConstructorCollectionNameArgument($collectionName, string $e new Collection($this->manager, $this->getDatabaseName(), $collectionName); } - public function provideInvalidDatabaseAndCollectionNames() + public static function provideInvalidDatabaseAndCollectionNames() { return [ [null, TypeError::class], @@ -58,36 +59,39 @@ public function provideInvalidDatabaseAndCollectionNames() ]; } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions(): array { - $options = []; - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } + return self::createOptionDataProvider([ + 'builderEncoder' => self::getInvalidObjectValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); + } - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } + public function testGetBuilderEncoder(): void + { + $collectionOptions = ['builderEncoder' => $this->createMock(Encoder::class)]; + $collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $collectionOptions); - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } + $this->assertSame($collectionOptions['builderEncoder'], $collection->getBuilderEncoder()); + } - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } + public function testGetCodec(): void + { + $collectionOptions = ['codec' => $this->createMock(DocumentCodec::class)]; + $collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), $collectionOptions); - return $options; + $this->assertSame($collectionOptions['codec'], $collection->getCodec()); } public function testGetManager(): void @@ -120,7 +124,7 @@ public function testAggregateWithinTransaction(): void $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -130,7 +134,7 @@ public function testAggregateWithinTransaction(): void $cursor = $this->collection->aggregate( [['$match' => ['_id' => ['$lt' => 3]]]], - ['session' => $session] + ['session' => $session], ); $expected = [ @@ -148,33 +152,41 @@ public function testAggregateWithinTransaction(): void public function testCreateIndexSplitsCommandOptions(): void { + $this->skipIfServerVersion('<', '4.4', 'commitQuorum and comment options are not supported'); + + if ($this->isStandalone()) { + $this->markTestSkipped('commitQuorum is not supported'); + } + (new CommandObserver())->observe( function (): void { $this->collection->createIndex( ['x' => 1], [ + 'comment' => 'foo', + 'commitQuorum' => 'majority', 'maxTimeMS' => 10000, 'session' => $this->manager->startSession(), 'sparse' => true, 'unique' => true, 'writeConcern' => new WriteConcern(1), - ] + ], ); }, function (array $event): void { $command = $event['started']->getCommand(); - $this->assertObjectHasAttribute('lsid', $command); - $this->assertObjectHasAttribute('maxTimeMS', $command); - $this->assertObjectHasAttribute('writeConcern', $command); - $this->assertObjectHasAttribute('sparse', $command->indexes[0]); - $this->assertObjectHasAttribute('unique', $command->indexes[0]); - } + $this->assertObjectHasProperty('comment', $command); + $this->assertObjectHasProperty('commitQuorum', $command); + $this->assertObjectHasProperty('lsid', $command); + $this->assertObjectHasProperty('maxTimeMS', $command); + $this->assertObjectHasProperty('writeConcern', $command); + $this->assertObjectHasProperty('sparse', $command->indexes[0]); + $this->assertObjectHasProperty('unique', $command->indexes[0]); + }, ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments): void { $bulkWrite = new BulkWrite(['ordered' => true]); @@ -203,8 +215,8 @@ public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments return 1; } - $a = json_encode($a); - $b = json_encode($b); + $a = json_encode($a, JSON_THROW_ON_ERROR); + $b = json_encode($b, JSON_THROW_ON_ERROR); } return $a < $b ? -1 : 1; @@ -216,7 +228,7 @@ public function testDistinctWithTypeMap(array $typeMap, array $expectedDocuments $this->assertEquals($expectedDocuments, $values); } - public function provideTypeMapOptionsAndExpectedDocuments() + public static function provideTypeMapOptionsAndExpectedDocuments() { return [ 'No type map' => [ @@ -259,14 +271,11 @@ public function testDrop(): void $writeResult = $this->collection->insertOne(['x' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->drop(); - $this->assertCommandSucceeded($commandResult); + $this->collection->drop(); $this->assertCollectionDoesNotExist($this->getCollectionName()); } - /** - * @todo Move this to a unit test once Manager can be mocked - */ + /** @todo Move this to a unit test once Manager can be mocked */ public function testDropIndexShouldNotAllowWildcardCharacter(): void { $this->expectException(InvalidArgumentException::class); @@ -304,7 +313,7 @@ public function testFindWithinTransaction(): void $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -314,7 +323,7 @@ public function testFindWithinTransaction(): void $cursor = $this->collection->find( ['_id' => ['$lt' => 3]], - ['session' => $session] + ['session' => $session], ); $expected = [ @@ -338,8 +347,7 @@ public function testRenameToSameDatabase(): void $writeResult = $this->collection->insertOne(['_id' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->rename($toCollectionName, null, ['dropTarget' => true]); - $this->assertCommandSucceeded($commandResult); + $this->collection->rename($toCollectionName, null, ['dropTarget' => true]); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName); @@ -367,8 +375,7 @@ public function testRenameToDifferentDatabase(): void $writeResult = $this->collection->insertOne(['_id' => 1]); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->collection->rename($toCollectionName, $toDatabaseName); - $this->assertCommandSucceeded($commandResult); + $this->collection->rename($toCollectionName, $toDatabaseName); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName, $toDatabaseName); @@ -380,8 +387,10 @@ public function testRenameToDifferentDatabase(): void public function testWithOptionsInheritsOptions(): void { $collectionOptions = [ + 'builderEncoder' => $this->createMock(Encoder::class), + 'codec' => $this->createMock(DocumentCodec::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -393,21 +402,28 @@ public function testWithOptionsInheritsOptions(): void $this->assertSame($this->manager, $debug['manager']); $this->assertSame($this->getDatabaseName(), $debug['databaseName']); $this->assertSame($this->getCollectionName(), $debug['collectionName']); - $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); - $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); - $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); - $this->assertIsArray($debug['typeMap']); - $this->assertSame(['root' => 'array'], $debug['typeMap']); - $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); - $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); + + foreach ($collectionOptions as $key => $value) { + $this->assertSame($value, $debug[$key]); + } + + // autoEncryptionEnabled is an internal option not reported via debug info + $collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), ['autoEncryptionEnabled' => true]); + $clone = $collection->withOptions(); + + $rc = new ReflectionClass($clone); + $rp = $rc->getProperty('autoEncryptionEnabled'); + + $this->assertSame(true, $rp->getValue($clone)); } public function testWithOptionsPassesOptions(): void { $collectionOptions = [ + 'builderEncoder' => $this->createMock(Encoder::class), + 'codec' => $this->createMock(DocumentCodec::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -415,68 +431,56 @@ public function testWithOptionsPassesOptions(): void $clone = $this->collection->withOptions($collectionOptions); $debug = $clone->__debugInfo(); - $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); - $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); - $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); - $this->assertIsArray($debug['typeMap']); - $this->assertSame(['root' => 'array'], $debug['typeMap']); - $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); - $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); - } - - /** - * @group matrix-testing-exclude-server-4.4-driver-4.0 - * @group matrix-testing-exclude-server-4.4-driver-4.2 - * @group matrix-testing-exclude-server-5.0-driver-4.0 - * @group matrix-testing-exclude-server-5.0-driver-4.2 - */ - public function testMapReduce(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(1, this.x); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $result = $this->collection->mapReduce($map, $reduce, $out); + foreach ($collectionOptions as $key => $value) { + $this->assertSame($value, $debug[$key]); + } - $this->assertInstanceOf(MapReduceResult::class, $result); - $expected = [ - [ '_id' => 1.0, 'value' => 66.0 ], - ]; + // autoEncryptionEnabled is an internal option not reported via debug info + $clone = $this->collection->withOptions(['autoEncryptionEnabled' => true]); - $this->assertSameDocuments($expected, $result); + $rc = new ReflectionClass($clone); + $rp = $rc->getProperty('autoEncryptionEnabled'); - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } + $this->assertSame(true, $rp->getValue($clone)); } - public function collectionMethodClosures() + public static function collectionMethodClosures() { return [ - [ + 'read-only aggregate' => [ function ($collection, $session, $options = []): void { $collection->aggregate( [['$match' => ['_id' => ['$lt' => 3]]]], + ['session' => $session] + $options, + ); + }, 'r', + ], + + /* Disabled, as write aggregations are not supported in transactions + 'read-write aggregate' => [ + function ($collection, $session, $options = []): void { + $collection->aggregate( + [ + ['$match' => ['_id' => ['$lt' => 3]]], + ['$merge' => $collection . '_out'], + ], ['session' => $session] + $options ); }, 'rw', ], + */ - [ + 'bulkWrite insertOne' => [ function ($collection, $session, $options = []): void { $collection->bulkWrite( [['insertOne' => [['test' => 'foo']]]], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], /* Disabled, as count command can't be used in transactions - [ + 'count' => [ function($collection, $session, $options = []) { $collection->count( [], @@ -486,17 +490,17 @@ function($collection, $session, $options = []) { ], */ - [ + 'countDocuments' => [ function ($collection, $session, $options = []): void { $collection->countDocuments( [], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'r', ], /* Disabled, as it's illegal to use createIndex command in transactions - [ + 'createIndex' => [ function($collection, $session, $options = []) { $collection->createIndex( ['test' => 1], @@ -506,36 +510,36 @@ function($collection, $session, $options = []) { ], */ - [ + 'deleteMany' => [ function ($collection, $session, $options = []): void { $collection->deleteMany( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'deleteOne' => [ function ($collection, $session, $options = []): void { $collection->deleteOne( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'distinct' => [ function ($collection, $session, $options = []): void { $collection->distinct( '_id', [], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'r', ], /* Disabled, as it's illegal to use drop command in transactions - [ + 'drop' => [ function($collection, $session, $options = []) { $collection->drop( ['session' => $session] + $options @@ -545,7 +549,7 @@ function($collection, $session, $options = []) { */ /* Disabled, as it's illegal to use dropIndexes command in transactions - [ + 'dropIndex' => [ function($collection, $session, $options = []) { $collection->dropIndex( '_id_1', @@ -555,7 +559,7 @@ function($collection, $session, $options = []) { ], */ /* Disabled, as it's illegal to use dropIndexes command in transactions - [ + 'dropIndexes' => [ function($collection, $session, $options = []) { $collection->dropIndexes( ['session' => $session] + $options @@ -565,7 +569,7 @@ function($collection, $session, $options = []) { */ /* Disabled, as count command can't be used in transactions - [ + 'estimatedDocumentCount' => [ function($collection, $session, $options = []) { $collection->estimatedDocumentCount( ['session' => $session] + $options @@ -574,76 +578,76 @@ function($collection, $session, $options = []) { ], */ - [ + 'find' => [ function ($collection, $session, $options = []): void { $collection->find( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'r', ], - [ + 'findOne' => [ function ($collection, $session, $options = []): void { $collection->findOne( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'r', ], - [ + 'findOneAndDelete' => [ function ($collection, $session, $options = []): void { $collection->findOneAndDelete( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'findOneAndReplace' => [ function ($collection, $session, $options = []): void { $collection->findOneAndReplace( ['test' => 'foo'], [], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'findOneAndUpdate' => [ function ($collection, $session, $options = []): void { $collection->findOneAndUpdate( ['test' => 'foo'], ['$set' => ['updated' => 1]], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'insertMany' => [ function ($collection, $session, $options = []): void { $collection->insertMany( [ ['test' => 'foo'], ['test' => 'bar'], ], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'insertOne' => [ function ($collection, $session, $options = []): void { $collection->insertOne( ['test' => 'foo'], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], /* Disabled, as it's illegal to use listIndexes command in transactions - [ + 'listIndexes' => [ function($collection, $session, $options = []) { $collection->listIndexes( ['session' => $session] + $options @@ -652,51 +656,38 @@ function($collection, $session, $options = []) { ], */ - /* Disabled, as it's illegal to use mapReduce command in transactions - [ - function($collection, $session, $options = []) { - $collection->mapReduce( - new \MongoDB\BSON\Javascript('function() { emit(this.state, this.pop); }'), - new \MongoDB\BSON\Javascript('function(key, values) { return Array.sum(values) }'), - ['inline' => 1], - ['session' => $session] + $options - ); - }, 'rw' - ], - */ - - [ + 'replaceOne' => [ function ($collection, $session, $options = []): void { $collection->replaceOne( ['test' => 'foo'], [], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'updateMany' => [ function ($collection, $session, $options = []): void { $collection->updateMany( ['test' => 'foo'], ['$set' => ['updated' => 1]], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], - [ + 'updateOne' => [ function ($collection, $session, $options = []): void { $collection->updateOne( ['test' => 'foo'], ['$set' => ['updated' => 1]], - ['session' => $session] + $options + ['session' => $session] + $options, ); }, 'w', ], /* Disabled, as it's illegal to use change streams in transactions - [ + 'watch' => [ function($collection, $session, $options = []) { $collection->watch( [], @@ -708,38 +699,28 @@ function($collection, $session, $options = []) { ]; } - public function collectionReadMethodClosures() + public static function collectionReadMethodClosures(): array { return array_filter( - $this->collectionMethodClosures(), - function ($rw) { - if (strchr($rw[1], 'r') !== false) { - return true; - } - } + self::collectionMethodClosures(), + fn ($rw) => str_contains($rw[1], 'r'), ); } - public function collectionWriteMethodClosures() + public static function collectionWriteMethodClosures(): array { return array_filter( - $this->collectionMethodClosures(), - function ($rw) { - if (strchr($rw[1], 'w') !== false) { - return true; - } - } + self::collectionMethodClosures(), + fn ($rw) => str_contains($rw[1], 'w'), ); } - /** - * @dataProvider collectionMethodClosures - */ - public function testMethodDoesNotInheritReadWriteConcernInTranasaction(Closure $method): void + #[DataProvider('collectionMethodClosures')] + public function testMethodDoesNotInheritReadWriteConcernInTransaction(Closure $method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -754,26 +735,24 @@ function () use ($method, $collection, $session): void { call_user_func($method, $collection, $session); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } - /** - * @dataProvider collectionWriteMethodClosures - */ + #[DataProvider('collectionWriteMethodClosures')] public function testMethodInTransactionWithWriteConcernOption($method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); $this->expectException(UnsupportedException::class); - $this->expectExceptionMessage('"writeConcern" option cannot be specified within a transaction'); + $this->expectExceptionMessage('Cannot set write concern after starting a transaction'); try { call_user_func($method, $this->collection, $session, ['writeConcern' => new WriteConcern(1)]); @@ -782,20 +761,18 @@ public function testMethodInTransactionWithWriteConcernOption($method): void } } - /** - * @dataProvider collectionReadMethodClosures - */ + #[DataProvider('collectionReadMethodClosures')] public function testMethodInTransactionWithReadConcernOption($method): void { $this->skipIfTransactionsAreNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); $this->expectException(UnsupportedException::class); - $this->expectExceptionMessage('"readConcern" option cannot be specified within a transaction'); + $this->expectExceptionMessage('Cannot set read concern after starting a transaction'); try { call_user_func($method, $this->collection, $session, ['readConcern' => new ReadConcern(ReadConcern::LOCAL)]); @@ -804,6 +781,32 @@ public function testMethodInTransactionWithReadConcernOption($method): void } } + public function testListSearchIndexesInheritTypeMap(): void + { + $this->skipIfAtlasSearchIndexIsNotSupported(); + + $collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName(), ['typeMap' => ['root' => 'array']]); + + // Insert a document to create the collection + $collection->insertOne(['_id' => 1]); + + try { + $collection->createSearchIndex(['mappings' => ['dynamic' => false]], ['name' => 'test-search-index']); + } catch (CommandException $e) { + // Ignore duplicate errors in case this test is re-run too quickly + // Index is asynchronously dropped during tearDown, we only need to + // ensure it exists for this test. + if ($e->getCode() !== 68 /* IndexAlreadyExists */) { + throw $e; + } + } + + $indexes = $collection->listSearchIndexes(); + $indexes = iterator_to_array($indexes); + $this->assertCount(1, $indexes); + $this->assertIsArray($indexes[0]); + } + /** * Create data fixtures. */ @@ -814,7 +817,7 @@ private function createFixtures(int $n, array $executeBulkWriteOptions = []): vo for ($i = 1; $i <= $n; $i++) { $bulkWrite->insert([ '_id' => $i, - 'x' => (integer) ($i . $i), + 'x' => (int) ($i . $i), ]); } diff --git a/tests/Collection/CrudSpecFunctionalTest.php b/tests/Collection/CrudSpecFunctionalTest.php deleted file mode 100644 index 4bb7ac174..000000000 --- a/tests/Collection/CrudSpecFunctionalTest.php +++ /dev/null @@ -1,556 +0,0 @@ -expectedCollection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName() . '.expected'); - $this->expectedCollection->drop(); - } - - /** - * @dataProvider provideSpecificationTests - */ - public function testSpecification(array $initialData, array $test, $minServerVersion, $maxServerVersion, $serverless): void - { - if (isset($minServerVersion) || isset($maxServerVersion)) { - $this->checkServerVersion($minServerVersion, $maxServerVersion); - } - - $this->checkServerlessRequirement($serverless); - - $expectedData = $test['outcome']['collection']['data'] ?? null; - $this->initializeData($initialData, $expectedData); - - if (isset($test['outcome']['collection']['name'])) { - $outputCollection = new Collection($this->manager, $this->getDatabaseName(), $test['outcome']['collection']['name']); - $outputCollection->drop(); - } - - $result = null; - $exception = null; - - try { - $result = $this->executeOperation($test['operation']); - } catch (RuntimeException $e) { - $exception = $e; - } - - $this->executeOutcome($test['operation'], $test['outcome'], $result, $exception); - } - - public function provideSpecificationTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/spec-tests/*/*.json') as $filename) { - $json = json_decode(file_get_contents($filename), true); - - foreach ($json['tests'] as $test) { - $name = str_replace(' ', '_', $test['description']); - $testArgs[$name] = [ - $json['data'], - $test, - $json['minServerVersion'] ?? null, - $json['maxServerVersion'] ?? null, - $json['serverless'] ?? null, - ]; - } - } - - return $testArgs; - } - - /** - * Assert that the collections contain equivalent documents. - */ - private function assertEquivalentCollections(Collection $expectedCollection, Collection $actualCollection): void - { - $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); - $mi->attachIterator($expectedCollection->find()); - $mi->attachIterator($actualCollection->find()); - - foreach ($mi as $documents) { - [$expectedDocument, $actualDocument] = $documents; - $this->assertSameDocument($expectedDocument, $actualDocument); - } - } - - private function checkServerlessRequirement(?string $serverless): void - { - switch ($serverless) { - case null: - case self::SERVERLESS_ALLOW: - return; - - case self::SERVERLESS_FORBID: - if ($this->isServerless()) { - $this->markTestSkipped('Test does not apply on serverless'); - } - - return; - - case self::SERVERLESS_REQUIRE: - if (! $this->isServerless()) { - $this->markTestSkipped('Test requires serverless'); - } - - return; - - default: - $this->fail(sprintf('Unknown serverless requirement "%s".', $serverless)); - } - } - - /** - * Checks that the server version is within the allowed bounds (if any). - * - * @throws PHPUnit_Framework_SkippedTestError - */ - private function checkServerVersion(?string $minServerVersion, ?string $maxServerVersion): void - { - $serverVersion = $this->getServerVersion(); - - if (isset($minServerVersion) && version_compare($serverVersion, $minServerVersion, '<')) { - $this->markTestSkipped(sprintf('Server version "%s" < minServerVersion "%s"', $serverVersion, $minServerVersion)); - } - - if (isset($maxServerVersion) && version_compare($serverVersion, $maxServerVersion, '>=')) { - $this->markTestSkipped(sprintf('Server version "%s" >= maxServerVersion "%s"', $serverVersion, $maxServerVersion)); - } - } - - /** - * Executes an "operation" block. - * - * @return mixed - * @throws LogicException if the operation is unsupported - */ - private function executeOperation(array $operation) - { - switch ($operation['name']) { - case 'aggregate': - return $this->collection->aggregate( - $operation['arguments']['pipeline'], - array_diff_key($operation['arguments'], ['pipeline' => 1]) - ); - - case 'bulkWrite': - return $this->collection->bulkWrite( - array_map([$this, 'prepareBulkWriteRequest'], $operation['arguments']['requests']), - $operation['arguments']['options'] ?? [] - ); - - case 'count': - case 'countDocuments': - case 'find': - return $this->collection->{$operation['name']}( - $operation['arguments']['filter'] ?? [], - array_diff_key($operation['arguments'], ['filter' => 1]) - ); - - case 'estimatedDocumentCount': - return $this->collection->estimatedDocumentCount($operation['arguments']); - - case 'deleteMany': - case 'deleteOne': - case 'findOneAndDelete': - return $this->collection->{$operation['name']}( - $operation['arguments']['filter'], - array_diff_key($operation['arguments'], ['filter' => 1]) - ); - - case 'distinct': - return $this->collection->distinct( - $operation['arguments']['fieldName'], - $operation['arguments']['filter'] ?? [], - array_diff_key($operation['arguments'], ['fieldName' => 1, 'filter' => 1]) - ); - - case 'findOneAndReplace': - $operation['arguments'] = $this->prepareFindAndModifyArguments($operation['arguments']); - // Fall through - - case 'replaceOne': - return $this->collection->{$operation['name']}( - $operation['arguments']['filter'], - $operation['arguments']['replacement'], - array_diff_key($operation['arguments'], ['filter' => 1, 'replacement' => 1]) - ); - - case 'findOneAndUpdate': - $operation['arguments'] = $this->prepareFindAndModifyArguments($operation['arguments']); - // Fall through - - case 'updateMany': - case 'updateOne': - return $this->collection->{$operation['name']}( - $operation['arguments']['filter'], - $operation['arguments']['update'], - array_diff_key($operation['arguments'], ['filter' => 1, 'update' => 1]) - ); - - case 'insertMany': - return $this->collection->insertMany( - $operation['arguments']['documents'], - $operation['arguments']['options'] ?? [] - ); - - case 'insertOne': - return $this->collection->insertOne( - $operation['arguments']['document'], - array_diff_key($operation['arguments'], ['document' => 1]) - ); - - default: - throw new LogicException('Unsupported operation: ' . $operation['name']); - } - } - - /** - * Executes an "outcome" block. - * - * @param mixed $result - * @return mixed - * @throws LogicException if the operation is unsupported - */ - private function executeOutcome(array $operation, array $outcome, $result, ?RuntimeException $exception = null) - { - $expectedError = array_key_exists('error', $outcome) ? $outcome['error'] : false; - - if ($expectedError) { - $this->assertNull($result); - $this->assertNotNull($exception); - - $result = $this->extractResultFromException($operation, $outcome, $exception); - } - - if (array_key_exists('result', $outcome)) { - $this->executeAssertResult($operation, $outcome['result'], $result); - } - - if (isset($outcome['collection'])) { - $actualCollection = isset($outcome['collection']['name']) - ? new Collection($this->manager, $this->getDatabaseName(), $outcome['collection']['name']) - : $this->collection; - - $this->assertEquivalentCollections($this->expectedCollection, $actualCollection); - } - } - - /** - * Extracts a result from an exception. - * - * Errors for bulkWrite and insertMany operations may still report a write - * result. This method will attempt to extract such a result so that it can - * be used in executeAssertResult(). - * - * If no result can be extracted, null will be returned. - * - * @return mixed - */ - private function extractResultFromException(array $operation, array $outcome, RuntimeException $exception) - { - switch ($operation['name']) { - case 'bulkWrite': - $insertedIds = $outcome['result']['insertedIds'] ?? []; - - if ($exception instanceof BulkWriteException) { - return new BulkWriteResult($exception->getWriteResult(), $insertedIds); - } - - break; - - case 'insertMany': - $insertedIds = $outcome['result']['insertedIds'] ?? []; - - if ($exception instanceof BulkWriteException) { - return new InsertManyResult($exception->getWriteResult(), $insertedIds); - } - - break; - } - - return null; - } - - /** - * Executes the "result" section of an "outcome" block. - * - * @param mixed $expectedResult - * @param mixed $actualResult - * @throws LogicException if the operation is unsupported - */ - private function executeAssertResult(array $operation, $expectedResult, $actualResult): void - { - switch ($operation['name']) { - case 'aggregate': - /* Returning a cursor for the $out collection is optional per - * the CRUD specification and is not implemented in the library - * since we have no concept of lazy cursors. We will not assert - * the result here; however, assertEquivalentCollections() will - * assert the output collection's contents later. - */ - if (! is_last_pipeline_operator_write($operation['arguments']['pipeline'])) { - $this->assertSameDocuments($expectedResult, $actualResult); - } - - break; - - case 'bulkWrite': - $this->assertIsArray($expectedResult); - $this->assertInstanceOf(BulkWriteResult::class, $actualResult); - - if (isset($expectedResult['deletedCount'])) { - $this->assertSame($expectedResult['deletedCount'], $actualResult->getDeletedCount()); - } - - if (isset($expectedResult['insertedCount'])) { - $this->assertSame($expectedResult['insertedCount'], $actualResult->getInsertedCount()); - } - - if (isset($expectedResult['insertedIds'])) { - $this->assertSameDocument( - ['insertedIds' => $expectedResult['insertedIds']], - ['insertedIds' => $actualResult->getInsertedIds()] - ); - } - - if (isset($expectedResult['matchedCount'])) { - $this->assertSame($expectedResult['matchedCount'], $actualResult->getMatchedCount()); - } - - if (isset($expectedResult['modifiedCount'])) { - $this->assertSame($expectedResult['modifiedCount'], $actualResult->getModifiedCount()); - } - - if (isset($expectedResult['upsertedCount'])) { - $this->assertSame($expectedResult['upsertedCount'], $actualResult->getUpsertedCount()); - } - - if (isset($expectedResult['upsertedIds'])) { - $this->assertSameDocument( - ['upsertedIds' => $expectedResult['upsertedIds']], - ['upsertedIds' => $actualResult->getUpsertedIds()] - ); - } - - break; - - case 'count': - case 'countDocuments': - case 'estimatedDocumentCount': - $this->assertSame($expectedResult, $actualResult); - break; - - case 'distinct': - $this->assertSameDocument( - ['values' => $expectedResult], - ['values' => $actualResult] - ); - break; - - case 'find': - $this->assertSameDocuments($expectedResult, $actualResult); - break; - - case 'deleteMany': - case 'deleteOne': - $this->assertIsArray($expectedResult); - $this->assertInstanceOf(DeleteResult::class, $actualResult); - - if (isset($expectedResult['deletedCount'])) { - $this->assertSame($expectedResult['deletedCount'], $actualResult->getDeletedCount()); - } - - break; - - case 'findOneAndDelete': - case 'findOneAndReplace': - case 'findOneAndUpdate': - $this->assertSameDocument( - ['result' => $expectedResult], - ['result' => $actualResult] - ); - break; - - case 'insertMany': - $this->assertIsArray($expectedResult); - $this->assertInstanceOf(InsertManyResult::class, $actualResult); - - if (isset($expectedResult['insertedCount'])) { - $this->assertSame($expectedResult['insertedCount'], $actualResult->getInsertedCount()); - } - - if (isset($expectedResult['insertedIds'])) { - $this->assertSameDocument( - ['insertedIds' => $expectedResult['insertedIds']], - ['insertedIds' => $actualResult->getInsertedIds()] - ); - } - - break; - - case 'insertOne': - $this->assertIsArray($expectedResult); - $this->assertInstanceOf(InsertOneResult::class, $actualResult); - - if (isset($expectedResult['insertedCount'])) { - $this->assertSame($expectedResult['insertedCount'], $actualResult->getInsertedCount()); - } - - if (isset($expectedResult['insertedId'])) { - $this->assertSameDocument( - ['insertedId' => $expectedResult['insertedId']], - ['insertedId' => $actualResult->getInsertedId()] - ); - } - - break; - - case 'replaceOne': - case 'updateMany': - case 'updateOne': - $this->assertIsArray($expectedResult); - $this->assertInstanceOf(UpdateResult::class, $actualResult); - - if (isset($expectedResult['matchedCount'])) { - $this->assertSame($expectedResult['matchedCount'], $actualResult->getMatchedCount()); - } - - if (isset($expectedResult['modifiedCount'])) { - $this->assertSame($expectedResult['modifiedCount'], $actualResult->getModifiedCount()); - } - - if (isset($expectedResult['upsertedCount'])) { - $this->assertSame($expectedResult['upsertedCount'], $actualResult->getUpsertedCount()); - } - - if (array_key_exists('upsertedId', $expectedResult)) { - $this->assertSameDocument( - ['upsertedId' => $expectedResult['upsertedId']], - ['upsertedId' => $actualResult->getUpsertedId()] - ); - } - - break; - - default: - throw new LogicException('Unsupported operation: ' . $operation['name']); - } - } - - /** - * Initializes data in the test collections. - */ - private function initializeData(array $initialData, ?array $expectedData = null): void - { - if (! empty($initialData)) { - $this->collection->insertMany($initialData); - } - - if (! empty($expectedData)) { - $this->expectedCollection->insertMany($expectedData); - } - } - - /** - * Prepares a request element for a bulkWrite operation. - */ - private function prepareBulkWriteRequest(array $request): array - { - switch ($request['name']) { - case 'deleteMany': - case 'deleteOne': - return [ - $request['name'] => [ - $request['arguments']['filter'], - array_diff_key($request['arguments'], ['filter' => 1]), - ], - ]; - - case 'insertOne': - return ['insertOne' => [$request['arguments']['document']]]; - - case 'replaceOne': - return [ - 'replaceOne' => [ - $request['arguments']['filter'], - $request['arguments']['replacement'], - array_diff_key($request['arguments'], ['filter' => 1, 'replacement' => 1]), - ], - ]; - - case 'updateMany': - case 'updateOne': - return [ - $request['name'] => [ - $request['arguments']['filter'], - $request['arguments']['update'], - array_diff_key($request['arguments'], ['filter' => 1, 'update' => 1]), - ], - ]; - - default: - throw new LogicException('Unsupported bulk write request: ' . $request['name']); - } - } - - /** - * Prepares arguments for findOneAndReplace and findOneAndUpdate operations. - */ - private function prepareFindAndModifyArguments(array $arguments): array - { - if (isset($arguments['returnDocument'])) { - $arguments['returnDocument'] = 'after' === strtolower($arguments['returnDocument']) - ? FindOneAndReplace::RETURN_DOCUMENT_AFTER - : FindOneAndReplace::RETURN_DOCUMENT_BEFORE; - } - - return $arguments; - } -} diff --git a/tests/Collection/FunctionalTestCase.php b/tests/Collection/FunctionalTestCase.php index 89aea89dc..9e5341501 100644 --- a/tests/Collection/FunctionalTestCase.php +++ b/tests/Collection/FunctionalTestCase.php @@ -10,26 +10,12 @@ */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - /** @var Collection */ - protected $collection; + protected Collection $collection; public function setUp(): void { parent::setUp(); - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); - - $this->dropCollection(); - } - - public function tearDown(): void - { - if ($this->hasFailed()) { - return; - } - - $this->dropCollection(); - - parent::tearDown(); + $this->collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } } diff --git a/tests/Collection/spec-tests/read/aggregate-collation.json b/tests/Collection/spec-tests/read/aggregate-collation.json deleted file mode 100644 index d958e447b..000000000 --- a/tests/Collection/spec-tests/read/aggregate-collation.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "Aggregate with collation", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "x": "PING" - } - } - ], - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": "ping" - } - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/aggregate-out.json b/tests/Collection/spec-tests/read/aggregate-out.json deleted file mode 100644 index c195e163e..000000000 --- a/tests/Collection/spec-tests/read/aggregate-out.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "serverless": "forbid", - "tests": [ - { - "description": "Aggregate with $out", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "batchSize": 2 - } - }, - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "Aggregate with $out and batch size of 0", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "batchSize": 0 - } - }, - "outcome": { - "collection": { - "name": "other_test_collection", - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/aggregate.json b/tests/Collection/spec-tests/read/aggregate.json deleted file mode 100644 index 797a92239..000000000 --- a/tests/Collection/spec-tests/read/aggregate.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Aggregate with multiple stages", - "operation": { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "batchSize": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/count-collation.json b/tests/Collection/spec-tests/read/count-collation.json deleted file mode 100644 index 7d6150849..000000000 --- a/tests/Collection/spec-tests/read/count-collation.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "PING" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "Count documents with collation", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": { - "x": "ping" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": 1 - } - }, - { - "description": "Deprecated count with collation", - "operation": { - "name": "count", - "arguments": { - "filter": { - "x": "ping" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": 1 - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/count-empty.json b/tests/Collection/spec-tests/read/count-empty.json deleted file mode 100644 index 2b8627e0c..000000000 --- a/tests/Collection/spec-tests/read/count-empty.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [], - "tests": [ - { - "description": "Estimated document count with empty collection", - "operation": { - "name": "estimatedDocumentCount", - "arguments": {} - }, - "outcome": { - "result": 0 - } - }, - { - "description": "Count documents with empty collection", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 0 - } - }, - { - "description": "Deprecated count with empty collection", - "operation": { - "name": "count", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 0 - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/count.json b/tests/Collection/spec-tests/read/count.json deleted file mode 100644 index 9642b2fbd..000000000 --- a/tests/Collection/spec-tests/read/count.json +++ /dev/null @@ -1,112 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Estimated document count", - "operation": { - "name": "estimatedDocumentCount", - "arguments": {} - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Count documents without a filter", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Count documents with a filter", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Count documents with skip and limit", - "operation": { - "name": "countDocuments", - "arguments": { - "filter": {}, - "skip": 1, - "limit": 3 - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Deprecated count without a filter", - "operation": { - "name": "count", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": 3 - } - }, - { - "description": "Deprecated count with a filter", - "operation": { - "name": "count", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": 2 - } - }, - { - "description": "Deprecated count with skip and limit", - "operation": { - "name": "count", - "arguments": { - "filter": {}, - "skip": 1, - "limit": 3 - } - }, - "outcome": { - "result": 2 - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/distinct-collation.json b/tests/Collection/spec-tests/read/distinct-collation.json deleted file mode 100644 index 984991a43..000000000 --- a/tests/Collection/spec-tests/read/distinct-collation.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "string": "PING" - }, - { - "_id": 2, - "string": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "Distinct with a collation", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "string", - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - "PING" - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/distinct.json b/tests/Collection/spec-tests/read/distinct.json deleted file mode 100644 index a57ee36a8..000000000 --- a/tests/Collection/spec-tests/read/distinct.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Distinct without a filter", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "x", - "filter": {} - } - }, - "outcome": { - "result": [ - 11, - 22, - 33 - ] - } - }, - { - "description": "Distinct with a filter", - "operation": { - "name": "distinct", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": [ - 22, - 33 - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/find-collation.json b/tests/Collection/spec-tests/read/find-collation.json deleted file mode 100644 index 4e56c0525..000000000 --- a/tests/Collection/spec-tests/read/find-collation.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "Find with a collation", - "operation": { - "name": "find", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": "ping" - } - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/read/find.json b/tests/Collection/spec-tests/read/find.json deleted file mode 100644 index 3597e37be..000000000 --- a/tests/Collection/spec-tests/read/find.json +++ /dev/null @@ -1,105 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "Find with filter", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": 11 - } - ] - } - }, - { - "description": "Find with filter, sort, skip, and limit", - "operation": { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gt": 2 - } - }, - "sort": { - "_id": 1 - }, - "skip": 2, - "limit": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 5, - "x": 55 - } - ] - } - }, - { - "description": "Find with limit, sort, and batchsize", - "operation": { - "name": "find", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4, - "batchSize": 2 - } - }, - "outcome": { - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/bulkWrite-arrayFilters.json b/tests/Collection/spec-tests/write/bulkWrite-arrayFilters.json deleted file mode 100644 index 99e73f5d7..000000000 --- a/tests/Collection/spec-tests/write/bulkWrite-arrayFilters.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "BulkWrite with arrayFilters", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 3, - "modifiedCount": 3, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/bulkWrite-collation.json b/tests/Collection/spec-tests/write/bulkWrite-collation.json deleted file mode 100644 index bc90aa817..000000000 --- a/tests/Collection/spec-tests/write/bulkWrite-collation.json +++ /dev/null @@ -1,218 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - }, - { - "_id": 4, - "x": "pong" - }, - { - "_id": 5, - "x": "pONg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "BulkWrite with delete operations and collation", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": "PONG" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 4, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "BulkWrite with update operations and collation", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "x": "ping" - }, - "update": { - "$set": { - "x": "PONG" - } - }, - "collation": { - "locale": "en_US", - "strength": 3 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "x": "ping" - }, - "update": { - "$set": { - "x": "PONG" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "x": "ping" - }, - "replacement": { - "_id": 6, - "x": "ping" - }, - "upsert": true, - "collation": { - "locale": "en_US", - "strength": 3 - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "x": "pong" - }, - "update": { - "$set": { - "x": "PONG" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 6, - "modifiedCount": 4, - "upsertedCount": 1, - "upsertedIds": { - "2": 6 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "PONG" - }, - { - "_id": 3, - "x": "PONG" - }, - { - "_id": 4, - "x": "PONG" - }, - { - "_id": 5, - "x": "PONG" - }, - { - "_id": 6, - "x": "ping" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/bulkWrite.json b/tests/Collection/spec-tests/write/bulkWrite.json deleted file mode 100644 index dc00da28a..000000000 --- a/tests/Collection/spec-tests/write/bulkWrite.json +++ /dev/null @@ -1,778 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "BulkWrite with deleteOne operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 3 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "BulkWrite with deleteMany operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": { - "$lt": 11 - } - } - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": { - "$lte": 22 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 2, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [] - } - } - }, - { - "description": "BulkWrite with insertOne operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4, - "x": 44 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 2, - "insertedIds": { - "0": 3, - "1": 4 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite with replaceOne operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 33 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 12 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 33 - }, - "upsert": true - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 1, - "upsertedIds": { - "2": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite with updateOne operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 0 - }, - "update": { - "$set": { - "x": 0 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 11 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "x": 33 - } - }, - "upsert": true - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 2, - "modifiedCount": 1, - "upsertedCount": 1, - "upsertedIds": { - "3": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite with updateMany operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "x": { - "$lt": 11 - } - }, - "update": { - "$set": { - "x": 0 - } - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "x": { - "$lte": 22 - } - }, - "update": { - "$unset": { - "y": 1 - } - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "x": { - "$lte": 22 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "x": 33 - } - }, - "upsert": true - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 4, - "modifiedCount": 2, - "upsertedCount": 1, - "upsertedIds": { - "3": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite with mixed ordered operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4, - "x": 44 - } - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 2, - "insertedCount": 2, - "insertedIds": { - "0": 3, - "3": 4 - }, - "matchedCount": 3, - "modifiedCount": 3, - "upsertedCount": 1, - "upsertedIds": { - "5": 4 - } - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 24 - }, - { - "_id": 3, - "x": 34 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite with mixed unordered operations", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "_id": 3, - "x": 33 - }, - "upsert": true - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 1, - "upsertedIds": { - "0": 3 - } - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite continue-on-error behavior with unordered (preexisting duplicate key)", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4, - "x": 44 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "BulkWrite continue-on-error behavior with unordered (duplicate key in requests)", - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4, - "x": 44 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/deleteMany-collation.json b/tests/Collection/spec-tests/write/deleteMany-collation.json deleted file mode 100644 index fce75e488..000000000 --- a/tests/Collection/spec-tests/write/deleteMany-collation.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "DeleteMany when many documents match with collation", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/deleteMany.json b/tests/Collection/spec-tests/write/deleteMany.json deleted file mode 100644 index 7eee85e77..000000000 --- a/tests/Collection/spec-tests/write/deleteMany.json +++ /dev/null @@ -1,76 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "DeleteMany when many documents match", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "DeleteMany when no document matches", - "operation": { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - "outcome": { - "result": { - "deletedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/deleteOne-collation.json b/tests/Collection/spec-tests/write/deleteOne-collation.json deleted file mode 100644 index 9bcef411e..000000000 --- a/tests/Collection/spec-tests/write/deleteOne-collation.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "DeleteOne when many documents matches with collation", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "x": "PING" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/deleteOne.json b/tests/Collection/spec-tests/write/deleteOne.json deleted file mode 100644 index a1106deae..000000000 --- a/tests/Collection/spec-tests/write/deleteOne.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "DeleteOne when many documents match", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - } - } - }, - { - "description": "DeleteOne when one document matches", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 2 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "DeleteOne when no documents match", - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - "outcome": { - "result": { - "deletedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndDelete-collation.json b/tests/Collection/spec-tests/write/findOneAndDelete-collation.json deleted file mode 100644 index 32480da84..000000000 --- a/tests/Collection/spec-tests/write/findOneAndDelete-collation.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "FindOneAndDelete when one document matches with collation", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 2, - "x": "PING" - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "ping" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndDelete.json b/tests/Collection/spec-tests/write/findOneAndDelete.json deleted file mode 100644 index e424e2aad..000000000 --- a/tests/Collection/spec-tests/write/findOneAndDelete.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndDelete when many documents match", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndDelete when one document matches", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 2 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndDelete when no documents match", - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 4 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndReplace-collation.json b/tests/Collection/spec-tests/write/findOneAndReplace-collation.json deleted file mode 100644 index 9b3c25005..000000000 --- a/tests/Collection/spec-tests/write/findOneAndReplace-collation.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "FindOneAndReplace when one document matches with collation returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "x": "PING" - }, - "replacement": { - "x": "pong" - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "pong" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndReplace-upsert.json b/tests/Collection/spec-tests/write/findOneAndReplace-upsert.json deleted file mode 100644 index 0f07bf9c1..000000000 --- a/tests/Collection/spec-tests/write/findOneAndReplace-upsert.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match without id specified with upsert returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 44 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match with id specified with upsert returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 44 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndReplace.json b/tests/Collection/spec-tests/write/findOneAndReplace.json deleted file mode 100644 index 70e5c3df4..000000000 --- a/tests/Collection/spec-tests/write/findOneAndReplace.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndReplace when many documents match returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when many documents match returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 32 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when one document matches returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when one document matches returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 32 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 32 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 32 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match returning the document before modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndReplace when no documents match returning the document after modification", - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 44 - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndUpdate-arrayFilters.json b/tests/Collection/spec-tests/write/findOneAndUpdate-arrayFilters.json deleted file mode 100644 index 1aa13b863..000000000 --- a/tests/Collection/spec-tests/write/findOneAndUpdate-arrayFilters.json +++ /dev/null @@ -1,203 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "FindOneAndUpdate when no document matches arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when multiple documents match arrayFilters", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json b/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json deleted file mode 100644 index 8abab7bd6..000000000 --- a/tests/Collection/spec-tests/write/findOneAndUpdate-collation.json +++ /dev/null @@ -1,68 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "FindOneAndUpdate when many documents match with collation returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "x": "PING" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "_id": 1 - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "x": "ping" - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - }, - { - "_id": 3, - "x": "pINg" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/findOneAndUpdate.json b/tests/Collection/spec-tests/write/findOneAndUpdate.json deleted file mode 100644 index 6da832527..000000000 --- a/tests/Collection/spec-tests/write/findOneAndUpdate.json +++ /dev/null @@ -1,379 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate when many documents match returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when many documents match returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 23 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 22 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when one document matches returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "x": 23 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match with upsert returning the document before modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "upsert": true - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": null, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate when no documents match with upsert returning the document after modification", - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "projection": { - "x": 1, - "_id": 0 - }, - "returnDocument": "After", - "sort": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "x": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/insertMany.json b/tests/Collection/spec-tests/write/insertMany.json deleted file mode 100644 index 6a2e5261b..000000000 --- a/tests/Collection/spec-tests/write/insertMany.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany with non-existing documents", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany continue-on-error behavior with unordered (preexisting duplicate key)", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany continue-on-error behavior with unordered (duplicate key in requests)", - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/insertOne.json b/tests/Collection/spec-tests/write/insertOne.json deleted file mode 100644 index 525de75e0..000000000 --- a/tests/Collection/spec-tests/write/insertOne.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertOne with a non-existing document", - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - "outcome": { - "result": { - "insertedId": 2 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/replaceOne-collation.json b/tests/Collection/spec-tests/write/replaceOne-collation.json deleted file mode 100644 index fa4cbe997..000000000 --- a/tests/Collection/spec-tests/write/replaceOne-collation.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "ReplaceOne when one document matches with collation", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "x": "PING" - }, - "replacement": { - "_id": 2, - "x": "pong" - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/replaceOne.json b/tests/Collection/spec-tests/write/replaceOne.json deleted file mode 100644 index 101af25c7..000000000 --- a/tests/Collection/spec-tests/write/replaceOne.json +++ /dev/null @@ -1,205 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "ReplaceOne when many documents match", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - }, - { - "description": "ReplaceOne when one document matches", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "ReplaceOne when no documents match", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 1 - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "ReplaceOne with upsert when no documents match without an id specified", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "ReplaceOne with upsert when no documents match with an id specified", - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 1 - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateMany-arrayFilters.json b/tests/Collection/spec-tests/write/updateMany-arrayFilters.json deleted file mode 100644 index ae4c123ea..000000000 --- a/tests/Collection/spec-tests/write/updateMany-arrayFilters.json +++ /dev/null @@ -1,185 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "UpdateMany when no documents match arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "UpdateMany when one document matches arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - } - }, - { - "description": "UpdateMany when multiple documents match arrayFilters", - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateMany-collation.json b/tests/Collection/spec-tests/write/updateMany-collation.json deleted file mode 100644 index 8becfd806..000000000 --- a/tests/Collection/spec-tests/write/updateMany-collation.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - }, - { - "_id": 3, - "x": "pINg" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "UpdateMany when many documents match with collation", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "x": "ping" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - }, - { - "_id": 3, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateMany.json b/tests/Collection/spec-tests/write/updateMany.json deleted file mode 100644 index a3c339987..000000000 --- a/tests/Collection/spec-tests/write/updateMany.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "UpdateMany when many documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateMany when one document matches", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateMany when no documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateMany with upsert when no documents match", - "operation": { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateOne-arrayFilters.json b/tests/Collection/spec-tests/write/updateOne-arrayFilters.json deleted file mode 100644 index 087ed4b82..000000000 --- a/tests/Collection/spec-tests/write/updateOne-arrayFilters.json +++ /dev/null @@ -1,395 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ], - "minServerVersion": "3.5.6", - "tests": [ - { - "description": "UpdateOne when no document matches arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 4 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when one document matches arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when multiple documents match arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when no documents match multiple arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "y.$[i].c.$[j].d": 0 - } - }, - "arrayFilters": [ - { - "i.b": 5 - }, - { - "j.d": 3 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 1 - } - ] - } - ] - } - ] - } - } - }, - { - "description": "UpdateOne when one document matches multiple arrayFilters", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "y.$[i].c.$[j].d": 0 - } - }, - "arrayFilters": [ - { - "i.b": 5 - }, - { - "j.d": 1 - } - ] - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - }, - { - "_id": 3, - "y": [ - { - "b": 5, - "c": [ - { - "d": 2 - }, - { - "d": 0 - } - ] - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateOne-collation.json b/tests/Collection/spec-tests/write/updateOne-collation.json deleted file mode 100644 index 3afdb83e0..000000000 --- a/tests/Collection/spec-tests/write/updateOne-collation.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "ping" - } - ], - "minServerVersion": "3.4", - "serverless": "forbid", - "tests": [ - { - "description": "UpdateOne when one document matches with collation", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "x": "PING" - }, - "update": { - "$set": { - "x": "pong" - } - }, - "collation": { - "locale": "en_US", - "strength": 2 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": "pong" - } - ] - } - } - } - ] -} diff --git a/tests/Collection/spec-tests/write/updateOne.json b/tests/Collection/spec-tests/write/updateOne.json deleted file mode 100644 index 76d2086bd..000000000 --- a/tests/Collection/spec-tests/write/updateOne.json +++ /dev/null @@ -1,167 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "minServerVersion": "2.6", - "tests": [ - { - "description": "UpdateOne when many documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - }, - { - "description": "UpdateOne when one document matches", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateOne when no documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert when no documents match", - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/tests/Command/ListCollectionsTest.php b/tests/Command/ListCollectionsTest.php index 8ea941a75..54ab1bbe3 100644 --- a/tests/Command/ListCollectionsTest.php +++ b/tests/Command/ListCollectionsTest.php @@ -5,38 +5,24 @@ use MongoDB\Command\ListCollections; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class ListCollectionsTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListCollections($this->getDatabaseName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions(): array { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['authorizedCollections' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['filter' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'authorizedCollections' => self::getInvalidBooleanValues(), + 'filter' => self::getInvalidDocumentValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'session' => self::getInvalidSessionValues(), + ]); } } diff --git a/tests/Command/ListDatabasesTest.php b/tests/Command/ListDatabasesTest.php index c1036ae94..f436bdb0d 100644 --- a/tests/Command/ListDatabasesTest.php +++ b/tests/Command/ListDatabasesTest.php @@ -5,42 +5,25 @@ use MongoDB\Command\ListDatabases; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; class ListDatabasesTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListDatabases($options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['authorizedDatabases' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['filter' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['nameOnly' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'authorizedDatabases' => self::getInvalidBooleanValues(), + 'filter' => self::getInvalidDocumentValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'nameOnly' => self::getInvalidBooleanValues(), + 'session' => self::getInvalidSessionValues(), + ]); } } diff --git a/tests/CommandObserver.php b/tests/CommandObserver.php index 2f5165138..89fc79de7 100644 --- a/tests/CommandObserver.php +++ b/tests/CommandObserver.php @@ -17,8 +17,7 @@ */ class CommandObserver implements CommandSubscriber { - /** @var array */ - private $commands = []; + private array $commands = []; public function observe(callable $execution, callable $commandCallback): void { diff --git a/tests/Comparator/Int64Comparator.php b/tests/Comparator/Int64Comparator.php new file mode 100644 index 000000000..83cc6621e --- /dev/null +++ b/tests/Comparator/Int64Comparator.php @@ -0,0 +1,47 @@ +isComparable($actual)) + || ($actual instanceof Int64 && $this->isComparable($expected)); + } + + public function assertEquals($expected, $actual, $delta = 0.0, $canonicalize = false, $ignoreCase = false): void + { + if ($expected == $actual) { + return; + } + + $exporter = new Exporter(); + + throw new ComparisonFailure( + $expected, + $actual, + '', + '', + sprintf( + 'Failed asserting that %s matches expected %s.', + $exporter->export($actual), + $exporter->export($expected), + ), + ); + } + + private function isComparable($value): bool + { + return $value instanceof Int64 || is_numeric($value); + } +} diff --git a/tests/Comparator/Int64ComparatorTest.php b/tests/Comparator/Int64ComparatorTest.php new file mode 100644 index 000000000..308b9cec7 --- /dev/null +++ b/tests/Comparator/Int64ComparatorTest.php @@ -0,0 +1,211 @@ +assertSame($expectedResult, (new Int64Comparator())->accepts($expectedValue, $actualValue)); + } + + public static function provideAcceptsValues(): Generator + { + yield 'Expects Int64, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => new Int64(123), + ]; + + yield 'Expects Int64, Actual int' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => 123, + ]; + + yield 'Expects Int64, Actual int string' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => '123', + ]; + + yield 'Expects Int64, Actual float' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => 123.0, + ]; + + yield 'Expects Int64, Actual float string' => [ + 'expectedResult' => true, + 'expectedValue' => new Int64(123), + 'actualValue' => '123.0', + ]; + + yield 'Expects Int64, Actual non-numeric string' => [ + 'expectedResult' => false, + 'expectedValue' => new Int64(123), + 'actualValue' => 'foo', + ]; + + yield 'Expects int, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => 123, + 'actualValue' => new Int64(123), + ]; + + yield 'Expects int string, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => '123', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => 123.0, + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float string, Actual Int64' => [ + 'expectedResult' => true, + 'expectedValue' => '123.0', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects non-numeric string, Actual Int64' => [ + 'expectedResult' => false, + 'expectedValue' => 'foo', + 'actualValue' => new Int64(123), + ]; + + yield 'Expects float, Actual float' => [ + 'expectedResult' => false, + 'expectedValue' => 123.0, + 'actualValue' => 123.0, + ]; + + yield 'Expects numeric string, Actual numeric string' => [ + 'expectedResult' => false, + 'expectedValue' => '123', + 'actualValue' => '123', + ]; + } + + #[DataProvider('provideMatchingAssertions')] + #[DoesNotPerformAssertions] + public function testMatchingAssertions($expected, $actual): void + { + (new Int64Comparator())->assertEquals($expected, $actual); + } + + public static function provideMatchingAssertions(): Generator + { + yield 'Expected Int64, Actual Int64' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => new Int64(8_589_934_592), + ]; + + yield 'Expected Int64, Actual int' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => 8_589_934_592, + ]; + + yield 'Expected Int64, Actual int string' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => '8589934592', + ]; + + yield 'Expected Int64, Actual float' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => 8_589_934_592.0, + ]; + + yield 'Expected Int64, Actual float string' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => '8589934592.0', + ]; + + yield 'Expected int, Actual Int64' => [ + 'expected' => 8_589_934_592, + 'actual' => new Int64(8_589_934_592), + ]; + + yield 'Expected int string, Actual Int64' => [ + 'expected' => '8589934592', + 'actual' => new Int64(8_589_934_592), + ]; + + yield 'Expected float, Actual Int64' => [ + 'expected' => 8_589_934_592.0, + 'actual' => new Int64(8_589_934_592), + ]; + + yield 'Expected float string, Actual Int64' => [ + 'expected' => '8589934592.0', + 'actual' => new Int64(8_589_934_592), + ]; + } + + #[DataProvider('provideFailingValues')] + public function testFailingAssertions($expected, $actual): void + { + $this->expectException(ComparisonFailure::class); + + (new Int64Comparator())->assertEquals($expected, $actual); + } + + public static function provideFailingValues(): Generator + { + yield 'Expected Int64, Actual Int64' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => new Int64(456), + ]; + + yield 'Expected Int64, Actual int' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => 456, + ]; + + yield 'Expected Int64, Actual int string' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => '456', + ]; + + yield 'Expected Int64, Actual float' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => 8_589_934_592.1, + ]; + + yield 'Expected Int64, Actual float string' => [ + 'expected' => new Int64(8_589_934_592), + 'actual' => '8589934592.1', + ]; + + yield 'Expected int, Actual Int64' => [ + 'expected' => 8_589_934_592, + 'actual' => new Int64(456), + ]; + + yield 'Expected int string, Actual Int64' => [ + 'expected' => '8589934592', + 'actual' => new Int64(456), + ]; + + yield 'Expected float, Actual Int64' => [ + 'expected' => 8_589_934_592.1, + 'actual' => new Int64(456), + ]; + + yield 'Expected float string, Actual Int64' => [ + 'expected' => '8589934592.1', + 'actual' => new Int64(456), + ]; + } +} diff --git a/tests/Comparator/ServerComparator.php b/tests/Comparator/ServerComparator.php new file mode 100644 index 000000000..28c958593 --- /dev/null +++ b/tests/Comparator/ServerComparator.php @@ -0,0 +1,38 @@ +getHost(), + $actual->getPort(), + $expected->getHost(), + $expected->getPort(), + ), + ); + } +} diff --git a/tests/Database/BuilderDatabaseFunctionalTest.php b/tests/Database/BuilderDatabaseFunctionalTest.php new file mode 100644 index 000000000..491d2ccc1 --- /dev/null +++ b/tests/Database/BuilderDatabaseFunctionalTest.php @@ -0,0 +1,70 @@ +dropCollection($this->getDatabaseName(), $this->getCollectionName()); + + parent::tearDown(); + } + + #[TestWith([true])] + #[TestWith([false])] + public function testAggregate(bool $pipelineAsArray): void + { + $this->skipIfServerVersion('<', '6.0.0', '$documents stage is not supported'); + + $pipeline = [ + Stage::documents([ + ['x' => 1], + ['x' => 2], + ['x' => 3], + ]), + Stage::bucketAuto( + groupBy: Expression::intFieldPath('x'), + buckets: 2, + ), + ]; + + if (! $pipelineAsArray) { + $pipeline = new Pipeline(...$pipeline); + } + + $results = $this->database->aggregate($pipeline)->toArray(); + $this->assertCount(2, $results); + } + + #[TestWith([true])] + #[TestWith([false])] + public function testWatch(bool $pipelineAsArray): void + { + $this->skipIfChangeStreamIsNotSupported(); + + if ($this->isShardedCluster()) { + $this->markTestSkipped('Test does not apply on sharded clusters: need more than a single getMore call on the change stream.'); + } + + $pipeline = [ + Stage::match(operationType: Query::eq('insert')), + ]; + + if (! $pipelineAsArray) { + $pipeline = new Pipeline(...$pipeline); + } + + $changeStream = $this->database->watch($pipeline); + $this->database->selectCollection($this->getCollectionName())->insertOne(['x' => 3]); + $changeStream->next(); + $this->assertTrue($changeStream->valid()); + $this->assertEquals('insert', $changeStream->current()->operationType); + } +} diff --git a/tests/Database/CollectionManagementFunctionalTest.php b/tests/Database/CollectionManagementFunctionalTest.php index 4cb5c6107..dc5b49974 100644 --- a/tests/Database/CollectionManagementFunctionalTest.php +++ b/tests/Database/CollectionManagementFunctionalTest.php @@ -2,9 +2,9 @@ namespace MongoDB\Tests\Database; +use Iterator; use MongoDB\Driver\BulkWrite; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; /** * Functional tests for collection management methods. @@ -16,8 +16,7 @@ public function testCreateCollection(): void $that = $this; $basicCollectionName = $this->getCollectionName() . '.basic'; - $commandResult = $this->database->createCollection($basicCollectionName); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($basicCollectionName); $this->assertCollectionExists($basicCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertFalse($info->isCapped()); }); @@ -26,15 +25,14 @@ public function testCreateCollection(): void $cappedCollectionOptions = [ 'capped' => true, 'max' => 100, - 'size' => 1048576, + 'size' => 1_048_576, ]; - $commandResult = $this->database->createCollection($cappedCollectionName, $cappedCollectionOptions); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($cappedCollectionName, $cappedCollectionOptions); $this->assertCollectionExists($cappedCollectionName, null, function (CollectionInfo $info) use ($that): void { $that->assertTrue($info->isCapped()); $that->assertEquals(100, $info->getCappedMax()); - $that->assertEquals(1048576, $info->getCappedSize()); + $that->assertEquals(1_048_576, $info->getCappedSize()); }); } @@ -46,18 +44,16 @@ public function testDropCollection(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->dropCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->dropCollection($this->getCollectionName()); $this->assertCollectionCount($this->getNamespace(), 0); } public function testListCollections(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collections = $this->database->listCollections(); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); @@ -66,14 +62,13 @@ public function testListCollections(): void public function testListCollectionsWithFilter(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collectionName = $this->getCollectionName(); $options = ['filter' => ['name' => $collectionName]]; $collections = $this->database->listCollections($options); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); @@ -83,8 +78,7 @@ public function testListCollectionsWithFilter(): void public function testListCollectionNames(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collections = $this->database->listCollectionNames(); @@ -95,8 +89,7 @@ public function testListCollectionNames(): void public function testListCollectionNamesWithFilter(): void { - $commandResult = $this->database->createCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->createCollection($this->getCollectionName()); $collectionName = $this->getCollectionName(); $options = ['filter' => ['name' => $collectionName]]; diff --git a/tests/Database/DatabaseFunctionalTest.php b/tests/Database/DatabaseFunctionalTest.php index 6eb2f9fd1..7f4e564c2 100644 --- a/tests/Database/DatabaseFunctionalTest.php +++ b/tests/Database/DatabaseFunctionalTest.php @@ -2,6 +2,8 @@ namespace MongoDB\Tests\Database; +use MongoDB\BSON\PackedArray; +use MongoDB\Codec\Encoder; use MongoDB\Collection; use MongoDB\Database; use MongoDB\Driver\BulkWrite; @@ -11,6 +13,9 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\CreateIndexes; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use ReflectionClass; use TypeError; use function array_key_exists; @@ -21,9 +26,7 @@ */ class DatabaseFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideInvalidDatabaseNames - */ + #[DataProvider('provideInvalidDatabaseNames')] public function testConstructorDatabaseNameArgument($databaseName, string $expectedExceptionClass): void { $this->expectException($expectedExceptionClass); @@ -31,7 +34,7 @@ public function testConstructorDatabaseNameArgument($databaseName, string $expec new Database($this->manager, $databaseName); } - public function provideInvalidDatabaseNames() + public static function provideInvalidDatabaseNames() { return [ [null, TypeError::class], @@ -39,36 +42,22 @@ public function provideInvalidDatabaseNames() ]; } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Database($this->manager, $this->getDatabaseName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'builderEncoder' => self::getInvalidObjectValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } public function testGetManager(): void @@ -90,7 +79,7 @@ public function testCommand(): void { $command = ['ping' => 1]; $options = [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; $cursor = $this->database->command($command, $options); @@ -99,7 +88,7 @@ public function testCommand(): void $commandResult = current($cursor->toArray()); $this->assertCommandSucceeded($commandResult); - $this->assertObjectHasAttribute('ok', $commandResult); + $this->assertObjectHasProperty('ok', $commandResult); $this->assertSame(1, (int) $commandResult->ok); } @@ -109,7 +98,7 @@ public function testCommandDoesNotInheritReadPreference(): void $this->markTestSkipped('Test only applies to replica sets'); } - $this->database = new Database($this->manager, $this->getDatabaseName(), ['readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY)]); + $this->database = new Database($this->manager, $this->getDatabaseName(), ['readPreference' => new ReadPreference(ReadPreference::SECONDARY)]); $command = ['ping' => 1]; @@ -123,7 +112,7 @@ public function testCommandAppliesTypeMapToCursor(): void { $command = ['ping' => 1]; $options = [ - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'typeMap' => ['root' => 'array'], ]; @@ -138,12 +127,10 @@ public function testCommandAppliesTypeMapToCursor(): void $this->assertSame(1, (int) $commandResult['ok']); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testCommandCommandArgumentTypeCheck($command): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($command instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); $this->database->command($command); } @@ -155,8 +142,7 @@ public function testDrop(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->drop(); - $this->assertCommandSucceeded($commandResult); + $this->database->drop(); $this->assertCollectionCount($this->getNamespace(), 0); } @@ -168,8 +154,7 @@ public function testDropCollection(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->dropCollection($this->getCollectionName()); - $this->assertCommandSucceeded($commandResult); + $this->database->dropCollection($this->getCollectionName()); $this->assertCollectionDoesNotExist($this->getCollectionName()); } @@ -188,11 +173,9 @@ public function testGetSelectsCollectionAndInheritsOptions(): void $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } - /** - * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster - */ + #[Group('matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster')] + #[Group('matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster')] + #[Group('matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster')] public function testModifyCollection(): void { $this->database->createCollection($this->getCollectionName()); @@ -204,7 +187,7 @@ public function testModifyCollection(): void $commandResult = $this->database->modifyCollection( $this->getCollectionName(), ['index' => ['keyPattern' => ['lastAccess' => 1], 'expireAfterSeconds' => 1000]], - ['typeMap' => ['root' => 'array', 'document' => 'array']] + ['typeMap' => ['root' => 'array', 'document' => 'array']], ); $this->assertCommandSucceeded($commandResult); @@ -237,13 +220,12 @@ public function testRenameCollectionToSameDatabase(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->renameCollection( + $this->database->renameCollection( $this->getCollectionName(), $toCollectionName, null, - ['dropTarget' => true] + ['dropTarget' => true], ); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName); @@ -274,12 +256,11 @@ public function testRenameCollectionToDifferentDatabase(): void $writeResult = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); $this->assertEquals(1, $writeResult->getInsertedCount()); - $commandResult = $this->database->renameCollection( + $this->database->renameCollection( $this->getCollectionName(), $toCollectionName, - $toDatabaseName + $toDatabaseName, ); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($toCollectionName, $toDatabaseName); @@ -292,7 +273,7 @@ public function testSelectCollectionInheritsOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -307,7 +288,7 @@ public function testSelectCollectionInheritsOptions(): void $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); @@ -318,7 +299,7 @@ public function testSelectCollectionPassesOptions(): void { $collectionOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -329,7 +310,7 @@ public function testSelectCollectionPassesOptions(): void $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertIsArray($debug['typeMap']); $this->assertSame(['root' => 'array'], $debug['typeMap']); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); @@ -340,7 +321,7 @@ public function testSelectGridFSBucketInheritsOptions(): void { $databaseOptions = [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -355,7 +336,7 @@ public function testSelectGridFSBucketInheritsOptions(): void $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } @@ -366,7 +347,7 @@ public function testSelectGridFSBucketPassesOptions(): void 'bucketName' => 'custom_fs', 'chunkSizeBytes' => 8192, 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -380,7 +361,7 @@ public function testSelectGridFSBucketPassesOptions(): void $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); + $this->assertSame(ReadPreference::SECONDARY_PREFERRED, $debug['readPreference']->getModeString()); $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); } @@ -388,8 +369,9 @@ public function testSelectGridFSBucketPassesOptions(): void public function testWithOptionsInheritsOptions(): void { $databaseOptions = [ + 'builderEncoder' => $this->createMock(Encoder::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -400,21 +382,27 @@ public function testWithOptionsInheritsOptions(): void $this->assertSame($this->manager, $debug['manager']); $this->assertSame($this->getDatabaseName(), $debug['databaseName']); - $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); - $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); - $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); - $this->assertIsArray($debug['typeMap']); - $this->assertSame(['root' => 'array'], $debug['typeMap']); - $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); - $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); + + foreach ($databaseOptions as $key => $value) { + $this->assertSame($value, $debug[$key]); + } + + // autoEncryptionEnabled is an internal option not reported via debug info + $database = new Database($this->manager, $this->getDatabaseName(), ['autoEncryptionEnabled' => true]); + $clone = $database->withOptions(); + + $rc = new ReflectionClass($clone); + $rp = $rc->getProperty('autoEncryptionEnabled'); + + $this->assertSame(true, $rp->getValue($clone)); } public function testWithOptionsPassesOptions(): void { $databaseOptions = [ + 'builderEncoder' => $this->createMock(Encoder::class), 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY_PREFERRED), + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), 'typeMap' => ['root' => 'array'], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), ]; @@ -422,13 +410,16 @@ public function testWithOptionsPassesOptions(): void $clone = $this->database->withOptions($databaseOptions); $debug = $clone->__debugInfo(); - $this->assertInstanceOf(ReadConcern::class, $debug['readConcern']); - $this->assertSame(ReadConcern::LOCAL, $debug['readConcern']->getLevel()); - $this->assertInstanceOf(ReadPreference::class, $debug['readPreference']); - $this->assertSame(ReadPreference::RP_SECONDARY_PREFERRED, $debug['readPreference']->getMode()); - $this->assertIsArray($debug['typeMap']); - $this->assertSame(['root' => 'array'], $debug['typeMap']); - $this->assertInstanceOf(WriteConcern::class, $debug['writeConcern']); - $this->assertSame(WriteConcern::MAJORITY, $debug['writeConcern']->getW()); + foreach ($databaseOptions as $key => $value) { + $this->assertSame($value, $debug[$key]); + } + + // autoEncryptionEnabled is an internal option not reported via debug info + $clone = $this->database->withOptions(['autoEncryptionEnabled' => true]); + + $rc = new ReflectionClass($clone); + $rp = $rc->getProperty('autoEncryptionEnabled'); + + $this->assertSame(true, $rp->getValue($clone)); } } diff --git a/tests/Database/FunctionalTestCase.php b/tests/Database/FunctionalTestCase.php index 5e0906731..4b6f62d8e 100644 --- a/tests/Database/FunctionalTestCase.php +++ b/tests/Database/FunctionalTestCase.php @@ -10,8 +10,7 @@ */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - /** @var Database */ - protected $database; + protected Database $database; public function setUp(): void { diff --git a/tests/DocumentationExamplesTest.php b/tests/DocumentationExamplesTest.php index e82e80c2b..c5876bebe 100644 --- a/tests/DocumentationExamplesTest.php +++ b/tests/DocumentationExamplesTest.php @@ -11,16 +11,17 @@ use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\Exception; use MongoDB\Driver\ReadPreference; -use MongoDB\Driver\WriteConcern; use MongoDB\Tests\SpecTests\ClientSideEncryptionSpecTest; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use PHPUnit\Framework\Attributes\Group; use function base64_decode; use function in_array; use function microtime; use function ob_end_clean; use function ob_start; +use function usleep; use function var_dump; -use function version_compare; /** * Documentation examples to be parsed for inclusion in the MongoDB manual. @@ -28,29 +29,13 @@ * @see https://jira.mongodb.org/browse/DRIVERS-356 * @see https://jira.mongodb.org/browse/DRIVERS-488 * @see https://jira.mongodb.org/browse/DRIVERS-547 + * @see https://jira.mongodb.org/browse/DRIVERS-2838 */ class DocumentationExamplesTest extends FunctionalTestCase { - public function setUp(): void - { - parent::setUp(); - - $this->dropCollection(); - } - - public function tearDown(): void - { - if ($this->hasFailed()) { - return; - } - - $this->dropCollection(); - - parent::tearDown(); - } - public function testExample_1_2(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 1 @@ -75,6 +60,7 @@ public function testExample_1_2(): void public function testExample_3(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 3 @@ -110,6 +96,7 @@ public function testExample_3(): void public function testExample_6_13(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 6 @@ -214,6 +201,7 @@ public function testExample_6_13(): void public function testExample_14_19(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 14 @@ -295,6 +283,7 @@ public function testExample_14_19(): void public function testExample_20_28(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 20 @@ -402,6 +391,7 @@ public function testExample_20_28(): void public function testExample_29_37(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 29 @@ -501,6 +491,7 @@ public function testExample_29_37(): void public function testExample_38_41(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 38 @@ -538,6 +529,7 @@ public function testExample_38_41(): void public function testExample_42_50(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 42 @@ -601,14 +593,14 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status', 'size', 'instock'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } } // Start Example 44 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['item' => 1, 'status' => 1]] + ['projection' => ['item' => 1, 'status' => 1]], ); // End Example 44 @@ -616,18 +608,18 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } foreach (['size', 'instock'] as $field) { - $this->assertObjectNotHasAttribute($field, $document); + $this->assertObjectNotHasProperty($field, $document); } } // Start Example 45 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['item' => 1, 'status' => 1, '_id' => 0]] + ['projection' => ['item' => 1, 'status' => 1, '_id' => 0]], ); // End Example 45 @@ -635,18 +627,18 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['item', 'status'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } foreach (['_id', 'size', 'instock'] as $field) { - $this->assertObjectNotHasAttribute($field, $document); + $this->assertObjectNotHasProperty($field, $document); } } // Start Example 46 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['status' => 0, 'instock' => 0]] + ['projection' => ['status' => 0, 'instock' => 0]], ); // End Example 46 @@ -654,18 +646,18 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'size'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } foreach (['status', 'instock'] as $field) { - $this->assertObjectNotHasAttribute($field, $document); + $this->assertObjectNotHasProperty($field, $document); } } // Start Example 47 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['item' => 1, 'status' => 1, 'size.uom' => 1]] + ['projection' => ['item' => 1, 'status' => 1, 'size.uom' => 1]], ); // End Example 47 @@ -673,19 +665,19 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status', 'size'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } - $this->assertObjectNotHasAttribute('instock', $document); - $this->assertObjectHasAttribute('uom', $document->size); - $this->assertObjectNotHasAttribute('h', $document->size); - $this->assertObjectNotHasAttribute('w', $document->size); + $this->assertObjectNotHasProperty('instock', $document); + $this->assertObjectHasProperty('uom', $document->size); + $this->assertObjectNotHasProperty('h', $document->size); + $this->assertObjectNotHasProperty('w', $document->size); } // Start Example 48 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['size.uom' => 0]] + ['projection' => ['size.uom' => 0]], ); // End Example 48 @@ -693,18 +685,18 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status', 'size', 'instock'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } - $this->assertObjectHasAttribute('h', $document->size); - $this->assertObjectHasAttribute('w', $document->size); - $this->assertObjectNotHasAttribute('uom', $document->size); + $this->assertObjectHasProperty('h', $document->size); + $this->assertObjectHasProperty('w', $document->size); + $this->assertObjectNotHasProperty('uom', $document->size); } // Start Example 49 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['item' => 1, 'status' => 1, 'instock.qty' => 1]] + ['projection' => ['item' => 1, 'status' => 1, 'instock.qty' => 1]], ); // End Example 49 @@ -712,20 +704,20 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status', 'instock'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } - $this->assertObjectNotHasAttribute('size', $document); + $this->assertObjectNotHasProperty('size', $document); foreach ($document->instock as $instock) { - $this->assertObjectHasAttribute('qty', $instock); - $this->assertObjectNotHasAttribute('warehouse', $instock); + $this->assertObjectHasProperty('qty', $instock); + $this->assertObjectNotHasProperty('warehouse', $instock); } } // Start Example 50 $cursor = $db->inventory->find( ['status' => 'A'], - ['projection' => ['item' => 1, 'status' => 1, 'instock' => ['$slice' => -1]]] + ['projection' => ['item' => 1, 'status' => 1, 'instock' => ['$slice' => -1]]], ); // End Example 50 @@ -733,16 +725,82 @@ public function testExample_42_50(): void $this->assertCount(3, $documents); foreach ($documents as $document) { foreach (['_id', 'item', 'status', 'instock'] as $field) { - $this->assertObjectHasAttribute($field, $document); + $this->assertObjectHasProperty($field, $document); } - $this->assertObjectNotHasAttribute('size', $document); + $this->assertObjectNotHasProperty('size', $document); $this->assertCount(1, $document->instock); } } + public function testAggregationProjectionExample_1(): void + { + $this->skipIfServerVersion('<', '4.4.0', '$project syntax for find and findAndModify is not supported'); + + $this->dropCollection($this->getDatabaseName(), 'inventory'); + $db = new Database($this->manager, $this->getDatabaseName()); + + $db->inventory->insertMany([ + [ + 'item' => 'journal', + 'status' => 'A', + 'size' => ['h' => 14, 'w' => 21, 'uom' => 'cm'], + ], + [ + 'item' => 'notebook', + 'status' => 'A', + 'size' => ['h' => 8.5, 'w' => 11, 'uom' => 'in'], + ], + [ + 'item' => 'paper', + 'status' => 'D', + 'size' => ['h' => 8.5, 'w' => 11, 'uom' => 'in'], + ], + ]); + + // Start Aggregation Projection Example 1 + $cursor = $db->inventory->find([], [ + 'projection' => [ + '_id' => 0, + 'item' => 1, + 'status' => [ + '$switch' => [ + 'branches' => [ + ['case' => ['$eq' => ['$status', 'A']], 'then' => 'Available'], + ['case' => ['$eq' => ['$status', 'D']], 'then' => 'Discontinued'], + ], + 'default' => 'No status found', + ], + ], + 'area' => [ + '$concat' => [ + ['$toString' => ['$multiply' => ['$size.h', '$size.w']]], + ' ', + '$size.uom', + ], + ], + 'reportNumber' => ['$literal' => 1], + ], + ]); + // End Aggregation Projection Example 1 + + $documents = $cursor->toArray(); + $this->assertCount(3, $documents); + foreach ($documents as $document) { + foreach (['item', 'status', 'area', 'reportNumber'] as $field) { + $this->assertObjectHasProperty($field, $document); + } + + $this->assertObjectNotHasProperty('_id', $document); + $this->assertIsString($document->status); + $this->assertIsString($document->area); + $this->assertSame(1, $document->reportNumber); + } + } + public function testExample_51_54(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 51 @@ -823,7 +881,7 @@ public function testExample_51_54(): void [ '$set' => ['size.uom' => 'cm', 'status' => 'P'], '$currentDate' => ['lastModified' => true], - ] + ], ); // End Example 52 @@ -843,7 +901,7 @@ public function testExample_51_54(): void [ '$set' => ['size.uom' => 'cm', 'status' => 'P'], '$currentDate' => ['lastModified' => true], - ] + ], ); // End Example 53 @@ -866,7 +924,7 @@ public function testExample_51_54(): void ['warehouse' => 'A', 'qty' => 60], ['warehouse' => 'B', 'qty' => 40], ], - ] + ], ); // End Example 54 @@ -884,6 +942,7 @@ public function testExample_51_54(): void public function testExample_55_58(): void { + $this->dropCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Example 55 @@ -952,7 +1011,7 @@ public function testExample_55_58(): void $this->assertInventoryCount(0); } - /** @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster */ + #[Group('matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster')] public function testChangeStreamExample_1_4(): void { $this->skipIfChangeStreamIsNotSupported(); @@ -961,9 +1020,8 @@ public function testChangeStreamExample_1_4(): void $this->markTestSkipped('Test does not apply on sharded clusters: need more than a single getMore call on the change stream.'); } + $this->createCollection($this->getDatabaseName(), 'inventory'); $db = new Database($this->manager, $this->getDatabaseName()); - $db->dropCollection('inventory'); - $db->createCollection('inventory'); // Start Changestream Example 1 $changeStream = $db->inventory->watch(); @@ -1061,6 +1119,7 @@ public function testChangeStreamExample_1_4(): void public function testAggregation_example_1(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 1 @@ -1075,6 +1134,7 @@ public function testAggregation_example_1(): void public function testAggregation_example_2(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 2 @@ -1103,6 +1163,7 @@ public function testAggregation_example_2(): void public function testAggregation_example_3(): void { + $this->dropCollection($this->getDatabaseName(), 'sales'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Aggregation Example 3 @@ -1141,6 +1202,7 @@ public function testAggregation_example_3(): void public function testAggregation_example_4(): void { + $this->dropCollection($this->getDatabaseName(), 'air_airlines'); $db = new Database($this->manager, $this->getDatabaseName()); // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps @@ -1191,22 +1253,9 @@ public function testRunCommand_example_1(): void $this->assertInstanceOf(Cursor::class, $cursor); } - public function testRunCommand_example_2(): void - { - $db = new Database($this->manager, $this->getDatabaseName()); - $db->dropCollection('restaurants'); - $db->createCollection('restaurants'); - - // Start runCommand Example 2 - $cursor = $db->command(['collStats' => 'restaurants']); - $result = $cursor->toArray()[0]; - // End runCommand Example 2 - - $this->assertInstanceOf(Cursor::class, $cursor); - } - public function testIndex_example_1(): void { + $this->dropCollection($this->getDatabaseName(), 'records'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Index Example 1 @@ -1218,12 +1267,13 @@ public function testIndex_example_1(): void public function testIndex_example_2(): void { + $this->dropCollection($this->getDatabaseName(), 'restaurants'); $db = new Database($this->manager, $this->getDatabaseName()); // Start Index Example 2 $indexName = $db->restaurants->createIndex( ['cuisine' => 1, 'name' => 1], - ['partialFilterExpression' => ['rating' => ['$gt' => 5]]] + ['partialFilterExpression' => ['rating' => ['$gt' => 5]]], ); // End Index Example 2 @@ -1245,11 +1295,11 @@ private function updateEmployeeInfo1(\MongoDB\Client $client, \MongoDB\Driver\Se $client->hr->employees->updateOne( ['employee' => 3], ['$set' => ['status' => 'Inactive']], - ['session' => $session] + ['session' => $session], ); $client->reporting->events->insertOne( ['employee' => 3, 'status' => ['new' => 'Inactive', 'old' => 'Active']], - ['session' => $session] + ['session' => $session], ); } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Caught exception during transaction, aborting.\n"; @@ -1292,13 +1342,9 @@ public function testTransactions_intro_example_1(): void $client = static::createTestClient(); - /* The WC is required: https://mongodb.com/docs/manual/core/transactions/#transactions-and-locks */ - $client->hr->dropCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->dropCollection('events', ['writeConcern' => new WriteConcern('majority')]); - /* Collections need to be created before a transaction starts */ - $client->hr->createCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->createCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $this->createCollection('hr', 'employees'); + $this->createCollection('reporting', 'events'); $session = $client->startSession(); @@ -1425,8 +1471,8 @@ private function commitWithRetry3(\MongoDB\Driver\Session $session): void private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Session $session): void { $session->startTransaction([ - 'readConcern' => new \MongoDB\Driver\ReadConcern("snapshot"), - 'readPrefernece' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY), + 'readConcern' => new \MongoDB\Driver\ReadConcern('snapshot'), + 'readPrefernece' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::PRIMARY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY), ]); @@ -1434,11 +1480,11 @@ private function updateEmployeeInfo3(\MongoDB\Client $client, \MongoDB\Driver\Se $client->hr->employees->updateOne( ['employee' => 3], ['$set' => ['status' => 'Inactive']], - ['session' => $session] + ['session' => $session], ); $client->reporting->events->insertOne( ['employee' => 3, 'status' => ['new' => 'Inactive', 'old' => 'Active']], - ['session' => $session] + ['session' => $session], ); } catch (\MongoDB\Driver\Exception\Exception $error) { echo "Caught exception during transaction, aborting.\n"; @@ -1457,7 +1503,7 @@ private function doUpdateEmployeeInfo(\MongoDB\Client $client): void try { $this->runTransactionWithRetry3([$this, 'updateEmployeeInfo3'], $client, $session); - } catch (\MongoDB\Driver\Exception\Exception $error) { + } catch (\MongoDB\Driver\Exception\Exception) { // Do something with error } } @@ -1472,13 +1518,9 @@ public function testTransactions_retry_example_3(): void $client = static::createTestClient(); - /* The WC is required: https://mongodb.com/docs/manual/core/transactions/#transactions-and-locks */ - $client->hr->dropCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->dropCollection('events', ['writeConcern' => new WriteConcern('majority')]); - /* Collections need to be created before a transaction starts */ - $client->hr->createCollection('employees', ['writeConcern' => new WriteConcern('majority')]); - $client->reporting->createCollection('events', ['writeConcern' => new WriteConcern('majority')]); + $this->createCollection('hr', 'employees'); + $this->createCollection('reporting', 'events'); ob_start(); try { @@ -1496,14 +1538,9 @@ public function testCausalConsistency(): void // Prep $client = static::createTestClient(); - $items = $client->selectDatabase( - 'test', - ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] - )->items; - - $items->drop(); + $items = $this->createCollection('test', 'items'); $items->insertOne( - ['sku' => '111', 'name' => 'Peanuts', 'start' => new UTCDateTime()] + ['sku' => '111', 'name' => 'Peanuts', 'start' => new UTCDateTime()], ); try { @@ -1511,8 +1548,8 @@ public function testCausalConsistency(): void * mode, so using $manager->selectServer does not work here. To work * around this, we run a query on a secondary and rely on an * exception to let us know that no secondary is available. */ - $items->countDocuments([], ['readPreference' => new ReadPreference(ReadPreference::RP_SECONDARY)]); - } catch (Exception $e) { + $items->countDocuments([], ['readPreference' => new ReadPreference(ReadPreference::SECONDARY)]); + } catch (Exception) { $this->markTestSkipped('Secondary is not available'); } @@ -1523,11 +1560,11 @@ public function testCausalConsistency(): void [ 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::MAJORITY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - ] + ], )->items; $s1 = $client->startSession( - ['causalConsistency' => true] + ['causalConsistency' => true], ); $currentDate = new \MongoDB\BSON\UTCDateTime(); @@ -1535,11 +1572,11 @@ public function testCausalConsistency(): void $items->updateOne( ['sku' => '111', 'end' => ['$exists' => false]], ['$set' => ['end' => $currentDate]], - ['session' => $s1] + ['session' => $s1], ); $items->insertOne( ['sku' => '111-nuts', 'name' => 'Pecans', 'start' => $currentDate], - ['session' => $s1] + ['session' => $s1], ); // End Causal Consistency Example 1 // phpcs:enable @@ -1549,7 +1586,7 @@ public function testCausalConsistency(): void // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly // Start Causal Consistency Example 2 $s2 = $client->startSession( - ['causalConsistency' => true] + ['causalConsistency' => true], ); $s2->advanceClusterTime($s1->getClusterTime()); $s2->advanceOperationTime($s1->getOperationTime()); @@ -1557,15 +1594,15 @@ public function testCausalConsistency(): void $items = $client->selectDatabase( 'test', [ - 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_SECONDARY), + 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::SECONDARY), 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::MAJORITY), 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - ] + ], )->items; $result = $items->find( ['end' => ['$exists' => false]], - ['session' => $s2] + ['session' => $s2], ); foreach ($result as $item) { var_dump($item); @@ -1579,25 +1616,19 @@ public function testCausalConsistency(): void public function testSnapshotQueries(): void { - if (version_compare($this->getServerVersion(), '5.0.0', '<')) { - $this->markTestSkipped('Snapshot queries outside of transactions are not supported'); - } + $this->skipIfServerVersion('<', '5.0.0', 'Snapshot queries outside of transactions are not supported'); - if (! ($this->isReplicaSet() || $this->isShardedClusterUsingReplicasets())) { + if ($this->isStandalone()) { $this->markTestSkipped('Snapshot read concern is only supported with replicasets'); } - $client = static::createTestClient(); - - $catsCollection = $client->selectCollection('pets', 'cats'); - $catsCollection->drop(); + $catsCollection = $this->dropCollection('pets', 'cats'); $catsCollection->insertMany([ ['name' => 'Whiskers', 'color' => 'white', 'adoptable' => true], ['name' => 'Garfield', 'color' => 'orange', 'adoptable' => false], ]); - $dogsCollection = $client->selectCollection('pets', 'dogs'); - $dogsCollection->drop(); + $dogsCollection = $this->dropCollection('pets', 'dogs'); $dogsCollection->insertMany([ ['name' => 'Toto', 'color' => 'black', 'adoptable' => true], ['name' => 'Milo', 'color' => 'black', 'adoptable' => false], @@ -1612,6 +1643,8 @@ public function testSnapshotQueries(): void $this->waitForSnapshot('pets', 'dogs'); } + $client = static::createTestClient(); + ob_start(); // Start Snapshot Query Example 1 @@ -1625,7 +1658,7 @@ public function testSnapshotQueries(): void ['$match' => ['adoptable' => true]], ['$count' => 'adoptableCatsCount'], ], - ['session' => $session] + ['session' => $session], )->toArray()[0]->adoptableCatsCount; $adoptablePetsCount += $dogsCollection->aggregate( @@ -1633,7 +1666,7 @@ public function testSnapshotQueries(): void ['$match' => ['adoptable' => true]], ['$count' => 'adoptableDogsCount'], ], - ['session' => $session] + ['session' => $session], )->toArray()[0]->adoptableDogsCount; var_dump($adoptablePetsCount); @@ -1643,11 +1676,7 @@ public function testSnapshotQueries(): void $this->assertSame(3, $adoptablePetsCount); - $catsCollection->drop(); - $dogsCollection->drop(); - - $salesCollection = $client->selectCollection('retail', 'sales'); - $salesCollection->drop(); + $salesCollection = $this->dropCollection('retail', 'sales'); $salesCollection->insertMany([ ['shoeType' => 'boot', 'price' => 30, 'saleDate' => new UTCDateTime()], ]); @@ -1681,18 +1710,14 @@ public function testSnapshotQueries(): void ], ['$count' => 'totalDailySales'], ], - ['session' => $session] + ['session' => $session], )->toArray()[0]->totalDailySales; // End Snapshot Query Example 2 $this->assertSame(1, $totalDailySales); - - $salesCollection->drop(); } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testVersionedApi(): void { $uriString = static::getUri(true); @@ -1722,26 +1747,19 @@ public function testVersionedApi(): void public function testVersionedApiMigration(): void { - if (version_compare($this->getServerVersion(), '5.0.0', '<')) { - $this->markTestSkipped('Versioned API is not supported'); - } - - if (version_compare($this->getServerVersion(), '5.0.9', '>=')) { - $this->markTestSkipped('The count command was added to API version 1 (SERVER-63850)'); - } + $this->skipIfServerVersion('<', '5.0.0', 'Versioned API is not supported'); + $this->skipIfServerVersion('>=', '5.0.9', 'The count command was added to API version 1 (SERVER-63850)'); + $this->dropCollection($this->getDatabaseName(), 'sales'); $uriString = static::getUri(true); // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly $serverApi = new \MongoDB\Driver\ServerApi('1', true); $client = new \MongoDB\Client($uriString, [], ['serverApi' => $serverApi]); $db = $client->selectDatabase($this->getDatabaseName()); - $db->dropCollection('sales'); // Start Versioned API Example 5 - $strtoutc = function (string $datetime) { - return new \MongoDB\BSON\UTCDateTime(new \DateTime($datetime)); - }; + $strtoutc = fn (string $datetime) => new \MongoDB\BSON\UTCDateTime(new \DateTime($datetime)); $db->sales->insertMany([ ['_id' => 1, 'item' => 'abc', 'price' => 10, 'quantity' => 2, 'date' => $strtoutc('2021-01-01T08:00:00Z')], @@ -1784,14 +1802,14 @@ public function testVersionedApiMigration(): void // phpcs:enable } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testWithTransactionExample(): void { $this->skipIfTransactionsAreNotSupported(); $uriString = static::getUri(true); + $this->dropCollection('mydb1', 'foo'); + $this->dropCollection('mydb2', 'bar'); // phpcs:disable SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly // Start Transactions withTxn API Example 1 @@ -1810,7 +1828,7 @@ public function testWithTransactionExample(): void 'foo', [ 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - ] + ], )->insertOne(['abc' => 0]); $client->selectCollection( @@ -1818,7 +1836,7 @@ public function testWithTransactionExample(): void 'bar', [ 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - ] + ], )->insertOne(['xyz' => 0]); // Step 1: Define the callback that specifies the sequence of operations to perform inside the transactions. @@ -1839,13 +1857,7 @@ public function testWithTransactionExample(): void // Step 3: Use with_transaction to start a transaction, execute the callback, and commit (or abort on error). - $transactionOptions = [ - 'readConcern' => new \MongoDB\Driver\ReadConcern(\MongoDB\Driver\ReadConcern::LOCAL), - 'writeConcern' => new \MongoDB\Driver\WriteConcern(\MongoDB\Driver\WriteConcern::MAJORITY, 1000), - 'readPreference' => new \MongoDB\Driver\ReadPreference(\MongoDB\Driver\ReadPreference::RP_PRIMARY), - ]; - - \MongoDB\with_transaction($session, $callback, $transactionOptions); + \MongoDB\with_transaction($session, $callback); // End Transactions withTxn API Example 1 // phpcs:enable @@ -1859,42 +1871,32 @@ public function testWithTransactionExample(): void */ public function testQueryableEncryption(): void { - if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + if ($this->isStandalone()) { $this->markTestSkipped('Queryable encryption requires replica sets'); } - if (version_compare($this->getServerVersion(), '6.0.0', '<')) { - $this->markTestSkipped('Queryable encryption requires MongoDB 6.0 or later'); - } - - // Note: this version requirement is consistent with QEv1 spec tests - if (version_compare($this->getServerVersion(), '6.2.99', '>')) { - $this->markTestSkipped('MongoDB 7.0 and later requires Queryable Encryption v2 protocol'); - } - - if (! $this->isEnterprise()) { - $this->markTestSkipped('Automatic encryption requires MongoDB Enterprise'); - } + $this->skipIfServerVersion('<', '7.0.0', 'Explicit encryption tests require MongoDB 7.0 or later'); + $this->skipIfClientSideEncryptionIsNotSupported(); // Fetch names for the database and collection under test $collectionName = $this->getCollectionName(); $databaseName = $this->getDatabaseName(); $namespace = $this->getNamespace(); - /* Create a client without auto encryption. Drop existing data in both - * the keyvault and database under test. The latter is necessary since - * setUp() only drops the collection under test, which will leave behind - * internal collections for queryable encryption. */ + /* Create a client without auto encryption. Drop existing data in both the + * keyvault and the collection under test, the "encryptedFields" option + * is necessary to clean up any internal collections for queryable + * encryption, assuming they use their default names */ $client = static::createTestClient(); - $client->selectDatabase('keyvault')->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); - $client->selectDatabase($databaseName)->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + $this->dropCollection('keyvault', 'datakeys'); + $this->dropCollection($databaseName, $collectionName, ['encryptedFields' => []]); /* Although ClientEncryption can be constructed directly, the library * provides a helper to do so. With this method, the keyVaultClient will * default to the same client. */ $clientEncryption = $client->createClientEncryption([ 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY))]], ]); // Create two data keys, one for each encrypted field @@ -1903,7 +1905,7 @@ public function testQueryableEncryption(): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(ClientSideEncryptionSpecTest::LOCAL_MASTERKEY))]], 'encryptedFieldsMap' => [ $namespace => [ 'fields' => [ @@ -1961,14 +1963,6 @@ public function testQueryableEncryption(): void $this->assertInstanceOf(Binary::class, $result['encryptedUnindexed']); } - /** - * Return the test collection name. - */ - protected function getCollectionName(): string - { - return 'inventory'; - } - private function assertCursorCount($count, Cursor $cursor): void { $this->assertCount($count, $cursor->toArray()); @@ -1976,7 +1970,7 @@ private function assertCursorCount($count, Cursor $cursor): void private function assertInventoryCount($count): void { - $this->assertCollectionCount($this->getDatabaseName() . '.' . $this->getCollectionName(), $count); + $this->assertCollectionCount($this->getDatabaseName() . '.inventory', $count); } private function waitForSnapshot(string $databaseName, string $collectionName): void @@ -1984,33 +1978,25 @@ private function waitForSnapshot(string $databaseName, string $collectionName): $collection = new Collection($this->manager, $databaseName, $collectionName); $session = $this->manager->startSession(['snapshot' => true]); - /* Retry until a snapshot query succeeds or ten seconds elapse, - * whichwever comes first. - * - * TODO: use hrtime() once the library requires PHP 7.3+ */ + // Retry until a snapshot query succeeds or ten seconds elapse. $retryUntil = microtime(true) + 10; do { try { - $collection->aggregate( - [['$match' => ['_id' => ['$exists' => true]]]], - ['session' => $session] - ); - - break; + if ($collection->findOne([], ['session' => $session]) !== null) { + break; + } } catch (CommandException $e) { - if ($e->getCode() === 246 /* SnapshotUnavailable */) { - continue; + if ($e->getCode() !== 246 /* SnapshotUnavailable */) { + throw $e; } - - throw $e; } + + usleep(1000); } while (microtime(true) < $retryUntil); } - /** - * @see https://jira.mongodb.org/browse/SERVER-39704 - */ + /** @see https://jira.mongodb.org/browse/SERVER-39704 */ private function preventStaleDbVersionError(string $databaseName, string $collectionName): void { $collection = new Collection($this->manager, $databaseName, $collectionName); diff --git a/tests/ExamplesTest.php b/tests/ExamplesTest.php index d90f53dd6..78ade169c 100644 --- a/tests/ExamplesTest.php +++ b/tests/ExamplesTest.php @@ -3,8 +3,17 @@ namespace MongoDB\Tests; use Generator; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses; -/** @runTestsInSeparateProcesses */ +use function bin2hex; +use function getenv; +use function putenv; +use function random_bytes; +use function sprintf; + +#[RunTestsInSeparateProcesses] final class ExamplesTest extends FunctionalTestCase { public function setUp(): void @@ -15,10 +24,18 @@ public function setUp(): void $this->markTestSkipped('Examples are not tested on sharded clusters.'); } - self::createTestClient()->dropDatabase('test'); + if ($this->isApiVersionRequired()) { + $this->markTestSkipped('Examples are not tested when the server requires specifying an API version.'); + } + } + + #[DataProvider('provideExamples')] + public function testExamples(string $file, string $expectedOutput): void + { + $this->assertExampleOutput($file, $expectedOutput); } - public function dataExamples(): Generator + public static function provideExamples(): Generator { $expectedOutput = <<<'OUTPUT' { "_id" : null, "totalCount" : 100, "evenCount" : %d, "oddCount" : %d, "maxValue" : %d, "minValue" : %d } @@ -86,32 +103,68 @@ public function dataExamples(): Generator ]; $expectedOutput = <<<'OUTPUT' -object(MongoDB\Examples\PersistableEntry)#%d (%d) { - ["id":"MongoDB\Examples\PersistableEntry":private]=> - object(MongoDB\BSON\ObjectId)#%d (%d) { - ["oid"]=> - string(24) "%s" - } - ["name"]=> - string(7) "alcaeus" - ["emails"]=> - array(2) { - [0]=> - object(MongoDB\Examples\PersistableEmail)#%d (%d) { - ["type"]=> - string(4) "work" - ["address"]=> - string(19) "alcaeus@example.com" - } - [1]=> - object(MongoDB\Examples\PersistableEmail)#%d (%d) { - ["type"]=> - string(7) "private" - ["address"]=> - string(18) "secret@example.com" - } - } -} +Read a total of 17888890 bytes +Deleted file with ID: %s +OUTPUT; + + yield 'gridfs_stream' => [ + 'file' => __DIR__ . '/../examples/gridfs_stream.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +Inserted file with ID: %s +File contents: Hello world! +Deleted file with ID: %s + +OUTPUT; + + yield 'gridfs_upload' => [ + 'file' => __DIR__ . '/../examples/gridfs_upload.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +File exists: no +Writing file +File exists: yes +Reading file: Hello, GridFS! +Writing new version of the file +Reading new version of the file: Hello, GridFS! (v2) +Reading previous version of the file: Hello, GridFS! +OUTPUT; + + yield 'gridfs_stream_wrapper' => [ + 'file' => __DIR__ . '/../examples/gridfs_stream_wrapper.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +MongoDB\Examples\Persistable\PersistableEntry Object +( + [id:MongoDB\Examples\Persistable\PersistableEntry:private] => MongoDB\BSON\ObjectId Object + ( + [oid] => %s + ) + + [emails] => Array + ( + [0] => MongoDB\Examples\Persistable\PersistableEmail Object + ( + [type] => work + [address] => alcaeus@example.com + ) + + [1] => MongoDB\Examples\Persistable\PersistableEmail Object + ( + [type] => private + [address] => secret@example.com + ) + + ) + + [name] => alcaeus +) OUTPUT; yield 'persistable' => [ @@ -119,33 +172,49 @@ public function dataExamples(): Generator 'expectedOutput' => $expectedOutput, ]; + /* Note: Do not assert output beyond the initial topology events, as it + * may vary based on the test environment. PHPUnit's matching behavior + * for "%A" also seems to differ slightly from run-tests.php, otherwise + * we could assert the final topologyClosed event. */ $expectedOutput = <<<'OUTPUT' -object(MongoDB\Examples\TypeMapEntry)#%d (%d) { - ["id":"MongoDB\Examples\TypeMapEntry":private]=> - object(MongoDB\BSON\ObjectId)#%d (%d) { - ["oid"]=> - string(24) "%s" - } - ["name":"MongoDB\Examples\TypeMapEntry":private]=> - string(7) "alcaeus" - ["emails":"MongoDB\Examples\TypeMapEntry":private]=> - array(2) { - [0]=> - object(MongoDB\Examples\TypeMapEmail)#%d (%d) { - ["type":"MongoDB\Examples\TypeMapEmail":private]=> - string(4) "work" - ["address":"MongoDB\Examples\TypeMapEmail":private]=> - string(19) "alcaeus@example.com" - } - [1]=> - object(MongoDB\Examples\TypeMapEmail)#%d (%d) { - ["type":"MongoDB\Examples\TypeMapEmail":private]=> - string(7) "private" - ["address":"MongoDB\Examples\TypeMapEmail":private]=> - string(18) "secret@example.com" - } - } -} +topologyOpening: %x was opened + +topologyChanged: %x changed from Unknown to %s + +%A +OUTPUT; + + yield 'sdam_logger' => [ + 'file' => __DIR__ . '/../examples/sdam_logger.php', + 'expectedOutput' => $expectedOutput, + ]; + + $expectedOutput = <<<'OUTPUT' +MongoDB\Examples\Typemap\TypeMapEntry Object +( + [id:MongoDB\Examples\Typemap\TypeMapEntry:private] => MongoDB\BSON\ObjectId Object + ( + [oid] => %s + ) + + [name:MongoDB\Examples\Typemap\TypeMapEntry:private] => alcaeus + [emails:MongoDB\Examples\Typemap\TypeMapEntry:private] => Array + ( + [0] => MongoDB\Examples\Typemap\TypeMapEmail Object + ( + [type:MongoDB\Examples\Typemap\TypeMapEmail:private] => work + [address:MongoDB\Examples\Typemap\TypeMapEmail:private] => alcaeus@example.com + ) + + [1] => MongoDB\Examples\Typemap\TypeMapEmail Object + ( + [type:MongoDB\Examples\Typemap\TypeMapEmail:private] => private + [address:MongoDB\Examples\Typemap\TypeMapEmail:private] => secret@example.com + ) + + ) + +) OUTPUT; yield 'typemap' => [ @@ -154,6 +223,53 @@ public function dataExamples(): Generator ]; } + /** + * MongoDB Atlas Search example requires a MongoDB Atlas cluster with MongoDB 7.0+ + * Tips for insiders: if using a cloud-dev server, append ".mongodb.net" to the MONGODB_URI. + */ + #[Group('atlas')] + public function testAtlasSearch(): void + { + $uri = getenv('MONGODB_URI') ?? ''; + if (! self::isAtlas($uri)) { + $this->markTestSkipped('Atlas Search examples are only supported on MongoDB Atlas'); + } + + $this->skipIfServerVersion('<', '7.0', 'Atlas Search examples require MongoDB 7.0 or later'); + + // Generate random collection name to avoid conflicts with consecutive runs as the index creation is asynchronous + $collectionName = sprintf('%s.%s', $this->getCollectionName(), bin2hex(random_bytes(5))); + $databaseName = $this->getDatabaseName(); + $collection = $this->createCollection($databaseName, $collectionName); + $collection->insertMany([ + ['name' => 'Ribeira Charming Duplex'], + ['name' => 'Ocean View Bondi Beach'], + ['name' => 'Luxury ocean view Beach Villa 622'], + ['name' => 'Ocean & Beach View Condo WBR H204'], + ['name' => 'Bondi Beach Spacious Studio With Ocean View'], + ['name' => 'New York City - Upper West Side Apt'], + ]); + putenv(sprintf('MONGODB_DATABASE=%s', $databaseName)); + putenv(sprintf('MONGODB_COLLECTION=%s', $collectionName)); + + $expectedOutput = <<<'OUTPUT' + +Creating the index. +%s +Performing a text search... + - Ocean View Bondi Beach + - Luxury ocean view Beach Villa 622 + - Ocean & Beach View Condo WBR H204 + - Bondi Beach Spacious Studio With Ocean View + +Enjoy MongoDB Atlas Search! + + +OUTPUT; + + $this->assertExampleOutput(__DIR__ . '/../examples/atlas_search.php', $expectedOutput); + } + public function testChangeStream(): void { $this->skipIfChangeStreamIsNotSupported(); @@ -172,15 +288,7 @@ public function testChangeStream(): void Aborting after 3 seconds... OUTPUT; - $this->testExample(__DIR__ . '/../examples/changestream.php', $expectedOutput); - } - - /** @dataProvider dataExamples */ - public function testExample(string $file, string $expectedOutput): void - { - require $file; - - self::assertStringMatchesFormat($expectedOutput, $this->getActualOutputForAssertion()); + $this->assertExampleOutput(__DIR__ . '/../examples/changestream.php', $expectedOutput); } public function testWithTransaction(): void @@ -193,6 +301,24 @@ public function testWithTransaction(): void %s OUTPUT; - $this->testExample(__DIR__ . '/../examples/with_transaction.php', $expectedOutput); + $this->assertExampleOutput(__DIR__ . '/../examples/with_transaction.php', $expectedOutput); + } + + private function assertExampleOutput(string $file, string $expectedOutput): void + { + require $file; + + $this->assertStringMatchesFormat($expectedOutput, $this->getActualOutputForAssertion()); + } + + private function setUpKeyVaultIndex(): void + { + self::createTestClient()->selectCollection('encryption', '__keyVault')->createIndex( + ['keyAltNames' => 1], + [ + 'unique' => true, + 'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]], + ], + ); } } diff --git a/tests/Exception/CreateEncryptedCollectionExceptionTest.php b/tests/Exception/CreateEncryptedCollectionExceptionTest.php new file mode 100644 index 000000000..b48273493 --- /dev/null +++ b/tests/Exception/CreateEncryptedCollectionExceptionTest.php @@ -0,0 +1,26 @@ + []]; + + $e = new CreateEncryptedCollectionException(new Exception(), $encryptedFields); + $this->assertSame($encryptedFields, $e->getEncryptedFields()); + } + + public function testGetPrevious(): void + { + $previous = new Exception(); + + $e = new CreateEncryptedCollectionException($previous, ['fields' => []]); + $this->assertSame($previous, $e->getPrevious()); + } +} diff --git a/tests/Exception/InvalidArgumentExceptionTest.php b/tests/Exception/InvalidArgumentExceptionTest.php new file mode 100644 index 000000000..95ce89061 --- /dev/null +++ b/tests/Exception/InvalidArgumentExceptionTest.php @@ -0,0 +1,47 @@ +assertStringContainsString($typeString, $e->getMessage()); + } + + public static function provideExpectedTypes() + { + yield 'expectedType is a string' => [ + 'array', + 'type "array"', + ]; + + yield 'expectedType is an array with one string' => [ + ['array'], + 'type "array"', + ]; + + yield 'expectedType is an array with two strings' => [ + ['array', 'integer'], + 'type "array" or "integer"', + ]; + + yield 'expectedType is an array with three strings' => [ + ['array', 'integer', 'object'], + 'type "array", "integer", or "object"', + ]; + } + + public function testExpectedTypeArrayMustNotBeEmpty(): void + { + $this->expectException(AssertionError::class); + InvalidArgumentException::invalidType('$arg', null, []); + } +} diff --git a/tests/Fixtures/Codec/TestDocumentCodec.php b/tests/Fixtures/Codec/TestDocumentCodec.php new file mode 100644 index 000000000..3df41e304 --- /dev/null +++ b/tests/Fixtures/Codec/TestDocumentCodec.php @@ -0,0 +1,71 @@ +id = $value->get('_id'); + $object->decoded = true; + + $object->x = new TestNestedObject(); + $object->x->foo = $value->get('x')->get('foo'); + + return $object; + } + + public function canEncode($value): bool + { + return $value instanceof TestObject; + } + + public function encode($value): Document + { + if (! $value instanceof TestObject) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return Document::fromPHP([ + '_id' => $value->id, + 'x' => Document::fromPHP(['foo' => $value->x->foo]), + 'encoded' => true, + ]); + } +} diff --git a/tests/Fixtures/Codec/TestFileCodec.php b/tests/Fixtures/Codec/TestFileCodec.php new file mode 100644 index 000000000..ff4767429 --- /dev/null +++ b/tests/Fixtures/Codec/TestFileCodec.php @@ -0,0 +1,71 @@ +id = $value->get('_id'); + $fileObject->length = (int) $value->get('length'); + $fileObject->chunkSize = (int) $value->get('chunkSize'); + $fileObject->filename = (string) $value->get('filename'); + + $uploadDate = $value->get('uploadDate'); + if ($uploadDate instanceof UTCDateTime) { + $fileObject->uploadDate = DateTimeImmutable::createFromMutable($uploadDate->toDateTime()); + } + + if ($value->has('metadata')) { + $metadata = $value->get('metadata'); + assert($metadata instanceof Document); + $fileObject->metadata = $metadata->toPHP(); + } + + return $fileObject; + } + + public function canEncode($value): bool + { + return $value instanceof TestFile; + } + + public function encode($value): Document + { + if (! $value instanceof TestFile) { + throw UnsupportedValueException::invalidEncodableValue($value); + } + + return Document::fromPHP([ + '_id' => $value->id, + 'length' => $value->length, + 'chunkSize' => $value->chunkSize, + 'uploadDate' => new UTCDateTime($value->uploadDate), + 'filename' => $value->filename, + ]); + } +} diff --git a/tests/Fixtures/Document/TestFile.php b/tests/Fixtures/Document/TestFile.php new file mode 100644 index 000000000..a9fcfd02d --- /dev/null +++ b/tests/Fixtures/Document/TestFile.php @@ -0,0 +1,31 @@ + $id, + 'x' => ['foo' => 'bar'], + ]); + } + + public static function createForFixture(int $id): self + { + $instance = new self(); + $instance->id = $id; + + $instance->x = new TestNestedObject(); + $instance->x->foo = 'bar'; + + return $instance; + } + + public static function createDecodedForFixture(int $id): self + { + $instance = self::createForFixture($id); + $instance->decoded = true; + + return $instance; + } +} diff --git a/tests/FunctionalTestCase.php b/tests/FunctionalTestCase.php index 76d81fb4e..156d80149 100644 --- a/tests/FunctionalTestCase.php +++ b/tests/FunctionalTestCase.php @@ -5,33 +5,33 @@ use InvalidArgumentException; use MongoDB\BSON\ObjectId; use MongoDB\Client; +use MongoDB\Collection; use MongoDB\Driver\Command; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Manager; -use MongoDB\Driver\Query; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; use MongoDB\Driver\ServerApi; use MongoDB\Driver\WriteConcern; use MongoDB\Operation\CreateCollection; use MongoDB\Operation\DatabaseCommand; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\ListCollections; use stdClass; use UnexpectedValueException; -use function array_merge; +use function array_intersect_key; use function call_user_func; use function count; use function current; use function explode; -use function filter_var; use function getenv; use function implode; use function in_array; use function is_array; use function is_callable; +use function is_executable; use function is_object; +use function is_readable; use function is_string; use function key; use function ob_get_clean; @@ -44,16 +44,20 @@ use function sprintf; use function version_compare; -use const FILTER_VALIDATE_BOOLEAN; +use const DIRECTORY_SEPARATOR; use const INFO_MODULES; +use const PATH_SEPARATOR; abstract class FunctionalTestCase extends TestCase { - /** @var Manager */ - protected $manager; + private const ATLAS_TLD = '/\.(mongodb\.net|mongodb-dev\.net)/'; - /** @var array */ - private $configuredFailPoints = []; + protected Manager $manager; + + private array $configuredFailPoints = []; + + /** @var array{int,{Collection,array}} */ + private array $collectionsToCleanup = []; public function setUp(): void { @@ -65,6 +69,10 @@ public function setUp(): void public function tearDown(): void { + if ($this->status()->isSuccess()) { + $this->cleanupCollections(); + } + $this->disableFailPoints(); parent::tearDown(); @@ -75,7 +83,7 @@ public static function createTestClient(?string $uri = null, array $options = [] return new Client( $uri ?? static::getUri(), static::appendAuthenticationOptions($options), - static::appendServerApiOption($driverOptions) + static::appendDriverOptions($driverOptions), ); } @@ -84,69 +92,22 @@ public static function createTestManager(?string $uri = null, array $options = [ return new Manager( $uri ?? static::getUri(), static::appendAuthenticationOptions($options), - static::appendServerApiOption($driverOptions) + static::appendDriverOptions($driverOptions), ); } public static function getUri($allowMultipleMongoses = false): string { - $uri = parent::getUri(); - + /* If multiple mongoses are allowed, the multi-mongos load balanced URI + * can be used if available; otherwise, fall back MONGODB_URI. */ if ($allowMultipleMongoses) { - return $uri; - } - - $urlParts = parse_url($uri); - if ($urlParts === false) { - return $uri; - } - - // Only modify URIs using the mongodb scheme - if ($urlParts['scheme'] !== 'mongodb') { - return $uri; + return getenv('MONGODB_MULTI_MONGOS_LB_URI') ?: parent::getUri(); } - $hosts = explode(',', $urlParts['host']); - $numHosts = count($hosts); - if ($numHosts === 1) { - return $uri; - } - - $manager = static::createTestManager($uri); - if ($manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY))->getType() !== Server::TYPE_MONGOS) { - return $uri; - } - - // Re-append port to last host - if (isset($urlParts['port'])) { - $hosts[$numHosts - 1] .= ':' . $urlParts['port']; - } - - $parts = ['mongodb://']; - - if (isset($urlParts['user'], $urlParts['pass'])) { - $parts += [ - $urlParts['user'], - ':', - $urlParts['pass'], - '@', - ]; - } - - $parts[] = $hosts[0]; - - if (isset($urlParts['path'])) { - $parts[] = $urlParts['path']; - } - - if (isset($urlParts['query'])) { - $parts = array_merge($parts, [ - '?', - $urlParts['query'], - ]); - } - - return implode('', $parts); + /* If multiple mongoses are prohibited, the single-mongos load balanced + * URI can be used if available; otherwise, we need to conditionally + * process MONGODB_URI. */ + return getenv('MONGODB_SINGLE_MONGOS_LB_URI') ?: static::getUriWithoutMultipleMongoses(); } protected function assertCollectionCount($namespace, $count): void @@ -250,12 +211,8 @@ protected function assertSameObjectId($expectedObjectId, $actualObjectId): void * @param array|stdClass $command configureFailPoint command document * @throws InvalidArgumentException if $command is not a configureFailPoint command */ - public function configureFailPoint($command, ?Server $server = null): void + public function configureFailPoint(array|stdClass $command, ?Server $server = null): void { - if (! $this->isFailCommandSupported()) { - $this->markTestSkipped('failCommand is only supported on mongod >= 4.0.0 and mongos >= 4.1.5.'); - } - if (! $this->isFailCommandEnabled()) { $this->markTestSkipped('The enableTestCommands parameter is not enabled.'); } @@ -275,10 +232,7 @@ public function configureFailPoint($command, ?Server $server = null): void $failPointServer = $server ?: $this->getPrimaryServer(); $operation = new DatabaseCommand('admin', $command); - $cursor = $operation->execute($failPointServer); - $result = $cursor->toArray()[0]; - - $this->assertCommandSucceeded($result); + $operation->execute($failPointServer); // Record the fail point so it can be disabled during tearDown() $this->configuredFailPoints[] = [$command->configureFailPoint, $failPointServer]; @@ -300,33 +254,62 @@ public static function getModuleInfo(string $row): ?string } /** - * Creates the test collection with the specified options. + * Creates the test collection with the specified options and ensures it is + * dropped again during tearDown(). If the collection already exists, it + * is dropped and recreated. + * + * A majority write concern is applied by default to ensure that the + * transaction can acquire the required locks. + * See: https://www.mongodb.com/docs/manual/core/transactions/#transactions-and-operations * - * If the "writeConcern" option is not specified but is supported by the - * server, a majority write concern will be used. This is helpful for tests - * using transactions or secondary reads. + * @param array $options CreateCollection options */ - protected function createCollection(array $options = []): void + protected function createCollection(string $databaseName, string $collectionName, array $options = []): Collection { - $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + // See: https://jira.mongodb.org/browse/PHPLIB-1145 + if (isset($options['encryptedFields'])) { + throw new InvalidArgumentException('The "encryptedFields" option is not supported by createCollection(). Time to refactor!'); + } + + // Pass only relevant options to drop the collection in case it already exists + $dropOptions = array_intersect_key($options, ['writeConcern' => 1, 'encryptedFields' => 1]); + $collection = $this->dropCollection($databaseName, $collectionName, $dropOptions); - $operation = new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $operation = new CreateCollection($databaseName, $collectionName, $options); $operation->execute($this->getPrimaryServer()); + + return $collection; } /** - * Drops the test collection with the specified options. + * Drops the test collection and ensures it is dropped again during tearDown(). + * + * A majority write concern is applied by default to ensure that the + * transaction can acquire the required locks. + * See: https://www.mongodb.com/docs/manual/core/transactions/#transactions-and-operations * - * If the "writeConcern" option is not specified but is supported by the - * server, a majority write concern will be used. This is helpful for tests - * using transactions or secondary reads. + * @param array $options Collection::dropCollection() options */ - protected function dropCollection(array $options = []): void + protected function dropCollection(string $databaseName, string $collectionName, array $options = []): Collection { + $collection = new Collection($this->manager, $databaseName, $collectionName); + $this->collectionsToCleanup[] = [$collection, $options]; + $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $collection->drop($options); - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName(), $options); - $operation->execute($this->getPrimaryServer()); + return $collection; + } + + private function cleanupCollections(): void + { + foreach ($this->collectionsToCleanup as [$collection, $options]) { + $options += ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]; + $collection->drop($options); + } + + $this->collectionsToCleanup = []; } protected function getFeatureCompatibilityVersion(?ReadPreference $readPreference = null) @@ -338,7 +321,7 @@ protected function getFeatureCompatibilityVersion(?ReadPreference $readPreferenc $cursor = $this->manager->executeCommand( 'admin', new Command(['getParameter' => 1, 'featureCompatibilityVersion' => 1]), - $readPreference ?: new ReadPreference(ReadPreference::RP_PRIMARY) + ['readPreference' => $readPreference ?: new ReadPreference(ReadPreference::PRIMARY)], ); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); @@ -353,7 +336,7 @@ protected function getFeatureCompatibilityVersion(?ReadPreference $readPreferenc protected function getPrimaryServer() { - return $this->manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + return $this->manager->selectServer(); } protected function getServerVersion(?ReadPreference $readPreference = null) @@ -361,7 +344,7 @@ protected function getServerVersion(?ReadPreference $readPreference = null) $buildInfo = $this->manager->executeCommand( $this->getDatabaseName(), new Command(['buildInfo' => 1]), - $readPreference ?: new ReadPreference(ReadPreference::RP_PRIMARY) + ['readPreference' => $readPreference ?: new ReadPreference(ReadPreference::PRIMARY)], )->toArray()[0]; if (isset($buildInfo->version) && is_string($buildInfo->version)) { @@ -376,7 +359,7 @@ protected function getServerStorageEngine(?ReadPreference $readPreference = null $cursor = $this->manager->executeCommand( $this->getDatabaseName(), new Command(['serverStatus' => 1]), - $readPreference ?: new ReadPreference('primary') + ['readPreference' => $readPreference ?: new ReadPreference(ReadPreference::PRIMARY)], ); $result = current($cursor->toArray()); @@ -388,18 +371,24 @@ protected function getServerStorageEngine(?ReadPreference $readPreference = null throw new UnexpectedValueException('Could not determine server storage engine'); } - protected function isEnterprise(): bool + /** + * Returns whether clients must specify an API version by checking the + * requireApiVersion server parameter. + */ + protected function isApiVersionRequired(): bool { - $buildInfo = $this->getPrimaryServer()->executeCommand( - $this->getDatabaseName(), - new Command(['buildInfo' => 1]) - )->toArray()[0]; + try { + $cursor = $this->manager->executeCommand( + 'admin', + new Command(['getParameter' => 1, 'requireApiVersion' => 1]), + ); - if (isset($buildInfo->modules) && is_array($buildInfo->modules)) { - return in_array('enterprise', $buildInfo->modules); + $document = current($cursor->toArray()); + } catch (CommandException) { + return false; } - throw new UnexpectedValueException('Could not determine server modules'); + return isset($document->requireApiVersion) && $document->requireApiVersion === true; } protected function isLoadBalanced() @@ -422,16 +411,6 @@ protected function isStandalone() return $this->getPrimaryServer()->getType() == Server::TYPE_STANDALONE; } - /** - * Return whether serverless (i.e. proxy as mongos) is being utilized. - */ - protected static function isServerless(): bool - { - $isServerless = getenv('MONGODB_IS_SERVERLESS'); - - return $isServerless !== false ? filter_var($isServerless, FILTER_VALIDATE_BOOLEAN) : false; - } - protected function isShardedCluster() { $type = $this->getPrimaryServer()->getType(); @@ -448,72 +427,43 @@ protected function isShardedCluster() return false; } - protected function isShardedClusterUsingReplicasets() + protected function skipIfServerVersion(string $operator, string $version, ?string $message = null): void { - // Assume serverless is a sharded cluster using replica sets - if (static::isServerless()) { - return true; + if (version_compare($this->getServerVersion(), $version, $operator)) { + $this->markTestSkipped($message ?? sprintf('Server version is %s %s', $operator, $version)); } + } - $cursor = $this->getPrimaryServer()->executeQuery( - 'config.shards', - new Query([], ['limit' => 1]) - ); - - $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); - $document = current($cursor->toArray()); - - if (! $document) { - return false; + protected function skipIfAtlasSearchIndexIsNotSupported(): void + { + if (! self::isAtlas()) { + self::markTestSkipped('Search Indexes are only supported on MongoDB Atlas 7.0+'); } - /** - * Use regular expression to distinguish between standalone or replicaset: - * Without a replicaset: "host" : "localhost:4100" - * With a replicaset: "host" : "dec6d8a7-9bc1-4c0e-960c-615f860b956f/localhost:4400,localhost:4401" - */ - return preg_match('@^.*/.*:\d+@', $document['host']); + $this->skipIfServerVersion('<', '7.0', 'Search Indexes are only supported on MongoDB Atlas 7.0+'); } protected function skipIfChangeStreamIsNotSupported(): void { - switch ($this->getPrimaryServer()->getType()) { - case Server::TYPE_MONGOS: - case Server::TYPE_LOAD_BALANCER: - if (! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('$changeStream is only supported with replicasets'); - } - - break; - - case Server::TYPE_RS_PRIMARY: - break; - - default: - $this->markTestSkipped('$changeStream is not supported'); + if ($this->isStandalone()) { + $this->markTestSkipped('$changeStream requires replica sets'); } } protected function skipIfCausalConsistencyIsNotSupported(): void { switch ($this->getPrimaryServer()->getType()) { - case Server::TYPE_MONGOS: - case Server::TYPE_LOAD_BALANCER: - if (! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('Causal Consistency is only supported with replicasets'); - } - + case Server::TYPE_STANDALONE: + $this->markTestSkipped('Causal consistency requires replica sets'); break; case Server::TYPE_RS_PRIMARY: + // Note: mongos does not report storage engine information if ($this->getServerStorageEngine() !== 'wiredTiger') { - $this->markTestSkipped('Causal Consistency requires WiredTiger storage engine'); + $this->markTestSkipped('Causal consistency requires WiredTiger storage engine'); } break; - - default: - $this->markTestSkipped('Causal Consistency is not supported'); } } @@ -526,6 +476,10 @@ protected function skipIfClientSideEncryptionIsNotSupported(): void if (static::getModuleInfo('libmongocrypt') === 'disabled') { $this->markTestSkipped('Client Side Encryption is not enabled in the MongoDB extension'); } + + if (! static::isCryptSharedLibAvailable() && ! static::isMongocryptdAvailable()) { + $this->markTestSkipped('Neither crypt_shared nor mongocryptd are available'); + } } protected function skipIfGeoHaystackIndexIsNotSupported(): void @@ -542,24 +496,57 @@ protected function skipIfTransactionsAreNotSupported(): void } if ($this->isShardedCluster()) { - if (! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('Transactions are not supported on sharded clusters without replica sets'); - } + $this->markTestSkipped('Transactions are only supported on FCV 4.2 or higher'); + } - if (version_compare($this->getFeatureCompatibilityVersion(), '4.2', '<')) { - $this->markTestSkipped('Transactions are only supported on FCV 4.2 or higher'); - } + if ($this->getServerStorageEngine() !== 'wiredTiger') { + $this->markTestSkipped('Transactions require WiredTiger storage engine'); + } + } - return; + protected function isEnterprise(): bool + { + $buildInfo = $this->getPrimaryServer()->executeCommand( + $this->getDatabaseName(), + new Command(['buildInfo' => 1]), + )->toArray()[0]; + + if (isset($buildInfo->modules) && is_array($buildInfo->modules)) { + return in_array('enterprise', $buildInfo->modules); } - if (version_compare($this->getFeatureCompatibilityVersion(), '4.0', '<')) { - $this->markTestSkipped('Transactions are only supported on FCV 4.0 or higher'); + throw new UnexpectedValueException('Could not determine server modules'); + } + + public static function isAtlas(?string $uri = null): bool + { + return preg_match(self::ATLAS_TLD, $uri ?? static::getUri()); + } + + /** @see https://www.mongodb.com/docs/manual/core/queryable-encryption/reference/shared-library/ */ + public static function isCryptSharedLibAvailable(): bool + { + $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); + + if ($cryptSharedLibPath === false) { + return false; } - if ($this->getServerStorageEngine() !== 'wiredTiger') { - $this->markTestSkipped('Transactions require WiredTiger storage engine'); + return is_readable($cryptSharedLibPath); + } + + /** @see https://www.mongodb.com/docs/manual/core/queryable-encryption/reference/mongocryptd/ */ + public static function isMongocryptdAvailable(): bool + { + $paths = explode(PATH_SEPARATOR, getenv('PATH')); + + foreach ($paths as $path) { + if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { + return true; + } } + + return false; } private static function appendAuthenticationOptions(array $options): array @@ -582,8 +569,12 @@ private static function appendAuthenticationOptions(array $options): array return $options; } - private static function appendServerApiOption(array $driverOptions): array + private static function appendDriverOptions(array $driverOptions): array { + if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { + $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } + if (getenv('API_VERSION') && ! isset($driverOptions['serverApi'])) { $driverOptions['serverApi'] = new ServerApi(getenv('API_VERSION')); } @@ -609,14 +600,68 @@ private function disableFailPoints(): void } } - /** - * Checks if the failCommand command is supported on this server version - */ - private function isFailCommandSupported(): bool + private static function getUriWithoutMultipleMongoses(): string { - $minVersion = $this->isShardedCluster() ? '4.1.5' : '4.0.0'; + /* Cache the result. We can safely assume the topology type will remain + * constant for the duration of the test suite. */ + static $uri; + + if (isset($uri)) { + return $uri; + } + + $uri = parent::getUri(); + $parsed = parse_url($uri); + + if (! isset($parsed['scheme'], $parsed['host'])) { + throw new UnexpectedValueException('Failed to parse scheme and host components from URI: ' . $uri); + } + + // Only modify URIs using the mongodb scheme + if ($parsed['scheme'] !== 'mongodb') { + return $uri; + } + + $hosts = explode(',', $parsed['host']); + $numHosts = count($hosts); + + if ($numHosts === 1) { + return $uri; + } + + $manager = static::createTestManager($uri); + if ($manager->selectServer()->getType() !== Server::TYPE_MONGOS) { + return $uri; + } + + // Re-append port to last host + if (isset($parsed['port'])) { + $hosts[$numHosts - 1] .= ':' . $parsed['port']; + } + + $parts = ['mongodb://']; + + if (isset($parsed['user'], $parsed['pass'])) { + $parts[] = $parsed['user'] . ':' . $parsed['pass'] . '@'; + } + + $parts[] = $hosts[0]; + + if (isset($parsed['path'])) { + $parts[] = $parsed['path']; + } elseif (isset($parsed['query'])) { + /* URIs containing connection options but no auth database component + * still require a slash before the question mark */ + $parts[] = '/'; + } + + if (isset($parsed['query'])) { + $parts[] = '?' . $parsed['query']; + } + + $uri = implode('', $parts); - return version_compare($this->getServerVersion(), $minVersion, '>='); + return $uri; } /** @@ -627,11 +672,11 @@ private function isFailCommandEnabled(): bool try { $cursor = $this->manager->executeCommand( 'admin', - new Command(['getParameter' => 1, 'enableTestCommands' => 1]) + new Command(['getParameter' => 1, 'enableTestCommands' => 1]), ); $document = current($cursor->toArray()); - } catch (CommandException $e) { + } catch (CommandException) { return false; } diff --git a/tests/Functions/GetEncryptedFieldsFromServerFunctionalTest.php b/tests/Functions/GetEncryptedFieldsFromServerFunctionalTest.php new file mode 100644 index 000000000..47849c1ce --- /dev/null +++ b/tests/Functions/GetEncryptedFieldsFromServerFunctionalTest.php @@ -0,0 +1,101 @@ +skipIfClientSideEncryptionIsNotSupported(); + + if ($this->isStandalone()) { + $this->markTestSkipped('Queryable encryption requires replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Queryable encryption requires MongoDB 7.0 or later'); + + $encryptionOptions = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => [ + 'key' => new Binary(str_repeat("\0", 96)), // 96-byte local master key + ], + ], + ]; + $client = static::createTestClient(driverOptions: ['autoEncryption' => $encryptionOptions]); + + // Ensure the key vault collection is dropped before each test + $this->keyVaultCollection = $client->getCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + $this->keyVaultCollection->drop(); + + $this->clientEncryption = $client->createClientEncryption($encryptionOptions); + + $this->database = $client->getDatabase($this->getDatabaseName()); + } + + public function tearDown(): void + { + $this->keyVaultCollection?->drop(); + } + + /** @see https://jira.mongodb.org/browse/PHPLIB-1702 */ + public function testDatabaseDropCollectionConsultsEncryptedFieldsFromServer(): void + { + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + 'local', + null, + ['encryptedFields' => ['fields' => []]], + ); + + $this->assertCountCollections(3, $this->getCollectionName(), 'createEncryptedCollection should create three collections'); + + $this->database->dropCollection($this->getCollectionName()); + + $this->assertCountCollections(0, $this->getCollectionName()); + } + + /** @see https://jira.mongodb.org/browse/PHPLIB-1702 */ + public function testCollectionDropConsultsEncryptedFieldsFromServer(): void + { + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + 'local', + null, + ['encryptedFields' => ['fields' => []]], + ); + + $this->assertCountCollections(3, $this->getCollectionName(), 'createEncryptedCollection should create three collections'); + + $this->database->getCollection($this->getCollectionName())->drop(); + + $this->assertCountCollections(0, $this->getCollectionName()); + } + + private function assertCountCollections(int $expected, $collectionName, string $message = ''): void + { + $collectionNames = $this->database->listCollectionNames([ + 'filter' => ['name' => new Regex(preg_quote($collectionName))], + ]); + $this->assertCount($expected, $collectionNames, $message); + } +} diff --git a/tests/Functions/SelectServerFunctionalTest.php b/tests/Functions/SelectServerFunctionalTest.php new file mode 100644 index 000000000..980541b35 --- /dev/null +++ b/tests/Functions/SelectServerFunctionalTest.php @@ -0,0 +1,52 @@ +skipIfTransactionsAreNotSupported(); + + if (! $this->isShardedCluster()) { + $this->markTestSkipped('Pinning requires a sharded cluster'); + } + + if ($this->isLoadBalanced()) { + $this->markTestSkipped('libmongoc does not pin for load-balanced topology'); + } + + /* By default, the Manager under test is created with a single-mongos + * URI. Explicitly create a Client with multiple mongoses. */ + $client = static::createTestClient(static::getUri(true)); + + // Collection must be created before the transaction starts + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + + $session = $client->startSession(); + $session->startTransaction(); + + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection->find([], ['session' => $session]); + + $this->assertTrue($session->isInTransaction()); + $this->assertInstanceOf(Server::class, $session->getServer(), 'Session is pinned'); + $this->assertEquals($session->getServer(), select_server($client->getManager(), ['session' => $session])); + } + + public static function providePinnedOptions(): array + { + return [ + [['readPreference' => new ReadPreference(ReadPreference::PRIMARY_PREFERRED)]], + [[]], + ]; + } +} diff --git a/tests/FunctionsTest.php b/tests/FunctionsTest.php index d3ead4abc..bf64b03ee 100644 --- a/tests/FunctionsTest.php +++ b/tests/FunctionsTest.php @@ -2,16 +2,22 @@ namespace MongoDB\Tests; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Builder\Stage\LimitStage; +use MongoDB\Builder\Stage\MatchStage; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; use function MongoDB\apply_type_map_to_document; use function MongoDB\create_field_path_type_map; -use function MongoDB\generate_index_name; +use function MongoDB\document_to_array; +use function MongoDB\is_builder_pipeline; use function MongoDB\is_first_key_operator; -use function MongoDB\is_mapreduce_output_inline; +use function MongoDB\is_last_pipeline_operator_write; use function MongoDB\is_pipeline; use function MongoDB\is_write_concern_acknowledged; @@ -20,15 +26,13 @@ */ class FunctionsTest extends TestCase { - /** - * @dataProvider provideDocumentAndTypeMap - */ + #[DataProvider('provideDocumentAndTypeMap')] public function testApplyTypeMapToDocument($document, array $typeMap, $expectedDocument): void { $this->assertEquals($expectedDocument, apply_type_map_to_document($document, $typeMap)); } - public function provideDocumentAndTypeMap() + public static function provideDocumentAndTypeMap() { return [ [ @@ -92,90 +96,77 @@ public function provideDocumentAndTypeMap() ]; } - /** - * @dataProvider provideIndexSpecificationDocumentsAndGeneratedNames - */ - public function testGenerateIndexName($document, $expectedName): void + #[DataProvider('provideDocumentsAndExpectedArrays')] + public function testDocumentToArray($document, array $expectedArray): void { - $this->assertSame($expectedName, generate_index_name($document)); + $this->assertSame($expectedArray, document_to_array($document)); } - public function provideIndexSpecificationDocumentsAndGeneratedNames() + public static function provideDocumentsAndExpectedArrays(): array { return [ - [ ['x' => 1], 'x_1' ], - [ ['x' => -1, 'y' => 1], 'x_-1_y_1' ], - [ ['x' => '2dsphere', 'y' => 1 ], 'x_2dsphere_y_1' ], - [ (object) ['x' => 1], 'x_1' ], - [ new BSONDocument(['x' => 1]), 'x_1' ], + 'array' => [['x' => 1], ['x' => 1]], + 'object' => [(object) ['x' => 1], ['x' => 1]], + 'Serializable' => [new BSONDocument(['x' => 1]), ['x' => 1]], + 'Document' => [Document::fromPHP(['x' => 1]), ['x' => 1]], + // PackedArray and array-returning Serializable are both allowed + 'PackedArray' => [PackedArray::fromPHP(['foo']), [0 => 'foo']], + 'Serializable:array' => [new BSONArray(['foo']), [0 => 'foo']], ]; } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testGenerateIndexNameArgumentTypeCheck($document): void + #[DataProvider('provideInvalidDocumentValuesForChecks')] + public function testDocumentToArrayArgumentTypeCheck($document): void { - $this->expectException(InvalidArgumentException::class); - generate_index_name($document); + $this->expectException(TypeError::class); + document_to_array($document); } - /** - * @dataProvider provideIsFirstKeyOperatorDocuments - */ - public function testIsFirstKeyOperator($document, $isFirstKeyOperator): void + public static function provideInvalidDocumentValuesForChecks(): array { - $this->assertSame($isFirstKeyOperator, is_first_key_operator($document)); + // PackedArray is intentionally left out, as document_to_array is used to convert aggregation pipelines + return self::wrapValuesForDataProvider([123, 3.14, 'foo', true]); } - public function provideIsFirstKeyOperatorDocuments() + public static function provideDocumentCasts(): array { + // phpcs:disable SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing + // phpcs:disable Squiz.Functions.MultiLineFunctionDeclaration + // phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore return [ - [ ['y' => 1], false ], - [ (object) ['y' => 1], false ], - [ new BSONDocument(['y' => 1]), false ], - [ ['$set' => ['y' => 1]], true ], - [ (object) ['$set' => ['y' => 1]], true ], - [ new BSONDocument(['$set' => ['y' => 1]]), true ], + 'array' => [fn ($value) => $value], + 'object' => [fn ($value) => (object) $value], + 'Serializable' => [fn ($value) => new BSONDocument($value)], + 'Document' => [fn ($value) => Document::fromPHP($value)], ]; + // phpcs:enable } - /** - * @dataProvider provideInvalidDocumentValues - */ - public function testIsFirstKeyOperatorArgumentTypeCheck($document): void + #[DataProvider('provideDocumentCasts')] + public function testIsFirstKeyOperator(callable $cast): void { - $this->expectException(InvalidArgumentException::class); - is_first_key_operator($document); - } + $this->assertFalse(is_first_key_operator($cast(['y' => 1]))); + $this->assertTrue(is_first_key_operator($cast(['$set' => ['y' => 1]]))); - /** - * @dataProvider provideMapReduceOutValues - */ - public function testIsMapReduceOutputInline($out, $isInline): void - { - $this->assertSame($isInline, is_mapreduce_output_inline($out)); + // Empty and packed arrays are unlikely arguments, but still valid + $this->assertFalse(is_first_key_operator($cast([]))); + $this->assertFalse(is_first_key_operator($cast(['foo']))); } - public function provideMapReduceOutValues() + #[DataProvider('provideInvalidDocumentValuesForChecks')] + public function testIsFirstKeyOperatorArgumentTypeCheck($document): void { - return [ - [ 'collectionName', false ], - [ ['inline' => 1], true ], - [ ['inline' => 0], true ], // only the key is significant - [ ['replace' => 'collectionName'], false ], - ]; + $this->expectException(TypeError::class); + is_first_key_operator($document); } - /** - * @dataProvider provideTypeMapValues - */ + #[DataProvider('provideTypeMapValues')] public function testCreateFieldPathTypeMap(array $expected, array $typeMap, $fieldPath = 'field'): void { $this->assertEquals($expected, create_field_path_type_map($typeMap, $fieldPath)); } - public function provideTypeMapValues() + public static function provideTypeMapValues() { return [ 'No root type' => [ @@ -197,7 +188,7 @@ public function provideTypeMapValues() 'Array field path converted to array' => [ [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => [ 'field' => 'array', 'field.$' => 'object', @@ -206,7 +197,7 @@ public function provideTypeMapValues() ], [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => ['nested' => 'array'], ], 'field.$', @@ -214,14 +205,14 @@ public function provideTypeMapValues() 'Array field path without root key' => [ [ 'root' => 'object', - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => [ 'field' => 'array', 'field.$.nested' => 'array', ], ], [ - 'array' => 'MongoDB\Model\BSONArray', + 'array' => BSONArray::class, 'fieldPaths' => ['nested' => 'array'], ], 'field.$', @@ -229,44 +220,108 @@ public function provideTypeMapValues() ]; } - /** - * @dataProvider providePipelines - */ - public function testIsPipeline($expected, $pipeline): void + #[DataProvider('provideDocumentCasts')] + public function testIsLastPipelineOperatorWrite(callable $cast): void + { + $match = ['$match' => ['x' => 1]]; + $merge = ['$merge' => ['into' => 'coll']]; + $out = ['$out' => ['db' => 'db', 'coll' => 'coll']]; + + $this->assertTrue(is_last_pipeline_operator_write([$cast($merge)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($out)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($match), $cast($merge)])); + $this->assertTrue(is_last_pipeline_operator_write([$cast($match), $cast($out)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($match)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($merge), $cast($match)])); + $this->assertFalse(is_last_pipeline_operator_write([$cast($out), $cast($match)])); + } + + #[DataProvider('providePipelines')] + public function testIsPipeline($expected, $pipeline, $allowEmpty = false): void { - $this->assertSame($expected, is_pipeline($pipeline)); + $this->assertSame($expected, is_pipeline($pipeline, $allowEmpty)); } - public function providePipelines() + public static function providePipelines(): array { + $valid = [ + ['$match' => ['foo' => 'bar']], + (object) ['$group' => ['_id' => 1]], + new BSONDocument(['$skip' => 1]), + Document::fromPHP(['$limit' => 1]), + ]; + + $invalidIndex = [1 => ['$group' => ['_id' => 1]]]; + + $invalidStageKey = [['group' => ['_id' => 1]]]; + + $dbrefInNumericField = ['0' => ['$ref' => 'foo', '$id' => 'bar']]; + return [ - 'Not an array' => [false, (object) []], - 'Empty array' => [false, []], - 'Non-sequential indexes in array' => [false, [1 => ['$group' => []]]], - 'Update document instead of pipeline' => [false, ['$set' => ['foo' => 'bar']]], - 'Invalid pipeline stage' => [false, [['group' => []]]], - 'Update with DbRef' => [false, ['x' => ['$ref' => 'foo', '$id' => 'bar']]], - 'Valid pipeline' => [ - true, - [ - ['$match' => ['foo' => 'bar']], - ['$group' => ['_id' => 1]], - ], - ], - 'False positive with DbRef in numeric field' => [true, ['0' => ['$ref' => 'foo', '$id' => 'bar']]], - 'DbRef in numeric field as object' => [false, (object) ['0' => ['$ref' => 'foo', '$id' => 'bar']]], + // Valid pipeline in various forms + 'valid: array' => [true, $valid], + 'valid: Serializable' => [true, new BSONArray($valid)], + 'valid: PackedArray' => [true, PackedArray::fromPHP($valid)], + // Invalid type for an otherwise valid pipeline + 'invalid type: stdClass' => [false, (object) $valid], + 'invalid type: Serializable' => [false, new BSONDocument($valid)], + 'invalid type: Document' => [false, Document::fromPHP($valid)], + // Invalid index in pipeline array + 'invalid index: array' => [false, $invalidIndex], + // Note: PackedArray::fromPHP() requires a list array + // Note: BSONArray::bsonSerialize() re-indexes the array + 'invalid index: array' => [true, new BSONArray($invalidIndex)], + // Invalid stage key in pipeline element + 'invalid stage key: array' => [false, $invalidStageKey], + 'invalid stage key: Serializable' => [false, new BSONArray($invalidStageKey)], + 'invalid stage key: PackedArray' => [false, PackedArray::fromPHP($invalidStageKey)], + // Invalid pipeline element type + 'invalid pipeline element type: array' => [false, [[[]]]], + 'invalid pipeline element type: Serializable' => [false, new BSONArray([new BSONArray([])])], + 'invalid pipeline element type: PackedArray' => [false, PackedArray::fromPHP([[]])], + // Empty array has no pipeline stages + 'valid empty: array' => [true, [], true], + 'valid empty: Serializable' => [true, new BSONArray([]), true], + 'valid empty: PackedArray' => [true, PackedArray::fromPHP([]), true], + 'invalid empty: array' => [false, []], + 'invalid empty: Serializable' => [false, new BSONArray([])], + 'invalid empty: PackedArray' => [false, PackedArray::fromPHP([])], + // False positive: DBRef in numeric field + 'false positive DBRef: array' => [true, $dbrefInNumericField], + 'false positive DBRef: Serializable' => [true, new BSONArray($dbrefInNumericField)], + 'false positive DBRef: PackedArray' => [true, PackedArray::fromPHP($dbrefInNumericField)], + // Invalid document containing DBRef in numeric field + 'invalid DBRef: stdClass' => [false, (object) $dbrefInNumericField], + 'invalid DBRef: Serializable' => [false, new BSONDocument($dbrefInNumericField)], + 'invalid DBRef: Document' => [false, Document::fromPHP($dbrefInNumericField)], + // Additional invalid cases + 'Update document' => [false, ['$set' => ['foo' => 'bar']]], + 'Replacement document with DBRef' => [false, ['x' => ['$ref' => 'foo', '$id' => 'bar']]], ]; } - /** - * @dataProvider provideWriteConcerns - */ + #[DataProvider('provideStagePipelines')] + public function testIsBuilderPipeline($expected, $pipeline): void + { + $this->assertSame($expected, is_builder_pipeline($pipeline)); + } + + public static function provideStagePipelines(): iterable + { + yield 'empty array' => [false, []]; + yield 'array of arrays' => [false, [['$match' => ['x' => 1]]]]; + yield 'map of stages' => [false, [1 => new MatchStage([])]]; + yield 'stages' => [true, [new MatchStage([]), new LimitStage(1)]]; + yield 'stages and operators' => [true, [new MatchStage([]), ['$limit' => 1]]]; + } + + #[DataProvider('provideWriteConcerns')] public function testIsWriteConcernAcknowledged($expected, WriteConcern $writeConcern): void { $this->assertSame($expected, is_write_concern_acknowledged($writeConcern)); } - public function provideWriteConcerns(): array + public static function provideWriteConcerns(): array { // Note: WriteConcern constructor prohibits w=-1 or w=0 and journal=true return [ diff --git a/tests/GridFS/BucketFunctionalTest.php b/tests/GridFS/BucketFunctionalTest.php index c59f4c128..8c347c5cd 100644 --- a/tests/GridFS/BucketFunctionalTest.php +++ b/tests/GridFS/BucketFunctionalTest.php @@ -3,22 +3,32 @@ namespace MongoDB\Tests\GridFS; use MongoDB\BSON\Binary; +use MongoDB\BSON\ObjectId; use MongoDB\Collection; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\GridFS\Bucket; +use MongoDB\GridFS\CollectionWrapper; use MongoDB\GridFS\Exception\CorruptFileException; use MongoDB\GridFS\Exception\FileNotFoundException; use MongoDB\GridFS\Exception\StreamException; use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInfo; use MongoDB\Operation\ListIndexes; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Codec\TestFileCodec; +use MongoDB\Tests\Fixtures\Document\TestFile; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use ReflectionMethod; +use stdClass; use function array_merge; use function call_user_func; use function current; +use function escapeshellarg; use function exec; use function fclose; use function fopen; @@ -35,71 +45,44 @@ use function strncasecmp; use function substr; -use const PHP_EOL; +use const PHP_BINARY; use const PHP_OS; -use const PHP_VERSION_ID; /** * Functional tests for the Bucket class. */ class BucketFunctionalTest extends FunctionalTestCase { - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testValidConstructorOptions(): void { new Bucket($this->manager, $this->getDatabaseName(), [ 'bucketName' => 'test', 'chunkSizeBytes' => 8192, 'readConcern' => new ReadConcern(ReadConcern::LOCAL), - 'readPreference' => new ReadPreference(ReadPreference::RP_PRIMARY), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY, 1000), ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Bucket($this->manager, $this->getDatabaseName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidStringValues(true) as $value) { - $options[][] = ['bucketName' => $value]; - } - - foreach ($this->getInvalidIntegerValues(true) as $value) { - $options[][] = ['chunkSizeBytes' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['disableMD5' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'bucketName' => self::getInvalidStringValues(true), + 'chunkSizeBytes' => self::getInvalidIntegerValues(true), + 'codec' => self::getInvalidDocumentCodecValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive(): void @@ -109,12 +92,21 @@ public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive(): new Bucket($this->manager, $this->getDatabaseName(), ['chunkSizeBytes' => 0]); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + public function testConstructorWithCodecAndTypeMapOptions(): void + { + $options = [ + 'codec' => new TestDocumentCodec(), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + ]; + + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); + new Bucket($this->manager, $this->getDatabaseName(), $options); + } + + #[DataProvider('provideInputDataAndExpectedChunks')] public function testDelete($input, $expectedChunks): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); + $id = $this->bucket->uploadFromStream('filename', self::createStream($input)); $this->assertCollectionCount($this->filesCollection, 1); $this->assertCollectionCount($this->chunksCollection, $expectedChunks); @@ -125,7 +117,7 @@ public function testDelete($input, $expectedChunks): void $this->assertCollectionCount($this->chunksCollection, 0); } - public function provideInputDataAndExpectedChunks() + public static function provideInputDataAndExpectedChunks() { return [ ['', 0], @@ -147,12 +139,10 @@ public function testDeleteShouldRequireFileToExist(): void $this->bucket->delete('nonexistent-id'); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + #[DataProvider('provideInputDataAndExpectedChunks')] public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expectedChunks): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); + $id = $this->bucket->uploadFromStream('filename', self::createStream($input)); $this->assertCollectionCount($this->filesCollection, 1); $this->assertCollectionCount($this->chunksCollection, $expectedChunks); @@ -162,15 +152,43 @@ public function testDeleteStillRemovesChunksIfFileDoesNotExist($input, $expected try { $this->bucket->delete($id); $this->fail('FileNotFoundException was not thrown'); - } catch (FileNotFoundException $e) { + } catch (FileNotFoundException) { } $this->assertCollectionCount($this->chunksCollection, 0); } + public function testDeleteByName(): void + { + $this->bucket->uploadFromStream('filename', self::createStream('foobar1')); + $this->bucket->uploadFromStream('filename', self::createStream('foobar2')); + $this->bucket->uploadFromStream('filename', self::createStream('foobar3')); + + $this->bucket->uploadFromStream('other', self::createStream('foobar')); + + $this->assertCollectionCount($this->filesCollection, 4); + $this->assertCollectionCount($this->chunksCollection, 4); + + $this->bucket->deleteByName('filename'); + + $this->assertCollectionCount($this->filesCollection, 1); + $this->assertCollectionCount($this->chunksCollection, 1); + + $this->bucket->deleteByName('other'); + + $this->assertCollectionCount($this->filesCollection, 0); + $this->assertCollectionCount($this->chunksCollection, 0); + } + + public function testDeleteByNameShouldRequireFileToExist(): void + { + $this->expectException(FileNotFoundException::class); + $this->bucket->deleteByName('nonexistent-name'); + } + public function testDownloadingFileWithMissingChunk(): void { - $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar')); $this->chunksCollection->deleteOne(['files_id' => $id, 'n' => 0]); @@ -181,11 +199,11 @@ public function testDownloadingFileWithMissingChunk(): void public function testDownloadingFileWithUnexpectedChunkIndex(): void { - $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar')); $this->chunksCollection->updateOne( ['files_id' => $id, 'n' => 0], - ['$set' => ['n' => 1]] + ['$set' => ['n' => 1]], ); $this->expectException(CorruptFileException::class); @@ -195,11 +213,11 @@ public function testDownloadingFileWithUnexpectedChunkIndex(): void public function testDownloadingFileWithUnexpectedChunkSize(): void { - $id = $this->bucket->uploadFromStream("filename", $this->createStream("foobar")); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar')); $this->chunksCollection->updateOne( ['files_id' => $id, 'n' => 0], - ['$set' => ['data' => new Binary('fooba', Binary::TYPE_GENERIC)]] + ['$set' => ['data' => new Binary('fooba')]], ); $this->expectException(CorruptFileException::class); @@ -207,96 +225,88 @@ public function testDownloadingFileWithUnexpectedChunkSize(): void stream_get_contents($this->bucket->openDownloadStream($id)); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + #[DataProvider('provideInputDataAndExpectedChunks')] public function testDownloadToStream($input): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); - $destination = $this->createStream(); + $id = $this->bucket->uploadFromStream('filename', self::createStream($input)); + $destination = self::createStream(); $this->bucket->downloadToStream($id, $destination); $this->assertStreamContents($input, $destination); } - /** - * @dataProvider provideInvalidStreamValues - */ + #[DataProvider('provideInvalidStreamValues')] public function testDownloadToStreamShouldRequireDestinationStream($destination): void { $this->expectException(InvalidArgumentException::class); $this->bucket->downloadToStream('id', $destination); } - public function provideInvalidStreamValues() + public static function provideInvalidStreamValues(): array { - return $this->wrapValuesForDataProvider($this->getInvalidStreamValues()); + return self::wrapValuesForDataProvider(self::getInvalidStreamValues()); } public function testDownloadToStreamShouldRequireFileToExist(): void { $this->expectException(FileNotFoundException::class); - $this->bucket->downloadToStream('nonexistent-id', $this->createStream()); + $this->bucket->downloadToStream('nonexistent-id', self::createStream()); } public function testDownloadToStreamByName(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); - $this->bucket->uploadFromStream('filename', $this->createStream('bar')); - $this->bucket->uploadFromStream('filename', $this->createStream('baz')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('bar')); + $this->bucket->uploadFromStream('filename', self::createStream('baz')); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination); $this->assertStreamContents('baz', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => -3]); $this->assertStreamContents('foo', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => -2]); $this->assertStreamContents('bar', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => -1]); $this->assertStreamContents('baz', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => 0]); $this->assertStreamContents('foo', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => 1]); $this->assertStreamContents('bar', $destination); - $destination = $this->createStream(); + $destination = self::createStream(); $this->bucket->downloadToStreamByName('filename', $destination, ['revision' => 2]); $this->assertStreamContents('baz', $destination); } - /** - * @dataProvider provideInvalidStreamValues - */ + #[DataProvider('provideInvalidStreamValues')] public function testDownloadToStreamByNameShouldRequireDestinationStream($destination): void { $this->expectException(InvalidArgumentException::class); $this->bucket->downloadToStreamByName('filename', $destination); } - /** - * @dataProvider provideNonexistentFilenameAndRevision - */ + #[DataProvider('provideNonexistentFilenameAndRevision')] public function testDownloadToStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); - $this->bucket->uploadFromStream('filename', $this->createStream('bar')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('bar')); - $destination = $this->createStream(); + $destination = self::createStream(); $this->expectException(FileNotFoundException::class); $this->bucket->downloadToStreamByName($filename, $destination, ['revision' => $revision]); } - public function provideNonexistentFilenameAndRevision() + public static function provideNonexistentFilenameAndRevision() { return [ ['filename', 2], @@ -308,7 +318,7 @@ public function provideNonexistentFilenameAndRevision() public function testDrop(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foobar')); + $this->bucket->uploadFromStream('filename', self::createStream('foobar')); $this->assertCollectionCount($this->filesCollection, 1); $this->assertCollectionCount($this->chunksCollection, 1); @@ -321,9 +331,9 @@ public function testDrop(): void public function testFind(): void { - $this->bucket->uploadFromStream('a', $this->createStream('foo')); - $this->bucket->uploadFromStream('b', $this->createStream('foobar')); - $this->bucket->uploadFromStream('c', $this->createStream('foobarbaz')); + $this->bucket->uploadFromStream('a', self::createStream('foo')); + $this->bucket->uploadFromStream('b', self::createStream('foobar')); + $this->bucket->uploadFromStream('c', self::createStream('foobarbaz')); $cursor = $this->bucket->find( ['length' => ['$lte' => 6]], @@ -334,7 +344,7 @@ public function testFind(): void '_id' => 0, ], 'sort' => ['length' => -1], - ] + ], ); $expected = [ @@ -347,7 +357,7 @@ public function testFind(): void public function testFindUsesTypeMap(): void { - $this->bucket->uploadFromStream('a', $this->createStream('foo')); + $this->bucket->uploadFromStream('a', self::createStream('foo')); $cursor = $this->bucket->find(); $fileDocument = current($cursor->toArray()); @@ -355,11 +365,46 @@ public function testFindUsesTypeMap(): void $this->assertInstanceOf(BSONDocument::class, $fileDocument); } + public function testFindUsesCodec(): void + { + $this->bucket->uploadFromStream('a', self::createStream('foo')); + + $cursor = $this->bucket->find([], ['codec' => new TestFileCodec()]); + $fileDocument = current($cursor->toArray()); + + $this->assertInstanceOf(TestFile::class, $fileDocument); + $this->assertSame('a', $fileDocument->filename); + } + + public function testFindInheritsBucketCodec(): void + { + $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['codec' => new TestFileCodec()]); + $bucket->uploadFromStream('a', self::createStream('foo')); + + $cursor = $bucket->find(); + $fileDocument = current($cursor->toArray()); + + $this->assertInstanceOf(TestFile::class, $fileDocument); + $this->assertSame('a', $fileDocument->filename); + } + + public function testFindResetsInheritedBucketCodec(): void + { + $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['codec' => new TestFileCodec()]); + $bucket->uploadFromStream('a', self::createStream('foo')); + + $cursor = $bucket->find([], ['codec' => null]); + $fileDocument = current($cursor->toArray()); + + $this->assertInstanceOf(BSONDocument::class, $fileDocument); + $this->assertSame('a', $fileDocument->filename); + } + public function testFindOne(): void { - $this->bucket->uploadFromStream('a', $this->createStream('foo')); - $this->bucket->uploadFromStream('b', $this->createStream('foobar')); - $this->bucket->uploadFromStream('c', $this->createStream('foobarbaz')); + $this->bucket->uploadFromStream('a', self::createStream('foo')); + $this->bucket->uploadFromStream('b', self::createStream('foobar')); + $this->bucket->uploadFromStream('c', self::createStream('foobarbaz')); $fileDocument = $this->bucket->findOne( ['length' => ['$lte' => 6]], @@ -370,13 +415,71 @@ public function testFindOne(): void '_id' => 0, ], 'sort' => ['length' => -1], - ] + ], ); $this->assertInstanceOf(BSONDocument::class, $fileDocument); $this->assertSameDocument(['filename' => 'b', 'length' => 6], $fileDocument); } + public function testFindOneUsesCodec(): void + { + $this->bucket->uploadFromStream('a', self::createStream('foo')); + $this->bucket->uploadFromStream('b', self::createStream('foobar')); + $this->bucket->uploadFromStream('c', self::createStream('foobarbaz')); + + $fileDocument = $this->bucket->findOne( + ['length' => ['$lte' => 6]], + [ + 'sort' => ['length' => -1], + 'codec' => new TestFileCodec(), + ], + ); + + $this->assertInstanceOf(TestFile::class, $fileDocument); + $this->assertSame('b', $fileDocument->filename); + $this->assertSame(6, $fileDocument->length); + } + + public function testFindOneInheritsBucketCodec(): void + { + $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['codec' => new TestFileCodec()]); + + $bucket->uploadFromStream('a', self::createStream('foo')); + $bucket->uploadFromStream('b', self::createStream('foobar')); + $bucket->uploadFromStream('c', self::createStream('foobarbaz')); + + $fileDocument = $bucket->findOne( + ['length' => ['$lte' => 6]], + ['sort' => ['length' => -1]], + ); + + $this->assertInstanceOf(TestFile::class, $fileDocument); + $this->assertSame('b', $fileDocument->filename); + $this->assertSame(6, $fileDocument->length); + } + + public function testFindOneResetsInheritedBucketCodec(): void + { + $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['codec' => new TestFileCodec()]); + + $bucket->uploadFromStream('a', self::createStream('foo')); + $bucket->uploadFromStream('b', self::createStream('foobar')); + $bucket->uploadFromStream('c', self::createStream('foobarbaz')); + + $fileDocument = $bucket->findOne( + ['length' => ['$lte' => 6]], + [ + 'sort' => ['length' => -1], + 'codec' => null, + ], + ); + + $this->assertInstanceOf(BSONDocument::class, $fileDocument); + $this->assertSame('b', $fileDocument->filename); + $this->assertSame(6, $fileDocument->length); + } + public function testGetBucketNameWithCustomValue(): void { $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['bucketName' => 'custom_fs']); @@ -426,10 +529,26 @@ public function testGetFileDocumentForStreamUsesTypeMap(): void $this->assertSame(['foo' => 'bar'], $fileDocument['metadata']->getArrayCopy()); } + public function testGetFileDocumentForStreamUsesCodec(): void + { + $bucket = new Bucket($this->manager, $this->getDatabaseName(), ['codec' => new TestFileCodec()]); + + $metadata = ['foo' => 'bar']; + $stream = $bucket->openUploadStream('filename', ['_id' => 1, 'metadata' => $metadata]); + + $fileDocument = $bucket->getFileDocumentForStream($stream); + + $this->assertInstanceOf(TestFile::class, $fileDocument); + + $this->assertSame('filename', $fileDocument->filename); + $this->assertInstanceOf(stdClass::class, $fileDocument->metadata); + $this->assertSame($metadata, (array) $fileDocument->metadata); + } + public function testGetFileDocumentForStreamWithReadableStream(): void { $metadata = ['foo' => 'bar']; - $id = $this->bucket->uploadFromStream('filename', $this->createStream('foobar'), ['metadata' => $metadata]); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar'), ['metadata' => $metadata]); $stream = $this->bucket->openDownloadStream($id); $fileDocument = $this->bucket->getFileDocumentForStream($stream); @@ -452,18 +571,16 @@ public function testGetFileDocumentForStreamWithWritableStream(): void $this->assertSameDocument($metadata, $fileDocument->metadata); } - /** - * @dataProvider provideInvalidGridFSStreamValues - */ + #[DataProvider('provideInvalidGridFSStreamValues')] public function testGetFileDocumentForStreamShouldRequireGridFSStreamResource($stream): void { $this->expectException(InvalidArgumentException::class); $this->bucket->getFileDocumentForStream($stream); } - public function provideInvalidGridFSStreamValues() + public static function provideInvalidGridFSStreamValues(): array { - return $this->wrapValuesForDataProvider(array_merge($this->getInvalidStreamValues(), [$this->createStream()])); + return self::wrapValuesForDataProvider(array_merge(self::getInvalidStreamValues(), [self::createStream()])); } public function testGetFileIdForStreamUsesTypeMap(): void @@ -478,7 +595,7 @@ public function testGetFileIdForStreamUsesTypeMap(): void public function testGetFileIdForStreamWithReadableStream(): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream('foobar')); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar')); $stream = $this->bucket->openDownloadStream($id); $this->assertSameObjectId($id, $this->bucket->getFileIdForStream($stream)); @@ -491,9 +608,7 @@ public function testGetFileIdForStreamWithWritableStream(): void $this->assertEquals(1, $this->bucket->getFileIdForStream($stream)); } - /** - * @dataProvider provideInvalidGridFSStreamValues - */ + #[DataProvider('provideInvalidGridFSStreamValues')] public function testGetFileIdForStreamShouldRequireGridFSStreamResource($stream): void { $this->expectException(InvalidArgumentException::class); @@ -508,22 +623,18 @@ public function testGetFilesCollection(): void $this->assertEquals('fs.files', $filesCollection->getCollectionName()); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + #[DataProvider('provideInputDataAndExpectedChunks')] public function testOpenDownloadStream($input): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); + $id = $this->bucket->uploadFromStream('filename', self::createStream($input)); $this->assertStreamContents($input, $this->bucket->openDownloadStream($id)); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + #[DataProvider('provideInputDataAndExpectedChunks')] public function testOpenDownloadStreamAndMultipleReadOperations($input): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream($input)); + $id = $this->bucket->uploadFromStream('filename', self::createStream($input)); $stream = $this->bucket->openDownloadStream($id); $buffer = ''; @@ -553,9 +664,9 @@ public function testOpenDownloadStreamByNameShouldRequireFilenameToExist(): void public function testOpenDownloadStreamByName(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); - $this->bucket->uploadFromStream('filename', $this->createStream('bar')); - $this->bucket->uploadFromStream('filename', $this->createStream('baz')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('bar')); + $this->bucket->uploadFromStream('filename', self::createStream('baz')); $this->assertStreamContents('baz', $this->bucket->openDownloadStreamByName('filename')); $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('filename', ['revision' => -3])); @@ -566,16 +677,14 @@ public function testOpenDownloadStreamByName(): void $this->assertStreamContents('baz', $this->bucket->openDownloadStreamByName('filename', ['revision' => 2])); } - /** - * @dataProvider provideNonexistentFilenameAndRevision - */ + #[DataProvider('provideNonexistentFilenameAndRevision')] public function testOpenDownloadStreamByNameShouldRequireFilenameAndRevisionToExist($filename, $revision): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); - $this->bucket->uploadFromStream('filename', $this->createStream('bar')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('bar')); $this->expectException(FileNotFoundException::class); - $this->bucket->openDownloadStream($filename, ['revision' => $revision]); + $this->bucket->openDownloadStreamByName($filename, ['revision' => $revision]); } public function testOpenUploadStream(): void @@ -588,9 +697,7 @@ public function testOpenUploadStream(): void $this->assertStreamContents('foobar', $this->bucket->openDownloadStreamByName('filename')); } - /** - * @dataProvider provideInputDataAndExpectedChunks - */ + #[DataProvider('provideInputDataAndExpectedChunks')] public function testOpenUploadStreamAndMultipleWriteOperations($input): void { $stream = $this->bucket->openUploadStream('filename'); @@ -610,12 +717,12 @@ public function testOpenUploadStreamAndMultipleWriteOperations($input): void public function testRename(): void { - $id = $this->bucket->uploadFromStream('a', $this->createStream('foo')); + $id = $this->bucket->uploadFromStream('a', self::createStream('foo')); $this->bucket->rename($id, 'b'); $fileDocument = $this->filesCollection->findOne( ['_id' => $id], - ['projection' => ['filename' => 1, '_id' => 0]] + ['projection' => ['filename' => 1, '_id' => 0]], ); $this->assertSameDocument(['filename' => 'b'], $fileDocument); @@ -624,12 +731,12 @@ public function testRename(): void public function testRenameShouldNotRequireFileToBeModified(): void { - $id = $this->bucket->uploadFromStream('a', $this->createStream('foo')); + $id = $this->bucket->uploadFromStream('a', self::createStream('foo')); $this->bucket->rename($id, 'a'); $fileDocument = $this->filesCollection->findOne( ['_id' => $id], - ['projection' => ['filename' => 1, '_id' => 0]] + ['projection' => ['filename' => 1, '_id' => 0]], ); $this->assertSameDocument(['filename' => 'a'], $fileDocument); @@ -642,6 +749,24 @@ public function testRenameShouldRequireFileToExist(): void $this->bucket->rename('nonexistent-id', 'b'); } + public function testRenameByName(): void + { + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); + + $this->bucket->renameByName('filename', 'newname'); + + $this->assertNull($this->bucket->findOne(['filename' => 'filename']), 'No file has the old name'); + $this->assertStreamContents('foo', $this->bucket->openDownloadStreamByName('newname')); + } + + public function testRenameByNameShouldRequireFileToExist(): void + { + $this->expectException(FileNotFoundException::class); + $this->bucket->renameByName('nonexistent-name', 'b'); + } + public function testUploadFromStream(): void { $options = [ @@ -650,7 +775,7 @@ public function testUploadFromStream(): void 'metadata' => ['foo' => 'bar'], ]; - $id = $this->bucket->uploadFromStream('filename', $this->createStream('foobar'), $options); + $id = $this->bucket->uploadFromStream('filename', self::createStream('foobar'), $options); $this->assertCollectionCount($this->filesCollection, 1); $this->assertCollectionCount($this->chunksCollection, 3); @@ -661,9 +786,7 @@ public function testUploadFromStream(): void $this->assertSameDocument(['foo' => 'bar'], $fileDocument['metadata']); } - /** - * @dataProvider provideInvalidStreamValues - */ + #[DataProvider('provideInvalidStreamValues')] public function testUploadFromStreamShouldRequireSourceStream($source): void { $this->expectException(InvalidArgumentException::class); @@ -672,8 +795,8 @@ public function testUploadFromStreamShouldRequireSourceStream($source): void public function testUploadingAnEmptyFile(): void { - $id = $this->bucket->uploadFromStream('filename', $this->createStream('')); - $destination = $this->createStream(); + $id = $this->bucket->uploadFromStream('filename', self::createStream('')); + $destination = self::createStream(); $this->bucket->downloadToStream($id, $destination); $this->assertStreamContents('', $destination); @@ -685,23 +808,19 @@ public function testUploadingAnEmptyFile(): void [ 'projection' => [ 'length' => 1, - 'md5' => 1, '_id' => 0, ], - ] + ], ); - $expected = [ - 'length' => 0, - 'md5' => 'd41d8cd98f00b204e9800998ecf8427e', - ]; + $expected = ['length' => 0]; $this->assertSameDocument($expected, $fileDocument); } public function testUploadingFirstFileCreatesIndexes(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); $this->assertIndexExists($this->filesCollection->getCollectionName(), 'filename_1_uploadDate_1'); $this->assertIndexExists($this->chunksCollection->getCollectionName(), 'files_id_1_n_1', function (IndexInfo $info): void { @@ -711,10 +830,23 @@ public function testUploadingFirstFileCreatesIndexes(): void public function testExistingIndexIsReused(): void { + // The collections may exist from other tests, ensure they are removed + // before and after to avoid potential conflicts. + $this->dropCollection($this->getDatabaseName(), 'fs.chunks'); + $this->dropCollection($this->getDatabaseName(), 'fs.files'); + + // Create indexes with different numeric types before interacting with + // GridFS to assert that the library respects the existing indexes and + // does not attempt to create its own. $this->filesCollection->createIndex(['filename' => 1.0, 'uploadDate' => 1], ['name' => 'test']); $this->chunksCollection->createIndex(['files_id' => 1.0, 'n' => 1], ['name' => 'test', 'unique' => true]); - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); + $this->assertIndexExists('fs.files', 'test'); + $this->assertIndexExists('fs.chunks', 'test', function (IndexInfo $info): void { + $this->assertTrue($info->isUnique()); + }); + + $this->bucket->uploadFromStream('filename', self::createStream('foo')); $this->assertIndexNotExists($this->filesCollection->getCollectionName(), 'filename_1_uploadDate_1'); $this->assertIndexNotExists($this->chunksCollection->getCollectionName(), 'files_id_1_n_1'); @@ -722,7 +854,7 @@ public function testExistingIndexIsReused(): void public function testDownloadToStreamFails(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo'), ['_id' => ['foo' => 'bar']]); + $this->bucket->uploadFromStream('filename', self::createStream('foo'), ['_id' => ['foo' => 'bar']]); $this->expectException(StreamException::class); $this->expectExceptionMessageMatches('#^Downloading file from "gridfs://.*/.*/.*" to "php://temp" failed. GridFS identifier: "{ "_id" : { "foo" : "bar" } }"$#'); @@ -731,7 +863,7 @@ public function testDownloadToStreamFails(): void public function testDownloadToStreamByNameFails(): void { - $this->bucket->uploadFromStream('filename', $this->createStream('foo')); + $this->bucket->uploadFromStream('filename', self::createStream('foo')); $this->expectException(StreamException::class); $this->expectExceptionMessageMatches('#^Downloading file from "gridfs://.*/.*/.*" to "php://temp" failed. GridFS filename: "filename"$#'); @@ -740,10 +872,6 @@ public function testDownloadToStreamByNameFails(): void public function testUploadFromStreamFails(): void { - if (PHP_VERSION_ID < 70400) { - $this->markTestSkipped('Test only works on PHP 7.4 and newer'); - } - UnusableStream::register(); $source = fopen('unusable://temp', 'w'); @@ -758,21 +886,116 @@ public function testDanglingOpenWritableStream(): void $this->markTestSkipped('Test does not apply to Windows'); } - $path = __DIR__ . '/../../vendor/autoload.php'; - $command = <<test->selectGridFSBucket()->openUploadStream('filename', ['disableMD5' => true]);" 2>&1 -CMD; + $code = <<<'PHP' + require '%s'; + // Don't report deprecations - if the issue exists this code will + // result in a fatal error + error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); + $client = MongoDB\Tests\FunctionalTestCase::createTestClient(); + $database = $client->selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); + $gridfs = $database->selectGridFSBucket(); + $stream = $gridfs->openUploadStream('hello.txt'); + fwrite($stream, 'Hello MongoDB!'); + PHP; @exec( - $command, + implode(' ', [ + PHP_BINARY, + '-r', + escapeshellarg( + sprintf( + $code, + __DIR__ . '/../../vendor/autoload.php', + ), + ), + '2>&1', + ]), $output, - $return + $return, ); + $this->assertSame([], $output); $this->assertSame(0, $return); - $output = implode(PHP_EOL, $output); - $this->assertSame('', $output); + $fileDocument = $this->filesCollection->findOne(['filename' => 'hello.txt']); + + $this->assertNotNull($fileDocument); + $this->assertSame(14, $fileDocument->length); + } + + public function testDanglingOpenWritableStreamWithGlobalStreamWrapperAlias(): void + { + if (! strncasecmp(PHP_OS, 'WIN', 3)) { + $this->markTestSkipped('Test does not apply to Windows'); + } + + $code = <<<'PHP' + require '%s'; + // Don't report deprecations - if the issue exists this code will + // result in a fatal error + error_reporting(E_ALL & ~E_DEPRECATED & ~E_USER_DEPRECATED); + $client = MongoDB\Tests\FunctionalTestCase::createTestClient(); + $database = $client->selectDatabase(getenv('MONGODB_DATABASE') ?: 'phplib_test'); + $database->selectGridFSBucket()->registerGlobalStreamWrapperAlias('alias'); + $stream = fopen('gridfs://alias/hello.txt', 'w'); + fwrite($stream, 'Hello MongoDB!'); + PHP; + + @exec( + implode(' ', [ + PHP_BINARY, + '-r', + escapeshellarg( + sprintf( + $code, + __DIR__ . '/../../vendor/autoload.php', + ), + ), + '2>&1', + ]), + $output, + $return, + ); + + $this->assertSame([], $output); + $this->assertSame(0, $return); + + $fileDocument = $this->filesCollection->findOne(['filename' => 'hello.txt']); + + $this->assertNotNull($fileDocument); + $this->assertSame(14, $fileDocument->length); + } + + public function testResolveStreamContextForRead(): void + { + $stream = $this->bucket->openUploadStream('filename'); + fwrite($stream, 'foobar'); + fclose($stream); + + $method = new ReflectionMethod($this->bucket, 'resolveStreamContext'); + $context = $method->invokeArgs($this->bucket, ['gridfs://bucket/filename', 'rb', []]); + + $this->assertIsArray($context); + $this->assertArrayHasKey('collectionWrapper', $context); + $this->assertInstanceOf(CollectionWrapper::class, $context['collectionWrapper']); + $this->assertArrayHasKey('file', $context); + $this->assertIsObject($context['file']); + $this->assertInstanceOf(ObjectId::class, $context['file']->_id); + $this->assertSame('filename', $context['file']->filename); + } + + public function testResolveStreamContextForWrite(): void + { + $method = new ReflectionMethod($this->bucket, 'resolveStreamContext'); + $context = $method->invokeArgs($this->bucket, ['gridfs://bucket/filename', 'wb', []]); + + $this->assertIsArray($context); + $this->assertArrayHasKey('collectionWrapper', $context); + $this->assertInstanceOf(CollectionWrapper::class, $context['collectionWrapper']); + $this->assertArrayHasKey('filename', $context); + $this->assertSame('filename', $context['filename']); + $this->assertArrayHasKey('options', $context); + $this->assertSame(['chunkSizeBytes' => 261120], $context['options']); } /** @@ -831,7 +1054,7 @@ private function assertIndexNotExists(string $collectionName, string $indexName) /** * Return a list of invalid stream values. */ - private function getInvalidStreamValues(): array + private static function getInvalidStreamValues(): array { return [null, 123, 'foo', [], hash_init('md5')]; } diff --git a/tests/GridFS/FunctionalTestCase.php b/tests/GridFS/FunctionalTestCase.php index d78b1e6b1..ad2ca614a 100644 --- a/tests/GridFS/FunctionalTestCase.php +++ b/tests/GridFS/FunctionalTestCase.php @@ -4,7 +4,10 @@ use MongoDB\Collection; use MongoDB\GridFS\Bucket; +use MongoDB\Operation\DropCollection; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; +use PHPUnit\Framework\Attributes\AfterClass; +use PHPUnit\Framework\Attributes\BeforeClass; use function fopen; use function fwrite; @@ -17,26 +20,45 @@ */ abstract class FunctionalTestCase extends BaseFunctionalTestCase { - /** @var Bucket */ - protected $bucket; + protected Bucket $bucket; - /** @var Collection */ - protected $chunksCollection; + protected Collection $chunksCollection; - /** @var Collection */ - protected $filesCollection; + protected Collection $filesCollection; public function setUp(): void { parent::setUp(); $this->bucket = new Bucket($this->manager, $this->getDatabaseName()); - $this->bucket->drop(); $this->chunksCollection = new Collection($this->manager, $this->getDatabaseName(), 'fs.chunks'); $this->filesCollection = new Collection($this->manager, $this->getDatabaseName(), 'fs.files'); } + public function tearDown(): void + { + $this->chunksCollection->deleteMany([]); + $this->filesCollection->deleteMany([]); + + parent::tearDown(); + } + + /** + * The bucket's collections are created by the first test that runs and + * kept for all subsequent tests. This is done to avoid creating the + * collections and their indexes for each test, which would be slow. + */ + #[BeforeClass] + #[AfterClass] + public static function dropCollectionsBeforeAfterClass(): void + { + $manager = static::createTestManager(); + + (new DropCollection(self::getDatabaseName(), 'fs.chunks'))->execute($manager->selectServer()); + (new DropCollection(self::getDatabaseName(), 'fs.files'))->execute($manager->selectServer()); + } + /** * Asserts that a variable is a stream containing the expected data. * @@ -56,7 +78,7 @@ protected function assertStreamContents(string $expectedContents, $stream): void * * @return resource */ - protected function createStream(string $data = '') + protected static function createStream(string $data = '') { $stream = fopen('php://temp', 'w+b'); fwrite($stream, $data); diff --git a/tests/GridFS/ReadableStreamFunctionalTest.php b/tests/GridFS/ReadableStreamFunctionalTest.php index 42c9ddae1..3a14af698 100644 --- a/tests/GridFS/ReadableStreamFunctionalTest.php +++ b/tests/GridFS/ReadableStreamFunctionalTest.php @@ -8,6 +8,7 @@ use MongoDB\GridFS\Exception\CorruptFileException; use MongoDB\GridFS\ReadableStream; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; use function array_filter; @@ -16,8 +17,7 @@ */ class ReadableStreamFunctionalTest extends FunctionalTestCase { - /** @var CollectionWrapper */ - private $collectionWrapper; + private CollectionWrapper $collectionWrapper; public function setUp(): void { @@ -34,13 +34,13 @@ public function setUp(): void ]); $this->chunksCollection->insertMany([ - ['_id' => 1, 'files_id' => 'length-0-with-empty-chunk', 'n' => 0, 'data' => new Binary('', Binary::TYPE_GENERIC)], - ['_id' => 2, 'files_id' => 'length-2', 'n' => 0, 'data' => new Binary('ab', Binary::TYPE_GENERIC)], - ['_id' => 3, 'files_id' => 'length-8', 'n' => 0, 'data' => new Binary('abcd', Binary::TYPE_GENERIC)], - ['_id' => 4, 'files_id' => 'length-8', 'n' => 1, 'data' => new Binary('efgh', Binary::TYPE_GENERIC)], - ['_id' => 5, 'files_id' => 'length-10', 'n' => 0, 'data' => new Binary('abcd', Binary::TYPE_GENERIC)], - ['_id' => 6, 'files_id' => 'length-10', 'n' => 1, 'data' => new Binary('efgh', Binary::TYPE_GENERIC)], - ['_id' => 7, 'files_id' => 'length-10', 'n' => 2, 'data' => new Binary('ij', Binary::TYPE_GENERIC)], + ['_id' => 1, 'files_id' => 'length-0-with-empty-chunk', 'n' => 0, 'data' => new Binary('')], + ['_id' => 2, 'files_id' => 'length-2', 'n' => 0, 'data' => new Binary('ab')], + ['_id' => 3, 'files_id' => 'length-8', 'n' => 0, 'data' => new Binary('abcd')], + ['_id' => 4, 'files_id' => 'length-8', 'n' => 1, 'data' => new Binary('efgh')], + ['_id' => 5, 'files_id' => 'length-10', 'n' => 0, 'data' => new Binary('abcd')], + ['_id' => 6, 'files_id' => 'length-10', 'n' => 1, 'data' => new Binary('efgh')], + ['_id' => 7, 'files_id' => 'length-10', 'n' => 2, 'data' => new Binary('ij')], ]); } @@ -51,24 +51,22 @@ public function testGetFile(): void $this->assertSame($fileDocument, $stream->getFile()); } - /** - * @dataProvider provideInvalidConstructorFileDocuments - */ + #[DataProvider('provideInvalidConstructorFileDocuments')] public function testConstructorFileDocumentChecks($file): void { $this->expectException(CorruptFileException::class); new ReadableStream($this->collectionWrapper, $file); } - public function provideInvalidConstructorFileDocuments() + public static function provideInvalidConstructorFileDocuments() { $options = []; - foreach ($this->getInvalidIntegerValues() as $value) { + foreach (self::getInvalidIntegerValues() as $value) { $options[][] = (object) ['_id' => 1, 'chunkSize' => $value, 'length' => 0]; } - foreach ($this->getInvalidIntegerValues() as $value) { + foreach (self::getInvalidIntegerValues() as $value) { $options[][] = (object) ['_id' => 1, 'chunkSize' => 1, 'length' => $value]; } @@ -79,9 +77,7 @@ public function provideInvalidConstructorFileDocuments() return $options; } - /** - * @dataProvider provideFileIdAndExpectedBytes - */ + #[DataProvider('provideFileIdAndExpectedBytes')] public function testReadBytes($fileId, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById($fileId); @@ -90,7 +86,7 @@ public function testReadBytes($fileId, $length, $expectedBytes): void $this->assertSame($expectedBytes, $stream->readBytes($length)); } - public function provideFileIdAndExpectedBytes() + public static function provideFileIdAndExpectedBytes() { return [ ['length-0', 0, ''], @@ -116,19 +112,15 @@ public function provideFileIdAndExpectedBytes() ]; } - public function provideFilteredFileIdAndExpectedBytes() + public static function provideFilteredFileIdAndExpectedBytes() { return array_filter( - $this->provideFileIdAndExpectedBytes(), - function (array $args) { - return $args[1] > 0; - } + self::provideFileIdAndExpectedBytes(), + fn (array $args) => $args[1] > 0, ); } - /** - * @dataProvider provideFilteredFileIdAndExpectedBytes - */ + #[DataProvider('provideFilteredFileIdAndExpectedBytes')] public function testReadBytesCalledMultipleTimes($fileId, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById($fileId); @@ -167,7 +159,7 @@ public function testReadBytesWithUnexpectedChunkSize(): void { $this->chunksCollection->updateOne( ['files_id' => 'length-10', 'n' => 2], - ['$set' => ['data' => new Binary('i', Binary::TYPE_GENERIC)]] + ['$set' => ['data' => new Binary('i')]], ); $fileDocument = $this->collectionWrapper->findFileById('length-10'); @@ -206,9 +198,7 @@ public function testSeekOutOfRange(): void $stream->seek(11); } - /** - * @dataProvider providePreviousChunkSeekOffsetAndBytes - */ + #[DataProvider('providePreviousChunkSeekOffsetAndBytes')] public function testSeekPreviousChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); @@ -226,13 +216,13 @@ function () use ($stream, $offset, $length, $expectedBytes): void { }, function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); - } + }, ); $this->assertSame(['find'], $commands); } - public function providePreviousChunkSeekOffsetAndBytes() + public static function providePreviousChunkSeekOffsetAndBytes() { return [ [0, 4, 'abcd'], @@ -242,9 +232,7 @@ public function providePreviousChunkSeekOffsetAndBytes() ]; } - /** - * @dataProvider provideSameChunkSeekOffsetAndBytes - */ + #[DataProvider('provideSameChunkSeekOffsetAndBytes')] public function testSeekSameChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); @@ -262,13 +250,13 @@ function () use ($stream, $offset, $length, $expectedBytes): void { }, function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); - } + }, ); $this->assertSame([], $commands); } - public function provideSameChunkSeekOffsetAndBytes() + public static function provideSameChunkSeekOffsetAndBytes() { return [ [4, 4, 'efgh'], @@ -276,9 +264,7 @@ public function provideSameChunkSeekOffsetAndBytes() ]; } - /** - * @dataProvider provideSubsequentChunkSeekOffsetAndBytes - */ + #[DataProvider('provideSubsequentChunkSeekOffsetAndBytes')] public function testSeekSubsequentChunk($offset, $length, $expectedBytes): void { $fileDocument = $this->collectionWrapper->findFileById('length-10'); @@ -296,13 +282,13 @@ function () use ($stream, $offset, $length, $expectedBytes): void { }, function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); - } + }, ); $this->assertSame([], $commands); } - public function provideSubsequentChunkSeekOffsetAndBytes() + public static function provideSubsequentChunkSeekOffsetAndBytes() { return [ [4, 4, 'efgh'], diff --git a/tests/GridFS/StreamWrapperFunctionalTest.php b/tests/GridFS/StreamWrapperFunctionalTest.php index 792338d36..1960e5518 100644 --- a/tests/GridFS/StreamWrapperFunctionalTest.php +++ b/tests/GridFS/StreamWrapperFunctionalTest.php @@ -4,13 +4,34 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\UTCDateTime; +use MongoDB\GridFS\Exception\FileNotFoundException; +use MongoDB\GridFS\Exception\LogicException; +use MongoDB\GridFS\StreamWrapper; +use PHPUnit\Framework\Attributes\DataProvider; +use function copy; use function fclose; use function feof; +use function file_exists; +use function file_get_contents; +use function file_put_contents; +use function filemtime; +use function filesize; +use function filetype; +use function fopen; use function fread; use function fseek; use function fstat; use function fwrite; +use function is_dir; +use function is_file; +use function is_link; +use function rename; +use function stream_context_create; +use function stream_get_contents; +use function time; +use function unlink; +use function usleep; use const SEEK_CUR; use const SEEK_END; @@ -26,16 +47,23 @@ public function setUp(): void parent::setUp(); $this->filesCollection->insertMany([ - ['_id' => 'length-10', 'length' => 10, 'chunkSize' => 4, 'uploadDate' => new UTCDateTime('1484202200000')], + ['_id' => 'length-10', 'length' => 10, 'chunkSize' => 4, 'uploadDate' => new UTCDateTime(1484202200000)], ]); $this->chunksCollection->insertMany([ - ['_id' => 1, 'files_id' => 'length-10', 'n' => 0, 'data' => new Binary('abcd', Binary::TYPE_GENERIC)], - ['_id' => 2, 'files_id' => 'length-10', 'n' => 1, 'data' => new Binary('efgh', Binary::TYPE_GENERIC)], - ['_id' => 3, 'files_id' => 'length-10', 'n' => 2, 'data' => new Binary('ij', Binary::TYPE_GENERIC)], + ['_id' => 1, 'files_id' => 'length-10', 'n' => 0, 'data' => new Binary('abcd')], + ['_id' => 2, 'files_id' => 'length-10', 'n' => 1, 'data' => new Binary('efgh')], + ['_id' => 3, 'files_id' => 'length-10', 'n' => 2, 'data' => new Binary('ij')], ]); } + public function tearDown(): void + { + StreamWrapper::setContextResolver('bucket', null); + + parent::tearDown(); + } + public function testReadableStreamClose(): void { $stream = $this->bucket->openDownloadStream('length-10'); @@ -96,10 +124,10 @@ public function testReadableStreamStat(): void $this->assertSame(0100444, $stat['mode']); $this->assertSame(10, $stat[7]); $this->assertSame(10, $stat['size']); - $this->assertSame(1484202200, $stat[9]); - $this->assertSame(1484202200, $stat['mtime']); - $this->assertSame(1484202200, $stat[10]); - $this->assertSame(1484202200, $stat['ctime']); + $this->assertSame(1_484_202_200, $stat[9]); + $this->assertSame(1_484_202_200, $stat['mtime']); + $this->assertSame(1_484_202_200, $stat[10]); + $this->assertSame(1_484_202_200, $stat['ctime']); $this->assertSame(4, $stat[11]); $this->assertSame(4, $stat['blksize']); } @@ -190,10 +218,10 @@ public function testWritableStreamStatAfterSaving(): void $this->assertSame(0100444, $stat['mode']); $this->assertSame(10, $stat[7]); $this->assertSame(10, $stat['size']); - $this->assertSame(1484202200, $stat[9]); - $this->assertSame(1484202200, $stat['mtime']); - $this->assertSame(1484202200, $stat[10]); - $this->assertSame(1484202200, $stat['ctime']); + $this->assertSame(1_484_202_200, $stat[9]); + $this->assertSame(1_484_202_200, $stat['mtime']); + $this->assertSame(1_484_202_200, $stat[10]); + $this->assertSame(1_484_202_200, $stat['ctime']); $this->assertSame(4, $stat[11]); $this->assertSame(4, $stat['blksize']); } @@ -204,4 +232,202 @@ public function testWritableStreamWrite(): void $this->assertSame(6, fwrite($stream, 'foobar')); } + + #[DataProvider('provideUrl')] + public function testStreamWithContextResolver(string $url, string $expectedFilename): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + + $stream = fopen($url, 'wb'); + + $this->assertSame(6, fwrite($stream, 'foobar')); + $this->assertTrue(fclose($stream)); + + $file = $this->filesCollection->findOne(['filename' => $expectedFilename]); + $this->assertNotNull($file); + + $stream = fopen($url, 'rb'); + + $this->assertSame('foobar', fread($stream, 10)); + $this->assertTrue(fclose($stream)); + } + + public static function provideUrl() + { + yield 'simple file' => ['gridfs://bucket/filename', 'filename']; + yield 'subdirectory file' => ['gridfs://bucket/path/to/filename.txt', 'path/to/filename.txt']; + yield 'question mark can be used in file name' => ['gridfs://bucket/file%20name?foo=bar', 'file%20name?foo=bar']; + } + + public function testFilePutAndGetContents(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + + $filename = 'gridfs://bucket/path/to/filename'; + + $this->assertSame(6, file_put_contents($filename, 'foobar')); + + $file = $this->filesCollection->findOne(['filename' => 'path/to/filename']); + $this->assertNotNull($file); + + $this->assertSame('foobar', file_get_contents($filename)); + } + + public function testEmptyFilename(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + + $filename = 'gridfs://bucket'; + + $this->assertSame(6, file_put_contents($filename, 'foobar')); + + $file = $this->filesCollection->findOne(['filename' => '']); + $this->assertNotNull($file); + + $this->assertSame('foobar', file_get_contents($filename)); + } + + public function testOpenSpecificRevision(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + + $filename = 'gridfs://bucket/path/to/filename'; + + // Insert 3 revisions, wait 1ms between each to ensure they have different uploadDate + file_put_contents($filename, 'version 0'); + usleep(1000); + file_put_contents($filename, 'version 1'); + usleep(1000); + file_put_contents($filename, 'version 2'); + + $context = stream_context_create([ + 'gridfs' => ['revision' => -2], + ]); + $stream = fopen($filename, 'r', false, $context); + $this->assertSame('version 1', stream_get_contents($stream)); + fclose($stream); + + // Revision not existing + $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('File with name "path/to/filename" and revision "10" not found in "gridfs://bucket/path/to/filename"'); + $context = stream_context_create([ + 'gridfs' => ['revision' => 10], + ]); + fopen($filename, 'r', false, $context); + } + + public function testFileNoFoundWithContextResolver(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + + $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('File with name "filename" and revision "-1" not found in "gridfs://bucket/filename"'); + + fopen('gridfs://bucket/filename', 'r'); + } + + public function testFileNoFoundWithoutDefaultResolver(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('GridFS stream wrapper has no bucket alias: "bucket"'); + + fopen('gridfs://bucket/filename', 'w'); + } + + public function testFileStats(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + $path = 'gridfs://bucket/filename'; + + $this->assertFalse(file_exists($path)); + $this->assertFalse(is_file($path)); + + $time = time(); + $this->assertSame(6, file_put_contents($path, 'foobar')); + + $this->assertTrue(file_exists($path)); + $this->assertSame('file', filetype($path)); + $this->assertTrue(is_file($path)); + $this->assertFalse(is_dir($path)); + $this->assertFalse(is_link($path)); + $this->assertSame(6, filesize($path)); + $this->assertGreaterThanOrEqual($time, filemtime($path)); + $this->assertLessThanOrEqual(time(), filemtime($path)); + } + + public function testCopy(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + $path = 'gridfs://bucket/filename'; + + $this->assertSame(6, file_put_contents($path, 'foobar')); + + copy($path, $path . '.copy'); + $this->assertSame('foobar', file_get_contents($path . '.copy')); + $this->assertSame('foobar', file_get_contents($path)); + } + + public function testRenameAllRevisions(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + $path = 'gridfs://bucket/filename'; + + $this->assertSame(6, file_put_contents($path, 'foobar')); + $this->assertSame(6, file_put_contents($path, 'foobar')); + $this->assertSame(6, file_put_contents($path, 'foobar')); + + $result = rename($path, $path . '.renamed'); + $this->assertTrue($result); + $this->assertTrue(file_exists($path . '.renamed')); + $this->assertFalse(file_exists($path)); + $this->assertSame('foobar', file_get_contents($path . '.renamed')); + + $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('File with name "gridfs://bucket/filename" not found'); + rename($path, $path . '.renamed'); + } + + public function testRenameSameFilename(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + $path = 'gridfs://bucket/filename'; + + $this->assertSame(6, file_put_contents($path, 'foobar')); + + $result = rename($path, $path); + $this->assertTrue($result); + $this->assertTrue(file_exists($path)); + $this->assertSame('foobar', file_get_contents($path)); + + $path = 'gridfs://bucket/missing'; + $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('File with name "gridfs://bucket/missing" not found'); + rename($path, $path); + } + + public function testRenamePathMismatch(): void + { + $this->expectException(LogicException::class); + $this->expectExceptionMessage('Cannot rename "gridfs://bucket/filename" to "gridfs://other/newname" because they are not in the same GridFS bucket.'); + + rename('gridfs://bucket/filename', 'gridfs://other/newname'); + } + + public function testUnlinkAllRevisions(): void + { + $this->bucket->registerGlobalStreamWrapperAlias('bucket'); + $path = 'gridfs://bucket/path/to/filename'; + + file_put_contents($path, 'version 0'); + file_put_contents($path, 'version 1'); + + $result = unlink($path); + + $this->assertTrue($result); + $this->assertFalse(file_exists($path)); + + $this->expectException(FileNotFoundException::class); + $this->expectExceptionMessage('File with name "gridfs://bucket/path/to/filename" not found'); + unlink($path); + } } diff --git a/tests/GridFS/UnusableStream.php b/tests/GridFS/UnusableStream.php index c7686638b..34e964da4 100644 --- a/tests/GridFS/UnusableStream.php +++ b/tests/GridFS/UnusableStream.php @@ -20,7 +20,7 @@ public static function register($protocol = 'unusable'): void stream_wrapper_unregister($protocol); } - stream_wrapper_register($protocol, static::class, STREAM_IS_URL); + stream_wrapper_register($protocol, self::class, STREAM_IS_URL); } public function stream_close(): void diff --git a/tests/GridFS/WritableStreamFunctionalTest.php b/tests/GridFS/WritableStreamFunctionalTest.php index 8a550bffc..2b22baa53 100644 --- a/tests/GridFS/WritableStreamFunctionalTest.php +++ b/tests/GridFS/WritableStreamFunctionalTest.php @@ -5,6 +5,8 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\GridFS\CollectionWrapper; use MongoDB\GridFS\WritableStream; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; use function str_repeat; @@ -13,8 +15,7 @@ */ class WritableStreamFunctionalTest extends FunctionalTestCase { - /** @var CollectionWrapper */ - private $collectionWrapper; + private CollectionWrapper $collectionWrapper; public function setUp(): void { @@ -23,9 +24,7 @@ public function setUp(): void $this->collectionWrapper = new CollectionWrapper($this->manager, $this->getDatabaseName(), 'fs'); } - /** - * @doesNotPerformAssertions - */ + #[DoesNotPerformAssertions] public function testValidConstructorOptions(): void { new WritableStream($this->collectionWrapper, 'filename', [ @@ -35,32 +34,19 @@ public function testValidConstructorOptions(): void ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new WritableStream($this->collectionWrapper, 'filename', $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidIntegerValues(true) as $value) { - $options[][] = ['chunkSizeBytes' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['disableMD5' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['metadata' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'chunkSizeBytes' => self::getInvalidIntegerValues(true), + 'metadata' => self::getInvalidDocumentValues(), + ]); } public function testConstructorShouldRequireChunkSizeBytesOptionToBePositive(): void @@ -85,33 +71,4 @@ public function testWriteBytesAlwaysUpdatesFileSize(): void $stream->close(); $this->assertSame(1536, $stream->getSize()); } - - /** - * @dataProvider provideInputDataAndExpectedMD5 - */ - public function testWriteBytesCalculatesMD5($input, $expectedMD5): void - { - $stream = new WritableStream($this->collectionWrapper, 'filename'); - $stream->writeBytes($input); - $stream->close(); - - $fileDocument = $this->filesCollection->findOne( - ['_id' => $stream->getFile()->_id], - ['projection' => ['md5' => 1, '_id' => 0]] - ); - - $this->assertSameDocument(['md5' => $expectedMD5], $fileDocument); - } - - public function provideInputDataAndExpectedMD5() - { - return [ - ['', 'd41d8cd98f00b204e9800998ecf8427e'], - ['foobar', '3858f62230ac3c915f300c664312c63f'], - [str_repeat('foobar', 43520), '88ff0e5fcb0acb27947d736b5d69cb73'], - [str_repeat('foobar', 43521), '8ff86511c95a06a611842ceb555d8454'], - [str_repeat('foobar', 87040), '45bfa1a9ec36728ee7338d15c5a30c13'], - [str_repeat('foobar', 87041), '95e78f624f8e745bcfd2d11691fa601e'], - ]; - } } diff --git a/tests/LogNonGenuineHostTest.php b/tests/LogNonGenuineHostTest.php new file mode 100644 index 000000000..2de5e2d95 --- /dev/null +++ b/tests/LogNonGenuineHostTest.php @@ -0,0 +1,142 @@ +logger = $this->createTestPsrLogger(); + + add_logger($this->logger); + } + + public function tearDown(): void + { + remove_logger($this->logger); + } + + #[DataProvider('provideCosmosUris')] + public function testCosmosUriLogsInfoMessage(string $uri): void + { + $this->createClientAndIgnoreSrvLookupError($uri); + + $expectedLog = [ + 'info', + 'You appear to be connected to a CosmosDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/cosmosdb', + ['domain' => 'mongoc'], + ]; + + $this->assertContains($expectedLog, $this->logger->logs); + } + + public static function provideCosmosUris(): array + { + return [ + ['mongodb://a.mongo.cosmos.azure.com:19555/'], + ['mongodb://a.MONGO.COSMOS.AZURE.COM:19555/'], + ['mongodb+srv://a.mongo.cosmos.azure.com/'], + ['mongodb+srv://A.MONGO.COSMOS.AZURE.COM/'], + // Mixing genuine and nongenuine hosts (unlikely in practice) + ['mongodb://a.example.com:27017,b.mongo.cosmos.azure.com:19555/'], + ]; + } + + #[DataProvider('provideDocumentDbUris')] + public function testDocumentDbUriLogsInfoMessage(string $uri): void + { + $this->createClientAndIgnoreSrvLookupError($uri); + + $expectedLog = [ + 'info', + 'You appear to be connected to a DocumentDB cluster. For more information regarding feature compatibility and support please visit https://www.mongodb.com/supportability/documentdb', + ['domain' => 'mongoc'], + ]; + + $this->assertContains($expectedLog, $this->logger->logs); + } + + public static function provideDocumentDbUris(): array + { + return [ + ['mongodb://a.docdb.amazonaws.com:27017/'], + ['mongodb://a.docdb-elastic.amazonaws.com:27017/'], + ['mongodb://a.DOCDB.AMAZONAWS.COM:27017/'], + ['mongodb://a.DOCDB-ELASTIC.AMAZONAWS.COM:27017/'], + ['mongodb+srv://a.DOCDB.AMAZONAWS.COM/'], + ['mongodb+srv://a.DOCDB-ELASTIC.AMAZONAWS.COM/'], + // Mixing genuine and nongenuine hosts (unlikely in practice) + ['mongodb://a.example.com:27017,b.docdb.amazonaws.com:27017/'], + ['mongodb://a.example.com:27017,b.docdb-elastic.amazonaws.com:27017/'], + ]; + } + + #[DataProvider('provideGenuineUris')] + public function testGenuineUriDoesNotLog(string $uri): void + { + $this->createClientAndIgnoreSrvLookupError($uri); + $this->assertEmpty($this->logger->logs); + } + + public static function provideGenuineUris(): array + { + return [ + ['mongodb://a.example.com:27017,b.example.com:27017/'], + ['mongodb://a.mongodb.net:27017'], + ['mongodb+srv://a.example.com/'], + ['mongodb+srv://a.mongodb.net/'], + // Host names do not end with expected suffix + ['mongodb://a.mongo.cosmos.azure.com.tld:19555/'], + ['mongodb://a.docdb.amazonaws.com.tld:27017/'], + ['mongodb://a.docdb-elastic.amazonaws.com.tld:27017/'], + // SRV host names do not end with expected suffix + ['mongodb+srv://a.mongo.cosmos.azure.com.tld/'], + ['mongodb+srv://a.docdb.amazonaws.com.tld/'], + ['mongodb+srv://a.docdb-elastic.amazonaws.com.tld/'], + ]; + } + + private function createClientAndIgnoreSrvLookupError(string $uri): void + { + try { + $client = new Client($uri); + } catch (InvalidArgumentException $e) { + $this->assertStringContainsString('Failed to look up SRV record', $e->getMessage()); + } + } + + private function createTestPsrLogger(): LoggerInterface + { + return new class extends AbstractLogger { + public $logs = []; + + /** + * Note: parameter type hints are omitted for compatibility with + * psr/log 1.1.4 and PHP 7.4. + */ + public function log($level, $message, array $context = []): void + { + /* Ignore debug-level log messages from PHPC (e.g. connection + * string, Manager creation, handshake data). */ + if ($level === 'debug') { + return; + } + + $this->logs[] = func_get_args(); + } + }; + } +} diff --git a/tests/Model/BSONArrayTest.php b/tests/Model/BSONArrayTest.php index 9f420bed9..d6f6c162e 100644 --- a/tests/Model/BSONArrayTest.php +++ b/tests/Model/BSONArrayTest.php @@ -11,6 +11,8 @@ use function json_encode; +use const JSON_THROW_ON_ERROR; + class BSONArrayTest extends TestCase { public function testBsonSerializeReindexesKeys(): void @@ -88,7 +90,7 @@ public function testJsonSerialize(): void $expectedJson = '["foo",[1,2,3],{"foo":1,"bar":2,"baz":3},[[[]]]]'; - $this->assertSame($expectedJson, json_encode($document)); + $this->assertSame($expectedJson, json_encode($document, JSON_THROW_ON_ERROR)); } public function testJsonSerializeReindexesKeys(): void diff --git a/tests/Model/BSONDocumentTest.php b/tests/Model/BSONDocumentTest.php index bd0009dcf..2000aef63 100644 --- a/tests/Model/BSONDocumentTest.php +++ b/tests/Model/BSONDocumentTest.php @@ -12,6 +12,8 @@ use function json_encode; +use const JSON_THROW_ON_ERROR; + class BSONDocumentTest extends TestCase { public function testConstructorDefaultsToPropertyAccess(): void @@ -21,6 +23,13 @@ public function testConstructorDefaultsToPropertyAccess(): void $this->assertSame('bar', $document->foo); } + public function testConstructorWithStandardObject(): void + { + $object = (object) ['foo' => 'bar']; + $document = new BSONDocument($object); + $this->assertEquals($object, $document->bsonSerialize()); + } + public function testBsonSerializeCastsToObject(): void { $data = [0 => 'foo', 2 => 'bar']; @@ -96,7 +105,7 @@ public function testJsonSerialize(): void $expectedJson = '{"foo":"bar","array":[1,2,3],"object":{"0":1,"1":2,"2":3},"nested":{"0":{"0":{}}}}'; - $this->assertSame($expectedJson, json_encode($document)); + $this->assertSame($expectedJson, json_encode($document, JSON_THROW_ON_ERROR)); } public function testJsonSerializeCastsToObject(): void diff --git a/tests/Model/BSONIteratorTest.php b/tests/Model/BSONIteratorTest.php index 69c2f9d26..bac019156 100644 --- a/tests/Model/BSONIteratorTest.php +++ b/tests/Model/BSONIteratorTest.php @@ -2,23 +2,31 @@ namespace MongoDB\Tests\Model; +use Generator; +use MongoDB\BSON\Document; use MongoDB\Exception\UnexpectedValueException; use MongoDB\Model\BSONIterator; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; use function array_map; use function implode; use function iterator_to_array; -use function MongoDB\BSON\fromPHP; use function substr; class BSONIteratorTest extends TestCase { - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testValidValues(?array $typeMap, $binaryString, array $expectedDocuments): void + #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] + public function testValidValues(?array $typeMap, array $expectedDocuments): void { + $binaryString = implode('', array_map( + fn ($input) => (string) Document::fromPHP($input), + [ + ['_id' => 1, 'x' => ['foo' => 'bar']], + ['_id' => 3, 'x' => ['foo' => 'bar']], + ], + )); + $bsonIt = new BSONIterator($binaryString, ['typeMap' => $typeMap]); $results = iterator_to_array($bsonIt); @@ -26,71 +34,44 @@ public function testValidValues(?array $typeMap, $binaryString, array $expectedD $this->assertEquals($expectedDocuments, $results); } - public function provideTypeMapOptionsAndExpectedDocuments() + public static function provideTypeMapOptionsAndExpectedDocuments(): Generator { - return [ - [ - null, - implode(array_map( - 'MongoDB\BSON\fromPHP', - [ - ['_id' => 1, 'x' => ['foo' => 'bar']], - ['_id' => 3, 'x' => ['foo' => 'bar']], - ] - )), - [ - (object) ['_id' => 1, 'x' => (object) ['foo' => 'bar']], - (object) ['_id' => 3, 'x' => (object) ['foo' => 'bar']], - ], + yield 'No type map' => [ + 'typeMap' => null, + 'expectedDocuments' => [ + (object) ['_id' => 1, 'x' => (object) ['foo' => 'bar']], + (object) ['_id' => 3, 'x' => (object) ['foo' => 'bar']], ], - [ - ['root' => 'array', 'document' => 'array'], - implode(array_map( - 'MongoDB\BSON\fromPHP', - [ - ['_id' => 1, 'x' => ['foo' => 'bar']], - ['_id' => 3, 'x' => ['foo' => 'bar']], - ] - )), - [ - ['_id' => 1, 'x' => ['foo' => 'bar']], - ['_id' => 3, 'x' => ['foo' => 'bar']], - ], + ]; + + yield 'Array type map' => [ + 'typeMap' => ['root' => 'array', 'document' => 'array'], + 'expectedDocuments' => [ + ['_id' => 1, 'x' => ['foo' => 'bar']], + ['_id' => 3, 'x' => ['foo' => 'bar']], ], - [ - ['root' => 'object', 'document' => 'array'], - implode(array_map( - 'MongoDB\BSON\fromPHP', - [ - ['_id' => 1, 'x' => ['foo' => 'bar']], - ['_id' => 3, 'x' => ['foo' => 'bar']], - ] - )), - [ - (object) ['_id' => 1, 'x' => ['foo' => 'bar']], - (object) ['_id' => 3, 'x' => ['foo' => 'bar']], - ], + ]; + + yield 'Root as object' => [ + 'typeMap' => ['root' => 'object', 'document' => 'array'], + 'expectedDocuments' => [ + (object) ['_id' => 1, 'x' => ['foo' => 'bar']], + (object) ['_id' => 3, 'x' => ['foo' => 'bar']], ], - [ - ['root' => 'array', 'document' => 'stdClass'], - implode(array_map( - 'MongoDB\BSON\fromPHP', - [ - ['_id' => 1, 'x' => ['foo' => 'bar']], - ['_id' => 3, 'x' => ['foo' => 'bar']], - ] - )), - [ - ['_id' => 1, 'x' => (object) ['foo' => 'bar']], - ['_id' => 3, 'x' => (object) ['foo' => 'bar']], - ], + ]; + + yield 'Document as object' => [ + 'typeMap' => ['root' => 'array', 'document' => 'stdClass'], + 'expectedDocuments' => [ + ['_id' => 1, 'x' => (object) ['foo' => 'bar']], + ['_id' => 3, 'x' => (object) ['foo' => 'bar']], ], ]; } public function testCannotReadLengthFromFirstDocument(): void { - $binaryString = substr(fromPHP([]), 0, 3); + $binaryString = substr((string) Document::fromPHP([]), 0, 3); $bsonIt = new BSONIterator($binaryString); @@ -101,7 +82,7 @@ public function testCannotReadLengthFromFirstDocument(): void public function testCannotReadLengthFromSubsequentDocument(): void { - $binaryString = fromPHP([]) . substr(fromPHP([]), 0, 3); + $binaryString = (string) Document::fromPHP([]) . substr((string) Document::fromPHP([]), 0, 3); $bsonIt = new BSONIterator($binaryString); $bsonIt->rewind(); @@ -113,7 +94,7 @@ public function testCannotReadLengthFromSubsequentDocument(): void public function testCannotReadFirstDocument(): void { - $binaryString = substr(fromPHP([]), 0, 4); + $binaryString = substr((string) Document::fromPHP([]), 0, 4); $bsonIt = new BSONIterator($binaryString); @@ -124,7 +105,7 @@ public function testCannotReadFirstDocument(): void public function testCannotReadSecondDocument(): void { - $binaryString = fromPHP([]) . substr(fromPHP([]), 0, 4); + $binaryString = (string) Document::fromPHP([]) . substr((string) Document::fromPHP([]), 0, 4); $bsonIt = new BSONIterator($binaryString); $bsonIt->rewind(); diff --git a/tests/Model/CachingIteratorFunctionalTest.php b/tests/Model/CachingIteratorFunctionalTest.php new file mode 100644 index 000000000..bb600013c --- /dev/null +++ b/tests/Model/CachingIteratorFunctionalTest.php @@ -0,0 +1,49 @@ +dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $cursor = $collection->find(); + $iterator = new CachingIterator($cursor); + + $this->assertSame(0, $iterator->count()); + $iterator->rewind(); + $this->assertFalse($iterator->valid()); + $this->assertNull($iterator->current()); + $this->assertNull($iterator->key()); + } + + public function testCursor(): void + { + $collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection->insertOne(['_id' => 1]); + $collection->insertOne(['_id' => 2]); + $cursor = $collection->find(); + $iterator = new CachingIterator($cursor); + + $this->assertSame(2, $iterator->count()); + + $iterator->rewind(); + $this->assertTrue($iterator->valid()); + $this->assertNotNull($iterator->current()); + $this->assertSame(0, $iterator->key()); + + $iterator->next(); + $this->assertTrue($iterator->valid()); + $this->assertNotNull($iterator->current()); + $this->assertSame(1, $iterator->key()); + + $iterator->next(); + $this->assertFalse($iterator->valid()); + $this->assertNull($iterator->current()); + $this->assertNull($iterator->key()); + } +} diff --git a/tests/Model/CachingIteratorTest.php b/tests/Model/CachingIteratorTest.php index 1539db3e3..b8e43aece 100644 --- a/tests/Model/CachingIteratorTest.php +++ b/tests/Model/CachingIteratorTest.php @@ -119,8 +119,7 @@ public function testCountWithEmptySet(): void public function testWithWrongIterator(): void { $nestedIterator = new class implements Iterator { - /** @var int */ - private $i = 0; + private int $i = 0; public function current(): int { diff --git a/tests/Model/CallbackIteratorTest.php b/tests/Model/CallbackIteratorTest.php new file mode 100644 index 000000000..e38a26c4f --- /dev/null +++ b/tests/Model/CallbackIteratorTest.php @@ -0,0 +1,83 @@ +assertEquals($expected, iterator_to_array($callbackIterator)); + } + + public static function provideTests(): Generator + { + $listIterator = new ArrayIterator([1, 2, 3]); + $hashIterator = new ArrayIterator(['a' => 1, 'b' => 2, 'c' => 3]); + + $iteratorAggregate = new class ($listIterator) implements IteratorAggregate + { + public function __construct(private Iterator $iterator) + { + } + + public function getIterator(): Iterator + { + return $this->iterator; + } + }; + + yield 'List with closure' => [ + 'expected' => [2, 4, 6], + 'source' => $listIterator, + 'callback' => function ($value, $key) use ($listIterator) { + self::assertSame($listIterator->key(), $key); + + return $value * 2; + }, + ]; + + yield 'List with callable' => [ + 'expected' => [2, 4, 6], + 'source' => $listIterator, + 'callback' => [self::class, 'doubleValue'], + ]; + + yield 'Hash with closure' => [ + 'expected' => ['a' => 2, 'b' => 4, 'c' => 6], + 'source' => $hashIterator, + 'callback' => function ($value, $key) use ($hashIterator) { + self::assertSame($hashIterator->key(), $key); + + return $value * 2; + }, + ]; + + yield 'IteratorAggregate with closure' => [ + 'expected' => [2, 4, 6], + 'source' => $iteratorAggregate, + 'callback' => function ($value, $key) use ($listIterator) { + self::assertSame($listIterator->key(), $key); + + return $value * 2; + }, + ]; + } + + public static function doubleValue($value, $key) + { + return $value * 2; + } +} diff --git a/tests/Model/ChangeStreamIteratorTest.php b/tests/Model/ChangeStreamIteratorTest.php index 9be703bab..84b4390fb 100644 --- a/tests/Model/ChangeStreamIteratorTest.php +++ b/tests/Model/ChangeStreamIteratorTest.php @@ -7,41 +7,32 @@ namespace MongoDB\Tests\Model; +use MongoDB\BSON\PackedArray; use MongoDB\Collection; use MongoDB\Driver\Exception\LogicException; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Model\ChangeStreamIterator; -use MongoDB\Operation\CreateCollection; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\Find; use MongoDB\Tests\CommandObserver; use MongoDB\Tests\FunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; use TypeError; -use function array_merge; use function sprintf; class ChangeStreamIteratorTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { parent::setUp(); - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); - - $operation = new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['capped' => true, 'size' => 8192]); - $operation->execute($this->getPrimaryServer()); - - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); + // Drop and re-create the collection + $this->collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName(), ['capped' => true, 'size' => 8192]); } - /** - * @dataProvider provideInvalidIntegerValues - */ + #[DataProvider('provideInvalidIntegerValues')] public function testFirstBatchArgumentTypeCheck($firstBatchSize): void { $this->expectException(TypeError::class); @@ -60,27 +51,23 @@ public function testInitialResumeToken(): void $this->assertSameDocument((object) ['resumeToken' => 2], $iterator->getResumeToken()); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testInitialResumeTokenArgumentTypeCheck($initialResumeToken): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($initialResumeToken instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new ChangeStreamIterator($this->collection->find(), 0, $initialResumeToken, null); } - /** - * @dataProvider provideInvalidObjectValues - */ + #[DataProvider('provideInvalidObjectValues')] public function testPostBatchResumeTokenArgumentTypeCheck($postBatchResumeToken): void { $this->expectException(TypeError::class); new ChangeStreamIterator($this->collection->find(), 0, null, $postBatchResumeToken); } - public function provideInvalidObjectValues() + public static function provideInvalidObjectValues() { - return $this->wrapValuesForDataProvider(array_merge($this->getInvalidDocumentValues(), [[]])); + return self::wrapValuesForDataProvider([123, 3.14, 'foo', true, []]); } public function testPostBatchResumeTokenIsReturnedForLastElementInFirstBatch(): void @@ -158,7 +145,7 @@ private function assertNoCommandExecuted(callable $callable): void $callable, function (array $event) use (&$commands): void { $this->fail(sprintf('"%s" command was executed', $event['started']->getCommandName())); - } + }, ); $this->assertEmpty($commands); diff --git a/tests/Model/CodecCursorFunctionalTest.php b/tests/Model/CodecCursorFunctionalTest.php new file mode 100644 index 000000000..64c7268b7 --- /dev/null +++ b/tests/Model/CodecCursorFunctionalTest.php @@ -0,0 +1,40 @@ +dropCollection($this->getDatabaseName(), $this->getCollectionName()); + } + + public function testSetTypeMap(): void + { + $collection = self::createTestClient()->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $cursor = $collection->find(); + + $codecCursor = CodecCursor::fromCursor($cursor, $this->createMock(DocumentCodec::class)); + + $this->assertError(E_USER_WARNING, fn () => $codecCursor->setTypeMap(['root' => 'array'])); + } + + public function testGetIdReturnTypeWithArgument(): void + { + $collection = self::createTestClient()->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $cursor = $collection->find(); + + $codecCursor = CodecCursor::fromCursor($cursor, $this->createMock(DocumentCodec::class)); + + self::assertInstanceOf(Int64::class, $codecCursor->getId()); + } +} diff --git a/tests/Model/CollectionInfoTest.php b/tests/Model/CollectionInfoTest.php index ff5a112db..9b926cfad 100644 --- a/tests/Model/CollectionInfoTest.php +++ b/tests/Model/CollectionInfoTest.php @@ -10,28 +10,35 @@ class CollectionInfoTest extends TestCase { public function testGetBasicInformation(): void { - $info = new CollectionInfo([ + $viewInfo = new CollectionInfo([ 'name' => 'foo', 'type' => 'view', - 'options' => ['capped' => true, 'size' => 1048576], + 'options' => ['capped' => true, 'size' => 1_048_576], 'info' => ['readOnly' => true], 'idIndex' => ['idIndex' => true], // Dummy option ]); - $this->assertSame('foo', $info->getName()); - $this->assertSame('foo', $info['name']); + $this->assertSame('foo', $viewInfo->getName()); + $this->assertSame('foo', $viewInfo['name']); + + $this->assertTrue($viewInfo->isView()); + $this->assertSame('view', $viewInfo['type']); - $this->assertSame('view', $info->getType()); - $this->assertSame('view', $info['type']); + $this->assertSame(['capped' => true, 'size' => 1_048_576], $viewInfo->getOptions()); + $this->assertSame(['capped' => true, 'size' => 1_048_576], $viewInfo['options']); - $this->assertSame(['capped' => true, 'size' => 1048576], $info->getOptions()); - $this->assertSame(['capped' => true, 'size' => 1048576], $info['options']); + $this->assertSame(['readOnly' => true], $viewInfo->getInfo()); + $this->assertSame(['readOnly' => true], $viewInfo['info']); - $this->assertSame(['readOnly' => true], $info->getInfo()); - $this->assertSame(['readOnly' => true], $info['info']); + $this->assertSame(['idIndex' => true], $viewInfo->getIdIndex()); + $this->assertSame(['idIndex' => true], $viewInfo['idIndex']); + + $collectionInfo = new CollectionInfo([ + 'name' => 'bar', + 'type' => 'collection', + ]); - $this->assertSame(['idIndex' => true], $info->getIdIndex()); - $this->assertSame(['idIndex' => true], $info['idIndex']); + $this->assertFalse($collectionInfo->isView()); } public function testMissingFields(): void @@ -58,22 +65,22 @@ public function testCappedCollectionMethods(): void $this->assertNull($info->getCappedMax()); $this->assertNull($info->getCappedSize()); - $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); + $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1_048_576]]); $this->assertTrue($info->isCapped()); $this->assertNull($info->getCappedMax()); - $this->assertSame(1048576, $info->getCappedSize()); + $this->assertSame(1_048_576, $info->getCappedSize()); - $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576, 'max' => 100]]); + $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1_048_576, 'max' => 100]]); $this->assertTrue($info->isCapped()); $this->assertSame(100, $info->getCappedMax()); - $this->assertSame(1048576, $info->getCappedSize()); + $this->assertSame(1_048_576, $info->getCappedSize()); } public function testDebugInfo(): void { $expectedInfo = [ 'name' => 'foo', - 'options' => ['capped' => true, 'size' => 1048576], + 'options' => ['capped' => true, 'size' => 1_048_576], ]; $info = new CollectionInfo($expectedInfo); @@ -90,7 +97,7 @@ public function testImplementsArrayAccess(): void public function testOffsetSetCannotBeCalled(): void { - $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); + $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1_048_576]]); $this->expectException(BadMethodCallException::class); $this->expectExceptionMessage(CollectionInfo::class . ' is immutable'); $info['options'] = ['capped' => false]; @@ -98,7 +105,7 @@ public function testOffsetSetCannotBeCalled(): void public function testOffsetUnsetCannotBeCalled(): void { - $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1048576]]); + $info = new CollectionInfo(['name' => 'foo', 'options' => ['capped' => true, 'size' => 1_048_576]]); $this->expectException(BadMethodCallException::class); $this->expectExceptionMessage(CollectionInfo::class . ' is immutable'); unset($info['options']); diff --git a/tests/Model/DatabaseInfoTest.php b/tests/Model/DatabaseInfoTest.php index 14e083a08..dd3e6093e 100644 --- a/tests/Model/DatabaseInfoTest.php +++ b/tests/Model/DatabaseInfoTest.php @@ -16,8 +16,8 @@ public function testGetName(): void public function testGetSizeOnDisk(): void { - $info = new DatabaseInfo(['sizeOnDisk' => 1048576]); - $this->assertSame(1048576, $info->getSizeOnDisk()); + $info = new DatabaseInfo(['sizeOnDisk' => 1_048_576]); + $this->assertSame(1_048_576, $info->getSizeOnDisk()); } public function testIsEmpty(): void @@ -33,7 +33,7 @@ public function testDebugInfo(): void { $expectedInfo = [ 'name' => 'foo', - 'sizeOnDisk' => 1048576, + 'sizeOnDisk' => 1_048_576, 'empty' => false, ]; @@ -51,7 +51,7 @@ public function testImplementsArrayAccess(): void public function testOffsetSetCannotBeCalled(): void { - $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1048576, 'empty' => false]); + $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1_048_576, 'empty' => false]); $this->expectException(BadMethodCallException::class); $this->expectExceptionMessage(DatabaseInfo::class . ' is immutable'); $info['empty'] = true; @@ -59,7 +59,7 @@ public function testOffsetSetCannotBeCalled(): void public function testOffsetUnsetCannotBeCalled(): void { - $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1048576, 'empty' => false]); + $info = new DatabaseInfo(['name' => 'foo', 'sizeOnDisk' => 1_048_576, 'empty' => false]); $this->expectException(BadMethodCallException::class); $this->expectExceptionMessage(DatabaseInfo::class . ' is immutable'); unset($info['empty']); diff --git a/tests/Model/IndexInfoFunctionalTest.php b/tests/Model/IndexInfoFunctionalTest.php index f9c437eac..824507ec6 100644 --- a/tests/Model/IndexInfoFunctionalTest.php +++ b/tests/Model/IndexInfoFunctionalTest.php @@ -7,26 +7,13 @@ class IndexInfoFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { parent::setUp(); - $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); - $this->collection->drop(); - } - - public function tearDown(): void - { - if ($this->hasFailed()) { - return; - } - - $this->collection->drop(); - - parent::tearDown(); + $this->collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } public function testIs2dSphere(): void @@ -45,27 +32,6 @@ public function testIs2dSphere(): void $this->assertEquals(3, $index['2dsphereIndexVersion']); } - /** - * @group matrix-testing-exclude-server-5.0-driver-4.0 - * @group matrix-testing-exclude-server-5.0-driver-4.2 - * @group matrix-testing-exclude-server-5.0-driver-4.4 - */ - public function testIsGeoHaystack(): void - { - $this->skipIfGeoHaystackIndexIsNotSupported(); - - $indexName = $this->collection->createIndex(['pos' => 'geoHaystack', 'x' => 1], ['bucketSize' => 5]); - $result = $this->collection->listIndexes(); - - $result->rewind(); - $result->next(); - $index = $result->current(); - - $this->assertEquals($indexName, $index->getName()); - $this->assertTrue($index->isGeoHaystack()); - $this->assertEquals(5, $index['bucketSize']); - } - public function testIsText(): void { $indexName = $this->collection->createIndex(['x' => 'text']); diff --git a/tests/Model/IndexInfoTest.php b/tests/Model/IndexInfoTest.php index b4c482597..189b522bd 100644 --- a/tests/Model/IndexInfoTest.php +++ b/tests/Model/IndexInfoTest.php @@ -14,15 +14,12 @@ public function testBasicIndex(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['x' => 1], $info->getKey()); $this->assertSame('x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -35,16 +32,13 @@ public function testSparseIndex(): void 'v' => 1, 'key' => ['y' => 1], 'name' => 'y_sparse', - 'ns' => 'foo.bar', 'sparse' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['y' => 1], $info->getKey()); $this->assertSame('y_sparse', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); $this->assertTrue($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -57,16 +51,13 @@ public function testUniqueIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'unique' => true, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -79,16 +70,13 @@ public function testTtlIndex(): void 'v' => 1, 'key' => ['z' => 1], 'name' => 'z_unique', - 'ns' => 'foo.bar', 'expireAfterSeconds' => 100, ]); $this->assertSame(1, $info->getVersion()); $this->assertSame(['z' => 1], $info->getKey()); $this->assertSame('z_unique', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertTrue($info->isTtl()); @@ -103,7 +91,6 @@ public function testDebugInfo(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]; $info = new IndexInfo($expectedInfo); @@ -116,7 +103,6 @@ public function testImplementsArrayAccess(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->assertInstanceOf('ArrayAccess', $info); @@ -130,7 +116,6 @@ public function testOffsetSetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -144,7 +129,6 @@ public function testOffsetUnsetCannotBeCalled(): void 'v' => 1, 'key' => ['x' => 1], 'name' => 'x_1', - 'ns' => 'foo.bar', ]); $this->expectException(BadMethodCallException::class); @@ -158,36 +142,12 @@ public function testIs2dSphere(): void 'v' => 2, 'key' => ['pos' => '2dsphere'], 'name' => 'pos_2dsphere', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['pos' => '2dsphere'], $info->getKey()); $this->assertSame('pos_2dsphere', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertTrue($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); - $this->assertFalse($info->isSparse()); - $this->assertFalse($info->isText()); - $this->assertFalse($info->isTtl()); - $this->assertFalse($info->isUnique()); - } - - public function testIsGeoHaystack(): void - { - $info = new IndexInfo([ - 'v' => 2, - 'key' => ['pos2' => 'geoHaystack', 'x' => 1], - 'name' => 'pos2_geoHaystack_x_1', - 'ns' => 'foo.bar', - ]); - - $this->assertSame(2, $info->getVersion()); - $this->assertSame(['pos2' => 'geoHaystack', 'x' => 1], $info->getKey()); - $this->assertSame('pos2_geoHaystack_x_1', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); - $this->assertFalse($info->is2dSphere()); - $this->assertTrue($info->isGeoHaystack()); $this->assertFalse($info->isSparse()); $this->assertFalse($info->isText()); $this->assertFalse($info->isTtl()); @@ -200,15 +160,12 @@ public function testIsText(): void 'v' => 2, 'key' => ['_fts' => 'text', '_ftsx' => 1], 'name' => 'title_text_description_text', - 'ns' => 'foo.bar', ]); $this->assertSame(2, $info->getVersion()); $this->assertSame(['_fts' => 'text', '_ftsx' => 1], $info->getKey()); $this->assertSame('title_text_description_text', $info->getName()); - $this->assertSame('foo.bar', $info->getNamespace()); $this->assertFalse($info->is2dSphere()); - $this->assertFalse($info->isGeoHaystack()); $this->assertFalse($info->isSparse()); $this->assertTrue($info->isText()); $this->assertFalse($info->isTtl()); diff --git a/tests/Model/IndexInputTest.php b/tests/Model/IndexInputTest.php index 673df1e5d..a0a44fa1a 100644 --- a/tests/Model/IndexInputTest.php +++ b/tests/Model/IndexInputTest.php @@ -2,10 +2,13 @@ namespace MongoDB\Tests\Model; +use MongoDB\BSON\Document; use MongoDB\BSON\Serializable; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInput; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; use stdClass; class IndexInputTest extends TestCase @@ -13,47 +16,52 @@ class IndexInputTest extends TestCase public function testConstructorShouldRequireKey(): void { $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Required "key" document is missing from index specification'); new IndexInput([]); } - public function testConstructorShouldRequireKeyToBeArrayOrObject(): void + #[DataProvider('provideInvalidDocumentValues')] + public function testConstructorShouldRequireKeyToBeArrayOrObject($key): void { $this->expectException(InvalidArgumentException::class); - new IndexInput(['key' => 'foo']); + $this->expectExceptionMessage('Expected "key" option to have type "document"'); + new IndexInput(['key' => $key]); } - /** - * @dataProvider provideInvalidFieldOrderValues - */ + #[DataProvider('provideInvalidFieldOrderValues')] public function testConstructorShouldRequireKeyFieldOrderToBeNumericOrString($order): void { $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected order value for "x" field within "key" option to have type "numeric or string"'); new IndexInput(['key' => ['x' => $order]]); } - public function provideInvalidFieldOrderValues() + public static function provideInvalidFieldOrderValues() { - return $this->wrapValuesForDataProvider([true, [], new stdClass()]); + return self::wrapValuesForDataProvider([true, [], new stdClass()]); } - public function testConstructorShouldRequireNameToBeString(): void + #[DataProvider('provideInvalidStringValues')] + public function testConstructorShouldRequireNameToBeString($name): void { $this->expectException(InvalidArgumentException::class); - new IndexInput(['key' => ['x' => 1], 'name' => 1]); + $this->expectExceptionMessage('Expected "name" option to have type "string"'); + new IndexInput(['key' => ['x' => 1], 'name' => $name]); } - /** - * @dataProvider provideExpectedNameAndKey - */ - public function testNameGeneration($expectedName, array $key): void + #[DataProvider('provideExpectedNameAndKey')] + public function testNameGeneration($expectedName, array|object $key): void { $this->assertSame($expectedName, (string) new IndexInput(['key' => $key])); } - public function provideExpectedNameAndKey() + public static function provideExpectedNameAndKey(): array { return [ ['x_1', ['x' => 1]], + ['x_1', (object) ['x' => 1]], + ['x_1', new BSONDocument(['x' => 1])], + ['x_1', Document::fromPHP(['x' => 1])], ['x_1_y_-1', ['x' => 1, 'y' => -1]], ['loc_2dsphere', ['loc' => '2dsphere']], ['loc_2dsphere_x_1', ['loc' => '2dsphere', 'x' => 1]], @@ -63,7 +71,7 @@ public function provideExpectedNameAndKey() public function testBsonSerialization(): void { - $expected = [ + $expected = (object) [ 'key' => ['x' => 1], 'unique' => true, 'name' => 'x_1', @@ -75,6 +83,6 @@ public function testBsonSerialization(): void ]); $this->assertInstanceOf(Serializable::class, $indexInput); - $this->assertSame($expected, $indexInput->bsonSerialize()); + $this->assertEquals($expected, $indexInput->bsonSerialize()); } } diff --git a/tests/Model/SearchIndexInputTest.php b/tests/Model/SearchIndexInputTest.php new file mode 100644 index 000000000..507ced5c9 --- /dev/null +++ b/tests/Model/SearchIndexInputTest.php @@ -0,0 +1,59 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Required "definition" document is missing from search index specification'); + new SearchIndexInput([]); + } + + #[DataProvider('provideInvalidDocumentValues')] + public function testConstructorIndexDefinitionMustBeADocument($definition): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "definition" option to have type "document"'); + new SearchIndexInput(['definition' => $definition]); + } + + #[DataProvider('provideInvalidStringValues')] + public function testConstructorShouldRequireNameToBeString($name): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "name" option to have type "string"'); + new SearchIndexInput(['definition' => ['mapping' => ['dynamic' => true]], 'name' => $name]); + } + + #[DataProvider('provideInvalidStringValues')] + public function testConstructorShouldRequireTypeToBeString($type): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "type" option to have type "string"'); + new SearchIndexInput(['definition' => ['mapping' => ['dynamic' => true]], 'type' => $type]); + } + + public function testBsonSerialization(): void + { + $expected = (object) [ + 'name' => 'my_search', + 'definition' => ['mapping' => ['dynamic' => true]], + ]; + + $indexInput = new SearchIndexInput([ + 'name' => 'my_search', + 'definition' => ['mapping' => ['dynamic' => true]], + ]); + + $this->assertInstanceOf(Serializable::class, $indexInput); + $this->assertEquals($expected, $indexInput->bsonSerialize()); + } +} diff --git a/tests/Operation/AggregateFunctionalTest.php b/tests/Operation/AggregateFunctionalTest.php index d8b6c13af..2d70ea299 100644 --- a/tests/Operation/AggregateFunctionalTest.php +++ b/tests/Operation/AggregateFunctionalTest.php @@ -9,8 +9,13 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Operation\Aggregate; use MongoDB\Tests\CommandObserver; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; use stdClass; +use function array_key_exists; +use function array_key_first; use function current; use function iterator_to_array; @@ -23,14 +28,14 @@ function (): void { $operation = new Aggregate( $this->getDatabaseName(), $this->getCollectionName(), - [['$match' => ['x' => 1]]] + [['$match' => ['x' => 1]]], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('allowDiskUse', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('allowDiskUse', $event['started']->getCommand()); + }, ); } @@ -42,14 +47,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['$out' => $this->getCollectionName() . '.output']], - ['batchSize' => 0] + ['batchSize' => 0], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { $this->assertEquals(new stdClass(), $event['started']->getCommand()->cursor); - } + }, ); $outCollection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName() . '.output'); @@ -63,14 +68,14 @@ function (): void { $operation = new Aggregate( 'admin', null, - [['$currentOp' => (object) []]] + [['$currentOp' => (object) []]], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { $this->assertSame(1, $event['started']->getCommand()->aggregate); - } + }, ); } @@ -82,14 +87,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['$match' => ['x' => 1]]], - ['readConcern' => $this->createDefaultReadConcern()] + ['readConcern' => $this->createDefaultReadConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } @@ -101,14 +106,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['$out' => $this->getCollectionName() . '.output']], - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); $outCollection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName() . '.output'); @@ -146,20 +151,18 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] public function testTypeMapOption(?array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); @@ -182,11 +185,19 @@ public function testExplainOption(): void $this->assertCount(1, $results); + $checkResult = $results[0]; + + // Sharded clusters and load balanced servers list plans per shard + if (array_key_exists('shards', $checkResult)) { + $firstShard = array_key_first((array) $checkResult['shards']); + $checkResult = (array) $checkResult['shards']->$firstShard; + } + /* MongoDB 4.2 may optimize aggregate pipelines into queries, which can * result in different explain output (see: SERVER-24860) */ - $this->assertThat($results[0], $this->logicalOr( + $this->assertThat($checkResult, $this->logicalOr( $this->arrayHasKey('stages'), - $this->arrayHasKey('queryPlanner') + $this->arrayHasKey('queryPlanner'), )); } @@ -208,15 +219,15 @@ function () use ($pipeline, $options): void { if (isset($result->shards)) { foreach ($result->shards as $shard) { - $this->assertObjectHasAttribute('stages', $shard); + $this->assertObjectHasProperty('stages', $shard); } } else { - $this->assertObjectHasAttribute('stages', $result); + $this->assertObjectHasProperty('stages', $result); } }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); $this->assertCollectionCount($this->getCollectionName() . '.output', 0); @@ -230,15 +241,15 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['$match' => ['x' => 1]]], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -250,18 +261,18 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['$match' => ['x' => 1]]], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } - public function provideTypeMapOptionsAndExpectedDocuments() + public static function provideTypeMapOptionsAndExpectedDocuments() { return [ [ @@ -307,7 +318,7 @@ public function testReadPreferenceWithinTransaction(): void $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -317,7 +328,7 @@ public function testReadPreferenceWithinTransaction(): void $pipeline = [['$match' => ['_id' => ['$lt' => 3]]]]; $options = [ - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'session' => $session, ]; @@ -337,6 +348,30 @@ public function testReadPreferenceWithinTransaction(): void } } + public function testCodecOption(): void + { + $this->createFixtures(3); + + $codec = new TestDocumentCodec(); + + $operation = new Aggregate( + $this->getDatabaseName(), + $this->getCollectionName(), + [['$match' => ['_id' => ['$gt' => 1]]]], + ['codec' => $codec], + ); + + $cursor = $operation->execute($this->getPrimaryServer()); + + $this->assertEquals( + [ + TestObject::createDecodedForFixture(2), + TestObject::createDecodedForFixture(3), + ], + $cursor->toArray(), + ); + } + /** * Create data fixtures. */ @@ -345,10 +380,7 @@ private function createFixtures(int $n, array $executeBulkWriteOptions = []): vo $bulkWrite = new BulkWrite(['ordered' => true]); for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert([ - '_id' => $i, - 'x' => (object) ['foo' => 'bar'], - ]); + $bulkWrite->insert(TestObject::createDocument($i)); } $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite, $executeBulkWriteOptions); diff --git a/tests/Operation/AggregateTest.php b/tests/Operation/AggregateTest.php index db7a62b97..f9befaf36 100644 --- a/tests/Operation/AggregateTest.php +++ b/tests/Operation/AggregateTest.php @@ -2,108 +2,83 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Aggregate; +use PHPUnit\Framework\Attributes\DataProvider; class AggregateTest extends TestCase { public function testConstructorPipelineArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$pipeline is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$pipeline is not a valid aggregation pipeline'); new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [1 => ['$match' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$match' => ['x' => 1]]], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['allowDiskUse' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['batchSize' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidHintValues() as $value) { - $options[][] = ['hint' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['let' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['explain' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxAwaitTimeMS' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['useCursor' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; - } - - public function testConstructorBatchSizeOptionRequiresUseCursor(): void - { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('"batchSize" option should not be used if "useCursor" is false'); - new Aggregate( - $this->getDatabaseName(), - $this->getCollectionName(), - [['$match' => ['x' => 1]]], - ['batchSize' => 100, 'useCursor' => false] - ); + return self::createOptionDataProvider([ + 'allowDiskUse' => self::getInvalidBooleanValues(), + 'batchSize' => self::getInvalidIntegerValues(), + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'let' => self::getInvalidDocumentValues(), + 'explain' => self::getInvalidBooleanValues(), + 'maxAwaitTimeMS' => self::getInvalidIntegerValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } - private function getInvalidHintValues() + public function testExplainableCommandDocument(): void { - return [123, 3.14, true]; + $options = [ + 'allowDiskUse' => true, + 'batchSize' => 100, + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'let' => ['a' => 1], + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + // Intentionally omitted options + // The "explain" option is illegal + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array', 'document' => 'array'], + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), [['$project' => ['_id' => 0]]], $options); + + $expected = [ + 'aggregate' => $this->getCollectionName(), + 'pipeline' => [['$project' => ['_id' => 0]]], + 'allowDiskUse' => true, + 'bypassDocumentValidation' => true, + 'collation' => (object) ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'let' => (object) ['a' => 1], + 'cursor' => ['batchSize' => 100], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/BulkWriteFunctionalTest.php b/tests/Operation/BulkWriteFunctionalTest.php index b935e5501..9fce1c8ac 100644 --- a/tests/Operation/BulkWriteFunctionalTest.php +++ b/tests/Operation/BulkWriteFunctionalTest.php @@ -2,22 +2,27 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\BulkWriteResult; use MongoDB\Collection; use MongoDB\Driver\BulkWrite as Bulk; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Model\BSONDocument; use MongoDB\Operation\BulkWrite; use MongoDB\Tests\CommandObserver; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use stdClass; -use function version_compare; +use function is_array; class BulkWriteFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -57,6 +62,58 @@ public function testInserts(): void $this->assertSameDocuments($expected, $this->collection->find()); } + #[DataProvider('provideDocumentsWithIds')] + #[DataProvider('provideDocumentsWithoutIds')] + public function testInsertDocumentEncoding($document, stdClass $expectedDocument): void + { + (new CommandObserver())->observe( + function () use ($document, $expectedDocument): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [['insertOne' => [$document]]], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $result->getInsertedIds()[0]; + } + }, + function (array $event) use ($expectedDocument): void { + $this->assertEquals($expectedDocument, $event['started']->getCommand()->documents[0] ?? null); + }, + ); + } + + public static function provideDocumentsWithIds(): array + { + $expectedDocument = (object) ['_id' => 1]; + + return [ + 'with_id:array' => [['_id' => 1], $expectedDocument], + 'with_id:object' => [(object) ['_id' => 1], $expectedDocument], + 'with_id:Serializable' => [new BSONDocument(['_id' => 1]), $expectedDocument], + 'with_id:Document' => [Document::fromPHP(['_id' => 1]), $expectedDocument], + ]; + } + + public static function provideDocumentsWithoutIds(): array + { + /* Note: _id placeholders must be replaced with generated ObjectIds. We + * also clone the value for each data set since tests may need to modify + * the object. */ + $expectedDocument = (object) ['_id' => null, 'x' => 1]; + + return [ + 'without_id:array' => [['x' => 1], clone $expectedDocument], + 'without_id:object' => [(object) ['x' => 1], clone $expectedDocument], + 'without_id:Serializable' => [new BSONDocument(['x' => 1]), clone $expectedDocument], + 'without_id:Document' => [Document::fromPHP(['x' => 1]), clone $expectedDocument], + ]; + } + public function testUpdates(): void { $this->createFixtures(4); @@ -93,6 +150,78 @@ public function testUpdates(): void $this->assertSameDocuments($expected, $this->collection->find()); } + #[DataProvider('provideFilterDocuments')] + public function testUpdateFilterDocuments($filter, stdClass $expectedFilter): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['replaceOne' => [$filter, ['x' => 1]]], + ['updateOne' => [$filter, ['$set' => ['x' => 1]]]], + ['updateMany' => [$filter, ['$set' => ['x' => 1]]]], + ], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedFilter): void { + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[0]->q ?? null); + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[1]->q ?? null); + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[2]->q ?? null); + }, + ); + } + + #[DataProvider('provideReplacementDocuments')] + public function testReplacementDocuments($replacement, stdClass $expectedReplacement): void + { + (new CommandObserver())->observe( + function () use ($replacement): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [['replaceOne' => [['x' => 1], $replacement]]], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedReplacement): void { + $this->assertEquals($expectedReplacement, $event['started']->getCommand()->updates[0]->u ?? null); + }, + ); + } + + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + public function testUpdateDocuments($update, $expectedUpdate): void + { + if (is_array($expectedUpdate)) { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); + } + + (new CommandObserver())->observe( + function () use ($update): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['updateOne' => [['x' => 1], $update]], + ['updateMany' => [['x' => 1], $update]], + ], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[0]->u ?? null); + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[1]->u ?? null); + }, + ); + } + public function testDeletes(): void { $this->createFixtures(4); @@ -115,6 +244,29 @@ public function testDeletes(): void $this->assertSameDocuments($expected, $this->collection->find()); } + #[DataProvider('provideFilterDocuments')] + public function testDeleteFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + ['deleteOne' => [$filter]], + ['deleteMany' => [$filter]], + ], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[0]->q ?? null); + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[1]->q ?? null); + }, + ); + } + public function testMixedOrderedOperations(): void { $this->createFixtures(3); @@ -163,63 +315,51 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesDeletedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getDeletedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesMatchedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesModifiedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedCount(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedIds(BulkWriteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedIds(); } @@ -231,14 +371,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['insertOne' => [['_id' => 1]]]], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -250,15 +390,15 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['insertOne' => [['_id' => 1]]]], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -270,22 +410,20 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['insertOne' => [['_id' => 1]]]], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } public function testBulkWriteWithPipelineUpdates(): void { - if (version_compare($this->getServerVersion(), '4.2.0', '<')) { - $this->markTestSkipped('Pipeline-style updates are not supported'); - } + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); $this->createFixtures(4); @@ -311,6 +449,47 @@ public function testBulkWriteWithPipelineUpdates(): void $this->assertSameDocuments($expected, $this->collection->find()); } + public function testCodecOption(): void + { + $this->createFixtures(3); + + $codec = new TestDocumentCodec(); + + $replaceObject = TestObject::createForFixture(3); + $replaceObject->x->foo = 'baz'; + + $operations = [ + ['insertOne' => [TestObject::createForFixture(4)]], + ['replaceOne' => [['_id' => 3], $replaceObject]], + ]; + + $operation = new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + $operations, + ['codec' => $codec], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + $this->assertInstanceOf(BulkWriteResult::class, $result); + $this->assertSame(1, $result->getInsertedCount()); + $this->assertSame(1, $result->getMatchedCount()); + $this->assertSame(1, $result->getModifiedCount()); + + $replacedObject = TestObject::createDecodedForFixture(3); + $replacedObject->x->foo = 'baz'; + + // Only read the last two documents as the other two don't fit our codec + $this->assertEquals( + [ + $replacedObject, + TestObject::createDecodedForFixture(4), + ], + $this->collection->find(['_id' => ['$gte' => 3]], ['codec' => $codec])->toArray(), + ); + } + /** * Create data fixtures. */ @@ -321,7 +500,7 @@ private function createFixtures(int $n): void for ($i = 1; $i <= $n; $i++) { $bulkWrite->insert([ '_id' => $i, - 'x' => (integer) ($i . $i), + 'x' => (int) ($i . $i), ]); } diff --git a/tests/Operation/BulkWriteTest.php b/tests/Operation/BulkWriteTest.php index aafaa4833..30d5c6374 100644 --- a/tests/Operation/BulkWriteTest.php +++ b/tests/Operation/BulkWriteTest.php @@ -2,8 +2,14 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\BulkWrite; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use TypeError; class BulkWriteTest extends TestCase { @@ -17,7 +23,7 @@ public function testOperationsMustNotBeEmpty(): void public function testOperationsMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$operations is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$operations is not a list'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ 1 => [BulkWrite::INSERT_ONE => [['x' => 1]]], ]); @@ -53,18 +59,28 @@ public function testInsertOneDocumentArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testInsertOneDocumentArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["insertOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["insertOne"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::INSERT_ONE => [$document]], ]); } + public function testInsertOneWithCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [[BulkWrite::INSERT_ONE => [['x' => 1]]]], + ['codec' => new TestDocumentCodec()], + ); + } + public function testDeleteManyFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -74,35 +90,26 @@ public function testDeleteManyFilterArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testDeleteManyFilterArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::DELETE_MANY => [$document]], ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testDeleteManyCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteMany"\]\[1\]\["collation"\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::DELETE_MANY => [['x' => 1], ['collation' => $collation]]], ]); } - public function provideInvalidDocumentValues() - { - return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues()); - } - public function testDeleteOneFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -112,25 +119,21 @@ public function testDeleteOneFilterArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testDeleteOneFilterArgumentTypeCheck($document): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::DELETE_ONE => [$document]], ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testDeleteOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[1\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["deleteOne"\]\[1\]\["collation"\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::DELETE_ONE => [['x' => 1], ['collation' => $collation]]], ]); @@ -145,18 +148,25 @@ public function testReplaceOneFilterArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testReplaceOneFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::REPLACE_ONE => [$filter, ['y' => 1]]], ]); } + #[DataProvider('provideReplacementDocuments')] + #[DoesNotPerformAssertions] + public function testReplaceOneReplacementArgument($replacement): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], + ]); + } + public function testReplaceOneReplacementArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -166,54 +176,82 @@ public function testReplaceOneReplacementArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testReplaceOneReplacementArgumentTypeCheck($replacement): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[1\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[1\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], ]); } - public function testReplaceOneReplacementArgumentRequiresNoOperators(): void + #[DataProvider('provideUpdateDocuments')] + public function testReplaceOneReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('First key in $operations[0]["replaceOne"][1] is an update operator'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::REPLACE_ONE => [['_id' => 1], ['$inc' => ['x' => 1]]]], + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideUpdatePipelines')] + #[DataProvider('provideEmptyUpdatePipelinesExcludingArray')] + public function testReplaceOneReplacementArgumentProhibitsUpdatePipeline($replacement): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('#(\$operations\[0\]\["replaceOne"\]\[1\] is an update pipeline)|(\$operations\[0\]\["replaceOne"\]\[1\] to have type "document" \(array or object\))#'); + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::REPLACE_ONE => [['x' => 1], $replacement]], + ]); + } + + #[DataProvider('provideInvalidDocumentValues')] public function testReplaceOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["collation"\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1], ['collation' => $collation]]], ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ + #[DataProvider('provideInvalidDocumentValues')] + public function testReplaceOneSortOptionTypeCheck($sort): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["sort"\] to have type "document" \(array or object\) but found ".+"/'); + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1], ['sort' => $sort]]], + ]); + } + + #[DataProvider('provideInvalidBooleanValues')] public function testReplaceOneUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["replaceOne"\]\[2\]\["upsert"\] to have type "boolean" but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1], ['upsert' => $upsert]]], ]); } - public function provideInvalidBooleanValues() + public static function provideInvalidBooleanValues() { - return $this->wrapValuesForDataProvider($this->getInvalidBooleanValues()); + return self::wrapValuesForDataProvider(self::getInvalidBooleanValues()); + } + + public function testReplaceOneWithCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new BulkWrite( + $this->getDatabaseName(), + $this->getCollectionName(), + [[BulkWrite::REPLACE_ONE => [['x' => 1], ['y' => 1]]]], + ['codec' => new TestDocumentCodec()], + ); } public function testUpdateManyFilterArgumentMissing(): void @@ -225,18 +263,26 @@ public function testUpdateManyFilterArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateManyFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_MANY => [$filter, ['$set' => ['x' => 1]]]], ]); } + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DoesNotPerformAssertions] + public function testUpdateManyUpdateArgument($update): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_MANY => [['x' => 1], $update]], + ]); + } + public function testUpdateManyUpdateArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -246,63 +292,75 @@ public function testUpdateManyUpdateArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateManyUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[1\] to have type "array or object" but found "[\w ]+"/'); + $this->expectException($update instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_MANY => [['x' => 1], $update]], ]); } - public function testUpdateManyUpdateArgumentRequiresOperatorsOrPipeline(): void + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testUpdateManyUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $operations[0]["updateMany"][1] is neither an update operator nor a pipeline'); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $operations[0]["updateMany"][1]'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::UPDATE_MANY => [['_id' => ['$gt' => 1]], ['x' => 1]]], + [BulkWrite::UPDATE_MANY => [['x' => 1], $update]], ]); } - /** - * @dataProvider provideInvalidArrayValues - */ + #[DataProvider('provideInvalidArrayValues')] public function testUpdateManyArrayFiltersOptionTypeCheck($arrayFilters): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["arrayFilters"\] to have type "array" but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['arrayFilters' => $arrayFilters]]], ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateManyCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["collation"\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]], ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ + public function testUpdateManyProhibitsSortOption(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"sort" option cannot be used with $operations[0]["updateMany"]'); + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['y' => 1]], ['sort' => ['z' => 1]]]], + ]); + } + + #[DataProvider('provideInvalidBooleanValues')] public function testUpdateManyUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateMany"\]\[2\]\["upsert"\] to have type "boolean" but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_MANY => [['x' => 1], ['$set' => ['x' => 1]], ['upsert' => $upsert]]], ]); } + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DoesNotPerformAssertions] + public function testUpdateOneUpdateArgument($update): void + { + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_ONE => [['x' => 1], $update]], + ]); + } + public function testUpdateOneFilterArgumentMissing(): void { $this->expectException(InvalidArgumentException::class); @@ -312,13 +370,11 @@ public function testUpdateOneFilterArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateOneFilterArgumentTypeCheck($filter): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[0\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[0\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_ONE => [$filter, ['$set' => ['x' => 1]]]], ]); @@ -333,66 +389,67 @@ public function testUpdateOneUpdateArgumentMissing(): void ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateOneUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[1\] to have type "array or object" but found "[\w ]+"/'); + $this->expectException($update instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_ONE => [['x' => 1], $update]], ]); } - public function testUpdateOneUpdateArgumentRequiresOperatorsOrPipeline(): void + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testUpdateOneUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $operations[0]["updateOne"][1] is neither an update operator nor a pipeline'); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $operations[0]["updateOne"][1]'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ - [BulkWrite::UPDATE_ONE => [['_id' => 1], ['x' => 1]]], + [BulkWrite::UPDATE_ONE => [['x' => 1], $update]], ]); } - /** - * @dataProvider provideInvalidArrayValues - */ + #[DataProvider('provideInvalidArrayValues')] public function testUpdateOneArrayFiltersOptionTypeCheck($arrayFilters): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["arrayFilters"\] to have type "array" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["arrayFilters"\] to have type "array" but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['arrayFilters' => $arrayFilters]]], ]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testUpdateOneCollationOptionTypeCheck($collation): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "array or object" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["collation"\] to have type "document" \(array or object\) but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['collation' => $collation]]], ]); } - /** - * @dataProvider provideInvalidBooleanValues - */ + #[DataProvider('provideInvalidDocumentValues')] + public function testUpdateOneSortOptionTypeCheck($sort): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["sort"\] to have type "document" \(array or object\) but found ".+"/'); + new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ + [BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['y' => 1]], ['sort' => $sort]]], + ]); + } + + #[DataProvider('provideInvalidBooleanValues')] public function testUpdateOneUpsertOptionTypeCheck($upsert): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found "[\w ]+"/'); + $this->expectExceptionMessageMatches('/Expected \$operations\[0\]\["updateOne"\]\[2\]\["upsert"\] to have type "boolean" but found ".+"/'); new BulkWrite($this->getDatabaseName(), $this->getCollectionName(), [ [BulkWrite::UPDATE_ONE => [['x' => 1], ['$set' => ['x' => 1]], ['upsert' => $upsert]]], ]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); @@ -400,30 +457,18 @@ public function testConstructorOptionTypeChecks(array $options): void $this->getDatabaseName(), $this->getCollectionName(), [[BulkWrite::INSERT_ONE => [['x' => 1]]]], - $options + $options, ); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['ordered' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'ordered' => self::getInvalidBooleanValues(true), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/CountDocumentsFunctionalTest.php b/tests/Operation/CountDocumentsFunctionalTest.php index aaec28480..be6c64bbd 100644 --- a/tests/Operation/CountDocumentsFunctionalTest.php +++ b/tests/Operation/CountDocumentsFunctionalTest.php @@ -4,9 +4,31 @@ use MongoDB\Operation\CountDocuments; use MongoDB\Operation\InsertMany; +use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; +use stdClass; class CountDocumentsFunctionalTest extends FunctionalTestCase { + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedMatchStage): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new CountDocuments( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedMatchStage): void { + $this->assertEquals($expectedMatchStage, $event['started']->getCommand()->pipeline[0]->{'$match'} ?? null); + }, + ); + } + public function testEmptyCollection(): void { $operation = new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), []); diff --git a/tests/Operation/CountDocumentsTest.php b/tests/Operation/CountDocumentsTest.php index 650d45af2..44ee49825 100644 --- a/tests/Operation/CountDocumentsTest.php +++ b/tests/Operation/CountDocumentsTest.php @@ -2,70 +2,39 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\CountDocuments; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class CountDocumentsTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CountDocuments($this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() - { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidHintValues() as $value) { - $options[][] = ['hint' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['limit' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['skip' => $value]; - } - - return $options; - } - - private function getInvalidHintValues() + public static function provideInvalidConstructorOptions() { - return [123, 3.14, true]; + return self::createOptionDataProvider([ + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'limit' => self::getInvalidIntegerValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'skip' => self::getInvalidIntegerValues(), + ]); } } diff --git a/tests/Operation/CountFunctionalTest.php b/tests/Operation/CountFunctionalTest.php index 550dcbe00..58326a01a 100644 --- a/tests/Operation/CountFunctionalTest.php +++ b/tests/Operation/CountFunctionalTest.php @@ -6,9 +6,30 @@ use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\InsertMany; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; +use stdClass; class CountFunctionalTest extends FunctionalTestCase { + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Count( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + }, + ); + } + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( @@ -17,14 +38,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [], - ['readConcern' => $this->createDefaultReadConcern()] + ['readConcern' => $this->createDefaultReadConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } @@ -73,14 +94,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/CountTest.php b/tests/Operation/CountTest.php index 623717516..b8a5d357e 100644 --- a/tests/Operation/CountTest.php +++ b/tests/Operation/CountTest.php @@ -2,70 +2,67 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\ReadConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Count; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class CountTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new Count($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Count($this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidHintValues() as $value) { - $options[][] = ['hint' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['limit' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['skip' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'limit' => self::getInvalidIntegerValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'skip' => self::getInvalidIntegerValues(), + ]); } - private function getInvalidHintValues() + public function testExplainableCommandDocument(): void { - return [123, 3.14, true]; + $options = [ + 'hint' => '_id_', + 'limit' => 10, + 'skip' => 20, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'maxTimeMS' => 100, + ]; + $operation = new Count($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); + + $expected = [ + 'count' => $this->getCollectionName(), + 'query' => (object) ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'hint' => '_id_', + 'comment' => 'explain me', + 'limit' => 10, + 'skip' => 20, + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/CreateCollectionFunctionalTest.php b/tests/Operation/CreateCollectionFunctionalTest.php index aa9d9e592..023a67e60 100644 --- a/tests/Operation/CreateCollectionFunctionalTest.php +++ b/tests/Operation/CreateCollectionFunctionalTest.php @@ -14,14 +14,14 @@ function (): void { $operation = new CreateCollection( $this->getDatabaseName(), $this->getCollectionName(), - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -32,14 +32,14 @@ function (): void { $operation = new CreateCollection( $this->getDatabaseName(), $this->getCollectionName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/CreateCollectionTest.php b/tests/Operation/CreateCollectionTest.php index 64b2f35db..4ec92b32f 100644 --- a/tests/Operation/CreateCollectionTest.php +++ b/tests/Operation/CreateCollectionTest.php @@ -4,128 +4,46 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\CreateCollection; +use PHPUnit\Framework\Attributes\DataProvider; class CreateCollectionTest extends TestCase { public function testConstructorPipelineOptionMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('The "pipeline" option is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('"pipeline" option is not a valid aggregation pipeline'); new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['pipeline' => [1 => ['$match' => ['x' => 1]]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['autoIndexId' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['capped' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['changeStreamPreAndPostImages' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['clusteredIndex' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['encryptedFields' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['expireAfterSeconds' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['flags' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['indexOptionDefaults' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['max' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['pipeline' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['size' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['storageEngine' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['timeseries' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['validationAction' => $value]; - } - - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['validationLevel' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['validator' => $value]; - } - - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['viewOn' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; - } - - public function testAutoIndexIdOptionIsDeprecated(): void - { - $this->assertDeprecated(function (): void { - new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => true]); - }); - - $this->assertDeprecated(function (): void { - new CreateCollection($this->getDatabaseName(), $this->getCollectionName(), ['autoIndexId' => false]); - }); + return self::createOptionDataProvider([ + 'capped' => self::getInvalidBooleanValues(), + 'changeStreamPreAndPostImages' => self::getInvalidDocumentValues(), + 'clusteredIndex' => self::getInvalidDocumentValues(), + 'collation' => self::getInvalidDocumentValues(), + 'encryptedFields' => self::getInvalidDocumentValues(), + 'expireAfterSeconds' => self::getInvalidIntegerValues(), + 'indexOptionDefaults' => self::getInvalidDocumentValues(), + 'max' => self::getInvalidIntegerValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'pipeline' => self::getInvalidArrayValues(), + 'session' => self::getInvalidSessionValues(), + 'size' => self::getInvalidIntegerValues(), + 'storageEngine' => self::getInvalidDocumentValues(), + 'timeseries' => self::getInvalidDocumentValues(), + 'validationAction' => self::getInvalidStringValues(), + 'validationLevel' => self::getInvalidStringValues(), + 'validator' => self::getInvalidDocumentValues(), + 'viewOn' => self::getInvalidStringValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/CreateEncryptedCollectionFunctionalTest.php b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php new file mode 100644 index 000000000..7c0cb644f --- /dev/null +++ b/tests/Operation/CreateEncryptedCollectionFunctionalTest.php @@ -0,0 +1,205 @@ +skipIfClientSideEncryptionIsNotSupported(); + + if ($this->isStandalone()) { + $this->markTestSkipped('Queryable Encryption requires replica sets'); + } + + $this->skipIfServerVersion('<', '6.0.0', 'Queryable Encryption requires MongoDB 6.0 or later'); + + $client = static::createTestClient(); + + /* Since test cases may create encrypted collections, ensure that the + * collection and any "enxcol_" collections do not exist. Specify + * "encryptedFields" to ensure "enxcol_" collections are handled. */ + $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->drop(['encryptedFields' => []]); + + // Drop the key vault with a majority write concern + $client->selectCollection('keyvault', 'datakeys')->drop(['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], + ], + ]); + } + + #[DataProvider('provideEncryptedFieldsAndFieldsIsMissing')] + public function testCreateDataKeysNopIfFieldsIsMissing($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input], + ); + + $encryptedFieldsOutput = $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public static function provideEncryptedFieldsAndFieldsIsMissing(): array + { + $ef = []; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument($ef), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + #[DataProvider('provideEncryptedFieldsAndFieldsHasInvalidType')] + public function testCreateDataKeysNopIfFieldsHasInvalidType($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input], + ); + + $encryptedFieldsOutput = $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public static function provideEncryptedFieldsAndFieldsHasInvalidType(): array + { + $ef = ['fields' => 'not-an-array']; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument($ef), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + #[DataProvider('provideEncryptedFieldsElementHasInvalidType')] + public function testCreateDataKeysSkipsNonDocumentFields($input, array $expectedOutput): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input], + ); + + $encryptedFieldsOutput = $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + ); + + $this->assertSame($expectedOutput, $encryptedFieldsOutput); + } + + public static function provideEncryptedFieldsElementHasInvalidType(): array + { + $ef = ['fields' => ['not-an-array-or-object']]; + + return [ + 'array' => [$ef, $ef], + 'object' => [(object) $ef, $ef], + 'Serializable' => [new BSONDocument(['fields' => new BSONArray(['not-an-array-or-object'])]), $ef], + 'Document' => [Document::fromPHP($ef), $ef], + ]; + } + + public function testCreateDataKeysDoesNotModifyOriginalEncryptedFieldsOption(): void + { + $originalField = (object) ['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]; + $originalEncryptedFields = (object) ['fields' => [$originalField]]; + + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $originalEncryptedFields], + ); + + $modifiedEncryptedFields = $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + ); + + $this->assertSame($originalField, $originalEncryptedFields->fields[0]); + $this->assertNull($originalField->keyId); + + $this->assertInstanceOf(Binary::class, $modifiedEncryptedFields['fields'][0]['keyId'] ?? null); + } + + #[DataProvider('provideEncryptedFields')] + public function testEncryptedFieldsDocuments($input): void + { + $operation = new CreateEncryptedCollection( + $this->getDatabaseName(), + $this->getCollectionName(), + ['encryptedFields' => $input], + ); + + $modifiedEncryptedFields = $operation->createDataKeys( + $this->clientEncryption, + 'local', + null, + ); + + $this->assertInstanceOf(Binary::class, $modifiedEncryptedFields['fields'][0]['keyId'] ?? null); + } + + public static function provideEncryptedFields(): array + { + $ef = ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]; + + return [ + 'array' => [$ef], + 'object' => [(object) $ef], + 'Serializable' => [new BSONDocument(['fields' => new BSONArray([new BSONDocument($ef['fields'][0])])])], + 'Document' => [Document::fromPHP($ef)], + ]; + } + + public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client + { + if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { + $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); + } + + return parent::createTestClient($uri, $options, $driverOptions); + } +} diff --git a/tests/Operation/CreateEncryptedCollectionTest.php b/tests/Operation/CreateEncryptedCollectionTest.php new file mode 100644 index 000000000..b5c8bf4ea --- /dev/null +++ b/tests/Operation/CreateEncryptedCollectionTest.php @@ -0,0 +1,29 @@ +expectException(InvalidArgumentException::class); + new CreateEncryptedCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + } + + public static function provideInvalidConstructorOptions(): Generator + { + yield 'encryptedFields is required' => [ + [], + ]; + + yield from self::createOptionDataProvider([ + 'encryptedFields' => self::getInvalidDocumentValues(), + ]); + } +} diff --git a/tests/Operation/CreateIndexesFunctionalTest.php b/tests/Operation/CreateIndexesFunctionalTest.php index d3d1b8e63..a90eaec75 100644 --- a/tests/Operation/CreateIndexesFunctionalTest.php +++ b/tests/Operation/CreateIndexesFunctionalTest.php @@ -3,18 +3,20 @@ namespace MongoDB\Tests\Operation; use InvalidArgumentException; +use MongoDB\BSON\Document; use MongoDB\Driver\Exception\RuntimeException; use MongoDB\Driver\Server; use MongoDB\Exception\UnsupportedException; +use MongoDB\Model\BSONDocument; use MongoDB\Model\IndexInfo; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; use function call_user_func; use function is_callable; use function sprintf; -use function version_compare; class CreateIndexesFunctionalTest extends FunctionalTestCase { @@ -78,15 +80,16 @@ public function testCreateTTLIndex(): void }); } - public function testCreateIndexes(): void + #[DataProvider('provideKeyCasts')] + public function testCreateIndexes(callable $cast): void { $expectedNames = ['x_1', 'y_-1_z_1', 'g_2dsphere_z_1', 'my_ttl']; $indexes = [ - ['key' => ['x' => 1], 'sparse' => true, 'unique' => true], - ['key' => ['y' => -1, 'z' => 1]], - ['key' => ['g' => '2dsphere', 'z' => 1]], - ['key' => ['t' => 1], 'expireAfterSeconds' => 0, 'name' => 'my_ttl'], + ['key' => $cast(['x' => 1]), 'sparse' => true, 'unique' => true], + ['key' => $cast(['y' => -1, 'z' => 1])], + ['key' => $cast(['g' => '2dsphere', 'z' => 1])], + ['key' => $cast(['t' => 1]), 'expireAfterSeconds' => 0, 'name' => 'my_ttl'], ]; $operation = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), $indexes); @@ -119,6 +122,20 @@ public function testCreateIndexes(): void }); } + public static function provideKeyCasts(): array + { + // phpcs:disable SlevomatCodingStandard.ControlStructures.JumpStatementsSpacing + // phpcs:disable Squiz.Functions.MultiLineFunctionDeclaration + // phpcs:disable Squiz.WhiteSpace.ScopeClosingBrace.ContentBefore + return [ + 'array' => [fn ($key) => $key], + 'object' => [fn ($key) => (object) $key], + 'Serializable' => [fn ($key) => new BSONDocument($key)], + 'Document' => [fn ($key) => Document::fromPHP($key)], + ]; + // phpcs:enable + } + public function testCreateConflictingIndexesWithCommand(): void { $indexes = [ @@ -140,14 +157,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -159,22 +176,20 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } public function testCommitQuorumOption(): void { - if (version_compare($this->getServerVersion(), '4.3.4', '<')) { - $this->markTestSkipped('commitQuorum is not supported'); - } + $this->skipIfServerVersion('<', '4.3.4', 'commitQuorum is not supported'); if ($this->getPrimaryServer()->getType() !== Server::TYPE_RS_PRIMARY) { $this->markTestSkipped('commitQuorum is only supported on replica sets'); @@ -186,28 +201,26 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], - ['commitQuorum' => 'majority'] + ['commitQuorum' => 'majority'], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('commitQuorum', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('commitQuorum', $event['started']->getCommand()); + }, ); } public function testCommitQuorumUnsupported(): void { - if (version_compare($this->getServerVersion(), '4.3.4', '>=')) { - $this->markTestSkipped('commitQuorum is supported'); - } + $this->skipIfServerVersion('>=', '4.3.4', 'commitQuorum is supported'); $operation = new CreateIndexes( $this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], - ['commitQuorum' => 'majority'] + ['commitQuorum' => 'majority'], ); $this->expectException(UnsupportedException::class); diff --git a/tests/Operation/CreateIndexesTest.php b/tests/Operation/CreateIndexesTest.php index 1604b0c72..5db02f67f 100644 --- a/tests/Operation/CreateIndexesTest.php +++ b/tests/Operation/CreateIndexesTest.php @@ -2,8 +2,12 @@ namespace MongoDB\Tests\Operation; +use Generator; +use MongoDB\BSON\Document; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\CreateIndexes; +use PHPUnit\Framework\Attributes\DataProvider; use stdClass; class CreateIndexesTest extends TestCase @@ -11,40 +15,26 @@ class CreateIndexesTest extends TestCase public function testConstructorIndexesArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$indexes is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$indexes is not a list'); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [1 => ['key' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1]]], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ([3.14, true, [], new stdClass()] as $value) { - $options[][] = ['commitQuorum' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + // commitQuorum is int|string, for which no helper exists + 'commitQuorum' => ['float' => 3.14, 'bool' => true, 'array' => [], 'object' => new stdClass()], + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } public function testConstructorRequiresAtLeastOneIndex(): void @@ -54,17 +44,59 @@ public function testConstructorRequiresAtLeastOneIndex(): void new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), []); } - /** - * @dataProvider provideInvalidIndexSpecificationTypes - */ + #[DataProvider('provideInvalidIndexSpecificationTypes')] public function testConstructorRequiresIndexSpecificationsToBeAnArray($index): void { $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected $index[0] to have type "array"'); new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [$index]); } - public function provideInvalidIndexSpecificationTypes() + public static function provideInvalidIndexSpecificationTypes() + { + return self::wrapValuesForDataProvider(self::getInvalidArrayValues()); + } + + public function testConstructorRequiresIndexSpecificationKey(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Required "key" document is missing from index specification'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [[]]); + } + + #[DataProvider('provideInvalidDocumentValues')] + public function testConstructorRequiresIndexSpecificationKeyToBeADocument($key): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "key" option to have type "document" (array or object)'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => $key]]); + } + + #[DataProvider('provideKeyDocumentsWithInvalidOrder')] + public function testConstructorValidatesIndexSpecificationKeyOrder($key): void { - return $this->wrapValuesForDataProvider($this->getInvalidArrayValues()); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected order value for "x" field within "key" option to have type "numeric or string"'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => $key]]); + } + + public static function provideKeyDocumentsWithInvalidOrder(): Generator + { + $invalidOrderValues = [true, [], new stdClass(), null]; + + foreach ($invalidOrderValues as $order) { + yield [['x' => $order]]; + yield [(object) ['x' => $order]]; + yield [new BSONDocument(['x' => $order])]; + yield [Document::fromPHP(['x' => $order])]; + } + } + + #[DataProvider('provideInvalidStringValues')] + public function testConstructorRequiresIndexSpecificationNameToBeString($name): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "name" option to have type "string"'); + new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [['key' => ['x' => 1], 'name' => $name]]); } } diff --git a/tests/Operation/CreateSearchIndexesTest.php b/tests/Operation/CreateSearchIndexesTest.php new file mode 100644 index 000000000..d55915048 --- /dev/null +++ b/tests/Operation/CreateSearchIndexesTest.php @@ -0,0 +1,56 @@ +expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('$indexes is not a list'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [1 => ['name' => 'index name', 'definition' => ['mappings' => ['dynamic' => true]]]], []); + } + + #[DataProvider('provideInvalidArrayValues')] + public function testConstructorIndexDefinitionMustBeADocument($index): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected $indexes[0] to have type "array"'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [$index], []); + } + + #[DataProvider('provideInvalidStringValues')] + public function testConstructorIndexNameMustBeAString($name): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "name" option to have type "string"'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [['name' => $name, 'definition' => ['mappings' => ['dynamic' => true]]]], []); + } + + #[DataProvider('provideInvalidStringValues')] + public function testConstructorIndexTypeMustBeAString($type): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "type" option to have type "string"'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [['type' => $type, 'definition' => ['mappings' => ['dynamic' => true]]]], []); + } + + public function testConstructorIndexDefinitionMustBeDefined(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Required "definition" document is missing from search index specification'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [['name' => 'index name']], []); + } + + #[DataProvider('provideInvalidDocumentValues')] + public function testConstructorIndexDefinitionMustBeAnArray($definition): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('Expected "definition" option to have type "document"'); + new CreateSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), [['definition' => $definition]], []); + } +} diff --git a/tests/Operation/DatabaseCommandFunctionalTest.php b/tests/Operation/DatabaseCommandFunctionalTest.php index eee2f2bd2..abed3b2c1 100644 --- a/tests/Operation/DatabaseCommandFunctionalTest.php +++ b/tests/Operation/DatabaseCommandFunctionalTest.php @@ -2,11 +2,47 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\Driver\Command; +use MongoDB\Model\BSONDocument; use MongoDB\Operation\DatabaseCommand; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; class DatabaseCommandFunctionalTest extends FunctionalTestCase { + #[DataProvider('provideCommandDocuments')] + public function testCommandDocuments($command): void + { + (new CommandObserver())->observe( + function () use ($command): void { + $operation = new DatabaseCommand( + $this->getDatabaseName(), + $command, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertEquals(1, $event['started']->getCommand()->ping ?? null); + }, + ); + } + + public static function provideCommandDocuments(): array + { + return [ + 'array' => [['ping' => 1]], + 'object' => [(object) ['ping' => 1]], + 'Serializable' => [new BSONDocument(['ping' => 1])], + 'Document' => [Document::fromPHP(['ping' => 1])], + 'Command:array' => [new Command(['ping' => 1])], + 'Command:object' => [new Command((object) ['ping' => 1])], + 'Command:Serializable' => [new Command(new BSONDocument(['ping' => 1]))], + 'Command:Document' => [new Command(Document::fromPHP(['ping' => 1]))], + ]; + } + public function testSessionOption(): void { (new CommandObserver())->observe( @@ -14,14 +50,14 @@ function (): void { $operation = new DatabaseCommand( $this->getDatabaseName(), ['ping' => 1], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/DatabaseCommandTest.php b/tests/Operation/DatabaseCommandTest.php index ffd3afa1f..c579ea5b2 100644 --- a/tests/Operation/DatabaseCommandTest.php +++ b/tests/Operation/DatabaseCommandTest.php @@ -2,45 +2,34 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\DatabaseCommand; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class DatabaseCommandTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorCommandArgumentTypeCheck($command): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($command instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new DatabaseCommand($this->getDatabaseName(), $command); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DatabaseCommand($this->getDatabaseName(), ['ping' => 1], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'typeMap' => self::getInvalidArrayValues(), + ]); } } diff --git a/tests/Operation/DeleteFunctionalTest.php b/tests/Operation/DeleteFunctionalTest.php index d97d53474..2aceafa1b 100644 --- a/tests/Operation/DeleteFunctionalTest.php +++ b/tests/Operation/DeleteFunctionalTest.php @@ -5,18 +5,18 @@ use MongoDB\Collection; use MongoDB\DeleteResult; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Delete; use MongoDB\Tests\CommandObserver; - -use function version_compare; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use stdClass; class DeleteFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -25,6 +25,26 @@ public function setUp(): void $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Delete( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + 1, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->deletes[0]->q ?? null); + }, + ); + } + public function testDeleteOne(): void { $this->createFixtures(3); @@ -66,16 +86,14 @@ public function testDeleteMany(): void public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.4.0', '>=')) { - $this->markTestSkipped('hint is supported'); - } + $this->skipIfServerVersion('>=', '4.4.0', 'hint is supported'); $operation = new Delete( $this->getDatabaseName(), $this->getCollectionName(), [], 0, - ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)], ); $this->expectException(UnsupportedException::class); @@ -93,14 +111,14 @@ function (): void { $this->getCollectionName(), [], 0, - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -117,13 +135,11 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesDeletedCount(DeleteResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getDeletedCount(); } @@ -137,7 +153,7 @@ private function createFixtures(int $n): void for ($i = 1; $i <= $n; $i++) { $bulkWrite->insert([ '_id' => $i, - 'x' => (integer) ($i . $i), + 'x' => (int) ($i . $i), ]); } diff --git a/tests/Operation/DeleteTest.php b/tests/Operation/DeleteTest.php index 859721f6d..e1cd5bd74 100644 --- a/tests/Operation/DeleteTest.php +++ b/tests/Operation/DeleteTest.php @@ -7,33 +7,30 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Delete; +use PHPUnit\Framework\Attributes\DataProvider; use TypeError; class DeleteTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new Delete($this->getDatabaseName(), $this->getCollectionName(), $filter, 0); } - /** - * @dataProvider provideInvalidIntegerValues - */ + #[DataProvider('provideInvalidIntegerValues')] public function testConstructorLimitArgumentMustBeInt($limit): void { $this->expectException(TypeError::class); new Delete($this->getDatabaseName(), $this->getCollectionName(), [], $limit); } - /** - * @dataProvider provideInvalidLimitValues - */ + #[DataProvider('provideInvalidLimitValues')] public function testConstructorLimitArgumentMustBeOneOrZero($limit): void { $this->expectException(InvalidArgumentException::class); @@ -41,36 +38,54 @@ public function testConstructorLimitArgumentMustBeOneOrZero($limit): void new Delete($this->getDatabaseName(), $this->getCollectionName(), [], $limit); } - public function provideInvalidLimitValues() + public static function provideInvalidLimitValues() { - return $this->wrapValuesForDataProvider([-1, 2]); + return self::wrapValuesForDataProvider([-1, 2]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Delete($this->getDatabaseName(), $this->getCollectionName(), [], 1, $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } + return self::createOptionDataProvider([ + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'let' => self::getInvalidDocumentValues(), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); + } - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'hint' => '_id_', + 'let' => ['a' => 1], + 'comment' => 'explain me', + // Intentionally omitted options + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new Delete($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], 0, $options); - return $options; + $expected = [ + 'delete' => $this->getCollectionName(), + 'deletes' => [ + [ + 'q' => ['x' => 1], + 'limit' => 0, + 'collation' => (object) ['locale' => 'fr'], + 'hint' => '_id_', + ], + ], + 'comment' => 'explain me', + 'let' => (object) ['a' => 1], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/DistinctFunctionalTest.php b/tests/Operation/DistinctFunctionalTest.php index 8c02b80a0..5106f881d 100644 --- a/tests/Operation/DistinctFunctionalTest.php +++ b/tests/Operation/DistinctFunctionalTest.php @@ -3,15 +3,42 @@ namespace MongoDB\Tests\Operation; use MongoDB\Driver\BulkWrite; +use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\Distinct; +use MongoDB\Operation\InsertMany; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; +use stdClass; use function is_scalar; use function json_encode; +use function sort; use function usort; +use const JSON_THROW_ON_ERROR; + class DistinctFunctionalTest extends FunctionalTestCase { + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Distinct( + $this->getDatabaseName(), + $this->getCollectionName(), + 'x', + $filter, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + }, + ); + } + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( @@ -21,17 +48,58 @@ function (): void { $this->getCollectionName(), 'x', [], - ['readConcern' => $this->createDefaultReadConcern()] + ['readConcern' => $this->createDefaultReadConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } + public function testHintOption(): void + { + $this->skipIfServerVersion('<', '7.1.0', 'hint is not supported'); + + $insertMany = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [ + ['x' => 1], + ['x' => 2, 'y' => 2], + ['y' => 3], + ]); + $insertMany->execute($this->getPrimaryServer()); + + $createIndexes = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), [ + ['key' => ['x' => 1], 'sparse' => true, 'name' => 'sparse_x'], + ['key' => ['y' => 1]], + ]); + $createIndexes->execute($this->getPrimaryServer()); + + $hintsUsingSparseIndex = [ + ['x' => 1], + 'sparse_x', + ]; + + foreach ($hintsUsingSparseIndex as $hint) { + $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'y', [], ['hint' => $hint]); + $this->assertSame([2], $operation->execute($this->getPrimaryServer())); + } + + $hintsNotUsingSparseIndex = [ + ['_id' => 1], + ['y' => 1], + 'y_1', + ]; + + foreach ($hintsNotUsingSparseIndex as $hint) { + $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', [], ['hint' => $hint]); + $values = $operation->execute($this->getPrimaryServer()); + sort($values); + $this->assertSame([1, 2], $values); + } + } + public function testSessionOption(): void { (new CommandObserver())->observe( @@ -41,20 +109,18 @@ function (): void { $this->getCollectionName(), 'x', [], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] public function testTypeMapOption(array $typeMap, array $expectedDocuments): void { $bulkWrite = new BulkWrite(['ordered' => true]); @@ -84,8 +150,8 @@ public function testTypeMapOption(array $typeMap, array $expectedDocuments): voi return 1; } - $a = json_encode($a); - $b = json_encode($b); + $a = json_encode($a, JSON_THROW_ON_ERROR); + $b = json_encode($b, JSON_THROW_ON_ERROR); } return $a < $b ? -1 : 1; @@ -97,7 +163,7 @@ public function testTypeMapOption(array $typeMap, array $expectedDocuments): voi $this->assertEquals($expectedDocuments, $values); } - public function provideTypeMapOptionsAndExpectedDocuments() + public static function provideTypeMapOptionsAndExpectedDocuments() { return [ 'No type map' => [ diff --git a/tests/Operation/DistinctTest.php b/tests/Operation/DistinctTest.php index dc168bf91..c8cffc337 100644 --- a/tests/Operation/DistinctTest.php +++ b/tests/Operation/DistinctTest.php @@ -2,57 +2,67 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Distinct; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class DistinctTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } + return self::createOptionDataProvider([ + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'typeMap' => self::getInvalidArrayValues(), + ]); + } - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'comment' => 'explain me', + // Intentionally omitted options + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array'], + ]; + $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'f', ['x' => 1], $options); - return $options; + $expected = [ + 'distinct' => $this->getCollectionName(), + 'key' => 'f', + 'query' => (object) ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'hint' => '_id_', + 'comment' => 'explain me', + 'maxTimeMS' => 100, + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/DropCollectionFunctionalTest.php b/tests/Operation/DropCollectionFunctionalTest.php index 29cd9ead4..3d2015521 100644 --- a/tests/Operation/DropCollectionFunctionalTest.php +++ b/tests/Operation/DropCollectionFunctionalTest.php @@ -5,6 +5,7 @@ use MongoDB\Operation\DropCollection; use MongoDB\Operation\InsertOne; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\Depends; class DropCollectionFunctionalTest extends FunctionalTestCase { @@ -15,14 +16,14 @@ function (): void { $operation = new DropCollection( $this->getDatabaseName(), $this->getCollectionName(), - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -35,25 +36,18 @@ public function testDropExistingCollection(): void $this->assertEquals(1, $writeResult->getInsertedCount()); $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $commandResult = $operation->execute($server); + $operation->execute($server); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); } - /** - * @depends testDropExistingCollection - */ + #[Depends('testDropExistingCollection')] public function testDropNonexistentCollection(): void { $this->assertCollectionDoesNotExist($this->getCollectionName()); $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $commandResult = $operation->execute($this->getPrimaryServer()); - - /* Avoid inspecting the result document as mongos returns {ok:1.0}, - * which is inconsistent from the expected mongod response of {ok:0}. */ - $this->assertIsObject($commandResult); + $operation->execute($this->getPrimaryServer()); } public function testSessionOption(): void @@ -63,14 +57,14 @@ function (): void { $operation = new DropCollection( $this->getDatabaseName(), $this->getCollectionName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/DropCollectionTest.php b/tests/Operation/DropCollectionTest.php index 45042e6b9..5aa580520 100644 --- a/tests/Operation/DropCollectionTest.php +++ b/tests/Operation/DropCollectionTest.php @@ -4,34 +4,22 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\DropCollection; +use PHPUnit\Framework\Attributes\DataProvider; class DropCollectionTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropCollection($this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/DropDatabaseFunctionalTest.php b/tests/Operation/DropDatabaseFunctionalTest.php index 217bf1e63..0ef812a10 100644 --- a/tests/Operation/DropDatabaseFunctionalTest.php +++ b/tests/Operation/DropDatabaseFunctionalTest.php @@ -7,6 +7,7 @@ use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\Depends; use function sprintf; @@ -18,14 +19,14 @@ public function testDefaultWriteConcernIsOmitted(): void function (): void { $operation = new DropDatabase( $this->getDatabaseName(), - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -43,9 +44,7 @@ public function testDropExistingDatabase(): void $this->assertDatabaseDoesNotExist($server, $this->getDatabaseName()); } - /** - * @depends testDropExistingDatabase - */ + #[Depends('testDropExistingDatabase')] public function testDropNonexistentDatabase(): void { $server = $this->getPrimaryServer(); @@ -65,14 +64,14 @@ public function testSessionOption(): void function (): void { $operation = new DropDatabase( $this->getDatabaseName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } diff --git a/tests/Operation/DropDatabaseTest.php b/tests/Operation/DropDatabaseTest.php index 06e33d961..c2d950223 100644 --- a/tests/Operation/DropDatabaseTest.php +++ b/tests/Operation/DropDatabaseTest.php @@ -4,34 +4,22 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\DropDatabase; +use PHPUnit\Framework\Attributes\DataProvider; class DropDatabaseTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropDatabase($this->getDatabaseName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/DropEncryptedCollectionTest.php b/tests/Operation/DropEncryptedCollectionTest.php new file mode 100644 index 000000000..a0055104c --- /dev/null +++ b/tests/Operation/DropEncryptedCollectionTest.php @@ -0,0 +1,29 @@ +expectException(InvalidArgumentException::class); + new DropEncryptedCollection($this->getDatabaseName(), $this->getCollectionName(), $options); + } + + public static function provideInvalidConstructorOptions(): Generator + { + yield 'encryptedFields is required' => [ + [], + ]; + + yield from self::createOptionDataProvider([ + 'encryptedFields' => self::getInvalidDocumentValues(), + ]); + } +} diff --git a/tests/Operation/DropIndexesFunctionalTest.php b/tests/Operation/DropIndexesFunctionalTest.php index a7cecf548..f273cb216 100644 --- a/tests/Operation/DropIndexesFunctionalTest.php +++ b/tests/Operation/DropIndexesFunctionalTest.php @@ -26,14 +26,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), 'x_1', - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -48,7 +48,7 @@ public function testDropOneIndexByName(): void $this->assertIndexExists('x_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), 'x_1'); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -76,7 +76,7 @@ public function testDropAllIndexesByWildcard(): void $this->assertIndexExists('y_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), '*'); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -108,7 +108,7 @@ public function testDropByIndexInfo(): void $this->assertIndexExists('x_1'); $operation = new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), $info); - $this->assertCommandSucceeded($operation->execute($this->getPrimaryServer())); + $operation->execute($this->getPrimaryServer()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -131,14 +131,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), '*', - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } diff --git a/tests/Operation/DropIndexesTest.php b/tests/Operation/DropIndexesTest.php index ab09aeadf..214330512 100644 --- a/tests/Operation/DropIndexesTest.php +++ b/tests/Operation/DropIndexesTest.php @@ -4,6 +4,7 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\DropIndexes; +use PHPUnit\Framework\Attributes\DataProvider; class DropIndexesTest extends TestCase { @@ -13,35 +14,19 @@ public function testDropIndexShouldNotAllowEmptyIndexName(): void new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), ''); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new DropIndexes($this->getDatabaseName(), $this->getCollectionName(), '*', $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/DropSearchIndexTest.php b/tests/Operation/DropSearchIndexTest.php new file mode 100644 index 000000000..06be34e59 --- /dev/null +++ b/tests/Operation/DropSearchIndexTest.php @@ -0,0 +1,15 @@ +expectException(InvalidArgumentException::class); + new DropSearchIndex($this->getDatabaseName(), $this->getCollectionName(), ''); + } +} diff --git a/tests/Operation/EstimatedDocumentCountTest.php b/tests/Operation/EstimatedDocumentCountTest.php index 803e1194d..c530d0e2d 100644 --- a/tests/Operation/EstimatedDocumentCountTest.php +++ b/tests/Operation/EstimatedDocumentCountTest.php @@ -4,38 +4,24 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\EstimatedDocumentCount; +use PHPUnit\Framework\Attributes\DataProvider; class EstimatedDocumentCountTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new EstimatedDocumentCount($this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + ]); } } diff --git a/tests/Operation/ExplainFunctionalTest.php b/tests/Operation/ExplainFunctionalTest.php index fe4f154e7..772479deb 100644 --- a/tests/Operation/ExplainFunctionalTest.php +++ b/tests/Operation/ExplainFunctionalTest.php @@ -5,7 +5,6 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Operation\Aggregate; use MongoDB\Operation\Count; -use MongoDB\Operation\CreateCollection; use MongoDB\Operation\Delete; use MongoDB\Operation\DeleteMany; use MongoDB\Operation\DeleteOne; @@ -21,14 +20,14 @@ use MongoDB\Operation\UpdateMany; use MongoDB\Operation\UpdateOne; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; -use function version_compare; +use function array_key_exists; +use function array_key_first; class ExplainFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testCount($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -41,9 +40,7 @@ public function testCount($verbosity, $executionStatsExpected, $allPlansExecutio $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -58,9 +55,7 @@ public function testDelete($verbosity, $executionStatsExpected, $allPlansExecuti $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testDeleteMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -75,9 +70,7 @@ public function testDeleteMany($verbosity, $executionStatsExpected, $allPlansExe $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testDeleteOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -92,9 +85,7 @@ public function testDeleteOne($verbosity, $executionStatsExpected, $allPlansExec $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testDistinct($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $operation = new Distinct($this->getDatabaseName(), $this->getCollectionName(), 'x', []); @@ -105,9 +96,7 @@ public function testDistinct($verbosity, $executionStatsExpected, $allPlansExecu $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFindAndModify($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true]); @@ -118,9 +107,7 @@ public function testFindAndModify($verbosity, $executionStatsExpected, $allPlans $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFind($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -148,11 +135,10 @@ public function testFindMaxAwait(): void $cappedCollectionOptions = [ 'capped' => true, 'max' => 100, - 'size' => 1048576, + 'size' => 1_048_576, ]; - $operation = new CreateCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); - $operation->execute($this->getPrimaryServer()); + $this->createCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); $this->createFixtures(2); @@ -165,40 +151,14 @@ function () use ($operation): void { }, function (array $event): void { $command = $event['started']->getCommand(); - $this->assertObjectNotHasAttribute('maxAwaitTimeMS', $command->explain); - $this->assertObjectHasAttribute('tailable', $command->explain); - $this->assertObjectHasAttribute('awaitData', $command->explain); - } - ); - } - - public function testFindModifiers(): void - { - $this->createFixtures(3); - - $operation = new Find( - $this->getDatabaseName(), - $this->getCollectionName(), - [], - ['modifiers' => ['$orderby' => ['_id' => 1]]] - ); - - (new CommandObserver())->observe( - function () use ($operation): void { - $explainOperation = new Explain($this->getDatabaseName(), $operation, ['typeMap' => ['root' => 'array', 'document' => 'array']]); - $explainOperation->execute($this->getPrimaryServer()); + $this->assertObjectNotHasProperty('maxAwaitTimeMS', $command->explain); + $this->assertObjectHasProperty('tailable', $command->explain); + $this->assertObjectHasProperty('awaitData', $command->explain); }, - function (array $event): void { - $command = $event['started']->getCommand(); - $this->assertObjectHasAttribute('sort', $command->explain); - $this->assertObjectNotHasAttribute('modifiers', $command->explain); - } ); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(1); @@ -211,9 +171,7 @@ public function testFindOne($verbosity, $executionStatsExpected, $allPlansExecut $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFindOneAndDelete($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), []); @@ -224,9 +182,7 @@ public function testFindOneAndDelete($verbosity, $executionStatsExpected, $allPl $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFindOneAndReplace($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1.1], ['x' => 5]); @@ -237,9 +193,7 @@ public function testFindOneAndReplace($verbosity, $executionStatsExpected, $allP $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testFindOneAndUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $operation = new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], ['$rename' => ['x' => 'y']]); @@ -250,9 +204,7 @@ public function testFindOneAndUpdate($verbosity, $executionStatsExpected, $allPl $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testUpdate($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -279,19 +231,19 @@ function (): void { $this->getCollectionName(), ['_id' => ['$gt' => 1]], ['$inc' => ['x' => 1]], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $explainOperation = new Explain($this->getDatabaseName(), $operation); $result = $explainOperation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute( + $this->assertObjectHasProperty( 'bypassDocumentValidation', - $event['started']->getCommand()->explain + $event['started']->getCommand()->explain, ); $this->assertEquals(true, $event['started']->getCommand()->explain->bypassDocumentValidation); - } + }, ); } @@ -306,24 +258,22 @@ function (): void { $this->getCollectionName(), ['_id' => ['$gt' => 1]], ['$inc' => ['x' => 1]], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $explainOperation = new Explain($this->getDatabaseName(), $operation); $result = $explainOperation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute( + $this->assertObjectNotHasProperty( 'bypassDocumentValidation', - $event['started']->getCommand()->explain + $event['started']->getCommand()->explain, ); - } + }, ); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testUpdateMany($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -339,9 +289,7 @@ public function testUpdateMany($verbosity, $executionStatsExpected, $allPlansExe $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testUpdateOne($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { $this->createFixtures(3); @@ -359,29 +307,22 @@ public function testUpdateOne($verbosity, $executionStatsExpected, $allPlansExec public function testAggregate(): void { - if (version_compare($this->getServerVersion(), '4.0.0', '<')) { - $this->markTestSkipped('Explaining aggregate command requires server version >= 4.0'); - } - $this->createFixtures(3); - $pipeline = [['$group' => ['_id' => null]]]; + // Use a $sort stage to ensure the aggregate does not get optimised to a query + $pipeline = [['$group' => ['_id' => null]], ['$sort' => ['_id' => 1]]]; $operation = new Aggregate($this->getDatabaseName(), $this->getCollectionName(), $pipeline); $explainOperation = new Explain($this->getDatabaseName(), $operation, ['verbosity' => Explain::VERBOSITY_QUERY, 'typeMap' => ['root' => 'array', 'document' => 'array']]); $result = $explainOperation->execute($this->getPrimaryServer()); - $this->assertExplainResult($result, false, false, true); + $this->assertExplainResult($result, false, false); } - /** - * @dataProvider provideVerbosityInformation - */ + #[DataProvider('provideVerbosityInformation')] public function testAggregateOptimizedToQuery($verbosity, $executionStatsExpected, $allPlansExecutionExpected): void { - if (version_compare($this->getServerVersion(), '4.2.0', '<')) { - $this->markTestSkipped('MongoDB < 4.2 does not optimize simple aggregation pipelines'); - } + $this->skipIfServerVersion('<', '4.2.0', 'MongoDB < 4.2 does not optimize simple aggregation pipelines'); $this->createFixtures(3); @@ -394,7 +335,7 @@ public function testAggregateOptimizedToQuery($verbosity, $executionStatsExpecte $this->assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected); } - public function provideVerbosityInformation() + public static function provideVerbosityInformation() { return [ [Explain::VERBOSITY_ALL_PLANS, true, true], @@ -403,23 +344,29 @@ public function provideVerbosityInformation() ]; } - private function assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected, $stagesExpected = false): void + private function assertExplainResult($result, $executionStatsExpected, $allPlansExecutionExpected): void { - if ($stagesExpected) { - $this->assertArrayHasKey('stages', $result); - } else { - $this->assertArrayHasKey('queryPlanner', $result); + $checkResult = $result; + + if (array_key_exists('shards', $result)) { + $firstShard = array_key_first($result['shards']); + $checkResult = $result['shards'][$firstShard]; } + $this->assertThat($checkResult, $this->logicalOr( + $this->arrayHasKey('stages'), + $this->arrayHasKey('queryPlanner'), + )); + if ($executionStatsExpected) { - $this->assertArrayHasKey('executionStats', $result); + $this->assertArrayHasKey('executionStats', $checkResult); if ($allPlansExecutionExpected) { - $this->assertArrayHasKey('allPlansExecution', $result['executionStats']); + $this->assertArrayHasKey('allPlansExecution', $checkResult['executionStats']); } else { - $this->assertArrayNotHasKey('allPlansExecution', $result['executionStats']); + $this->assertArrayNotHasKey('allPlansExecution', $checkResult['executionStats']); } } else { - $this->assertArrayNotHasKey('executionStats', $result); + $this->assertArrayNotHasKey('executionStats', $checkResult); } } @@ -433,7 +380,7 @@ private function createFixtures(int $n): void for ($i = 1; $i <= $n; $i++) { $bulkWrite->insert([ '_id' => $i, - 'x' => (integer) ($i . $i), + 'x' => (int) ($i . $i), ]); } diff --git a/tests/Operation/ExplainTest.php b/tests/Operation/ExplainTest.php index 184705662..1255d787c 100644 --- a/tests/Operation/ExplainTest.php +++ b/tests/Operation/ExplainTest.php @@ -5,12 +5,11 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Explain; use MongoDB\Operation\Explainable; +use PHPUnit\Framework\Attributes\DataProvider; class ExplainTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $explainable = $this->getMockBuilder(Explainable::class)->getMock(); @@ -18,26 +17,13 @@ public function testConstructorOptionTypeChecks(array $options): void new Explain($this->getDatabaseName(), $explainable, $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['verbosity' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'session' => self::getInvalidSessionValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'verbosity' => self::getInvalidStringValues(), + ]); } } diff --git a/tests/Operation/FindAndModifyFunctionalTest.php b/tests/Operation/FindAndModifyFunctionalTest.php index bc89fb524..576b0474c 100644 --- a/tests/Operation/FindAndModifyFunctionalTest.php +++ b/tests/Operation/FindAndModifyFunctionalTest.php @@ -2,40 +2,109 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\Driver\BulkWrite; use MongoDB\Driver\Exception\CommandException; -use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\UnsupportedException; use MongoDB\Model\BSONDocument; use MongoDB\Operation\FindAndModify; use MongoDB\Tests\CommandObserver; - -use function version_compare; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; +use stdClass; class FindAndModifyFunctionalTest extends FunctionalTestCase { - /** - * @see https://jira.mongodb.org/browse/PHPLIB-344 - */ + #[DataProvider('provideQueryDocuments')] + public function testQueryDocuments($query, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($query): void { + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['query' => $query, 'remove' => true], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->query ?? null); + }, + ); + } + + public static function provideQueryDocuments(): array + { + $expected = (object) ['x' => 1]; + + return [ + 'array' => [['x' => 1], $expected], + 'object' => [(object) ['x' => 1], $expected], + 'Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'Document' => [Document::fromPHP(['x' => 1]), $expected], + ]; + } + + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DataProvider('provideReplacementDocumentLikePipeline')] + public function testUpdateDocuments($update, $expectedUpdate): void + { + (new CommandObserver())->observe( + function () use ($update): void { + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + [ + 'query' => ['x' => 1], + 'update' => $update, + ], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->update ?? null); + }, + ); + } + + public static function provideReplacementDocumentLikePipeline(): array + { + /* Note: this expected value differs from UpdateFunctionalTest because + * FindAndModify is not affected by libmongoc's pipeline detection for + * update commands (see: CDRIVER-4658). */ + return [ + 'replacement_like_pipeline' => [ + (object) ['0' => ['$set' => ['x' => 1]]], + (object) ['0' => (object) ['$set' => (object) ['x' => 1]]], + ], + ]; + } + + /** @see https://jira.mongodb.org/browse/PHPLIB-344 */ public function testManagerReadConcernIsOmitted(): void { $manager = static::createTestManager(null, ['readConcernLevel' => 'majority']); - $server = $manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + $server = $manager->selectServer(); (new CommandObserver())->observe( function () use ($server): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true] + ['remove' => true], ); $operation->execute($server); }, function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } @@ -46,27 +115,25 @@ function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'writeConcern' => $this->createDefaultWriteConcern()] + ['remove' => true, 'writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } public function testHintOptionUnsupportedClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.2.0', '>=')) { - $this->markTestSkipped('server reports error for unsupported findAndModify options'); - } + $this->skipIfServerVersion('>=', '4.2.0', 'server reports error for unsupported findAndModify options'); $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'hint' => '_id_'] + ['remove' => true, 'hint' => '_id_'], ); $this->expectException(UnsupportedException::class); @@ -77,14 +144,12 @@ public function testHintOptionUnsupportedClientSideError(): void public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.4.0', '>=')) { - $this->markTestSkipped('hint is supported'); - } + $this->skipIfServerVersion('>=', '4.4.0', 'hint is supported'); $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ['remove' => true, 'hint' => '_id_', 'writeConcern' => new WriteConcern(0)], ); $this->expectException(UnsupportedException::class); @@ -95,7 +160,7 @@ public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSide public function testFindAndModifyReportedWriteConcernError(): void { - if (($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets()) || ! $this->isReplicaSet()) { + if ($this->isStandalone()) { $this->markTestSkipped('Test only applies to replica sets'); } @@ -106,7 +171,7 @@ public function testFindAndModifyReportedWriteConcernError(): void $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'writeConcern' => new WriteConcern(50)] + ['remove' => true, 'writeConcern' => new WriteConcern(50)], ); $operation->execute($this->getPrimaryServer()); @@ -119,14 +184,14 @@ function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'session' => $this->createSession()] + ['remove' => true, 'session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -137,15 +202,15 @@ function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'bypassDocumentValidation' => true] + ['remove' => true, 'bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -156,20 +221,18 @@ function (): void { $operation = new FindAndModify( $this->getDatabaseName(), $this->getCollectionName(), - ['remove' => true, 'bypassDocumentValidation' => false] + ['remove' => true, 'bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocument - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocument')] public function testTypeMapOption(?array $typeMap, $expectedDocument): void { $this->createFixtures(1); @@ -180,14 +243,14 @@ public function testTypeMapOption(?array $typeMap, $expectedDocument): void [ 'remove' => true, 'typeMap' => $typeMap, - ] + ], ); $document = $operation->execute($this->getPrimaryServer()); $this->assertEquals($expectedDocument, $document); } - public function provideTypeMapOptionsAndExpectedDocument() + public static function provideTypeMapOptionsAndExpectedDocument() { return [ [ @@ -217,6 +280,93 @@ public function provideTypeMapOptionsAndExpectedDocument() ]; } + public function testFindOneAndDeleteWithCodec(): void + { + $this->createFixtures(1); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['remove' => true, 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertEquals(TestObject::createDecodedForFixture(1), $result); + } + + public function testFindOneAndDeleteNothingWithCodec(): void + { + // When the query does not match any documents, the operation returns null + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['remove' => true, 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertNull($result); + } + + public function testFindOneAndUpdateWithCodec(): void + { + $this->createFixtures(1); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['update' => ['$set' => ['x.foo' => 'baz']], 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertEquals(TestObject::createDecodedForFixture(1), $result); + } + + public function testFindOneAndUpdateNothingWithCodec(): void + { + // When the query does not match any documents, the operation returns null + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['update' => ['$set' => ['x.foo' => 'baz']], 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertNull($result); + } + + public function testFindOneAndReplaceWithCodec(): void + { + $this->createFixtures(1); + + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['update' => ['_id' => 1], 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertEquals(TestObject::createDecodedForFixture(1), $result); + } + + public function testFindOneAndReplaceNothingWithCodec(): void + { + // When the query does not match any documents, the operation returns null + $operation = new FindAndModify( + $this->getDatabaseName(), + $this->getCollectionName(), + ['update' => ['_id' => 1], 'codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + + self::assertNull($result); + } + /** * Create data fixtures. */ @@ -225,10 +375,7 @@ private function createFixtures(int $n): void $bulkWrite = new BulkWrite(['ordered' => true]); for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert([ - '_id' => $i, - 'x' => (object) ['foo' => 'bar'], - ]); + $bulkWrite->insert(TestObject::createDocument($i)); } $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); diff --git a/tests/Operation/FindAndModifyTest.php b/tests/Operation/FindAndModifyTest.php index 4506d59e8..7abc5bfb9 100644 --- a/tests/Operation/FindAndModifyTest.php +++ b/tests/Operation/FindAndModifyTest.php @@ -2,81 +2,39 @@ namespace MongoDB\Tests\Operation; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindAndModify; +use PHPUnit\Framework\Attributes\DataProvider; class FindAndModifyTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['arrayFilters' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['fields' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['new' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['query' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['remove' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['sort' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['update' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['upsert' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'arrayFilters' => self::getInvalidArrayValues(), + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'collation' => self::getInvalidDocumentValues(), + 'fields' => self::getInvalidDocumentValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'new' => self::getInvalidBooleanValues(), + 'query' => self::getInvalidDocumentValues(), + 'remove' => self::getInvalidBooleanValues(), + 'session' => self::getInvalidSessionValues(), + 'sort' => self::getInvalidDocumentValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'update' => self::getInvalidUpdateValues(), + 'upsert' => self::getInvalidBooleanValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive(): void @@ -85,4 +43,46 @@ public function testConstructorUpdateAndRemoveOptionsAreMutuallyExclusive(): voi $this->expectExceptionMessage('The "remove" option must be true or an "update" document must be specified, but not both'); new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), ['remove' => true, 'update' => []]); } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'fields' => ['_id' => 0], + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'new' => true, + 'query' => ['y' => 2], + 'sort' => ['x' => 1], + 'update' => ['$set' => ['x' => 2]], + 'upsert' => true, + 'let' => ['a' => 3], + // Intentionally omitted options + 'remove' => false, // When "update" is set + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(0), + ]; + $operation = new FindAndModify($this->getDatabaseName(), $this->getCollectionName(), $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'upsert' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['$set' => ['x' => 2]], + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); + } } diff --git a/tests/Operation/FindFunctionalTest.php b/tests/Operation/FindFunctionalTest.php index 5ff55752b..0927b29fb 100644 --- a/tests/Operation/FindFunctionalTest.php +++ b/tests/Operation/FindFunctionalTest.php @@ -4,15 +4,37 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Driver\ReadPreference; -use MongoDB\Operation\CreateCollection; use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\Find; use MongoDB\Tests\CommandObserver; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; +use stdClass; use function microtime; class FindFunctionalTest extends FunctionalTestCase { + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedQuery): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Find( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedQuery): void { + $this->assertEquals($expectedQuery, $event['started']->getCommand()->filter ?? null); + }, + ); + } + public function testDefaultReadConcernIsOmitted(): void { (new CommandObserver())->observe( @@ -21,14 +43,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [], - ['readConcern' => $this->createDefaultReadConcern()] + ['readConcern' => $this->createDefaultReadConcern()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('readConcern', $event['started']->getCommand()); + }, ); } @@ -91,20 +113,18 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocuments')] public function testTypeMapOption(array $typeMap, array $expectedDocuments): void { $this->createFixtures(3); @@ -115,7 +135,7 @@ public function testTypeMapOption(array $typeMap, array $expectedDocuments): voi $this->assertEquals($expectedDocuments, $cursor->toArray()); } - public function provideTypeMapOptionsAndExpectedDocuments() + public static function provideTypeMapOptionsAndExpectedDocuments() { return [ [ @@ -145,6 +165,25 @@ public function provideTypeMapOptionsAndExpectedDocuments() ]; } + public function testCodecOption(): void + { + $this->createFixtures(3); + + $codec = new TestDocumentCodec(); + + $operation = new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['codec' => $codec]); + $cursor = $operation->execute($this->getPrimaryServer()); + + $this->assertEquals( + [ + TestObject::createDecodedForFixture(1), + TestObject::createDecodedForFixture(2), + TestObject::createDecodedForFixture(3), + ], + $cursor->toArray(), + ); + } + public function testMaxAwaitTimeMS(): void { $maxAwaitTimeMS = 100; @@ -160,11 +199,10 @@ public function testMaxAwaitTimeMS(): void $cappedCollectionOptions = [ 'capped' => true, 'max' => 100, - 'size' => 1048576, + 'size' => 1_048_576, ]; - $operation = new CreateCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); - $operation->execute($this->getPrimaryServer()); + $this->createCollection($databaseName, $cappedCollectionName, $cappedCollectionOptions); // Insert documents into the capped collection. $bulkWrite = new BulkWrite(['ordered' => true]); @@ -214,7 +252,7 @@ public function testReadPreferenceWithinTransaction(): void $this->skipIfTransactionsAreNotSupported(); // Collection must be created before the transaction starts - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $session = $this->manager->startSession(); $session->startTransaction(); @@ -224,7 +262,7 @@ public function testReadPreferenceWithinTransaction(): void $filter = ['_id' => ['$lt' => 3]]; $options = [ - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), 'session' => $session, ]; @@ -252,10 +290,7 @@ private function createFixtures(int $n, array $executeBulkWriteOptions = []): vo $bulkWrite = new BulkWrite(['ordered' => true]); for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert([ - '_id' => $i, - 'x' => (object) ['foo' => 'bar'], - ]); + $bulkWrite->insert(TestObject::createDocument($i)); } $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite, $executeBulkWriteOptions); diff --git a/tests/Operation/FindOneAndDeleteTest.php b/tests/Operation/FindOneAndDeleteTest.php index c2a325e6f..9f4cefd1f 100644 --- a/tests/Operation/FindOneAndDeleteTest.php +++ b/tests/Operation/FindOneAndDeleteTest.php @@ -2,37 +2,64 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndDelete; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class FindOneAndDeleteTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; + return self::createOptionDataProvider([ + 'projection' => self::getInvalidDocumentValues(), + ]); + } - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['projection' => $value]; - } + public function testExplainableCommandDocument(): void + { + $options = [ + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'sort' => ['x' => 1], + 'let' => ['a' => 3], + // Intentionally omitted options + 'projection' => ['_id' => 0], + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndDelete($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], $options); - return $options; + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'remove' => true, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/FindOneAndReplaceFunctionalTest.php b/tests/Operation/FindOneAndReplaceFunctionalTest.php new file mode 100644 index 000000000..9bbd8047c --- /dev/null +++ b/tests/Operation/FindOneAndReplaceFunctionalTest.php @@ -0,0 +1,63 @@ +createFixtures(1); + + (new CommandObserver())->observe( + function (): void { + $replaceObject = TestObject::createForFixture(1); + $replaceObject->x->foo = 'baz'; + + $operation = new FindOneAndReplace( + $this->getDatabaseName(), + $this->getCollectionName(), + ['_id' => 1], + $replaceObject, + ['codec' => new TestDocumentCodec()], + ); + + $this->assertEquals( + TestObject::createDecodedForFixture(1), + $operation->execute($this->getPrimaryServer()), + ); + }, + function (array $event): void { + $this->assertEquals( + (object) [ + '_id' => 1, + 'x' => (object) ['foo' => 'baz'], + 'encoded' => true, + ], + $event['started']->getCommand()->update ?? null, + ); + }, + ); + } + + /** + * Create data fixtures. + */ + private function createFixtures(int $n): void + { + $bulkWrite = new BulkWrite(['ordered' => true]); + + for ($i = 1; $i <= $n; $i++) { + $bulkWrite->insert(TestObject::createDocument($i)); + } + + $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); + + $this->assertEquals($n, $result->getInsertedCount()); + } +} diff --git a/tests/Operation/FindOneAndReplaceTest.php b/tests/Operation/FindOneAndReplaceTest.php index ad7cbd36b..dd273a46d 100644 --- a/tests/Operation/FindOneAndReplaceTest.php +++ b/tests/Operation/FindOneAndReplaceTest.php @@ -2,71 +2,115 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndReplace; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use TypeError; class FindOneAndReplaceTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), $filter, []); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorReplacementArgumentTypeCheck($replacement): void + { + $this->expectException($replacement instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); + } + + #[DataProvider('provideReplacementDocuments')] + #[DoesNotPerformAssertions] + public function testConstructorReplacementArgument($replacement): void + { + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); + } + + #[DataProvider('provideUpdateDocuments')] + public function testConstructorReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('First key in $replacement is an update operator'); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); } - public function testConstructorReplacementArgumentRequiresNoOperators(): void + #[DataProvider('provideUpdatePipelines')] + #[DataProvider('provideEmptyUpdatePipelinesExcludingArray')] + public function testConstructorReplacementArgumentProhibitsUpdatePipeline($replacement): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $replacement argument is an update operator'); - new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]]); + $this->expectExceptionMessageMatches('#(\$replacement is an update pipeline)|(Expected \$replacement to have type "document" \(array or object\))#'); + new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], $replacement); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['projection' => $value]; - } - - foreach ($this->getInvalidIntegerValues(true) as $value) { - $options[][] = ['returnDocument' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'codec' => self::getInvalidDocumentCodecValues(), + 'projection' => self::getInvalidDocumentValues(), + 'returnDocument' => self::getInvalidIntegerValues(true), + ]); } - /** - * @dataProvider provideInvalidConstructorReturnDocumentOptions - */ + #[DataProvider('provideInvalidConstructorReturnDocumentOptions')] public function testConstructorReturnDocumentOption($returnDocument): void { $this->expectException(InvalidArgumentException::class); new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]); } - public function provideInvalidConstructorReturnDocumentOptions() + public static function provideInvalidConstructorReturnDocumentOptions() { - return $this->wrapValuesForDataProvider([-1, 0, 3]); + return self::wrapValuesForDataProvider([-1, 0, 3]); + } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'fields' => ['_id' => 0], + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'projection' => ['_id' => 0], + 'sort' => ['x' => 1], + 'let' => ['a' => 3], + // Intentionally omitted options + 'returnDocument' => FindOneAndReplace::RETURN_DOCUMENT_AFTER, + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndReplace($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['y' => 3], $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['y' => 3], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/FindOneAndUpdateTest.php b/tests/Operation/FindOneAndUpdateTest.php index 8bf142148..b20ab59ac 100644 --- a/tests/Operation/FindOneAndUpdateTest.php +++ b/tests/Operation/FindOneAndUpdateTest.php @@ -2,71 +2,101 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\FindOneAndUpdate; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class FindOneAndUpdateTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), $filter, []); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($update instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], $update); } - public function testConstructorUpdateArgumentRequiresOperatorsOrPipeline(): void + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], []); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], $update); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], ['$set' => ['x' => 1]], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['projection' => $value]; - } - - foreach ($this->getInvalidIntegerValues(true) as $value) { - $options[][] = ['returnDocument' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'projection' => self::getInvalidDocumentValues(), + 'returnDocument' => self::getInvalidIntegerValues(), + ]); } - /** - * @dataProvider provideInvalidConstructorReturnDocumentOptions - */ + #[DataProvider('provideInvalidConstructorReturnDocumentOptions')] public function testConstructorReturnDocumentOption($returnDocument): void { $this->expectException(InvalidArgumentException::class); new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), [], [], ['returnDocument' => $returnDocument]); } - public function provideInvalidConstructorReturnDocumentOptions() + public static function provideInvalidConstructorReturnDocumentOptions() { - return $this->wrapValuesForDataProvider([-1, 0, 3]); + return self::wrapValuesForDataProvider([-1, 0, 3]); + } + + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + 'sort' => ['x' => 1], + 'upsert' => true, + 'let' => ['a' => 3], + // Intentionally omitted options + 'projection' => ['_id' => 0], + 'returnDocument' => FindOneAndUpdate::RETURN_DOCUMENT_AFTER, + 'typeMap' => ['root' => 'array'], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new FindOneAndUpdate($this->getDatabaseName(), $this->getCollectionName(), ['y' => 2], ['$set' => ['x' => 2]], $options); + + $expected = [ + 'findAndModify' => $this->getCollectionName(), + 'new' => true, + 'upsert' => true, + 'collation' => (object) ['locale' => 'fr'], + 'fields' => (object) ['_id' => 0], + 'let' => (object) ['a' => 3], + 'query' => (object) ['y' => 2], + 'sort' => (object) ['x' => 1], + 'update' => (object) ['$set' => ['x' => 2]], + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'comment' => 'explain me', + 'hint' => '_id_', + 'maxTimeMS' => 100, + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/FindOneFunctionalTest.php b/tests/Operation/FindOneFunctionalTest.php index cd1096fa7..de9058b7c 100644 --- a/tests/Operation/FindOneFunctionalTest.php +++ b/tests/Operation/FindOneFunctionalTest.php @@ -4,12 +4,13 @@ use MongoDB\Driver\BulkWrite; use MongoDB\Operation\FindOne; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; class FindOneFunctionalTest extends FunctionalTestCase { - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocument - */ + #[DataProvider('provideTypeMapOptionsAndExpectedDocument')] public function testTypeMapOption(array $typeMap, $expectedDocument): void { $this->createFixtures(1); @@ -20,7 +21,7 @@ public function testTypeMapOption(array $typeMap, $expectedDocument): void $this->assertEquals($expectedDocument, $document); } - public function provideTypeMapOptionsAndExpectedDocument() + public static function provideTypeMapOptionsAndExpectedDocument() { return [ [ @@ -38,6 +39,18 @@ public function provideTypeMapOptionsAndExpectedDocument() ]; } + public function testCodecOption(): void + { + $this->createFixtures(1); + + $codec = new TestDocumentCodec(); + + $operation = new FindOne($this->getDatabaseName(), $this->getCollectionName(), [], ['codec' => $codec]); + $document = $operation->execute($this->getPrimaryServer()); + + $this->assertEquals(TestObject::createDecodedForFixture(1), $document); + } + /** * Create data fixtures. */ @@ -46,10 +59,7 @@ private function createFixtures(int $n): void $bulkWrite = new BulkWrite(['ordered' => true]); for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert([ - '_id' => $i, - 'x' => (object) ['foo' => 'bar'], - ]); + $bulkWrite->insert(TestObject::createDocument($i)); } $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); diff --git a/tests/Operation/FindTest.php b/tests/Operation/FindTest.php index c8bc59647..97cc1d388 100644 --- a/tests/Operation/FindTest.php +++ b/tests/Operation/FindTest.php @@ -2,162 +2,119 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\ReadConcern; +use MongoDB\Driver\ReadPreference; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Find; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class FindTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new Find($this->getDatabaseName(), $this->getCollectionName(), $filter); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Find($this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() - { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['allowPartialResults' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['batchSize' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['cursorType' => $value]; - } - - foreach ($this->getInvalidHintValues() as $value) { - $options[][] = ['hint' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['limit' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['max' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxAwaitTimeMS' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxScan' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['min' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['modifiers' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['oplogReplay' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['projection' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['returnKey' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['showRecordId' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['skip' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['snapshot' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['sort' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - return $options; - } - - public function testSnapshotOptionIsDeprecated(): void + public static function provideInvalidConstructorOptions() { - $this->assertDeprecated(function (): void { - new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['snapshot' => true]); - }); - - $this->assertDeprecated(function (): void { - new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['snapshot' => false]); - }); + return self::createOptionDataProvider([ + 'allowPartialResults' => self::getInvalidBooleanValues(), + 'batchSize' => self::getInvalidIntegerValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'collation' => self::getInvalidDocumentValues(), + 'cursorType' => self::getInvalidIntegerValues(), + 'hint' => self::getInvalidHintValues(), + 'limit' => self::getInvalidIntegerValues(), + 'max' => self::getInvalidDocumentValues(), + 'maxAwaitTimeMS' => self::getInvalidIntegerValues(), + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'min' => self::getInvalidDocumentValues(), + 'projection' => self::getInvalidDocumentValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(), + 'returnKey' => self::getInvalidBooleanValues(), + 'session' => self::getInvalidSessionValues(), + 'showRecordId' => self::getInvalidBooleanValues(), + 'skip' => self::getInvalidIntegerValues(), + 'sort' => self::getInvalidDocumentValues(), + 'typeMap' => self::getInvalidArrayValues(), + ]); } - public function testMaxScanOptionIsDeprecated(): void - { - $this->assertDeprecated(function (): void { - new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['maxScan' => 1]); - }); - } - - private function getInvalidHintValues() - { - return [123, 3.14, true]; - } - - /** - * @dataProvider provideInvalidConstructorCursorTypeOptions - */ + #[DataProvider('provideInvalidConstructorCursorTypeOptions')] public function testConstructorCursorTypeOption($cursorType): void { $this->expectException(InvalidArgumentException::class); new Find($this->getDatabaseName(), $this->getCollectionName(), [], ['cursorType' => $cursorType]); } - public function provideInvalidConstructorCursorTypeOptions() + public static function provideInvalidConstructorCursorTypeOptions() + { + return self::wrapValuesForDataProvider([-1, 0, 4]); + } + + public function testExplainableCommandDocument(): void { - return $this->wrapValuesForDataProvider([-1, 0, 4]); + $options = [ + 'allowDiskUse' => true, + 'allowPartialResults' => true, + 'batchSize' => 123, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'limit' => 15, + 'max' => ['x' => 100], + 'maxTimeMS' => 100, + 'min' => ['x' => 10], + 'noCursorTimeout' => true, + 'projection' => ['_id' => 0], + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'returnKey' => true, + 'showRecordId' => true, + 'skip' => 5, + 'sort' => ['x' => 1], + 'let' => ['y' => 2], + // Intentionally omitted options + 'cursorType' => Find::NON_TAILABLE, + 'maxAwaitTimeMS' => 500, + 'readPreference' => new ReadPreference(ReadPreference::SECONDARY_PREFERRED), + 'typeMap' => ['root' => 'array'], + ]; + $operation = new Find($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); + + $expected = [ + 'find' => $this->getCollectionName(), + 'filter' => (object) ['x' => 1], + 'allowDiskUse' => true, + 'allowPartialResults' => true, + 'batchSize' => 123, + 'comment' => 'explain me', + 'hint' => '_id_', + 'limit' => 15, + 'maxTimeMS' => 100, + 'noCursorTimeout' => true, + 'projection' => ['_id' => 0], + 'readConcern' => new ReadConcern(ReadConcern::LOCAL), + 'returnKey' => true, + 'showRecordId' => true, + 'skip' => 5, + 'sort' => ['x' => 1], + 'collation' => (object) ['locale' => 'fr'], + 'let' => (object) ['y' => 2], + 'max' => (object) ['x' => 100], + 'min' => (object) ['x' => 10], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/FunctionalTestCase.php b/tests/Operation/FunctionalTestCase.php index 4cf9f33cb..98e05c3cb 100644 --- a/tests/Operation/FunctionalTestCase.php +++ b/tests/Operation/FunctionalTestCase.php @@ -2,8 +2,12 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\WriteConcern; +use MongoDB\Model\BSONArray; +use MongoDB\Model\BSONDocument; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; /** @@ -15,18 +19,65 @@ public function setUp(): void { parent::setUp(); - $this->dropCollection(); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } - public function tearDown(): void + public static function provideFilterDocuments(): array { - if ($this->hasFailed()) { - return; - } + $expected = (object) ['x' => 1]; - $this->dropCollection(); + return [ + 'filter:array' => [['x' => 1], $expected], + 'filter:object' => [(object) ['x' => 1], $expected], + 'filter:Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'filter:Document' => [Document::fromPHP(['x' => 1]), $expected], + ]; + } + + public static function provideReplacementDocuments(): array + { + $expected = (object) ['x' => 1]; + $expectedEmpty = (object) []; + + return [ + 'replacement:array' => [['x' => 1], $expected], + 'replacement:object' => [(object) ['x' => 1], $expected], + 'replacement:Serializable' => [new BSONDocument(['x' => 1]), $expected], + 'replacement:Document' => [Document::fromPHP(['x' => 1]), $expected], + /* Note: empty arrays could also express an empty pipeline, but we + * should interpret them as an empty replacement document for BC. */ + 'empty_replacement:array' => [[], $expectedEmpty], + 'empty_replacement:object' => [(object) [], $expectedEmpty], + 'empty_replacement:Serializable' => [new BSONDocument([]), $expectedEmpty], + 'empty_replacement:Document' => [Document::fromPHP([]), $expectedEmpty], + ]; + } + + public static function provideUpdateDocuments(): array + { + $expected = (object) ['$set' => (object) ['x' => 1]]; + + return [ + 'update:array' => [['$set' => ['x' => 1]], $expected], + 'update:object' => [(object) ['$set' => ['x' => 1]], $expected], + 'update:Serializable' => [new BSONDocument(['$set' => ['x' => 1]]), $expected], + 'update:Document' => [Document::fromPHP(['$set' => ['x' => 1]]), $expected], + ]; + } + + public static function provideUpdatePipelines(): array + { + $expected = [(object) ['$set' => (object) ['x' => 1]]]; - parent::tearDown(); + return [ + 'pipeline:array' => [[['$set' => ['x' => 1]]], $expected], + 'pipeline:Serializable' => [new BSONArray([['$set' => ['x' => 1]]]), $expected], + 'pipeline:PackedArray' => [PackedArray::fromPHP([['$set' => ['x' => 1]]]), $expected], + /* Note: although empty pipelines are valid NOPs for update and + * findAndModify commands, they are not supported for updates in + * libmongoc since they are indistinguishable from empty replacement + * documents (both are empty bson_t structs). */ + ]; } protected function createDefaultReadConcern() diff --git a/tests/Operation/InsertManyFunctionalTest.php b/tests/Operation/InsertManyFunctionalTest.php index e35905f2d..d8e520c98 100644 --- a/tests/Operation/InsertManyFunctionalTest.php +++ b/tests/Operation/InsertManyFunctionalTest.php @@ -2,19 +2,22 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\InsertManyResult; use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertMany; use MongoDB\Tests\CommandObserver; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\Depends; class InsertManyFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -23,32 +26,94 @@ public function setUp(): void $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } + public function testDocumentEncoding(): void + { + $documents = [ + ['_id' => 1], + (object) ['_id' => 2], + new BSONDocument(['_id' => 3]), + Document::fromPHP(['_id' => 4]), + ['x' => 1], + (object) ['x' => 2], + new BSONDocument(['x' => 3]), + Document::fromPHP(['x' => 4]), + ]; + + $expectedDocuments = [ + (object) ['_id' => 1], + (object) ['_id' => 2], + (object) ['_id' => 3], + (object) ['_id' => 4], + // Note: _id placeholders must be replaced with generated ObjectIds + (object) ['_id' => null, 'x' => 1], + (object) ['_id' => null, 'x' => 2], + (object) ['_id' => null, 'x' => 3], + (object) ['_id' => null, 'x' => 4], + ]; + + (new CommandObserver())->observe( + function () use ($documents, $expectedDocuments): void { + $operation = new InsertMany( + $this->getDatabaseName(), + $this->getCollectionName(), + $documents, + ); + + $result = $operation->execute($this->getPrimaryServer()); + $insertedIds = $result->getInsertedIds(); + + foreach ($expectedDocuments as $i => $expectedDocument) { + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $insertedIds[$i]; + } + } + }, + function (array $event) use ($expectedDocuments): void { + $this->assertEquals($expectedDocuments, $event['started']->getCommand()->documents ?? null); + }, + ); + } + public function testInsertMany(): void { $documents = [ - ['_id' => 'foo', 'x' => 11], - ['x' => 22], - (object) ['_id' => 'bar', 'x' => 33], - new BSONDocument(['_id' => 'baz', 'x' => 44]), + ['_id' => 1], + (object) ['_id' => 2], + new BSONDocument(['_id' => 3]), + Document::fromPHP(['_id' => 4]), + ['x' => 1], + (object) ['x' => 2], + new BSONDocument(['x' => 3]), + Document::fromPHP(['x' => 4]), ]; $operation = new InsertMany($this->getDatabaseName(), $this->getCollectionName(), $documents); $result = $operation->execute($this->getPrimaryServer()); $this->assertInstanceOf(InsertManyResult::class, $result); - $this->assertSame(4, $result->getInsertedCount()); + $this->assertSame(8, $result->getInsertedCount()); $insertedIds = $result->getInsertedIds(); - $this->assertSame('foo', $insertedIds[0]); - $this->assertInstanceOf(ObjectId::class, $insertedIds[1]); - $this->assertSame('bar', $insertedIds[2]); - $this->assertSame('baz', $insertedIds[3]); + $this->assertSame(1, $insertedIds[0]); + $this->assertSame(2, $insertedIds[1]); + $this->assertSame(3, $insertedIds[2]); + $this->assertSame(4, $insertedIds[3]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[4]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[5]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[6]); + $this->assertInstanceOf(ObjectId::class, $insertedIds[7]); $expected = [ - ['_id' => 'foo', 'x' => 11], - ['_id' => $insertedIds[1], 'x' => 22], - ['_id' => 'bar', 'x' => 33], - ['_id' => 'baz', 'x' => 44], + ['_id' => 1], + ['_id' => 2], + ['_id' => 3], + ['_id' => 4], + ['_id' => $insertedIds[4], 'x' => 1], + ['_id' => $insertedIds[5], 'x' => 2], + ['_id' => $insertedIds[6], 'x' => 3], + ['_id' => $insertedIds[7], 'x' => 4], + ]; $this->assertSameDocuments($expected, $this->collection->find()); @@ -62,14 +127,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['_id' => 1], ['_id' => 2]], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -81,15 +146,15 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['_id' => 1], ['_id' => 2]], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -101,14 +166,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), [['_id' => 1], ['_id' => 2]], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } @@ -125,21 +190,61 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertManyResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertManyResult $result): void { $this->assertInstanceOf(ObjectId::class, $result->getInsertedIds()[0]); } + + public function testInsertingWithCodec(): void + { + $documents = [ + TestObject::createForFixture(1), + TestObject::createForFixture(2), + TestObject::createForFixture(3), + ]; + + $expectedDocuments = [ + (object) [ + '_id' => 1, + 'x' => (object) ['foo' => 'bar'], + 'encoded' => true, + ], + (object) [ + '_id' => 2, + 'x' => (object) ['foo' => 'bar'], + 'encoded' => true, + ], + (object) [ + '_id' => 3, + 'x' => (object) ['foo' => 'bar'], + 'encoded' => true, + ], + ]; + + (new CommandObserver())->observe( + function () use ($documents): void { + $operation = new InsertMany( + $this->getDatabaseName(), + $this->getCollectionName(), + $documents, + ['codec' => new TestDocumentCodec()], + ); + + $result = $operation->execute($this->getPrimaryServer()); + $this->assertEquals([1, 2, 3], $result->getInsertedIds()); + }, + function (array $event) use ($expectedDocuments): void { + $this->assertEquals($expectedDocuments, $event['started']->getCommand()->documents ?? null); + }, + ); + } } diff --git a/tests/Operation/InsertManyTest.php b/tests/Operation/InsertManyTest.php index e8326f100..8ee11ff5d 100644 --- a/tests/Operation/InsertManyTest.php +++ b/tests/Operation/InsertManyTest.php @@ -3,7 +3,10 @@ namespace MongoDB\Tests\Operation; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\InsertMany; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use PHPUnit\Framework\Attributes\DataProvider; class InsertManyTest extends TestCase { @@ -17,13 +20,11 @@ public function testConstructorDocumentsMustNotBeEmpty(): void public function testConstructorDocumentsMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$documents is not a list (unexpected index: "1")'); + $this->expectExceptionMessage('$documents is not a list'); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [1 => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorDocumentsArgumentElementTypeChecks($document): void { $this->expectException(InvalidArgumentException::class); @@ -31,35 +32,28 @@ public function testConstructorDocumentsArgumentElementTypeChecks($document): vo new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [$document]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [['x' => 1]], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['ordered' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } + return self::createOptionDataProvider([ + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'ordered' => self::getInvalidBooleanValues(true), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); + } - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); - return $options; + new InsertMany($this->getDatabaseName(), $this->getCollectionName(), [['x' => 1]], ['codec' => new TestDocumentCodec()]); } } diff --git a/tests/Operation/InsertOneFunctionalTest.php b/tests/Operation/InsertOneFunctionalTest.php index 1aa1790b7..204d8bacc 100644 --- a/tests/Operation/InsertOneFunctionalTest.php +++ b/tests/Operation/InsertOneFunctionalTest.php @@ -2,19 +2,24 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; use MongoDB\BSON\ObjectId; use MongoDB\Collection; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\InsertOneResult; use MongoDB\Model\BSONDocument; use MongoDB\Operation\InsertOne; use MongoDB\Tests\CommandObserver; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use MongoDB\Tests\Fixtures\Document\TestObject; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use stdClass; class InsertOneFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -23,38 +28,74 @@ public function setUp(): void $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } - /** - * @dataProvider provideDocumentWithExistingId - */ - public function testInsertOneWithExistingId($document): void + #[DataProvider('provideDocumentsWithIds')] + #[DataProvider('provideDocumentsWithoutIds')] + public function testDocumentEncoding($document, stdClass $expectedDocument): void { - $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); - $result = $operation->execute($this->getPrimaryServer()); + (new CommandObserver())->observe( + function () use ($document, $expectedDocument): void { + $operation = new InsertOne( + $this->getDatabaseName(), + $this->getCollectionName(), + $document, + ); - $this->assertInstanceOf(InsertOneResult::class, $result); - $this->assertSame(1, $result->getInsertedCount()); - $this->assertSame('foo', $result->getInsertedId()); + $result = $operation->execute($this->getPrimaryServer()); - $expected = [ - ['_id' => 'foo', 'x' => 11], - ]; + // Replace _id placeholder if necessary + if ($expectedDocument->_id === null) { + $expectedDocument->_id = $result->getInsertedId(); + } + }, + function (array $event) use ($expectedDocument): void { + $this->assertEquals($expectedDocument, $event['started']->getCommand()->documents[0] ?? null); + }, + ); + } - $this->assertSameDocuments($expected, $this->collection->find()); + public static function provideDocumentsWithIds(): array + { + $expectedDocument = (object) ['_id' => 1]; + + return [ + 'with_id:array' => [['_id' => 1], $expectedDocument], + 'with_id:object' => [(object) ['_id' => 1], $expectedDocument], + 'with_id:Serializable' => [new BSONDocument(['_id' => 1]), $expectedDocument], + 'with_id:Document' => [Document::fromPHP(['_id' => 1]), $expectedDocument], + ]; } - public function provideDocumentWithExistingId() + public static function provideDocumentsWithoutIds(): array { + /* Note: _id placeholders must be replaced with generated ObjectIds. We + * also clone the value for each data set since tests may need to modify + * the object. */ + $expectedDocument = (object) ['_id' => null, 'x' => 1]; + return [ - [['_id' => 'foo', 'x' => 11]], - [(object) ['_id' => 'foo', 'x' => 11]], - [new BSONDocument(['_id' => 'foo', 'x' => 11])], + 'without_id:array' => [['x' => 1], clone $expectedDocument], + 'without_id:object' => [(object) ['x' => 1], clone $expectedDocument], + 'without_id:Serializable' => [new BSONDocument(['x' => 1]), clone $expectedDocument], + 'without_id:Document' => [Document::fromPHP(['x' => 1]), clone $expectedDocument], ]; } - public function testInsertOneWithGeneratedId(): void + #[DataProvider('provideDocumentsWithIds')] + public function testInsertOneWithExistingId($document, stdClass $expectedDocument): void { - $document = ['x' => 11]; + $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); + $result = $operation->execute($this->getPrimaryServer()); + $this->assertInstanceOf(InsertOneResult::class, $result); + $this->assertSame(1, $result->getInsertedCount()); + $this->assertSame($expectedDocument->_id, $result->getInsertedId()); + + $this->assertSameDocuments([$expectedDocument], $this->collection->find()); + } + + #[DataProvider('provideDocumentsWithoutIds')] + public function testInsertOneWithGeneratedId($document, stdClass $expectedDocument): void + { $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); $result = $operation->execute($this->getPrimaryServer()); @@ -62,11 +103,10 @@ public function testInsertOneWithGeneratedId(): void $this->assertSame(1, $result->getInsertedCount()); $this->assertInstanceOf(ObjectId::class, $result->getInsertedId()); - $expected = [ - ['_id' => $result->getInsertedId(), 'x' => 11], - ]; + // Replace _id placeholder + $expectedDocument->_id = $result->getInsertedId(); - $this->assertSameDocuments($expected, $this->collection->find()); + $this->assertSameDocuments([$expectedDocument], $this->collection->find()); } public function testSessionOption(): void @@ -77,14 +117,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -96,15 +136,15 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -116,14 +156,14 @@ function (): void { $this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } @@ -140,21 +180,42 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedCount(InsertOneResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getInsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesInsertedId(InsertOneResult $result): void { $this->assertInstanceOf(ObjectId::class, $result->getInsertedId()); } + + public function testInsertingWithCodec(): void + { + (new CommandObserver())->observe( + function (): void { + $document = TestObject::createForFixture(1); + $options = ['codec' => new TestDocumentCodec()]; + + $operation = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document, $options); + $result = $operation->execute($this->getPrimaryServer()); + + $this->assertSame(1, $result->getInsertedId()); + }, + function (array $event): void { + $this->assertEquals( + (object) [ + '_id' => 1, + 'x' => (object) ['foo' => 'bar'], + 'encoded' => true, + ], + $event['started']->getCommand()->documents[0] ?? null, + ); + }, + ); + } } diff --git a/tests/Operation/InsertOneTest.php b/tests/Operation/InsertOneTest.php index f957ba9d3..1f641ea88 100644 --- a/tests/Operation/InsertOneTest.php +++ b/tests/Operation/InsertOneTest.php @@ -2,45 +2,44 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\InsertOne; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class InsertOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorDocumentArgumentTypeCheck($document): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($document instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new InsertOne($this->getDatabaseName(), $this->getCollectionName(), $document); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } + return self::createOptionDataProvider([ + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); + } - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); - return $options; + new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['codec' => new TestDocumentCodec()]); } } diff --git a/tests/Operation/ListCollectionNamesFunctionalTest.php b/tests/Operation/ListCollectionNamesFunctionalTest.php index e72c7789c..74e188627 100644 --- a/tests/Operation/ListCollectionNamesFunctionalTest.php +++ b/tests/Operation/ListCollectionNamesFunctionalTest.php @@ -35,15 +35,15 @@ public function testAuthorizedCollectionsOption(): void function (): void { $operation = new ListCollectionNames( $this->getDatabaseName(), - ['authorizedCollections' => true] + ['authorizedCollections' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('authorizedCollections', $event['started']->getCommand()); + $this->assertObjectHasProperty('authorizedCollections', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->authorizedCollections); - } + }, ); } @@ -53,14 +53,14 @@ public function testSessionOption(): void function (): void { $operation = new ListCollectionNames( $this->getDatabaseName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/ListCollectionsFunctionalTest.php b/tests/Operation/ListCollectionsFunctionalTest.php index ec48e7e79..ed8cb9b14 100644 --- a/tests/Operation/ListCollectionsFunctionalTest.php +++ b/tests/Operation/ListCollectionsFunctionalTest.php @@ -2,12 +2,13 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\CollectionInfo; -use MongoDB\Model\CollectionInfoIterator; use MongoDB\Operation\DropDatabase; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListCollections; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\Group; class ListCollectionsFunctionalTest extends FunctionalTestCase { @@ -25,7 +26,7 @@ public function testListCollectionsForNewlyCreatedDatabase(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); $this->assertCount(1, $collections); @@ -35,12 +36,10 @@ public function testListCollectionsForNewlyCreatedDatabase(): void } } - /** - * @group matrix-testing-exclude-server-4.4-driver-4.0 - * @group matrix-testing-exclude-server-4.4-driver-4.2 - * @group matrix-testing-exclude-server-5.0-driver-4.0 - * @group matrix-testing-exclude-server-5.0-driver-4.2 - */ + #[Group('matrix-testing-exclude-server-4.4-driver-4.0')] + #[Group('matrix-testing-exclude-server-4.4-driver-4.2')] + #[Group('matrix-testing-exclude-server-5.0-driver-4.0')] + #[Group('matrix-testing-exclude-server-5.0-driver-4.2')] public function testIdIndexAndInfo(): void { $server = $this->getPrimaryServer(); @@ -52,12 +51,14 @@ public function testIdIndexAndInfo(): void $operation = new ListCollections($this->getDatabaseName(), ['filter' => ['name' => $this->getCollectionName()]]); $collections = $operation->execute($server); - $this->assertInstanceOf(CollectionInfoIterator::class, $collections); + $this->assertInstanceOf(Iterator::class, $collections); foreach ($collections as $collection) { $this->assertInstanceOf(CollectionInfo::class, $collection); $this->assertArrayHasKey('readOnly', $collection['info']); - $this->assertEquals(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_', 'ns' => $this->getNamespace()], $collection['idIndex']); + // Use assertMatchesDocument as MongoDB 4.0 and 4.2 include a ns field + // TODO: change to assertEquals when dropping support for MongoDB 4.2 + $this->assertMatchesDocument(['v' => 2, 'key' => ['_id' => 1], 'name' => '_id_'], $collection['idIndex']); } } @@ -80,15 +81,15 @@ public function testAuthorizedCollectionsOption(): void function (): void { $operation = new ListCollections( $this->getDatabaseName(), - ['authorizedCollections' => true] + ['authorizedCollections' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('authorizedCollections', $event['started']->getCommand()); + $this->assertObjectHasProperty('authorizedCollections', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->authorizedCollections); - } + }, ); } @@ -98,14 +99,14 @@ public function testSessionOption(): void function (): void { $operation = new ListCollections( $this->getDatabaseName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/ListDatabaseNamesFunctionalTest.php b/tests/Operation/ListDatabaseNamesFunctionalTest.php index b0b728a89..b9bbcc9a7 100644 --- a/tests/Operation/ListDatabaseNamesFunctionalTest.php +++ b/tests/Operation/ListDatabaseNamesFunctionalTest.php @@ -24,9 +24,9 @@ function () use (&$databases, $server): void { $databases = $operation->execute($server); }, function (array $event): void { - $this->assertObjectNotHasAttribute('authorizedDatabases', $event['started']->getCommand()); + $this->assertObjectNotHasProperty('authorizedDatabases', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->nameOnly); - } + }, ); foreach ($databases as $database) { @@ -39,15 +39,15 @@ public function testAuthorizedDatabasesOption(): void (new CommandObserver())->observe( function (): void { $operation = new ListDatabaseNames( - ['authorizedDatabases' => true] + ['authorizedDatabases' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('authorizedDatabases', $event['started']->getCommand()); + $this->assertObjectHasProperty('authorizedDatabases', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->authorizedDatabases); - } + }, ); } @@ -73,14 +73,14 @@ public function testSessionOption(): void (new CommandObserver())->observe( function (): void { $operation = new ListDatabaseNames( - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/ListDatabasesFunctionalTest.php b/tests/Operation/ListDatabasesFunctionalTest.php index 45283ccfb..f881b34dd 100644 --- a/tests/Operation/ListDatabasesFunctionalTest.php +++ b/tests/Operation/ListDatabasesFunctionalTest.php @@ -2,8 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\DatabaseInfo; -use MongoDB\Model\DatabaseInfoIterator; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListDatabases; use MongoDB\Tests\CommandObserver; @@ -26,11 +26,11 @@ function () use (&$databases, $server): void { $databases = $operation->execute($server); }, function (array $event): void { - $this->assertObjectNotHasAttribute('authorizedDatabases', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('authorizedDatabases', $event['started']->getCommand()); + }, ); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); foreach ($databases as $database) { $this->assertInstanceOf(DatabaseInfo::class, $database); @@ -42,15 +42,15 @@ public function testAuthorizedDatabasesOption(): void (new CommandObserver())->observe( function (): void { $operation = new ListDatabases( - ['authorizedDatabases' => true] + ['authorizedDatabases' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('authorizedDatabases', $event['started']->getCommand()); + $this->assertObjectHasProperty('authorizedDatabases', $event['started']->getCommand()); $this->assertSame(true, $event['started']->getCommand()->authorizedDatabases); - } + }, ); } @@ -65,7 +65,7 @@ public function testFilterOption(): void $operation = new ListDatabases(['filter' => ['name' => $this->getDatabaseName()]]); $databases = $operation->execute($server); - $this->assertInstanceOf(DatabaseInfoIterator::class, $databases); + $this->assertInstanceOf(Iterator::class, $databases); $this->assertCount(1, $databases); @@ -80,14 +80,14 @@ public function testSessionOption(): void (new CommandObserver())->observe( function (): void { $operation = new ListDatabases( - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/ListIndexesFunctionalTest.php b/tests/Operation/ListIndexesFunctionalTest.php index d2d03bf48..f5a2f6bc2 100644 --- a/tests/Operation/ListIndexesFunctionalTest.php +++ b/tests/Operation/ListIndexesFunctionalTest.php @@ -2,9 +2,8 @@ namespace MongoDB\Tests\Operation; +use Iterator; use MongoDB\Model\IndexInfo; -use MongoDB\Model\IndexInfoIterator; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\InsertOne; use MongoDB\Operation\ListIndexes; use MongoDB\Tests\CommandObserver; @@ -13,8 +12,7 @@ class ListIndexesFunctionalTest extends FunctionalTestCase { public function testListIndexesForNewlyCreatedCollection(): void { - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $insertOne = new InsertOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1]); $writeResult = $insertOne->execute($this->getPrimaryServer()); @@ -23,21 +21,19 @@ public function testListIndexesForNewlyCreatedCollection(): void $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); - $this->assertInstanceOf(IndexInfoIterator::class, $indexes); + $this->assertInstanceOf(Iterator::class, $indexes); $this->assertCount(1, $indexes); foreach ($indexes as $index) { $this->assertInstanceOf(IndexInfo::class, $index); $this->assertEquals(['_id' => 1], $index->getKey()); - $this->assertSame($this->getNamespace(), $index->getNamespace()); } } public function testListIndexesForNonexistentCollection(): void { - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName()); - $operation->execute($this->getPrimaryServer()); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $operation = new ListIndexes($this->getDatabaseName(), $this->getCollectionName()); $indexes = $operation->execute($this->getPrimaryServer()); @@ -52,14 +48,14 @@ function (): void { $operation = new ListIndexes( $this->getDatabaseName(), $this->getCollectionName(), - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/ListIndexesTest.php b/tests/Operation/ListIndexesTest.php index dd4c8978d..f6c7a095b 100644 --- a/tests/Operation/ListIndexesTest.php +++ b/tests/Operation/ListIndexesTest.php @@ -4,30 +4,22 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\ListIndexes; +use PHPUnit\Framework\Attributes\DataProvider; class ListIndexesTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ListIndexes($this->getDatabaseName(), $this->getCollectionName(), $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'maxTimeMS' => self::getInvalidIntegerValues(), + 'session' => self::getInvalidSessionValues(), + ]); } } diff --git a/tests/Operation/ListSearchIndexesTest.php b/tests/Operation/ListSearchIndexesTest.php new file mode 100644 index 000000000..d47e5e893 --- /dev/null +++ b/tests/Operation/ListSearchIndexesTest.php @@ -0,0 +1,36 @@ +expectException(InvalidArgumentException::class); + new ListSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), ['name' => '']); + } + + #[DataProvider('provideInvalidConstructorOptions')] + public function testConstructorOptionTypeChecks(array $options): void + { + $this->expectException(InvalidArgumentException::class); + new ListSearchIndexes($this->getDatabaseName(), $this->getCollectionName(), $options); + } + + public static function provideInvalidConstructorOptions(): array + { + $options = []; + + foreach (self::getInvalidIntegerValues() as $value) { + $options[][] = ['batchSize' => $value]; + } + + $options[][] = ['codec' => 'foo']; + + return $options; + } +} diff --git a/tests/Operation/MapReduceFunctionalTest.php b/tests/Operation/MapReduceFunctionalTest.php deleted file mode 100644 index d47109b03..000000000 --- a/tests/Operation/MapReduceFunctionalTest.php +++ /dev/null @@ -1,326 +0,0 @@ -createCollection(); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['readConcern' => $this->createDefaultReadConcern()] - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasAttribute('readConcern', $event['started']->getCommand()); - } - ); - } - - public function testDefaultWriteConcernIsOmitted(): void - { - // Collection must exist for mapReduce command - $this->createCollection(); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - $this->getCollectionName() . '.output', - ['writeConcern' => $this->createDefaultWriteConcern()] - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } - ); - - $operation = new DropCollection($this->getDatabaseName(), $this->getCollectionName() . '.output'); - $operation->execute($this->getPrimaryServer()); - } - - public function testFinalize(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - $finalize = new Javascript('function(key, reducedValue) { return reducedValue; }'); - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['finalize' => $finalize]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertNotNull($result); - } - - public function testResult(): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - - if (version_compare($this->getServerVersion(), '4.3.0', '<')) { - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - } - } - - public function testResultIncludesTimingWithVerboseOption(): void - { - if (version_compare($this->getServerVersion(), '4.3.0', '>=')) { - $this->markTestSkipped('mapReduce statistics are no longer exposed'); - } - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => true]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertNotEmpty($result->getTiming()); - } - - public function testResultDoesNotIncludeTimingWithoutVerboseOption(): void - { - if (version_compare($this->getServerVersion(), '4.3.0', '>=')) { - $this->markTestSkipped('mapReduce statistics are no longer exposed'); - } - - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['verbose' => false]); - $result = $operation->execute($this->getPrimaryServer()); - - $this->assertInstanceOf(MapReduceResult::class, $result); - $this->assertGreaterThanOrEqual(0, $result->getExecutionTimeMS()); - $this->assertNotEmpty($result->getCounts()); - $this->assertEmpty($result->getTiming()); - } - - public function testSessionOption(): void - { - $this->createFixtures(3); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['session' => $this->createSession()] - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } - ); - } - - public function testBypassDocumentValidationSetWhenTrue(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => true] - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } - ); - } - - public function testBypassDocumentValidationUnsetWhenFalse(): void - { - $this->createFixtures(1); - - (new CommandObserver())->observe( - function (): void { - $operation = new MapReduce( - $this->getDatabaseName(), - $this->getCollectionName(), - new Javascript('function() { emit(this.x, this.y); }'), - new Javascript('function(key, values) { return Array.sum(values); }'), - ['inline' => 1], - ['bypassDocumentValidation' => false] - ); - - $operation->execute($this->getPrimaryServer()); - }, - function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } - ); - } - - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOptionWithInlineResults(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - } - - public function provideTypeMapOptionsAndExpectedDocuments() - { - return [ - [ - null, - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'array'], - [ - ['_id' => 1, 'value' => 3], - ['_id' => 2, 'value' => 6], - ['_id' => 3, 'value' => 9], - ], - ], - [ - ['root' => 'object'], - [ - (object) ['_id' => 1, 'value' => 3], - (object) ['_id' => 2, 'value' => 6], - (object) ['_id' => 3, 'value' => 9], - ], - ], - ]; - } - - /** - * @dataProvider provideTypeMapOptionsAndExpectedDocuments - */ - public function testTypeMapOptionWithOutputCollection(?array $typeMap, array $expectedDocuments): void - { - $this->createFixtures(3); - - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = $this->getCollectionName() . '.output'; - - $operation = new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, ['typeMap' => $typeMap]); - $results = iterator_to_array($operation->execute($this->getPrimaryServer())); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults($results)); - - $operation = new Find($this->getDatabaseName(), $out, [], ['typeMap' => $typeMap]); - $cursor = $operation->execute($this->getPrimaryServer()); - - $this->assertEquals($this->sortResults($expectedDocuments), $this->sortResults(iterator_to_array($cursor))); - - $operation = new DropCollection($this->getDatabaseName(), $out); - $operation->execute($this->getPrimaryServer()); - } - - /** - * Create data fixtures. - */ - private function createFixtures(int $n): void - { - $bulkWrite = new BulkWrite(['ordered' => true]); - - for ($i = 1; $i <= $n; $i++) { - $bulkWrite->insert(['x' => $i, 'y' => $i]); - $bulkWrite->insert(['x' => $i, 'y' => $i * 2]); - } - - $result = $this->manager->executeBulkWrite($this->getNamespace(), $bulkWrite); - - $this->assertEquals($n * 2, $result->getInsertedCount()); - } - - private function sortResults(array $results): array - { - $sortFunction = static function ($resultA, $resultB): int { - $idA = is_object($resultA) ? $resultA->_id : $resultA['_id']; - $idB = is_object($resultB) ? $resultB->_id : $resultB['_id']; - - return $idA <=> $idB; - }; - - $sortedResults = $results; - usort($sortedResults, $sortFunction); - - return $sortedResults; - } -} diff --git a/tests/Operation/MapReduceTest.php b/tests/Operation/MapReduceTest.php deleted file mode 100644 index 1376362db..000000000 --- a/tests/Operation/MapReduceTest.php +++ /dev/null @@ -1,114 +0,0 @@ -expectException(InvalidArgumentException::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out); - } - - public function provideInvalidOutValues() - { - return $this->wrapValuesForDataProvider([123, 3.14, true]); - } - - /** - * @dataProvider provideInvalidConstructorOptions - */ - public function testConstructorOptionTypeChecks(array $options): void - { - $map = new Javascript('function() { emit(this.x, this.y); }'); - $reduce = new Javascript('function(key, values) { return Array.sum(values); }'); - $out = ['inline' => 1]; - - $this->expectException(InvalidArgumentException::class); - new MapReduce($this->getDatabaseName(), $this->getCollectionName(), $map, $reduce, $out, $options); - } - - public function provideInvalidConstructorOptions() - { - $options = []; - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidJavascriptValues() as $value) { - $options[][] = ['finalize' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['jsMode' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['limit' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxTimeMS' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['query' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues() as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['scope' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['sort' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['verbose' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; - } - - private function getInvalidJavascriptValues() - { - return [123, 3.14, 'foo', true, [], new stdClass(), new ObjectId()]; - } -} diff --git a/tests/Operation/ModifyCollectionFunctionalTest.php b/tests/Operation/ModifyCollectionFunctionalTest.php index 567bcdef7..9c6e3a42d 100644 --- a/tests/Operation/ModifyCollectionFunctionalTest.php +++ b/tests/Operation/ModifyCollectionFunctionalTest.php @@ -4,21 +4,20 @@ use MongoDB\Operation\CreateIndexes; use MongoDB\Operation\ModifyCollection; +use PHPUnit\Framework\Attributes\Group; class ModifyCollectionFunctionalTest extends FunctionalTestCase { - /** - * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster - */ + #[Group('matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster')] + #[Group('matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster')] + #[Group('matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster')] public function testCollMod(): void { if ($this->isShardedCluster()) { $this->markTestSkipped('Sharded clusters may report result inconsistently'); } - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); $indexes = [['key' => ['lastAccess' => 1], 'expireAfterSeconds' => 3]]; $createIndexes = new CreateIndexes($this->getDatabaseName(), $this->getCollectionName(), $indexes); @@ -28,7 +27,7 @@ public function testCollMod(): void $this->getDatabaseName(), $this->getCollectionName(), ['index' => ['keyPattern' => ['lastAccess' => 1], 'expireAfterSeconds' => 1000]], - ['typeMap' => ['root' => 'array', 'document' => 'array']] + ['typeMap' => ['root' => 'array', 'document' => 'array']], ); $result = $modifyCollection->execute($this->getPrimaryServer()); diff --git a/tests/Operation/ModifyCollectionTest.php b/tests/Operation/ModifyCollectionTest.php index 2faed3e1f..c835d6a6d 100644 --- a/tests/Operation/ModifyCollectionTest.php +++ b/tests/Operation/ModifyCollectionTest.php @@ -4,6 +4,7 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\ModifyCollection; +use PHPUnit\Framework\Attributes\DataProvider; class ModifyCollectionTest extends TestCase { @@ -14,31 +15,19 @@ public function testConstructorEmptyCollectionOptions(): void new ModifyCollection($this->getDatabaseName(), $this->getCollectionName(), []); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new ModifyCollection($this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'session' => self::getInvalidSessionValues(), + 'typeMap' => self::getInvalidArrayValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/RenameCollectionFunctionalTest.php b/tests/Operation/RenameCollectionFunctionalTest.php index 198adc369..37fb23d4d 100644 --- a/tests/Operation/RenameCollectionFunctionalTest.php +++ b/tests/Operation/RenameCollectionFunctionalTest.php @@ -3,7 +3,6 @@ namespace MongoDB\Tests\Operation; use MongoDB\Driver\Exception\CommandException; -use MongoDB\Operation\DropCollection; use MongoDB\Operation\FindOne; use MongoDB\Operation\InsertOne; use MongoDB\Operation\RenameCollection; @@ -11,34 +10,19 @@ class RenameCollectionFunctionalTest extends FunctionalTestCase { - /** @var integer */ - private static $errorCodeNamespaceNotFound = 26; + private static int $errorCodeNamespaceNotFound = 26; - /** @var integer */ - private static $errorCodeNamespaceExists = 48; + private static int $errorCodeNamespaceExists = 48; - /** @var string */ - private $toCollectionName; + private string $toCollectionName; public function setUp(): void { parent::setUp(); $this->toCollectionName = $this->getCollectionName() . '.renamed'; - $operation = new DropCollection($this->getDatabaseName(), $this->toCollectionName); - $operation->execute($this->getPrimaryServer()); - } - - public function tearDown(): void - { - if ($this->hasFailed()) { - return; - } - - $operation = new DropCollection($this->getDatabaseName(), $this->toCollectionName); - $operation->execute($this->getPrimaryServer()); - - parent::tearDown(); + $this->dropCollection($this->getDatabaseName(), $this->toCollectionName); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); } public function testDefaultWriteConcernIsOmitted(): void @@ -56,14 +40,14 @@ function (): void { $this->getCollectionName(), $this->getDatabaseName(), $this->toCollectionName, - ['writeConcern' => $this->createDefaultWriteConcern()] + ['writeConcern' => $this->createDefaultWriteConcern()], ); $operation->execute($server); }, function (array $event): void { - $this->assertObjectNotHasAttribute('writeConcern', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('writeConcern', $event['started']->getCommand()); + }, ); } @@ -79,11 +63,10 @@ public function testRenameCollectionToNonexistentTarget(): void $this->getDatabaseName(), $this->getCollectionName(), $this->getDatabaseName(), - $this->toCollectionName + $this->toCollectionName, ); - $commandResult = $operation->execute($server); + $operation->execute($server); - $this->assertCommandSucceeded($commandResult); $this->assertCollectionDoesNotExist($this->getCollectionName()); $this->assertCollectionExists($this->toCollectionName); @@ -114,7 +97,7 @@ public function testRenameCollectionExistingTarget(): void $this->getDatabaseName(), $this->getCollectionName(), $this->getDatabaseName(), - $this->toCollectionName + $this->toCollectionName, ); $operation->execute($server); } @@ -128,7 +111,7 @@ public function testRenameNonexistentCollection(): void $this->getDatabaseName(), $this->getCollectionName(), $this->getDatabaseName(), - $this->toCollectionName + $this->toCollectionName, ); $operation->execute($this->getPrimaryServer()); } @@ -148,14 +131,14 @@ function (): void { $this->getCollectionName(), $this->getDatabaseName(), $this->toCollectionName, - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($server); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } } diff --git a/tests/Operation/RenameCollectionTest.php b/tests/Operation/RenameCollectionTest.php index f25eea78d..e0a438329 100644 --- a/tests/Operation/RenameCollectionTest.php +++ b/tests/Operation/RenameCollectionTest.php @@ -4,12 +4,11 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\RenameCollection; +use PHPUnit\Framework\Attributes\DataProvider; class RenameCollectionTest extends TestCase { - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); @@ -18,30 +17,16 @@ public function testConstructorOptionTypeChecks(array $options): void $this->getCollectionName(), $this->getDatabaseName(), $this->getCollectionName() . '.renamed', - $options + $options, ); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } - - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['dropTarget' => $value]; - } - - return $options; + return self::createOptionDataProvider([ + 'dropTarget' => self::getInvalidBooleanValues(), + 'session' => self::getInvalidSessionValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); } } diff --git a/tests/Operation/ReplaceOneFunctionalTest.php b/tests/Operation/ReplaceOneFunctionalTest.php new file mode 100644 index 000000000..7a15efbf4 --- /dev/null +++ b/tests/Operation/ReplaceOneFunctionalTest.php @@ -0,0 +1,38 @@ +observe( + function (): void { + $operation = new ReplaceOne( + $this->getDatabaseName(), + $this->getCollectionName(), + ['x' => 1], + TestObject::createForFixture(1), + ['codec' => new TestDocumentCodec()], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event): void { + $this->assertEquals( + (object) [ + '_id' => 1, + 'x' => (object) ['foo' => 'bar'], + 'encoded' => true, + ], + $event['started']->getCommand()->updates[0]->u ?? null, + ); + }, + ); + } +} diff --git a/tests/Operation/ReplaceOneTest.php b/tests/Operation/ReplaceOneTest.php index 21b6a396c..d74cd7566 100644 --- a/tests/Operation/ReplaceOneTest.php +++ b/tests/Operation/ReplaceOneTest.php @@ -2,64 +2,73 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\ReplaceOne; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use TypeError; class ReplaceOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), $filter, ['y' => 1]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorReplacementArgumentTypeCheck($replacement): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($replacement instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - /** - * @dataProvider provideReplacementDocuments - * @doesNotPerformAssertions - */ + #[DataProvider('provideReplacementDocuments')] + #[DoesNotPerformAssertions] public function testConstructorReplacementArgument($replacement): void { new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - /** - * @dataProvider provideUpdateDocuments - */ - public function testConstructorReplacementArgumentRequiresNoOperators($replacement): void + #[DataProvider('provideUpdateDocuments')] + public function testConstructorReplacementArgumentProhibitsUpdateDocument($replacement): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('First key in $replacement argument is an update operator'); + $this->expectExceptionMessage('First key in $replacement is an update operator'); new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - public function provideReplacementDocuments() + #[DataProvider('provideUpdatePipelines')] + #[DataProvider('provideEmptyUpdatePipelinesExcludingArray')] + public function testConstructorReplacementArgumentProhibitsUpdatePipeline($replacement): void { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessageMatches('#(\$replacement is an update pipeline)|(Expected \$replacement to have type "document" \(array or object\))#'); + new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); } - public function provideUpdateDocuments() + #[DataProvider('provideInvalidConstructorOptions')] + public function testConstructorOptionsTypeCheck($options): void { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), + $this->expectException(InvalidArgumentException::class); + new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['y' => 1], $options); + } + + public static function provideInvalidConstructorOptions() + { + return self::createOptionDataProvider([ + 'codec' => self::getInvalidDocumentCodecValues(), ]); } + + public function testCodecRejectsInvalidDocuments(): void + { + $this->expectExceptionObject(UnsupportedValueException::invalidEncodableValue([])); + + new ReplaceOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['y' => 1], ['codec' => new TestDocumentCodec()]); + } } diff --git a/tests/Operation/TestCase.php b/tests/Operation/TestCase.php index 2de8b0e68..4bb5c5cec 100644 --- a/tests/Operation/TestCase.php +++ b/tests/Operation/TestCase.php @@ -2,6 +2,10 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Model\BSONArray; +use MongoDB\Model\BSONDocument; use MongoDB\Tests\TestCase as BaseTestCase; /** @@ -9,4 +13,75 @@ */ abstract class TestCase extends BaseTestCase { + public static function provideReplacementDocuments(): array + { + return [ + 'replacement:array' => [['x' => 1]], + 'replacement:object' => [(object) ['x' => 1]], + 'replacement:Serializable' => [new BSONDocument(['x' => 1])], + 'replacement:Document' => [Document::fromPHP(['x' => 1])], + /* Note: empty arrays could also express a pipeline, but PHPLIB + * interprets them as a replacement document for BC. */ + 'empty_replacement:array' => [[]], + 'empty_replacement:object' => [(object) []], + 'empty_replacement:Serializable' => [new BSONDocument([])], + 'empty_replacement:Document' => [Document::fromPHP([])], + ]; + } + + public static function provideUpdateDocuments(): array + { + return [ + 'update:array' => [['$set' => ['x' => 1]]], + 'update:object' => [(object) ['$set' => ['x' => 1]]], + 'update:Serializable' => [new BSONDocument(['$set' => ['x' => 1]])], + 'update:Document' => [Document::fromPHP(['$set' => ['x' => 1]])], + ]; + } + + public static function provideUpdatePipelines(): array + { + return [ + 'pipeline:array' => [[['$set' => ['x' => 1]]]], + 'pipeline:Serializable' => [new BSONArray([['$set' => ['x' => 1]]])], + 'pipeline:PackedArray' => [PackedArray::fromPHP([['$set' => ['x' => 1]]])], + ]; + } + + public static function provideEmptyUpdatePipelines(): array + { + /* Empty update pipelines are accepted by the update and findAndModify + * commands (as NOPs); however, they are not supported for updates in + * libmongoc because empty arrays and documents have the same bson_t + * representation (libmongoc considers it an empty replacement for BC). + * For consistency, PHPLIB rejects empty pipelines for updateOne, + * updateMany, and findOneAndUpdate operations. Replace operations + * interpret empty arrays as replacement documents for BC, but rejects + * other representations. */ + return [ + 'empty_pipeline:array' => [[]], + 'empty_pipeline:Serializable' => [new BSONArray([])], + 'empty_pipeline:PackedArray' => [PackedArray::fromPHP([])], + ]; + } + + public static function provideEmptyUpdatePipelinesExcludingArray(): array + { + /* This data provider is used for replace operations, which accept empty + * arrays as replacement documents for BC. */ + return [ + 'empty_pipeline:Serializable' => [new BSONArray([])], + 'empty_pipeline:PackedArray' => [PackedArray::fromPHP([])], + ]; + } + + public static function provideInvalidUpdateValues(): array + { + return self::wrapValuesForDataProvider(self::getInvalidUpdateValues()); + } + + protected static function getInvalidUpdateValues(): array + { + return [123, 3.14, 'foo', true]; + } } diff --git a/tests/Operation/UpdateFunctionalTest.php b/tests/Operation/UpdateFunctionalTest.php index c0b6ba76a..b9424975b 100644 --- a/tests/Operation/UpdateFunctionalTest.php +++ b/tests/Operation/UpdateFunctionalTest.php @@ -5,19 +5,21 @@ use MongoDB\BSON\ObjectId; use MongoDB\Collection; use MongoDB\Driver\BulkWrite; +use MongoDB\Driver\Exception\LogicException; use MongoDB\Driver\WriteConcern; -use MongoDB\Exception\BadMethodCallException; use MongoDB\Exception\UnsupportedException; use MongoDB\Operation\Update; use MongoDB\Tests\CommandObserver; use MongoDB\UpdateResult; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Depends; +use stdClass; -use function version_compare; +use function is_array; class UpdateFunctionalTest extends FunctionalTestCase { - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -26,6 +28,65 @@ public function setUp(): void $this->collection = new Collection($this->manager, $this->getDatabaseName(), $this->getCollectionName()); } + #[DataProvider('provideFilterDocuments')] + public function testFilterDocuments($filter, stdClass $expectedFilter): void + { + (new CommandObserver())->observe( + function () use ($filter): void { + $operation = new Update( + $this->getDatabaseName(), + $this->getCollectionName(), + $filter, + ['$set' => ['x' => 1]], + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedFilter): void { + $this->assertEquals($expectedFilter, $event['started']->getCommand()->updates[0]->q ?? null); + }, + ); + } + + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DataProvider('provideReplacementDocumentLikePipeline')] + public function testUpdateDocuments($update, $expectedUpdate): void + { + if (is_array($expectedUpdate)) { + $this->skipIfServerVersion('<', '4.2.0', 'Pipeline-style updates are not supported'); + } + + (new CommandObserver())->observe( + function () use ($update): void { + $operation = new Update( + $this->getDatabaseName(), + $this->getCollectionName(), + ['x' => 1], + $update, + ); + + $operation->execute($this->getPrimaryServer()); + }, + function (array $event) use ($expectedUpdate): void { + $this->assertEquals($expectedUpdate, $event['started']->getCommand()->updates[0]->u ?? null); + }, + ); + } + + public static function provideReplacementDocumentLikePipeline(): array + { + /* Note: libmongoc encodes this replacement document as a BSON array + * because it resembles an update pipeline (see: CDRIVER-4658). */ + return [ + 'replacement_like_pipeline' => [ + (object) ['0' => ['$set' => ['x' => 1]]], + [(object) ['$set' => (object) ['x' => 1]]], + ], + ]; + } + public function testSessionOption(): void { (new CommandObserver())->observe( @@ -35,14 +96,14 @@ function (): void { $this->getCollectionName(), ['_id' => 1], ['$inc' => ['x' => 1]], - ['session' => $this->createSession()] + ['session' => $this->createSession()], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('lsid', $event['started']->getCommand()); - } + $this->assertObjectHasProperty('lsid', $event['started']->getCommand()); + }, ); } @@ -55,15 +116,15 @@ function (): void { $this->getCollectionName(), ['_id' => 1], ['$inc' => ['x' => 1]], - ['bypassDocumentValidation' => true] + ['bypassDocumentValidation' => true], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); + $this->assertObjectHasProperty('bypassDocumentValidation', $event['started']->getCommand()); $this->assertEquals(true, $event['started']->getCommand()->bypassDocumentValidation); - } + }, ); } @@ -76,29 +137,27 @@ function (): void { $this->getCollectionName(), ['_id' => 1], ['$inc' => ['x' => 1]], - ['bypassDocumentValidation' => false] + ['bypassDocumentValidation' => false], ); $operation->execute($this->getPrimaryServer()); }, function (array $event): void { - $this->assertObjectNotHasAttribute('bypassDocumentValidation', $event['started']->getCommand()); - } + $this->assertObjectNotHasProperty('bypassDocumentValidation', $event['started']->getCommand()); + }, ); } public function testHintOptionAndUnacknowledgedWriteConcernUnsupportedClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.2.0', '>=')) { - $this->markTestSkipped('hint is supported'); - } + $this->skipIfServerVersion('>=', '4.2.0', 'hint is supported'); $operation = new Update( $this->getDatabaseName(), $this->getCollectionName(), ['_id' => 1], ['$inc' => ['x' => 1]], - ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)] + ['hint' => '_id_', 'writeConcern' => new WriteConcern(0)], ); $this->expectException(UnsupportedException::class); @@ -225,43 +284,35 @@ public function testUnacknowledgedWriteConcern() return $result; } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesMatchedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getMatchedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesModifiedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getModifiedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedCount(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedCount(); } - /** - * @depends testUnacknowledgedWriteConcern - */ + #[Depends('testUnacknowledgedWriteConcern')] public function testUnacknowledgedWriteConcernAccessesUpsertedId(UpdateResult $result): void { - $this->expectException(BadMethodCallException::class); - $this->expectExceptionMessageMatches('/[\w:\\\\]+ should not be called for an unacknowledged write result/'); + $this->expectException(LogicException::class); + $this->expectExceptionMessageMatches('/[\w:\\\\\(\)]+ should not be called for an unacknowledged write result/'); $result->getUpsertedId(); } @@ -275,7 +326,7 @@ private function createFixtures(int $n): void for ($i = 1; $i <= $n; $i++) { $bulkWrite->insert([ '_id' => $i, - 'x' => (integer) ($i . $i), + 'x' => (int) ($i . $i), ]); } diff --git a/tests/Operation/UpdateManyTest.php b/tests/Operation/UpdateManyTest.php index cf1d770a6..30c0a0836 100644 --- a/tests/Operation/UpdateManyTest.php +++ b/tests/Operation/UpdateManyTest.php @@ -2,64 +2,43 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\UpdateMany; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use TypeError; class UpdateManyTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($update instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideUpdateDocuments - * @doesNotPerformAssertions - */ + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DoesNotPerformAssertions] public function testConstructorUpdateArgument($update): void { new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideReplacementDocuments - */ - public function testConstructorUpdateArgumentRequiresOperators($replacement): void + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); - } - - public function provideReplacementDocuments() - { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); - } - - public function provideUpdateDocuments() - { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), - ]); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new UpdateMany($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } } diff --git a/tests/Operation/UpdateOneTest.php b/tests/Operation/UpdateOneTest.php index 9933692d8..25d67f45c 100644 --- a/tests/Operation/UpdateOneTest.php +++ b/tests/Operation/UpdateOneTest.php @@ -2,64 +2,43 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; use MongoDB\Exception\InvalidArgumentException; -use MongoDB\Model\BSONDocument; use MongoDB\Operation\UpdateOne; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\DoesNotPerformAssertions; +use TypeError; class UpdateOneTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); + $this->expectException($update instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideUpdateDocuments - * @doesNotPerformAssertions - */ + #[DataProvider('provideUpdateDocuments')] + #[DataProvider('provideUpdatePipelines')] + #[DoesNotPerformAssertions] public function testConstructorUpdateArgument($update): void { new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideReplacementDocuments - */ - public function testConstructorUpdateArgumentRequiresOperators($replacement): void + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testConstructorUpdateArgumentProhibitsReplacementDocumentOrEmptyPipeline($update): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected an update document with operator as first key or a pipeline'); - new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $replacement); - } - - public function provideReplacementDocuments() - { - return $this->wrapValuesForDataProvider([ - ['y' => 1], - (object) ['y' => 1], - new BSONDocument(['y' => 1]), - ]); - } - - public function provideUpdateDocuments() - { - return $this->wrapValuesForDataProvider([ - ['$set' => ['y' => 1]], - (object) ['$set' => ['y' => 1]], - new BSONDocument(['$set' => ['y' => 1]]), - ]); + $this->expectExceptionMessage('Expected update operator(s) or non-empty pipeline for $update'); + new UpdateOne($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } } diff --git a/tests/Operation/UpdateSearchIndexTest.php b/tests/Operation/UpdateSearchIndexTest.php new file mode 100644 index 000000000..f61db793e --- /dev/null +++ b/tests/Operation/UpdateSearchIndexTest.php @@ -0,0 +1,25 @@ +expectException(InvalidArgumentException::class); + new UpdateSearchIndex($this->getDatabaseName(), $this->getCollectionName(), '', []); + } + + #[DataProvider('provideInvalidDocumentValues')] + public function testConstructorIndexDefinitionMustBeADocument($definition): void + { + $this->expectException($definition instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); + new UpdateSearchIndex($this->getDatabaseName(), $this->getCollectionName(), 'index name', $definition); + } +} diff --git a/tests/Operation/UpdateTest.php b/tests/Operation/UpdateTest.php index 2e3773d34..9617c77e8 100644 --- a/tests/Operation/UpdateTest.php +++ b/tests/Operation/UpdateTest.php @@ -2,72 +2,97 @@ namespace MongoDB\Tests\Operation; +use MongoDB\BSON\PackedArray; +use MongoDB\Driver\WriteConcern; use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Update; +use PHPUnit\Framework\Attributes\DataProvider; +use TypeError; class UpdateTest extends TestCase { - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidDocumentValues')] public function testConstructorFilterArgumentTypeCheck($filter): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$filter to have type "array or object" but found "[\w ]+"/'); + $this->expectException($filter instanceof PackedArray ? InvalidArgumentException::class : TypeError::class); new Update($this->getDatabaseName(), $this->getCollectionName(), $filter, ['$set' => ['x' => 1]]); } - /** - * @dataProvider provideInvalidDocumentValues - */ + #[DataProvider('provideInvalidUpdateValues')] public function testConstructorUpdateArgumentTypeCheck($update): void { - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessageMatches('/Expected \$update to have type "array or object" but found "[\w ]+"/'); + $this->expectException(TypeError::class); new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['y' => 1], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['arrayFilters' => $value]; - } - - foreach ($this->getInvalidBooleanValues() as $value) { - $options[][] = ['bypassDocumentValidation' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['multi' => $value]; - } + return self::createOptionDataProvider([ + 'arrayFilters' => self::getInvalidArrayValues(), + 'bypassDocumentValidation' => self::getInvalidBooleanValues(), + 'collation' => self::getInvalidDocumentValues(), + 'hint' => self::getInvalidHintValues(), + 'multi' => self::getInvalidBooleanValues(), + 'sort' => self::getInvalidDocumentValues(), + 'session' => self::getInvalidSessionValues(), + 'upsert' => self::getInvalidBooleanValues(), + 'writeConcern' => self::getInvalidWriteConcernValues(), + ]); + } - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } + #[DataProvider('provideReplacementDocuments')] + #[DataProvider('provideEmptyUpdatePipelines')] + public function testConstructorMultiOptionProhibitsReplacementDocumentOrEmptyPipeline($update): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"multi" option cannot be true unless $update has update operator(s) or non-empty pipeline'); + new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], $update, ['multi' => true]); + } - foreach ($this->getInvalidBooleanValues(true) as $value) { - $options[][] = ['upsert' => $value]; - } + public function testConstructorMultiOptionProhibitsSortOption(): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"sort" option cannot be used with multi-document updates'); + new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['$set' => ['x' => 2]], ['multi' => true, 'sort' => ['x' => 1]]); + } - foreach ($this->getInvalidWriteConcernValues() as $value) { - $options[][] = ['writeConcern' => $value]; - } + public function testExplainableCommandDocument(): void + { + $options = [ + 'arrayFilters' => [['x' => 1]], + 'bypassDocumentValidation' => true, + 'collation' => ['locale' => 'fr'], + 'comment' => 'explain me', + 'hint' => '_id_', + 'multi' => true, + 'upsert' => true, + 'let' => ['a' => 3], + 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), + ]; + $operation = new Update($this->getDatabaseName(), $this->getCollectionName(), ['x' => 1], ['$set' => ['x' => 2]], $options); - return $options; + $expected = [ + 'update' => $this->getCollectionName(), + 'bypassDocumentValidation' => true, + 'updates' => [ + [ + 'q' => ['x' => 1], + 'u' => ['$set' => ['x' => 2]], + 'multi' => true, + 'upsert' => true, + 'arrayFilters' => [['x' => 1]], + 'hint' => '_id_', + 'collation' => (object) ['locale' => 'fr'], + ], + ], + ]; + $this->assertEquals($expected, $operation->getCommandDocument()); } } diff --git a/tests/Operation/WatchFunctionalTest.php b/tests/Operation/WatchFunctionalTest.php index e55be49d5..522b01ba4 100644 --- a/tests/Operation/WatchFunctionalTest.php +++ b/tests/Operation/WatchFunctionalTest.php @@ -3,9 +3,13 @@ namespace MongoDB\Tests\Operation; use Closure; +use Generator; use Iterator; -use MongoDB\BSON\TimestampInterface; +use MongoDB\BSON\Document; use MongoDB\ChangeStream; +use MongoDB\Codec\DecodeIfSupported; +use MongoDB\Codec\DocumentCodec; +use MongoDB\Codec\EncodeIfSupported; use MongoDB\Driver\Cursor; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\ConnectionTimeoutException; @@ -13,11 +17,16 @@ use MongoDB\Driver\Exception\ServerException; use MongoDB\Driver\Monitoring\CommandSucceededEvent; use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\Server; use MongoDB\Driver\WriteConcern; use MongoDB\Exception\ResumeTokenException; +use MongoDB\Exception\UnsupportedValueException; use MongoDB\Operation\InsertOne; use MongoDB\Operation\Watch; use MongoDB\Tests\CommandObserver; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Constraint\ObjectHasProperty; use PHPUnit\Framework\ExpectationFailedException; use ReflectionClass; use stdClass; @@ -27,65 +36,73 @@ use function assert; use function bin2hex; use function microtime; -use function MongoDB\server_supports_feature; use function sprintf; -use function version_compare; -/** - * @group matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster - * @group matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster - */ +#[Group('matrix-testing-exclude-server-4.2-driver-4.0-topology-sharded_cluster')] +#[Group('matrix-testing-exclude-server-4.4-driver-4.0-topology-sharded_cluster')] +#[Group('matrix-testing-exclude-server-5.0-driver-4.0-topology-sharded_cluster')] class WatchFunctionalTest extends FunctionalTestCase { public const INTERRUPTED = 11601; public const NOT_PRIMARY = 10107; - /** @var integer */ - private static $wireVersionForStartAtOperationTime = 7; - - /** @var array */ - private $defaultOptions = ['maxAwaitTimeMS' => 500]; + private array $defaultOptions = ['maxAwaitTimeMS' => 500]; public function setUp(): void { parent::setUp(); $this->skipIfChangeStreamIsNotSupported(); - $this->createCollection(); + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); } - /** - * Prose test 1: "ChangeStream must continuously track the last seen - * resumeToken" - */ - public function testGetResumeToken(): void + public static function provideCodecOptions(): Generator { - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is supported'); - } + yield 'No codec' => [ + 'options' => [], + 'getIdentifier' => static fn (object $document): object => $document->_id, + ]; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); - $changeStream = $operation->execute($this->getPrimaryServer()); + $codec = new class implements DocumentCodec { + use DecodeIfSupported; + use EncodeIfSupported; - $changeStream->rewind(); - $this->assertFalse($changeStream->valid()); - $this->assertNull($changeStream->getResumeToken()); + public function canDecode($value): bool + { + return $value instanceof Document; + } - $this->insertDocument(['x' => 1]); - $this->insertDocument(['x' => 2]); + public function canEncode($value): bool + { + return false; + } - $this->advanceCursorUntilValid($changeStream); - $this->assertSameDocument($changeStream->current()->_id, $changeStream->getResumeToken()); + public function decode($value): stdClass + { + if (! $value instanceof Document) { + throw UnsupportedValueException::invalidDecodableValue($value); + } - $changeStream->next(); - $this->assertTrue($changeStream->valid()); - $this->assertSameDocument($changeStream->current()->_id, $changeStream->getResumeToken()); + return (object) [ + 'id' => $value->get('_id')->toPHP(), + 'fullDocument' => $value->get('fullDocument')->toPHP(), + ]; + } - $this->insertDocument(['x' => 3]); + public function encode($value): Document + { + return Document::fromPHP([]); + } + }; - $this->advanceCursorUntilValid($changeStream); - $this->assertSameDocument($changeStream->current()->_id, $changeStream->getResumeToken()); + yield 'Codec' => [ + 'options' => ['codec' => $codec], + 'getIdentifier' => static function (object $document): object { + self::assertObjectHasProperty('id', $document); + + return $document->id; + }, + ]; } /** @@ -106,13 +123,16 @@ public function testGetResumeToken(): void * Expected result: getResumeToken must return the _id of the previous * document returned. */ - public function testGetResumeTokenWithPostBatchResumeToken(): void + #[DataProvider('provideCodecOptions')] + public function testGetResumeTokenWithPostBatchResumeToken(array $options, Closure $getIdentifier): void { - if (! $this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is not supported'); - } - - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $events = []; @@ -122,7 +142,7 @@ function () use ($operation, &$changeStream): void { }, function (array $event) use (&$events): void { $events[] = $event; - } + }, ); $this->assertCount(1, $events); @@ -144,14 +164,14 @@ function () use ($changeStream): void { }, function (array $event) use (&$lastEvent): void { $lastEvent = $event; - } + }, ); $this->assertNotNull($lastEvent); $this->assertSame('getMore', $lastEvent['started']->getCommandName()); $postBatchResumeToken = $this->getPostBatchResumeTokenFromReply($lastEvent['succeeded']->getReply()); - $this->assertSameDocument($changeStream->current()->_id, $changeStream->getResumeToken()); + $this->assertSameDocument($getIdentifier($changeStream->current()), $changeStream->getResumeToken()); $changeStream->next(); $this->assertSameDocument($postBatchResumeToken, $changeStream->getResumeToken()); @@ -165,7 +185,7 @@ public function testNextResumesAfterConnectionException(): void * a socket timeout that is less than the change stream's maxAwaitTimeMS * option. */ $manager = static::createTestManager(null, ['socketTimeoutMS' => 50]); - $primaryServer = $manager->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + $primaryServer = $manager->selectServer(); $operation = new Watch($manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($primaryServer); @@ -179,7 +199,7 @@ function () use ($changeStream): void { }, function (array $event) use (&$commands): void { $commands[] = $event['started']->getCommandName(); - } + }, ); $expectedCommands = [ @@ -206,10 +226,6 @@ function (array $event) use (&$commands): void { public function testResumeBeforeReceivingAnyResultsIncludesPostBatchResumeToken(): void { - if (! $this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is not supported'); - } - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $events = []; @@ -220,7 +236,7 @@ function () use ($operation, &$changeStream): void { }, function (array $event) use (&$events): void { $events[] = $event; - } + }, ); $this->assertCount(1, $events); @@ -242,114 +258,35 @@ function () use ($changeStream): void { }, function (array $event) use (&$events): void { $events[] = $event; - } + }, ); $this->assertCount(3, $events); $this->assertSame('getMore', $events[0]['started']->getCommandName()); - $this->arrayHasKey('failed', $events[0]); + $this->assertArrayHasKey('failed', $events[0]); $this->assertSame('aggregate', $events[1]['started']->getCommandName()); $this->assertResumeAfter($postBatchResumeToken, $events[1]['started']->getCommand()); - $this->arrayHasKey('succeeded', $events[1]); + $this->assertArrayHasKey('succeeded', $events[1]); // Original cursor is freed immediately after the change stream resumes $this->assertSame('killCursors', $events[2]['started']->getCommandName()); - $this->arrayHasKey('succeeded', $events[2]); + $this->assertArrayHasKey('succeeded', $events[2]); $this->assertFalse($changeStream->valid()); } private function assertResumeAfter($expectedResumeToken, stdClass $command): void { - $this->assertObjectHasAttribute('pipeline', $command); + $this->assertObjectHasProperty('pipeline', $command); $this->assertIsArray($command->pipeline); $this->assertArrayHasKey(0, $command->pipeline); - $this->assertObjectHasAttribute('$changeStream', $command->pipeline[0]); - $this->assertObjectHasAttribute('resumeAfter', $command->pipeline[0]->{'$changeStream'}); + $this->assertObjectHasProperty('$changeStream', $command->pipeline[0]); + $this->assertObjectHasProperty('resumeAfter', $command->pipeline[0]->{'$changeStream'}); $this->assertEquals($expectedResumeToken, $command->pipeline[0]->{'$changeStream'}->resumeAfter); } - /** - * Prose test 9: "$changeStream stage for ChangeStream against a server - * >=4.0 and <4.0.7 that has not received any results yet MUST include a - * startAtOperationTime option when resuming a changestream." - */ - public function testResumeBeforeReceivingAnyResultsIncludesStartAtOperationTime(): void - { - if (! $this->isStartAtOperationTimeSupported()) { - $this->markTestSkipped('startAtOperationTime is not supported'); - } - - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken takes precedence over startAtOperationTime'); - } - - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); - - $events = []; - - (new CommandObserver())->observe( - function () use ($operation, &$changeStream): void { - $changeStream = $operation->execute($this->getPrimaryServer()); - }, - function (array $event) use (&$events): void { - $events[] = $event; - } - ); - - $this->assertCount(1, $events); - $this->assertSame('aggregate', $events[0]['started']->getCommandName()); - $reply = $events[0]['succeeded']->getReply(); - $this->assertObjectHasAttribute('operationTime', $reply); - $operationTime = $reply->operationTime; - $this->assertInstanceOf(TimestampInterface::class, $operationTime); - - $this->assertFalse($changeStream->valid()); - $this->forceChangeStreamResume(); - - $this->assertNoCommandExecuted(function () use ($changeStream): void { - $changeStream->rewind(); - }); - - $events = []; - - (new CommandObserver())->observe( - function () use ($changeStream): void { - $changeStream->next(); - }, - function (array $event) use (&$events): void { - $events[] = $event; - } - ); - - $this->assertCount(3, $events); - - $this->assertSame('getMore', $events[0]['started']->getCommandName()); - $this->arrayHasKey('failed', $events[0]); - - $this->assertSame('aggregate', $events[1]['started']->getCommandName()); - $this->assertStartAtOperationTime($operationTime, $events[1]['started']->getCommand()); - $this->arrayHasKey('succeeded', $events[1]); - - // Original cursor is freed immediately after the change stream resumes - $this->assertSame('killCursors', $events[2]['started']->getCommandName()); - $this->arrayHasKey('succeeded', $events[2]); - - $this->assertFalse($changeStream->valid()); - } - - private function assertStartAtOperationTime(TimestampInterface $expectedOperationTime, stdClass $command): void - { - $this->assertObjectHasAttribute('pipeline', $command); - $this->assertIsArray($command->pipeline); - $this->assertArrayHasKey(0, $command->pipeline); - $this->assertObjectHasAttribute('$changeStream', $command->pipeline[0]); - $this->assertObjectHasAttribute('startAtOperationTime', $command->pipeline[0]->{'$changeStream'}); - $this->assertEquals($expectedOperationTime, $command->pipeline[0]->{'$changeStream'}->startAtOperationTime); - } - public function testRewindMultipleTimesWithResults(): void { $this->skipIfIsShardedCluster('Cursor needs to be advanced multiple times and can\'t be rewound afterwards.'); @@ -434,9 +371,16 @@ public function testRewindMultipleTimesWithNoResults(): void $this->assertNull($changeStream->current()); } - public function testNoChangeAfterResumeBeforeInsert(): void + #[DataProvider('provideCodecOptions')] + public function testNoChangeAfterResumeBeforeInsert(array $options, Closure $getIdentifier): void { - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertNoCommandExecuted(function () use ($changeStream): void { @@ -448,15 +392,7 @@ public function testNoChangeAfterResumeBeforeInsert(): void $this->advanceCursorUntilValid($changeStream); - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 1, 'x' => 'foo'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 1], - ]; - - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 1, 'x' => 'foo'], $changeStream->current()->fullDocument); $this->forceChangeStreamResume(); @@ -467,15 +403,7 @@ public function testNoChangeAfterResumeBeforeInsert(): void $this->advanceCursorUntilValid($changeStream); - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 2, 'x' => 'bar'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 2], - ]; - - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 2, 'x' => 'bar'], $changeStream->current()->fullDocument); } public function testResumeMultipleTimesInSuccession(): void @@ -673,16 +601,13 @@ public function testInitialCursorIsNotClosed(): void /* The spec requests that we assert that the cursor returned from the * aggregate command is not closed on the driver side. We will verify * this by checking that the cursor ID is non-zero and that libmongoc - * reports the cursor as alive. While the cursor ID is easily accessed - * through ChangeStream, we'll need to use reflection to access the - * internal Cursor and call isDead(). */ - $this->assertNotEquals('0', (string) $changeStream->getCursorId()); + * reports the cursor as alive. While the cursor ID is accessed through + * ChangeStream, we'll need to use reflection to access the internal + * Cursor and call isDead(). */ + $this->assertNotEquals(0, $changeStream->getCursorId()); $rc = new ReflectionClass(ChangeStream::class); - $rp = $rc->getProperty('iterator'); - $rp->setAccessible(true); - - $iterator = $rp->getValue($changeStream); + $iterator = $rc->getProperty('iterator')->getValue($changeStream); $this->assertInstanceOf('IteratorIterator', $iterator); @@ -699,9 +624,7 @@ public function testInitialCursorIsNotClosed(): void */ public function testResumeTokenNotFoundClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '>=')) { - $this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)'); - } + $this->skipIfServerVersion('>=', '4.1.8', 'Server rejects change streams that modify resume token (SERVER-37786)'); $pipeline = [['$project' => ['_id' => 0]]]; @@ -727,9 +650,7 @@ public function testResumeTokenNotFoundClientSideError(): void */ public function testResumeTokenNotFoundServerSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '<')) { - $this->markTestSkipped('Server does not reject change streams that modify resume token'); - } + $this->skipIfServerVersion('<', '4.1.8', 'Server does not reject change streams that modify resume token'); $pipeline = [['$project' => ['_id' => 0]]]; @@ -750,9 +671,7 @@ public function testResumeTokenNotFoundServerSideError(): void */ public function testResumeTokenInvalidTypeClientSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '>=')) { - $this->markTestSkipped('Server rejects change streams that modify resume token (SERVER-37786)'); - } + $this->skipIfServerVersion('>=', '4.1.8', 'Server rejects change streams that modify resume token (SERVER-37786)'); $pipeline = [['$project' => ['_id' => ['$literal' => 'foo']]]]; @@ -778,9 +697,7 @@ public function testResumeTokenInvalidTypeClientSideError(): void */ public function testResumeTokenInvalidTypeServerSideError(): void { - if (version_compare($this->getServerVersion(), '4.1.8', '<')) { - $this->markTestSkipped('Server does not reject change streams that modify resume token'); - } + $this->skipIfServerVersion('<', '4.1.8', 'Server does not reject change streams that modify resume token'); $pipeline = [['$project' => ['_id' => ['$literal' => 'foo']]]]; @@ -860,9 +777,16 @@ public function testMaxAwaitTimeMS(): void } } - public function testRewindExtractsResumeTokenAndNextResumes(): void + #[DataProvider('provideCodecOptions')] + public function testRewindExtractsResumeTokenAndNextResumes(array $options, Closure $getIdentifier): void { - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->insertDocument(['_id' => 1, 'x' => 'foo']); @@ -878,9 +802,14 @@ public function testRewindExtractsResumeTokenAndNextResumes(): void $this->advanceCursorUntilValid($changeStream); - $resumeToken = $changeStream->current()->_id; - $options = ['resumeAfter' => $resumeToken] + $this->defaultOptions; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $resumeToken = $getIdentifier($changeStream->current()); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['resumeAfter' => $resumeToken] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertSameDocument($resumeToken, $changeStream->getResumeToken()); @@ -896,33 +825,26 @@ public function testRewindExtractsResumeTokenAndNextResumes(): void } $this->assertSame(0, $changeStream->key()); - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 2, 'x' => 'bar'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 2], - ]; - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 2, 'x' => 'bar'], $changeStream->current()->fullDocument); $this->forceChangeStreamResume(); $this->advanceCursorUntilValid($changeStream); $this->assertSame(1, $changeStream->key()); - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 3, 'x' => 'baz'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 3], - ]; - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 3, 'x' => 'baz'], $changeStream->current()->fullDocument); } - public function testResumeAfterOption(): void + #[DataProvider('provideCodecOptions')] + public function testResumeAfterOption(array $options, Closure $getIdentifier): void { - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $changeStream->rewind(); @@ -933,10 +855,15 @@ public function testResumeAfterOption(): void $this->advanceCursorUntilValid($changeStream); - $resumeToken = $changeStream->current()->_id; + $resumeToken = $getIdentifier($changeStream->current()); - $options = $this->defaultOptions + ['resumeAfter' => $resumeToken]; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['resumeAfter' => $resumeToken] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertSameDocument($resumeToken, $changeStream->getResumeToken()); @@ -951,24 +878,21 @@ public function testResumeAfterOption(): void $this->assertTrue($changeStream->valid()); } - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 2, 'x' => 'bar'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 2], - ]; - - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 2, 'x' => 'bar'], $changeStream->current()->fullDocument); } - public function testStartAfterOption(): void + #[DataProvider('provideCodecOptions')] + public function testStartAfterOption(array $options, Closure $getIdentifier): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('startAfter is not supported'); - } + $this->skipIfServerVersion('<', '4.1.1', 'startAfter is not supported'); - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $changeStream->rewind(); @@ -979,10 +903,15 @@ public function testStartAfterOption(): void $this->advanceCursorUntilValid($changeStream); - $resumeToken = $changeStream->current()->_id; + $resumeToken = $getIdentifier($changeStream->current()); - $options = $this->defaultOptions + ['startAfter' => $resumeToken]; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['startAfter' => $resumeToken] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertSameDocument($resumeToken, $changeStream->getResumeToken()); @@ -997,20 +926,10 @@ public function testStartAfterOption(): void $this->assertTrue($changeStream->valid()); } - $expectedResult = [ - '_id' => $changeStream->current()->_id, - 'operationType' => 'insert', - 'fullDocument' => ['_id' => 2, 'x' => 'bar'], - 'ns' => ['db' => $this->getDatabaseName(), 'coll' => $this->getCollectionName()], - 'documentKey' => ['_id' => 2], - ]; - - $this->assertMatchesDocument($expectedResult, $changeStream->current()); + $this->assertMatchesDocument(['_id' => 2, 'x' => 'bar'], $changeStream->current()->fullDocument); } - /** - * @dataProvider provideTypeMapOptionsAndExpectedChangeDocument - */ + #[DataProvider('provideTypeMapOptionsAndExpectedChangeDocument')] public function testTypeMapOption(array $typeMap, $expectedChangeDocument): void { $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], ['typeMap' => $typeMap] + $this->defaultOptions); @@ -1026,7 +945,7 @@ public function testTypeMapOption(array $typeMap, $expectedChangeDocument): void $this->assertMatchesDocument($expectedChangeDocument, $changeStream->current()); } - public function provideTypeMapOptionsAndExpectedChangeDocument() + public static function provideTypeMapOptionsAndExpectedChangeDocument() { /* Note: the "_id" and "ns" fields are purposefully omitted because the * resume token's value cannot be anticipated and the collection name, @@ -1097,9 +1016,9 @@ public function testResumeTokenNotFoundDoesNotAdvanceKey(): void try { $this->advanceCursorUntilValid($changeStream); $this->fail('Exception for missing resume token was not thrown'); - } catch (ResumeTokenException $e) { + } catch (ResumeTokenException) { /* On server versions < 4.1.8, a client-side error is thrown. */ - } catch (ServerException $e) { + } catch (ServerException) { /* On server versions >= 4.1.8, the error is thrown server-side. */ } @@ -1109,8 +1028,7 @@ public function testResumeTokenNotFoundDoesNotAdvanceKey(): void try { $changeStream->next(); $this->fail('Exception for missing resume token was not thrown'); - } catch (ResumeTokenException $e) { - } catch (ServerException $e) { + } catch (ResumeTokenException | ServerException) { } $this->assertFalse($changeStream->valid()); @@ -1140,7 +1058,7 @@ function (array $event) use (&$originalSession): void { if (isset($command->aggregate)) { $originalSession = bin2hex((string) $command->lsid->id); } - } + }, ); $changeStream->rewind(); @@ -1153,7 +1071,7 @@ function () use (&$changeStream): void { function (array $event) use (&$sessionAfterResume, &$commands): void { $commands[] = $event['started']->getCommandName(); $sessionAfterResume[] = bin2hex((string) $event['started']->getCommand()->lsid->id); - } + }, ); $expectedCommands = [ @@ -1178,8 +1096,8 @@ function (array $event) use (&$sessionAfterResume, &$commands): void { public function testSessionFreed(): void { - if ($this->isShardedCluster() && version_compare($this->getServerVersion(), '5.1.0', '>=')) { - $this->markTestSkipped('mongos still reports non-zero cursor ID for invalidated change stream (SERVER-60764)'); + if ($this->isShardedCluster()) { + $this->skipIfServerVersion('>=', '5.1.0', 'mongos still reports non-zero cursor ID for invalidated change stream (SERVER-60764)'); } $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); @@ -1187,12 +1105,11 @@ public function testSessionFreed(): void $rc = new ReflectionClass($changeStream); $rp = $rc->getProperty('resumeCallable'); - $rp->setAccessible(true); $this->assertIsCallable($rp->getValue($changeStream)); // Invalidate the cursor to verify that resumeCallable is unset when the cursor is exhausted. - $this->dropCollection(); + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); $this->advanceCursorUntilValid($changeStream); @@ -1235,7 +1152,7 @@ function (array $event) use (&$aggregateCommands): void { } $aggregateCommands[] = (array) $command; - } + }, ); $this->assertCount(2, $aggregateCommands); @@ -1244,20 +1161,20 @@ function (array $event) use (&$aggregateCommands): void { $aggregateCommands[0]['pipeline'][0]->{'$changeStream'}, $this->logicalNot( $this->logicalOr( - $this->objectHasAttribute('resumeAfter'), - $this->objectHasAttribute('startAfter'), - $this->objectHasAttribute('startAtOperationTime') - ) - ) + new ObjectHasProperty('resumeAfter'), + new ObjectHasProperty('startAfter'), + new ObjectHasProperty('startAtOperationTime'), + ), + ), ); $this->assertThat( $aggregateCommands[1]['pipeline'][0]->{'$changeStream'}, $this->logicalOr( - $this->objectHasAttribute('resumeAfter'), - $this->objectHasAttribute('startAfter'), - $this->objectHasAttribute('startAtOperationTime') - ) + new ObjectHasProperty('resumeAfter'), + new ObjectHasProperty('startAfter'), + new ObjectHasProperty('startAtOperationTime'), + ), ); $aggregateCommands = array_map( @@ -1266,14 +1183,14 @@ function (array $aggregateCommand) { if (isset($aggregateCommand['pipeline'][0]->{'$changeStream'})) { $aggregateCommand['pipeline'][0]->{'$changeStream'} = array_diff_key( (array) $aggregateCommand['pipeline'][0]->{'$changeStream'}, - ['resumeAfter' => false, 'startAfter' => false, 'startAtOperationTime' => false] + ['resumeAfter' => false, 'startAfter' => false, 'startAtOperationTime' => false], ); } // Remove options we don't want to compare between commands return array_diff_key($aggregateCommand, ['lsid' => false, '$clusterTime' => false]); }, - $aggregateCommands + $aggregateCommands, ); // Ensure options in original and resuming aggregate command match @@ -1286,10 +1203,6 @@ function (array $aggregateCommand) { */ public function testErrorDuringAggregateCommandDoesNotCauseResume(): void { - if (version_compare($this->getServerVersion(), '4.0.0', '<')) { - $this->markTestSkipped('failCommand is not supported'); - } - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $commandCount = 0; @@ -1308,7 +1221,7 @@ function () use ($operation): void { }, function (array $event) use (&$commandCount): void { $commandCount++; - } + }, ); $this->assertSame(1, $commandCount); @@ -1324,70 +1237,33 @@ public function testOriginalReadPreferenceIsPreservedOnResume(): void $this->markTestSkipped('Test does not apply to sharded clusters'); } - $readPreference = new ReadPreference('secondary'); + $readPreference = new ReadPreference(ReadPreference::SECONDARY); $options = ['readPreference' => $readPreference] + $this->defaultOptions; $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); try { $secondary = $this->manager->selectServer($readPreference); - } catch (ConnectionTimeoutException $e) { + } catch (ConnectionTimeoutException) { $this->markTestSkipped('Secondary is not available'); } $changeStream = $operation->execute($secondary); $previousCursorId = $changeStream->getCursorId(); - $this->forceChangeStreamResume(); + $this->forceChangeStreamResume($secondary); $changeStream->next(); - $this->assertNotSame($previousCursorId, $changeStream->getCursorId()); + $this->assertNotEquals($previousCursorId, $changeStream->getCursorId()); $getCursor = Closure::bind( - function () { - return $this->iterator->getInnerIterator(); - }, + fn () => $this->iterator->getInnerIterator(), $changeStream, - ChangeStream::class + ChangeStream::class, ); $cursor = $getCursor(); assert($cursor instanceof Cursor); self::assertTrue($cursor->getServer()->isSecondary()); } - /** - * Prose test 12 - * For a ChangeStream under these conditions: - * - Running against a server <4.0.7. - * - The batch is empty or has been iterated to the last document. - * Expected result: - * - getResumeToken must return the _id of the last document returned if one exists. - * - getResumeToken must return resumeAfter from the initial aggregate if the option was specified. - * - If resumeAfter was not specified, the getResumeToken result must be empty. - */ - public function testGetResumeTokenReturnsOriginalResumeTokenOnEmptyBatch(): void - { - if ($this->isPostBatchResumeTokenSupported()) { - $this->markTestSkipped('postBatchResumeToken is supported'); - } - - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); - $changeStream = $operation->execute($this->getPrimaryServer()); - - $this->assertNull($changeStream->getResumeToken()); - - $this->insertDocument(['x' => 1]); - - $changeStream->next(); - $this->assertTrue($changeStream->valid()); - $resumeToken = $changeStream->getResumeToken(); - $this->assertSame($resumeToken, $changeStream->current()->_id); - - $options = ['resumeAfter' => $resumeToken] + $this->defaultOptions; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); - $changeStream = $operation->execute($this->getPrimaryServer()); - - $this->assertSame($resumeToken, $changeStream->getResumeToken()); - } - /** * Prose test 14 * For a ChangeStream under these conditions: @@ -1399,15 +1275,18 @@ public function testGetResumeTokenReturnsOriginalResumeTokenOnEmptyBatch(): void * - getResumeToken must return resumeAfter from the initial aggregate if the option was specified. * - If neither the startAfter nor resumeAfter options were specified, the getResumeToken result must be empty. */ - public function testResumeTokenBehaviour(): void + #[DataProvider('provideCodecOptions')] + public function testResumeTokenBehaviour(array $options, Closure $getIdentifier): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } - $this->skipIfIsShardedCluster('Resume token behaviour can\'t be reliably tested on sharded clusters.'); - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + $options + $this->defaultOptions, + ); $lastOpTime = null; @@ -1418,7 +1297,7 @@ public function testResumeTokenBehaviour(): void $this->assertInstanceOf(CommandSucceededEvent::class, $event['succeeded']); $reply = $event['succeeded']->getReply(); - $this->assertObjectHasAttribute('operationTime', $reply); + $this->assertObjectHasProperty('operationTime', $reply); $lastOpTime = $reply->operationTime; }); @@ -1431,22 +1310,37 @@ public function testResumeTokenBehaviour(): void $this->insertDocument(['x' => 2]); // Test startAfter option - $options = ['startAfter' => $resumeToken] + $this->defaultOptions; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['startAfter' => $resumeToken] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertEquals($resumeToken, $changeStream->getResumeToken()); // Test resumeAfter option - $options = ['resumeAfter' => $resumeToken] + $this->defaultOptions; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['resumeAfter' => $resumeToken] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertEquals($resumeToken, $changeStream->getResumeToken()); // Test without option - $options = ['startAtOperationTime' => $lastOpTime] + $this->defaultOptions; - $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); + $operation = new Watch( + $this->manager, + $this->getDatabaseName(), + $this->getCollectionName(), + [], + ['startAtOperationTime' => $lastOpTime] + $options + $this->defaultOptions, + ); $changeStream = $operation->execute($this->getPrimaryServer()); $this->assertNull($changeStream->getResumeToken()); @@ -1460,9 +1354,7 @@ public function testResumeTokenBehaviour(): void */ public function testResumingChangeStreamWithoutPreviousResultsIncludesStartAfterOption(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } + $this->skipIfServerVersion('<', '4.1.1', 'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1491,12 +1383,12 @@ function (array $event) use (&$aggregateCommand): void { } $aggregateCommand = $event['started']->getCommand(); - } + }, ); $this->assertNotNull($aggregateCommand); - $this->assertObjectNotHasAttribute('resumeAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); - $this->assertObjectHasAttribute('startAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); + $this->assertObjectNotHasProperty('resumeAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); + $this->assertObjectHasProperty('startAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); } /** @@ -1507,9 +1399,7 @@ function (array $event) use (&$aggregateCommand): void { */ public function testResumingChangeStreamWithPreviousResultsIncludesResumeAfterOption(): void { - if (version_compare($this->getServerVersion(), '4.1.1', '<')) { - $this->markTestSkipped('Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); - } + $this->skipIfServerVersion('<', '4.1.1', 'Testing resumeAfter and startAfter can only be tested on servers >= 4.1.1'); $operation = new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $this->defaultOptions); $changeStream = $operation->execute($this->getPrimaryServer()); @@ -1542,12 +1432,12 @@ function (array $event) use (&$aggregateCommand): void { } $aggregateCommand = $event['started']->getCommand(); - } + }, ); $this->assertNotNull($aggregateCommand); - $this->assertObjectNotHasAttribute('startAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); - $this->assertObjectHasAttribute('resumeAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); + $this->assertObjectNotHasProperty('startAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); + $this->assertObjectHasProperty('resumeAfter', $aggregateCommand->pipeline[0]->{'$changeStream'}); } private function assertNoCommandExecuted(callable $callable): void @@ -1558,30 +1448,33 @@ private function assertNoCommandExecuted(callable $callable): void $callable, function (array $event) use (&$commands): void { $this->fail(sprintf('"%s" command was executed', $event['started']->getCommandName())); - } + }, ); $this->assertEmpty($commands); } - private function forceChangeStreamResume(): void + private function forceChangeStreamResume(?Server $server = null): void { - $this->configureFailPoint([ - 'configureFailPoint' => 'failCommand', - 'mode' => ['times' => 1], - 'data' => [ - 'failCommands' => ['getMore'], - 'errorCode' => self::NOT_PRIMARY, - 'errorLabels' => ['ResumableChangeStreamError'], + $this->configureFailPoint( + [ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['getMore'], + 'errorCode' => self::NOT_PRIMARY, + 'errorLabels' => ['ResumableChangeStreamError'], + ], ], - ]); + $server, + ); } private function getPostBatchResumeTokenFromReply(stdClass $reply) { - $this->assertObjectHasAttribute('cursor', $reply); + $this->assertObjectHasProperty('cursor', $reply); $this->assertIsObject($reply->cursor); - $this->assertObjectHasAttribute('postBatchResumeToken', $reply->cursor); + $this->assertObjectHasProperty('postBatchResumeToken', $reply->cursor); $this->assertIsObject($reply->cursor->postBatchResumeToken); return $reply->cursor->postBatchResumeToken; @@ -1593,22 +1486,12 @@ private function insertDocument($document): void $this->getDatabaseName(), $this->getCollectionName(), $document, - ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] + ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)], ); $writeResult = $insertOne->execute($this->getPrimaryServer()); $this->assertEquals(1, $writeResult->getInsertedCount()); } - private function isPostBatchResumeTokenSupported() - { - return version_compare($this->getServerVersion(), '4.0.7', '>='); - } - - private function isStartAtOperationTimeSupported() - { - return server_supports_feature($this->getPrimaryServer(), self::$wireVersionForStartAtOperationTime); - } - private function advanceCursorUntilValid(Iterator $iterator, $limitOnShardedClusters = 10): void { if (! $this->isShardedCluster()) { diff --git a/tests/Operation/WatchTest.php b/tests/Operation/WatchTest.php index d4d1be19c..9e874c484 100644 --- a/tests/Operation/WatchTest.php +++ b/tests/Operation/WatchTest.php @@ -4,6 +4,8 @@ use MongoDB\Exception\InvalidArgumentException; use MongoDB\Operation\Watch; +use MongoDB\Tests\Fixtures\Codec\TestDocumentCodec; +use PHPUnit\Framework\Attributes\DataProvider; use stdClass; /** @@ -23,7 +25,7 @@ public function testConstructorCollectionNameShouldBeNullIfDatabaseNameIsNull(): public function testConstructorPipelineArgumentMustBeAList(): void { $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('$pipeline is not a list (unexpected index: "foo")'); + $this->expectExceptionMessage('$pipeline is not a valid aggregation pipeline'); /* Note: Watch uses array_unshift() to prepend the $changeStream stage * to the pipeline. Since array_unshift() reindexes numeric keys, we'll @@ -31,67 +33,41 @@ public function testConstructorPipelineArgumentMustBeAList(): void new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), ['foo' => ['$match' => ['x' => 1]]]); } - /** - * @dataProvider provideInvalidConstructorOptions - */ + #[DataProvider('provideInvalidConstructorOptions')] public function testConstructorOptionTypeChecks(array $options): void { $this->expectException(InvalidArgumentException::class); new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); } - public function provideInvalidConstructorOptions() + public static function provideInvalidConstructorOptions() { - $options = []; - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['batchSize' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['collation' => $value]; - } - - foreach ($this->getInvalidStringValues(true) as $value) { - $options[][] = ['fullDocument' => $value]; - } - - foreach ($this->getInvalidStringValues() as $value) { - $options[][] = ['fullDocumentBeforeChange' => $value]; - } - - foreach ($this->getInvalidIntegerValues() as $value) { - $options[][] = ['maxAwaitTimeMS' => $value]; - } - - foreach ($this->getInvalidReadConcernValues() as $value) { - $options[][] = ['readConcern' => $value]; - } - - foreach ($this->getInvalidReadPreferenceValues(true) as $value) { - $options[][] = ['readPreference' => $value]; - } - - foreach ($this->getInvalidDocumentValues() as $value) { - $options[][] = ['resumeAfter' => $value]; - } - - foreach ($this->getInvalidSessionValues() as $value) { - $options[][] = ['session' => $value]; - } - - foreach ($this->getInvalidTimestampValues() as $value) { - $options[][] = ['startAtOperationTime' => $value]; - } + return self::createOptionDataProvider([ + 'batchSize' => self::getInvalidIntegerValues(), + 'codec' => self::getInvalidDocumentCodecValues(), + 'collation' => self::getInvalidDocumentValues(), + 'fullDocument' => self::getInvalidStringValues(true), + 'fullDocumentBeforeChange' => self::getInvalidStringValues(), + 'maxAwaitTimeMS' => self::getInvalidIntegerValues(), + 'readConcern' => self::getInvalidReadConcernValues(), + 'readPreference' => self::getInvalidReadPreferenceValues(true), + 'resumeAfter' => self::getInvalidDocumentValues(), + 'session' => self::getInvalidSessionValues(), + 'startAfter' => self::getInvalidDocumentValues(), + 'startAtOperationTime' => self::getInvalidTimestampValues(), + 'typeMap' => self::getInvalidArrayValues(), + ]); + } - foreach ($this->getInvalidArrayValues() as $value) { - $options[][] = ['typeMap' => $value]; - } + public function testConstructorRejectsCodecAndTypemap(): void + { + $this->expectExceptionObject(InvalidArgumentException::cannotCombineCodecAndTypeMap()); - return $options; + $options = ['codec' => new TestDocumentCodec(), 'typeMap' => ['root' => 'array']]; + new Watch($this->manager, $this->getDatabaseName(), $this->getCollectionName(), [], $options); } - private function getInvalidTimestampValues() + private static function getInvalidTimestampValues() { return [123, 3.14, 'foo', true, [], new stdClass()]; } diff --git a/tests/PHPUnit/Functions.php b/tests/PHPUnit/Functions.php deleted file mode 100644 index 55edd3a6f..000000000 --- a/tests/PHPUnit/Functions.php +++ /dev/null @@ -1,2729 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace PHPUnit\Framework; - -use ArrayAccess; -use Countable; -use DOMDocument; -use DOMElement; -use PHPUnit\Framework\Constraint\ArrayHasKey; -use PHPUnit\Framework\Constraint\Callback; -use PHPUnit\Framework\Constraint\ClassHasAttribute; -use PHPUnit\Framework\Constraint\ClassHasStaticAttribute; -use PHPUnit\Framework\Constraint\Constraint; -use PHPUnit\Framework\Constraint\Count; -use PHPUnit\Framework\Constraint\DirectoryExists; -use PHPUnit\Framework\Constraint\FileExists; -use PHPUnit\Framework\Constraint\GreaterThan; -use PHPUnit\Framework\Constraint\IsAnything; -use PHPUnit\Framework\Constraint\IsEmpty; -use PHPUnit\Framework\Constraint\IsEqual; -use PHPUnit\Framework\Constraint\IsEqualCanonicalizing; -use PHPUnit\Framework\Constraint\IsEqualIgnoringCase; -use PHPUnit\Framework\Constraint\IsEqualWithDelta; -use PHPUnit\Framework\Constraint\IsFalse; -use PHPUnit\Framework\Constraint\IsFinite; -use PHPUnit\Framework\Constraint\IsIdentical; -use PHPUnit\Framework\Constraint\IsInfinite; -use PHPUnit\Framework\Constraint\IsInstanceOf; -use PHPUnit\Framework\Constraint\IsJson; -use PHPUnit\Framework\Constraint\IsNan; -use PHPUnit\Framework\Constraint\IsNull; -use PHPUnit\Framework\Constraint\IsReadable; -use PHPUnit\Framework\Constraint\IsTrue; -use PHPUnit\Framework\Constraint\IsType; -use PHPUnit\Framework\Constraint\IsWritable; -use PHPUnit\Framework\Constraint\LessThan; -use PHPUnit\Framework\Constraint\LogicalAnd; -use PHPUnit\Framework\Constraint\LogicalNot; -use PHPUnit\Framework\Constraint\LogicalOr; -use PHPUnit\Framework\Constraint\LogicalXor; -use PHPUnit\Framework\Constraint\ObjectHasAttribute; -use PHPUnit\Framework\Constraint\RegularExpression; -use PHPUnit\Framework\Constraint\StringContains; -use PHPUnit\Framework\Constraint\StringEndsWith; -use PHPUnit\Framework\Constraint\StringMatchesFormatDescription; -use PHPUnit\Framework\Constraint\StringStartsWith; -use PHPUnit\Framework\Constraint\TraversableContainsEqual; -use PHPUnit\Framework\Constraint\TraversableContainsIdentical; -use PHPUnit\Framework\Constraint\TraversableContainsOnly; -use PHPUnit\Framework\MockObject\Rule\AnyInvokedCount as AnyInvokedCountMatcher; -use PHPUnit\Framework\MockObject\Rule\InvokedAtIndex as InvokedAtIndexMatcher; -use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastCount as InvokedAtLeastCountMatcher; -use PHPUnit\Framework\MockObject\Rule\InvokedAtLeastOnce as InvokedAtLeastOnceMatcher; -use PHPUnit\Framework\MockObject\Rule\InvokedAtMostCount as InvokedAtMostCountMatcher; -use PHPUnit\Framework\MockObject\Rule\InvokedCount as InvokedCountMatcher; -use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; -use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; -use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; -use PHPUnit\Framework\MockObject\Stub\ReturnCallback as ReturnCallbackStub; -use PHPUnit\Framework\MockObject\Stub\ReturnSelf as ReturnSelfStub; -use PHPUnit\Framework\MockObject\Stub\ReturnStub; -use PHPUnit\Framework\MockObject\Stub\ReturnValueMap as ReturnValueMapStub; -use PHPUnit\Util\Exception; -use PHPUnit\Util\Xml\Exception as XmlException; -use SebastianBergmann\RecursionContext\InvalidArgumentException; -use Throwable; - -use function func_get_args; -use function function_exists; - -if (! function_exists('PHPUnit\Framework\assertArrayHasKey')) { - /** - * Asserts that an array has a specified key. - * - * @see Assert::assertArrayHasKey - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertArrayHasKey($key, $array, string $message = ''): void - { - Assert::assertArrayHasKey(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertArrayNotHasKey')) { - /** - * Asserts that an array does not have a specified key. - * - * @see Assert::assertArrayNotHasKey - * - * @param int|string $key - * @param array|ArrayAccess $array - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertArrayNotHasKey($key, $array, string $message = ''): void - { - Assert::assertArrayNotHasKey(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertContains')) { - /** - * Asserts that a haystack contains a needle. - * - * @see Assert::assertContains - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertContains($needle, $haystack, string $message = ''): void - { - Assert::assertContains(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertContainsEquals')) { - function assertContainsEquals($needle, $haystack, string $message = ''): void - { - Assert::assertContainsEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotContains')) { - /** - * Asserts that a haystack does not contain a needle. - * - * @see Assert::assertNotContains - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertNotContains($needle, $haystack, string $message = ''): void - { - Assert::assertNotContains(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotContainsEquals')) { - function assertNotContainsEquals($needle, $haystack, string $message = ''): void - { - Assert::assertNotContainsEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertContainsOnly')) { - /** - * Asserts that a haystack contains only values of a given type. - * - * @see Assert::assertContainsOnly - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertContainsOnly(string $type, $haystack, ?bool $isNativeType = null, string $message = ''): void - { - Assert::assertContainsOnly(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertContainsOnlyInstancesOf')) { - /** - * Asserts that a haystack contains only instances of a given class name. - * - * @see Assert::assertContainsOnlyInstancesOf - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertContainsOnlyInstancesOf(string $className, $haystack, string $message = ''): void - { - Assert::assertContainsOnlyInstancesOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotContainsOnly')) { - /** - * Asserts that a haystack does not contain only values of a given type. - * - * @see Assert::assertNotContainsOnly - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotContainsOnly(string $type, $haystack, ?bool $isNativeType = null, string $message = ''): void - { - Assert::assertNotContainsOnly(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertCount')) { - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @see Assert::assertCount - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertCount(int $expectedCount, $haystack, string $message = ''): void - { - Assert::assertCount(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotCount')) { - /** - * Asserts the number of elements of an array, Countable or Traversable. - * - * @see Assert::assertNotCount - * - * @param Countable|iterable $haystack - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertNotCount(int $expectedCount, $haystack, string $message = ''): void - { - Assert::assertNotCount(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEquals')) { - /** - * Asserts that two variables are equal. - * - * @see Assert::assertEquals - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertEquals($expected, $actual, string $message = ''): void - { - Assert::assertEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEqualsCanonicalizing')) { - /** - * Asserts that two variables are equal (canonicalizing). - * - * @see Assert::assertEqualsCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertEqualsCanonicalizing($expected, $actual, string $message = ''): void - { - Assert::assertEqualsCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEqualsIgnoringCase')) { - /** - * Asserts that two variables are equal (ignoring case). - * - * @see Assert::assertEqualsIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertEqualsIgnoringCase($expected, $actual, string $message = ''): void - { - Assert::assertEqualsIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEqualsWithDelta')) { - /** - * Asserts that two variables are equal (with delta). - * - * @see Assert::assertEqualsWithDelta - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void - { - Assert::assertEqualsWithDelta(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotEquals')) { - /** - * Asserts that two variables are not equal. - * - * @see Assert::assertNotEquals - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotEquals($expected, $actual, string $message = ''): void - { - Assert::assertNotEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotEqualsCanonicalizing')) { - /** - * Asserts that two variables are not equal (canonicalizing). - * - * @see Assert::assertNotEqualsCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotEqualsCanonicalizing($expected, $actual, string $message = ''): void - { - Assert::assertNotEqualsCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotEqualsIgnoringCase')) { - /** - * Asserts that two variables are not equal (ignoring case). - * - * @see Assert::assertNotEqualsIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotEqualsIgnoringCase($expected, $actual, string $message = ''): void - { - Assert::assertNotEqualsIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotEqualsWithDelta')) { - /** - * Asserts that two variables are not equal (with delta). - * - * @see Assert::assertNotEqualsWithDelta - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotEqualsWithDelta($expected, $actual, float $delta, string $message = ''): void - { - Assert::assertNotEqualsWithDelta(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEmpty')) { - /** - * Asserts that a variable is empty. - * - * @see Assert::assertEmpty - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert empty $actual - */ - function assertEmpty($actual, string $message = ''): void - { - Assert::assertEmpty(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotEmpty')) { - /** - * Asserts that a variable is not empty. - * - * @see Assert::assertNotEmpty - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !empty $actual - */ - function assertNotEmpty($actual, string $message = ''): void - { - Assert::assertNotEmpty(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertGreaterThan')) { - /** - * Asserts that a value is greater than another value. - * - * @see Assert::assertGreaterThan - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertGreaterThan($expected, $actual, string $message = ''): void - { - Assert::assertGreaterThan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertGreaterThanOrEqual')) { - /** - * Asserts that a value is greater than or equal to another value. - * - * @see Assert::assertGreaterThanOrEqual - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertGreaterThanOrEqual($expected, $actual, string $message = ''): void - { - Assert::assertGreaterThanOrEqual(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertLessThan')) { - /** - * Asserts that a value is smaller than another value. - * - * @see Assert::assertLessThan - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertLessThan($expected, $actual, string $message = ''): void - { - Assert::assertLessThan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertLessThanOrEqual')) { - /** - * Asserts that a value is smaller than or equal to another value. - * - * @see Assert::assertLessThanOrEqual - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertLessThanOrEqual($expected, $actual, string $message = ''): void - { - Assert::assertLessThanOrEqual(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileEquals')) { - /** - * Asserts that the contents of one file is equal to the contents of another - * file. - * - * @see Assert::assertFileEquals - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileEquals(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileEqualsCanonicalizing')) { - /** - * Asserts that the contents of one file is equal to the contents of another - * file (canonicalizing). - * - * @see Assert::assertFileEqualsCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileEqualsCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileEqualsIgnoringCase')) { - /** - * Asserts that the contents of one file is equal to the contents of another - * file (ignoring case). - * - * @see Assert::assertFileEqualsIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileEqualsIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotEquals')) { - /** - * Asserts that the contents of one file is not equal to the contents of - * another file. - * - * @see Assert::assertFileNotEquals - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileNotEquals(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileNotEquals(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotEqualsCanonicalizing')) { - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (canonicalizing). - * - * @see Assert::assertFileNotEqualsCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileNotEqualsCanonicalizing(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileNotEqualsCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotEqualsIgnoringCase')) { - /** - * Asserts that the contents of one file is not equal to the contents of another - * file (ignoring case). - * - * @see Assert::assertFileNotEqualsIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileNotEqualsIgnoringCase(string $expected, string $actual, string $message = ''): void - { - Assert::assertFileNotEqualsIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringEqualsFile')) { - /** - * Asserts that the contents of a string is equal - * to the contents of a file. - * - * @see Assert::assertStringEqualsFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringEqualsFile(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringEqualsFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringEqualsFileCanonicalizing')) { - /** - * Asserts that the contents of a string is equal - * to the contents of a file (canonicalizing). - * - * @see Assert::assertStringEqualsFileCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringEqualsFileCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringEqualsFileIgnoringCase')) { - /** - * Asserts that the contents of a string is equal - * to the contents of a file (ignoring case). - * - * @see Assert::assertStringEqualsFileIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringEqualsFileIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotEqualsFile')) { - /** - * Asserts that the contents of a string is not equal - * to the contents of a file. - * - * @see Assert::assertStringNotEqualsFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotEqualsFile(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringNotEqualsFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotEqualsFileCanonicalizing')) { - /** - * Asserts that the contents of a string is not equal - * to the contents of a file (canonicalizing). - * - * @see Assert::assertStringNotEqualsFileCanonicalizing - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotEqualsFileCanonicalizing(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringNotEqualsFileCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotEqualsFileIgnoringCase')) { - /** - * Asserts that the contents of a string is not equal - * to the contents of a file (ignoring case). - * - * @see Assert::assertStringNotEqualsFileIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotEqualsFileIgnoringCase(string $expectedFile, string $actualString, string $message = ''): void - { - Assert::assertStringNotEqualsFileIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsReadable')) { - /** - * Asserts that a file/dir is readable. - * - * @see Assert::assertIsReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertIsReadable(string $filename, string $message = ''): void - { - Assert::assertIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotReadable')) { - /** - * Asserts that a file/dir exists and is not readable. - * - * @see Assert::assertIsNotReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertIsNotReadable(string $filename, string $message = ''): void - { - Assert::assertIsNotReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotIsReadable')) { - /** - * Asserts that a file/dir exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4062 - * @see Assert::assertNotIsReadable - */ - function assertNotIsReadable(string $filename, string $message = ''): void - { - Assert::assertNotIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsWritable')) { - /** - * Asserts that a file/dir exists and is writable. - * - * @see Assert::assertIsWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertIsWritable(string $filename, string $message = ''): void - { - Assert::assertIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotWritable')) { - /** - * Asserts that a file/dir exists and is not writable. - * - * @see Assert::assertIsNotWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertIsNotWritable(string $filename, string $message = ''): void - { - Assert::assertIsNotWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotIsWritable')) { - /** - * Asserts that a file/dir exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4065 - * @see Assert::assertNotIsWritable - */ - function assertNotIsWritable(string $filename, string $message = ''): void - { - Assert::assertNotIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryExists')) { - /** - * Asserts that a directory exists. - * - * @see Assert::assertDirectoryExists - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryExists(string $directory, string $message = ''): void - { - Assert::assertDirectoryExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryDoesNotExist')) { - /** - * Asserts that a directory does not exist. - * - * @see Assert::assertDirectoryDoesNotExist - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryDoesNotExist(string $directory, string $message = ''): void - { - Assert::assertDirectoryDoesNotExist(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryNotExists')) { - /** - * Asserts that a directory does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4068 - * @see Assert::assertDirectoryNotExists - */ - function assertDirectoryNotExists(string $directory, string $message = ''): void - { - Assert::assertDirectoryNotExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryIsReadable')) { - /** - * Asserts that a directory exists and is readable. - * - * @see Assert::assertDirectoryIsReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryIsReadable(string $directory, string $message = ''): void - { - Assert::assertDirectoryIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryIsNotReadable')) { - /** - * Asserts that a directory exists and is not readable. - * - * @see Assert::assertDirectoryIsNotReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryIsNotReadable(string $directory, string $message = ''): void - { - Assert::assertDirectoryIsNotReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryNotIsReadable')) { - /** - * Asserts that a directory exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4071 - * @see Assert::assertDirectoryNotIsReadable - */ - function assertDirectoryNotIsReadable(string $directory, string $message = ''): void - { - Assert::assertDirectoryNotIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryIsWritable')) { - /** - * Asserts that a directory exists and is writable. - * - * @see Assert::assertDirectoryIsWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryIsWritable(string $directory, string $message = ''): void - { - Assert::assertDirectoryIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryIsNotWritable')) { - /** - * Asserts that a directory exists and is not writable. - * - * @see Assert::assertDirectoryIsNotWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDirectoryIsNotWritable(string $directory, string $message = ''): void - { - Assert::assertDirectoryIsNotWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDirectoryNotIsWritable')) { - /** - * Asserts that a directory exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4074 - * @see Assert::assertDirectoryNotIsWritable - */ - function assertDirectoryNotIsWritable(string $directory, string $message = ''): void - { - Assert::assertDirectoryNotIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileExists')) { - /** - * Asserts that a file exists. - * - * @see Assert::assertFileExists - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileExists(string $filename, string $message = ''): void - { - Assert::assertFileExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileDoesNotExist')) { - /** - * Asserts that a file does not exist. - * - * @see Assert::assertFileDoesNotExist - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileDoesNotExist(string $filename, string $message = ''): void - { - Assert::assertFileDoesNotExist(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotExists')) { - /** - * Asserts that a file does not exist. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4077 - * @see Assert::assertFileNotExists - */ - function assertFileNotExists(string $filename, string $message = ''): void - { - Assert::assertFileNotExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileIsReadable')) { - /** - * Asserts that a file exists and is readable. - * - * @see Assert::assertFileIsReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileIsReadable(string $file, string $message = ''): void - { - Assert::assertFileIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileIsNotReadable')) { - /** - * Asserts that a file exists and is not readable. - * - * @see Assert::assertFileIsNotReadable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileIsNotReadable(string $file, string $message = ''): void - { - Assert::assertFileIsNotReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotIsReadable')) { - /** - * Asserts that a file exists and is not readable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4080 - * @see Assert::assertFileNotIsReadable - */ - function assertFileNotIsReadable(string $file, string $message = ''): void - { - Assert::assertFileNotIsReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileIsWritable')) { - /** - * Asserts that a file exists and is writable. - * - * @see Assert::assertFileIsWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileIsWritable(string $file, string $message = ''): void - { - Assert::assertFileIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileIsNotWritable')) { - /** - * Asserts that a file exists and is not writable. - * - * @see Assert::assertFileIsNotWritable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFileIsNotWritable(string $file, string $message = ''): void - { - Assert::assertFileIsNotWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFileNotIsWritable')) { - /** - * Asserts that a file exists and is not writable. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4083 - * @see Assert::assertFileNotIsWritable - */ - function assertFileNotIsWritable(string $file, string $message = ''): void - { - Assert::assertFileNotIsWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertTrue')) { - /** - * Asserts that a condition is true. - * - * @see Assert::assertTrue - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert true $condition - */ - function assertTrue($condition, string $message = ''): void - { - Assert::assertTrue(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotTrue')) { - /** - * Asserts that a condition is not true. - * - * @see Assert::assertNotTrue - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !true $condition - */ - function assertNotTrue($condition, string $message = ''): void - { - Assert::assertNotTrue(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFalse')) { - /** - * Asserts that a condition is false. - * - * @see Assert::assertFalse - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert false $condition - */ - function assertFalse($condition, string $message = ''): void - { - Assert::assertFalse(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotFalse')) { - /** - * Asserts that a condition is not false. - * - * @see Assert::assertNotFalse - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !false $condition - */ - function assertNotFalse($condition, string $message = ''): void - { - Assert::assertNotFalse(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNull')) { - /** - * Asserts that a variable is null. - * - * @see Assert::assertNull - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert null $actual - */ - function assertNull($actual, string $message = ''): void - { - Assert::assertNull(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotNull')) { - /** - * Asserts that a variable is not null. - * - * @see Assert::assertNotNull - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !null $actual - */ - function assertNotNull($actual, string $message = ''): void - { - Assert::assertNotNull(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertFinite')) { - /** - * Asserts that a variable is finite. - * - * @see Assert::assertFinite - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertFinite($actual, string $message = ''): void - { - Assert::assertFinite(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertInfinite')) { - /** - * Asserts that a variable is infinite. - * - * @see Assert::assertInfinite - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertInfinite($actual, string $message = ''): void - { - Assert::assertInfinite(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNan')) { - /** - * Asserts that a variable is nan. - * - * @see Assert::assertNan - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNan($actual, string $message = ''): void - { - Assert::assertNan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertClassHasAttribute')) { - /** - * Asserts that a class has a specified attribute. - * - * @see Assert::assertClassHasAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertClassHasAttribute(string $attributeName, string $className, string $message = ''): void - { - Assert::assertClassHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertClassNotHasAttribute')) { - /** - * Asserts that a class does not have a specified attribute. - * - * @see Assert::assertClassNotHasAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertClassNotHasAttribute(string $attributeName, string $className, string $message = ''): void - { - Assert::assertClassNotHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertClassHasStaticAttribute')) { - /** - * Asserts that a class has a specified static attribute. - * - * @see Assert::assertClassHasStaticAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertClassHasStaticAttribute(string $attributeName, string $className, string $message = ''): void - { - Assert::assertClassHasStaticAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertClassNotHasStaticAttribute')) { - /** - * Asserts that a class does not have a specified static attribute. - * - * @see Assert::assertClassNotHasStaticAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertClassNotHasStaticAttribute(string $attributeName, string $className, string $message = ''): void - { - Assert::assertClassNotHasStaticAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertObjectHasAttribute')) { - /** - * Asserts that an object has a specified attribute. - * - * @see Assert::assertObjectHasAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertObjectHasAttribute(string $attributeName, object $object, string $message = ''): void - { - Assert::assertObjectHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertObjectNotHasAttribute')) { - /** - * Asserts that an object does not have a specified attribute. - * - * @see Assert::assertObjectNotHasAttribute - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertObjectNotHasAttribute(string $attributeName, object $object, string $message = ''): void - { - Assert::assertObjectNotHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertSame')) { - /** - * Asserts that two variables have the same type and value. - * Used on objects, it asserts that two variables reference - * the same object. - * - * @see Assert::assertSame - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-template ExpectedType - * @psalm-param ExpectedType $expected - * @psalm-assert =ExpectedType $actual - */ - function assertSame($expected, $actual, string $message = ''): void - { - Assert::assertSame(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotSame')) { - /** - * Asserts that two variables do not have the same type and value. - * Used on objects, it asserts that two variables do not reference - * the same object. - * - * @see Assert::assertNotSame - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertNotSame($expected, $actual, string $message = ''): void - { - Assert::assertNotSame(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertInstanceOf')) { - /** - * Asserts that a variable is of a given type. - * - * @see Assert::assertInstanceOf - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * @psalm-param class-string $expected - * @psalm-assert ExpectedType $actual - */ - function assertInstanceOf(string $expected, $actual, string $message = ''): void - { - Assert::assertInstanceOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotInstanceOf')) { - /** - * Asserts that a variable is not of a given type. - * - * @see Assert::assertNotInstanceOf - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - * - * @psalm-template ExpectedType of object - * @psalm-param class-string $expected - * @psalm-assert !ExpectedType $actual - */ - function assertNotInstanceOf(string $expected, $actual, string $message = ''): void - { - Assert::assertNotInstanceOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsArray')) { - /** - * Asserts that a variable is of type array. - * - * @see Assert::assertIsArray - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert array $actual - */ - function assertIsArray($actual, string $message = ''): void - { - Assert::assertIsArray(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsBool')) { - /** - * Asserts that a variable is of type bool. - * - * @see Assert::assertIsBool - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert bool $actual - */ - function assertIsBool($actual, string $message = ''): void - { - Assert::assertIsBool(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsFloat')) { - /** - * Asserts that a variable is of type float. - * - * @see Assert::assertIsFloat - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert float $actual - */ - function assertIsFloat($actual, string $message = ''): void - { - Assert::assertIsFloat(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsInt')) { - /** - * Asserts that a variable is of type int. - * - * @see Assert::assertIsInt - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert int $actual - */ - function assertIsInt($actual, string $message = ''): void - { - Assert::assertIsInt(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNumeric')) { - /** - * Asserts that a variable is of type numeric. - * - * @see Assert::assertIsNumeric - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert numeric $actual - */ - function assertIsNumeric($actual, string $message = ''): void - { - Assert::assertIsNumeric(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsObject')) { - /** - * Asserts that a variable is of type object. - * - * @see Assert::assertIsObject - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert object $actual - */ - function assertIsObject($actual, string $message = ''): void - { - Assert::assertIsObject(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsResource')) { - /** - * Asserts that a variable is of type resource. - * - * @see Assert::assertIsResource - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert resource $actual - */ - function assertIsResource($actual, string $message = ''): void - { - Assert::assertIsResource(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsClosedResource')) { - /** - * Asserts that a variable is of type resource and is closed. - * - * @see Assert::assertIsClosedResource - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert resource $actual - */ - function assertIsClosedResource($actual, string $message = ''): void - { - Assert::assertIsClosedResource(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsString')) { - /** - * Asserts that a variable is of type string. - * - * @see Assert::assertIsString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert string $actual - */ - function assertIsString($actual, string $message = ''): void - { - Assert::assertIsString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsScalar')) { - /** - * Asserts that a variable is of type scalar. - * - * @see Assert::assertIsScalar - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert scalar $actual - */ - function assertIsScalar($actual, string $message = ''): void - { - Assert::assertIsScalar(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsCallable')) { - /** - * Asserts that a variable is of type callable. - * - * @see Assert::assertIsCallable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert callable $actual - */ - function assertIsCallable($actual, string $message = ''): void - { - Assert::assertIsCallable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsIterable')) { - /** - * Asserts that a variable is of type iterable. - * - * @see Assert::assertIsIterable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert iterable $actual - */ - function assertIsIterable($actual, string $message = ''): void - { - Assert::assertIsIterable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotArray')) { - /** - * Asserts that a variable is not of type array. - * - * @see Assert::assertIsNotArray - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !array $actual - */ - function assertIsNotArray($actual, string $message = ''): void - { - Assert::assertIsNotArray(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotBool')) { - /** - * Asserts that a variable is not of type bool. - * - * @see Assert::assertIsNotBool - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !bool $actual - */ - function assertIsNotBool($actual, string $message = ''): void - { - Assert::assertIsNotBool(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotFloat')) { - /** - * Asserts that a variable is not of type float. - * - * @see Assert::assertIsNotFloat - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !float $actual - */ - function assertIsNotFloat($actual, string $message = ''): void - { - Assert::assertIsNotFloat(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotInt')) { - /** - * Asserts that a variable is not of type int. - * - * @see Assert::assertIsNotInt - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !int $actual - */ - function assertIsNotInt($actual, string $message = ''): void - { - Assert::assertIsNotInt(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotNumeric')) { - /** - * Asserts that a variable is not of type numeric. - * - * @see Assert::assertIsNotNumeric - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !numeric $actual - */ - function assertIsNotNumeric($actual, string $message = ''): void - { - Assert::assertIsNotNumeric(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotObject')) { - /** - * Asserts that a variable is not of type object. - * - * @see Assert::assertIsNotObject - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !object $actual - */ - function assertIsNotObject($actual, string $message = ''): void - { - Assert::assertIsNotObject(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotResource')) { - /** - * Asserts that a variable is not of type resource. - * - * @see Assert::assertIsNotResource - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !resource $actual - */ - function assertIsNotResource($actual, string $message = ''): void - { - Assert::assertIsNotResource(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotClosedResource')) { - /** - * Asserts that a variable is not of type resource. - * - * @see Assert::assertIsNotClosedResource - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !resource $actual - */ - function assertIsNotClosedResource($actual, string $message = ''): void - { - Assert::assertIsNotClosedResource(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotString')) { - /** - * Asserts that a variable is not of type string. - * - * @see Assert::assertIsNotString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !string $actual - */ - function assertIsNotString($actual, string $message = ''): void - { - Assert::assertIsNotString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotScalar')) { - /** - * Asserts that a variable is not of type scalar. - * - * @see Assert::assertIsNotScalar - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !scalar $actual - */ - function assertIsNotScalar($actual, string $message = ''): void - { - Assert::assertIsNotScalar(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotCallable')) { - /** - * Asserts that a variable is not of type callable. - * - * @see Assert::assertIsNotCallable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !callable $actual - */ - function assertIsNotCallable($actual, string $message = ''): void - { - Assert::assertIsNotCallable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertIsNotIterable')) { - /** - * Asserts that a variable is not of type iterable. - * - * @see Assert::assertIsNotIterable - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @psalm-assert !iterable $actual - */ - function assertIsNotIterable($actual, string $message = ''): void - { - Assert::assertIsNotIterable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertMatchesRegularExpression')) { - /** - * Asserts that a string matches a given regular expression. - * - * @see Assert::assertMatchesRegularExpression - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertMatchesRegularExpression(string $pattern, string $string, string $message = ''): void - { - Assert::assertMatchesRegularExpression(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertRegExp')) { - /** - * Asserts that a string matches a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4086 - * @see Assert::assertRegExp - */ - function assertRegExp(string $pattern, string $string, string $message = ''): void - { - Assert::assertRegExp(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertDoesNotMatchRegularExpression')) { - /** - * Asserts that a string does not match a given regular expression. - * - * @see Assert::assertDoesNotMatchRegularExpression - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertDoesNotMatchRegularExpression(string $pattern, string $string, string $message = ''): void - { - Assert::assertDoesNotMatchRegularExpression(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotRegExp')) { - /** - * Asserts that a string does not match a given regular expression. - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4089 - * @see Assert::assertNotRegExp - */ - function assertNotRegExp(string $pattern, string $string, string $message = ''): void - { - Assert::assertNotRegExp(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertSameSize')) { - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is the same. - * - * @see Assert::assertSameSize - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertSameSize($expected, $actual, string $message = ''): void - { - Assert::assertSameSize(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertNotSameSize')) { - /** - * Assert that the size of two arrays (or `Countable` or `Traversable` objects) - * is not the same. - * - * @see Assert::assertNotSameSize - * - * @param Countable|iterable $expected - * @param Countable|iterable $actual - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertNotSameSize($expected, $actual, string $message = ''): void - { - Assert::assertNotSameSize(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringMatchesFormat')) { - /** - * Asserts that a string matches a given format string. - * - * @see Assert::assertStringMatchesFormat - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringMatchesFormat(string $format, string $string, string $message = ''): void - { - Assert::assertStringMatchesFormat(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotMatchesFormat')) { - /** - * Asserts that a string does not match a given format string. - * - * @see Assert::assertStringNotMatchesFormat - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotMatchesFormat(string $format, string $string, string $message = ''): void - { - Assert::assertStringNotMatchesFormat(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringMatchesFormatFile')) { - /** - * Asserts that a string matches a given format file. - * - * @see Assert::assertStringMatchesFormatFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - Assert::assertStringMatchesFormatFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotMatchesFormatFile')) { - /** - * Asserts that a string does not match a given format string. - * - * @see Assert::assertStringNotMatchesFormatFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotMatchesFormatFile(string $formatFile, string $string, string $message = ''): void - { - Assert::assertStringNotMatchesFormatFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringStartsWith')) { - /** - * Asserts that a string starts with a given prefix. - * - * @see Assert::assertStringStartsWith - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringStartsWith(string $prefix, string $string, string $message = ''): void - { - Assert::assertStringStartsWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringStartsNotWith')) { - /** - * Asserts that a string starts not with a given prefix. - * - * @see Assert::assertStringStartsNotWith - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringStartsNotWith(string $prefix, string $string, string $message = ''): void - { - Assert::assertStringStartsNotWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringContainsString')) { - /** - * @see Assert::assertStringContainsString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringContainsString(string $needle, string $haystack, string $message = ''): void - { - Assert::assertStringContainsString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringContainsStringIgnoringCase')) { - /** - * @see Assert::assertStringContainsStringIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void - { - Assert::assertStringContainsStringIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotContainsString')) { - /** - * @see Assert::assertStringNotContainsString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotContainsString(string $needle, string $haystack, string $message = ''): void - { - Assert::assertStringNotContainsString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringNotContainsStringIgnoringCase')) { - /** - * @see Assert::assertStringNotContainsStringIgnoringCase - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringNotContainsStringIgnoringCase(string $needle, string $haystack, string $message = ''): void - { - Assert::assertStringNotContainsStringIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringEndsWith')) { - /** - * Asserts that a string ends with a given suffix. - * - * @see Assert::assertStringEndsWith - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringEndsWith(string $suffix, string $string, string $message = ''): void - { - Assert::assertStringEndsWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertStringEndsNotWith')) { - /** - * Asserts that a string ends not with a given suffix. - * - * @see Assert::assertStringEndsNotWith - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertStringEndsNotWith(string $suffix, string $string, string $message = ''): void - { - Assert::assertStringEndsNotWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlFileEqualsXmlFile')) { - /** - * Asserts that two XML files are equal. - * - * @see Assert::assertXmlFileEqualsXmlFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void - { - Assert::assertXmlFileEqualsXmlFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlFileNotEqualsXmlFile')) { - /** - * Asserts that two XML files are not equal. - * - * @see Assert::assertXmlFileNotEqualsXmlFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws Exception - */ - function assertXmlFileNotEqualsXmlFile(string $expectedFile, string $actualFile, string $message = ''): void - { - Assert::assertXmlFileNotEqualsXmlFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlFile')) { - /** - * Asserts that two XML documents are equal. - * - * @see Assert::assertXmlStringEqualsXmlFile - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws XmlException - */ - function assertXmlStringEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void - { - Assert::assertXmlStringEqualsXmlFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlFile')) { - /** - * Asserts that two XML documents are not equal. - * - * @see Assert::assertXmlStringNotEqualsXmlFile - * - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws XmlException - */ - function assertXmlStringNotEqualsXmlFile(string $expectedFile, $actualXml, string $message = ''): void - { - Assert::assertXmlStringNotEqualsXmlFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlStringEqualsXmlString')) { - /** - * Asserts that two XML documents are equal. - * - * @see Assert::assertXmlStringEqualsXmlString - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws XmlException - */ - function assertXmlStringEqualsXmlString($expectedXml, $actualXml, string $message = ''): void - { - Assert::assertXmlStringEqualsXmlString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertXmlStringNotEqualsXmlString')) { - /** - * Asserts that two XML documents are not equal. - * - * @see Assert::assertXmlStringNotEqualsXmlString - * - * @param DOMDocument|string $expectedXml - * @param DOMDocument|string $actualXml - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * @throws XmlException - */ - function assertXmlStringNotEqualsXmlString($expectedXml, $actualXml, string $message = ''): void - { - Assert::assertXmlStringNotEqualsXmlString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertEqualXMLStructure')) { - /** - * Asserts that a hierarchy of DOMElements matches. - * - * @throws AssertionFailedError - * @throws ExpectationFailedException - * @throws InvalidArgumentException - * - * @codeCoverageIgnore - * - * @deprecated https://github.com/sebastianbergmann/phpunit/issues/4091 - * @see Assert::assertEqualXMLStructure - */ - function assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement, bool $checkAttributes = false, string $message = ''): void - { - Assert::assertEqualXMLStructure(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertThat')) { - /** - * Evaluates a PHPUnit\Framework\Constraint matcher object. - * - * @see Assert::assertThat - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertThat($value, Constraint $constraint, string $message = ''): void - { - Assert::assertThat(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJson')) { - /** - * Asserts that a string is a valid JSON string. - * - * @see Assert::assertJson - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJson(string $actualJson, string $message = ''): void - { - Assert::assertJson(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonString')) { - /** - * Asserts that two given JSON encoded objects or arrays are equal. - * - * @see Assert::assertJsonStringEqualsJsonString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonStringEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void - { - Assert::assertJsonStringEqualsJsonString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonString')) { - /** - * Asserts that two given JSON encoded objects or arrays are not equal. - * - * @see Assert::assertJsonStringNotEqualsJsonString - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonStringNotEqualsJsonString(string $expectedJson, string $actualJson, string $message = ''): void - { - Assert::assertJsonStringNotEqualsJsonString(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonStringEqualsJsonFile')) { - /** - * Asserts that the generated JSON encoded object and the content of the given file are equal. - * - * @see Assert::assertJsonStringEqualsJsonFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonStringEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void - { - Assert::assertJsonStringEqualsJsonFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonStringNotEqualsJsonFile')) { - /** - * Asserts that the generated JSON encoded object and the content of the given file are not equal. - * - * @see Assert::assertJsonStringNotEqualsJsonFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonStringNotEqualsJsonFile(string $expectedFile, string $actualJson, string $message = ''): void - { - Assert::assertJsonStringNotEqualsJsonFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonFileEqualsJsonFile')) { - /** - * Asserts that two JSON files are equal. - * - * @see Assert::assertJsonFileEqualsJsonFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonFileEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void - { - Assert::assertJsonFileEqualsJsonFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\assertJsonFileNotEqualsJsonFile')) { - /** - * Asserts that two JSON files are not equal. - * - * @see Assert::assertJsonFileNotEqualsJsonFile - * - * @throws ExpectationFailedException - * @throws InvalidArgumentException - */ - function assertJsonFileNotEqualsJsonFile(string $expectedFile, string $actualFile, string $message = ''): void - { - Assert::assertJsonFileNotEqualsJsonFile(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\logicalAnd')) { - function logicalAnd(): LogicalAnd - { - return Assert::logicalAnd(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\logicalOr')) { - function logicalOr(): LogicalOr - { - return Assert::logicalOr(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\logicalNot')) { - function logicalNot(Constraint $constraint): LogicalNot - { - return Assert::logicalNot(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\logicalXor')) { - function logicalXor(): LogicalXor - { - return Assert::logicalXor(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\anything')) { - function anything(): IsAnything - { - return Assert::anything(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isTrue')) { - function isTrue(): IsTrue - { - return Assert::isTrue(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\callback')) { - function callback(callable $callback): Callback - { - return Assert::callback(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isFalse')) { - function isFalse(): IsFalse - { - return Assert::isFalse(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isJson')) { - function isJson(): IsJson - { - return Assert::isJson(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isNull')) { - function isNull(): IsNull - { - return Assert::isNull(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isFinite')) { - function isFinite(): IsFinite - { - return Assert::isFinite(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isInfinite')) { - function isInfinite(): IsInfinite - { - return Assert::isInfinite(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isNan')) { - function isNan(): IsNan - { - return Assert::isNan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\containsEqual')) { - function containsEqual($value): TraversableContainsEqual - { - return Assert::containsEqual(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\containsIdentical')) { - function containsIdentical($value): TraversableContainsIdentical - { - return Assert::containsIdentical(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\containsOnly')) { - function containsOnly(string $type): TraversableContainsOnly - { - return Assert::containsOnly(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\containsOnlyInstancesOf')) { - function containsOnlyInstancesOf(string $className): TraversableContainsOnly - { - return Assert::containsOnlyInstancesOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\arrayHasKey')) { - function arrayHasKey($key): ArrayHasKey - { - return Assert::arrayHasKey(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\equalTo')) { - function equalTo($value): IsEqual - { - return Assert::equalTo(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\equalToCanonicalizing')) { - function equalToCanonicalizing($value): IsEqualCanonicalizing - { - return Assert::equalToCanonicalizing(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\equalToIgnoringCase')) { - function equalToIgnoringCase($value): IsEqualIgnoringCase - { - return Assert::equalToIgnoringCase(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\equalToWithDelta')) { - function equalToWithDelta($value, float $delta): IsEqualWithDelta - { - return Assert::equalToWithDelta(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isEmpty')) { - function isEmpty(): IsEmpty - { - return Assert::isEmpty(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isWritable')) { - function isWritable(): IsWritable - { - return Assert::isWritable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isReadable')) { - function isReadable(): IsReadable - { - return Assert::isReadable(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\directoryExists')) { - function directoryExists(): DirectoryExists - { - return Assert::directoryExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\fileExists')) { - function fileExists(): FileExists - { - return Assert::fileExists(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\greaterThan')) { - function greaterThan($value): GreaterThan - { - return Assert::greaterThan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\greaterThanOrEqual')) { - function greaterThanOrEqual($value): LogicalOr - { - return Assert::greaterThanOrEqual(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\classHasAttribute')) { - function classHasAttribute(string $attributeName): ClassHasAttribute - { - return Assert::classHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\classHasStaticAttribute')) { - function classHasStaticAttribute(string $attributeName): ClassHasStaticAttribute - { - return Assert::classHasStaticAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\objectHasAttribute')) { - function objectHasAttribute($attributeName): ObjectHasAttribute - { - return Assert::objectHasAttribute(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\identicalTo')) { - function identicalTo($value): IsIdentical - { - return Assert::identicalTo(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isInstanceOf')) { - function isInstanceOf(string $className): IsInstanceOf - { - return Assert::isInstanceOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\isType')) { - function isType(string $type): IsType - { - return Assert::isType(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\lessThan')) { - function lessThan($value): LessThan - { - return Assert::lessThan(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\lessThanOrEqual')) { - function lessThanOrEqual($value): LogicalOr - { - return Assert::lessThanOrEqual(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\matchesRegularExpression')) { - function matchesRegularExpression(string $pattern): RegularExpression - { - return Assert::matchesRegularExpression(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\matches')) { - function matches(string $string): StringMatchesFormatDescription - { - return Assert::matches(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\stringStartsWith')) { - function stringStartsWith($prefix): StringStartsWith - { - return Assert::stringStartsWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\stringContains')) { - function stringContains(string $string, bool $case = true): StringContains - { - return Assert::stringContains(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\stringEndsWith')) { - function stringEndsWith(string $suffix): StringEndsWith - { - return Assert::stringEndsWith(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\countOf')) { - function countOf(int $count): Count - { - return Assert::countOf(...func_get_args()); - } -} - -if (! function_exists('PHPUnit\Framework\any')) { - /** - * Returns a matcher that matches when the method is executed - * zero or more times. - */ - function any(): AnyInvokedCountMatcher - { - return new AnyInvokedCountMatcher(); - } -} - -if (! function_exists('PHPUnit\Framework\never')) { - /** - * Returns a matcher that matches when the method is never executed. - */ - function never(): InvokedCountMatcher - { - return new InvokedCountMatcher(0); - } -} - -if (! function_exists('PHPUnit\Framework\atLeast')) { - /** - * Returns a matcher that matches when the method is executed - * at least N times. - */ - function atLeast(int $requiredInvocations): InvokedAtLeastCountMatcher - { - return new InvokedAtLeastCountMatcher( - $requiredInvocations - ); - } -} - -if (! function_exists('PHPUnit\Framework\atLeastOnce')) { - /** - * Returns a matcher that matches when the method is executed at least once. - */ - function atLeastOnce(): InvokedAtLeastOnceMatcher - { - return new InvokedAtLeastOnceMatcher(); - } -} - -if (! function_exists('PHPUnit\Framework\once')) { - /** - * Returns a matcher that matches when the method is executed exactly once. - */ - function once(): InvokedCountMatcher - { - return new InvokedCountMatcher(1); - } -} - -if (! function_exists('PHPUnit\Framework\exactly')) { - /** - * Returns a matcher that matches when the method is executed - * exactly $count times. - */ - function exactly(int $count): InvokedCountMatcher - { - return new InvokedCountMatcher($count); - } -} - -if (! function_exists('PHPUnit\Framework\atMost')) { - /** - * Returns a matcher that matches when the method is executed - * at most N times. - */ - function atMost(int $allowedInvocations): InvokedAtMostCountMatcher - { - return new InvokedAtMostCountMatcher($allowedInvocations); - } -} - -if (! function_exists('PHPUnit\Framework\at')) { - /** - * Returns a matcher that matches when the method is executed - * at the given index. - */ - function at(int $index): InvokedAtIndexMatcher - { - return new InvokedAtIndexMatcher($index); - } -} - -if (! function_exists('PHPUnit\Framework\returnValue')) { - function returnValue($value): ReturnStub - { - return new ReturnStub($value); - } -} - -if (! function_exists('PHPUnit\Framework\returnValueMap')) { - function returnValueMap(array $valueMap): ReturnValueMapStub - { - return new ReturnValueMapStub($valueMap); - } -} - -if (! function_exists('PHPUnit\Framework\returnArgument')) { - function returnArgument(int $argumentIndex): ReturnArgumentStub - { - return new ReturnArgumentStub($argumentIndex); - } -} - -if (! function_exists('PHPUnit\Framework\returnCallback')) { - function returnCallback($callback): ReturnCallbackStub - { - return new ReturnCallbackStub($callback); - } -} - -if (! function_exists('PHPUnit\Framework\returnSelf')) { - /** - * Returns the current object. - * - * This method is useful when mocking a fluent interface. - */ - function returnSelf(): ReturnSelfStub - { - return new ReturnSelfStub(); - } -} - -if (! function_exists('PHPUnit\Framework\throwException')) { - function throwException(Throwable $exception): ExceptionStub - { - return new ExceptionStub($exception); - } -} - -if (! function_exists('PHPUnit\Framework\onConsecutiveCalls')) { - function onConsecutiveCalls(): ConsecutiveCallsStub - { - $args = func_get_args(); - - return new ConsecutiveCallsStub($args); - } -} diff --git a/tests/PedantryTest.php b/tests/PedantryTest.php index 5e4cbf631..4a5cd7096 100644 --- a/tests/PedantryTest.php +++ b/tests/PedantryTest.php @@ -2,6 +2,8 @@ namespace MongoDB\Tests; +use MongoDB; +use PHPUnit\Framework\Attributes\DataProvider; use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use ReflectionClass; @@ -10,7 +12,10 @@ use function array_filter; use function array_map; +use function array_values; +use function in_array; use function realpath; +use function str_contains; use function str_replace; use function strcasecmp; use function strlen; @@ -24,54 +29,44 @@ */ class PedantryTest extends TestCase { - /** - * @dataProvider provideProjectClassNames - */ + private const SKIPPED_CLASSES = [ + // Generated + MongoDB\Builder\Stage\FluentFactoryTrait::class, + ]; + + #[DataProvider('provideProjectClassNames')] public function testMethodsAreOrderedAlphabeticallyByVisibility($className): void { $class = new ReflectionClass($className); $methods = $class->getMethods(); - $methods = array_filter( + $methods = array_values(array_filter( $methods, - function (ReflectionMethod $method) use ($class) { - return $method->getDeclaringClass() == $class; - } - ); + fn (ReflectionMethod $method) => $method->getDeclaringClass() == $class // Exclude inherited methods + && $method->getFileName() === $class->getFileName() // Exclude methods inherited from traits + && ! ($method->isConstructor() && ! $method->isPublic()), // Exclude non-public constructors + )); $getSortValue = function (ReflectionMethod $method) { - if ($method->getModifiers() & ReflectionMethod::IS_PRIVATE) { - return '2' . $method->getName(); - } - - if ($method->getModifiers() & ReflectionMethod::IS_PROTECTED) { - return '1' . $method->getName(); - } + $prefix = $method->isPrivate() ? '2' : ($method->isProtected() ? '1' : '0'); + $prefix .= str_contains($method->getDocComment(), '@internal') ? '1' : '0'; - if ($method->getModifiers() & ReflectionMethod::IS_PUBLIC) { - return '0' . $method->getName(); - } + return $prefix . $method->getName(); }; $sortedMethods = $methods; usort( $sortedMethods, - function (ReflectionMethod $a, ReflectionMethod $b) use ($getSortValue) { - return strcasecmp($getSortValue($a), $getSortValue($b)); - } + fn (ReflectionMethod $a, ReflectionMethod $b) => strcasecmp($getSortValue($a), $getSortValue($b)), ); - $methods = array_map(function (ReflectionMethod $method) { - return $method->getName(); - }, $methods); - $sortedMethods = array_map(function (ReflectionMethod $method) { - return $method->getName(); - }, $sortedMethods); + $methods = array_map(fn (ReflectionMethod $method) => $method->getName(), $methods); + $sortedMethods = array_map(fn (ReflectionMethod $method) => $method->getName(), $sortedMethods); $this->assertEquals($sortedMethods, $methods); } - public function provideProjectClassNames() + public static function provideProjectClassNames() { $classNames = []; $srcDir = realpath(__DIR__ . '/../src/'); @@ -88,7 +83,12 @@ public function provideProjectClassNames() continue; } - $classNames[][] = 'MongoDB\\' . str_replace(DIRECTORY_SEPARATOR, '\\', substr($file->getRealPath(), strlen($srcDir) + 1, -4)); + $className = 'MongoDB\\' . str_replace(DIRECTORY_SEPARATOR, '\\', substr($file->getRealPath(), strlen($srcDir) + 1, -4)); + if (in_array($className, self::SKIPPED_CLASSES)) { + continue; + } + + $classNames[$className][] = $className; } return $classNames; diff --git a/tests/PsrLogAdapterTest.php b/tests/PsrLogAdapterTest.php new file mode 100644 index 000000000..1907b8532 --- /dev/null +++ b/tests/PsrLogAdapterTest.php @@ -0,0 +1,140 @@ +logger = $this->createTestPsrLogger(); + + PsrLogAdapter::addLogger($this->logger); + } + + public function tearDown(): void + { + PsrLogAdapter::removeLogger($this->logger); + } + + public function testAddAndRemoveLoggerFunctions(): void + { + $logger = $this->createTestPsrLogger(); + + mongoc_log(LogSubscriber::LEVEL_INFO, 'domain1', 'info1'); + PsrLogAdapter::writeLog(PsrLogAdapter::INFO, 'domain2', 'info2'); + + add_logger($logger); + + mongoc_log(LogSubscriber::LEVEL_INFO, 'domain3', 'info3'); + PsrLogAdapter::writeLog(PsrLogAdapter::INFO, 'domain4', 'info4'); + + remove_logger($logger); + + mongoc_log(LogSubscriber::LEVEL_INFO, 'domain5', 'info5'); + PsrLogAdapter::writeLog(PsrLogAdapter::INFO, 'domain6', 'info6'); + + $expectedLogs = [ + [LogLevel::INFO, 'info3', ['domain' => 'domain3']], + [LogLevel::INFO, 'info4', ['domain' => 'domain4']], + ]; + + $this->assertSame($expectedLogs, $logger->logs); + } + + public function testLog(): void + { + /* This uses PHPC's internal mongoc_log() function to write messages + * directly to libmongoc. Those messages are then relayed to + * PsrLogAdapter and forwarded to each registered PSR logger. + * + * Note: it's not possible to test PsrLogAdapter::log() with an invalid + * level since mongoc_log() already validates its level parameter. */ + mongoc_log(LogSubscriber::LEVEL_ERROR, 'domain1', 'error'); + mongoc_log(LogSubscriber::LEVEL_CRITICAL, 'domain2', 'critical'); + mongoc_log(LogSubscriber::LEVEL_WARNING, 'domain3', 'warning'); + mongoc_log(LogSubscriber::LEVEL_MESSAGE, 'domain4', 'message'); + mongoc_log(LogSubscriber::LEVEL_INFO, 'domain5', 'info'); + mongoc_log(LogSubscriber::LEVEL_DEBUG, 'domain6', 'debug'); + + $expectedLogs = [ + [LogLevel::ERROR, 'error', ['domain' => 'domain1']], + [LogLevel::ERROR, 'critical', ['domain' => 'domain2']], + [LogLevel::WARNING, 'warning', ['domain' => 'domain3']], + [LogLevel::NOTICE, 'message', ['domain' => 'domain4']], + [LogLevel::INFO, 'info', ['domain' => 'domain5']], + [LogLevel::DEBUG, 'debug', ['domain' => 'domain6']], + ]; + + $this->assertSame($expectedLogs, $this->logger->logs); + } + + #[TestWith([-1])] + #[TestWith([9])] + public function testWriteLogWithInvalidLevel(int $level): void + { + $this->expectException(UnexpectedValueException::class); + $this->expectExceptionMessage(sprintf('Expected level to be >= 0 and <= 8, %d given for domain "domain" and message: message', $level)); + + PsrLogAdapter::writeLog($level, 'domain', 'message'); + } + + public function testWriteLog(): void + { + PsrLogAdapter::writeLog(PsrLogAdapter::EMERGENCY, 'domain1', 'emergency'); + PsrLogAdapter::writeLog(PsrLogAdapter::ALERT, 'domain2', 'alert'); + PsrLogAdapter::writeLog(PsrLogAdapter::CRITICAL, 'domain3', 'critical'); + PsrLogAdapter::writeLog(PsrLogAdapter::ERROR, 'domain4', 'error'); + PsrLogAdapter::writeLog(PsrLogAdapter::WARN, 'domain5', 'warn'); + PsrLogAdapter::writeLog(PsrLogAdapter::NOTICE, 'domain6', 'notice'); + PsrLogAdapter::writeLog(PsrLogAdapter::INFO, 'domain7', 'info'); + PsrLogAdapter::writeLog(PsrLogAdapter::DEBUG, 'domain8', 'debug'); + PsrLogAdapter::writeLog(PsrLogAdapter::TRACE, 'domain9', 'trace'); + + $expectedLogs = [ + [LogLevel::EMERGENCY, 'emergency', ['domain' => 'domain1']], + [LogLevel::ALERT, 'alert', ['domain' => 'domain2']], + [LogLevel::CRITICAL, 'critical', ['domain' => 'domain3']], + [LogLevel::ERROR, 'error', ['domain' => 'domain4']], + [LogLevel::WARNING, 'warn', ['domain' => 'domain5']], + [LogLevel::NOTICE, 'notice', ['domain' => 'domain6']], + [LogLevel::INFO, 'info', ['domain' => 'domain7']], + [LogLevel::DEBUG, 'debug', ['domain' => 'domain8']], + [LogLevel::DEBUG, 'trace', ['domain' => 'domain9']], + ]; + + $this->assertSame($expectedLogs, $this->logger->logs); + } + + private function createTestPsrLogger(): LoggerInterface + { + return new class extends AbstractLogger { + public $logs = []; + + /** + * Note: parameter type hints are omitted for compatibility with + * psr/log 1.1.4 and PHP 7.4. + */ + public function log($level, $message, array $context = []): void + { + $this->logs[] = func_get_args(); + } + }; + } +} diff --git a/tests/SpecTests/AtlasDataLakeSpecTest.php b/tests/SpecTests/AtlasDataLakeSpecTest.php deleted file mode 100644 index 4b37a2f12..000000000 --- a/tests/SpecTests/AtlasDataLakeSpecTest.php +++ /dev/null @@ -1,249 +0,0 @@ -isAtlasDataLake()) { - $this->markTestSkipped('Server is not Atlas Data Lake'); - } - } - - /** - * Assert that the expected and actual command documents match. - * - * @param stdClass $expected Expected command document - * @param stdClass $actual Actual command document - */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual): void - { - foreach ($expected as $key => $value) { - if ($value === null) { - static::assertObjectNotHasAttribute($key, $actual); - unset($expected->{$key}); - } - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Execute an individual test case from the specification. - * - * @dataProvider provideTests - * @param stdClass $test Individual "tests[]" document - * @param array $runOn Top-level "runOn" array with server requirements - * @param array $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - */ - public function testAtlasDataLake(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void - { - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - if (isset($test->skipReason)) { - $this->markTestSkipped($test->skipReason); - } - - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - $context = Context::fromCrud($test, $databaseName, $collectionName); - $this->setContext($context); - - /* Note: Atlas Data Lake is read-only, so do not attempt to drop the - * collection under test or insert data fixtures. Necesarry data - * fixtures are already specified in the mongohoused configuration. */ - - if (isset($test->failPoint)) { - throw new LogicException('ADL tests are not expected to configure fail points'); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromCrud($context->getClient(), (array) $test->expectations); - $commandExpectations->startMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromCrud($operation)->assert($this, $context); - } - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - - if (isset($test->outcome->collection->data)) { - throw new LogicException('ADL tests are not expected to assert collection data'); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/atlas_data_lake/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName]; - } - } - - return $testArgs; - } - - /** - * Prose test 1: Connect without authentication - */ - public function testKillCursors(): void - { - $cursorId = null; - $cursorNamespace = null; - - (new CommandObserver())->observe( - function (): void { - $client = static::createTestClient(); - $client->test->driverdata->find([], ['batchSize' => 2, 'limit' => 3]); - }, - function (array $event) use (&$cursorId, &$cursorNamespace): void { - if ($event['started']->getCommandName() === 'find') { - $this->assertArrayHasKey('succeeded', $event); - - $reply = $event['succeeded']->getReply(); - $this->assertObjectHasAttribute('cursor', $reply); - $this->assertIsObject($reply->cursor); - $this->assertObjectHasAttribute('id', $reply->cursor); - $this->assertIsInt($reply->cursor->id); - $this->assertObjectHasAttribute('ns', $reply->cursor); - $this->assertIsString($reply->cursor->ns); - - /* Note: MongoDB\Driver\CursorId is not used here; however, - * we shouldn't have to worry about encoutnering a 64-bit - * cursor IDs on a 32-bit platform mongohoused allocates IDs - * sequentially (starting from 1). */ - $cursorId = $reply->cursor->id; - $cursorNamespace = $reply->cursor->ns; - - return; - } - - /* After the initial find command, expect that killCursors is - * next and that a cursor ID and namespace were collected. */ - $this->assertSame('killCursors', $event['started']->getCommandName()); - $this->assertIsInt($cursorId); - $this->assertIsString($cursorNamespace); - - [$databaseName, $collectionName] = explode('.', $cursorNamespace, 2); - $command = $event['started']->getCommand(); - - /* Assert that the killCursors command uses the namespace and - * cursor ID from the find command reply. */ - $this->assertSame($databaseName, $event['started']->getDatabaseName()); - $this->assertSame($databaseName, $command->{'$db'}); - $this->assertObjectHasAttribute('killCursors', $command); - $this->assertSame($collectionName, $command->killCursors); - $this->assertObjectHasAttribute('cursors', $command); - $this->assertIsArray($command->cursors); - $this->assertArrayHasKey(0, $command->cursors); - $this->assertSame($cursorId, $command->cursors[0]); - - /* Assert that the killCursors command reply indicates that the - * expected cursor ID was killed. */ - $reply = $event['succeeded']->getReply(); - $this->assertObjectHasAttribute('cursorsKilled', $reply); - $this->assertIsArray($reply->cursorsKilled); - $this->assertArrayHasKey(0, $reply->cursorsKilled); - $this->assertSame($cursorId, $reply->cursorsKilled[0]); - } - ); - } - - /** - * Prose test 2: Connect without authentication - */ - public function testConnectWithoutAuth(): void - { - /* Parse URI to remove userinfo component. The query string is left - * as-is and must not include authMechanism or credentials. */ - $parts = parse_url(static::getUri()); - $port = isset($parts['port']) ? ':' . $parts['port'] : ''; - $path = $parts['path'] ?? '/'; - $query = isset($parts['query']) ? '?' . $parts['query'] : ''; - - $uri = $parts['scheme'] . '://' . $parts['host'] . $port . $path . $query; - - $client = static::createTestClient($uri); - $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); - - $this->assertInstanceOf(Cursor::class, $cursor); - $this->assertCommandSucceeded(current($cursor->toArray())); - } - - /** - * Prose test 3: Connect with SCRAM-SHA-1 authentication - */ - public function testConnectwithSCRAMSHA1(): void - { - $client = static::createTestClient(null, ['authMechanism' => 'SCRAM-SHA-1']); - $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); - - $this->assertInstanceOf(Cursor::class, $cursor); - $this->assertCommandSucceeded(current($cursor->toArray())); - } - - /** - * Prose test 4: Connect with SCRAM-SHA-256 authentication - */ - public function testConnectwithSCRAMSHA256(): void - { - $client = static::createTestClient(null, ['authMechanism' => 'SCRAM-SHA-256']); - $cursor = $client->selectDatabase($this->getDatabaseName())->command(['ping' => 1]); - - $this->assertInstanceOf(Cursor::class, $cursor); - $this->assertCommandSucceeded(current($cursor->toArray())); - } - - private function isAtlasDataLake(): bool - { - $cursor = $this->manager->executeCommand( - $this->getDatabaseName(), - new Command(['buildInfo' => 1]) - ); - - $document = current($cursor->toArray()); - - return ! empty($document->dataLake); - } -} diff --git a/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php b/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php new file mode 100644 index 000000000..58c2aa24d --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/FunctionalTestCase.php @@ -0,0 +1,76 @@ +skipIfClientSideEncryptionIsNotSupported(); + } + + protected static function getAWSCredentials(): array + { + return [ + 'accessKeyId' => static::getEnv('AWS_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_SECRET_ACCESS_KEY'), + ]; + } + + protected static function insertKeyVaultData(Client $client, ?array $keyVaultData = null): void + { + $collection = $client->selectCollection('keyvault', 'datakeys', ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)]); + $collection->drop(); + + if (empty($keyVaultData)) { + return; + } + + $collection->insertMany($keyVaultData); + } + + private function createTestCollection(?stdClass $encryptedFields = null, ?stdClass $jsonSchema = null): void + { + $context = $this->getContext(); + $options = $context->defaultWriteOptions; + + if (! empty($encryptedFields)) { + $options['encryptedFields'] = $encryptedFields; + } + + if (! empty($jsonSchema)) { + $options['validator'] = ['$jsonSchema' => $jsonSchema]; + } + + $context->getDatabase()->createCollection($context->collectionName, $options); + } + + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } +} diff --git a/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php new file mode 100644 index 000000000..ee4d61b1a --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/Prose21_AutomaticDataEncryptionKeysTest.php @@ -0,0 +1,160 @@ +isStandalone()) { + $this->markTestSkipped('Automatic data encryption key tests require replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Automatic data encryption key tests require MongoDB 7.0 or later'); + + $client = static::createTestClient(); + $this->database = $client->selectDatabase($this->getDatabaseName()); + + /* Since test cases may create encrypted collections, ensure that the + * collection and any "enxcol_" collections do not exist. Specify + * "encryptedFields" to ensure "enxcol_" collections are handled. */ + $this->database->dropCollection($this->getCollectionName(), ['encryptedFields' => []]); + + // Ensure that the key vault is dropped with a majority write concern + self::insertKeyVaultData($client, []); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'aws' => self::getAWSCredentials(), + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], + ], + ]); + } + + public function tearDown(): void + { + $this->clientEncryption = null; + $this->database = null; + } + + /** @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-1-simple-creation-and-validation */ + #[DataProvider('provideKmsProviderAndMasterKey')] + public function testCase1_SimpleCreationAndValidation(string $kmsProvider, ?array $masterKey): void + { + $encryptedFields = $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]], + ); + + $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); + + $this->expectException(BulkWriteException::class); + $this->expectExceptionMessage('Document failed validation'); + $this->database->selectCollection($this->getCollectionName())->insertOne(['ssn' => '123-45-6789']); + } + + public static function provideKmsProviderAndMasterKey(): Generator + { + yield [ + 'aws', + ['region' => 'us-east-1', 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0'], + ]; + + yield [ + 'local', + null, + ]; + } + + /** @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-2-missing-encryptedfields */ + #[DataProvider('provideKmsProviderAndMasterKey')] + public function testCase2_MissingEncryptedFields(string $kmsProvider, ?array $masterKey): void + { + $this->expectException(InvalidArgumentException::class); + $this->expectExceptionMessage('"encryptedFields" option'); + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + [], + ); + } + + /** @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-3-invalid-keyid */ + #[DataProvider('provideKmsProviderAndMasterKey')] + public function testCase3_InvalidKeyId(string $kmsProvider, ?array $masterKey): void + { + try { + $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => false]]]], + ); + $this->fail('CreateEncryptedCollectionException was not thrown'); + } catch (CreateEncryptedCollectionException $e) { + $this->assertFalse($e->getEncryptedFields()['fields'][0]['keyId'], 'Invalid keyId should not be modified'); + + $previous = $e->getPrevious(); + $this->assertInstanceOf(CommandException::class, $previous); + $this->assertSame(self::SERVER_ERROR_TYPEMISMATCH, $previous->getCode()); + $this->assertStringContainsString('keyId', $previous->getMessage()); + } + } + + /** @see https://github.com/mongodb/specifications/blob/bc37892f360cab9df4082922384e0f4d4233f6d3/source/client-side-encryption/tests/README.rst#case-4-insert-encrypted-value */ + #[DataProvider('provideKmsProviderAndMasterKey')] + public function testCase4_InsertEncryptedValue(string $kmsProvider, ?array $masterKey): void + { + $encryptedFields = $this->database->createEncryptedCollection( + $this->getCollectionName(), + $this->clientEncryption, + $kmsProvider, + $masterKey, + ['encryptedFields' => ['fields' => [['path' => 'ssn', 'bsonType' => 'string', 'keyId' => null]]]], + ); + + $this->assertInstanceOf(Binary::class, $encryptedFields['fields'][0]['keyId'] ?? null); + + $encrypted = $this->clientEncryption->encrypt('123-45-6789', [ + 'keyId' => $encryptedFields['fields'][0]['keyId'], + 'algorithm' => ClientEncryption::ALGORITHM_UNINDEXED, + ]); + + $collection = $this->database->selectCollection($this->getCollectionName()); + $insertedId = $collection->insertOne(['ssn' => $encrypted])->getInsertedId(); + $this->assertNotNull($collection->findOne(['_id' => $insertedId])); + } +} diff --git a/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php new file mode 100644 index 000000000..fc8da6446 --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/Prose22_RangeExplicitEncryptionTest.php @@ -0,0 +1,435 @@ +isStandalone()) { + $this->markTestSkipped('Range explicit encryption tests require replica sets'); + } + + $this->skipIfServerVersion('<', '8.0.0', 'Range explicit encryption tests require MongoDB 8.0 or later'); + + $client = static::createTestClient(); + + $key1Document = $this->decodeJson(file_get_contents(self::$specDir . '/etc/data/keys/key1-document.json')); + $this->key1Id = $key1Document->_id; + + // Drop the key vault collection and insert key1Document with a majority write concern + self::insertKeyVaultData($client, [$key1Document]); + + $this->clientEncryption = $client->createClientEncryption([ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], + ]); + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], + 'bypassQueryAnalysis' => true, + ]; + + $this->encryptedClient = self::createTestClient(null, [], [ + 'autoEncryption' => $autoEncryptionOpts, + /* libmongocrypt caches results from listCollections. Use a new + * client in each test to ensure its encryptedFields is applied. */ + 'disableClientPersistence' => true, + ]); + } + + public function setUpWithTypeAndRangeOpts(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DecimalPrecision') { + $this->markTestSkipped('Bundled libmongocrypt does not support Decimal128 (PHPC-2207)'); + } + + /* Read the encryptedFields file directly into BSON to preserve typing + * for 64-bit integers. This means that DropEncryptedCollection and + * CreateEncryptedCollection will be unable to inspect the option for + * metadata collection names, but that's not necessary for the test. */ + $encryptedFields = Document::fromJSON(file_get_contents(self::$specDir . '/etc/data/range-encryptedFields-' . $type . '.json')); + + $database = $this->encryptedClient->selectDatabase($this->getDatabaseName()); + $database->dropCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + $database->createCollection('explicit_encryption', ['encryptedFields' => $encryptedFields]); + $this->collection = $database->selectCollection('explicit_encryption'); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $fieldName = 'encrypted' . $type; + + $this->collection->insertMany([ + ['_id' => 0, $fieldName => $this->clientEncryption->encrypt(self::cast($type, 0), $encryptOpts)], + ['_id' => 1, $fieldName => $this->clientEncryption->encrypt(self::cast($type, 6), $encryptOpts)], + ['_id' => 2, $fieldName => $this->clientEncryption->encrypt(self::cast($type, 30), $encryptOpts)], + ['_id' => 3, $fieldName => $this->clientEncryption->encrypt(self::cast($type, 200), $encryptOpts)], + ]); + } + + public function tearDown(): void + { + /* Since encryptedClient is created with disableClientPersistence=true, + * free any objects that may hold a reference to its mongoc_client_t */ + $this->collection = null; + $this->clientEncryption = null; + $this->encryptedClient = null; + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#test-setup-rangeopts */ + public static function provideTypeAndRangeOpts(): Generator + { + // TODO: skip DecimalNoPrecision test on mongos + yield 'DecimalNoPrecision' => [ + 'DecimalNoPrecision', + ['sparsity' => 1], + ]; + + yield 'DecimalPrecision' => [ + 'DecimalPrecision', + [ + 'min' => new Decimal128('0'), + 'max' => new Decimal128('200'), + 'sparsity' => 1, + 'precision' => 2, + ], + ]; + + yield 'DoubleNoPrecision' => [ + 'DoubleNoPrecision', + ['sparsity' => 1], + ]; + + yield 'DoublePrecision' => [ + 'DoublePrecision', + [ + 'min' => 0.0, + 'max' => 200.0, + 'sparsity' => 1, + 'precision' => 2, + ], + ]; + + yield 'Date' => [ + 'Date', + [ + 'min' => new UTCDateTime(0), + 'max' => new UTCDateTime(200), + 'sparsity' => 1, + ], + ]; + + yield 'Int' => [ + 'Int', + [ + 'min' => 0, + 'max' => 200, + 'sparsity' => 1, + ], + ]; + + yield 'Long' => [ + 'Long', + [ + 'min' => new Int64(0), + 'max' => new Int64(200), + 'sparsity' => 1, + ], + ]; + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-1-can-decrypt-a-payload */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase1_CanDecryptAPayload(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $originalValue = self::cast($type, 6); + + $insertPayload = $this->clientEncryption->encrypt($originalValue, $encryptOpts); + $decryptedValue = $this->clientEncryption->decrypt($insertPayload); + + /* Decryption of a 64-bit integer will likely result in a scalar int, so + * cast it back to an Int64 before comparing to the original value. */ + if ($type === 'Long' && is_int($decryptedValue)) { + $decryptedValue = self::cast($type, $decryptedValue); + } + + /* Use separate assertions for type and equality as assertSame isn't + * suitable for comparing BSON objects and using assertEquals alone + * would disregard scalar type differences. */ + $this->assertSame(get_debug_type($originalValue), get_debug_type($decryptedValue)); + $this->assertEquals($originalValue, $decryptedValue); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-2-can-find-encrypted-range-and-return-the-maximum */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase2_CanFindEncryptedRangeAndReturnTheMaximum(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $fieldName = 'encrypted' . $type; + + $expr = [ + '$and' => [ + [$fieldName => ['$gte' => self::cast($type, 6)]], + [$fieldName => ['$lte' => self::cast($type, 200)]], + ], + ]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 1, $fieldName => self::cast($type, 6)], + ['_id' => 2, $fieldName => self::cast($type, 30)], + ['_id' => 3, $fieldName => self::cast($type, 200)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-3-can-find-encrypted-range-and-return-the-minimum */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase3_CanFindEncryptedRangeAndReturnTheMinimum(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $fieldName = 'encrypted' . $type; + + $expr = [ + '$and' => [ + [$fieldName => ['$gte' => self::cast($type, 0)]], + [$fieldName => ['$lte' => self::cast($type, 6)]], + ], + ]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 0, $fieldName => self::cast($type, 0)], + ['_id' => 1, $fieldName => self::cast($type, 6)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-4-can-find-encrypted-range-with-an-open-range-query */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase4_CanFindEncryptedRangeWithAnOpenRangeQuery(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $fieldName = 'encrypted' . $type; + + $expr = ['$and' => [[$fieldName => ['$gt' => self::cast($type, 30)]]]]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find($encryptedExpr, ['sort' => ['_id' => 1]]); + $expectedDocuments = [['_id' => 3, $fieldName => self::cast($type, 200)]]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-5-can-run-an-aggregation-expression-inside-expr */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase5_CanRunAnAggregationExpressionInsideExpr(string $type, array $rangeOpts): void + { + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'queryType' => ClientEncryption::QUERY_TYPE_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $fieldName = 'encrypted' . $type; + $fieldPath = '$' . $fieldName; + + $expr = ['$and' => [['$lt' => [$fieldPath, self::cast($type, 30)]]]]; + + $encryptedExpr = $this->clientEncryption->encryptExpression($expr, $encryptOpts); + $cursor = $this->collection->find(['$expr' => $encryptedExpr], ['sort' => ['_id' => 1]]); + + $expectedDocuments = [ + ['_id' => 0, $fieldName => self::cast($type, 0)], + ['_id' => 1, $fieldName => self::cast($type, 6)], + ]; + + $this->assertMultipleDocumentsMatch($expectedDocuments, $cursor); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-6-encrypting-a-document-greater-than-the-maximum-errors */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase6_EncryptingADocumentGreaterThanTheMaximumErrors(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DoubleNoPrecision') { + $this->markTestSkipped('Test is not applicable to "NoPrecision" types'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('Value must be greater than or equal to the minimum value and less than or equal to the maximum value'); + $this->clientEncryption->encrypt(self::cast($type, 201), $encryptOpts); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-7-encrypting-a-value-of-a-different-type-errors */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase7_EncryptingAValueOfADifferentTypeErrors(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DoubleNoPrecision') { + /* Explicit encryption relies on min/max range options to check + * types and "NoPrecision" intentionally omits those options. */ + $this->markTestSkipped('Test is not applicable to DoubleNoPrecision and DecimalNoPrecision'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts, + ]; + + $value = $type === 'Int' ? 6.0 : 6; + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('expected matching \'min\' and value type'); + $this->clientEncryption->encrypt($value, $encryptOpts); + } + + /** @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-8-setting-precision-errors-if-the-type-is-not-double-or-decimal128 */ + #[DataProvider('provideTypeAndRangeOpts')] + public function testCase8_SettingPrecisionErrorsIfTheTypeIsNotDoubleOrDecimal128(string $type, array $rangeOpts): void + { + if ($type === 'DecimalNoPrecision' || $type === 'DecimalPrecision' || $type === 'DoubleNoPrecision' || $type === 'DoublePrecision') { + $this->markTestSkipped('Test is not applicable to Double and Decimal types'); + } + + $this->setUpWithTypeAndRangeOpts($type, $rangeOpts); + + $encryptOpts = [ + 'keyId' => $this->key1Id, + 'algorithm' => ClientEncryption::ALGORITHM_RANGE, + 'contentionFactor' => 0, + 'rangeOpts' => $rangeOpts + ['precision' => 2], + ]; + + $this->expectException(EncryptionException::class); + $this->expectExceptionMessage('expected \'precision\' to be set with double or decimal128 index'); + $this->clientEncryption->encrypt(self::cast($type, 6), $encryptOpts); + } + + private function assertMultipleDocumentsMatch(array $expectedDocuments, Iterator $actualDocuments): void + { + $mi = new MultipleIterator(MultipleIterator::MIT_NEED_ANY); + $mi->attachIterator(new ArrayIterator($expectedDocuments)); + $mi->attachIterator($actualDocuments); + + foreach ($mi as $documents) { + [$expectedDocument, $actualDocument] = $documents; + $this->assertNotNull($expectedDocument); + $this->assertNotNull($actualDocument); + + $this->assertDocumentsMatch($expectedDocument, $actualDocument); + } + } + + private static function cast(string $type, int $value): mixed + { + return match ($type) { + 'DecimalNoPrecision', 'DecimalPrecision' => new Decimal128((string) $value), + 'DoubleNoPrecision', 'DoublePrecision' => (float) $value, + 'Date' => new UTCDateTime($value), + 'Int' => $value, + 'Long' => new Int64($value), + default => throw new LogicException('Unsupported type: ' . $type), + }; + } +} diff --git a/tests/SpecTests/ClientSideEncryption/Prose25_LookupTest.php b/tests/SpecTests/ClientSideEncryption/Prose25_LookupTest.php new file mode 100644 index 000000000..6abfbda13 --- /dev/null +++ b/tests/SpecTests/ClientSideEncryption/Prose25_LookupTest.php @@ -0,0 +1,372 @@ +isStandalone()) { + $this->markTestSkipped('Lookup tests require replica sets'); + } + + $this->skipIfServerVersion('<', '7.0.0', 'Lookup encryption tests require MongoDB 7.0 or later'); + + $key1Document = $this->decodeJson(file_get_contents(self::$dataDir . '/key-doc.json')); + $this->key1Id = $key1Document->_id; + + $encryptedClient = $this->getEncryptedClient(); + + // Drop the key vault collection and insert key1Document with a majority write concern + self::insertKeyVaultData($encryptedClient, [$key1Document]); + + $this->refreshCollections($encryptedClient); + } + + private function getEncryptedClient(): Client + { + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], + ]; + + return self::createTestClient(null, [], [ + 'autoEncryption' => $autoEncryptionOpts, + /* libmongocrypt caches results from listCollections. Use a new + * client in each test to ensure its encryptedFields is applied. */ + 'disableClientPersistence' => true, + ]); + } + + private function refreshCollections(Client $client): void + { + $encryptedDb = $client->getDatabase(self::getDatabaseName()); + $unencryptedDb = self::createTestClient()->getDatabase(self::getDatabaseName()); + + $optionsMap = [ + self::COLL_CSFLE => [ + 'validator' => [ + '$jsonSchema' => $this->decodeJson(file_get_contents(self::$dataDir . '/schema-csfle.json')), + ], + ], + self::COLL_CSFLE2 => [ + 'validator' => [ + '$jsonSchema' => $this->decodeJson(file_get_contents(self::$dataDir . '/schema-csfle2.json')), + ], + ], + self::COLL_QE => [ + 'encryptedFields' => $this->decodeJson(file_get_contents(self::$dataDir . '/schema-qe.json')), + ], + self::COLL_QE2 => [ + 'encryptedFields' => $this->decodeJson(file_get_contents(self::$dataDir . '/schema-qe2.json')), + ], + self::COLL_NO_SCHEMA => [], + self::COLL_NO_SCHEMA2 => [], + ]; + + foreach ($optionsMap as $collectionName => $options) { + $encryptedDb->dropCollection($collectionName); + $encryptedDb->createCollection($collectionName, $options); + + $collection = $unencryptedDb->getCollection($collectionName); + + $result = $encryptedDb->getCollection($collectionName)->insertOne([$collectionName => $collectionName]); + + if ($options) { + $document = $collection->findOne(['_id' => $result->getInsertedId()]); + $this->assertInstanceOf(Binary::class, $document->{$collectionName}); + } + } + } + + private function assertPipelineReturnsSingleDocument(string $collection, array $pipeline, array $expected): void + { + $this->skipIfServerVersion('<', '8.1.0', 'Lookup test case requires server version 8.1.0 or later'); + $this->skipIfClientSideEncryptionIsNotSupported(); + + $cursor = $this + ->getEncryptedClient() + ->getCollection(self::getDatabaseName(), $collection) + ->aggregate($pipeline); + + $cursor->rewind(); + $this->assertMatchesDocument( + $expected, + $cursor->current(), + ); + $this->assertNull($cursor->next()); + } + + public function testCase1_CsfleJoinsNoSchema(): void + { + $pipeline = [ + [ + '$match' => ['csfle' => 'csfle'], + ], + [ + '$lookup' => [ + 'from' => 'no_schema', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['no_schema' => 'no_schema'], + ], + [ + '$project' => ['_id' => 0], + ], + ], + ], + ], + [ + '$project' => ['_id' => 0], + ], + ]; + $expected = [ + 'csfle' => 'csfle', + 'matched' => [ + ['no_schema' => 'no_schema'], + ], + ]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_CSFLE, $pipeline, $expected); + } + + public function testCase2_QeJoinsNoSchema(): void + { + $pipeline = [ + [ + '$match' => ['qe' => 'qe'], + ], + [ + '$lookup' => [ + 'from' => 'no_schema', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['no_schema' => 'no_schema'], + ], + [ + '$project' => [ + '_id' => 0, + '__safeContent__' => 0, + ], + ], + ], + ], + ], + [ + '$project' => [ + '_id' => 0, + '__safeContent__' => 0, + ], + ], + ]; + $expected = [ + 'qe' => 'qe', + 'matched' => [ + ['no_schema' => 'no_schema'], + ], + ]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_QE, $pipeline, $expected); + } + + public function testCase3_NoSchemaJoinsCsfle(): void + { + $pipeline = [['$match' => ['no_schema' => 'no_schema']], + [ + '$lookup' => [ + 'from' => 'csfle', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['csfle' => 'csfle'], + ], + [ + '$project' => ['_id' => 0], + ], + ], + ], + ], + ['$project' => ['_id' => 0]], + ]; + $expected = ['no_schema' => 'no_schema', 'matched' => [['csfle' => 'csfle']]]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_NO_SCHEMA, $pipeline, $expected); + } + + public function testCase4_NoSchemaJoinsQe(): void + { + $pipeline = [ + [ + '$match' => ['no_schema' => 'no_schema'], + ], + [ + '$lookup' => [ + 'from' => 'qe', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['qe' => 'qe'], + ], + [ + '$project' => [ + '_id' => 0, + '__safeContent__' => 0, + ], + ], + ], + ], + ], + [ + '$project' => ['_id' => 0], + ], + ]; + $expected = [ + 'no_schema' => 'no_schema', + 'matched' => [ + ['qe' => 'qe'], + ], + ]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_NO_SCHEMA, $pipeline, $expected); + } + + public function testCase5_CsfleJoinsCsfle2(): void + { + $pipeline = [ + ['$match' => ['csfle' => 'csfle']], + [ + '$lookup' => [ + 'from' => 'csfle2', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['csfle2' => 'csfle2'], + ], + [ + '$project' => ['_id' => 0], + ], + ], + ], + ], + ['$project' => ['_id' => 0]], + ]; + $expected = ['csfle' => 'csfle', 'matched' => [['csfle2' => 'csfle2']]]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_CSFLE, $pipeline, $expected); + } + + public function testCase6_QeJoinsQe2(): void + { + $pipeline = [ + ['$match' => ['qe' => 'qe']], + [ + '$lookup' => [ + 'from' => 'qe2', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['qe2' => 'qe2'], + ], + [ + '$project' => [ + '_id' => 0, + '__safeContent__' => 0, + ], + ], + ], + ], + ], + ['$project' => ['_id' => 0, '__safeContent__' => 0]], + ]; + $expected = ['qe' => 'qe', 'matched' => [['qe2' => 'qe2']]]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_QE, $pipeline, $expected); + } + + public function testCase7_NoSchemaJoinsNoSchema2(): void + { + $pipeline = [ + ['$match' => ['no_schema' => 'no_schema']], + [ + '$lookup' => [ + 'from' => 'no_schema2', + 'as' => 'matched', + 'pipeline' => [ + ['$match' => ['no_schema2' => 'no_schema2']], + ['$project' => ['_id' => 0]], + ], + ], + ], + ['$project' => ['_id' => 0]], + ]; + $expected = ['no_schema' => 'no_schema', 'matched' => [['no_schema2' => 'no_schema2']]]; + + $this->assertPipelineReturnsSingleDocument(self::COLL_NO_SCHEMA, $pipeline, $expected); + } + + public function testCase8_CsfleJoinsQeFails(): void + { + $this->skipIfServerVersion('<', '8.1.0', 'Lookup test case requires server version 8.1.0 or later'); + $this->skipIfClientSideEncryptionIsNotSupported(); + + $this->expectExceptionMessage('not supported'); + + $this->getEncryptedClient() + ->getCollection(self::getDatabaseName(), self::COLL_CSFLE) + ->aggregate([ + [ + '$match' => ['csfle' => 'qe'], + ], + [ + '$lookup' => [ + 'from' => 'qe', + 'as' => 'matched', + 'pipeline' => [ + [ + '$match' => ['qe' => 'qe'], + ], + [ + '$project' => ['_id' => 0], + ], + ], + ], + ], + [ + '$project' => ['_id' => 0], + ], + ]); + } + + public function testCase9_TestErrorWithLessThan8_1(): void + { + $this->markTestSkipped('Depends on PHPC-2616 to determine crypt shared version.'); + } +} diff --git a/tests/SpecTests/ClientSideEncryptionSpecTest.php b/tests/SpecTests/ClientSideEncryptionSpecTest.php index b0eb983f4..585883ae0 100644 --- a/tests/SpecTests/ClientSideEncryptionSpecTest.php +++ b/tests/SpecTests/ClientSideEncryptionSpecTest.php @@ -4,6 +4,7 @@ use Closure; use MongoDB\BSON\Binary; +use MongoDB\BSON\Document; use MongoDB\BSON\Int64; use MongoDB\Client; use MongoDB\Collection; @@ -23,6 +24,9 @@ use MongoDB\Driver\WriteConcern; use MongoDB\Tests\CommandObserver; use PHPUnit\Framework\Assert; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; +use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\SkippedTestError; use stdClass; use Throwable; @@ -31,81 +35,116 @@ use function base64_decode; use function basename; use function count; -use function explode; use function file_get_contents; use function getenv; use function glob; use function in_array; -use function is_executable; -use function is_readable; use function iterator_to_array; use function json_decode; use function sprintf; use function str_repeat; -use function strlen; use function substr; -use function unserialize; -use function version_compare; -use const DIRECTORY_SEPARATOR; -use const PATH_SEPARATOR; +use const JSON_THROW_ON_ERROR; /** * Client-side encryption spec tests. * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption - * @group csfle - * @group serverless */ +#[Group('csfle')] class ClientSideEncryptionSpecTest extends FunctionalTestCase { public const LOCAL_MASTERKEY = 'Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk'; - /** @var array */ - private static $incompleteTests = [ - 'awsTemporary: Insert a document with auto encryption using the AWS provider with temporary credentials' => 'Not yet implemented (PHPC-1751)', - 'awsTemporary: Insert with invalid temporary credentials' => 'Not yet implemented (PHPC-1751)', - 'azureKMS: Insert a document with auto encryption using Azure KMS provider' => 'RHEL platform is missing Azure root certificate (PHPLIB-619)', + private static array $incompleteTests = [ 'explain: Explain a find with deterministic encryption' => 'crypt_shared does not add apiVersion field to explain commands (PHPLIB-947, SERVER-69564)', + 'fle2v2-Rangev2-Decimal-Aggregate: FLE2 Range Decimal. Aggregate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $gt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Find with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Aggregate with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Wrong type: Insert Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Correctness: Wrong type: Find Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Delete: FLE2 Range Decimal. Delete.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-FindOneAndUpdate: FLE2 Range Decimal. FindOneAndUpdate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-InsertFind: FLE2 Range Decimal. Insert and Find.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Aggregate: FLE2 Range DecimalPrecision. Aggregate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $gt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $lt below min' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $gt above max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with full range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Find with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Insert out of range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Insert min and max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $gte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $gt with no results' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $lte' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $lt below min' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $gt above max' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $gt and $lt' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with equality' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with full range' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Aggregate with $in' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Wrong type: Insert Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Correctness: Wrong type: Find Int' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Delete: FLE2 Range DecimalPrecision. Delete.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-FindOneAndUpdate: FLE2 Range DecimalPrecision. FindOneAndUpdate.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-InsertFind: FLE2 Range DecimalPrecision. Insert and Find.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-DecimalPrecision-Update: FLE2 Range DecimalPrecision. Update.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', + 'fle2v2-Rangev2-Decimal-Update: FLE2 Range Decimal. Update.' => 'Bundled libmongocrypt does not support Decimal128 (PHPC-2207)', 'timeoutMS: timeoutMS applied to listCollections to get collection schema' => 'Not yet implemented (PHPC-1760)', 'timeoutMS: remaining timeoutMS applied to find to get keyvault data' => 'Not yet implemented (PHPC-1760)', + 'namedKMS: Automatically encrypt and decrypt with a named KMS provider' => 'Not yet implemented (PHPLIB-1328)', + 'fle2v2-Compact: Compact works' => 'Failing due to bug in libmongocrypt (LIBMONGOCRYPT-699)', + 'keyCache: Insert with deterministic encryption, then find it' => 'Wait operation not supported (PHPLIB-1496)', ]; + private static string $specDir = __DIR__ . '/../specifications/source/client-side-encryption'; + public function setUp(): void { parent::setUp(); $this->skipIfClientSideEncryptionIsNotSupported(); - - if (! static::isCryptSharedLibAvailable() && ! static::isMongocryptdAvailable()) { - $this->markTestSkipped('Neither crypt_shared nor mongocryptd are available'); - } } /** * Assert that the expected and actual command documents match. * + * Note: this method may modify the $expected object. + * * @param stdClass $expected Expected command document * @param stdClass $actual Actual command document */ public static function assertCommandMatches(stdClass $expected, stdClass $actual): void { - static::assertDocumentsMatch($expected, $actual); - } - - public static function createTestClient(?string $uri = null, array $options = [], array $driverOptions = []): Client - { - if (isset($driverOptions['autoEncryption']) && getenv('CRYPT_SHARED_LIB_PATH')) { - $driverOptions['autoEncryption']['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); - } + static::assertCommandOmittedFields($expected, $actual); - return parent::createTestClient($uri, $options, $driverOptions); + static::assertDocumentsMatch($expected, $actual); } /** * Execute an individual test case from the specification. * - * @dataProvider provideTests * @param stdClass $test Individual "tests[]" document * @param array $runOn Top-level "runOn" array with server requirements * @param array $data Top-level "data" array to initialize collection @@ -114,6 +153,7 @@ public static function createTestClient(?string $uri = null, array $options = [] * @param string $databaseName Name of database under test * @param string $collectionName Name of collection under test */ + #[DataProvider('provideTests')] public function testClientSideEncryption(stdClass $test, ?array $runOn, array $data, ?stdClass $encryptedFields = null, ?array $keyVaultData = null, ?stdClass $jsonSchema = null, ?string $databaseName = null, ?string $collectionName = null): void { if (isset(self::$incompleteTests[$this->dataDescription()])) { @@ -128,13 +168,8 @@ public function testClientSideEncryption(stdClass $test, ?array $runOn, array $d $this->markTestSkipped($test->skipReason); } - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - // TODO: Remove this once SERVER-66901 is implemented (see: PHPLIB-884) - if (isset($test->clientOptions->autoEncryptOpts->encryptedFieldsMap)) { - $test->clientOptions->autoEncryptOpts->encryptedFieldsMap = $test->clientOptions->autoEncryptOpts->encryptedFieldsMap; - } + $databaseName ??= $this->getDatabaseName(); + $collectionName ??= $this->getCollectionName(); try { $context = Context::fromClientSideEncryption($test, $databaseName, $collectionName); @@ -176,15 +211,28 @@ public function testClientSideEncryption(stdClass $test, ?array $runOn, array $d } } - public function provideTests() + public static function provideTests() { $testArgs = []; - foreach (glob(__DIR__ . '/client-side-encryption/tests/*.json') as $filename) { + foreach (glob(self::$specDir . '/tests/legacy/*.json') as $filename) { $group = basename($filename, '.json'); + /* Some tests need to differentiate int32 and int64 BSON types. + * Decode certain field paths as BSON to preserve type information + * that would otherwise be lost by round-tripping through PHP. */ + $typeMap = [ + 'fieldPaths' => [ + 'encrypted_fields.fields' => 'bson', + 'tests.$.operations.$.arguments.document' => 'bson', + 'tests.$.operations.$.arguments.pipeline.$' => 'bson', + 'tests.$.operations.$.arguments.filter' => 'bson', + 'tests.$.operations.$.arguments.update' => 'bson', + ], + ]; + try { - $json = $this->decodeJson(file_get_contents($filename)); + $json = Document::fromJSON(file_get_contents($filename))->toPHP($typeMap); } catch (Throwable $e) { $testArgs[$group] = [ (object) ['skipReason' => sprintf('Exception loading file "%s": %s', $filename, $e->getMessage())], @@ -218,8 +266,8 @@ public function provideTests() * Prose test 2: Data Key and Double Encryption * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#data-key-and-double-encryption - * @dataProvider dataKeyProvider */ + #[DataProvider('dataKeyProvider')] public function testDataKeyAndDoubleEncryption(string $providerName, $masterKey): void { $client = static::createTestClient(); @@ -234,7 +282,7 @@ public function testDataKeyAndDoubleEncryption(string $providerName, $masterKey) 'aws' => Context::getAWSCredentials(), 'azure' => Context::getAzureCredentials(), 'gcp' => Context::getGCPCredentials(), - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], 'kmip' => ['endpoint' => Context::getKmipEndpoint()], ], 'tlsOptions' => [ @@ -281,15 +329,15 @@ function ($command) use (&$insertCommand): void { if ($command['started']->getCommandName() === 'insert') { $insertCommand = $command['started']->getCommand(); } - } + }, ); $this->assertInstanceOf(Binary::class, $dataKeyId); $this->assertSame(Binary::TYPE_UUID, $dataKeyId->getType()); $this->assertNotNull($insertCommand); - $this->assertObjectHasAttribute('writeConcern', $insertCommand); - $this->assertObjectHasAttribute('w', $insertCommand->writeConcern); + $this->assertObjectHasProperty('writeConcern', $insertCommand); + $this->assertObjectHasProperty('w', $insertCommand->writeConcern); $this->assertSame(WriteConcern::MAJORITY, $insertCommand->writeConcern->w); $keys = $client->selectCollection('keyvault', 'datakeys')->find(['_id' => $dataKeyId]); @@ -357,22 +405,22 @@ public static function dataKeyProvider() * Prose test 3: External Key Vault * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#external-key-vault-test - * @testWith [false] - * [true] */ + #[TestWith([false])] + #[TestWith([true])] public function testExternalKeyVault($withExternalKeyVault): void { $client = static::createTestClient(); $client->selectCollection('db', 'coll')->drop(); self::insertKeyVaultData($client, [ - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-key.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/external/external-key.json')), ]); $encryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], ]; @@ -382,7 +430,7 @@ public function testExternalKeyVault($withExternalKeyVault): void $autoEncryptionOpts = $encryptionOpts + [ 'schemaMap' => [ - 'db.coll' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-schema.json')), + 'db.coll' => $this->decodeJson(file_get_contents(self::$specDir . '/external/external-schema.json')), ], ]; @@ -419,7 +467,7 @@ public static function provideBSONSizeLimitsAndBatchSplittingTests() { yield 'Test 1' => [ static function (self $test, Collection $collection): void { - $collection->insertOne(['_id' => 'over_2mib_under_16mib', 'unencrypted' => str_repeat('a', 2097152)]); + $collection->insertOne(['_id' => 'over_2mib_under_16mib', 'unencrypted' => str_repeat('a', 2_097_152)]); $test->assertCollectionCount($collection->getNamespace(), 1); }, ]; @@ -427,7 +475,7 @@ static function (self $test, Collection $collection): void { yield 'Test 2' => [ static function (self $test, Collection $collection, array $document): void { $collection->insertOne( - ['_id' => 'encryption_exceeds_2mib', 'unencrypted' => str_repeat('a', 2097152 - 2000)] + $document + ['_id' => 'encryption_exceeds_2mib', 'unencrypted' => str_repeat('a', 2_097_152 - 2000)] + $document, ); $test->assertCollectionCount($collection->getNamespace(), 1); }, @@ -439,8 +487,8 @@ static function (self $test, Collection $collection): void { (new CommandObserver())->observe( function () use ($collection): void { $collection->insertMany([ - ['_id' => 'over_2mib_1', 'unencrypted' => str_repeat('a', 2097152)], - ['_id' => 'over_2mib_2', 'unencrypted' => str_repeat('a', 2097152)], + ['_id' => 'over_2mib_1', 'unencrypted' => str_repeat('a', 2_097_152)], + ['_id' => 'over_2mib_2', 'unencrypted' => str_repeat('a', 2_097_152)], ]); }, function ($command) use (&$commands): void { @@ -449,7 +497,7 @@ function ($command) use (&$commands): void { } $commands[] = $command; - } + }, ); $test->assertCount(2, $commands); @@ -464,11 +512,11 @@ function () use ($collection, $document): void { $collection->insertMany([ [ '_id' => 'encryption_exceeds_2mib_1', - 'unencrypted' => str_repeat('a', 2097152 - 2000), + 'unencrypted' => str_repeat('a', 2_097_152 - 2000), ] + $document, [ '_id' => 'encryption_exceeds_2mib_2', - 'unencrypted' => str_repeat('a', 2097152 - 2000), + 'unencrypted' => str_repeat('a', 2_097_152 - 2000), ] + $document, ]); }, @@ -478,7 +526,7 @@ function ($command) use (&$commands): void { } $commands[] = $command; - } + }, ); $test->assertCount(2, $commands); @@ -487,7 +535,7 @@ function ($command) use (&$commands): void { yield 'Test 5' => [ static function (self $test, Collection $collection): void { - $collection->insertOne(['_id' => 'under_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)]); + $collection->insertOne(['_id' => 'under_16mib', 'unencrypted' => str_repeat('a', 16_777_216 - 2000)]); $test->assertCollectionCount($collection->getNamespace(), 1); }, ]; @@ -496,7 +544,7 @@ static function (self $test, Collection $collection): void { static function (self $test, Collection $collection, array $document): void { $test->expectException(BulkWriteException::class); $test->expectExceptionMessageMatches('#object to insert too large#'); - $collection->insertOne(['_id' => 'encryption_exceeds_16mib', 'unencrypted' => str_repeat('a', 16777216 - 2000)] + $document); + $collection->insertOne(['_id' => 'encryption_exceeds_16mib', 'unencrypted' => str_repeat('a', 16_777_216 - 2000)] + $document); }, ]; } @@ -505,23 +553,23 @@ static function (self $test, Collection $collection, array $document): void { * Prose test 4: BSON Size Limits and Batch Splitting * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#bson-size-limits-and-batch-splitting - * @dataProvider provideBSONSizeLimitsAndBatchSplittingTests */ + #[DataProvider('provideBSONSizeLimitsAndBatchSplittingTests')] public function testBSONSizeLimitsAndBatchSplitting(Closure $test): void { $client = static::createTestClient(); $client->selectCollection('db', 'coll')->drop(); - $client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-schema.json'))]]); + $client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $this->decodeJson(file_get_contents(self::$specDir . '/limits/limits-schema.json'))]]); self::insertKeyVaultData($client, [ - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-key.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/limits/limits-key.json')), ]); $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], 'keyVaultClient' => $client, ]; @@ -530,7 +578,7 @@ public function testBSONSizeLimitsAndBatchSplitting(Closure $test): void $collection = $clientEncrypted->selectCollection('db', 'coll'); - $document = json_decode(file_get_contents(__DIR__ . '/client-side-encryption/limits/limits-doc.json'), true); + $document = json_decode(file_get_contents(self::$specDir . '/limits/limits-doc.json'), true, 512, JSON_THROW_ON_ERROR); $test($this, $collection, $document); } @@ -550,7 +598,7 @@ public function testViewsAreProhibited(): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], ]; @@ -563,7 +611,7 @@ public function testViewsAreProhibited(): void $previous = $e->getPrevious(); $this->assertInstanceOf(EncryptionException::class, $previous); - $this->assertSame('cannot auto encrypt a view', $previous->getMessage()); + $this->assertStringContainsString('cannot auto encrypt a view', $previous->getMessage()); } } @@ -571,26 +619,26 @@ public function testViewsAreProhibited(): void * Prose test 6: BSON Corpus * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#corpus-test - * @testWith [true] - * [false] */ + #[TestWith([true])] + #[TestWith([false])] public function testCorpus($schemaMap = true): void { $client = static::createTestClient(); $client->selectDatabase('db')->dropCollection('coll'); - $schema = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-schema.json')); + $schema = $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-schema.json')); if (! $schemaMap) { $client->selectDatabase('db')->createCollection('coll', ['validator' => ['$jsonSchema' => $schema]]); } self::insertKeyVaultData($client, [ - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-local.json')), - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-aws.json')), - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-azure.json')), - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-gcp.json')), - $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-key-kmip.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-key-local.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-key-aws.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-key-azure.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-key-gcp.json')), + $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-key-kmip.json')), ]); $encryptionOpts = [ @@ -599,7 +647,7 @@ public function testCorpus($schemaMap = true): void 'aws' => Context::getAWSCredentials(), 'azure' => Context::getAzureCredentials(), 'gcp' => Context::getGCPCredentials(), - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], 'kmip' => ['endpoint' => Context::getKmipEndpoint()], ], 'tlsOptions' => [ @@ -615,7 +663,7 @@ public function testCorpus($schemaMap = true): void ]; } - $corpus = (array) $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus.json')); + $corpus = (array) $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus.json')); $corpusCopied = []; $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); @@ -646,7 +694,7 @@ public function testCorpus($schemaMap = true): void $this->assertDocumentsMatch($corpus, $corpusDecrypted); - $corpusEncryptedExpected = (array) $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/corpus/corpus-encrypted.json')); + $corpusEncryptedExpected = (array) $this->decodeJson(file_get_contents(self::$specDir . '/corpus/corpus-encrypted.json')); $corpusEncryptedActual = $client->selectCollection('db', 'coll')->findOne(['_id' => 'client_side_encryption_corpus'], ['typeMap' => ['root' => 'array', 'document' => stdClass::class, 'array' => 'array']]); foreach ($corpusEncryptedExpected as $fieldName => $expectedData) { @@ -668,7 +716,7 @@ public function testCorpus($schemaMap = true): void $this->assertEquals( $clientEncryption->decrypt($expectedData->value), $clientEncryption->decrypt($actualData->value), - 'Decrypted value for field ' . $fieldName . ' does not match.' + 'Decrypted value for field ' . $fieldName . ' does not match.', ); } else { $this->assertEquals($corpus[$fieldName]->value, $actualData->value, 'Value for field ' . $fieldName . ' does not match original value.'); @@ -680,8 +728,8 @@ public function testCorpus($schemaMap = true): void * Prose test 7: Custom Endpoint * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#custom-endpoint-test - * @dataProvider customEndpointProvider */ + #[DataProvider('customEndpointProvider')] public function testCustomEndpoint(Closure $test): void { $client = static::createTestClient(); @@ -704,7 +752,7 @@ public function testCustomEndpoint(Closure $test): void 'kmsProviders' => [ 'azure' => Context::getAzureCredentials() + ['identityPlatformEndpoint' => 'doesnotexist.invalid:443'], 'gcp' => Context::getGCPCredentials() + ['endpoint' => 'doesnotexist.invalid:443'], - 'kmip' => ['endpoint' => 'doesnotexist.local:5698'], + 'kmip' => ['endpoint' => 'doesnotexist.invalid:5698'], ], 'tlsOptions' => [ 'kmip' => Context::getKmsTlsOptions(), @@ -752,9 +800,9 @@ static function (self $test, ClientEncryption $clientEncryption, ClientEncryptio ]; yield 'Test 4' => [ - static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($awsMasterKey): void { + static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($kmipMasterKey): void { $test->expectException(ConnectionException::class); - $clientEncryption->createDataKey('aws', ['masterKey' => $awsMasterKey + ['endpoint' => 'kms.us-east-1.amazonaws.com:12345']]); + $clientEncryption->createDataKey('kmip', ['masterKey' => $kmipMasterKey + ['endpoint' => 'localhost:12345']]); }, ]; @@ -815,7 +863,7 @@ static function (self $test, ClientEncryption $clientEncryption, ClientEncryptio $test->assertSame('test', $clientEncryption->decrypt($encrypted)); $test->expectException(RuntimeException::class); - $test->expectExceptionMessageMatches('#doesnotexist.local#'); + $test->expectExceptionMessageMatches('#doesnotexist.invalid#'); $clientEncryptionInvalid->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); }, ]; @@ -832,15 +880,53 @@ static function (self $test, ClientEncryption $clientEncryption, ClientEncryptio yield 'Test 12' => [ static function (self $test, ClientEncryption $clientEncryption, ClientEncryption $clientEncryptionInvalid) use ($kmipMasterKey): void { - $kmipMasterKey['endpoint'] = 'doesnotexist.local:5698'; + $kmipMasterKey['endpoint'] = 'doesnotexist.invalid:5698'; $test->expectException(RuntimeException::class); - $test->expectExceptionMessageMatches('#doesnotexist.local#'); + $test->expectExceptionMessageMatches('#doesnotexist.invalid#'); $clientEncryption->createDataKey('kmip', ['masterKey' => $kmipMasterKey]); }, ]; } + /** + * Prose test 8: Bypass Spawning mongocryptd (via loading shared library) + * + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/#via-loading-shared-library + */ + public function testBypassSpawningMongocryptdViaLoadingSharedLibrary(): void + { + if (! static::isCryptSharedLibAvailable()) { + $this->markTestSkipped('Bypass spawning of mongocryptd cannot be tested when crypt_shared is not available'); + } + + $autoEncryptionOpts = [ + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => [ + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], + ], + 'schemaMap' => [ + 'db.coll' => $this->decodeJson(file_get_contents(self::$specDir . '/external/external-schema.json')), + ], + 'extraOptions' => [ + 'mongocryptdBypassSpawn' => true, + 'mongocryptdURI' => 'mongodb://localhost:27021/?serverSelectionTimeoutMS=1000', + 'mongocryptdSpawnArgs' => ['--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=27021'], + 'cryptSharedLibRequired' => true, + ], + ]; + + $clientEncrypted = static::createTestClient(null, [], ['autoEncryption' => $autoEncryptionOpts]); + + $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); + + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); + + $this->expectException(ConnectionTimeoutException::class); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); + } + /** * Prose test 8: Bypass Spawning mongocryptd (via mongocryptdBypassSpawn) * @@ -859,14 +945,14 @@ public function testBypassSpawningMongocryptdViaBypassSpawn(): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], 'schemaMap' => [ - 'db.coll' => $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/external/external-schema.json')), + 'db.coll' => $this->decodeJson(file_get_contents(self::$specDir . '/external/external-schema.json')), ], 'extraOptions' => [ 'mongocryptdBypassSpawn' => true, - 'mongocryptdURI' => 'mongodb://localhost:27021/db?serverSelectionTimeoutMS=1000', + 'mongocryptdURI' => 'mongodb://localhost:27021/?serverSelectionTimeoutMS=1000', 'mongocryptdSpawnArgs' => ['--pidfilepath=bypass-spawning-mongocryptd.pid', '--port=27021'], ], ]; @@ -899,7 +985,7 @@ public function testBypassSpawningMongocryptdViaBypassAutoEncryption(): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], 'bypassAutoEncryption' => true, 'extraOptions' => [ @@ -912,16 +998,17 @@ public function testBypassSpawningMongocryptdViaBypassAutoEncryption(): void $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); - $clientMongocryptd = static::createTestClient('mongodb://localhost:27021'); + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); $this->expectException(ConnectionTimeoutException::class); - $clientMongocryptd->selectDatabase('db')->command(['ping' => 1]); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); } /** * Prose test 8: Bypass spawning mongocryptd (via bypassQueryAnalysis) * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#via-bypassqueryanalysis + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#via-bypassqueryanalysis */ public function testBypassSpawningMongocryptdViaBypassQueryAnalysis(): void { @@ -932,7 +1019,7 @@ public function testBypassSpawningMongocryptdViaBypassQueryAnalysis(): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', 'kmsProviders' => [ - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], 'bypassQueryAnalysis' => true, 'extraOptions' => [ @@ -945,16 +1032,17 @@ public function testBypassSpawningMongocryptdViaBypassQueryAnalysis(): void $clientEncrypted->selectCollection('db', 'coll')->insertOne(['unencrypted' => 'test']); - $clientMongocryptd = static::createTestClient('mongodb://localhost:27021'); + $clientMongocryptd = static::createTestClient('mongodb://localhost:27021/?serverSelectionTimeoutMS=1000'); $this->expectException(ConnectionTimeoutException::class); - $clientMongocryptd->selectDatabase('db')->command(['ping' => 1]); + $this->expectExceptionMessageMatches('#(No suitable servers found)|(No servers yet eligible for rescan)#'); + $clientMongocryptd->getManager()->selectServer(); } /** * Prose test 10: KMS TLS Tests (Invalid KMS Certificate) * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#invalid-kms-certificate + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#invalid-kms-certificate */ public function testInvalidKmsCertificate(): void { @@ -982,7 +1070,7 @@ public function testInvalidKmsCertificate(): void /** * Prose test 10: KMS TLS Tests (Invalid Hostname in KMS Certificate) * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#invalid-hostname-in-kms-certificate + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#invalid-hostname-in-kms-certificate */ public function testInvalidHostnameInKmsCertificate(): void { @@ -1010,9 +1098,9 @@ public function testInvalidHostnameInKmsCertificate(): void /** * Prose test 11: KMS TLS Options * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#kms-tls-options-tests - * @dataProvider provideKmsTlsOptionsTests + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#kms-tls-options-tests */ + #[DataProvider('provideKmsTlsOptionsTests')] public function testKmsTlsOptions(Closure $test): void { $client = static::createTestClient(); @@ -1093,7 +1181,7 @@ public static function provideKmsTlsOptionsTests() // Note: expected exception messages below assume OpenSSL is used - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-aws + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-1-aws yield 'AWS: client_encryption_no_client_cert' => [ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($awsMasterKey): void { $test->expectException(ConnectionException::class); @@ -1126,7 +1214,7 @@ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, Cli }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-azure + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-2-azure yield 'Azure: client_encryption_no_client_cert' => [ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($azureMasterKey): void { $test->expectException(ConnectionException::class); @@ -1159,7 +1247,7 @@ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, Cli }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-3-gcp + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-3-gcp yield 'GCP: client_encryption_no_client_cert' => [ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($gcpMasterKey): void { $test->expectException(ConnectionException::class); @@ -1192,7 +1280,7 @@ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, Cli }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-4-kmip + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-4-kmip yield 'KMIP: client_encryption_no_client_cert' => [ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, ClientEncryption $clientEncryptionWithTls, ClientEncryption $clientEncryptionExpired, ClientEncryption $clientEncryptionInvalidHostname) use ($kmipMasterKey): void { $test->expectException(ConnectionException::class); @@ -1228,27 +1316,20 @@ static function (self $test, ClientEncryption $clientEncryptionNoClientCert, Cli /** * Prose test 12: Explicit Encryption * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#explicit-encryption - * @dataProvider provideExplicitEncryptionTests + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#explicit-encryption */ + #[DataProvider('provideExplicitEncryptionTests')] public function testExplicitEncryption(Closure $test): void { - if ($this->isStandalone() || ($this->isShardedCluster() && ! $this->isShardedClusterUsingReplicasets())) { + if ($this->isStandalone()) { $this->markTestSkipped('Explicit encryption tests require replica sets'); } - if (version_compare($this->getServerVersion(), '6.0.0', '<')) { - $this->markTestSkipped('Explicit encryption tests require MongoDB 6.0 or later'); - } - - // Note: this version requirement is consistent with QEv1 spec tests - if (version_compare($this->getServerVersion(), '6.2.99', '>')) { - $this->markTestSkipped('MongoDB 7.0 and later requires Queryable Encryption v2 protocol'); - } + $this->skipIfServerVersion('<', '7.0.0', 'Explicit encryption tests require MongoDB 7.0 or later'); // Test setup - $encryptedFields = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/etc/data/encryptedFields.json')); - $key1Document = $this->decodeJson(file_get_contents(__DIR__ . '/client-side-encryption/etc/data/keys/key1-document.json')); + $encryptedFields = $this->decodeJson(file_get_contents(self::$specDir . '/etc/data/encryptedFields.json')); + $key1Document = $this->decodeJson(file_get_contents(self::$specDir . '/etc/data/keys/key1-document.json')); $key1Id = $key1Document->_id; $client = static::createTestClient(); @@ -1268,12 +1349,12 @@ public function testExplicitEncryption(Closure $test): void $clientEncryption = new ClientEncryption([ 'keyVaultClient' => $keyVaultClient->getManager(), 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], ]); $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], 'bypassQueryAnalysis' => true, ]; @@ -1284,7 +1365,7 @@ public function testExplicitEncryption(Closure $test): void public static function provideExplicitEncryptionTests() { - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-can-insert-encrypted-indexed-and-find + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-1-can-insert-encrypted-indexed-and-find yield 'Case 1: can insert encrypted indexed and find' => [ static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { $value = 'encrypted indexed value'; @@ -1312,7 +1393,7 @@ static function (self $test, ClientEncryption $clientEncryption, Client $encrypt }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-can-insert-encrypted-indexed-and-find-with-non-zero-contention + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-2-can-insert-encrypted-indexed-and-find-with-non-zero-contention yield 'Case 2: can insert encrypted indexed and find with non-zero contention' => [ static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { $value = 'encrypted indexed value'; @@ -1361,7 +1442,7 @@ static function (self $test, ClientEncryption $clientEncryption, Client $encrypt }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-3-can-insert-encrypted-unindexed + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-3-can-insert-encrypted-unindexed yield 'Case 3: can insert encrypted unindexed' => [ static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { $value = 'encrypted unindexed value'; @@ -1381,7 +1462,7 @@ static function (self $test, ClientEncryption $clientEncryption, Client $encrypt }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-4-can-roundtrip-encrypted-indexed + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-4-can-roundtrip-encrypted-indexed yield 'Case 4: can roundtrip encrypted indexed' => [ static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { $value = 'encrypted indexed value'; @@ -1396,7 +1477,7 @@ static function (self $test, ClientEncryption $clientEncryption, Client $encrypt }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-5-can-roundtrip-encrypted-unindexed + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-5-can-roundtrip-encrypted-unindexed yield 'Case 5: can roundtrip encrypted unindexed' => [ static function (self $test, ClientEncryption $clientEncryption, Client $encryptedClient, Client $keyVaultClient, Binary $key1Id): void { $value = 'encrypted unindexed value'; @@ -1414,9 +1495,9 @@ static function (self $test, ClientEncryption $clientEncryption, Client $encrypt /** * Prose test 13: Unique Index on keyAltNames * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#unique-index-on-keyaltnames - * @dataProvider provideUniqueIndexOnKeyAltNamesTests + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#unique-index-on-keyaltnames */ + #[DataProvider('provideUniqueIndexOnKeyAltNamesTests')] public function testUniqueIndexOnKeyAltNames(Closure $test): void { // Test setup @@ -1431,13 +1512,13 @@ public function testUniqueIndexOnKeyAltNames(Closure $test): void 'unique' => true, 'partialFilterExpression' => ['keyAltNames' => ['$exists' => true]], 'writeConcern' => new WriteConcern(WriteConcern::MAJORITY), - ] + ], ); $clientEncryption = new ClientEncryption([ 'keyVaultClient' => $client->getManager(), 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], ]); $clientEncryption->createDataKey('local', ['keyAltNames' => ['def']]); @@ -1447,7 +1528,7 @@ public function testUniqueIndexOnKeyAltNames(Closure $test): void public static function provideUniqueIndexOnKeyAltNamesTests() { - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-1-createdatakey + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-1-createdatakey yield 'Case 1: createDataKey()' => [ static function (self $test, Client $client, ClientEncryption $clientEncryption): void { $clientEncryption->createDataKey('local', ['keyAltNames' => ['abc']]); @@ -1468,16 +1549,16 @@ static function (self $test, Client $client, ClientEncryption $clientEncryption) }, ]; - // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#case-2-addkeyaltname + // See: https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#case-2-addkeyaltname yield 'Case 2: addKeyAltName()' => [ static function (self $test, Client $client, ClientEncryption $clientEncryption): void { $keyId = $clientEncryption->createDataKey('local'); $keyBeforeUpdate = $clientEncryption->addKeyAltName($keyId, 'abc'); - $test->assertObjectNotHasAttribute('keyAltNames', $keyBeforeUpdate); + $test->assertObjectNotHasProperty('keyAltNames', $keyBeforeUpdate); $keyBeforeUpdate = $clientEncryption->addKeyAltName($keyId, 'abc'); - $test->assertObjectHasAttribute('keyAltNames', $keyBeforeUpdate); + $test->assertObjectHasProperty('keyAltNames', $keyBeforeUpdate); $test->assertIsArray($keyBeforeUpdate->keyAltNames); $test->assertContains('abc', $keyBeforeUpdate->keyAltNames); @@ -1491,7 +1572,7 @@ static function (self $test, Client $client, ClientEncryption $clientEncryption) $originalKeyId = $clientEncryption->getKeyByAltName('def')->_id; $originalKeyBeforeUpdate = $clientEncryption->addKeyAltName($originalKeyId, 'def'); - $test->assertObjectHasAttribute('keyAltNames', $originalKeyBeforeUpdate); + $test->assertObjectHasProperty('keyAltNames', $originalKeyBeforeUpdate); $test->assertIsArray($originalKeyBeforeUpdate->keyAltNames); $test->assertContains('def', $originalKeyBeforeUpdate->keyAltNames); }, @@ -1502,8 +1583,8 @@ static function (self $test, Client $client, ClientEncryption $clientEncryption) * Prose test 14: Decryption Events * * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#decryption-events - * @dataProvider provideDecryptionEventsTests */ + #[DataProvider('provideDecryptionEventsTests')] public function testDecryptionEvents(Closure $test): void { // Test setup @@ -1516,7 +1597,7 @@ public function testDecryptionEvents(Closure $test): void $clientEncryption = new ClientEncryption([ 'keyVaultClient' => $setupClient->getManager(), 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], ]); $keyId = $clientEncryption->createDataKey('local'); @@ -1531,7 +1612,7 @@ public function testDecryptionEvents(Closure $test): void $autoEncryptionOpts = [ 'keyVaultNamespace' => 'keyvault.datakeys', - 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)]], + 'kmsProviders' => ['local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))]], ]; $encryptedClient = static::createTestClient(null, ['retryReads' => false], ['autoEncryption' => $autoEncryptionOpts]); @@ -1606,7 +1687,7 @@ static function (self $test, Client $setupClient, ClientEncryption $clientEncryp try { $encryptedClient->selectCollection('db', 'decryption_events')->aggregate([]); $test->fail('Expected exception to be thrown'); - } catch (ConnectionTimeoutException $e) { + } catch (ConnectionTimeoutException) { $test->addToAssertionCount(1); } @@ -1647,12 +1728,55 @@ static function (self $test, Client $setupClient, ClientEncryption $clientEncryp ]; } + /** + * Prose test 15: On-demand AWS Credentials + * + * @see https://github.com/mongodb/specifications/tree/master/source/client-side-encryption/tests#on-demand-aws-credentials + */ + #[TestWith([true])] + #[TestWith([false])] + #[Group('csfle-without-aws-creds')] + public function testOnDemandAwsCredentials(bool $shouldSucceed): void + { + $hasCredentials = (getenv('AWS_ACCESS_KEY_ID') && getenv('AWS_SECRET_ACCESS_KEY')); + + if ($hasCredentials !== $shouldSucceed) { + Assert::markTestSkipped(sprintf('AWS credentials %s available', $hasCredentials ? 'are' : 'are not')); + } + + $keyVaultClient = static::createTestClient(); + + $clientEncryption = new ClientEncryption([ + 'keyVaultClient' => $keyVaultClient->getManager(), + 'keyVaultNamespace' => 'keyvault.datakeys', + 'kmsProviders' => ['aws' => (object) []], + ]); + + $dataKeyOpts = [ + 'masterKey' => [ + 'region' => 'us-east-1', + 'key' => 'arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0', + ], + ]; + + if (! $shouldSucceed) { + $this->expectException(RuntimeException::class); + } + + $dataKeyId = $clientEncryption->createDataKey('aws', $dataKeyOpts); + + if ($shouldSucceed) { + $this->assertInstanceOf(Binary::class, $dataKeyId); + $this->assertSame(Binary::TYPE_UUID, $dataKeyId->getType()); + } + } + /** * Prose test 16: RewrapManyDataKey * - * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.rst#rewrap - * @dataProvider provideRewrapManyDataKeySrcAndDstProviders + * @see https://github.com/mongodb/specifications/blob/master/source/client-side-encryption/tests/README.md#rewrap */ + #[DataProvider('provideRewrapManyDataKeySrcAndDstProviders')] public function testRewrapManyDataKey(string $srcProvider, string $dstProvider): void { $providerMasterKeys = [ @@ -1675,7 +1799,7 @@ public function testRewrapManyDataKey(string $srcProvider, string $dstProvider): 'azure' => Context::getAzureCredentials(), 'gcp' => Context::getGCPCredentials(), 'kmip' => ['endpoint' => Context::getKmipEndpoint()], - 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY), 0)], + 'local' => ['key' => new Binary(base64_decode(self::LOCAL_MASTERKEY))], ], 'tlsOptions' => [ 'kmip' => Context::getKmsTlsOptions(), @@ -1704,10 +1828,10 @@ public function testRewrapManyDataKey(string $srcProvider, string $dstProvider): $result = $clientEncryption2->rewrapManyDataKey([], $rewrapManyDataKeyOpts); - $this->assertObjectHasAttribute('bulkWriteResult', $result); + $this->assertObjectHasProperty('bulkWriteResult', $result); $this->assertIsObject($result->bulkWriteResult); // libmongoc uses different field names for its BulkWriteResult - $this->assertObjectHasAttribute('nModified', $result->bulkWriteResult); + $this->assertObjectHasProperty('nModified', $result->bulkWriteResult); $this->assertSame(1, $result->bulkWriteResult->nModified); $this->assertSame('test', $clientEncryption1->decrypt($ciphertext)); @@ -1725,14 +1849,6 @@ public static function provideRewrapManyDataKeySrcAndDstProviders() } } - private function createInt64(string $value): Int64 - { - $array = sprintf('a:1:{s:7:"integer";s:%d:"%s";}', strlen($value), $value); - $int64 = sprintf('C:%d:"%s":%d:{%s}', strlen(Int64::class), Int64::class, strlen($array), $array); - - return unserialize($int64); - } - private function createTestCollection(?stdClass $encryptedFields = null, ?stdClass $jsonSchema = null): void { $context = $this->getContext(); @@ -1783,7 +1899,7 @@ private function encryptCorpusValue(string $fieldName, stdClass $data, ClientEnc switch ($data->identifier) { case 'id': - $encryptionOptions['keyId'] = new Binary(base64_decode($keyId), 4); + $encryptionOptions['keyId'] = new Binary(base64_decode($keyId), Binary::TYPE_UUID); break; case 'altname': @@ -1799,7 +1915,7 @@ private function encryptCorpusValue(string $fieldName, stdClass $data, ClientEnc /* Note: workaround issue where mongocryptd refuses to encrypt * 32-bit integers if schemaMap defines a "long" BSON type. */ $value = $data->type === 'long' && ! $data->value instanceof Int64 - ? $this->createInt64($data->value) + ? new Int64($data->value) : $data->value; $encrypted = $clientEncryption->encrypt($value, $encryptionOptions); @@ -1815,7 +1931,7 @@ private function encryptCorpusValue(string $fieldName, stdClass $data, ClientEnc try { $clientEncryption->encrypt($data->value, $encryptionOptions); $this->fail('Expected exception to be thrown'); - } catch (RuntimeException $e) { + } catch (RuntimeException) { } return $data->value; @@ -1850,7 +1966,7 @@ private function prepareCorpusData(string $fieldName, stdClass $data, ClientEncr /* Note: workaround issue where mongocryptd refuses to encrypt * 32-bit integers if schemaMap defines a "long" BSON type. */ if ($data->type === 'long' && ! $data->value instanceof Int64) { - $data->value = $this->createInt64($data->value); + $data->value = new Int64($data->value); } return $data; @@ -1861,28 +1977,4 @@ private function prepareCorpusData(string $fieldName, stdClass $data, ClientEncr return $data->allowed ? $returnData : $data; } - - private static function isCryptSharedLibAvailable(): bool - { - $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); - - if ($cryptSharedLibPath === false) { - return false; - } - - return is_readable($cryptSharedLibPath); - } - - private static function isMongocryptdAvailable(): bool - { - $paths = explode(PATH_SEPARATOR, getenv("PATH")); - - foreach ($paths as $path) { - if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { - return true; - } - } - - return false; - } } diff --git a/tests/SpecTests/CommandExpectations.php b/tests/SpecTests/CommandExpectations.php index 86492a3ed..8adc8cd5c 100644 --- a/tests/SpecTests/CommandExpectations.php +++ b/tests/SpecTests/CommandExpectations.php @@ -20,57 +20,35 @@ */ class CommandExpectations implements CommandSubscriber { - /** @var array */ - private $actualEvents = []; + private array $actualEvents = []; - /** @var array */ - private $expectedEvents = []; + private array $expectedEvents = []; - /** @var boolean */ - private $ignoreCommandFailed = false; + private bool $ignoreCommandFailed = false; - /** @var boolean */ - private $ignoreCommandStarted = false; + private bool $ignoreCommandStarted = false; - /** @var boolean */ - private $ignoreCommandSucceeded = false; + private bool $ignoreCommandSucceeded = false; - /** @var boolean */ - private $ignoreExtraEvents = false; + private bool $ignoreExtraEvents = false; - /** @var boolean */ - private $ignoreKeyVaultListCollections = false; + private bool $ignoreKeyVaultListCollections = false; - /** @var string[] */ - private $ignoredCommandNames = []; + /** @var list */ + private array $ignoredCommandNames = []; - /** @var Client */ - private $observedClient; - - private function __construct(Client $observedClient, array $events) + private function __construct(private Client $observedClient, array $events) { - $this->observedClient = $observedClient; - foreach ($events as $event) { - switch (key((array) $event)) { - case 'command_failed_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_failed_event, CommandFailedEvent::class]; - break; - - case 'command_started_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_started_event, CommandStartedEvent::class]; - break; - - case 'command_succeeded_event': - // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $this->expectedEvents[] = [$event->command_succeeded_event, CommandSucceededEvent::class]; - break; - - default: - throw new LogicException('Unsupported event type: ' . key($event)); - } + $this->expectedEvents[] = match (key((array) $event)) { + // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_failed_event' => [$event->command_failed_event, CommandFailedEvent::class], + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_started_event' => [$event->command_started_event, CommandStartedEvent::class], + // phpcs:ignore Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps + 'command_succeeded_event' => [$event->command_succeeded_event, CommandSucceededEvent::class], + default => throw new LogicException('Unsupported event type: ' . key($event)), + }; } } diff --git a/tests/SpecTests/Context.php b/tests/SpecTests/Context.php index 0ead3e961..4d2db90cb 100644 --- a/tests/SpecTests/Context.php +++ b/tests/SpecTests/Context.php @@ -15,6 +15,8 @@ use function array_keys; use function getenv; use function implode; +use function PHPUnit\Framework\assertLessThanOrEqual; +use function sprintf; /** * Execution context for spec tests. @@ -24,52 +26,32 @@ */ final class Context { - /** @var string|null */ - public $bucketName; + public ?string $bucketName = null; - /** @var Client|null */ - private $client; + private ?Client $client = null; - /** @var string|null */ - public $collectionName; + public array $defaultWriteOptions = []; - /** @var string */ - public $databaseName; + public array $outcomeReadOptions = []; - /** @var array */ - public $defaultWriteOptions = []; + public ?string $outcomeCollectionName = null; - /** @var array */ - public $outcomeReadOptions = []; + public ?Session $session0 = null; - /** @var string|null */ - public $outcomeCollectionName; + public object $session0Lsid; - /** @var Session|null */ - public $session0; + public ?Session $session1 = null; - /** @var object */ - public $session0Lsid; + public object $session1Lsid; - /** @var Session|null */ - public $session1; + public bool $useEncryptedClientIfConfigured = false; - /** @var object */ - public $session1Lsid; + private Client $internalClient; - /** @var bool */ - public $useEncryptedClientIfConfigured = false; + private ?Client $encryptedClient = null; - /** @var Client */ - private $internalClient; - - /** @var Client|null */ - private $encryptedClient; - - private function __construct(string $databaseName, ?string $collectionName) + private function __construct(public string $databaseName, public ?string $collectionName = null) { - $this->databaseName = $databaseName; - $this->collectionName = $collectionName; $this->outcomeCollectionName = $collectionName; $this->internalClient = FunctionalTestCase::createTestClient(); } @@ -86,10 +68,26 @@ public static function fromClientSideEncryption(stdClass $test, $databaseName, $ $autoEncryptionOptions = (array) $clientOptions['autoEncryptOpts'] + ['keyVaultNamespace' => 'keyvault.datakeys']; unset($clientOptions['autoEncryptOpts']); + // Ensure test doesn't specify conflicting options for AWS + $countAws = (isset($autoEncryptionOptions['kmsProviders']->aws) ? 1 : 0); + $countAws += (isset($autoEncryptionOptions['kmsProviders']->awsTemporary) ? 1 : 0); + $countAws += (isset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken) ? 1 : 0); + assertLessThanOrEqual(1, $countAws, 'aws, awsTemporary, and awsTemporaryNoSessionToken are mutually exclusive'); + if (isset($autoEncryptionOptions['kmsProviders']->aws)) { $autoEncryptionOptions['kmsProviders']->aws = self::getAWSCredentials(); } + if (isset($autoEncryptionOptions['kmsProviders']->awsTemporary)) { + unset($autoEncryptionOptions['kmsProviders']->awsTemporary); + $autoEncryptionOptions['kmsProviders']->aws = self::getAWSTempCredentials(true); + } + + if (isset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken)) { + unset($autoEncryptionOptions['kmsProviders']->awsTemporaryNoSessionToken); + $autoEncryptionOptions['kmsProviders']->aws = self::getAWSTempCredentials(false); + } + if (isset($autoEncryptionOptions['kmsProviders']->azure)) { $autoEncryptionOptions['kmsProviders']->azure = self::getAzureCredentials(); } @@ -107,11 +105,6 @@ public static function fromClientSideEncryption(stdClass $test, $databaseName, $ $autoEncryptionOptions['tlsOptions']->kmip = self::getKmsTlsOptions(); } - - // Intentionally ignore empty values for CRYPT_SHARED_LIB_PATH - if (getenv('CRYPT_SHARED_LIB_PATH')) { - $autoEncryptionOptions['extraOptions']['cryptSharedLibPath'] = getenv('CRYPT_SHARED_LIB_PATH'); - } } if (isset($test->outcome->collection->name)) { @@ -145,7 +138,7 @@ public static function fromCrud(stdClass $test, $databaseName, $collectionName) $o->outcomeReadOptions = [ 'readConcern' => new ReadConcern('local'), - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; $o->client = self::createTestClient(null, $clientOptions); @@ -181,21 +174,6 @@ public static function fromRetryableReads(stdClass $test, $databaseName, $collec return $o; } - public static function fromRetryableWrites(stdClass $test, $databaseName, $collectionName, $useMultipleMongoses) - { - $o = new self($databaseName, $collectionName); - - $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; - - if (isset($test->outcome->collection->name)) { - $o->outcomeCollectionName = $test->outcome->collection->name; - } - - $o->client = self::createTestClient(FunctionalTestCase::getUri($useMultipleMongoses), $clientOptions); - - return $o; - } - public static function fromTransactions(stdClass $test, $databaseName, $collectionName, $useMultipleMongoses) { $o = new self($databaseName, $collectionName); @@ -206,7 +184,7 @@ public static function fromTransactions(stdClass $test, $databaseName, $collecti $o->outcomeReadOptions = [ 'readConcern' => new ReadConcern('local'), - 'readPreference' => new ReadPreference('primary'), + 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), ]; $clientOptions = isset($test->clientOptions) ? (array) $test->clientOptions : []; @@ -227,59 +205,53 @@ public static function fromTransactions(stdClass $test, $databaseName, $collecti public static function getAWSCredentials(): array { - if (! getenv('AWS_ACCESS_KEY_ID') || ! getenv('AWS_SECRET_ACCESS_KEY')) { - Assert::markTestSkipped('Please configure AWS credentials to use AWS KMS provider.'); - } - return [ - 'accessKeyId' => getenv('AWS_ACCESS_KEY_ID'), - 'secretAccessKey' => getenv('AWS_SECRET_ACCESS_KEY'), + 'accessKeyId' => static::getEnv('AWS_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_SECRET_ACCESS_KEY'), ]; } - public static function getAzureCredentials(): array + public static function getAWSTempCredentials(bool $withSessionToken): array { - if (! getenv('AZURE_TENANT_ID') || ! getenv('AZURE_CLIENT_ID') || ! getenv('AZURE_CLIENT_SECRET')) { - Assert::markTestSkipped('Please configure Azure credentials to use Azure KMS provider.'); + $awsTempCredentials = [ + 'accessKeyId' => static::getEnv('AWS_TEMP_ACCESS_KEY_ID'), + 'secretAccessKey' => static::getEnv('AWS_TEMP_SECRET_ACCESS_KEY'), + ]; + + if ($withSessionToken) { + $awsTempCredentials['sessionToken'] = static::getEnv('AWS_TEMP_SESSION_TOKEN'); } + return $awsTempCredentials; + } + + public static function getAzureCredentials(): array + { return [ - 'tenantId' => getenv('AZURE_TENANT_ID'), - 'clientId' => getenv('AZURE_CLIENT_ID'), - 'clientSecret' => getenv('AZURE_CLIENT_SECRET'), + 'tenantId' => static::getEnv('AZURE_TENANT_ID'), + 'clientId' => static::getEnv('AZURE_CLIENT_ID'), + 'clientSecret' => static::getEnv('AZURE_CLIENT_SECRET'), ]; } public static function getKmipEndpoint(): string { - if (! getenv('KMIP_ENDPOINT')) { - Assert::markTestSkipped('Please configure KMIP endpoint to use KMIP KMS provider.'); - } - - return getenv('KMIP_ENDPOINT'); + return static::getEnv('KMIP_ENDPOINT'); } public static function getKmsTlsOptions(): array { - if (! getenv('KMS_TLS_CA_FILE') || ! getenv('KMS_TLS_CERTIFICATE_KEY_FILE')) { - Assert::markTestSkipped('Please configure KMS TLS options.'); - } - return [ - 'tlsCAFile' => getenv('KMS_TLS_CA_FILE'), - 'tlsCertificateKeyFile' => getenv('KMS_TLS_CERTIFICATE_KEY_FILE'), + 'tlsCAFile' => static::getEnv('KMS_TLS_CA_FILE'), + 'tlsCertificateKeyFile' => static::getEnv('KMS_TLS_CERTIFICATE_KEY_FILE'), ]; } public static function getGCPCredentials(): array { - if (! getenv('GCP_EMAIL') || ! getenv('GCP_PRIVATE_KEY')) { - Assert::markTestSkipped('Please configure GCP credentials to use GCP KMS provider.'); - } - return [ - 'email' => getenv('GCP_EMAIL'), - 'privateKey' => getenv('GCP_PRIVATE_KEY'), + 'email' => static::getEnv('GCP_EMAIL'), + 'privateKey' => static::getEnv('GCP_PRIVATE_KEY'), ]; } @@ -294,7 +266,7 @@ public function getCollection(array $collectionOptions = [], array $databaseOpti $this->databaseName, $this->collectionName, $collectionOptions, - $databaseOptions + $databaseOptions, ); } @@ -381,18 +353,11 @@ public function replaceArgumentSessionPlaceholder(array &$args): void return; } - switch ($args['session']) { - case 'session0': - $args['session'] = $this->session0; - break; - - case 'session1': - $args['session'] = $this->session1; - break; - - default: - throw new LogicException('Unsupported session placeholder: ' . $args['session']); - } + $args['session'] = match ($args['session']) { + 'session0' => $this->session0, + 'session1' => $this->session1, + default => throw new LogicException('Unsupported session placeholder: ' . $args['session']), + }; } /** @@ -409,18 +374,11 @@ public function replaceCommandSessionPlaceholder(stdClass $command): void return; } - switch ($command->lsid) { - case 'session0': - $command->lsid = $this->session0Lsid; - break; - - case 'session1': - $command->lsid = $this->session1Lsid; - break; - - default: - throw new LogicException('Unsupported session placeholder: ' . $command->lsid); - } + $command->lsid = match ($command->lsid) { + 'session0' => $this->session0Lsid, + 'session1' => $this->session1Lsid, + default => throw new LogicException('Unsupported session placeholder: ' . $command->lsid), + }; } public function selectCollection($databaseName, $collectionName, array $collectionOptions = [], array $databaseOptions = []) @@ -434,7 +392,7 @@ public function selectDatabase($databaseName, array $databaseOptions = []) { return $this->getClient()->selectDatabase( $databaseName, - $this->prepareOptions($databaseOptions) + $this->prepareOptions($databaseOptions), ); } @@ -453,6 +411,17 @@ private static function createTestClient(?string $uri = null, array $options = [ return FunctionalTestCase::createTestClient($uri, $options, $driverOptions); } + private static function getEnv(string $name): string + { + $value = getenv($name); + + if ($value === false) { + Assert::markTestSkipped(sprintf('Environment variable "%s" is not defined', $name)); + } + + return $value; + } + private function prepareGridFSBucketOptions(array $options, $bucketPrefix) { if ($bucketPrefix !== null) { diff --git a/tests/SpecTests/Crud/Prose11_BulkWriteBatchSplitsWhenNamespaceExceedsMessageSizeTest.php b/tests/SpecTests/Crud/Prose11_BulkWriteBatchSplitsWhenNamespaceExceedsMessageSizeTest.php new file mode 100644 index 000000000..ae6a46d5d --- /dev/null +++ b/tests/SpecTests/Crud/Prose11_BulkWriteBatchSplitsWhenNamespaceExceedsMessageSizeTest.php @@ -0,0 +1,123 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $this->client = self::createTestClient(); + + $hello = $this->getPrimaryServer()->getInfo(); + self::assertIsInt($maxBsonObjectSize = $hello['maxBsonObjectSize'] ?? null); + self::assertIsInt($maxMessageSizeBytes = $hello['maxMessageSizeBytes'] ?? null); + + $opsBytes = $maxMessageSizeBytes - 1122; + $this->numModels = (int) ($opsBytes / $maxBsonObjectSize); + $remainderBytes = $opsBytes % $maxBsonObjectSize; + + // Use namespaces specific to the test, as they are relevant to batch calculations + $this->dropCollection('db', 'coll'); + $collection = $this->client->selectCollection('db', 'coll'); + + $this->bulkWrite = ClientBulkWrite::createWithCollection($collection); + + for ($i = 0; $i < $this->numModels; ++$i) { + $this->bulkWrite->insertOne(['a' => str_repeat('b', $maxBsonObjectSize - 57)]); + } + + if ($remainderBytes >= 217) { + ++$this->numModels; + $this->bulkWrite->insertOne(['a' => str_repeat('b', $remainderBytes - 57)]); + } + } + + public function testNoBatchSplittingRequired(): void + { + $subscriber = $this->createSubscriber(); + $this->client->addSubscriber($subscriber); + + $this->bulkWrite->insertOne(['a' => 'b']); + + $result = $this->client->bulkWrite($this->bulkWrite); + + self::assertSame($this->numModels + 1, $result->getInsertedCount()); + self::assertCount(1, $subscriber->commandStartedEvents); + $command = $subscriber->commandStartedEvents[0]->getCommand(); + self::assertCount($this->numModels + 1, $command->ops); + self::assertCount(1, $command->nsInfo); + self::assertSame('db.coll', $command->nsInfo[0]->ns ?? null); + } + + public function testBatchSplittingRequired(): void + { + $subscriber = $this->createSubscriber(); + $this->client->addSubscriber($subscriber); + + $secondCollectionName = str_repeat('c', 200); + $this->dropCollection('db', $secondCollectionName); + $secondCollection = $this->client->selectCollection('db', $secondCollectionName); + $this->bulkWrite->withCollection($secondCollection)->insertOne(['a' => 'b']); + + $result = $this->client->bulkWrite($this->bulkWrite); + + self::assertSame($this->numModels + 1, $result->getInsertedCount()); + self::assertCount(2, $subscriber->commandStartedEvents); + [$firstEvent, $secondEvent] = $subscriber->commandStartedEvents; + + $firstCommand = $firstEvent->getCommand(); + self::assertCount($this->numModels, $firstCommand->ops); + self::assertCount(1, $firstCommand->nsInfo); + self::assertSame('db.coll', $firstCommand->nsInfo[0]->ns ?? null); + + $secondCommand = $secondEvent->getCommand(); + self::assertCount(1, $secondCommand->ops); + self::assertCount(1, $secondCommand->nsInfo); + self::assertSame($secondCollection->getNamespace(), $secondCommand->nsInfo[0]->ns ?? null); + } + + private function createSubscriber(): CommandSubscriber + { + return new class implements CommandSubscriber { + public array $commandStartedEvents = []; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + $this->commandStartedEvents[] = $event; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + } +} diff --git a/tests/SpecTests/Crud/Prose12_BulkWriteExceedsMaxMessageSizeBytesTest.php b/tests/SpecTests/Crud/Prose12_BulkWriteExceedsMaxMessageSizeBytesTest.php new file mode 100644 index 000000000..4fbe5adcd --- /dev/null +++ b/tests/SpecTests/Crud/Prose12_BulkWriteExceedsMaxMessageSizeBytesTest.php @@ -0,0 +1,63 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + } + + public function testDocumentTooLarge(): void + { + $client = self::createTestClient(); + + $maxMessageSizeBytes = $this->getPrimaryServer()->getInfo()['maxMessageSizeBytes'] ?? null; + self::assertIsInt($maxMessageSizeBytes); + + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + $bulkWrite->insertOne(['a' => str_repeat('b', $maxMessageSizeBytes)]); + + try { + $client->bulkWrite($bulkWrite); + self::fail('Exception was not thrown'); + } catch (InvalidArgumentException $e) { + self::assertStringContainsString('unable to send document', $e->getMessage()); + } + } + + public function testNamespaceTooLarge(): void + { + $client = self::createTestClient(); + + $maxMessageSizeBytes = $this->getPrimaryServer()->getInfo()['maxMessageSizeBytes'] ?? null; + self::assertIsInt($maxMessageSizeBytes); + + $collectionName = str_repeat('c', $maxMessageSizeBytes); + $collection = $client->selectCollection($this->getDatabaseName(), $collectionName); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + $bulkWrite->insertOne(['a' => 'b']); + + try { + $client->bulkWrite($bulkWrite); + self::fail('Exception was not thrown'); + } catch (InvalidArgumentException $e) { + self::assertStringContainsString('unable to send document', $e->getMessage()); + } + } +} diff --git a/tests/SpecTests/Crud/Prose13_BulkWriteUnsupportedForAutoEncryptionTest.php b/tests/SpecTests/Crud/Prose13_BulkWriteUnsupportedForAutoEncryptionTest.php new file mode 100644 index 000000000..c59078f5f --- /dev/null +++ b/tests/SpecTests/Crud/Prose13_BulkWriteUnsupportedForAutoEncryptionTest.php @@ -0,0 +1,40 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $this->skipIfClientSideEncryptionIsNotSupported(); + + $client = self::createTestClient(null, [], [ + 'autoEncryption' => [ + 'keyVaultNamespace' => $this->getNamespace(), + 'kmsProviders' => ['aws' => ['accessKeyId' => 'foo', 'secretAccessKey' => 'bar']], + ], + ]); + + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + $bulkWrite->insertOne(['a' => 'b']); + + try { + $client->bulkWrite($bulkWrite); + self::fail('InvalidArgumentException was not thrown'); + } catch (InvalidArgumentException $e) { + self::assertStringContainsString('bulkWrite does not currently support automatic encryption', $e->getMessage()); + } + } +} diff --git a/tests/SpecTests/Crud/Prose15_BulkWriteUnacknowledgedWriteConcernTest.php b/tests/SpecTests/Crud/Prose15_BulkWriteUnacknowledgedWriteConcernTest.php new file mode 100644 index 000000000..f7aa6b5bc --- /dev/null +++ b/tests/SpecTests/Crud/Prose15_BulkWriteUnacknowledgedWriteConcernTest.php @@ -0,0 +1,83 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(); + + $hello = $this->getPrimaryServer()->getInfo(); + self::assertIsInt($maxBsonObjectSize = $hello['maxBsonObjectSize'] ?? null); + self::assertIsInt($maxMessageSizeBytes = $hello['maxMessageSizeBytes'] ?? null); + + // Explicitly create the collection to work around SERVER-95537 + $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['ordered' => false]); + + $numModels = (int) ($maxMessageSizeBytes / $maxBsonObjectSize) + 1; + + for ($i = 0; $i < $numModels; ++$i) { + $bulkWrite->insertOne(['a' => str_repeat('b', $maxBsonObjectSize - 500)]); + } + + $subscriber = new class implements CommandSubscriber { + public array $commandStartedEvents = []; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + $this->commandStartedEvents[] = $event; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + $result = $client->bulkWrite($bulkWrite, ['writeConcern' => new WriteConcern(0)]); + + self::assertFalse($result->isAcknowledged()); + self::assertCount(2, $subscriber->commandStartedEvents); + [$firstEvent, $secondEvent] = $subscriber->commandStartedEvents; + + $firstCommand = $firstEvent->getCommand(); + self::assertIsArray($firstCommand->ops ?? null); + self::assertCount($numModels - 1, $firstCommand->ops); + self::assertSame(0, $firstCommand->writeConcern->w ?? null); + + $secondCommand = $secondEvent->getCommand(); + self::assertIsArray($secondCommand->ops ?? null); + self::assertCount(1, $secondCommand->ops); + self::assertSame(0, $secondCommand->writeConcern->w ?? null); + + self::assertSame($numModels, $collection->countDocuments()); + } +} diff --git a/tests/SpecTests/Crud/Prose3_BulkWriteSplitsOnMaxWriteBatchSizeTest.php b/tests/SpecTests/Crud/Prose3_BulkWriteSplitsOnMaxWriteBatchSizeTest.php new file mode 100644 index 000000000..4173bffcf --- /dev/null +++ b/tests/SpecTests/Crud/Prose3_BulkWriteSplitsOnMaxWriteBatchSizeTest.php @@ -0,0 +1,68 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(); + + $maxWriteBatchSize = $this->getPrimaryServer()->getInfo()['maxWriteBatchSize'] ?? null; + self::assertIsInt($maxWriteBatchSize); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + + for ($i = 0; $i < $maxWriteBatchSize + 1; ++$i) { + $bulkWrite->insertOne(['a' => 'b']); + } + + $subscriber = new class implements CommandSubscriber { + public array $commandStartedEvents = []; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + $this->commandStartedEvents[] = $event; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + $result = $client->bulkWrite($bulkWrite); + + self::assertSame($maxWriteBatchSize + 1, $result->getInsertedCount()); + self::assertCount(2, $subscriber->commandStartedEvents); + [$firstEvent, $secondEvent] = $subscriber->commandStartedEvents; + self::assertIsArray($firstCommandOps = $firstEvent->getCommand()->ops ?? null); + self::assertCount($maxWriteBatchSize, $firstCommandOps); + self::assertIsArray($secondCommandOps = $secondEvent->getCommand()->ops ?? null); + self::assertCount(1, $secondCommandOps); + self::assertEquals($firstEvent->getOperationId(), $secondEvent->getOperationId()); + } +} diff --git a/tests/SpecTests/Crud/Prose4_BulkWriteSplitsOnMaxMessageSizeBytesTest.php b/tests/SpecTests/Crud/Prose4_BulkWriteSplitsOnMaxMessageSizeBytesTest.php new file mode 100644 index 000000000..74678cedc --- /dev/null +++ b/tests/SpecTests/Crud/Prose4_BulkWriteSplitsOnMaxMessageSizeBytesTest.php @@ -0,0 +1,74 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(); + + $hello = $this->getPrimaryServer()->getInfo(); + self::assertIsInt($maxBsonObjectSize = $hello['maxBsonObjectSize'] ?? null); + self::assertIsInt($maxMessageSizeBytes = $hello['maxMessageSizeBytes'] ?? null); + + $numModels = (int) ($maxMessageSizeBytes / $maxBsonObjectSize + 1); + $document = ['a' => str_repeat('b', $maxBsonObjectSize - 500)]; + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + + for ($i = 0; $i < $numModels; ++$i) { + $bulkWrite->insertOne($document); + } + + $subscriber = new class implements CommandSubscriber { + public array $commandStartedEvents = []; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + $this->commandStartedEvents[] = $event; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + $result = $client->bulkWrite($bulkWrite); + + self::assertSame($numModels, $result->getInsertedCount()); + self::assertCount(2, $subscriber->commandStartedEvents); + [$firstEvent, $secondEvent] = $subscriber->commandStartedEvents; + self::assertIsArray($firstCommandOps = $firstEvent->getCommand()->ops ?? null); + self::assertCount($numModels - 1, $firstCommandOps); + self::assertIsArray($secondCommandOps = $secondEvent->getCommand()->ops ?? null); + self::assertCount(1, $secondCommandOps); + self::assertEquals($firstEvent->getOperationId(), $secondEvent->getOperationId()); + } +} diff --git a/tests/SpecTests/Crud/Prose5_BulkWriteCollectsWriteConcernErrorsAcrossBatchesTest.php b/tests/SpecTests/Crud/Prose5_BulkWriteCollectsWriteConcernErrorsAcrossBatchesTest.php new file mode 100644 index 000000000..8a0943db7 --- /dev/null +++ b/tests/SpecTests/Crud/Prose5_BulkWriteCollectsWriteConcernErrorsAcrossBatchesTest.php @@ -0,0 +1,81 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(null, ['retryWrites' => false]); + + $maxWriteBatchSize = $this->getPrimaryServer()->getInfo()['maxWriteBatchSize'] ?? null; + self::assertIsInt($maxWriteBatchSize); + + $this->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 2], + 'data' => [ + 'failCommands' => ['bulkWrite'], + 'writeConcernError' => [ + 'code' => 91, // ShutdownInProgress + 'errmsg' => 'Replication is being shut down', + ], + ], + ]); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $bulkWrite = ClientBulkWrite::createWithCollection($collection); + + for ($i = 0; $i < $maxWriteBatchSize + 1; ++$i) { + $bulkWrite->insertOne(['a' => 'b']); + } + + $subscriber = new class implements CommandSubscriber { + public int $numBulkWriteObserved = 0; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + ++$this->numBulkWriteObserved; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + try { + $client->bulkWrite($bulkWrite); + self::fail('BulkWriteCommandException was not thrown'); + } catch (BulkWriteCommandException $e) { + self::assertCount(2, $e->getWriteConcernErrors()); + $partialResult = $e->getPartialResult(); + self::assertNotNull($partialResult); + self::assertSame($maxWriteBatchSize + 1, $partialResult->getInsertedCount()); + self::assertSame(2, $subscriber->numBulkWriteObserved); + } + } +} diff --git a/tests/SpecTests/Crud/Prose6_BulkWriteHandlesWriteErrorsAcrossBatchesTest.php b/tests/SpecTests/Crud/Prose6_BulkWriteHandlesWriteErrorsAcrossBatchesTest.php new file mode 100644 index 000000000..8ea8bcf05 --- /dev/null +++ b/tests/SpecTests/Crud/Prose6_BulkWriteHandlesWriteErrorsAcrossBatchesTest.php @@ -0,0 +1,106 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + } + + public function testOrdered(): void + { + $client = self::createTestClient(); + + $maxWriteBatchSize = $this->getPrimaryServer()->getInfo()['maxWriteBatchSize'] ?? null; + self::assertIsInt($maxWriteBatchSize); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection->insertOne(['_id' => 1]); + + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['ordered' => true]); + + for ($i = 0; $i < $maxWriteBatchSize + 1; ++$i) { + $bulkWrite->insertOne(['_id' => 1]); + } + + $subscriber = $this->createSubscriber(); + $client->addSubscriber($subscriber); + + try { + $client->bulkWrite($bulkWrite); + self::fail('BulkWriteCommandException was not thrown'); + } catch (BulkWriteCommandException $e) { + self::assertCount(1, $e->getWriteErrors()); + self::assertSame(1, $subscriber->numBulkWriteObserved); + } + } + + public function testUnordered(): void + { + $client = self::createTestClient(); + + $maxWriteBatchSize = $this->getPrimaryServer()->getInfo()['maxWriteBatchSize'] ?? null; + self::assertIsInt($maxWriteBatchSize); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection->insertOne(['_id' => 1]); + + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['ordered' => false]); + + for ($i = 0; $i < $maxWriteBatchSize + 1; ++$i) { + $bulkWrite->insertOne(['_id' => 1]); + } + + $subscriber = $this->createSubscriber(); + $client->addSubscriber($subscriber); + + try { + $client->bulkWrite($bulkWrite); + self::fail('BulkWriteCommandException was not thrown'); + } catch (BulkWriteCommandException $e) { + self::assertCount($maxWriteBatchSize + 1, $e->getWriteErrors()); + self::assertSame(2, $subscriber->numBulkWriteObserved); + } + } + + private function createSubscriber(): CommandSubscriber + { + return new class implements CommandSubscriber { + public int $numBulkWriteObserved = 0; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'bulkWrite') { + ++$this->numBulkWriteObserved; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + } +} diff --git a/tests/SpecTests/Crud/Prose7_BulkWriteHandlesCursorRequiringGetMoreTest.php b/tests/SpecTests/Crud/Prose7_BulkWriteHandlesCursorRequiringGetMoreTest.php new file mode 100644 index 000000000..29149babe --- /dev/null +++ b/tests/SpecTests/Crud/Prose7_BulkWriteHandlesCursorRequiringGetMoreTest.php @@ -0,0 +1,72 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(); + + $maxBsonObjectSize = $this->getPrimaryServer()->getInfo()['maxBsonObjectSize'] ?? null; + self::assertIsInt($maxBsonObjectSize); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['verboseResults' => true]); + $bulkWrite->updateOne( + ['_id' => str_repeat('a', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + $bulkWrite->updateOne( + ['_id' => str_repeat('b', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + + $subscriber = new class implements CommandSubscriber { + public int $numGetMoreObserved = 0; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'getMore') { + ++$this->numGetMoreObserved; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + $result = $client->bulkWrite($bulkWrite); + + self::assertSame(2, $result->getUpsertedCount()); + self::assertCount(2, $result->getUpdateResults()); + self::assertSame(1, $subscriber->numGetMoreObserved); + } +} diff --git a/tests/SpecTests/Crud/Prose8_BulkWriteHandlesCursorRequiringGetMoreWithinTransactionTest.php b/tests/SpecTests/Crud/Prose8_BulkWriteHandlesCursorRequiringGetMoreWithinTransactionTest.php new file mode 100644 index 000000000..3c4d64d98 --- /dev/null +++ b/tests/SpecTests/Crud/Prose8_BulkWriteHandlesCursorRequiringGetMoreWithinTransactionTest.php @@ -0,0 +1,78 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + $this->skipIfTransactionsAreNotSupported(); + + $client = self::createTestClient(); + + $maxBsonObjectSize = $this->getPrimaryServer()->getInfo()['maxBsonObjectSize'] ?? null; + self::assertIsInt($maxBsonObjectSize); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['verboseResults' => true]); + $bulkWrite->updateOne( + ['_id' => str_repeat('a', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + $bulkWrite->updateOne( + ['_id' => str_repeat('b', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + + $subscriber = new class implements CommandSubscriber { + public int $numGetMoreObserved = 0; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'getMore') { + ++$this->numGetMoreObserved; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + $session = $client->startSession(); + $session->startTransaction(); + + /* Note: the prose test does not call for committing the transaction. + * The transaction will be aborted when the Session object is freed. */ + $result = $client->bulkWrite($bulkWrite, ['session' => $session]); + + self::assertSame(2, $result->getUpsertedCount()); + self::assertCount(2, $result->getUpdateResults()); + self::assertSame(1, $subscriber->numGetMoreObserved); + } +} diff --git a/tests/SpecTests/Crud/Prose9_BulkWriteHandlesGetMoreErrorTest.php b/tests/SpecTests/Crud/Prose9_BulkWriteHandlesGetMoreErrorTest.php new file mode 100644 index 000000000..903bf486b --- /dev/null +++ b/tests/SpecTests/Crud/Prose9_BulkWriteHandlesGetMoreErrorTest.php @@ -0,0 +1,100 @@ +skipIfServerVersion('<', '8.0', 'bulkWrite command is not supported'); + + $client = self::createTestClient(); + + $maxBsonObjectSize = $this->getPrimaryServer()->getInfo()['maxBsonObjectSize'] ?? null; + self::assertIsInt($maxBsonObjectSize); + + $this->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['getMore'], + 'errorCode' => self::UNKNOWN_ERROR, + ], + ]); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); + + $bulkWrite = ClientBulkWrite::createWithCollection($collection, ['verboseResults' => true]); + $bulkWrite->updateOne( + ['_id' => str_repeat('a', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + $bulkWrite->updateOne( + ['_id' => str_repeat('b', (int) ($maxBsonObjectSize / 2))], + ['$set' => ['x' => 1]], + ['upsert' => true], + ); + + $subscriber = new class implements CommandSubscriber { + public int $numGetMoreObserved = 0; + public int $numKillCursorsObserved = 0; + + public function commandStarted(CommandStartedEvent $event): void + { + if ($event->getCommandName() === 'getMore') { + ++$this->numGetMoreObserved; + } elseif ($event->getCommandName() === 'killCursors') { + ++$this->numKillCursorsObserved; + } + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + try { + $client->bulkWrite($bulkWrite); + self::fail('BulkWriteCommandException was not thrown'); + } catch (BulkWriteCommandException $e) { + $errorReply = $e->getErrorReply(); + $this->assertNotNull($errorReply); + $this->assertSame(self::UNKNOWN_ERROR, $errorReply['code'] ?? null); + + // PHPC will also apply the top-level error code to BulkWriteCommandException + $this->assertSame(self::UNKNOWN_ERROR, $e->getCode()); + + $partialResult = $e->getPartialResult(); + self::assertNotNull($partialResult); + self::assertSame(2, $partialResult->getUpsertedCount()); + self::assertCount(1, $partialResult->getUpdateResults()); + self::assertSame(1, $subscriber->numGetMoreObserved); + self::assertSame(1, $subscriber->numKillCursorsObserved); + } + } +} diff --git a/tests/SpecTests/DocumentsMatchConstraint.php b/tests/SpecTests/DocumentsMatchConstraint.php index 3dde8e705..82f4885d0 100644 --- a/tests/SpecTests/DocumentsMatchConstraint.php +++ b/tests/SpecTests/DocumentsMatchConstraint.php @@ -12,11 +12,10 @@ use RuntimeException; use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Comparator\Factory; +use SebastianBergmann\Exporter\Exporter; use stdClass; -use Symfony\Bridge\PhpUnit\ConstraintTrait; use function array_values; -use function get_class; use function get_debug_type; use function is_array; use function is_float; @@ -30,8 +29,6 @@ use function PHPUnit\Framework\logicalOr; use function sprintf; -use const PHP_INT_SIZE; - /** * Constraint that checks if one document matches another. * @@ -39,50 +36,34 @@ */ class DocumentsMatchConstraint extends Constraint { - use ConstraintTrait; - - /** @var boolean */ - private $ignoreExtraKeysInRoot = false; - - /** @var boolean */ - private $ignoreExtraKeysInEmbedded = false; - /** * TODO: This is not currently used, but was preserved from the design of * TestCase::assertMatchesDocument(), which would sort keys and then compare * documents as JSON strings. If the TODO item in matches() is implemented * to make document comparisons more efficient, we may consider supporting * this option. - * - * @var boolean */ - private $sortKeys = false; + private bool $sortKeys = false; - /** @var BSONArray|BSONDocument */ - private $value; + private BSONArray|BSONDocument $value; - /** @var ComparisonFailure|null */ - private $lastFailure; + private ?ComparisonFailure $lastFailure = null; - /** @var Factory */ - private $comparatorFactory; + private Factory $comparatorFactory; /** * Creates a new constraint. * - * @param array|object $value - * @param boolean $ignoreExtraKeysInRoot If true, ignore extra keys within the root document - * @param boolean $ignoreExtraKeysInEmbedded If true, ignore extra keys within embedded documents + * @param boolean $ignoreExtraKeysInRoot If true, ignore extra keys within the root document + * @param boolean $ignoreExtraKeysInEmbedded If true, ignore extra keys within embedded documents */ - public function __construct($value, bool $ignoreExtraKeysInRoot = false, bool $ignoreExtraKeysInEmbedded = false) + public function __construct(array|object $value, private bool $ignoreExtraKeysInRoot = false, private bool $ignoreExtraKeysInEmbedded = false) { $this->value = $this->prepareBSON($value, true, $this->sortKeys); - $this->ignoreExtraKeysInRoot = $ignoreExtraKeysInRoot; - $this->ignoreExtraKeysInEmbedded = $ignoreExtraKeysInEmbedded; $this->comparatorFactory = Factory::getInstance(); } - private function doEvaluate($other, $description = '', $returnResult = false) + public function evaluate($other, string $description = '', bool $returnResult = false): ?bool { /* TODO: If ignoreExtraKeys and sortKeys are both false, then we may be * able to skip preparation, convert both documents to extended JSON, @@ -100,13 +81,13 @@ private function doEvaluate($other, $description = '', $returnResult = false) $this->assertEquals($this->value, $other, $this->ignoreExtraKeysInRoot); $success = true; } catch (RuntimeException $e) { + $exporter = new Exporter(); $this->lastFailure = new ComparisonFailure( $this->value, $other, - $this->exporter()->export($this->value), - $this->exporter()->export($other), - false, - $e->getMessage() + $exporter->export($this->value), + $exporter->export($other), + $e->getMessage(), ); } @@ -117,18 +98,17 @@ private function doEvaluate($other, $description = '', $returnResult = false) if (! $success) { $this->fail($other, $description, $this->lastFailure); } + + return null; } - /** - * @param string|string[] $expectedType - * @param mixed $actualValue - */ - private function assertBSONType($expectedType, $actualValue): void + /** @param string|BSONArray[] $expectedType */ + private function assertBSONType(string|BSONArray $expectedType, mixed $actualValue): void { assertThat( $expectedType, logicalOr(isType('string'), logicalAnd(isInstanceOf(BSONArray::class), containsOnly('string'))), - '$$type requires string or string[]' + '$$type requires string or string[]', ); IsBsonType::anyOf(...(array) $expectedType)->evaluate($actualValue); @@ -141,11 +121,11 @@ private function assertBSONType($expectedType, $actualValue): void */ private function assertEquals(ArrayObject $expected, ArrayObject $actual, bool $ignoreExtraKeys, string $keyPrefix = ''): void { - if (get_class($expected) !== get_class($actual)) { + if ($expected::class !== $actual::class) { throw new RuntimeException(sprintf( '%s is not instance of expected class "%s"', - $this->exporter()->shortenedExport($actual), - get_class($expected) + (new Exporter())->shortenedExport($actual), + $expected::class, )); } @@ -183,13 +163,12 @@ private function assertEquals(ArrayObject $expected, ArrayObject $actual, bool $ $actualValue, '', '', - false, sprintf( 'Field path "%s": %s is not instance of expected type "%s".', $keyPrefix . $key, - $this->exporter()->shortenedExport($actualValue), - $expectedType - ) + (new Exporter())->shortenedExport($actualValue), + $expectedType, + ), ); } @@ -201,8 +180,7 @@ private function assertEquals(ArrayObject $expected, ArrayObject $actual, bool $ $actualValue, '', '', - false, - sprintf('Field path "%s": %s', $keyPrefix . $key, $failure->getMessage()) + sprintf('Field path "%s": %s', $keyPrefix . $key, $failure->getMessage()), ); } } @@ -218,7 +196,7 @@ private function assertEquals(ArrayObject $expected, ArrayObject $actual, bool $ } } - private function doAdditionalFailureDescription($other) + protected function additionalFailureDescription($other): string { if ($this->lastFailure === null) { return ''; @@ -227,12 +205,12 @@ private function doAdditionalFailureDescription($other) return $this->lastFailure->getMessage(); } - private function doFailureDescription($other) + protected function failureDescription($other): string { return 'two BSON objects are equal'; } - private function doMatches($other) + protected function matches($other): bool { /* TODO: If ignoreExtraKeys and sortKeys are both false, then we may be * able to skip preparation, convert both documents to extended JSON, @@ -245,16 +223,16 @@ private function doMatches($other) try { $this->assertEquals($this->value, $other, $this->ignoreExtraKeysInRoot); - } catch (RuntimeException $e) { + } catch (RuntimeException) { return false; } return true; } - private function doToString() + public function toString(): string { - return 'matches ' . $this->exporter()->export($this->value); + return 'matches ' . (new Exporter())->export($this->value); } private static function isNumeric($value): bool @@ -269,12 +247,10 @@ private static function isNumeric($value): bool * its type and keys. Keys within documents will optionally be sorted. Each * value within the array or document will then be prepared recursively. * - * @param array|object $bson - * @param boolean $isRoot If true, ensure an array value is converted to a document - * @return BSONDocument|BSONArray + * @param boolean $isRoot If true, ensure an array value is converted to a document * @throws InvalidArgumentException if $bson is not an array or object */ - private function prepareBSON($bson, bool $isRoot, bool $sortKeys = false) + private function prepareBSON(array|object $bson, bool $isRoot, bool $sortKeys = false): BSONDocument|BSONArray { if (! is_array($bson) && ! is_object($bson)) { throw new InvalidArgumentException('$bson is not an array or object'); @@ -308,12 +284,6 @@ private function prepareBSON($bson, bool $isRoot, bool $sortKeys = false) $bson[$key] = $this->prepareBSON($value, false, $sortKeys); continue; } - - /* Convert Int64 objects to integers on 64-bit platforms for - * compatibility reasons. */ - if ($value instanceof Int64 && PHP_INT_SIZE != 4) { - $bson[$key] = (int) ((string) $value); - } } return $bson; diff --git a/tests/SpecTests/DocumentsMatchConstraintTest.php b/tests/SpecTests/DocumentsMatchConstraintTest.php index 4e7e3203a..f410591bf 100644 --- a/tests/SpecTests/DocumentsMatchConstraintTest.php +++ b/tests/SpecTests/DocumentsMatchConstraintTest.php @@ -4,6 +4,8 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\Decimal128; +use MongoDB\BSON\Document; +use MongoDB\BSON\Int64; use MongoDB\BSON\Javascript; use MongoDB\BSON\MaxKey; use MongoDB\BSON\MinKey; @@ -14,12 +16,9 @@ use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\ExpectationFailedException; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; -use function unserialize; - use const PHP_INT_SIZE; class DocumentsMatchConstraintTest extends TestCase @@ -72,9 +71,7 @@ public function testIgnoreExtraKeysInEmbedded(): void $this->assertResult(false, $c, [1, ['a' => 2]], 'Keys must have the correct value'); } - /** - * @dataProvider provideBSONTypes - */ + #[DataProvider('provideBSONTypes')] public function testBSONTypeAssertions($type, $value): void { $constraint = new DocumentsMatchConstraint(['x' => ['$$type' => $type]]); @@ -82,20 +79,20 @@ public function testBSONTypeAssertions($type, $value): void $this->assertResult(true, $constraint, ['x' => $value], 'Type matches'); } - public function provideBSONTypes() + public static function provideBSONTypes() { - $undefined = toPHP(fromJSON('{ "x": {"$undefined": true} }'))->x; - $symbol = toPHP(fromJSON('{ "x": {"$symbol": "test"} }'))->x; - $dbPointer = toPHP(fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }'))->x; - $int64 = unserialize('C:18:"MongoDB\BSON\Int64":28:{a:1:{s:7:"integer";s:1:"1";}}'); - $long = PHP_INT_SIZE == 4 ? unserialize('C:18:"MongoDB\BSON\Int64":38:{a:1:{s:7:"integer";s:10:"4294967296";}}') : 4294967296; + $undefined = Document::fromJSON('{ "x": {"$undefined": true} }')->toPHP()->x; + $symbol = Document::fromJSON('{ "x": {"$symbol": "test"} }')->toPHP()->x; + $dbPointer = Document::fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }')->toPHP()->x; + $int64 = new Int64(1); + $long = PHP_INT_SIZE == 4 ? new Int64('4294967296') : 4_294_967_296; return [ 'double' => ['double', 1.4], 'string' => ['string', 'foo'], 'object' => ['object', new BSONDocument()], 'array' => ['array', ['foo']], - 'binData' => ['binData', new Binary('', 0)], + 'binData' => ['binData', new Binary('')], 'undefined' => ['undefined', $undefined], 'objectId' => ['objectId', new ObjectId()], 'bool' => ['bool', true], @@ -136,9 +133,7 @@ public function testBSONTypeAssertionsWithMultipleTypes(): void $this->assertResult(false, $c2, ['x' => true], 'bool is not number or string'); } - /** - * @dataProvider errorMessageProvider - */ + #[DataProvider('errorMessageProvider')] public function testErrorMessages($expectedMessagePart, DocumentsMatchConstraint $constraint, $actualValue): void { try { @@ -150,7 +145,7 @@ public function testErrorMessages($expectedMessagePart, DocumentsMatchConstraint } } - public function errorMessageProvider() + public static function errorMessageProvider() { return [ 'Root type mismatch' => [ diff --git a/tests/SpecTests/ErrorExpectation.php b/tests/SpecTests/ErrorExpectation.php index 55ee97cc1..cc10dcba9 100644 --- a/tests/SpecTests/ErrorExpectation.php +++ b/tests/SpecTests/ErrorExpectation.php @@ -12,7 +12,6 @@ use stdClass; use Throwable; -use function get_class; use function is_array; use function is_string; use function sprintf; @@ -24,10 +23,9 @@ final class ErrorExpectation { /** * @see https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.err - * - * @var array + * @var array */ - private static $codeNameMap = [ + private static array $codeNameMap = [ 'Interrupted' => 11601, 'MaxTimeMSExpired' => 50, 'NoSuchTransaction' => 251, @@ -35,23 +33,19 @@ final class ErrorExpectation 'WriteConflict' => 112, ]; - /** @var integer */ - private $code; + private int $code; - /** @var string */ - private $codeName; + private string $codeName; - /** @var boolean */ - private $isExpected = false; + private bool $isExpected = false; - /** @var string[] */ - private $excludedLabels = []; + /** @var list */ + private array $excludedLabels = []; - /** @var string[] */ - private $includedLabels = []; + /** @var list */ + private array $includedLabels = []; - /** @var string */ - private $messageContains; + private string $messageContains; private function __construct() { @@ -89,38 +83,7 @@ public static function fromRetryableReads(stdClass $operation) return $o; } - public static function fromRetryableWrites(stdClass $outcome) - { - $o = new self(); - - if (isset($outcome->error)) { - $o->isExpected = $outcome->error; - } - - /* outcome.result will only contain error label assertions if an error - * is expected (i.e. outcome.error is true). */ - if ($o->isExpected && isset($outcome->result->errorLabelsContain)) { - if (! self::isArrayOfStrings($outcome->result->errorLabelsContain)) { - throw InvalidArgumentException::invalidType('errorLabelsContain', $outcome->result->errorLabelsContain, 'string[]'); - } - - $o->includedLabels = $outcome->result->errorLabelsContain; - } - - if ($o->isExpected && isset($outcome->result->errorLabelsOmit)) { - if (! self::isArrayOfStrings($outcome->result->errorLabelsOmit)) { - throw InvalidArgumentException::invalidType('errorLabelsOmit', $outcome->result->errorLabelsOmit, 'string[]'); - } - - $o->excludedLabels = $outcome->result->errorLabelsOmit; - } - - return $o; - } - - /** - * @throws InvalidArgumentException - */ + /** @throws InvalidArgumentException */ public static function fromTransactions(stdClass $operation) { return self::fromGenericOperation($operation); @@ -141,7 +104,7 @@ public function assert(TestCase $test, ?Throwable $actual = null): void { if (! $this->isExpected) { if ($actual !== null) { - $test->fail(sprintf("Operation threw unexpected %s: %s\n%s", get_class($actual), $actual->getMessage(), $actual->getTraceAsString())); + $test->fail(sprintf("Operation threw unexpected %s: %s\n%s", $actual::class, $actual->getMessage(), $actual->getTraceAsString())); } $test->addToAssertionCount(1); @@ -200,19 +163,17 @@ private function assertCodeName(TestCase $test, ?Throwable $actual = null): void $result = $actual->getResultDocument(); if (isset($result->writeConcernError)) { - $test->assertObjectHasAttribute('codeName', $result->writeConcernError); + $test->assertObjectHasProperty('codeName', $result->writeConcernError); $test->assertSame($this->codeName, $result->writeConcernError->codeName); return; } - $test->assertObjectHasAttribute('codeName', $result); + $test->assertObjectHasProperty('codeName', $result); $test->assertSame($this->codeName, $result->codeName); } - /** - * @throws InvalidArgumentException - */ + /** @throws InvalidArgumentException */ private static function fromGenericOperation(stdClass $operation) { $o = new self(); diff --git a/tests/SpecTests/FunctionalTestCase.php b/tests/SpecTests/FunctionalTestCase.php index 3b266fb04..6ee6d0e46 100644 --- a/tests/SpecTests/FunctionalTestCase.php +++ b/tests/SpecTests/FunctionalTestCase.php @@ -4,6 +4,7 @@ use ArrayIterator; use LogicException; +use MongoDB\BSON\Document; use MongoDB\Collection; use MongoDB\Driver\Server; use MongoDB\Tests\FunctionalTestCase as BaseFunctionalTestCase; @@ -14,11 +15,11 @@ use function in_array; use function json_encode; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; use function sprintf; use function version_compare; +use const JSON_THROW_ON_ERROR; + /** * Base class for spec test runners. * @@ -35,8 +36,7 @@ class FunctionalTestCase extends BaseFunctionalTestCase public const SERVERLESS_FORBID = 'forbid'; public const SERVERLESS_REQUIRE = 'require'; - /** @var Context|null */ - private $context; + private ?Context $context = null; public function setUp(): void { @@ -84,17 +84,32 @@ public static function assertCommandReplyMatches(stdClass $expected, stdClass $a * Asserts that two given documents match. * * Extra keys in the actual value's document(s) will be ignored. - * - * @param array|object $expectedDocument - * @param array|object $actualDocument */ - public static function assertDocumentsMatch($expectedDocument, $actualDocument, string $message = ''): void + public static function assertDocumentsMatch(array|object $expectedDocument, array|object $actualDocument, string $message = ''): void { $constraint = new DocumentsMatchConstraint($expectedDocument, true, true); static::assertThat($actualDocument, $constraint, $message); } + /** + * Assert omitted top-level fields in command documents. + * + * Note: this method may modify the $expected object. + * + * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.md#null-values + * @see https://github.com/mongodb/specifications/blob/09ee1ebc481f1502e3246971a9419e484d736207/source/command-monitoring/tests/README.rst#additional-values + */ + protected static function assertCommandOmittedFields(stdClass $expected, stdClass $actual): void + { + foreach ($expected as $key => $value) { + if ($value === null) { + static::assertObjectNotHasProperty($key, $actual); + unset($expected->{$key}); + } + } + } + /** * Assert data within the outcome collection. */ @@ -111,18 +126,11 @@ protected function assertOutcomeCollectionData(array $expectedDocuments, int $re $this->assertNotNull($expectedDocument); $this->assertNotNull($actualDocument); - switch ($resultExpectation) { - case ResultExpectation::ASSERT_SAME_DOCUMENT: - $this->assertSameDocument($expectedDocument, $actualDocument); - break; - - case ResultExpectation::ASSERT_DOCUMENTS_MATCH: - $this->assertDocumentsMatch($expectedDocument, $actualDocument); - break; - - default: - $this->fail(sprintf('Invalid result expectation "%d" for %s', $resultExpectation, __METHOD__)); - } + match ($resultExpectation) { + ResultExpectation::ASSERT_SAME_DOCUMENT => $this->assertSameDocument($expectedDocument, $actualDocument), + ResultExpectation::ASSERT_DOCUMENTS_MATCH => $this->assertDocumentsMatch($expectedDocument, $actualDocument), + default => $this->fail(sprintf('Invalid result expectation "%d" for %s', $resultExpectation, __METHOD__)), + }; } } @@ -147,7 +155,7 @@ protected function checkServerRequirements(array $runOn): void $serverVersion = $this->getServerVersion(); $topology = $this->getTopology(); - $this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements: %s', $serverVersion, $topology, json_encode($runOn))); + $this->markTestSkipped(sprintf('Server version "%s" and topology "%s" do not meet test requirements: %s', $serverVersion, $topology, json_encode($runOn, JSON_THROW_ON_ERROR))); } /** @@ -155,12 +163,10 @@ protected function checkServerRequirements(array $runOn): void * * This decodes the file through the driver's extended JSON parser to ensure * proper handling of special types. - * - * @return array|object */ - protected function decodeJson(string $json) + protected function decodeJson(string $json): array|object { - return toPHP(fromJSON($json)); + return Document::fromJSON($json)->toPHP(); } /** @@ -271,18 +277,7 @@ private function isServerlessRequirementSatisfied(?string $serverlessMode): bool return true; } - switch ($serverlessMode) { - case self::SERVERLESS_ALLOW: - return true; - - case self::SERVERLESS_FORBID: - return ! static::isServerless(); - - case self::SERVERLESS_REQUIRE: - return static::isServerless(); - } - - throw new UnexpectedValueException(sprintf('Invalid serverless requirement "%s" found.', $serverlessMode)); + return $serverlessMode !== self::SERVERLESS_REQUIRE; } /** diff --git a/tests/SpecTests/Operation.php b/tests/SpecTests/Operation.php index 42d9da990..d605a52aa 100644 --- a/tests/SpecTests/Operation.php +++ b/tests/SpecTests/Operation.php @@ -12,10 +12,9 @@ use MongoDB\Driver\Server; use MongoDB\Driver\Session; use MongoDB\GridFS\Bucket; -use MongoDB\MapReduceResult; -use MongoDB\Model\IndexInfo; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; +use PHPUnit\Framework\Assert; use stdClass; use function array_diff_key; @@ -43,32 +42,23 @@ final class Operation public const OBJECT_SESSION1 = 'session1'; public const OBJECT_TEST_RUNNER = 'testRunner'; - /** @var ErrorExpectation|null */ - public $errorExpectation; + public ?ErrorExpectation $errorExpectation = null; - /** @var ResultExpectation|null */ - public $resultExpectation; + public ?ResultExpectation $resultExpectation = null; - /** @var array */ - private $arguments = []; + private array $arguments = []; - /** @var string|null */ - private $collectionName; + private ?string $collectionName = null; - /** @var array */ - private $collectionOptions = []; + private array $collectionOptions = []; - /** @var string|null */ - private $databaseName; + private ?string $databaseName = null; - /** @var array */ - private $databaseOptions = []; + private array $databaseOptions = []; - /** @var string */ - private $name; + private string $name; - /** @var string */ - private $object = self::OBJECT_COLLECTION; + private string $object = self::OBJECT_COLLECTION; private function __construct(stdClass $operation) { @@ -160,16 +150,6 @@ public static function fromRetryableReads(stdClass $operation) return $o; } - public static function fromRetryableWrites(stdClass $operation, stdClass $outcome) - { - $o = new self($operation); - - $o->errorExpectation = ErrorExpectation::fromRetryableWrites($outcome); - $o->resultExpectation = ResultExpectation::fromRetryableWrites($outcome, $o->getResultAssertionType()); - - return $o; - } - public static function fromTransactions(stdClass $operation) { $o = new self($operation); @@ -203,10 +183,6 @@ public function assert(FunctionalTestCase $test, Context $context, bool $bubbleE * is not used (e.g. Command Monitoring spec). */ if ($result instanceof Cursor) { $result = $result->toArray(); - } elseif ($result instanceof MapReduceResult) { - /* For mapReduce operations, we ignore the mapReduce metadata - * and only return the result iterator for evaluation. */ - $result = iterator_to_array($result->getIterator()); } } catch (Exception $e) { $exception = $e; @@ -233,10 +209,9 @@ public function assert(FunctionalTestCase $test, Context $context, bool $bubbleE /** * Executes the operation with a given context. * - * @return mixed * @throws LogicException if the operation is unsupported */ - private function execute(FunctionalTestCase $test, Context $context) + private function execute(FunctionalTestCase $test, Context $context): mixed { switch ($this->object) { case self::OBJECT_CLIENT: @@ -286,10 +261,9 @@ private function execute(FunctionalTestCase $test, Context $context) /** * Executes the client operation and return its result. * - * @return mixed * @throws LogicException if the collection operation is unsupported */ - private function executeForClient(Client $client, Context $context) + private function executeForClient(Client $client, Context $context): mixed { $args = $context->prepareOptions($this->arguments); $context->replaceArgumentSessionPlaceholder($args); @@ -304,7 +278,7 @@ private function executeForClient(Client $client, Context $context) case 'watch': return $client->watch( $args['pipeline'] ?? [], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); default: @@ -315,10 +289,9 @@ private function executeForClient(Client $client, Context $context) /** * Executes the collection operation and return its result. * - * @return mixed * @throws LogicException if the collection operation is unsupported */ - private function executeForCollection(Collection $collection, Context $context) + private function executeForCollection(Collection $collection, Context $context): mixed { $args = $context->prepareOptions($this->arguments); $context->replaceArgumentSessionPlaceholder($args); @@ -327,7 +300,7 @@ private function executeForCollection(Collection $collection, Context $context) case 'aggregate': return $collection->aggregate( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); case 'bulkWrite': @@ -338,19 +311,19 @@ private function executeForCollection(Collection $collection, Context $context) return $collection->bulkWrite( // TODO: Check if self can be used with a private static function array_map([$this, 'prepareBulkWriteRequest'], $args['requests']), - $options + $options, ); case 'createIndex': return $collection->createIndex( $args['keys'], - array_diff_key($args, ['keys' => 1]) + array_diff_key($args, ['keys' => 1]), ); case 'dropIndex': return $collection->dropIndex( $args['name'], - array_diff_key($args, ['name' => 1]) + array_diff_key($args, ['name' => 1]), ); case 'count': @@ -376,7 +349,7 @@ private function executeForCollection(Collection $collection, Context $context) return $collection->distinct( $args['fieldName'], $args['filter'] ?? [], - array_diff_key($args, ['fieldName' => 1, 'filter' => 1]) + array_diff_key($args, ['fieldName' => 1, 'filter' => 1]), ); case 'drop': @@ -421,30 +394,26 @@ private function executeForCollection(Collection $collection, Context $context) return $collection->insertMany( $args['documents'], - $options + $options, ); case 'insertOne': return $collection->insertOne( $args['document'], - array_diff_key($args, ['document' => 1]) + array_diff_key($args, ['document' => 1]), ); case 'listIndexes': return $collection->listIndexes($args); case 'mapReduce': - return $collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]) - ); + Assert::markTestSkipped('mapReduce is not supported'); + break; case 'watch': return $collection->watch( $args['pipeline'] ?? [], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); default: @@ -455,10 +424,9 @@ private function executeForCollection(Collection $collection, Context $context) /** * Executes the database operation and return its result. * - * @return mixed * @throws LogicException if the database operation is unsupported */ - private function executeForDatabase(Database $database, Context $context) + private function executeForDatabase(Database $database, Context $context): mixed { $args = $context->prepareOptions($this->arguments); $context->replaceArgumentSessionPlaceholder($args); @@ -467,19 +435,19 @@ private function executeForDatabase(Database $database, Context $context) case 'aggregate': return $database->aggregate( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); case 'createCollection': return $database->createCollection( $args['collection'], - array_diff_key($args, ['collection' => 1]) + array_diff_key($args, ['collection' => 1]), ); case 'dropCollection': return $database->dropCollection( $args['collection'], - array_diff_key($args, ['collection' => 1]) + array_diff_key($args, ['collection' => 1]), ); case 'listCollectionNames': @@ -491,13 +459,13 @@ private function executeForDatabase(Database $database, Context $context) case 'runCommand': return $database->command( $args['command'], - array_diff_key($args, ['command' => 1]) + array_diff_key($args, ['command' => 1]), )->toArray()[0]; case 'watch': return $database->watch( $args['pipeline'] ?? [], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); default: @@ -508,10 +476,9 @@ private function executeForDatabase(Database $database, Context $context) /** * Executes the GridFS bucket operation and return its result. * - * @return mixed * @throws LogicException if the database operation is unsupported */ - private function executeForGridFSBucket(Bucket $bucket, Context $context) + private function executeForGridFSBucket(Bucket $bucket, Context $context): mixed { $args = $context->prepareOptions($this->arguments); $context->replaceArgumentSessionPlaceholder($args); @@ -549,10 +516,9 @@ private function executeForGridFSBucket(Bucket $bucket, Context $context) /** * Executes the session operation and return its result. * - * @return mixed * @throws LogicException if the session operation is unsupported */ - private function executeForSession(Session $session, FunctionalTestCase $test, Context $context) + private function executeForSession(Session $session, FunctionalTestCase $test, Context $context): mixed { switch ($this->name) { case 'abortTransaction': @@ -567,10 +533,11 @@ private function executeForSession(Session $session, FunctionalTestCase $test, C return $session->startTransaction($context->prepareOptions($options)); case 'withTransaction': - /** @var self[] $callbackOperations */ - $callbackOperations = array_map(function ($operation) { - return self::fromConvenientTransactions($operation); - }, $this->arguments['callback']->operations); + /** @var list $callbackOperations */ + $callbackOperations = array_map( + fn ($operation) => self::fromConvenientTransactions($operation), + $this->arguments['callback']->operations, + ); $callback = function () use ($callbackOperations, $test, $context): void { foreach ($callbackOperations as $operation) { @@ -647,6 +614,7 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context case 'targetedFailPoint': $test->assertInstanceOf(Session::class, $args['session']); + $test->assertInstanceOf(Server::class, $args['session']->getServer()); $test->configureFailPoint($this->arguments['failPoint'], $args['session']->getServer()); return null; @@ -659,16 +627,17 @@ private function executeForTestRunner(FunctionalTestCase $test, Context $context private function getIndexNames(Context $context, string $databaseName, string $collectionName): array { return array_map( - function (IndexInfo $indexInfo) { - return $indexInfo->getName(); - }, - iterator_to_array($context->getInternalClient()->selectCollection($databaseName, $collectionName)->listIndexes()) + 'strval', + iterator_to_array( + $context + ->getInternalClient() + ->selectCollection($databaseName, $collectionName) + ->listIndexes(), + ), ); } - /** - * @throws LogicException if the operation object is unsupported - */ + /** @throws LogicException if the operation object is unsupported */ private function getResultAssertionType() { switch ($this->object) { @@ -694,9 +663,7 @@ private function getResultAssertionType() } } - /** - * @throws LogicException if the collection operation is unsupported - */ + /** @throws LogicException if the collection operation is unsupported */ private function getResultAssertionTypeForClient() { switch ($this->name) { @@ -714,9 +681,7 @@ private function getResultAssertionTypeForClient() } } - /** - * @throws LogicException if the collection operation is unsupported - */ + /** @throws LogicException if the collection operation is unsupported */ private function getResultAssertionTypeForCollection() { switch ($this->name) { @@ -729,7 +694,7 @@ private function getResultAssertionTypeForCollection() return ResultExpectation::ASSERT_NOTHING; } - return ResultExpectation::ASSERT_SAME_DOCUMENTS; + return ResultExpectation::ASSERT_DOCUMENTS_MATCH; case 'bulkWrite': return ResultExpectation::ASSERT_BULKWRITE; @@ -787,9 +752,7 @@ private function getResultAssertionTypeForCollection() } } - /** - * @throws LogicException if the database operation is unsupported - */ + /** @throws LogicException if the database operation is unsupported */ private function getResultAssertionTypeForDatabase() { switch ($this->name) { diff --git a/tests/SpecTests/PrimaryStepDownSpecTest.php b/tests/SpecTests/PrimaryStepDownSpecTest.php index 48aa55cc0..bc060c061 100644 --- a/tests/SpecTests/PrimaryStepDownSpecTest.php +++ b/tests/SpecTests/PrimaryStepDownSpecTest.php @@ -17,20 +17,16 @@ use function current; use function sprintf; -/** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests - */ +/** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests */ class PrimaryStepDownSpecTest extends FunctionalTestCase { public const INTERRUPTED_AT_SHUTDOWN = 11600; public const NOT_PRIMARY = 10107; public const SHUTDOWN_IN_PROGRESS = 91; - /** @var Client */ - private $client; + private Client $client; - /** @var Collection */ - private $collection; + private Collection $collection; public function setUp(): void { @@ -42,9 +38,7 @@ public function setUp(): void $this->collection = $this->client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-keep-connection-pool - */ + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-keep-connection-pool */ public function testNotPrimaryKeepsConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.1.11', 'topology' => [self::TOPOLOGY_REPLICASET]]]; @@ -78,9 +72,7 @@ public function testNotPrimaryKeepsConnectionPool(): void $this->assertSame($totalConnectionsCreated, $this->getTotalConnectionsCreated()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-reset-connection-pool - */ + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#not-primary-reset-connection-pool */ public function testNotPrimaryResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0', 'maxServerVersion' => '4.0.999', 'topology' => [self::TOPOLOGY_REPLICASET]]]; @@ -117,9 +109,7 @@ public function testNotPrimaryResetConnectionPool(): void $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#shutdown-in-progress-reset-connection-pool - */ + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#shutdown-in-progress-reset-connection-pool */ public function testShutdownResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0']]; @@ -156,9 +146,7 @@ public function testShutdownResetConnectionPool(): void $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#interrupted-at-shutdown-reset-connection-pool - */ + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#interrupted-at-shutdown-reset-connection-pool */ public function testInterruptedAtShutdownResetConnectionPool(): void { $runOn = [(object) ['minServerVersion' => '4.0.0']]; @@ -195,9 +183,7 @@ public function testInterruptedAtShutdownResetConnectionPool(): void $this->assertSame(1, $result->getInsertedCount()); } - /** - * @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#getmore-iteration - */ + /** @see https://github.com/mongodb/specifications/tree/master/source/connections-survive-step-down/tests#getmore-iteration */ public function testGetMoreIteration(): void { $this->markTestSkipped('Test causes subsequent failures in other tests (see PHPLIB-471)'); @@ -220,7 +206,7 @@ public function testGetMoreIteration(): void $totalConnectionsCreated = $this->getTotalConnectionsCreated(); // Send a {replSetStepDown: 5, force: true} command to the current primary and verify that the command succeeded - $primary = $this->client->getManager()->selectServer(new ReadPreference(ReadPreference::RP_PRIMARY)); + $primary = $this->client->getManager()->selectServer(); $success = false; $attempts = 0; @@ -229,7 +215,7 @@ public function testGetMoreIteration(): void $attempts++; $primary->executeCommand('admin', new Command(['replSetStepDown' => 5, 'force' => true])); $success = true; - } catch (DriverException $e) { + } catch (DriverException) { if ($attempts == 10) { $this->fail(sprintf('Could not successfully execute replSetStepDown within %d attempts', $attempts)); } @@ -245,7 +231,7 @@ function () use ($cursor): void { }, function ($event) use (&$events): void { $events[] = $event; - } + }, ); $this->assertTrue($cursor->valid()); $this->assertCount(1, $events); @@ -279,12 +265,12 @@ private function dropAndRecreateCollection(): void private function getTotalConnectionsCreated(?Server $server = null) { - $server = $server ?: $this->client->getManager()->selectServer(new ReadPreference('primary')); + $server = $server ?: $this->client->getManager()->selectServer(); $cursor = $server->executeCommand( $this->getDatabaseName(), new Command(['serverStatus' => 1]), - new ReadPreference(ReadPreference::RP_PRIMARY) + ['readPreference' => new ReadPreference(ReadPreference::PRIMARY)], ); $cursor->setTypeMap(['root' => 'array', 'document' => 'array']); @@ -303,8 +289,8 @@ private function waitForPrimaryReelection(): void $this->insertDocuments(1); return; - } catch (DriverException $e) { - $this->client->getManager()->selectServer(new ReadPreference('primary')); + } catch (DriverException) { + $this->client->getManager()->selectServer(); return; } diff --git a/tests/SpecTests/ReadWriteConcernSpecTest.php b/tests/SpecTests/ReadWriteConcernSpecTest.php deleted file mode 100644 index 96560a86a..000000000 --- a/tests/SpecTests/ReadWriteConcernSpecTest.php +++ /dev/null @@ -1,116 +0,0 @@ - $value) { - if ($value === null) { - static::assertObjectNotHasAttribute($key, $actual); - unset($expected->{$key}); - } - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Execute an individual test case from the specification. - * - * @dataProvider provideTests - * @param stdClass $test Individual "tests[]" document - * @param array $runOn Top-level "runOn" array with server requirements - * @param array $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - */ - public function testReadWriteConcern(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void - { - if (isset(self::$incompleteTests[$this->dataDescription()])) { - $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); - } - - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - if (isset($test->skipReason)) { - $this->markTestSkipped($test->skipReason); - } - - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - $context = Context::fromReadWriteConcern($test, $databaseName, $collectionName); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - $this->insertDataFixtures($data); - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromReadWriteConcern($context->getClient(), $test->expectations); - $commandExpectations->startMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromReadWriteConcern($operation)->assert($this, $context); - } - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - - if (isset($test->outcome->collection->data)) { - $this->assertOutcomeCollectionData($test->outcome->collection->data); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/read-write-concern/operation/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename(dirname($filename)) . '/' . basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName]; - } - } - - return $testArgs; - } -} diff --git a/tests/SpecTests/ResultExpectation.php b/tests/SpecTests/ResultExpectation.php index 9626719e2..b4db23cca 100644 --- a/tests/SpecTests/ResultExpectation.php +++ b/tests/SpecTests/ResultExpectation.php @@ -36,16 +36,12 @@ final class ResultExpectation public const ASSERT_CALLABLE = 11; public const ASSERT_DOCUMENTS_MATCH = 12; - /** @var integer */ - private $assertionType = self::ASSERT_NOTHING; - - /** @var mixed */ - private $expectedValue; + private mixed $expectedValue; /** @var callable */ private $assertionCallable; - private function __construct(int $assertionType, $expectedValue) + private function __construct(private int $assertionType, $expectedValue) { switch ($assertionType) { case self::ASSERT_BULKWRITE: @@ -67,7 +63,6 @@ private function __construct(int $assertionType, $expectedValue) break; } - $this->assertionType = $assertionType; $this->expectedValue = $expectedValue; } @@ -123,19 +118,6 @@ public static function fromRetryableReads(stdClass $operation, $defaultAssertion return new self($assertionType, $expectedValue); } - public static function fromRetryableWrites(stdClass $outcome, $defaultAssertionType) - { - if (property_exists($outcome, 'result') && ! self::isErrorResult($outcome->result)) { - $assertionType = $outcome->result === null ? self::ASSERT_NULL : $defaultAssertionType; - $expectedValue = $outcome->result; - } else { - $assertionType = self::ASSERT_NOTHING; - $expectedValue = null; - } - - return new self($assertionType, $expectedValue); - } - public static function fromTransactions(stdClass $operation, $defaultAssertionType) { if (property_exists($operation, 'result') && ! self::isErrorResult($operation->result)) { @@ -167,7 +149,7 @@ public function assert(FunctionalTestCase $test, $actual): void * from the BulkWriteException. */ $test->assertThat($actual, $test->logicalOr( $test->isInstanceOf(BulkWriteResult::class), - $test->isInstanceOf(WriteResult::class) + $test->isInstanceOf(WriteResult::class), )); if (! $actual->isAcknowledged()) { @@ -224,7 +206,7 @@ public function assert(FunctionalTestCase $test, $actual): void * from the BulkWriteException. */ $test->assertThat($actual, $test->logicalOr( $test->isInstanceOf(InsertManyResult::class), - $test->isInstanceOf(WriteResult::class) + $test->isInstanceOf(WriteResult::class), )); if (isset($expected->insertedCount)) { @@ -241,7 +223,7 @@ public function assert(FunctionalTestCase $test, $actual): void case self::ASSERT_INSERTONE: $test->assertThat($actual, $test->logicalOr( $test->isInstanceOf(InsertOneResult::class), - $test->isInstanceOf(WriteResult::class) + $test->isInstanceOf(WriteResult::class), )); if (isset($expected->insertedCount)) { @@ -251,7 +233,7 @@ public function assert(FunctionalTestCase $test, $actual): void if (property_exists($expected, 'insertedId')) { $test->assertSameDocument( ['insertedId' => $expected->insertedId], - ['insertedId' => $actual->getInsertedId()] + ['insertedId' => $actual->getInsertedId()], ); } @@ -261,7 +243,7 @@ public function assert(FunctionalTestCase $test, $actual): void $test->assertIsObject($expected); $test->assertThat($actual, $test->logicalOr( $test->isType('array'), - $test->isType('object') + $test->isType('object'), )); $test->assertMatchesDocument($expected, $actual); break; @@ -281,7 +263,7 @@ public function assert(FunctionalTestCase $test, $actual): void $test->assertIsObject($expected); $test->assertThat($actual, $test->logicalOr( $test->isType('array'), - $test->isType('object') + $test->isType('object'), )); $test->assertSameDocument($expected, $actual); break; @@ -312,7 +294,7 @@ public function assert(FunctionalTestCase $test, $actual): void if (property_exists($expected, 'upsertedId')) { $test->assertSameDocument( ['upsertedId' => $expected->upsertedId], - ['upsertedId' => $actual->getUpsertedId()] + ['upsertedId' => $actual->getUpsertedId()], ); } @@ -346,10 +328,9 @@ private static function isArrayOfObjects($array) /** * Determines whether the result is actually an error expectation. * - * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.rst#test-format - * @param mixed $result + * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.md#test-format */ - private static function isErrorResult($result): bool + private static function isErrorResult(mixed $result): bool { if (! is_object($result)) { return false; diff --git a/tests/SpecTests/RetryableReads/Prose2_RetryOnMongosTest.php b/tests/SpecTests/RetryableReads/Prose2_RetryOnMongosTest.php new file mode 100644 index 000000000..2040c3d49 --- /dev/null +++ b/tests/SpecTests/RetryableReads/Prose2_RetryOnMongosTest.php @@ -0,0 +1,168 @@ +isMongos()) { + $this->markTestSkipped('Test requires connections to mongos'); + } + + $this->skipIfServerVersion('<', '4.1.7', 'Test requires mongos support for configureFailPoint'); + + /* By default, the Manager under test is created with a single-mongos + * URI. Explicitly create a Client with multiple mongoses and invoke + * server selection to initialize SDAM. */ + $client = static::createTestClient(static::getUri(true), ['retryReads' => true]); + $client->getManager()->selectServer(); + + /* Step 1: Select servers for each mongos in the cluster. + * + * TODO: Support topologies with 3+ servers by selecting only two and + * recreating a client URI. + */ + $servers = $client->getManager()->getServers(); + assert(count($servers) === 2); + $this->assertNotEquals($servers[0], $servers[1]); + + // Step 2: Configure the following fail point on each mongos + foreach ($servers as $server) { + $this->configureFailPoint( + [ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['find'], + 'errorCode' => self::HOST_UNREACHABLE, + ], + ], + $server, + ); + } + + /* Step 3: Use the previously created client with retryReads=true, + * which is connected to a cluster with two mongoses */ + + // Step 4: Enable failed command event monitoring for client + $subscriber = new class implements CommandSubscriber { + /** @var string[] */ + public array $commandFailedServers = []; + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + $this->commandFailedServers[] = $event->getHost() . ':' . $event->getPort(); + } + }; + + $client->addSubscriber($subscriber); + + // Step 5: Execute a find command. Assert that the command failed. + try { + $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->find(['x' => 1]); + $this->fail('BulkWriteException was not thrown'); + } catch (CommandException $e) { + $this->assertSame(self::HOST_UNREACHABLE, $e->getCode()); + } + + $client->removeSubscriber($subscriber); + + /* Step 6: Assert that two failed command events occurred. Assert that + * the failed command events occurred on different mongoses. */ + $this->assertCount(2, $subscriber->commandFailedServers); + $this->assertNotEquals($subscriber->commandFailedServers[0], $subscriber->commandFailedServers[1]); + + // Step 7: The fail points will be disabled during tearDown() + } + + public function testRetryOnSameMongos(): void + { + if (! $this->isMongos()) { + $this->markTestSkipped('Test requires connections to mongos'); + } + + $this->skipIfServerVersion('<', '4.1.7', 'Test requires mongos support for configureFailPoint'); + + // Step 1: Create a client that connects to a single mongos + $client = static::createTestClient(null, ['directConnection' => false, 'retryReads' => true]); + $server = $client->getManager()->selectServer(); + + // Step 2: Configure the following fail point on the mongos + $this->configureFailPoint( + [ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['find'], + 'errorCode' => self::HOST_UNREACHABLE, + ], + ], + $server, + ); + + // Step 3 is omitted because we can re-use the same client + + // Step 4: Enable succeeded and failed command event monitoring + $subscriber = new class implements CommandSubscriber { + public ?string $commandSucceededServer = null; + public ?string $commandFailedServer = null; + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + $this->commandSucceededServer = $event->getHost() . ':' . $event->getPort(); + } + + public function commandFailed(CommandFailedEvent $event): void + { + $this->commandFailedServer = $event->getHost() . ':' . $event->getPort(); + } + }; + + $client->addSubscriber($subscriber); + + // Step 5: Execute a find command. Assert that the command succeeded. + $cursor = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->find(['x' => 1]); + $this->assertSame([], $cursor->toArray()); + + $client->removeSubscriber($subscriber); + + /* Step 6: Assert that exactly one failed command event and one + * succeeded command event occurred. Assert that both events occurred on + * the same mongos. */ + $this->assertNotNull($subscriber->commandSucceededServer); + $this->assertNotNull($subscriber->commandFailedServer); + $this->assertEquals($subscriber->commandSucceededServer, $subscriber->commandFailedServer); + + // Step 7: The fail point will be disabled during tearDown() + } +} diff --git a/tests/SpecTests/RetryableReadsSpecTest.php b/tests/SpecTests/RetryableReadsSpecTest.php deleted file mode 100644 index 37465aff9..000000000 --- a/tests/SpecTests/RetryableReadsSpecTest.php +++ /dev/null @@ -1,136 +0,0 @@ - 'Not implemented', - 'listDatabaseObjects' => 'Not implemented', - 'listIndexNames' => 'Not implemented', - ]; - - /** @var array */ - private static $incompleteTests = []; - - /** - * Assert that the expected and actual command documents match. - * - * @param stdClass $expected Expected command document - * @param stdClass $actual Actual command document - */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual): void - { - static::assertDocumentsMatch($expected, $actual); - } - - /** - * Execute an individual test case from the specification. - * - * @param stdClass $test Individual "tests[]" document - * @param array $runOn Top-level "runOn" array with server requirements - * @param array|object $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string|null $collectionName Name of collection under test - * @param string|null $bucketName Name of GridFS bucket under test - * - * @dataProvider provideTests - * @group matrix-testing-exclude-server-4.4-driver-4.2 - * @group matrix-testing-exclude-server-5.0-driver-4.2 - */ - public function testRetryableReads(stdClass $test, ?array $runOn, $data, string $databaseName, ?string $collectionName, ?string $bucketName): void - { - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - foreach (self::$skippedOperations as $operation => $skipReason) { - if (strpos($this->dataDescription(), $operation) === 0) { - $this->markTestSkipped($skipReason); - } - } - - if (strpos($this->dataDescription(), 'changeStreams-') === 0) { - $this->skipIfChangeStreamIsNotSupported(); - } - - if (isset(self::$incompleteTests[$this->dataDescription()])) { - $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); - } - - $context = Context::fromRetryableReads($test, $databaseName, $collectionName, $bucketName); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - - if (is_object($data)) { - foreach ($data as $collectionName => $documents) { - $this->assertIsArray($documents); - $this->insertDataFixtures($documents, $collectionName); - } - } else { - $this->insertDataFixtures($data); - } - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromRetryableReads($context->getClient(), $test->expectations); - $commandExpectations->startMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromRetryableReads($operation)->assert($this, $context); - } - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - - if (isset($test->outcome->collection->data)) { - $this->assertOutcomeCollectionData($test->outcome->collection->data); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/retryable-reads/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - $bucketName = $json->bucket_name ?? null; - // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName, $bucketName]; - } - } - - return $testArgs; - } -} diff --git a/tests/SpecTests/RetryableWrites/Prose3_ReturnOriginalErrorTest.php b/tests/SpecTests/RetryableWrites/Prose3_ReturnOriginalErrorTest.php new file mode 100644 index 000000000..55afae5e0 --- /dev/null +++ b/tests/SpecTests/RetryableWrites/Prose3_ReturnOriginalErrorTest.php @@ -0,0 +1,92 @@ +isReplicaSet()) { + $this->markTestSkipped('Test only applies to replica sets'); + } + + /* Note: the NoWritesPerformed label was introduced in MongoDB 6.1 + * (SERVER-66479), but the test can still be run on earlier versions. */ + $this->skipIfServerVersion('<', '6.0.0', 'Test should only be run for MongoDB 6.0+'); + + $client = self::createTestClient(null, ['retryWrites' => true]); + + // Step 2: Configure a fail point with error code 91 + $this->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['insert'], + 'errorLabels' => ['RetryableWriteError'], + 'writeConcernError' => ['code' => self::SHUTDOWN_IN_PROGRESS], + ], + ]); + + $subscriber = new class ($this) implements CommandSubscriber { + public function __construct(private FunctionalTestCase $testCase) + { + } + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + if ($event->getCommandName() === 'insert') { + // Step 3: Configure a fail point with code 10107 + $this->testCase->configureFailPoint([ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'errorCode' => Prose3_ReturnOriginalErrorTest::NOT_WRITABLE_PRIMARY, + 'errorLabels' => ['RetryableWriteError', 'NoWritesPerformed'], + 'failCommands' => ['insert'], + ], + ]); + } + } + + public function commandFailed(CommandFailedEvent $event): void + { + } + }; + + $client->addSubscriber($subscriber); + + // Step 4: Run insertOne + try { + $client->selectCollection('db', 'retryable_writes')->insertOne(['write' => 1]); + } catch (BulkWriteException $e) { + $writeConcernError = $e->getWriteResult()->getWriteConcernError(); + $this->assertNotNull($writeConcernError); + + // Assert that the write concern error is from the first failpoint + $this->assertSame(self::SHUTDOWN_IN_PROGRESS, $writeConcernError->getCode()); + } + + $client->removeSubscriber($subscriber); + + // Step 5: The fail point will be disabled during tearDown() + } +} diff --git a/tests/SpecTests/RetryableWrites/Prose4_RetryOnDifferentMongosTest.php b/tests/SpecTests/RetryableWrites/Prose4_RetryOnDifferentMongosTest.php new file mode 100644 index 000000000..f78ba3b75 --- /dev/null +++ b/tests/SpecTests/RetryableWrites/Prose4_RetryOnDifferentMongosTest.php @@ -0,0 +1,106 @@ +isMongos()) { + $this->markTestSkipped('Test requires connections to mongos'); + } + + $this->skipIfServerVersion('<', '4.3.1', 'Test requires configureFailPoint to support errorLabels'); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + + /* By default, the Manager under test is created with a single-mongos + * URI. Explicitly create a Client with multiple mongoses and invoke + * server selection to initialize SDAM. */ + $client = static::createTestClient(static::getUri(true), ['retryWrites' => true]); + $client->getManager()->selectServer(); + + /* Step 1: Select servers for each mongos in the cluster. + * + * TODO: Support topologies with 3+ servers by selecting only two and + * recreating a client URI. + */ + $servers = $client->getManager()->getServers(); + assert(count($servers) === 2); + $this->assertNotEquals($servers[0], $servers[1]); + + // Step 2: Configure the following fail point on each mongos + foreach ($servers as $server) { + $this->configureFailPoint( + [ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['insert'], + 'errorCode' => self::HOST_UNREACHABLE, + 'errorLabels' => ['RetryableWriteError'], + ], + ], + $server, + ); + } + + /* Step 3: Use the previously created client with retryWrites=true, + * which is connected to a cluster with two mongoses */ + + // Step 4: Enable failed command event monitoring for client + $subscriber = new class implements CommandSubscriber { + /** @var string[] */ + public array $commandFailedServers = []; + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + } + + public function commandFailed(CommandFailedEvent $event): void + { + $this->commandFailedServers[] = $event->getHost() . ':' . $event->getPort(); + } + }; + + $client->addSubscriber($subscriber); + + // Step 5: Execute an insert command. Assert that the command failed. + try { + $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->insertOne(['x' => 1]); + $this->fail('BulkWriteException was not thrown'); + } catch (BulkWriteException $e) { + $this->assertSame(self::HOST_UNREACHABLE, $e->getCode()); + } + + $client->removeSubscriber($subscriber); + + /* Step 6: Assert that two failed command events occurred. Assert that + * the failed command events occurred on different mongoses. */ + $this->assertCount(2, $subscriber->commandFailedServers); + $this->assertNotEquals($subscriber->commandFailedServers[0], $subscriber->commandFailedServers[1]); + + // Step 7: The fail points will be disabled during tearDown() + } +} diff --git a/tests/SpecTests/RetryableWrites/Prose5_RetryOnSameMongosTest.php b/tests/SpecTests/RetryableWrites/Prose5_RetryOnSameMongosTest.php new file mode 100644 index 000000000..565ebf201 --- /dev/null +++ b/tests/SpecTests/RetryableWrites/Prose5_RetryOnSameMongosTest.php @@ -0,0 +1,87 @@ +isMongos()) { + $this->markTestSkipped('Test requires connections to mongos'); + } + + $this->skipIfServerVersion('<', '4.3.1', 'Test requires configureFailPoint to support errorLabels'); + + $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + + // Step 1: Create a client that connects to a single mongos + $client = static::createTestClient(null, ['directConnection' => false, 'retryWrites' => true]); + $server = $client->getManager()->selectServer(); + + // Step 2: Configure the following fail point on the mongos + $this->configureFailPoint( + [ + 'configureFailPoint' => 'failCommand', + 'mode' => ['times' => 1], + 'data' => [ + 'failCommands' => ['insert'], + 'errorCode' => self::HOST_UNREACHABLE, + 'errorLabels' => ['RetryableWriteError'], + ], + ], + $server, + ); + + // Step 3 is omitted because we can re-use the same client + + // Step 4: Enable succeeded and failed command event monitoring + $subscriber = new class implements CommandSubscriber { + public ?string $commandSucceededServer = null; + public ?string $commandFailedServer = null; + + public function commandStarted(CommandStartedEvent $event): void + { + } + + public function commandSucceeded(CommandSucceededEvent $event): void + { + $this->commandSucceededServer = $event->getHost() . ':' . $event->getPort(); + } + + public function commandFailed(CommandFailedEvent $event): void + { + $this->commandFailedServer = $event->getHost() . ':' . $event->getPort(); + } + }; + + $client->addSubscriber($subscriber); + + // Step 5: Execute an insert command. Assert that the command succeeded. + $insertOneResult = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName())->insertOne(['x' => 1]); + $this->assertSame(1, $insertOneResult->getInsertedCount()); + + $client->removeSubscriber($subscriber); + + /* Step 6: Assert that exactly one failed command event and one + * succeeded command event occurred. Assert that both events occurred on + * the same mongos connection. */ + $this->assertNotNull($subscriber->commandSucceededServer); + $this->assertNotNull($subscriber->commandFailedServer); + $this->assertEquals($subscriber->commandSucceededServer, $subscriber->commandFailedServer); + + // Step 7: The fail point will be disabled during tearDown() + } +} diff --git a/tests/SpecTests/RetryableWritesSpecTest.php b/tests/SpecTests/RetryableWritesSpecTest.php deleted file mode 100644 index bdbb3b293..000000000 --- a/tests/SpecTests/RetryableWritesSpecTest.php +++ /dev/null @@ -1,74 +0,0 @@ -isShardedCluster() && ! $this->isShardedClusterUsingReplicasets()) { - $this->markTestSkipped('Transaction numbers are only allowed on a replica set member or mongos (PHPC-1415)'); - } - - $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isMongos(); - - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - $context = Context::fromRetryableWrites($test, $this->getDatabaseName(), $this->getCollectionName(), $useMultipleMongoses); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - $this->insertDataFixtures($data); - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - Operation::fromRetryableWrites($test->operation, $test->outcome)->assert($this, $context); - - if (isset($test->outcome->collection->data)) { - $this->assertOutcomeCollectionData($test->outcome->collection->data); - } - } - - public function provideTests() - { - $testArgs = []; - - foreach (glob(__DIR__ . '/retryable-writes/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data]; - } - } - - return $testArgs; - } -} diff --git a/tests/SpecTests/SearchIndexSpecTest.php b/tests/SpecTests/SearchIndexSpecTest.php new file mode 100644 index 000000000..6f8d3b144 --- /dev/null +++ b/tests/SpecTests/SearchIndexSpecTest.php @@ -0,0 +1,201 @@ +skipIfAtlasSearchIndexIsNotSupported(); + } + + /** + * Case 1: Driver can successfully create and list search indexes + * + * @see https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.md#case-1-driver-can-successfully-create-and-list-search-indexes + */ + public function testCreateAndListSearchIndexes(): void + { + $collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + $name = 'test-search-index'; + $mapping = ['mappings' => ['dynamic' => false]]; + + $createdName = $collection->createSearchIndex( + $mapping, + ['name' => $name, 'comment' => 'Index creation test'], + ); + $this->assertSame($name, $createdName); + + $indexes = $this->waitForIndexes($collection, fn ($indexes) => $this->allIndexesAreQueryable($indexes)); + + $this->assertCount(1, $indexes); + $this->assertSame($name, $indexes[0]->name); + $this->assertSameDocument($mapping, $indexes[0]->latestDefinition); + } + + /** + * Case 2: Driver can successfully create multiple indexes in batch + * + * @see https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.md#case-2-driver-can-successfully-create-multiple-indexes-in-batch + */ + public function testCreateMultipleIndexesInBatch(): void + { + $collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + $names = ['test-search-index-1', 'test-search-index-2']; + $mapping = ['mappings' => ['dynamic' => false]]; + + $createdNames = $collection->createSearchIndexes([ + ['name' => $names[0], 'definition' => $mapping], + ['name' => $names[1], 'definition' => $mapping], + ]); + $this->assertSame($names, $createdNames); + + $indexes = $this->waitForIndexes($collection, fn ($indexes) => $this->allIndexesAreQueryable($indexes)); + + $this->assertCount(2, $indexes); + foreach ($names as $key => $name) { + $index = $indexes[$key]; + $this->assertSame($name, $index->name); + $this->assertSameDocument($mapping, $index->latestDefinition); + } + } + + /** + * Case 3: Driver can successfully drop search indexes + * + * @see https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.md#case-3-driver-can-successfully-drop-search-indexes + */ + public function testDropSearchIndexes(): void + { + $collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + $name = 'test-search-index'; + $mapping = ['mappings' => ['dynamic' => false]]; + + $createdName = $collection->createSearchIndex( + $mapping, + ['name' => $name], + ); + $this->assertSame($name, $createdName); + + $indexes = $this->waitForIndexes($collection, fn ($indexes) => $this->allIndexesAreQueryable($indexes)); + $this->assertCount(1, $indexes); + + $collection->dropSearchIndex($name); + + $indexes = $this->waitForIndexes($collection, fn (array $indexes): bool => count($indexes) === 0); + $this->assertCount(0, $indexes); + } + + /** + * Case 4: Driver can update a search index + * + * @see https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.md#case-4-driver-can-update-a-search-index + */ + public function testUpdateSearchIndex(): void + { + $collection = $this->createCollection($this->getDatabaseName(), $this->getCollectionName()); + $name = 'test-search-index'; + $mapping = ['mappings' => ['dynamic' => false]]; + + $createdName = $collection->createSearchIndex( + $mapping, + ['name' => $name], + ); + $this->assertSame($name, $createdName); + + $indexes = $this->waitForIndexes($collection, fn ($indexes) => $this->allIndexesAreQueryable($indexes)); + $this->assertCount(1, $indexes); + + $mapping = ['mappings' => ['dynamic' => true]]; + $collection->updateSearchIndex($name, $mapping); + + $indexes = $this->waitForIndexes($collection, fn ($indexes) => $this->allIndexesAreQueryable($indexes)); + + $this->assertCount(1, $indexes); + $this->assertSame($name, $indexes[0]->name); + $this->assertSameDocument($mapping, $indexes[0]->latestDefinition); + } + + /** + * Case 5: dropSearchIndex suppresses namespace not found errors + * + * @see https://github.com/mongodb/specifications/blob/master/source/index-management/tests/README.md#case-5-dropsearchindex-suppresses-namespace-not-found-errors + */ + public function testDropSearchIndexSuppressNamespaceNotFoundError(): void + { + $collection = $this->dropCollection($this->getDatabaseName(), $this->getCollectionName()); + + $collection->dropSearchIndex('test-seach-index'); + + $this->expectNotToPerformAssertions(); + } + + /** + * Randomize the collection name to avoid duplicate index names when running tests concurrently. + * Search index operations are asynchronous and can take up to a few minutes. + */ + protected function getCollectionName(): string + { + return sprintf('%s.%s', parent::getCollectionName(), bin2hex(random_bytes(5))); + } + + private function waitForIndexes(Collection $collection, Closure $callback): array + { + $timeout = hrtime()[0] + self::WAIT_TIMEOUT_SEC; + while (hrtime()[0] < $timeout) { + sleep(5); + $result = $collection->listSearchIndexes(); + $this->assertInstanceOf(CachingIterator::class, $result); + $result = iterator_to_array($result); + if ($callback($result)) { + return $result; + } + } + + $this->fail('Operation did not complete in time'); + } + + private function allIndexesAreQueryable(array $indexes): bool + { + if (count($indexes) === 0) { + return false; + } + + foreach ($indexes as $index) { + if (! $index->queryable) { + return false; + } + + if (! $index->status === self::STATUS_READY) { + return false; + } + } + + return true; + } +} diff --git a/tests/SpecTests/TransactionsSpecTest.php b/tests/SpecTests/TransactionsSpecTest.php index 15f4fefe6..148f29d0f 100644 --- a/tests/SpecTests/TransactionsSpecTest.php +++ b/tests/SpecTests/TransactionsSpecTest.php @@ -2,226 +2,18 @@ namespace MongoDB\Tests\SpecTests; -use MongoDB\BSON\Int64; -use MongoDB\BSON\Timestamp; -use MongoDB\Driver\Command; -use MongoDB\Driver\Exception\ServerException; -use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; -use stdClass; use function array_unique; -use function basename; use function count; -use function dirname; -use function file_get_contents; -use function get_object_vars; -use function glob; /** - * Transactions spec tests. + * Transactions spec prose tests. * - * @see https://github.com/mongodb/specifications/tree/master/source/transactions + * @see https://github.com/mongodb/specifications/blob/master/source/transactions/tests/README.md#mongos-pinning-prose-tests */ class TransactionsSpecTest extends FunctionalTestCase { - public const INTERRUPTED = 11601; - - /** - * In addition to the useMultipleMongoses tests, these should all pass - * before the driver can be considered compatible with MongoDB 4.2. - * - * @var array - */ - private static $incompleteTests = [ - 'transactions/mongos-recovery-token: commitTransaction retry fails on new mongos' => 'isMaster failpoints cannot be disabled', - 'transactions/pin-mongos: remain pinned after non-transient error on commit' => 'Blocked on SPEC-1320', - 'transactions/pin-mongos: unpin after transient error within a transaction and commit' => 'isMaster failpoints cannot be disabled', - ]; - - public function setUp(): void - { - parent::setUp(); - - static::killAllSessions(); - - $this->skipIfTransactionsAreNotSupported(); - } - - public function tearDown(): void - { - if ($this->hasFailed()) { - static::killAllSessions(); - } - - parent::tearDown(); - } - - /** - * Assert that the expected and actual command documents match. - * - * Note: this method may modify the $expected object. - * - * @param stdClass $expected Expected command document - * @param stdClass $actual Actual command document - */ - public static function assertCommandMatches(stdClass $expected, stdClass $actual): void - { - if (isset($expected->getMore) && $expected->getMore === 42) { - static::assertObjectHasAttribute('getMore', $actual); - static::assertThat($actual->getMore, static::logicalOr( - static::isInstanceOf(Int64::class), - static::isType('integer') - )); - unset($expected->getMore); - } - - if (isset($expected->recoveryToken) && $expected->recoveryToken === 42) { - static::assertObjectHasAttribute('recoveryToken', $actual); - static::assertIsObject($actual->recoveryToken); - unset($expected->recoveryToken); - } - - if (isset($expected->readConcern->afterClusterTime) && $expected->readConcern->afterClusterTime === 42) { - static::assertObjectHasAttribute('readConcern', $actual); - static::assertIsObject($actual->readConcern); - static::assertObjectHasAttribute('afterClusterTime', $actual->readConcern); - static::assertInstanceOf(Timestamp::class, $actual->readConcern->afterClusterTime); - unset($expected->readConcern->afterClusterTime); - - /* If "afterClusterTime" was the only assertion for "readConcern", - * unset the field to avoid expecting an empty document later. */ - if (get_object_vars($expected->readConcern) === []) { - unset($expected->readConcern); - } - } - - /* TODO: Determine if forcing a new libmongoc client in Context is - * preferable to skipping the txnNumber assertion. */ - //unset($expected['txnNumber']); - - foreach ($expected as $key => $value) { - if ($value === null) { - static::assertObjectNotHasAttribute($key, $actual); - unset($expected->{$key}); - } - } - - static::assertDocumentsMatch($expected, $actual); - } - - /** - * @dataProvider provideTransactionsTests - * @group serverless - */ - public function testTransactions(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void - { - $this->runTransactionTest($test, $runOn, $data, $databaseName, $collectionName); - } - - public function provideTransactionsTests(): array - { - return $this->provideTests('transactions'); - } - - /** - * @dataProvider provideTransactionsConvenientApiTests - */ - public function testTransactionsConvenientApi(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void - { - $this->runTransactionTest($test, $runOn, $data, $databaseName, $collectionName); - } - - public function provideTransactionsConvenientApiTests(): array - { - return $this->provideTests('transactions-convenient-api'); - } - - /** - * Execute an individual test case from the specification. - * - * @param stdClass $test Individual "tests[]" document - * @param array $runOn Top-level "runOn" array with server requirements - * @param array $data Top-level "data" array to initialize collection - * @param string $databaseName Name of database under test - * @param string $collectionName Name of collection under test - */ - private function runTransactionTest(stdClass $test, ?array $runOn, array $data, ?string $databaseName = null, ?string $collectionName = null): void - { - if (isset(self::$incompleteTests[$this->dataDescription()])) { - $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); - } - - $useMultipleMongoses = isset($test->useMultipleMongoses) && $test->useMultipleMongoses && $this->isMongos(); - - if (isset($runOn)) { - $this->checkServerRequirements($runOn); - } - - if (isset($test->skipReason)) { - $this->markTestSkipped($test->skipReason); - } - - $databaseName = $databaseName ?? $this->getDatabaseName(); - $collectionName = $collectionName ?? $this->getCollectionName(); - - $context = Context::fromTransactions($test, $databaseName, $collectionName, $useMultipleMongoses); - $this->setContext($context); - - $this->dropTestAndOutcomeCollections(); - $this->createTestCollection(); - $this->insertDataFixtures($data); - $this->preventStaleDbVersionError($test->operations); - - if (isset($test->failPoint)) { - $this->configureFailPoint($test->failPoint); - } - - if (isset($test->expectations)) { - $commandExpectations = CommandExpectations::fromTransactions($context->getClient(), $test->expectations); - $commandExpectations->startMonitoring(); - } - - foreach ($test->operations as $operation) { - Operation::fromTransactions($operation)->assert($this, $context); - } - - $context->session0->endSession(); - $context->session1->endSession(); - - if (isset($commandExpectations)) { - $commandExpectations->stopMonitoring(); - $commandExpectations->assert($this, $context); - } - - if (isset($test->outcome->collection->data)) { - $this->assertOutcomeCollectionData($test->outcome->collection->data); - } - } - - private function provideTests(string $dir): array - { - $testArgs = []; - - foreach (glob(__DIR__ . '/' . $dir . '/*.json') as $filename) { - $json = $this->decodeJson(file_get_contents($filename)); - $group = basename(dirname($filename)) . '/' . basename($filename, '.json'); - $runOn = $json->runOn ?? null; - $data = $json->data ?? []; - // phpcs:disable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - $databaseName = $json->database_name ?? null; - $collectionName = $json->collection_name ?? null; - // phpcs:enable Squiz.NamingConventions.ValidVariableName.MemberNotCamelCaps - - foreach ($json->tests as $test) { - $name = $group . ': ' . $test->description; - $testArgs[$name] = [$test, $runOn, $data, $databaseName, $collectionName]; - } - } - - return $testArgs; - } - /** * Prose test 1: Test that starting a new transaction on a pinned * ClientSession unpins the session and normal server selection is performed @@ -235,7 +27,7 @@ public function testStartingNewTransactionOnPinnedSessionUnpinsSession(): void $this->markTestSkipped('Pinning tests require mongos'); } - $client = self::createTestClient($this->getUri(true)); + $client = self::createTestClient(static::getUri(true)); $session = $client->startSession(); $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); @@ -275,7 +67,7 @@ public function testRunningNonTransactionOperationOnPinnedSessionUnpinsSession() $this->markTestSkipped('Pinning tests require mongos'); } - $client = self::createTestClient($this->getUri(true)); + $client = self::createTestClient(static::getUri(true)); $session = $client->startSession(); $collection = $client->selectCollection($this->getDatabaseName(), $this->getCollectionName()); @@ -299,73 +91,4 @@ public function testRunningNonTransactionOperationOnPinnedSessionUnpinsSession() $session->endSession(); } - - /** - * Create the collection, since it cannot be created within a transaction. - */ - protected function createTestCollection(): void - { - $context = $this->getContext(); - - $database = $context->getDatabase(); - $database->createCollection($context->collectionName, $context->defaultWriteOptions); - } - - /** - * Kill all sessions on the cluster. - * - * This will clean up any open transactions that may remain from a - * previously failed test. For sharded clusters, this command will be run - * on all mongos nodes. - */ - private static function killAllSessions(): void - { - // killAllSessions is not supported on serverless, see CLOUDP-84298 - if (static::isServerless()) { - return; - } - - $manager = static::createTestManager(); - $primary = $manager->selectServer(new ReadPreference('primary')); - - $servers = $primary->getType() === Server::TYPE_MONGOS - ? $manager->getServers() - : [$primary]; - - foreach ($servers as $server) { - try { - // Skip servers that do not support sessions - if (! isset($server->getInfo()['logicalSessionTimeoutMinutes'])) { - continue; - } - - $server->executeCommand('admin', new Command(['killAllSessions' => []])); - } catch (ServerException $e) { - // Interrupted error is safe to ignore (see: SERVER-38335) - if ($e->getCode() != self::INTERRUPTED) { - throw $e; - } - } - } - } - - /** - * Work around potential error executing distinct on sharded clusters. - * - * @see https://github.com/mongodb/specifications/tree/master/source/transactions/tests#why-do-tests-that-run-distinct-sometimes-fail-with-staledbversion - */ - private function preventStaleDbVersionError(array $operations): void - { - if (! $this->isShardedCluster()) { - return; - } - - foreach ($operations as $operation) { - if ($operation->name === 'distinct') { - $this->getContext()->getCollection()->distinct('foo'); - - return; - } - } - } } diff --git a/tests/SpecTests/atlas_data_lake/aggregate.json b/tests/SpecTests/atlas_data_lake/aggregate.json deleted file mode 100644 index 99995bca4..000000000 --- a/tests/SpecTests/atlas_data_lake/aggregate.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "collection_name": "driverdata", - "database_name": "test", - "tests": [ - { - "description": "Aggregate with pipeline (project, sort, limit)", - "operations": [ - { - "object": "collection", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0 - } - }, - { - "$sort": { - "a": 1 - } - }, - { - "$limit": 2 - } - ] - }, - "result": [ - { - "a": 1, - "b": 2, - "c": 3 - }, - { - "a": 2, - "b": 3, - "c": 4 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "driverdata" - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json b/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json deleted file mode 100644 index 997a3ab3f..000000000 --- a/tests/SpecTests/atlas_data_lake/estimatedDocumentCount.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "collection_name": "driverdata", - "database_name": "test", - "tests": [ - { - "description": "estimatedDocumentCount succeeds", - "operations": [ - { - "object": "collection", - "name": "estimatedDocumentCount", - "result": 15 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "driverdata" - }, - "command_name": "count", - "database_name": "test" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/find.json b/tests/SpecTests/atlas_data_lake/find.json deleted file mode 100644 index 8a3468a13..000000000 --- a/tests/SpecTests/atlas_data_lake/find.json +++ /dev/null @@ -1,65 +0,0 @@ -{ - "collection_name": "driverdata", - "database_name": "test", - "tests": [ - { - "description": "Find with projection and sort", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": { - "b": { - "$gt": 5 - } - }, - "projection": { - "_id": 0 - }, - "sort": { - "a": 1 - }, - "limit": 5 - }, - "result": [ - { - "a": 5, - "b": 6, - "c": 7 - }, - { - "a": 6, - "b": 7, - "c": 8 - }, - { - "a": 7, - "b": 8, - "c": 9 - }, - { - "a": 8, - "b": 9, - "c": 10 - }, - { - "a": 9, - "b": 10, - "c": 11 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "driverdata" - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/getMore.json b/tests/SpecTests/atlas_data_lake/getMore.json deleted file mode 100644 index e2e1d4788..000000000 --- a/tests/SpecTests/atlas_data_lake/getMore.json +++ /dev/null @@ -1,57 +0,0 @@ -{ - "collection_name": "driverdata", - "database_name": "test", - "tests": [ - { - "description": "A successful find event with getMore", - "operations": [ - { - "object": "collection", - "name": "find", - "arguments": { - "filter": { - "a": { - "$gte": 2 - } - }, - "sort": { - "a": 1 - }, - "batchSize": 3, - "limit": 4 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "driverdata", - "filter": { - "a": { - "$gte": 2 - } - }, - "sort": { - "a": 1 - }, - "batchSize": 3, - "limit": 4 - }, - "command_name": "find", - "database_name": "test" - } - }, - { - "command_started_event": { - "command": { - "batchSize": 1 - }, - "command_name": "getMore", - "database_name": "cursors" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/listCollections.json b/tests/SpecTests/atlas_data_lake/listCollections.json deleted file mode 100644 index e419f7b3e..000000000 --- a/tests/SpecTests/atlas_data_lake/listCollections.json +++ /dev/null @@ -1,25 +0,0 @@ -{ - "database_name": "test", - "tests": [ - { - "description": "ListCollections succeeds", - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command_name": "listCollections", - "database_name": "test", - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/listDatabases.json b/tests/SpecTests/atlas_data_lake/listDatabases.json deleted file mode 100644 index 6458148e4..000000000 --- a/tests/SpecTests/atlas_data_lake/listDatabases.json +++ /dev/null @@ -1,24 +0,0 @@ -{ - "tests": [ - { - "description": "ListDatabases succeeds", - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command_name": "listDatabases", - "database_name": "admin", - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/atlas_data_lake/runCommand.json b/tests/SpecTests/atlas_data_lake/runCommand.json deleted file mode 100644 index d81ff1a64..000000000 --- a/tests/SpecTests/atlas_data_lake/runCommand.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "database_name": "test", - "tests": [ - { - "description": "ping succeeds using runCommand", - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "ping", - "arguments": { - "command": { - "ping": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command_name": "ping", - "database_name": "test", - "command": { - "ping": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json b/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json deleted file mode 100644 index 1b72aa8a3..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-encrypted.json +++ /dev/null @@ -1,9515 +0,0 @@ -{ - "_id": "client_side_encryption_corpus", - "altname_aws": "aws", - "altname_local": "local", - "aws_double_rand_auto_id": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAABchrWPF5OPeuFpk4tUV325TmoNpGW+L5iPSXcLQIr319WJFIp3EDy5QiAHBfz2rThI7imU4eLXndIUrsjM0S/vg==", - "subType": "06" - } - } - }, - "aws_double_rand_auto_altname": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAABga5hXFiFvH/wOr0wOHSHFWRZ4pEs/UCC1XJWf46Dod3GY9Ry5j1ZyzeHueJxc4Ym5M8UHKSmJuXmNo9m9ZnkiA==", - "subType": "06" - } - } - }, - "aws_double_rand_explicit_id": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAABjTYZbsro/YxLWBb88qPXEIDQdzY7UZyK4UaZZ8h62OTxp43Zp9j6WvOEzKhXt4oJPMxlAxyTdqO6MllX5bsDrw==", - "subType": "06" - } - } - }, - "aws_double_rand_explicit_altname": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAABqkyXdeS3aWH2tRFoKxsIIL3ZH05gkiAEbutrjrdfw0b110iPhuCCOb0gP/nX/NRNCg1kCFZ543Vu0xZ0BRXlvQ==", - "subType": "06" - } - } - }, - "aws_double_det_explicit_id": { - "kms": "aws", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$numberDouble": "1.234" } - }, - "aws_double_det_explicit_altname": { - "kms": "aws", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$numberDouble": "1.234" } - }, - "aws_string_rand_auto_id": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAACAsI5E0rVT8TpIONY3TnbRvIxUjKsiy9ynVd/fE7U1lndE7KR6dTzs8QWK13kdKxO+njKPeC2ObBX904QmJ65Sw==", - "subType": "06" - } - } - }, - "aws_string_rand_auto_altname": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAACgBE6J6MRxPSDe+gfJPL8nBvuEIRBYxNS/73LqBTDJYyN/lsHQ6UlFDT5B4EkIPmHPTe+UBMOhZQ1bsP+DK8Aog==", - "subType": "06" - } - } - }, - "aws_string_rand_explicit_id": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAACbdTVDBWn35M5caKZgLFoiSVeFGKRj5K/QtupKNc8/dPIyCE+/a4PU51G/YIzFpYmp91nLpyq7lD/eJ/V0q66Zw==", - "subType": "06" - } - } - }, - "aws_string_rand_explicit_altname": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAACa4O+kE2BaqM0E+yiBrbCuE0YEGTrZ7L/+SuWm9gN3UupxwAQpRfxXAuUCTc9u1CXnvL+ga+VJMcWD2bawnn/Rg==", - "subType": "06" - } - } - }, - "aws_string_det_auto_id": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", - "subType": "06" - } - } - }, - "aws_string_det_explicit_id": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", - "subType": "06" - } - } - }, - "aws_string_det_explicit_altname": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAACyvOW8NcqRkZYzujivwVmYptJkic27PWr3Nq3Yv5Njz8cJdoyesVaQan6mn+U3wdfGEH8zbUUISdCx5qgvXEpvw==", - "subType": "06" - } - } - }, - "aws_object_rand_auto_id": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAADI+/afY6Eka8j1VNThWIeGkDZ7vo4/l66a01Z+lVUFFnVLeUV/nz9kM6uTTplNRUa+RXmNmwkoR/BHRnGc7wRNA==", - "subType": "06" - } - } - }, - "aws_object_rand_auto_altname": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAADzN4hVXWXKerhggRRtwWnDu2W2wQ5KIWb/X1WCZJKTjQSQ5LNHVasabBCa4U1q46PQ5pDDM1PkVjW6o+zzl/4xw==", - "subType": "06" - } - } - }, - "aws_object_rand_explicit_id": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAADhSs5zKFMuuux3fqFFuPito3N+bp5TgmkUtJtFXjmA/EnLuexGARvEeGUsMJ/n0VzKbbsiE8+AsUNY3o9YXutqQ==", - "subType": "06" - } - } - }, - "aws_object_rand_explicit_altname": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAADpj8MSov16h26bFDrHepsNkW+tOLOjRP7oj1Tnj75qZ+uqxxVkQ5B/t/Ihk5fikHTJGAcRBR5Vv6kJ/ulMaDnvQ==", - "subType": "06" - } - } - }, - "aws_object_det_explicit_id": { - "kms": "aws", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "x": { "$numberInt": "1" } } - }, - "aws_object_det_explicit_altname": { - "kms": "aws", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "x": { "$numberInt": "1" } } - }, - "aws_array_rand_auto_id": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAETWDOZ6zV39H2+W+BkwZIoxI3BNF6phKoiBZ9+i4T9uEoyU3TmoTPjuI0YNwR1v/p5/9rlVCG0KLZd16eeMb3zxZXjqh6IAJqfhsBQ7bzBYI=", - "subType": "06" - } - } - }, - "aws_array_rand_auto_altname": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAE1xeHbld2JjUiPB1k+xMZuIzNSai7mv1iusCswxKEfYCZ7YtR0GDQTxN4676CwhcodSDiysjgOxSFIGlptKCvl0k46LNq0EGypP9yWBLvdjQ=", - "subType": "06" - } - } - }, - "aws_array_rand_explicit_id": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAEFVa4U2uW65MGihhdOmpZFgnwGTs3VeN5TXXbXJ5cfm0CwXF3EPlzAVjy5WO/+lbvFufpQnIiLH59/kVygmwn+2P9zPNJnSGIJW9gaV8Vye8=", - "subType": "06" - } - } - }, - "aws_array_rand_explicit_altname": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAE11VXbfg7DJQ5/CB9XdBO0hCrxOkK3RrEjPGJ0FXlUo76IMna1uo+NVmDnM63CRlGE3/TEbZPpp0w0jn4vZLKvBmGr7o7WQusRY4jnRf5oH4=", - "subType": "06" - } - } - }, - "aws_array_det_explicit_id": { - "kms": "aws", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } - ] - }, - "aws_array_det_explicit_altname": { - "kms": "aws", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } - ] - }, - "aws_binData=00_rand_auto_id": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFpZYSktIHzGLZ6mcBFxywICqxdurqLVJcQR34ngix5YIOOulCYEhBSDzzSEyixEPCuU6cEzeuafpZRHX4qgcr9Q==", - "subType": "06" - } - } - }, - "aws_binData=00_rand_auto_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFshzESR9SyR++9r2yeaEjJYScMDez414s8pZkB3C8ihDa+rsyaxNy4yrF7qNEWjFrdFaH7zD2LdlPx+TKZgROlg==", - "subType": "06" - } - } - }, - "aws_binData=00_rand_explicit_id": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFpYwZRPDom7qyAe5WW/QNSq97/OYgRT8xUEaaR5pkbQEFd/Cwtl8Aib/3Bs1CT3MVaHVWna2u5Gcc4s/v18zLhg==", - "subType": "06" - } - } - }, - "aws_binData=00_rand_explicit_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFBq1RIU1YGHKAS1SAtS42fKtQBHQ/BCQzRutirNdvWlrXxF81LSaS7QgQyycZ2ePiOLsSm2vZS4xaQETeCgRC4g==", - "subType": "06" - } - } - }, - "aws_binData=00_det_auto_id": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", - "subType": "06" - } - } - }, - "aws_binData=00_det_explicit_id": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", - "subType": "06" - } - } - }, - "aws_binData=00_det_explicit_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAF6SJGmfD3hLVc4tLPm4v2zFuHoRxUDLumBR8Q0AlKK2nQPyvuHEPVBD3vQdDi+Q7PwFxmovJsHccr59VnzvpJeg==", - "subType": "06" - } - } - }, - "aws_binData=04_rand_auto_id": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFM5685zqlM8pc3xubtCFuf724g/bWXsebpNzw5E5HrxUqSBBVOvjs3IJH74+Supe169qejY358nOG41mLZvO2wJByvT14qmgUGpgBaLaxPR0=", - "subType": "06" - } - } - }, - "aws_binData=04_rand_auto_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFfLqOzpfjz/XYHDLnliUAA5ehi6s+OIjvrLa59ubqEf8DuoCEWlO13Dl8X42IBB4hoSsO2RUeWtc9MeH4SdIUh/xJN3qS7qzjh/H+GvZRdAM=", - "subType": "06" - } - } - }, - "aws_binData=04_rand_explicit_id": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFkmKfKAbz9tqVaiM9MRhYttiY3vgDwXpdYLQ4uUgWX89KRayLADWortYL+Oq+roFhO3oiwB9vjeWGIdgbj5wSh/50JT/2Gs85TXFe1GFjfWs=", - "subType": "06" - } - } - }, - "aws_binData=04_rand_explicit_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAFKbufv83ddN+07Q5Ocq0VxUEV+BesSrVM7Bol3cMlWjHi7P+MrdwhNEa94xlxlDwU3b+RD6kW+AuNEQ2byA3CX2JjZE1gHwN7l0ukXuqpD0A=", - "subType": "06" - } - } - }, - "aws_binData=04_det_auto_id": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", - "subType": "06" - } - } - }, - "aws_binData=04_det_explicit_id": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", - "subType": "06" - } - } - }, - "aws_binData=04_det_explicit_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAFlg7ceq9w/JMhHcNzQks6UrKYAffpUyeWuBIpcuLoB7YbFO61Dphseh77pzZbk3OvmveUq6EtCP2pmsq7hA+QV4hkv6BTn4m6wnXw6ss/qfE=", - "subType": "06" - } - } - }, - "aws_undefined_rand_explicit_id": { - "kms": "aws", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$undefined": true } - }, - "aws_undefined_rand_explicit_altname": { - "kms": "aws", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$undefined": true } - }, - "aws_undefined_det_explicit_id": { - "kms": "aws", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$undefined": true } - }, - "aws_undefined_det_explicit_altname": { - "kms": "aws", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$undefined": true } - }, - "aws_objectId_rand_auto_id": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAHASE+V+LlkmwgF9QNjBK8QBvC973NaTMk6wbd57VB2EpQzrgxMtR5gYzVeqq4xaaHqrncyZCOIxDJkFlaim2NqA==", - "subType": "06" - } - } - }, - "aws_objectId_rand_auto_altname": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAHf/+9Qj/ozcDoUb8RNBnajU1d9hJ/6fE17IEZnw+ma6v5yH8LqZk9w3dtm6Sfw1unMhcMKrmIgs6kxqRWhNREJg==", - "subType": "06" - } - } - }, - "aws_objectId_rand_explicit_id": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAHzX8ejVLhoarQ5xgWsJitU/9eBm/Hlt2IIbZtS0SBc80qzkkWTaP9Zl9wrILH/Hwwx8RFnts855eKII3NJFa3BA==", - "subType": "06" - } - } - }, - "aws_objectId_rand_explicit_altname": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAHG5l6nUCY8f/6xO6TsPDrZHcdPRyMe3muMlY2DxHwv9GJNDR5Ne5VEAzUjnbgoy+B29SX4oY8cXJ6XhVz8mt3Eg==", - "subType": "06" - } - } - }, - "aws_objectId_det_auto_id": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", - "subType": "06" - } - } - }, - "aws_objectId_det_explicit_id": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", - "subType": "06" - } - } - }, - "aws_objectId_det_explicit_altname": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAHTMY2l+gY8glm4HeSsGfCSfOsTVTzYU8qnQV8iqEFHrO5SBJac59gv3N/jukMwAnt0j6vIIQrROkVetU24YY7sQ==", - "subType": "06" - } - } - }, - "aws_bool_rand_auto_id": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAISm4UFt1HC2j0ObpTBg7SvF2Dq31i9To2ED4F3JcTihhq0fVzaSCsUz9VTJ0ziHmeNPNdfPPZO6qA/CDEZBO4jg==", - "subType": "06" - } - } - }, - "aws_bool_rand_auto_altname": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAIj93KeAa96DmZXdB8boFvW19jhJSMmtSs5ag5FDSkH8MdKG2d2VoBOdUlBrL+LHYELqeDHCszY7qCirvb5mIgZg==", - "subType": "06" - } - } - }, - "aws_bool_rand_explicit_id": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAIMbDFEuHIl5MNEsWnYLIand1vpK6EMv7Mso6qxrN4wHSVVwmxK+GCPgrKoUQsNuTssFWNCu0IhwrXOagDEfmlxw==", - "subType": "06" - } - } - }, - "aws_bool_rand_explicit_altname": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAIkIaWfmPdxgAV5Rtb6on6T0NGt9GPFDScQD5I/Ch0ngiTCCKceJOjU0ljd3YTgfWRA1p/MlMIV0I5YAWZXKTHlg==", - "subType": "06" - } - } - }, - "aws_bool_det_explicit_id": { - "kms": "aws", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "aws_bool_det_explicit_altname": { - "kms": "aws", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "aws_date_rand_auto_id": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAJz1VG4+QnQXEE+TGu/pzfPugGMVTiC1xnenG1ByRdPvsERVw9WComWl1tb9tt9oblD7H/q0y1+y8HevkDqohB2Q==", - "subType": "06" - } - } - }, - "aws_date_rand_auto_altname": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAJa1kI2mIIYWjf7zjf5dD9+psvAQpjZ3nnsoXA5upcIwEtZaC8bxKKHVpOLOP3rTbvT5EV6vLhXkferGoyaqd/8w==", - "subType": "06" - } - } - }, - "aws_date_rand_explicit_id": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAJ9Q5Xe4UuOLQTUwosk47A6xx40XJcNoICCNtKrHqsUYy0QLCFRc5v4nA0160BVghURizbUtX8iuIp11pnsDyRtA==", - "subType": "06" - } - } - }, - "aws_date_rand_explicit_altname": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAJkHOdUc/4U82wxWJZ0SYABkJjQqNApkH2Iy/5S+PoatPgynoeSFTU9FmAbuWV/gbtIfBiaCOIjlsdonl/gf9+5w==", - "subType": "06" - } - } - }, - "aws_date_det_auto_id": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", - "subType": "06" - } - } - }, - "aws_date_det_explicit_id": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", - "subType": "06" - } - } - }, - "aws_date_det_explicit_altname": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAJEEpQNsiqMWPqD4lhMkiOJHGE8FxOeYrKPiiAp/bZTrLKyCSS0ZL1WT9H3cGzxWPm5veihCjKqWhjatC/pjtzbQ==", - "subType": "06" - } - } - }, - "aws_null_rand_explicit_id": { - "kms": "aws", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "aws_null_rand_explicit_altname": { - "kms": "aws", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "aws_null_det_explicit_id": { - "kms": "aws", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "aws_null_det_explicit_altname": { - "kms": "aws", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "aws_regex_rand_auto_id": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAALnhViSt3HqTDzyLN4mWO9srBU8TjRvPWsAJYfj/5sgI/yFuWdrggMs3Aq6G+K3tRrX3Yb+osy5CLiFCxq9WIvAA==", - "subType": "06" - } - } - }, - "aws_regex_rand_auto_altname": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAALbL2RS2tGQLBZ+6LtXLKAWFKcoKui+u4+gMIlFemLgpdO2eLqrMJB53ccqZImX8ons9UgAwDkiD68hWy8e7KHfg==", - "subType": "06" - } - } - }, - "aws_regex_rand_explicit_id": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAALa0+ftF6W/0Ul4J9VT/3chXFktE1o+OK4S14h2kyOqDVNA8yMKuyCK5nWl1yZvjJ76TuhEABte23oxcBP5QwalQ==", - "subType": "06" - } - } - }, - "aws_regex_rand_explicit_altname": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAALS4Yo9Fwk6OTx2CWdnObFT2L4rHngeIbdCyT4/YMJYd+jLU3mph14M1ptZZg+TBIgSPHq+BkvpRDifbMmOVr/Hg==", - "subType": "06" - } - } - }, - "aws_regex_det_auto_id": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", - "subType": "06" - } - } - }, - "aws_regex_det_explicit_id": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", - "subType": "06" - } - } - }, - "aws_regex_det_explicit_altname": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAALpwNlokiTCUtTa2Kx9NVGvXR/aKPGhR5iaCT7nHEk4BOiZ9Kr4cRHdPCeZ7A+gjG4cKoT62sm3Fj1FwSOl8J8aQ==", - "subType": "06" - } - } - }, - "aws_dbPointer_rand_auto_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAMfCVAnMNbRGsThnoVGb2KDsCIU2ehcPtebk/TFG4GZvEmculscLLih813lEz5NHS2sAXBn721EzUS7d0TKAPbmEYFwUBnijIQIPvUoUO8AQM=", - "subType": "06" - } - } - }, - "aws_dbPointer_rand_auto_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAMvYJ5BtaMLVXV+qj85q5WqKRlzlHOBIIxZfUE/BBXUwqSTpJLdQQD++DDh6F2dtorBeYa3oUv2ef3ImASk5j23joU35Pm3Zt9Ci1pMNGodWs=", - "subType": "06" - } - } - }, - "aws_dbPointer_rand_explicit_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAMdsmYtPDw8kKjfB2kWfx5W1oNEkWWct1lRpesN303pUWsawDJpfBx40lg18So2X/g4yGIwpY3qfEKQZA4vCJeT+MTjhRXFjXA7eS/mxv8f3E=", - "subType": "06" - } - } - }, - "aws_dbPointer_rand_explicit_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAM0hcvS5zmY3mlTp0SfME/rINlflF/sx2KvP0eJTdH+Uk0WHuTkFIJAza+bXvV/gB7iNC350qyzUX3M6NHx/9s/5yBpY8MawTZTZ7WCQIA+ZI=", - "subType": "06" - } - } - }, - "aws_dbPointer_det_auto_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", - "subType": "06" - } - } - }, - "aws_dbPointer_det_explicit_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", - "subType": "06" - } - } - }, - "aws_dbPointer_det_explicit_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAMp4QxbaEOij66L+RtaMekrDSm6QbfJBTQ8lQFhxfq9n7SVuQ9Zwdy14Ja8tyI3cGgQzQ/73rHUJ3CKA4+OYr63skYUkkkdlHxUrIMd5j5woc=", - "subType": "06" - } - } - }, - "aws_javascript_rand_auto_id": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAN3HzAC9BTD7Jgi0PR4RS/Z6L6QtAQ7VhbKRbX+1smmnYniH6jVBM6zyxMDM8h9YjMPNs8EJrGDnisuf33w5KI/A==", - "subType": "06" - } - } - }, - "aws_javascript_rand_auto_altname": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAANJpw+znlu3ecSiNyZ0EerVsow4aDRF2auI3Wy69EVexJkQlHO753PjRn8hG/x2kY8ROy5IUU43jaugP5AN1bwNQ==", - "subType": "06" - } - } - }, - "aws_javascript_rand_explicit_id": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAANzoDiq8uI0+l8COY8YdM9S3rpLvPOHOWmJqJNtOyS0ZXUx1SB5paRJ4W3Eg8KuXEeoFwvBDe9cW9YT66CzkjlBw==", - "subType": "06" - } - } - }, - "aws_javascript_rand_explicit_altname": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAN/JhtRongJweLC5SdrXHhsFz3p82q3cwXf8Sru21DK6S39S997y3uhVLn0xlX5d94PxK1XVYSjz1oVuMxZouZ7Q==", - "subType": "06" - } - } - }, - "aws_javascript_det_auto_id": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", - "subType": "06" - } - } - }, - "aws_javascript_det_explicit_id": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", - "subType": "06" - } - } - }, - "aws_javascript_det_explicit_altname": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAANE39aEGiuUZ1WyakVEBgkGzLp5whkIjJ4uiaFLXniRszJL70FRkcf+aFXlA5Y4So9/ODKF76qbSsH4Jk6L+3mog==", - "subType": "06" - } - } - }, - "aws_symbol_rand_auto_id": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAOBv1T9tleM0xwNe7efg/MlShyzvXe3Pmg1GzPl3gjFRHZGWXR578KqX+8oiz65eXGzNuyOFvcpnR2gYCs3NeKeQfctO5plEiIva6nzCI5SK8=", - "subType": "06" - } - } - }, - "aws_symbol_rand_auto_altname": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAOwLgGws8CMh+GgkEJFAx8tDIflyjsgG+/1FmZZobKAg8NOKqfXjtbnNCbvR28OCk6g/8SqBm8m53G6JciwvthJ0DirdfEexiUqu7IPtaeeyw=", - "subType": "06" - } - } - }, - "aws_symbol_rand_explicit_id": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAORQi3dNkXzZeruWu19kEhDu6fFD/h47ILzk+OVKQMoriAQC5YFyVRp1yAkIaWsrsPcyCHlfZ99FySSQeqSYbZZNj5FqyonWvDuPTduHDy3CI=", - "subType": "06" - } - } - }, - "aws_symbol_rand_explicit_altname": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAOj+Yl1pQPiJ6mESOISOyUYsKN/VIvC8f0derhxIPakXkwn57U0sxv+geUkrl3JZDxY3+cX5M1JZmY+PfjaYQhbTorf9RZaVC2Wwo2lMftWi0=", - "subType": "06" - } - } - }, - "aws_symbol_det_auto_id": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", - "subType": "06" - } - } - }, - "aws_symbol_det_explicit_id": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", - "subType": "06" - } - } - }, - "aws_symbol_det_explicit_altname": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAO5IHripygBGEsVK8RFWZ1rIIVUap8KVDuqOspZpERaj+5ZEfqIcyrP/WK9KdvwOfdOWXfP/mOwuImYgNdbaQe+ejkYe4W0Y0uneCuw88k95Q=", - "subType": "06" - } - } - }, - "aws_javascriptWithScope_rand_auto_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAPT31GSNkY1RM43miv1XPYtDX1vU/xORiM3U0pumjqA+JLU/HMhH++75OcMhcAQqMjm2nZtZScxdGJsJJPEEzqjbFNMJgYc9sqR5uLnzk+2dg=", - "subType": "06" - } - } - }, - "aws_javascriptWithScope_rand_auto_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAPUxgaKAxSQ1uzOZtzsbtrxtDT2P/zWY6lYsbChXuRUooqvyjXSkNDqKBBA7Gp5BdGiVB/JLR47Tihpbcw1s1yGhwQRvnqeDvPrf91nvElXRY=", - "subType": "06" - } - } - }, - "aws_javascriptWithScope_rand_explicit_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAPv8W0ZtquFCLTG0TqvRjdzKa/4mvqT2FuEGQ0mXG2k2BZh2LY5APr/kgW0tP4eLjHzVld6OLiM9ZKAvENCZ6/fKOvqSwpIfkdLWUIeB4REQg=", - "subType": "06" - } - } - }, - "aws_javascriptWithScope_rand_explicit_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAPMVhWjaxLffdAOkVgIJpjgNIldMS451NQs3C1jb+pzopHp3DlfZ+AHQpK9reMVVKjaqanhWBpL25q+feA60XVgZPCUDroiRYqMFqU//y0amw=", - "subType": "06" - } - } - }, - "aws_javascriptWithScope_det_explicit_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$code": "x=1", "$scope": {} } - }, - "aws_javascriptWithScope_det_explicit_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$code": "x=1", "$scope": {} } - }, - "aws_int_rand_auto_id": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAQFV5b3vsoZe+MT4z8soetpmrWJpm7be41FNu/rdEqHWTG32jCym6762PCNYH5+vA7ldCWQkdt+ncneHsxzPrm9w==", - "subType": "06" - } - } - }, - "aws_int_rand_auto_altname": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAQY9+QenvU1Tk/dEGZP11uOZJLHAJ9hWHbEhxbtxItt1LsdU/8gOZfypilIO5BUkLT/15PUuXV28GISNh6yIuWhw==", - "subType": "06" - } - } - }, - "aws_int_rand_explicit_id": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAQruCugbneumhcinuXm89WW1PXVuSOewttp9cpsPPsCRVqe/uAkZOdJnZ2KaEZ9zki2GeqaJTs1qDmaJofc6GMEA==", - "subType": "06" - } - } - }, - "aws_int_rand_explicit_altname": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAQb15qXl/tejk4pmgkc4pUxzt4eJrv/cetgzgcPVaROAQSzd8ptbgCjaV8vP46uqozRoaDFZbQ06t65c3f0x/Ucw==", - "subType": "06" - } - } - }, - "aws_int_det_auto_id": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", - "subType": "06" - } - } - }, - "aws_int_det_explicit_id": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", - "subType": "06" - } - } - }, - "aws_int_det_explicit_altname": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAAQCXo6ieWvfoqkG+rP7J2BV013AVf/oNMmmGWe44VEHahF+qZHzW5I/F2qIA+xgKkk172pFq0iTSOpe+K2WHMKFw==", - "subType": "06" - } - } - }, - "aws_timestamp_rand_auto_id": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAR63xXG8mrlixkQzD5VBIPE6NHicaWcS5CBhiIJDcZ0x8D9c5TgRJUfCeWhKvWFD4o0DoxcBQ2opPormFDpvmq/g==", - "subType": "06" - } - } - }, - "aws_timestamp_rand_auto_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAARAgY9LsUxP6gP4gYRvvzZ4iaHVQRNbycATiVag1YNSiDmEr4LYserYuBscdrIy4v3zgGaulFM9KV86bx0ItycZA==", - "subType": "06" - } - } - }, - "aws_timestamp_rand_explicit_id": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAARLneAZqPcHdzGGnXz2Ne5E7HP9cDC1+yoIwcA8OSF/IlzEjrrMAi3z6Izol6gWDlD7VOh7QYL3sASJOXyzF1hPQ==", - "subType": "06" - } - } - }, - "aws_timestamp_rand_explicit_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAARH2bU7KNo5SHxiO8JFEcT9wryuHNXyM7ADop1oPcESyay1Nc0WHPD3nr0yMAK481NxOkE3qXyaslu7bcP/744WA==", - "subType": "06" - } - } - }, - "aws_timestamp_det_auto_id": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", - "subType": "06" - } - } - }, - "aws_timestamp_det_explicit_id": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", - "subType": "06" - } - } - }, - "aws_timestamp_det_explicit_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAARG7kGfx0ky+d4Hl/fRBu8oUR1Mph26Dkv3J7fxGYanpzOFMiHIfVO0uwYMvsfzG54y0DDNlS3FmmS13DzepbzGQ==", - "subType": "06" - } - } - }, - "aws_long_rand_auto_id": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAASZbes2EdR78crt2pXVElW2YwAQh8HEBapYYeav2VQeg2syXaV/qZuD8ofnAVn4v/DydTTMVMmK+sVU/TlnAu2eA==", - "subType": "06" - } - } - }, - "aws_long_rand_auto_altname": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAASt+7fmMYH+fLHgybc+sng8/UmKP3YPUEPCz1SXVQljQp6orsCILSgtgGPsdeGnN5NSxh3XzerHs6zlR92fWpZCw==", - "subType": "06" - } - } - }, - "aws_long_rand_explicit_id": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAS01fF1uo6zYDToJnOT/EbDipzk7YZ6I+IspZF+avjU3XYfpRxT9NdAgKr0euWJwyAsdpWqqCwFummfrPeZOy04A==", - "subType": "06" - } - } - }, - "aws_long_rand_explicit_altname": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAS6tpH796bqy58mXf38rJvVtA1uBcxBE5yIGQ4RN44oypc/pvw0ouhFI1dkoneKMtAFU/5RygZV+RvQhRtgKn76A==", - "subType": "06" - } - } - }, - "aws_long_det_auto_id": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", - "subType": "06" - } - } - }, - "aws_long_det_explicit_id": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", - "subType": "06" - } - } - }, - "aws_long_det_explicit_altname": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQFkgAAAAAAAAAAAAAAAAAASC7O/8JeB4WTqQFPuMpFRsAuonPS3yu7IAPZeRPIr03CmM6HNndYIKMoFM13eELNZTdJSgg9u9ItGqRw+/XMHzQ==", - "subType": "06" - } - } - }, - "aws_decimal_rand_auto_id": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAATgf5zW9EgnWHPxj4HAGt472eN9UXP41TaF8V2J7S2zqSpiBZGKDuOIjw2FBSqaNp53vvfl9HpwAuQBJZhrwkBCKRkKV/AAR3/pTpuoqhSKaM=", - "subType": "06" - } - } - }, - "aws_decimal_rand_auto_altname": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAATPRfvZWdupE2N0W1DXUx7X8Zz7g43jawJL7PbQtTYetI78xRETkMdygwSEHgs+cvnUBBtYIeKRVkOGZQkwf568OclhDiPxUeD38cR5blBq/U=", - "subType": "06" - } - } - }, - "aws_decimal_rand_explicit_id": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAAT+ZnCg2lSMIohZ9RJ4CNs3LZ0g+nV04cYAmrxTSrTSBPGlZ7Ywh5A2rCss7AUijYZiKiYyZbuAzukbOuVRhdCtm+xo9+DyLAwTezF18okk6Y=", - "subType": "06" - } - } - }, - "aws_decimal_rand_explicit_altname": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgFkgAAAAAAAAAAAAAAAAAATlnQYASsTZRRHzFjcbCClXartcXBVRrYv7JImMkDmAj6EAjf/ZqpjeykkS/wohMhXaNwyZBdREr+n+GDV7imYoL4WRBOLnqB6hrYidlWqNzE=", - "subType": "06" - } - } - }, - "aws_decimal_det_explicit_id": { - "kms": "aws", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$numberDecimal": "1.234" } - }, - "aws_decimal_det_explicit_altname": { - "kms": "aws", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$numberDecimal": "1.234" } - }, - "aws_minKey_rand_explicit_id": { - "kms": "aws", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$minKey": 1 } - }, - "aws_minKey_rand_explicit_altname": { - "kms": "aws", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$minKey": 1 } - }, - "aws_minKey_det_explicit_id": { - "kms": "aws", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$minKey": 1 } - }, - "aws_minKey_det_explicit_altname": { - "kms": "aws", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$minKey": 1 } - }, - "aws_maxKey_rand_explicit_id": { - "kms": "aws", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "aws_maxKey_rand_explicit_altname": { - "kms": "aws", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "aws_maxKey_det_explicit_id": { - "kms": "aws", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "aws_maxKey_det_explicit_altname": { - "kms": "aws", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "local_double_rand_auto_id": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAABGF195CB8nRmK9+KxYO7T96MeXucC/ILQtEEQAS4zrwj3Qz7YEQrf/apvbKTCkn3siN2XSDLQ/7dmddZa9xa9yQ==", - "subType": "06" - } - } - }, - "local_double_rand_auto_altname": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAABY8g18z6ZOjGtfNxaAmU95tXMdoM6qbtDMpB72paqiHZTW1UGB22HPXiEnVz05JTBzzX4fc6tOldX6aJel812Zg==", - "subType": "06" - } - } - }, - "local_double_rand_explicit_id": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAABDlHwN8hYyScEhhx64TdJ2Qp2rmKRg8983zdqIL1914tyPwRQq7ySCOhmFif2S7v4KT+r0uOfimYvKD1n9rKHlg==", - "subType": "06" - } - } - }, - "local_double_rand_explicit_altname": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAB2VnTFlaCRzAZZTQiMWQORFNgXIuAJlHJXIHiYow2eO6JbVghWTpH+MsdafBNPVnc0zKuZBL0Qs2Nuk1xiQaqhA==", - "subType": "06" - } - } - }, - "local_double_det_explicit_id": { - "kms": "local", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$numberDouble": "1.234" } - }, - "local_double_det_explicit_altname": { - "kms": "local", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$numberDouble": "1.234" } - }, - "local_string_rand_auto_id": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAC5NBAPM8q2n9fnkwQfE9so/XcO51plPBNs5VlBRbDw68k9T6/uZ2TWsAvTYtVooY59zHHr2QS3usKbGQB6J61rA==", - "subType": "06" - } - } - }, - "local_string_rand_auto_altname": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACM/EjGMrkYHvSZra26m74upuvLkfKXTs+tTWquGzrgWYLnLt8I6XBIwx1VymS9EybrCU/ewmtgjLUNUFQacIeXA==", - "subType": "06" - } - } - }, - "local_string_rand_explicit_id": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACn4tD26UG8lO9gTZaxen6yXzHo/a2lokeY1ClxHMtJODoJr2JZzIDHP3A9aZ8L4+Vu+nyqphaWyGaGONKu8gpcQ==", - "subType": "06" - } - } - }, - "local_string_rand_explicit_altname": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACZfoO2LjY+IB31FZ1Tq7pHr0DCFKGJqWcXcOrnZ7bV9Euc9f101motJc31sp8nF5CTCfd83VQE0319eQrxDDaSw==", - "subType": "06" - } - } - }, - "local_string_det_auto_id": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", - "subType": "06" - } - } - }, - "local_string_det_explicit_id": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", - "subType": "06" - } - } - }, - "local_string_det_explicit_altname": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACW0cZMYWOY3eoqQQkSdBtS9iHC4CSQA27dy6XJGcmTV8EDuhGNnPmbx0EKFTDb0PCSyCjMyuE4nsgmNYgjTaSuw==", - "subType": "06" - } - } - }, - "local_object_rand_auto_id": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAADlekcUsETAkkKTjCVx5EISJN+sftrQax/VhaWXLyRgRz97adXXmwZkMyt+035SHZsF91i2LaXziMA4RHoP+nKFw==", - "subType": "06" - } - } - }, - "local_object_rand_auto_altname": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAADpaQmy5r6q9gLqEm+FIi/OyQgcuUnrICCP9rC4S3wR6qUHd82IW/3dFQUzwTkaXxgStjopamQMuZ4ESRj0xx0bA==", - "subType": "06" - } - } - }, - "local_object_rand_explicit_id": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAADCHRJCINzWY0u4gZPWEmHg/JoQ8IW4yMfUyzYJCQrEMp4rUeupIuxqSuq2QyLBYZBBv0r7t3lNH49I5qDeav2vA==", - "subType": "06" - } - } - }, - "local_object_rand_explicit_altname": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAADrHQQUnLF1jdNmFY/V266cS28XAB4nOKetHAcSbwkeUxNzgZT1g+XMQaYfcNMMv/ywypKU1KpgLMsEOpm4qcPkQ==", - "subType": "06" - } - } - }, - "local_object_det_explicit_id": { - "kms": "local", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "x": { "$numberInt": "1" } } - }, - "local_object_det_explicit_altname": { - "kms": "local", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "x": { "$numberInt": "1" } } - }, - "local_array_rand_auto_id": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAEXa7bQ5vGPNsLdklM/H+sop8aCL4vlDiVUoVjTAGjTngn2WLcdKLWxaNSyMdJpsI/NsxQJ58YrcwP+yHzi9rZVtRdbg7m8p+CYcq1vUm6UoQ=", - "subType": "06" - } - } - }, - "local_array_rand_auto_altname": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAEVlZlOvtRmGIhcYi/qPl3HKi/qf0yRQrkbVo9rScYkxDCBN9wA55pAWHDQ/5Sjy4d0DwL57k+M1G9e7xSIrv8xXKwoIuuabhSWaIX2eJHroY=", - "subType": "06" - } - } - }, - "local_array_rand_explicit_id": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAEYBLSYHHt2rohezMF4lMjNdqy9CY33EHf+pgRbJwVXZScLDgn9CcqeRsdU8bW5h2qgNpQvoSMBB7pW+Dgp1RauTHZSOd4PcZpAGjwoFDWSSM=", - "subType": "06" - } - } - }, - "local_array_rand_explicit_altname": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAES1IJ8S2NxWekolQockxLJvzFSGfKQ9Xbi55vO8LyWo0sIG9ZgPQXtVQkZ301CsdFduvx9A0vDqQ0MGYc4plxNnpUTizJPRUDyez5dOgZ9tI=", - "subType": "06" - } - } - }, - "local_array_det_explicit_id": { - "kms": "local", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } - ] - }, - "local_array_det_explicit_altname": { - "kms": "local", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { "$numberInt": "1" }, - { "$numberInt": "2" }, - { "$numberInt": "3" } - ] - }, - "local_binData=00_rand_auto_id": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAF+hgWs4ZCo9GnmhSM9SDSWzWX4E7Tlp4TwlEy3zfO/rrMREECGB4u8LD8Ju9b8YP+xcZhMI1tcz/vrQS87NffUg==", - "subType": "06" - } - } - }, - "local_binData=00_rand_auto_altname": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAFtEvaXWpGfXC1GlKu0AeRDaeBKHryGoS0tAUr48vfYk7umCr+fJKyXCY9vSv7wCiQxWLe8V/EZWkHsu0zqhJw9w==", - "subType": "06" - } - } - }, - "local_binData=00_rand_explicit_id": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAF/1L5bvmMX3Bk2nAw8KvvRd/7nZ82XHVasT0jrlPhSiJU7ehJMeUCOb7HCHU6KgCzZB9C2W3NoVhLKIhE9ZnYdg==", - "subType": "06" - } - } - }, - "local_binData=00_rand_explicit_altname": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAFK0W5IWKzggR4UU+fhwA2p8YCHLfmx5y1OEtHc/9be9eEYTORACDmWY6207Vd4LhBJCedd+Q5qMm7NRZjjhyLEQ==", - "subType": "06" - } - } - }, - "local_binData=00_det_auto_id": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", - "subType": "06" - } - } - }, - "local_binData=00_det_explicit_id": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", - "subType": "06" - } - } - }, - "local_binData=00_det_explicit_altname": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAF1ofBnK9+ERP29P/i14GQ/y3muic6tNKY532zCkzQkJSktYCOeXS8DdY1DdaOP/asZWzPTdgwby6/iZcAxJU+xQ==", - "subType": "06" - } - } - }, - "local_binData=04_rand_auto_id": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAFxq38aA4k/tYHPwJFRK0pahlo/3zjCe3VHJRqURRA+04lbJCvdkQTawxWlf8o+3Pcetl1UcPTQigdYp5KbIkstuPstLbT+TZXHVD1os9LTRw=", - "subType": "06" - } - } - }, - "local_binData=04_rand_auto_altname": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAFTXNWchCPmCSY0+AL22/kCBmAoDJDX5T18jpJHLdvZtHs0zwD64b9hLvfRK268BlNu4P37KDFE6LT0QzjG7brqzFJf3ZaadDCKeIw1q7DWQs=", - "subType": "06" - } - } - }, - "local_binData=04_rand_explicit_id": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAF7XgMgKjQmWYWmobrYWKiGYCKsy5kTgVweFBuzvFISaZjFsq2hrZB2DwUaOeT6XUPH/Onrdjc3fNElf3FdQDHif4rt+1lh9jEX+nMbRw9i3s=", - "subType": "06" - } - } - }, - "local_binData=04_rand_explicit_altname": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAFGoA/1H0waFLor6LbkUCLC2Wm9j/ZT7yifPbf0G7WvO0+gBLlffr3aJIQ9ik5vxPbmDDMCoYlbEYgb8i9I5tKC17WPhjVH2N2+4l9y7aEmS4=", - "subType": "06" - } - } - }, - "local_binData=04_det_auto_id": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", - "subType": "06" - } - } - }, - "local_binData=04_det_explicit_id": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", - "subType": "06" - } - } - }, - "local_binData=04_det_explicit_altname": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAFwO3hsD8ee/uwgUiHWem8fGe54LsTJWqgbRCacIe6sxrsyLT6EsVIqg4Sn7Ou+FC3WJbFld5kx8euLe/MHa8FGYjxD97z5j+rUx5tt3T6YbA=", - "subType": "06" - } - } - }, - "local_undefined_rand_explicit_id": { - "kms": "local", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$undefined": true } - }, - "local_undefined_rand_explicit_altname": { - "kms": "local", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$undefined": true } - }, - "local_undefined_det_explicit_id": { - "kms": "local", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$undefined": true } - }, - "local_undefined_det_explicit_altname": { - "kms": "local", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$undefined": true } - }, - "local_objectId_rand_auto_id": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAHfvxWRZOzfao3faE3RglL0IcDpBcNwqiGL5KgSokmRxWjjWeiel88Mbo5Plo0SswwNQ2H7C5GVG21L+UbvcW63g==", - "subType": "06" - } - } - }, - "local_objectId_rand_auto_altname": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAHhd9lSOO7bHE7PM+Uxa2v3X1FF66IwyEr0wqnyTaOM+cHQLmec/RlEaRIQ1x2AiW7LwmmVgZ0xBMK9CMh0Lhbyw==", - "subType": "06" - } - } - }, - "local_objectId_rand_explicit_id": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAHETwT9bo+JtboBVW/8GzzMQCpn22iiNJnlxYfyO45jvYJQRs29RRIouCsnFkmC7cfAO3GlVxv113euYjIO7AlAg==", - "subType": "06" - } - } - }, - "local_objectId_rand_explicit_altname": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAHhsguAMBzQUFBAitpJDzKEaMDGUGfvCzmUUhf4rnp8xeall/p91TUudaSMcU11XEgJ0Mym4IbYRd8+TfUai0nvw==", - "subType": "06" - } - } - }, - "local_objectId_det_auto_id": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", - "subType": "06" - } - } - }, - "local_objectId_det_explicit_id": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", - "subType": "06" - } - } - }, - "local_objectId_det_explicit_altname": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAH4ElF4AvQ+kkGfhadgKNy3GcYrDZPN6RpzaMYIhcCGDvC9W+cIS9dH1aJbPU7vTPmEZnnynPTDWjw3rAj2+9mOA==", - "subType": "06" - } - } - }, - "local_bool_rand_auto_id": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAIxGld4J/2vSWg5tjQulpkm9C6WeUcLbv2yfKRXPAbmLpv3u4Yrmr5qisJtqmDPTcb993WosvCYAh0UGW+zpsdEg==", - "subType": "06" - } - } - }, - "local_bool_rand_auto_altname": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAIpUFPiS2uoW1Aqs0WQkBa201OBmsuJ8WUKcv5aBPASkcwfaw9qSWs3QrbEDR2GyoU4SeYOByCAQMzXCPoIYAFdQ==", - "subType": "06" - } - } - }, - "local_bool_rand_explicit_id": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAIJuzu1a60meYlU3LMjw/7G4Vh/lqKopxdpGWoLXEmY/NoHgX6Fkv9iTwxv/Nv8rZwtawpFV+mQUG/6A1IHMBASQ==", - "subType": "06" - } - } - }, - "local_bool_rand_explicit_altname": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAIn9VjxL5TdGgJLckNHRrIaL32L31q5OERRZG2M5OYKk66TnrlfEs+ykcDvGwMGKpr/PYjY5kBHDc/oELGJJbWRQ==", - "subType": "06" - } - } - }, - "local_bool_det_explicit_id": { - "kms": "local", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "local_bool_det_explicit_altname": { - "kms": "local", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "local_date_rand_auto_id": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAJPPv4MC5xzt2uxPGBHH9g2z03o9SQjjmuxt97Ub1UcKCCHsGED3bx6YSrocuEMiFFI4d5Fqgl8HNeS4j0PR0tYA==", - "subType": "06" - } - } - }, - "local_date_rand_auto_altname": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAJ6i2A9Hi4xWlOMjFMGpwaRctR1VFnb4El166n18RvjKic46V+WoadvLHS32RhPOvkLVYwIeU4C+vrO5isBNoUdw==", - "subType": "06" - } - } - }, - "local_date_rand_explicit_id": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAJHcniV7Q0C8ZTWrE0hp5i5bUPlrrRdNLZckfODw8XNVtVPDjbznglccQmI7w1t8kOVp65eKzVzUOXN0YkqA+1QA==", - "subType": "06" - } - } - }, - "local_date_rand_explicit_altname": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAJKCUCjC3hsmEKKYwGP3ceh3zR+ArE8LYFOQfN87aEsTr60VrzHXmsE8PvizRhhMnrp07ljzQkuat39L+0QSR2qQ==", - "subType": "06" - } - } - }, - "local_date_det_auto_id": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", - "subType": "06" - } - } - }, - "local_date_det_explicit_id": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", - "subType": "06" - } - } - }, - "local_date_det_explicit_altname": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAJ1GMYQTruoKr6fv9XCbcVkx/3yivymPSMEkPCRDYxQv45w4TqBKMDfpRd1TOLOv1qvcb+gjH+z5IfVBMp2IpG/Q==", - "subType": "06" - } - } - }, - "local_null_rand_explicit_id": { - "kms": "local", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "local_null_rand_explicit_altname": { - "kms": "local", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "local_null_det_explicit_id": { - "kms": "local", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "local_null_det_explicit_altname": { - "kms": "local", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "local_regex_rand_auto_id": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAALXKw7zSgqQj1AKoWO0MoMxsBuu0cMB6KdJQCRKdupoLV/Y22owwsVpDDMv5sgUpkG5YIV+Fz7taHodXE07qHopw==", - "subType": "06" - } - } - }, - "local_regex_rand_auto_altname": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAALntOLXq7VW1+jwba/dSbidMo2bewNo7AtK9A1CPwk9XrjUQaEOQxfRpho3BYQEo2U67fQdsY/tyhaj4jduHn9JQ==", - "subType": "06" - } - } - }, - "local_regex_rand_explicit_id": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAALlMMG2iS/gEOEsVKR7sxBJP2IUzZ+aRbozDSkqADncresBvaPBSE17lng5NG7H1JRCAcP1rH/Te+0CrMd7JpRAQ==", - "subType": "06" - } - } - }, - "local_regex_rand_explicit_altname": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAL1YNnlVu5+njDLxh1LMhIPOH19RykAXhxrUbCy6TI5MLQsAOSgAJbXOTXeKr0D8/Ff0phToWOKl193gOOIp8yZQ==", - "subType": "06" - } - } - }, - "local_regex_det_auto_id": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", - "subType": "06" - } - } - }, - "local_regex_det_explicit_id": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", - "subType": "06" - } - } - }, - "local_regex_det_explicit_altname": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAALiZbL5nFIZl7cSLH5E3wK3jJeAeFc7hLHNITtLAu+o10raEs5i/UCihMHmkf8KHZxghs056pfm5BjPzlL9x7IHQ==", - "subType": "06" - } - } - }, - "local_dbPointer_rand_auto_id": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAMUdAA9uOSk1tXJVe/CG3Ps6avYTEF1eHj1wSlCHkFxqlMtTO+rIQpikpjH0MrcXvEEdAO8g5hFZ01I7DWyK5AAxTxDqVF+kOaQ2VfKs6hyuo=", - "subType": "06" - } - } - }, - "local_dbPointer_rand_auto_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAMiNqvqLwZrPnsF235z+Obl1K9iEXdJ5GucMGpJdRG4lRvRE0Oy1vh6ztNTpYPY/tXyUFTBWlzl/lITalSEm/dT1Bnlh0iPAFrAiNySf662og=", - "subType": "06" - } - } - }, - "local_dbPointer_rand_explicit_id": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAM+Tn31YcKiowBTJWRYCYAEO7UARDE2/jTVGEKXCpiwEqqP3JSAS0b80zYt8dxo5mVhUo2a02ClKrB8vs+B6sU1kXrahSaVSEHZlRSGN9fWgo=", - "subType": "06" - } - } - }, - "local_dbPointer_rand_explicit_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAMdOZZUvpJIqG9qiOLy5x4BdftyHipPDZn/eeLEc7ir3v4jJsY3dsv6fQERo5U9lMynNGA9PJePVzq5tWsIMX0EcCQcMfGmosfkYDzN1OX99A=", - "subType": "06" - } - } - }, - "local_dbPointer_det_auto_id": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", - "subType": "06" - } - } - }, - "local_dbPointer_det_explicit_id": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", - "subType": "06" - } - } - }, - "local_dbPointer_det_explicit_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAMQWace2C1w3yqtmo/rgz3YtIDnx1Ia/oDsoHnnMZlEy5RoK3uosi1hvNAZCSg3Sen0H7MH3XVhGGMCL4cS69uJ0ENSvh+K6fiZzAXCKUPfvM=", - "subType": "06" - } - } - }, - "local_javascript_rand_auto_id": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAANNL2AMKwTDyMIvxLKhBxZKx50C0tBdkLwuXmuMcrUqZeH8bsvjtttoM9LWkkileMyeTWgxblJ1b+uQ+V+4VT6fA==", - "subType": "06" - } - } - }, - "local_javascript_rand_auto_altname": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAANBjBlHGw3K3TWQHpvfa1z0bKhNnVFC/lZArIexo3wjdGq3MdkGA5cuBIp87HHmOIv6o/pvQ9K74v48RQl+JH44A==", - "subType": "06" - } - } - }, - "local_javascript_rand_explicit_id": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAANjvM7u3vNVyKpyI7g5kbzBpHPzXzOQToDSng5/c9yjMG+qi4TPtOyassobJOnMmDYBLyqRXCl/GsDLprbg5jxuA==", - "subType": "06" - } - } - }, - "local_javascript_rand_explicit_altname": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAANMtO7KneuVx4gSOjX4MQjKL80zJhnt+efDBylkpNsqKyxBXB60nkiredGzwaK3/4QhIfGJrC1fQpwUwu/v1L17g==", - "subType": "06" - } - } - }, - "local_javascript_det_auto_id": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", - "subType": "06" - } - } - }, - "local_javascript_det_explicit_id": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", - "subType": "06" - } - } - }, - "local_javascript_det_explicit_altname": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAANmQsg9E/BzGJVNVhSNyunS/TH0332oVFdPS6gjX0Cp/JC0YhB97DLz3N4e/q8ECaz7tTdQt9JacNUgxo+YCULUA==", - "subType": "06" - } - } - }, - "local_symbol_rand_auto_id": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAOOuO2b23mekwI8b6gWeEgRy1lLOCsNyBKvdmizK7/oOVKCvd+3kwUn9a6TxygooiVAN/Aohr1cjb8jRlMPWpkP0iO0+Tt6+vkizgFsQW4iio=", - "subType": "06" - } - } - }, - "local_symbol_rand_auto_altname": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAOhN4QPOcmGnFKGvTfhz6TQleDA02X6oWULLHTnOUJYfE3OUSyf2ULEQh1yhdKdwXMuYVgGl28pMosiwkBShrXYe5ZlMjiZCIMZWSdUMV0tXk=", - "subType": "06" - } - } - }, - "local_symbol_rand_explicit_id": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAO9aWi9RliwQHdXHoJME9VyN6XgyGd95Eclx+ZFYfLxBGAuUnPNjSfVuNZwYdyKC8JX79+mYhk7IXmcGV4z+4486sxyLk3idi4Kmpz2ESqV5g=", - "subType": "06" - } - } - }, - "local_symbol_rand_explicit_altname": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAO/qev3DPfpkQoSW9aHOyalwfI/VYDQVN5VMINx4kw2vEqHiI1HRdZRPOz3q74TlQEy3TMNMTYdCvh5bpN/PptRZCTQbzP6ugz9dTp79w5/Ok=", - "subType": "06" - } - } - }, - "local_symbol_det_auto_id": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", - "subType": "06" - } - } - }, - "local_symbol_det_explicit_id": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", - "subType": "06" - } - } - }, - "local_symbol_det_explicit_altname": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAOsg5cs6VpZWoTOFg4ztZmpj8kSTeCArVcI1Zz2pOnmMqNv/vcKQGhKSBbfniMripr7iuiYtlgkHGsdO2FqUp6Jb8NEWm5uWqdNU21zR9SRkE=", - "subType": "06" - } - } - }, - "local_javascriptWithScope_rand_auto_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAP5gLMvLOAc6vGAvC7bGmEC4eweptAiX3A7L0iCoHps/wm0FBLkfpF6F4pCjVYiY1lTID38wliRLPyhntCj+cfvlMfKSjouNgXMIWyQ8GKZ2c=", - "subType": "06" - } - } - }, - "local_javascriptWithScope_rand_auto_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAPVsw9Opn/P5SAdJhX4MTxIcsmaG8isIN4NKPi9k1u/Vj7AVkcxYqwurAghaJpmfoAgMruvzi1hcKvd05yHd9Nk0vkvODwDgnjJB6QO+qUce8=", - "subType": "06" - } - } - }, - "local_javascriptWithScope_rand_explicit_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAPLUa+nsrqiHkVdE5K1xl/ZsiZqQznG2yVXyA3b3loBylbcL2NEBp1JUeGnPZ0y5ZK4AmoL6NMH2Io313rW3V8FTArs/OOQWPRJSe6h0M3wXk=", - "subType": "06" - } - } - }, - "local_javascriptWithScope_rand_explicit_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAPzUKaXCH0JImSlY73HVop9g9c0YssNEiA7Dy7Vji61avxvnuJJfghDchdwwaY7Vc8+0bymoanUWcErRctLzjm+1uKeMnFQokR8wFtnS3PgpQ=", - "subType": "06" - } - } - }, - "local_javascriptWithScope_det_explicit_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$code": "x=1", "$scope": {} } - }, - "local_javascriptWithScope_det_explicit_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$code": "x=1", "$scope": {} } - }, - "local_int_rand_auto_id": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAQHXpXb3KlHA2KFTBgl0VoLCu0CUf1ae4DckkwDorbredVSqxvA5e+NvVudY5yuea6bC9F57JlbjI8NWYAUw4q0Q==", - "subType": "06" - } - } - }, - "local_int_rand_auto_altname": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAQSxXMF4+TKV+a3lcxXky8VepEqdg5wI/jg+C4CAUgNurq2XhgrxyqiMjkU8z07tfyoLYyX6P+dTrwj6nzvvchCw==", - "subType": "06" - } - } - }, - "local_int_rand_explicit_id": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAQmzteYnshCI8HBGd7UYUKvcg4xl6M8PRyi1xX/WHbjyQkAJXxczS8hO91wuqStE3tBNSmulUejz9S691ufTd6ZA==", - "subType": "06" - } - } - }, - "local_int_rand_explicit_altname": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAQLCHLru//++QSoWVEyw2v6TUfCnlrPJXrpLLezWf16vK85jTfm8vJbb2X2UzX04wGzVL9tCFFsWX6Z5gHXhgSBg==", - "subType": "06" - } - } - }, - "local_int_det_auto_id": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", - "subType": "06" - } - } - }, - "local_int_det_explicit_id": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", - "subType": "06" - } - } - }, - "local_int_det_explicit_altname": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAQIxWjLBromNUgiOoeoZ4RUJUYIfhfOmab0sa4qYlS9bgYI41FU6BtzaOevR16O9i+uACbiHL0X6FMXKjOmiRAug==", - "subType": "06" - } - } - }, - "local_timestamp_rand_auto_id": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAARntIycg0Xkd16GEa//VSJI4Rkl7dT6MpRa+D3MiTEeio5Yy8zGK0u2BtEP/9MCRQw2hJDYj5znVqwhdduM0OTiA==", - "subType": "06" - } - } - }, - "local_timestamp_rand_auto_altname": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAARWA9Ox5ejDPeWxfjbRgcGCtF/G5yrPMbBJD9ESDFc0NaVe0sdNNTisEVxsSkn7M/S4FCibKh+C8femr7xhu1iTw==", - "subType": "06" - } - } - }, - "local_timestamp_rand_explicit_id": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAARrEfOL4+4Qh7IkhHnHcBEANGfMF8n2wUDnsZ0lXEb0fACKzaN5OKaxMIQBs/3pFBw721qRfCHY+ByKeaQuABbzg==", - "subType": "06" - } - } - }, - "local_timestamp_rand_explicit_altname": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAARW8nwmnBt+LFIAcFWvOzX8llrGcveQKFhyYUIth9d7wtpTyc9myFp8GBQCnjDpKzA6lPmbqVYeLU0L9q0h6SHGQ==", - "subType": "06" - } - } - }, - "local_timestamp_det_auto_id": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", - "subType": "06" - } - } - }, - "local_timestamp_det_explicit_id": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", - "subType": "06" - } - } - }, - "local_timestamp_det_explicit_altname": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAR6uMylGytMq8QDr5Yz3w9HlW2MkGt6yIgUKcXYSaXru8eer+EkLv66/vy5rHqTfV0+8ryoi+d+PWO5U6b3Ng5Gg==", - "subType": "06" - } - } - }, - "local_long_rand_auto_id": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAASrinKUOpHIB7MNRmCAPWcP4CjZwfr5JaRT3G/GqY9B/6csj3+N9jmo1fYvM8uHcnmf5hzDDOamaE2FF1jDKkrHw==", - "subType": "06" - } - } - }, - "local_long_rand_auto_altname": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAShWMPYDkCpTC2XLYyykPJMihASLKn6HHcB2Eh7jFwQb/8D1HCQoPmOHMyXaN4AtIKm1oqEfma6FSnEPENQoledQ==", - "subType": "06" - } - } - }, - "local_long_rand_explicit_id": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAASd2h34ZLib+GiYayrm/FIZ/weg8wF41T0PfF8NCLTJCoT7gIkdpNRz2zkkQgZMR31efNKtsM8Bs4wgZbkrXsXWg==", - "subType": "06" - } - } - }, - "local_long_rand_explicit_altname": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAASPAvdjz+a3FvXqDSjazaGqwZxrfXlfFB5/VjQFXQB0gpodCEaz1qaLSKfCWBg83ftrYKa/1sa44gU5NBthDfDwQ==", - "subType": "06" - } - } - }, - "local_long_det_auto_id": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", - "subType": "06" - } - } - }, - "local_long_det_explicit_id": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", - "subType": "06" - } - } - }, - "local_long_det_explicit_altname": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAASQk372m/hW3WX82/GH+ikPv3QUwK7Hh/RBpAguiNxMdNhkgA/y2gznVNm17t6djyub7+d5zN4P5PLS/EOm2kjtw==", - "subType": "06" - } - } - }, - "local_decimal_rand_auto_id": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAATLnMMDZhnGSn5F5xHhsJXxiTGXd61Eq6fgppOlxUNVlsZNYyr5tZ3owfTTqRuD9yRg97x65WiHewBBnJJSeirCTAy9zZxWPVlJSiC0gO7rbM=", - "subType": "06" - } - } - }, - "local_decimal_rand_auto_altname": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAATenMh7NKQioGjpuEojIrYKFaJhbuGxUgu2yTTbe3TndhgHryhW9GXiUqo8WTpnXqpC5E/z03ZYLWfCbe7qGdL6T7bbrTpaTaWZnnAm3XaCqY=", - "subType": "06" - } - } - }, - "local_decimal_rand_explicit_id": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAT9vqXuKRh+2HxeCMr+pQYdhYNw7xrTdU4dySWz0X6tCK7LZO5AV72utmRJxID7Bqv1ZlXAk00V92oDLyKG9kHeG5+S34QE/aLCPsAWcppfxY=", - "subType": "06" - } - } - }, - "local_decimal_rand_explicit_altname": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAATtqOCFMbOkls3LikQNXlnlkRr5gJns1+5Kvbt7P7texMa/QlXkYSHhtwESyfOcCQ2sw1T0eZ9DDuNaznpdK2KIqZBkVEC9iMoxqIqXF7Nab0=", - "subType": "06" - } - } - }, - "local_decimal_det_explicit_id": { - "kms": "local", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$numberDecimal": "1.234" } - }, - "local_decimal_det_explicit_altname": { - "kms": "local", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$numberDecimal": "1.234" } - }, - "local_minKey_rand_explicit_id": { - "kms": "local", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$minKey": 1 } - }, - "local_minKey_rand_explicit_altname": { - "kms": "local", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$minKey": 1 } - }, - "local_minKey_det_explicit_id": { - "kms": "local", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$minKey": 1 } - }, - "local_minKey_det_explicit_altname": { - "kms": "local", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$minKey": 1 } - }, - "local_maxKey_rand_explicit_id": { - "kms": "local", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "local_maxKey_rand_explicit_altname": { - "kms": "local", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "local_maxKey_det_explicit_id": { - "kms": "local", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "local_maxKey_det_explicit_altname": { - "kms": "local", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { "$maxKey": 1 } - }, - "payload=0,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACcsBdT93ivCyvtkfQz9qb1A9Ll+I6hnGE0kFy3rmVG6xAvipmRJSoVq3iv7iUEDvaqmPXfjeH8h8cPYT86v3XSg==", - "subType": "06" - } - } - }, - "payload=1,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACQOzpNBEGSrANr3Wl8uYpqeIc7pjc8e2LS2FaSrb8tM9F3mR1FqGgfJtn3eD+HZf3Y3WEDGK8975a/1BufkMqIQ==", - "subType": "06" - } - } - }, - "payload=2,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACyGJEcuN1pG5oSEyxuKFwqddGHVU5Untbib7LkmtoJe9HngTofkOpeHZH/hV6Z3CFxLu6WFliJoySsFFbnFy9ag==", - "subType": "06" - } - } - }, - "payload=3,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACLbp4w6mx45lR1vvgmeRja/y8U+WnR2oH4IpfrDi4lKM+JPVnJweiN3/1wAy+sXSy0S1Yh9yxmhh9ISoTkAuVxw==", - "subType": "06" - } - } - }, - "payload=4,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACG0qMY/GPZ/2CR61cxbuizywefyMZVdeTCn5KFjqwejgxeBwX0JmGNHKKWbQIDQykRFv0q0WHUgsRmRhaotNCyQ==", - "subType": "06" - } - } - }, - "payload=5,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACJI1onNpQfZhaYWrPEzHvNaJRqUDZK2xoyonB5E473BPgp3zvn0Jmz1deL8GzS+HlkjCrx39OvHyVt3+3S0kYYw==", - "subType": "06" - } - } - }, - "payload=6,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAClyKY9tZBjl7SewSXr3MdoWRDUNgLaXDUjENpjyYvi/54EQ9a+J/LAAh1892i+mLpYxEUAmcftPyfX3VhbCgUQw==", - "subType": "06" - } - } - }, - "payload=7,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACAMbEA+kNvnVV7B//ds2/QoVot061kbazoMwB/psB5eFdLVB5qApAXEWgQEMwkNnsTUYbtSduQz6uGwdagtNBRw==", - "subType": "06" - } - } - }, - "payload=8,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACzdSK/d7Ni6D8qUgNopnEU5ia1K5llhBGk3O1Tf71t4ThnQjYW9eI/rIohWmev5CGWLHhwuvvKUtFcTAe+NMQww==", - "subType": "06" - } - } - }, - "payload=9,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACzQcEa+ktF2EZf35TtyatnSGGaIVvFhZNuo5P3VwQvoONJrK2cSad7PBDAv3xDAB+VPZAigXAGQvd051sHooOHg==", - "subType": "06" - } - } - }, - "payload=10,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACpfoDmApsR5xOD3TDhcHeD7Jco3kPFuuWjDpHtMepMOJ3S0c+ngGGhzPGZtEz2xuD/E7AQn1ryp/WAQ+WwkaJkQ==", - "subType": "06" - } - } - }, - "payload=11,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACICMRXmx3oKqYv0IpmzkSMBIGT4Li3MPBF4Lw1s5F69WvZApD58glIKB6b7koIrF5qc2Wrb1/Nw+stRv0zvQ8Y9CcFV4OHm6WoEw+XDlWXJ4=", - "subType": "06" - } - } - }, - "payload=12,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACTArUn0WUTojQC4fSvq3TwJVTsZNhWAK2WB057u2EnkUzMC0xsbU6611W6Okx6idZ7pMudXpBC34fRDrJPXOu3BxK+ZLCOWS2FqsvWq3HeTY=", - "subType": "06" - } - } - }, - "payload=13,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACU1Ojn7EM2i+5KK2Beh1gPLhryK3Y7PtaZ/v4JvstxuAV4OHOR9yROP7pwenHXxczkWXvcyMY9OCdmHO8pkQkXO21798IPkDDN/ejJUFI0Uw=", - "subType": "06" - } - } - }, - "payload=14,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAAC0ZLwSliCbcr/e1uiYWk6gRuD/5qiyulQ7IUNWjhpBR6SLUfX2+yExLzps9hoOp53j9zRSKIzyleZ8yGLTLeN+Lz9BUe2ZT+sV8NiqZz3pkA=", - "subType": "06" - } - } - }, - "payload=15,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACQ9pmlQeFDr+jEhFwjL/eGVxdv70JdnkLaKdJ3/jkvCX1VPU5HmQIi+JWY3Rrw844E/6sBR6zIODn5aM0WfyP8a2zKRAWaVQZ7n+QE9hDN/8=", - "subType": "06" - } - } - }, - "payload=16,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AizggCwAAAAAAAAAAAAAAAACiOcItInDGHqvkH0I3udp5nnX32XzDeqya/3KDjgZPT5GHek1vFTZ4924JVxFqFQz+No9rOVmyxm8O2fxjTK2vsjtADzKGnMTtFYZqghYCuc=", - "subType": "06" - } - } - }, - "payload=0,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACijFptWQy7a1Y0rpXEvamXWI9v9dnx0Qj84/mKUsVpc3agkQ0B04uPYeROdt2MeEeiZoEKVWV0NjBocAQCEz7dw==", - "subType": "06" - } - } - }, - "payload=1,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAChR90taVWsZk+++sgibX6CnFeQQHNoB8V+n2gmDe3CIT/t+WvhMf9D+mQipbAlrUyHgGihKMHcvAZ5RZ/spaH4Q==", - "subType": "06" - } - } - }, - "payload=2,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAC67wemDv1Xdu7+EMR9LMBTOxfyAqsGaxQibwamZItzplslL/Dp3t9g9vPuNzq0dWwhnfxQ9GBe8OA3dtRaifYCA==", - "subType": "06" - } - } - }, - "payload=3,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACVLxch+uC7weXrbtylCo1m4HYZmh0sd9JCrlTECO2M56JK1X9a30i2BDUdhPuoTvvODv74CGXkZKdist3o0mGAQ==", - "subType": "06" - } - } - }, - "payload=4,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACexfIZGkOYaCGktOUc6cgAYg7Bd/C5ZYmdb7b8+rd5BKWbthW6N6CxhDIyh/DHvkPAeIzfTYA2/9w6tsjfD/TPQ==", - "subType": "06" - } - } - }, - "payload=5,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACjUH/dPW4egOvFMJJnpWK8v27MeLkbXC4GFl1j+wPqTsIEeIWkzEmcXjHLTQGE2GplHHc/zxwRwD2dXdbzvsCDw==", - "subType": "06" - } - } - }, - "payload=6,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACzvS+QkGlvb05pNn+vBMml09yKmE8yM6lwccNIST5uZSsUxXf2hrxPtO7Ylc4lmBAJt/9bcM59JIeT9fpYMc75w==", - "subType": "06" - } - } - }, - "payload=7,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACSf2RxHJpRuh4j8nS1dfonUtsJEwgqfWrwOsfuT/tAGXgDN0ObUpzL2K7G2vmePjP4dwycCSIL3+2j34bqBJK1Q==", - "subType": "06" - } - } - }, - "payload=8,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACu96YYeLXXoYdEZYNU9UAZjSd6G4fOE1edrA6/RjZKVGWKxftmvj5g1VAOiom0XuTZUe1ihbnwhvKexeoa3Vc8Q==", - "subType": "06" - } - } - }, - "payload=9,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACX+UjBKo9+N0Z+mbyqZqkQv2ETMSn6aPTONWgJtw5nWklcxKjUSSLI+8LW/6M6Xf9a7177GsqmV2f/yCRF58Xtw==", - "subType": "06" - } - } - }, - "payload=10,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACL6TVscFzIJ9+Zj6LsCZ9xhaZuTZdvz1nJe4l69nKyj9hCjnyuiV6Ve4AXwQ5W1wiPfkJ0fCZS33NwiHw7QQ/vg==", - "subType": "06" - } - } - }, - "payload=11,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACPLq7IcWhTVwkKmy0flN7opoQzx7tTe1eD9JIc25FC9B6KGQkdcRDglDDR7/m6+kBtTnq88y63vBgomTxA8ZxQE+3pB7zCiBhX0QznuXvP44=", - "subType": "06" - } - } - }, - "payload=12,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACxv7v4pKtom5z1g9FUuyjEWAbdzJ3ytPNZlOfVr6KZnUPhIH7PfCz3/lTdYYWBTj01+SUZiC/7ruof9QDhsSiNWP7nUyHpQ/C3joI/BBjtDA=", - "subType": "06" - } - } - }, - "payload=13,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACZhiElQ/MvyVMwMkZPu8pT54Ap6TlpVSEbE4nIQzzeU3XKVuspMdI5IXvvgfULXKXc+AOu6oQXZ+wAJ1tErVOsb48HF1g0wbXbBA31C5qLEM=", - "subType": "06" - } - } - }, - "payload=14,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACdp8mDOeDuDLhE0LzTOT2p0CMaUsAQrGCzmiK6Ab9xvaIcPPcejUcpdO3XXAS/pPab4+TUwO5GbI5pDJ29zwaOiOz2H3OJ2m2p5BHQp9mCys=", - "subType": "06" - } - } - }, - "payload=15,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAACmtLohoP/gotuon2IvnGeLEfCWHRMhG9Wp4tPu/vbJJkJkbQTP35HRG9VrMV7KKrEQbOsJ2Y6UDBra4tyjn0fIkwwc/0X9i+xaP+TrwpNabE=", - "subType": "06" - } - } - }, - "payload=16,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASzggCwAAAAAAAAAAAAAAAAC6s9eUtSneKWj3/A7S+bPZLj3t1WtUh7ltW80b8jCRzA+kOI26j1MEb1tt68HgcnH1IJ3YQ/+UHlV95OgwSnIxlib/HJn3U0s8mpuCWe1Auo=", - "subType": "06" - } - } - }, - "azure_double_rand_auto_id": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAB0S2kOZe54q6iZqeTLndkX+kehTKtb30jTP7FS+Zx+cxhFs626OrGY+jrH41cLfroCccacyNHUZFRinfqZPNOyw==", - "subType": "06" - } - } - }, - "azure_double_rand_auto_altname": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAABYViH7PLjCIdmTibW9dGCJADwXx2dRSMYxEmulPu89clAoeLDa8pwJ7YxLFQCcTGmZRfmp58dDDAzV8tyyE8QMg==", - "subType": "06" - } - } - }, - "azure_double_rand_explicit_id": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAABeRahSj4pniBp0rLIEZE8MdeyiIKcYuTZiuGzGiXbFbntEPow88DFHIBSxbMGR7p/8jCpPL+GqBwFkPkafXbMzg==", - "subType": "06" - } - } - }, - "azure_double_rand_explicit_altname": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAABdaa3vKtO4cAEUjYJfOPl1KbbgeWtphfUuJd6MxR9VReNSf1jc+kONwmkPVQs2WyZ1n+TSQMGRoBp1nHRttDdTg==", - "subType": "06" - } - } - }, - "azure_double_det_explicit_id": { - "kms": "azure", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "azure_double_det_explicit_altname": { - "kms": "azure", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "azure_string_rand_auto_id": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAACeoztcDg9oZ7ixHinReWQTrAumpsfyb0E1s3BGOFHgBCi1tW79CEXfqN8riFRc1YeRTlN4k5ShgHaBWBlax+XoQ==", - "subType": "06" - } - } - }, - "azure_string_rand_auto_altname": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAACov9cXQvDHeKOS5Gxcxa8vdAcTsTXDYgUucGzsCyh4TnTWKGQEVk3DHndUXX569TKCjq5QsC//oWEwweCn1nZ4g==", - "subType": "06" - } - } - }, - "azure_string_rand_explicit_id": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAACKU5qTdMdO0buQ/37ZRANUAAafcsoNMOTxJsDOfkqUb+/kRgM1ePlwVvk4EJiAGhJ/4SEmEOpwv05TT3PxGur2Q==", - "subType": "06" - } - } - }, - "azure_string_rand_explicit_altname": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAACX/ODKGHUyAKxoJ/c/3lEDBTc+eP/VS8OHrLhYoP96McpnFSgYi5jfUwvrFYa715fkass4N0nAHE6TzoGTYyk6Q==", - "subType": "06" - } - } - }, - "azure_string_det_auto_id": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", - "subType": "06" - } - } - }, - "azure_string_det_explicit_id": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", - "subType": "06" - } - } - }, - "azure_string_det_explicit_altname": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAACmVI7YK4JLOzutEdQ79he817Vk5EDP/3hXwOlGmERZCtp8J8HcqClhV+pyvRLGbwmlh12fbSs9nEp7mrobQm9wA==", - "subType": "06" - } - } - }, - "azure_object_rand_auto_id": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAADWkZMsfCo4dOPMH1RXC7GkZFt1RCjJf0vaLDA09ih1Jl47SOetZELQ7B1TQjRQitktzrfD43jk8Fn4J5ZYZu1qQ==", - "subType": "06" - } - } - }, - "azure_object_rand_auto_altname": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAADJFMymfstltZP1oAqj4bgbCk8uLGtCd12eLqvSq0ZO+JDvls7PAovwmoWwigHunP8BBXT8sLydK+jn1sHfnhrlw==", - "subType": "06" - } - } - }, - "azure_object_rand_explicit_id": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAADCen+XrLYKg7gIVubVfdbQwuJ0mFHxhSUUyyBWj4RCeLeLUYXckboPGixXWB9XdwcOnInfF9u6qvktY67GtYASQ==", - "subType": "06" - } - } - }, - "azure_object_rand_explicit_altname": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAADnUyp/7eLmxxxOdsP+mNuJABK4PQoKFWDAY7lDrH6MYa03ryASOihPZWYZWXZLrbAf7cQQhElEkKqKwY8+NXgqg==", - "subType": "06" - } - } - }, - "azure_object_det_explicit_id": { - "kms": "azure", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_det_explicit_altname": { - "kms": "azure", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_array_rand_auto_id": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAEtk14WyoatZcNPlg3y/XJNsBt6neFJeQwR06B9rMGV58oIsmeE5zMtUOBYTgzlnwyKpqI/XVAg8s1VxvsrvGCyLVPwGVyDztwtMgVSW6QM3s=", - "subType": "06" - } - } - }, - "azure_array_rand_auto_altname": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAERTO63J4Nj1BpFlqVduA2IrAiGoV4jEOH3FnFgx7ZP7da/YBmLX/bc1EqdpC8v4faHxp74iU0xAB0yW4WgySDX7rriL5cw9sMpqgLRaBxGug=", - "subType": "06" - } - } - }, - "azure_array_rand_explicit_id": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAEs09qQdNVwh+KFqKPREQkw0XFdRNHAvjYJzs5MDE9+QxvtKlmVKSK3wkxDdCrcH4r7ePV2nCy2h1IHYqaDnnt4s5dSawI2l88iTT+bBcCSrU=", - "subType": "06" - } - } - }, - "azure_array_rand_explicit_altname": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAEaQ/YL50up4YIMJuVJSiAP06IQ+YjdKLIfkN/prbOZMiXErcD1Vq1hwGhfGdpEsLVu8E7IhJb4wakVC/2dLZoRP95az6HqRRauNNZAIQMKfY=", - "subType": "06" - } - } - }, - "azure_array_det_explicit_id": { - "kms": "azure", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_det_explicit_altname": { - "kms": "azure", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_binData=00_rand_auto_id": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFl/leuLAHf1p6aRKHdFyN9FM6MW2XzBemql2xQgqkwJ6YOQXW6Pu/aI1scXVOrvrSu3+wBvByjHu++1AqFgzZRQ==", - "subType": "06" - } - } - }, - "azure_binData=00_rand_auto_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAF4Nq/LwyufT/mx0LtFSkupNHTuyjbr4yUy1N5/37XhkpqZ1e4sWCHGNaTDEm5+cvdnbqZ/MMkBv855dc8N7vnGA==", - "subType": "06" - } - } - }, - "azure_binData=00_rand_explicit_id": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFv1Kbv54uXJ76Ih63vtmszQtzkXqDlv8LDCFO3sjzu70+tgRXOhLm3J8uZpwoiNkgM6oNLn0en7tnEekYB9++CA==", - "subType": "06" - } - } - }, - "azure_binData=00_rand_explicit_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFgcYC1n7cGGXpv0qf1Kb8t9y/6kbhscGt2QJkQpAiqadFPPYDU/wwaKdDz94NpAHMZizUbhf9tvZ3UXl1bozhDA==", - "subType": "06" - } - } - }, - "azure_binData=00_det_auto_id": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", - "subType": "06" - } - } - }, - "azure_binData=00_det_explicit_id": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", - "subType": "06" - } - } - }, - "azure_binData=00_det_explicit_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFvswfP3+jgia6rAyrypvbso3Xm4d7MEgJRUCWFYzA+9ov++vmeirgoTp/rFavTNOPb+61fvl1WKbVwrgODusaMg==", - "subType": "06" - } - } - }, - "azure_binData=04_rand_auto_id": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFMzMC3BLn/zWE9dxpcD8G0h4aifSY0zSHS9xTVJXgq21s2WU++Ov2UvHatVozmtZltsUN9JvSWqOBQRkFsrXvI7bc4lYfOoOmfpTHFcRDA/c=", - "subType": "06" - } - } - }, - "azure_binData=04_rand_auto_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFDlBN5hUTcjamOg/sgyeG0S52kphsjUgvlpuqHYz6VVdLtZ69cGHOVqqyml3x2rVqWUZJjd4ZodOhlwWq9p+i5IYNot2QaBvi8NZSaiThTc0=", - "subType": "06" - } - } - }, - "azure_binData=04_rand_explicit_id": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFjvS2ozJuAL3rCvyBpraVtgL91OMdiskmgYnyfKlzd8EhYLd1cL4yxnTUjRXx+W+p8uN0/QZo+mynhcWnwcq83raY+I1HftSTx+S6rZ0qyDM=", - "subType": "06" - } - } - }, - "azure_binData=04_rand_explicit_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAFqUMd/I0yOdy5W4THvFc6yrgSzB6arkRs/06b0M9Ii+QtAY6vbz+/aJ0Iy3Jm8TahC1wOZVmTj5luQpr+PHZMCEAFadv+0K/Nsx6xVhAh9gg=", - "subType": "06" - } - } - }, - "azure_binData=04_det_auto_id": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", - "subType": "06" - } - } - }, - "azure_binData=04_det_explicit_id": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", - "subType": "06" - } - } - }, - "azure_binData=04_det_explicit_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAFmN+KMrERGmfmue8/hG4D+ZcGzxC2HntdYBLjEolzvS9FV5JH/adxyUAnMpyL8FNznARL51rbv/G1nXPn9mPabsQ4BtWEAQbHx9TiXd+xbB0=", - "subType": "06" - } - } - }, - "azure_undefined_rand_explicit_id": { - "kms": "azure", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_rand_explicit_altname": { - "kms": "azure", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_det_explicit_id": { - "kms": "azure", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_det_explicit_altname": { - "kms": "azure", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_objectId_rand_auto_id": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAH3sYVJpCKi310YxndMwm5ltEbbiRO1RwZxxeEkzI8tptbNXC8t7RkrT8VSJZ43wbGYCiqH5RZy9v8pYwtUm4STw==", - "subType": "06" - } - } - }, - "azure_objectId_rand_auto_altname": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAHD7agzVEc0JwesHHhkpGYIDAHQ+3Hc691kqic6YmVvK2N45fD5aRKftaZNs5OxSj3tNHSo7lQ+DVtPj8uSSpsVg==", - "subType": "06" - } - } - }, - "azure_objectId_rand_explicit_id": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAHEgKgy2mpMLpfeEWqbvQOaRZAy+cEGXGon3e53/JoH6dZneEyyt4ZrcrK6uRqyUPWX0q104JbCYxfbtHtdzWgPQ==", - "subType": "06" - } - } - }, - "azure_objectId_rand_explicit_altname": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAHqSv6Nruw3TIi7y0FPRjSfnJmWSdv5XMhAtnHNkT8MVuHeM32ayo0yc8dTA1wlkRtAI5JrGxTfERCXYuCojvvXg==", - "subType": "06" - } - } - }, - "azure_objectId_det_auto_id": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", - "subType": "06" - } - } - }, - "azure_objectId_det_explicit_id": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", - "subType": "06" - } - } - }, - "azure_objectId_det_explicit_altname": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAHcPRjIOyLDUJCDcdWkUySKCFS2AFkIa1OQyQAfC3Zh5HwJ1O7j2o+iYKRerhbni8lBiZH7EUMm1JcxM99lLC5jQ==", - "subType": "06" - } - } - }, - "azure_bool_rand_auto_id": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAIYVWPvzSmiCs9LwRlv/AoQWhaS5mzoKX4W26M5eg/gPjOZbEVYOV80pWMxCcZWRAyV/NDWDUmKtRQDMU9b8lCJw==", - "subType": "06" - } - } - }, - "azure_bool_rand_auto_altname": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAIsAB01Ugqtw4T9SkuJBQN1y/ewpRAyz0vjFPdKI+jmPMmaXpMlXDJU8ZbTKm/nh6sjJCFcY5oZJ83ylbp2gHc6w==", - "subType": "06" - } - } - }, - "azure_bool_rand_explicit_id": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAIr8/qFd564X1mqHEhB0y7bzGFdrHuw+Gk45nXla3VvGHzeIJy6j2Wdl0uziWslMmBvNp8WweW+jQ6E2Fu7SiojQ==", - "subType": "06" - } - } - }, - "azure_bool_rand_explicit_altname": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAIWsca5FAnS2zhHnmKmexvvXMTgsZZ7uAFHnjQassUcay6mvIWH4hOnGiRxt5Zm0wO4S6cZq+PZrmEH5/n9rJcJQ==", - "subType": "06" - } - } - }, - "azure_bool_det_explicit_id": { - "kms": "azure", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "azure_bool_det_explicit_altname": { - "kms": "azure", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "azure_date_rand_auto_id": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAJwKo7XW5daIFlwY1mDAnJdHlcUgF+74oViL28hQGhde63pkPyyS6lPkYrc1gcCK5DL7PwsSX4Vb9SsNAG9860xw==", - "subType": "06" - } - } - }, - "azure_date_rand_auto_altname": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAJYZdWIqvqTztGKJkSASMEOjyrUFKnYql8fMIEzfEZWx2BYsIkxxOUUUCASg/Jsn09fTLVQ7yLD+LwycuI2uaXsw==", - "subType": "06" - } - } - }, - "azure_date_rand_explicit_id": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAJuWzKqi3KV8GbGGnT7i9N4BACUuNjt5AgKsjWIfrWRXK1+jRQFq0bYlVWaliT9CNIygL2aTF0H4eHl55PAI84MQ==", - "subType": "06" - } - } - }, - "azure_date_rand_explicit_altname": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAJ5JTtTuP4zTnEbaVlS/W59SrZ08LOC4ZIl+h+H4RnfHUfBXDwUou+APolVaYko+VZMKecrikdPeewgzWaqazJ1g==", - "subType": "06" - } - } - }, - "azure_date_det_auto_id": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", - "subType": "06" - } - } - }, - "azure_date_det_explicit_id": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", - "subType": "06" - } - } - }, - "azure_date_det_explicit_altname": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAJCREIp/SPolAZcVU1iOmaJaN2tFId5HhrjNmhp6xhA1AIPLnN+U7TAqesxFN7iebR9fXI5fZxYNgyWqQC1rqUJw==", - "subType": "06" - } - } - }, - "azure_null_rand_explicit_id": { - "kms": "azure", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "azure_null_rand_explicit_altname": { - "kms": "azure", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "azure_null_det_explicit_id": { - "kms": "azure", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "azure_null_det_explicit_altname": { - "kms": "azure", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "azure_regex_rand_auto_id": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAALsMm3W2ogEiI6m0l8dS5Xhqnw+vMBvN1EesOTqAZOk4tQleX6fWARwUUnjFxbuejU7ISb50fc/Ul+ntL9z/2nHQ==", - "subType": "06" - } - } - }, - "azure_regex_rand_auto_altname": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAALITQNQI0hfCeMTxH0Hce1Cf5tinQG+Bq8EolUACvxUUQcDqIXfFXn19tV/Qyj4lIdnnwh/18hiswgEpJRK7uLGw==", - "subType": "06" - } - } - }, - "azure_regex_rand_explicit_id": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAALw/1QI/bKeiGUrrtC+yXOTvxZ2mJjSelPPGOm1mge0ws8DsX0DPHmo6MjhnRO4u0c/LWiE3hwHG2rYjAFlFXZ5A==", - "subType": "06" - } - } - }, - "azure_regex_rand_explicit_altname": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAL6Sl58UfFCHCZzWIB4r19/ZjeSRAoWeTFCFedKiwyR8/xnL+8jzXK/9+vTIspP6j35lFapr+f4iBNB9WjdpYNKA==", - "subType": "06" - } - } - }, - "azure_regex_det_auto_id": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", - "subType": "06" - } - } - }, - "azure_regex_det_explicit_id": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", - "subType": "06" - } - } - }, - "azure_regex_det_explicit_altname": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAALxshM91Tsql/8kPe3dC16oP36XSUIN6godiRVIJLJ+NAwYtEkThthQsln7CrkIxIx6npN6A/hw1CBJERS/cqWhw==", - "subType": "06" - } - } - }, - "azure_dbPointer_rand_auto_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAMaAd1v/XCYM2Kzi/f4utR6aHOFORmzZ17EepEjkn5IeKshktUpPWjI/dBwSunn5Qxx2zI3nm06c3SDvp6tw8qb7u4qXjLQYhlsQ0bHvvm+vE=", - "subType": "06" - } - } - }, - "azure_dbPointer_rand_auto_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAM6VNjkN9bMIzfC7AX0ZhOEXPpyPE0nzYq3c5TNHrgeGWdZDR9GVdbO9t55zQrQJJ2Mmevh8c0WaAUV+YODv7ty6TDBsPbaKWWqMzu/v9RXHo=", - "subType": "06" - } - } - }, - "azure_dbPointer_rand_explicit_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAM66tywuMhwdyUjxfl7EOdKHNCLeIPnct3PgKrAKlOQFjiNQUIA2ShVy0qYpJcvvFsuQ5e8Bjr0IqeBc8mC7n4euRSM1UXpLqI5XHgXMMaYpI=", - "subType": "06" - } - } - }, - "azure_dbPointer_rand_explicit_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAMtPQEbZ4gWoSYjVZLd5X6j0XxutWY1Ecrys2ErKRgZaxP0uGe8uw0cnr2Z5PYylaYmsSicLwD1PwWY42PKmaGBDraHmdfqDOPvrNxhBrfU/E=", - "subType": "06" - } - } - }, - "azure_dbPointer_det_auto_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", - "subType": "06" - } - } - }, - "azure_dbPointer_det_explicit_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", - "subType": "06" - } - } - }, - "azure_dbPointer_det_explicit_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAMxUcVqq6RpAUCv08qGkmjuwVAIgLeYyh7xZnMeCYVGmhJKIP1Zdt1SvRGRV0jzwCQmXgxNd04adRwJnG/PRQIsL9aH3ilJgEnUbOo1nqR7yw=", - "subType": "06" - } - } - }, - "azure_javascript_rand_auto_id": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAANWXPb5z3a0S7F26vkmBF3fV+oXYUj15OEtnSlXlUrc+gbhbPDxSvCPnTBEy5sNu4ndkvEZZxYgZInkF2q4rhlfQ==", - "subType": "06" - } - } - }, - "azure_javascript_rand_auto_altname": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAANN4mcwLz/J4eOUknhVsy6kdF1ThDP8cx6dNpOwJWAiyPHEsn+i6JmMTlfQMBrUp9HB/u3R+jLO5yz4XgLUKE8Tw==", - "subType": "06" - } - } - }, - "azure_javascript_rand_explicit_id": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAANJ+t5Z8hSQaoNzszzkWndAo4A0avDf9bKFa7euznz8ZYInnl9RUVqWMyxjSuIotAvTyYSJzxh+w2hKCgVf+MjEA==", - "subType": "06" - } - } - }, - "azure_javascript_rand_explicit_altname": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAANRLOQFpmkEg/KdWMmaurkNtUhy45rgtoipc9kQz6olgDWiMim81XC0AW5cOvjbHXL3w7Du28Kwdsp4j0PTTXHUQ==", - "subType": "06" - } - } - }, - "azure_javascript_det_auto_id": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", - "subType": "06" - } - } - }, - "azure_javascript_det_explicit_id": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", - "subType": "06" - } - } - }, - "azure_javascript_det_explicit_altname": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAANUrNUS/7/dmKVWBd+2JKGEn1hxbFSyu3p5sDNatukG2m16t4WwxzmYAg8PuQbAxekprs7iaLA+7D2Kn3ZuMSQOw==", - "subType": "06" - } - } - }, - "azure_symbol_rand_auto_id": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAORMcgtQSU+/2Qlq57neRrVuAFSeSwkqdo+z1fh6IKjyEzhCy+u5bTzSzTopyKJQTCUZA2mSpRezWkM87oiGfhMFkBRVreMcE62eH+BLlgUaM=", - "subType": "06" - } - } - }, - "azure_symbol_rand_auto_altname": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAOIKlAw/A3nwHn0tO2cYtJx0azB8MGmXtt+bRptzn8yHlUSpMpYaiU0ssBBiLkmMLAITYebLqDk3NHESyP7PvbSfX1E2XVn2Nf694ZqPWMec8=", - "subType": "06" - } - } - }, - "azure_symbol_rand_explicit_id": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAO8SXW76AEr/6D6zyP1RYwmwdVM2AINaXZn3Ipy+fynWTUV6XIPIRR7xMTttNo2zlh7fgXDZ28PmjooGlQzn0q0JVQmXPCIPM3aqAmMcgyuqg=", - "subType": "06" - } - } - }, - "azure_symbol_rand_explicit_altname": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAOtoJWm2Ucre0foHIiOutsX1WIyub7t3Lby3/F8zRXn+l6ixlTjAPgWFwpRnYg96Lt2ACDDQ9CO51ejr9qk0b8LDBwG3qU5Cuibsp7vo1VsdI=", - "subType": "06" - } - } - }, - "azure_symbol_det_auto_id": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", - "subType": "06" - } - } - }, - "azure_symbol_det_explicit_id": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", - "subType": "06" - } - } - }, - "azure_symbol_det_explicit_altname": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAOvp/FMMmWVMkiuN51uFMFBiRQAcc9jftlNsHsLoNtohZaGni26kgX94b+/EI8pdWF5xA/73JlGlij0Rt+vC9s/zTDItRpn0bJL54WPphDcmA=", - "subType": "06" - } - } - }, - "azure_javascriptWithScope_rand_auto_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAPCw9NnvJyuTYIgZxr1w1UiG85PGZ4rO62DWWDF98HwVM/Y6u7hNdNjkaWjYFsPMl38ioHw/pS8GFR62QmH2RAw/BV0wI7pNy2evANr3i3gKg=", - "subType": "06" - } - } - }, - "azure_javascriptWithScope_rand_auto_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAPXQzqnQ2UWkIYof8/OfadNMa7iVKAbOaiu7YGm8iVrx+W6uxKLPFugVqHtQ29hYXXf33xr8rqGNxDlAe7/x1OeYEif71f7LUkmKF9WxJV9Ko=", - "subType": "06" - } - } - }, - "azure_javascriptWithScope_rand_explicit_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAP0nxlppgPyjLx0eBempbOlL21G6KbABSrE6+YuNDcsjJjxCQuLR9+aoAwa+yCDEC7GZ1E3oP489edKUuNpE4Ts26jy4aRegu4DmyECUeBwAg=", - "subType": "06" - } - } - }, - "azure_javascriptWithScope_rand_explicit_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAPO89afu9Sb+cK9wwM1cO1DPjvu5UNyObjjTScy1hy9PzllJGfj7b84f0Ah74jPYsMPwI0Eslu/IYF3+5jmquq5Qp/VUQESlxqRqRK0xIeMfs=", - "subType": "06" - } - } - }, - "azure_javascriptWithScope_det_explicit_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_det_explicit_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_int_rand_auto_id": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAQUyy4uWmWdzypsK81q9egREg4s80X3L2hzxJzC+fL08Xzy1z9grpPPCfJrluUVKMMGmmZR8gJPJ70igN3unJbzg==", - "subType": "06" - } - } - }, - "azure_int_rand_auto_altname": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAQr4gyoHKpGsSJo8CMsYSJk/KilFMJhsDCmxrha7yfNW1uR5sjyZj4B4s6uTXGw76x7aR/AvecDlY3QFJb8L1mjg==", - "subType": "06" - } - } - }, - "azure_int_rand_explicit_id": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAQ0zgXYPV1MuEFksmDpVDoWkoZQelm3+rYrMiT64KYywO//75799W8TbR3a7O6Q/ErjKQOin2OCp8EWwZqTDdz5w==", - "subType": "06" - } - } - }, - "azure_int_rand_explicit_altname": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAQG+qz00yizREbP3tla1elMiwf8TKLbUU2XWUP+E0vey/wvbjTTIzqwUlz/b9St77CHJhavypP3hMrngXR9GapbQ==", - "subType": "06" - } - } - }, - "azure_int_det_auto_id": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", - "subType": "06" - } - } - }, - "azure_int_det_explicit_id": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", - "subType": "06" - } - } - }, - "azure_int_det_explicit_altname": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAQCkJH+CataLqp/xBjO77QBprC2xPV+rE+goSZ3C6aqwXIeTYHTOqEbeaFb5iZcqYH5nWvNvnfbZSIMyvSfrPjhw==", - "subType": "06" - } - } - }, - "azure_timestamp_rand_auto_id": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAARwcXYtx+A7g/zGkjGdkyVxZGCO9Nzj3D70NIpl2TeH2j9qYGP4DenwL1xSgrL2Ez+X58d2BvNhKrjA9y2w1Z8kA==", - "subType": "06" - } - } - }, - "azure_timestamp_rand_auto_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAARQ0Pjx3l92Aqhn2e1hot2M9rQ6aLPE2Iw8AVhm5AD8FWywWih12Fn2p9+kiE33yKPOCyrTWQHKPtB4yYhqnJgGg==", - "subType": "06" - } - } - }, - "azure_timestamp_rand_explicit_id": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAARvFMlIzh2IjpHkTJ8buqTOqBA0+CxVDsZacUhSHVMgJLN+0DJsJy8OfkmKMu9Lk5hULY00Udoja87x+79mYfmeQ==", - "subType": "06" - } - } - }, - "azure_timestamp_rand_explicit_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAAR+2SCd7V5ukAkh7CYpNPIatzTL8osNoA4Mb5jjjbos8eMamImw0fbH8YA+Rdm4CgGdQQ9VDX7MtMWlArkj0Jpew==", - "subType": "06" - } - } - }, - "azure_timestamp_det_auto_id": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", - "subType": "06" - } - } - }, - "azure_timestamp_det_explicit_id": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", - "subType": "06" - } - } - }, - "azure_timestamp_det_explicit_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAARe72T/oC09QGE1vuljb6ZEHa6llEwMLT+C4s9u1fREkOKndpmrOlGE8zOey4teizY1ypOMkIZ8GDQJJ4kLSpNkQ==", - "subType": "06" - } - } - }, - "azure_long_rand_auto_id": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAASSSgX7k8iw0xFe0AiIzOu0e0P7Ujyfsk/Cdl0fR5X8V3QLVER+1Qa47Qpb8iWL2VLBSh+55HvIEtvhWn8SwXaog==", - "subType": "06" - } - } - }, - "azure_long_rand_auto_altname": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAASUhKr5K7ulGTeFbhIvJ2DDE10gRAFn5+2zqnsIFSY8lYV2PBYcENdeNBXZs6kyIAYhJdQyuOChVCerTI5jmQWDw==", - "subType": "06" - } - } - }, - "azure_long_rand_explicit_id": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAASHxawpjTHdXYRWQSZ7Qi7gFC+o4dW2mPH8s5nQkPFY/EubcJbdAZ5HFp66NfPaDJ/NSH6Vy+TkpX3683RC+bjSQ==", - "subType": "06" - } - } - }, - "azure_long_rand_explicit_altname": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAASVaMAv6UjuBOUZMJ9qz+58TQWmgaMpS9xrJziJY80ml9aRlDTtRubP7U40CgbDvrtY1QgHbkF/di1XDCB6iXMMg==", - "subType": "06" - } - } - }, - "azure_long_det_auto_id": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", - "subType": "06" - } - } - }, - "azure_long_det_explicit_id": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", - "subType": "06" - } - } - }, - "azure_long_det_explicit_altname": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQGVERAAAAAAAAAAAAAAAAAS06L8oEPeMvVlA32VlobdOWG24OoyMbv9PyYsHLsbT0bHFwU7lYUSQG9EkYVRNPEDzvXpciE1jT7KT8CRY8XT/g==", - "subType": "06" - } - } - }, - "azure_decimal_rand_auto_id": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAATJ6LZgPu9F+rPtYsMuvwOx62+g1dAk858BUtE9FjC/300DnbDiolhkHNcyoFs07NYUNgLthW2rISb/ejmsDCt/oqnf8zWYf9vrJEfHaS/Ocw=", - "subType": "06" - } - } - }, - "azure_decimal_rand_auto_altname": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAATX8eD6qFYWKwIGvXtQG79fXKuPW9hkIV0OwrmNNIqRltw6gPHl+/1X8Q6rgmjCxqvhB05AxTj7xz64gP+ILkPQY8e8VGuCOvOdwDo2IPwy18=", - "subType": "06" - } - } - }, - "azure_decimal_rand_explicit_id": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAATBjQ9E5wDdTS/iI1XDqGmDBC5aLbPB4nSyrjRLfv1zEoPRjmcHlQmMRJA0mori2VQv6EBFNHeczFCenJaSAkuh77czeXM2vH3T6qwEIDs4dw=", - "subType": "06" - } - } - }, - "azure_decimal_rand_explicit_altname": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AgGVERAAAAAAAAAAAAAAAAATtkjbhdve7MNuLaTm6qvaewuVUxeC1DMz1fd4RC4jeiBFMd5uZUVJTiOIerwQ6P5G5lkMlezKDWgKl2FUvZH6c7V3JknhsaWcV5iLWGUL6Zc=", - "subType": "06" - } - } - }, - "azure_decimal_det_explicit_id": { - "kms": "azure", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_det_explicit_altname": { - "kms": "azure", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_minKey_rand_explicit_id": { - "kms": "azure", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_rand_explicit_altname": { - "kms": "azure", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_det_explicit_id": { - "kms": "azure", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_det_explicit_altname": { - "kms": "azure", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_maxKey_rand_explicit_id": { - "kms": "azure", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_rand_explicit_altname": { - "kms": "azure", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_det_explicit_id": { - "kms": "azure", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_det_explicit_altname": { - "kms": "azure", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_double_rand_auto_id": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAABFoHQxnh1XSC0k1B01uFFg7rE9sZVBn4PXo26JX8gx9tuxu+4l9Avb23H9BfOzuWiEc43iw87K/W2y0VfKp5CCg==", - "subType": "06" - } - } - }, - "gcp_double_rand_auto_altname": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAABRkZkEtQEFB/r268cNfYRQbN4u5Cxjl9Uh+8wq9TFWLQH2E/9wj2vTLlxQ2cQsM7Qd+XxR5idjfBf9CKAfvUa/A==", - "subType": "06" - } - } - }, - "gcp_double_rand_explicit_id": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAABDSUZ+0BbDDEZxCXA+J2T6Js8Uor2dfXSf7s/hpLrg6dxcW2chpht9XLiLOXG5w83TzCAI5pF8cQgBpBpYjR8RQ==", - "subType": "06" - } - } - }, - "gcp_double_rand_explicit_altname": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAABCYxugs7L+4S+1rr0VILSbtBm79JPTLuzluQAv0+8hbu5Z6zReOL6Ta1vQH1oA+pSPGYA4euye3zNl1X6ZewbPw==", - "subType": "06" - } - } - }, - "gcp_double_det_explicit_id": { - "kms": "gcp", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "gcp_double_det_explicit_altname": { - "kms": "gcp", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "gcp_string_rand_auto_id": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAACx3wSslJEiD80YLTH0n4Bbs4yWVPQl15AU8pZMLLQePqEtI+BJy3t2bqNP1098jS0CGSf+LQmQvXhJn1aNFeMTw==", - "subType": "06" - } - } - }, - "gcp_string_rand_auto_altname": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAC5BTe5KP5UxSIk6dJlkz8aaZ/9fg44XPWHafiiL/48lcv3AWbu2gcBo1EDuc1sJQu6XMrtDCRQ7PCHsL7sEQMGQ==", - "subType": "06" - } - } - }, - "gcp_string_rand_explicit_id": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAACyJN55OcyXXJ71x8VphTaIuIg6kQtGgVKPhWx0LSdYc6JOjB6LTdA7SEWiSlSWWFZE26UmKcPbkbLDAYf4IVrzQ==", - "subType": "06" - } - } - }, - "gcp_string_rand_explicit_altname": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAACoa0d9gqfPP5s3+GoruwzxoQFgli8SmjpTVRLAOcFxqGdfrwSbpYffSw/OR45sZPxXCL6T2MtUvZsl7ukv0jBnw==", - "subType": "06" - } - } - }, - "gcp_string_det_auto_id": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", - "subType": "06" - } - } - }, - "gcp_string_det_explicit_id": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", - "subType": "06" - } - } - }, - "gcp_string_det_explicit_altname": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAACTCkyETcWayIZ9YEoQEBVIF3i7iXEe6M3KjYYaSVCYdqSbSHBzlwKWYbP+Xj/MMYBYTLZ1aiRQWCMK4gWPYppZw==", - "subType": "06" - } - } - }, - "gcp_object_rand_auto_id": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAADy+8fkyeNYdIK001YogXfKc25zRXS1VGIFVWR6jRfrexy9C8LBBfX3iDwGNPbP2pkC3Tq16OoziQB6iNGf7s7yg==", - "subType": "06" - } - } - }, - "gcp_object_rand_auto_altname": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAADixoDdvm57gH8ooOaKI57WyZD5uaPmuYgmrgAFuV8I+oaalqYctnNSYlzQKCMQX/mIcTxvW3oOWY7+IzAz7npvw==", - "subType": "06" - } - } - }, - "gcp_object_rand_explicit_id": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAADvq0OAoijgHaVMhsoNMdfWFLyISDo6Y13sYM0CoBXS/oXJNIJJvhgKPbFSV/h4IgiDLy4qNYOTJQvpqt094RPgQ==", - "subType": "06" - } - } - }, - "gcp_object_rand_explicit_altname": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAADuTZF7/uqGjFbjzBYspPkxGWvvVAEN/ib8bfPOQrEobtTWuU+ju9H3TlT9DMuFy7RdUZnPB0D3HkM8+zky5xeBw==", - "subType": "06" - } - } - }, - "gcp_object_det_explicit_id": { - "kms": "gcp", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_det_explicit_altname": { - "kms": "gcp", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_array_rand_auto_id": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAE085kJIBX6S93D94bcRjkOegEKsksi2R1cxoVDoOpSdHh3S6bZAOh50W405wvnOKf3KTP9SICDUehQKQZSC026Y5dwVQ2GiM7PtpSedthKJs=", - "subType": "06" - } - } - }, - "gcp_array_rand_auto_altname": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAEk/FAXsaqyVr6I+MY5L0axeLhskcEfLZeB8whLMKbjLDLa8Iep+IdrFVSfKo03Zr/7Ah8Js01aT6+Vt4EDMJK0mGKZJOjsrAf3b6RS+Mzebg=", - "subType": "06" - } - } - }, - "gcp_array_rand_explicit_id": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAEDY7J9JGiurctYr7ytakNjcryVm42fkubcVpQpUYEkpK/G9NLGjrJuFgNW5ZVjYiPKEBbDB7vEtJqGux0BU++hrvVHNJ3wUT2mbDE18NE4KE=", - "subType": "06" - } - } - }, - "gcp_array_rand_explicit_altname": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAErFFlw8W9J2y+751RnYLw0TSK9ThD6sP3i4zPbZtiuhc90RFoJhScvqM9i4sDKuYePZZRLBxdX4EZhZClOmswCGDLCIWsQlSvCwgDcIsRR/w=", - "subType": "06" - } - } - }, - "gcp_array_det_explicit_id": { - "kms": "gcp", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_det_explicit_altname": { - "kms": "gcp", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_binData=00_rand_auto_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAF0R5BNkQKfm6wx/tob8nVGDEYV/pvy9UeCqc9gFNuB5d9KxCkgyxryV65rbB90OriqvWFO2jcxzchRYgRI3fQ+A==", - "subType": "06" - } - } - }, - "gcp_binData=00_rand_auto_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAF4wcT8XGc3xNdKYDX5/cbUwPDdnkIXlWWCCYeSXSk2oWPxMZnPsVQ44nXKJJsKitoE3r/hL1sSG5239WzCWyx9g==", - "subType": "06" - } - } - }, - "gcp_binData=00_rand_explicit_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAF07OFs5mlx0AB6QBanaybLuhuFbG+19KxSqHlSgELcz6TQKI6equX97OZdaWSWf2SSeiYm5E6+Y3lgA5l4KxC2A==", - "subType": "06" - } - } - }, - "gcp_binData=00_rand_explicit_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAFZ74Q7JMm7y2i3wRmjIRKefhmdnrhP1NXJgploi+44eQ2eRraZsW7peGPYyIfsXEbhgV5+aLmiYgvemBywfdogQ==", - "subType": "06" - } - } - }, - "gcp_binData=00_det_auto_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", - "subType": "06" - } - } - }, - "gcp_binData=00_det_explicit_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", - "subType": "06" - } - } - }, - "gcp_binData=00_det_explicit_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFhwJkocj36WXoY3mg2GWUrJ5IQTo9MvkwEwRFKdkcxm9pX2PZPK7bN5ZWw3IFcQ/0GfaW6V4LYr8WarZdLF0p5g==", - "subType": "06" - } - } - }, - "gcp_binData=04_rand_auto_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAFmDO47RTVXzm8D4hfhLICILrQJg3yOwG3HYfCdz7yaanPow2Y6bMxvXxk+kDS29aS8pJKDqJQQoMGc1ZFD3yYKsLQHRi/8rW6TNDQd4sCQ00=", - "subType": "06" - } - } - }, - "gcp_binData=04_rand_auto_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAFpiu9Q3LTuPmgdWBqo5Kw0vGF9xU1rMyE4xwR8GccZ7ZMrUcR4AnZnAP7ah5Oz8e7qonNYX4d09obesYSLlIjyK7J7qg+GWiEURgbvmOngaA=", - "subType": "06" - } - } - }, - "gcp_binData=04_rand_explicit_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAFHRy8dveGuMng9WMmadIp39jD7iEfl3bEjKmzyNoAc0wIcSJZo9kdGbNEwZ4p+A1gz273fmAt/AJwAxwvqdlanLWBr4wiSKz1Mu9VaBcTlyY=", - "subType": "06" - } - } - }, - "gcp_binData=04_rand_explicit_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAFiqO+sKodqXuVox0zTbKuY4Ng0QE1If2hDLWXljAEZdYABPk20UJyL/CHR49WP2Cwvi4evJCf8sEfKpR+ugPiyxWzP3iVe6qqTzP93BBjqoc=", - "subType": "06" - } - } - }, - "gcp_binData=04_det_auto_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", - "subType": "06" - } - } - }, - "gcp_binData=04_det_explicit_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", - "subType": "06" - } - } - }, - "gcp_binData=04_det_explicit_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAFEp5Gut6iENHUqDMVdBm4cxQy35gnslTf7vSWW9InFh323BvaTTiubxbxTiMKIa/u47MfMprL9HNQSwgpAQc4lped+YnlRW8RYvTcG4frFtA=", - "subType": "06" - } - } - }, - "gcp_undefined_rand_explicit_id": { - "kms": "gcp", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_rand_explicit_altname": { - "kms": "gcp", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_det_explicit_id": { - "kms": "gcp", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_det_explicit_altname": { - "kms": "gcp", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_objectId_rand_auto_id": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAH8Kt6coc8bPI4QIwS1tIdk6pPA05xlZvrOyAQgvoqaozMtWzG15OunQLDdS3yJ5WRiV7kO6CIKqRrvL2RykB5sw==", - "subType": "06" - } - } - }, - "gcp_objectId_rand_auto_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAHU5Yzmz2mbgNQrGSvglgVuv14nQWzipBkZUVSO4eYZ7wLrj/9t0fnizsu7Isgg5oA9fV0Snh/A9pDnHZWoccXUw==", - "subType": "06" - } - } - }, - "gcp_objectId_rand_explicit_id": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAHsdq5/FLqbjMDiNzf+6k9yxUtFVjS/xSqErqaboOl21934pAzgkOzBGodpKKFuK0Ta4f3h21XS+84wlIYPMlTtw==", - "subType": "06" - } - } - }, - "gcp_objectId_rand_explicit_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAHokIdXxNQ/NBMdMAVNxyVuz/J5pMMdtfxxJxr7PbsRJ3FoD2QNjTgE1Wsz0G4o09Wv9UWD+/mIqPVlLgx1sRtPw==", - "subType": "06" - } - } - }, - "gcp_objectId_det_auto_id": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", - "subType": "06" - } - } - }, - "gcp_objectId_det_explicit_id": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", - "subType": "06" - } - } - }, - "gcp_objectId_det_explicit_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAHkcbaj3Hy3b4HkjRkMgiw5h6jBW7Sc56QSJmAPmVSc2T4B8d79A49dW0RyEiInZJcnVRjrYzUTRtgRaG4/FRd8g==", - "subType": "06" - } - } - }, - "gcp_bool_rand_auto_id": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAIf7vUYS5XFrEU4g03lzj9dk8a2MkaQdlH8nE/507D2Gm5XKQLi2jCENZ9UaQm3MQtVr4Uqrgz2GZiQHt9mXcG3w==", - "subType": "06" - } - } - }, - "gcp_bool_rand_auto_altname": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAIdOC4Tx/TaVLRtOL/Qh8RUFIzHFB6nSegZoITwZeDethd8V3+R+aIAgzfN3pvmZzagHyVCm2nbNYJNdjOJhuDrg==", - "subType": "06" - } - } - }, - "gcp_bool_rand_explicit_id": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAIzB14mX2vaZdiW9kGc+wYEgTCXA0FB5AVEyuERD00+K7U5Otlc6ZUwMtb9nGUu+M7PnnfxiDFHCrUWrTkAZzSUw==", - "subType": "06" - } - } - }, - "gcp_bool_rand_explicit_altname": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAIhRLg79ACCMfeERBgG1wirirrZXZzbK11RxHkAbf14Fji2L3sdMBdLBU5I028+rmtDdC7khcNMt11V6XGKpAjnA==", - "subType": "06" - } - } - }, - "gcp_bool_det_explicit_id": { - "kms": "gcp", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "gcp_bool_det_explicit_altname": { - "kms": "gcp", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "gcp_date_rand_auto_id": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAJL+mjI8xBmSahOOi3XkGRGxjhGNdJb445KZtRAaUdCV0vMKbrefuiDHJDPCYo7mLYNhRSIhQfs63IFYMrlKP26A==", - "subType": "06" - } - } - }, - "gcp_date_rand_auto_altname": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAJbeyqO5FRmqvPYyOb0tdKtK6JOg8QKbCl37/iFeEm7N0T0Pjb8Io4U0ndB3O6fjokc3kDQrZcQkV+OFWIMuKFjw==", - "subType": "06" - } - } - }, - "gcp_date_rand_explicit_id": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAJVz3rSYIcoYtM0tZ8pB2Ytgh8RvYPeZvW7aUVJfZkZlIhfUHOHEf5kHqxzt8E1l2n3lmK/7ZVCFUuCCmr8cZyWw==", - "subType": "06" - } - } - }, - "gcp_date_rand_explicit_altname": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAJAiQqNyUcpuDEpFt7skp2NSHFCux2XObrIIFgXReYgtWoapL/n4zksJXl89PGavzNPBZbzgEa8uwwAe+S+Y6TLg==", - "subType": "06" - } - } - }, - "gcp_date_det_auto_id": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", - "subType": "06" - } - } - }, - "gcp_date_det_explicit_id": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", - "subType": "06" - } - } - }, - "gcp_date_det_explicit_altname": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAJmATV2A1P5DmrS8uES6AMD9y+EU3x7u4K4J0p296iSkCEgIdZZORhPIEnuJK3FHw1II6IEShW2nd7sOJRZSGKcg==", - "subType": "06" - } - } - }, - "gcp_null_rand_explicit_id": { - "kms": "gcp", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "gcp_null_rand_explicit_altname": { - "kms": "gcp", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "gcp_null_det_explicit_id": { - "kms": "gcp", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "gcp_null_det_explicit_altname": { - "kms": "gcp", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "gcp_regex_rand_auto_id": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAALiebb3hWwJRqlgVEhLYKKvo6cnlU7BFnZnvlZ8GuIr11fUvcnS9Tg2m7vPmfL7WVyuNrXlR48x28Es49YuaxuIg==", - "subType": "06" - } - } - }, - "gcp_regex_rand_auto_altname": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAALouDFNLVgBXqhJvBRj9DKacuD1AQ2NAVDW93P9NpZDFFwGOFxmKUcklbPj8KkHqvma8ovVUBTLLUDR+tKFRvC2Q==", - "subType": "06" - } - } - }, - "gcp_regex_rand_explicit_id": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAALtdcT9+3R1he4eniT+1opqs/YtujFlqzBXssv+hCKhJQVY/IXde32nNpQ1WTgUc7jfIJl/v9HvuA9cDHPtDWWTg==", - "subType": "06" - } - } - }, - "gcp_regex_rand_explicit_altname": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAALAwlRAlj4Zpn+wu9eOcs5CsNgrkVwrgmu1tc4wyQp0Lt+3UcplYsXQMrMPcTx3yB0JcI4Kh65n/DrAaA+G/a6iw==", - "subType": "06" - } - } - }, - "gcp_regex_det_auto_id": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", - "subType": "06" - } - } - }, - "gcp_regex_det_explicit_id": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", - "subType": "06" - } - } - }, - "gcp_regex_det_explicit_altname": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAALbCutQ7D94gk0djewcQiEdMFVVa21+Dn5enQf/mqPi3o7vPy7OejDBk9fiZRffsioRMhlx2cxqa8T3+AkeN96yg==", - "subType": "06" - } - } - }, - "gcp_dbPointer_rand_auto_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAMG8P+Y2YNIgknxE0/yPDCHASBvCU1IJwsEyaJPuOjn03enxEN7z/wbjVMN0lGUptDP3SVL+OIZtQ35VRP84MtnbdhcfZWqMhLjzrCjmtHUEg=", - "subType": "06" - } - } - }, - "gcp_dbPointer_rand_auto_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAMKCLFUN6ApB5fSVEWazRddhKTEwgqI/mxfe0BBxht69pZQYhTjhOJP0YcIrtr+RCeHOa4FIJgQod1CFOellIzO5YH5CuV4wPxCAlOdbJcBK8=", - "subType": "06" - } - } - }, - "gcp_dbPointer_rand_explicit_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAM7ULEA6uKKv4Pu4Sa3aAt7dXtEwfQC98aJoLBapHT+xXtn5GWPynOZQNtV3lGaYExQjiGdYbzOcav3SVy/sYTe3ktgkQnuZfe0tk0zyvKIMM=", - "subType": "06" - } - } - }, - "gcp_dbPointer_rand_explicit_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAMoMveHO1MadAKuT498xiKWWBUKRbH7k7P2YETDg/BufVw0swos07rk6WJa1vqyF61QEmACjy4pmlK/5P0VfKJBAIvif51YqHPQkobJVS3nVA=", - "subType": "06" - } - } - }, - "gcp_dbPointer_det_auto_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", - "subType": "06" - } - } - }, - "gcp_dbPointer_det_explicit_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", - "subType": "06" - } - } - }, - "gcp_dbPointer_det_explicit_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAMz+9m1bE+Th9YeyPmJdtJPO0F5QYsGYtU/Eom/LSoYjDmTmV2ehkKx/cevIxJfZUc+Mvv/uGoeuubGl8tiX4l+f6yLrSIS6QBtIHYKXk+JNE=", - "subType": "06" - } - } - }, - "gcp_javascript_rand_auto_id": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAANqBD0ITMn4BaFnDp7BX7vXbRBkFwmjQRVUeBbwsQtv5WVlJMAd/2+w7tyH8Wc44x0/9U/DA5GVhpTrtdDyPBI3w==", - "subType": "06" - } - } - }, - "gcp_javascript_rand_auto_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAANtA0q4mbkAaKX4x1xk0/094Mln0wnh2bYnI6s6dh+l2WLDH7A9JMZxCl6kc4uOsEfbOvjP/PLIYtdMGs14EjM5A==", - "subType": "06" - } - } - }, - "gcp_javascript_rand_explicit_id": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAANfrW3pmeiFdBFt5tJS6Auq9Wo/J4r/vMRiueLWxig5S1zYuf9kFPJMK/nN9HqQPIcBIJIC2i/uEPgeepaNXACCw==", - "subType": "06" - } - } - }, - "gcp_javascript_rand_explicit_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAANL7UZNzpwfwhRn/HflWIE9CSxGYNwLSo9d86HsOJ42rrZKq6HQqm/hiEAg0lyqCxVIVFxYEc2BUWSaq4/+SSyZw==", - "subType": "06" - } - } - }, - "gcp_javascript_det_auto_id": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", - "subType": "06" - } - } - }, - "gcp_javascript_det_explicit_id": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", - "subType": "06" - } - } - }, - "gcp_javascript_det_explicit_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAANB2d97R8nUJqnG0JPsWzyFe5pct5jvUljdkPnlZvLN1ZH+wSu4WmLfjri6IzzYP//f8tywn4Il+R4lZ0Kr/RAeA==", - "subType": "06" - } - } - }, - "gcp_symbol_rand_auto_id": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAOsGdnr6EKcBdOAvYrP0o1pWbhhJbYsqfVwwwS1zq6ZkBayOss2J3TuYwBGXhJFlq3iIiWLdxGQ883XIvuAECnqUNuvpK2rOLwtDg8xJLiH24=", - "subType": "06" - } - } - }, - "gcp_symbol_rand_auto_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAOpfa6CUSnJBvnWdd7pSZ2pXAbYm68Yka6xa/fuyhVx/Tc926/JpqmOmQtXqbOj8dZra0rQ3/yxHySwgD7s9Qr+xvyL7LvAguGkGmEV5H4Xz4=", - "subType": "06" - } - } - }, - "gcp_symbol_rand_explicit_id": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAO085iqYGFdtjiFWHcNqE0HuKMNHmk49DVh+pX8Pb4p3ehB57JL1nRqaXqHPqhFenxSEInT/te9HQRr+ADcHADvUGsScfm/n85v85nq6X+5y4=", - "subType": "06" - } - } - }, - "gcp_symbol_rand_explicit_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAOiidb+2TsbAb2wc7MtDzb/UYsjgVNSw410Sz9pm+Uy7aZROE5SURKXdLjrCH2ZM2a+XCAl3o9yAoNgmAjEvYVxjmyzLK00EVjT42MBOrdA+k=", - "subType": "06" - } - } - }, - "gcp_symbol_det_auto_id": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", - "subType": "06" - } - } - }, - "gcp_symbol_det_explicit_id": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", - "subType": "06" - } - } - }, - "gcp_symbol_det_explicit_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAOFBGo77joqvZl7QQMB9ebMsAI3uro8ILQTJsTUgAqNzSh1mNzqihGHZYe84xtgMrVxNuwcjkidkRbNnLXWLuarOx4tgmOLx5A5G1eYEe3s7Q=", - "subType": "06" - } - } - }, - "gcp_javascriptWithScope_rand_auto_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAPUsQHeXWhdmyfQ2Sq1ev1HMuMhBTc/FZFKO9tMMcI9qzjr+z4IdCOFCcx24/T/6NCsDpMiOGNnCdaBCCNRwNM0CTIkpHNLO+RSZORDgAsm9Q=", - "subType": "06" - } - } - }, - "gcp_javascriptWithScope_rand_auto_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAPRZawtuu0gErebyFqiQw0LxniWhdeujGzaqfAXriGo/2fU7PalzTlWQa8wsv0y7Q/i1K4JbQwCEFpJWLppmtZshCGbVWjpPljB2BH4NNrLPE=", - "subType": "06" - } - } - }, - "gcp_javascriptWithScope_rand_explicit_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAP0qkQjuKmKIqdrsrR9djxt+1jFlEL7K9bP1oz7QWuY38dZJOoGwa6G1bP4wDzjsucJLCEgU2IY+t7BHraBFXvR/Aar8ID5eXcvJ7iOPIyqUw=", - "subType": "06" - } - } - }, - "gcp_javascriptWithScope_rand_explicit_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAP6L41iuBWGLg3hQZuhXp4MupTQvIT07+/+CRY292sC02mehk5BkuSOEVrehlvyvBJFKia4Bqd/UWvY8PnUPLqFKTLnokONWbAuh36y3gjStw=", - "subType": "06" - } - } - }, - "gcp_javascriptWithScope_det_explicit_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_det_explicit_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_int_rand_auto_id": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAQ+6oRKWMSvC+3UGrHSyGeVlR9bFnZtFTmYlUoGn04k6ndtCl8rsmBVUV6dMMYd7znnZtTSIGPI8q6jwf/NJjdIw==", - "subType": "06" - } - } - }, - "gcp_int_rand_auto_altname": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAQnz5jAbrrdutTPFA4m3MvlVJr3bpurTKY5xjwO5k8DZpeWTJzr+kVEJjG6M8/RgC/0UFNgBBrDbDhYa8PZHRijw==", - "subType": "06" - } - } - }, - "gcp_int_rand_explicit_id": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAQfRFoxUgjrv8up/eZ/fLlr/z++d/jFm30nYvKqsnQT7vkmmujJWc8yAtthR9OI6W5biBgAkounqRHhvatLZC6gA==", - "subType": "06" - } - } - }, - "gcp_int_rand_explicit_altname": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAQY/ePk59RY6vLejx9a5ITwkT9000KAubVSqMoQwv7lNXO+GKZfZoLHG6k1MA/IxTvl1Zbz1Tw1bTctmj0HPEGNA==", - "subType": "06" - } - } - }, - "gcp_int_det_auto_id": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", - "subType": "06" - } - } - }, - "gcp_int_det_explicit_id": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", - "subType": "06" - } - } - }, - "gcp_int_det_explicit_altname": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAQE9RVV9pOuysUUEGKq0u6ztFM0gTpoOHcHsTFQstA7+L9XTvxWEgL3RgNeq5KtKdODlxl62niV8dnQwlSoDSSWw==", - "subType": "06" - } - } - }, - "gcp_timestamp_rand_auto_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAARLnk1LpJIriKr6iiY1yBDGnfkRaHNwWcQyL+mORtYC4+AQ6oMv0qpGrJxS2QCbYY1tGmAISqZHCIExCG+TIv4bw==", - "subType": "06" - } - } - }, - "gcp_timestamp_rand_auto_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAARaqYXh9AVZI6gvRZrBwbprE5P3K5Qf4PIK1ca+mLRNOof0EExyAhtku7mYXusLeq0ww/tV6Zt1cA36KsT8a0Nog==", - "subType": "06" - } - } - }, - "gcp_timestamp_rand_explicit_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAARLXzBjkCN8BpfXDIrb94kuZCD07Uo/DMBfMIWQtAb1++tTheUoY2ClQz33Luh4g8NXwuMJ7h8ufE70N2+b1yrUg==", - "subType": "06" - } - } - }, - "gcp_timestamp_rand_explicit_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAARe44QH9ZvTAuHsWhEMoue8eHod+cJpBm+Kl/Xtw7NI/6UTOOHC5Kkg20EvX3+GwXdAGk0bUSCFiTZb/yPox1OlA==", - "subType": "06" - } - } - }, - "gcp_timestamp_det_auto_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", - "subType": "06" - } - } - }, - "gcp_timestamp_det_explicit_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", - "subType": "06" - } - } - }, - "gcp_timestamp_det_explicit_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAARzXjP6d6j/iQxiz1/TC/m+IfAGLFH9wY2ksS//i9x15QttlhcRrT3XmPvxaP5OjTHac4Gq3m2aXiJH56lETyl8A==", - "subType": "06" - } - } - }, - "gcp_long_rand_auto_id": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAASuGZs48eEyVBJ9vvM6cvRySfuR0WM4kL7lx52rSGXBKtkZywyP5rJwNtRn9WTBMDqc1O/4jUgYXpqHx39SLhUPA==", - "subType": "06" - } - } - }, - "gcp_long_rand_auto_altname": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAS/62F71oKTX1GlvOP89uNhXpIyLZ5OdnuLeM/hvL5HWyOudSb06cG3+xnPg3QgppAYFK5X2PGgrEcrA87AykLPg==", - "subType": "06" - } - } - }, - "gcp_long_rand_explicit_id": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAASSgx+p4YzTvjZ+GCZCFHEKHNXJUSloPnLRHE4iJ515Epb8Tox7h8/aIAkB3ulnDS9BiT5UKdye2TWf8OBEwkXzg==", - "subType": "06" - } - } - }, - "gcp_long_rand_explicit_altname": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAAStqszyEfltpgd3aYeoyqaJX27OX861o06VhNX/N2fdSfKx0NQq/hWlWTkX6hK3hjCijiTtHmhFQR6QLkHD/6THw==", - "subType": "06" - } - } - }, - "gcp_long_det_auto_id": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", - "subType": "06" - } - } - }, - "gcp_long_det_explicit_id": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", - "subType": "06" - } - } - }, - "gcp_long_det_explicit_altname": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ARgjwAAAAAAAAAAAAAAAAAAS0wJHtZKnxJlWnlSu0xuq7bZR25UdwcbdCRSaXBC0EXEFuqlzrZSn1lcwKPKGZQO8EQ6SdQDqK95alMLmM8eQrQ==", - "subType": "06" - } - } - }, - "gcp_decimal_rand_auto_id": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAATg4U3nbHBX/Az3ie2yurEIJO6cFryQWKiCpBbx1z0NF7RXd7kFC1XzaY6zcBjfl2AfRO8FFmgjTmFXb6gTRSSF0iAZJZTslfe3n6YFtwSKDI=", - "subType": "06" - } - } - }, - "gcp_decimal_rand_auto_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAATdSSyp0ewboV5zI3T3TV/FOrdx0UQbFHhqcH+yqpotoWPSw5dxE+BEoihYLeaPKuVU/rUIY4TUv05Egj7Ovg62Kpk3cPscxsGtE/T2Ppbt6o=", - "subType": "06" - } - } - }, - "gcp_decimal_rand_explicit_id": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAATl7k20T22pf5Y9knVwIDyOIlbHyZBJqyi3Mai8APEZIYjpSKDKs8QNAH69CIjupyge8Izw4Cuch0bRrvMbp6YFfrUgk1JIQ4iLKkqqzHpBTY=", - "subType": "06" - } - } - }, - "gcp_decimal_rand_explicit_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AhgjwAAAAAAAAAAAAAAAAAATF7YLkhkuLhXdxrQk2fJTs128tRNYHeodkqw7ha/TxW3Czr5gE272gnkdzfNoS7uu9XwOr1yjrC6y/8gHALAWn77WvGrAlBktLQbIIinsuds=", - "subType": "06" - } - } - }, - "gcp_decimal_det_explicit_id": { - "kms": "gcp", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_det_explicit_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_minKey_rand_explicit_id": { - "kms": "gcp", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_rand_explicit_altname": { - "kms": "gcp", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_det_explicit_id": { - "kms": "gcp", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_det_explicit_altname": { - "kms": "gcp", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_maxKey_rand_explicit_id": { - "kms": "gcp", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_rand_explicit_altname": { - "kms": "gcp", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_det_explicit_id": { - "kms": "gcp", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_det_explicit_altname": { - "kms": "gcp", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_double_rand_auto_id": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAB1hL/nPkpQtqxQUANbIJr30PQ98vPvaoy4JWUoElOL+cCnrSra3o7W+12dydy0rCS2EKrVm7Fw0C8L9nf1hpWjw==", - "subType": "06" - } - } - }, - "kmip_double_rand_auto_altname": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAABxlcphy2SxXlkRBvO1Z3nNUqchmeOhIhkdYBbbW7CwYeLVRDciXFsZN73Nb9Bm+W4IpUNpo6mqFEtfjevIjtFyg==", - "subType": "06" - } - } - }, - "kmip_double_rand_explicit_id": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAABx5AfRSiblFc1DGwxRIaUSP2kaM76ryzPUKL9KnEgnX1kjIlFz5B15uMht2cxdrntHFe1qZZk8V9PxTBpWZhJ8Q==", - "subType": "06" - } - } - }, - "kmip_double_rand_explicit_altname": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAABXUC9v9HPrmU9tINzFmr2sQM9f7GHDus+y5T4pWX28PRtfnTysN/ANCfB9RosoR/wuKsbznwwD2JfSzOvlKo3PQ==", - "subType": "06" - } - } - }, - "kmip_double_det_explicit_id": { - "kms": "kmip", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "kmip_double_det_explicit_altname": { - "kms": "kmip", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.2339999999999999858" - } - }, - "kmip_string_rand_auto_id": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAACGHmqW1qbfqVlfB0x0CkXCk9smhs3yXsxJ/8eypSgbDQqVLSW2nf5bbHpnoCHHNtQ7I7ZBXzPzDLH2GgMJpopeQ==", - "subType": "06" - } - } - }, - "kmip_string_rand_auto_altname": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAC9BJTD1pEMbslAjbJYt7yx/jzKkcZF3axu96+NYwp8afUCjXG5TOUZzODOwkbJuWgr7DBxa2GkZTvaAEk86h+Ow==", - "subType": "06" - } - } - }, - "kmip_string_rand_explicit_id": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAACQlG28ECy8KHXC7GEPdC8+raBo2RMJwl5pofcPaTGkPUEbkreguMd1mYctNb90vXxby1nNeJY4o5zJJCMiNhNXg==", - "subType": "06" - } - } - }, - "kmip_string_rand_explicit_altname": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAACbWuK+3nzeKSNVjmgHb0Ii7rA+CsAd+gYubPiMiHXZwE/o6i9FYWN+t/VK3p4K0CwIi6q3cycrMb2IgcvM27Q7Q==", - "subType": "06" - } - } - }, - "kmip_string_det_auto_id": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", - "subType": "06" - } - } - }, - "kmip_string_det_explicit_id": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", - "subType": "06" - } - } - }, - "kmip_string_det_explicit_altname": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAC5OZgr9keCXOIj5Fi06i4win1xt7gpsyPA4Os+HdFn1MIP9tnktvWNRb8Rqhuj2O9KO83brx74Hu3EQ4nT6uCMw==", - "subType": "06" - } - } - }, - "kmip_object_rand_auto_id": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAADh2nGqaAUwHDRVjqYpj8JAPH7scmiHp1Z9SGBZQ6Fapxm+zWDdTBHyitM9U69BctJ5DaaafyqFOj5yr6sJ+ebJQ==", - "subType": "06" - } - } - }, - "kmip_object_rand_auto_altname": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAD1YhOKyNle4y0Qbeio1HlCULLeTCALCLgKSITd50bilD+oDyqQawixJAwphcdjhLdFzbFwst5RWqpsiWMPHx4hQ==", - "subType": "06" - } - } - }, - "kmip_object_rand_explicit_id": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAADveILoWFgX7AhUWCv8UL52TUa75qHuoNadnTQydJlqd6PVmtRKj+8vS7VwxNWPaH4wB1Tk7emMyFEbZpvvzjxqQ==", - "subType": "06" - } - } - }, - "kmip_object_rand_explicit_altname": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAADB/LN9V/4SROJn+ESHRLM7wwcUltQUx3+LbbYXjPDXiiV14HK76Iyy6ZxJ+M5qC9bRj3afhTKuWLBblB8WwksOg==", - "subType": "06" - } - } - }, - "kmip_object_det_explicit_id": { - "kms": "kmip", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_det_explicit_altname": { - "kms": "kmip", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_array_rand_auto_id": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAEasWXQam8XtOkSO0nEttMCQ0iZ4V8DDmhMKyQDFDsiNHyF2h98Ya/xFv4ZSlbpGWXPBvBATEGgov/PDg2vhVi53y4Pk33RHfY60hABuksp3o=", - "subType": "06" - } - } - }, - "kmip_array_rand_auto_altname": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAEj3A1DYSEHm/3SlEmusA+pewxRPUoZ2NAjs60ioEBlCw9n6yiiB+X8d/w40TKsjZcOSfh05NC0z3gnpqQvrNolkxkvi9dmFiZeiiv5vBZUPI=", - "subType": "06" - } - } - }, - "kmip_array_rand_explicit_id": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAEqeJW+L6lP0bn5QcD0FMI0C8vv2n5kV7SKgqKi1o5mxaxmp3Cjlspf7yumfSiQ5js6G9yJVAvHuxlqv14UFyR9RgXS0PIA8WzsAqkL0sJSw0=", - "subType": "06" - } - } - }, - "kmip_array_rand_explicit_altname": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAEnPlPwy0B1VKuNum1GzkZwQjZia5jNYL5bf/k+PbfhnToTRWGxx8+E3R7XXp6YT/rFkjPlzU8ww9+iZNo2oqNpYuHdrIC8ybhO6HZAlvcERo=", - "subType": "06" - } - } - }, - "kmip_array_det_explicit_id": { - "kms": "kmip", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_det_explicit_altname": { - "kms": "kmip", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_binData=00_rand_auto_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFliNDZ6DmjoVcYQBCKDI9njpBsDELg+TD6XLF7xbZnMaJCCHLHr7w3x2/xFfrFSN44CtGAKOniYPCMAspaxHqOA==", - "subType": "06" - } - } - }, - "kmip_binData=00_rand_auto_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAF/P8LPmHKGgG0l5/Xi7jdkwfxpGPxoY0417suCvN6zjM3JNdufytzkektrm9CbBb1SnZCGYF9c0FCMzFG+tN/dg==", - "subType": "06" - } - } - }, - "kmip_binData=00_rand_explicit_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFWI0N4RbnYdEiFrzNpbRN9p+bSLm8Lthiu4K3/CvBg6GQpLMVQFhjW01Bud0lxpT2ohRnOK+ASUhiFcUU/t/lWQ==", - "subType": "06" - } - } - }, - "kmip_binData=00_rand_explicit_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFQZvAtpY4cjEr1rJWVoUGaZKmzocSJ0muHose7Tk5kRDczjFa4Jcu4hN7JLM9qz2z4g+WJC3KQTdW4ZBXStke/Q==", - "subType": "06" - } - } - }, - "kmip_binData=00_det_auto_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", - "subType": "06" - } - } - }, - "kmip_binData=00_det_explicit_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", - "subType": "06" - } - } - }, - "kmip_binData=00_det_explicit_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFohIHrvzu8xLxVHsnYEDhZmv8BpEoEtFSjMUQzvBLUInvvTuU/rOzlVL88CkAEII7M3hcvrz8FKY7b7lC1veoYg==", - "subType": "06" - } - } - }, - "kmip_binData=04_rand_auto_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFn7rhdO8tYq77uVxcqd9Qjz84Yg7JnJMYf0ULTMTh1vJHacckkhXw+8fIMMiAKwuOVwGkMAtu5RBvrFqdfxryCg8RLTxu1YYVthufiClEIS0=", - "subType": "06" - } - } - }, - "kmip_binData=04_rand_auto_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFwwXQx9dKyoyHq7GBMmHzYe9ysoJK/f/ZWzA6nErau9MtX1gqi7VRsYqkamb47/zVbsLZwPMmdgNyPxEh3kqbV2D61t5RG2A3VeqhO1pTF8c=", - "subType": "06" - } - } - }, - "kmip_binData=04_rand_explicit_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAFALeGeinJ8DE+WZniLdCIW2gfJUj445Ukp9PvRLgBXLGedl8mIXlLF2eu3BA9vP6s5y9w6peQjhn+oEofrsUVYD2duyzeIRMKgNiNchjf6TU=", - "subType": "06" - } - } - }, - "kmip_binData=04_rand_explicit_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAF06Fx8CO3OSKE3fGri0VwK0e22YiG9LH2QkDTsRdFbT2lBm+bDD9FrEY8vKWS5RljMuysaxjBOzZ98d2LEs6k8LMOm83Nz/RESe4ZbbcfdQ0=", - "subType": "06" - } - } - }, - "kmip_binData=04_det_auto_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", - "subType": "06" - } - } - }, - "kmip_binData=04_det_explicit_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", - "subType": "06" - } - } - }, - "kmip_binData=04_det_explicit_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAFzmZI909fJgxOykJtvOlv5LsX8z6BxUX2Xg5TsIwOxJMPSC8usm/zR7sZawoVBOuJxtNVLY/8oNP/4pFtAmQo02bUOtTo1yxNz/IZa9x+Q5E=", - "subType": "06" - } - } - }, - "kmip_undefined_rand_explicit_id": { - "kms": "kmip", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_rand_explicit_altname": { - "kms": "kmip", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_det_explicit_id": { - "kms": "kmip", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_det_explicit_altname": { - "kms": "kmip", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_objectId_rand_auto_id": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAHZFzE908RuO5deEt3t2QQdT12ybwqbm8D+sMJrdKt2Wp4kVPsw4ocAGGsRYN6VXe46P5fmyG5HqVWn0hkflZnQg==", - "subType": "06" - } - } - }, - "kmip_objectId_rand_auto_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAH3dPKyCCStvOtVGzlgIS33fsl8OAwQblt9i21pOVuLiliY1Tup9EtkSic88+nNEtXnq9gRknRzLthXv/k1ql+7Q==", - "subType": "06" - } - } - }, - "kmip_objectId_rand_explicit_id": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAHcEjxVfHDSfLzFxAuK/rs/Pn/XV7jLkgKXZYeY0PNlRi1MHojN2AvQqI3J2rOvAjuYfikGcpvGPp/goqUbV9HYw==", - "subType": "06" - } - } - }, - "kmip_objectId_rand_explicit_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAHX65sNHnRYpx3VbWPCdQyFe7u0Y5ItabLEduqDeVsPk/iK4X3GjCSHQfw1yPi+CA+/veVpgdonwws6RiYV4ZZ5Q==", - "subType": "06" - } - } - }, - "kmip_objectId_det_auto_id": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", - "subType": "06" - } - } - }, - "kmip_objectId_det_explicit_id": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", - "subType": "06" - } - } - }, - "kmip_objectId_det_explicit_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAHKU7mcdGEq2WGrDB6TicipLQstAk6G3PkiNt5F3bMavpKLjz04UBrd8aWGVG2gJTTON1UKRztiYFgRvb8f+LK/Q==", - "subType": "06" - } - } - }, - "kmip_bool_rand_auto_id": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAIw/xgJlKEvErmVtue3X3RFsOI2sttAbxnzh1INc9GUQ2vok1VwYt9k88RxMPiOwMAZG7P1MlAdx7zt865onPKOw==", - "subType": "06" - } - } - }, - "kmip_bool_rand_auto_altname": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAIn8IuzlNHbpTgXOd1wEp364zJOBxj2Zf7a9B5osUV1sDY0G1OVpEnuDvZeUsdiUSyRjTTxzyuD/KZlKZ3+qrnrA==", - "subType": "06" - } - } - }, - "kmip_bool_rand_explicit_id": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAI3Nz9PdjUYQRGfTtvYSR8EQuUKFL0wdlEdfSCTBmMBhBPuuF9KxqCgy+ldVu1DRRgg3346DOKEEtE9BJPPInJ6Q==", - "subType": "06" - } - } - }, - "kmip_bool_rand_explicit_altname": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAIEGjqoerIZBk8Rw+YTO7jFKWzagDS8mEpD+9Wm1Q0r0ZHUmV0dQZcIqRV4oUk8U8uHUn0N3t2qGLr+rhUs4GH/g==", - "subType": "06" - } - } - }, - "kmip_bool_det_explicit_id": { - "kms": "kmip", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "kmip_bool_det_explicit_altname": { - "kms": "kmip", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "kmip_date_rand_auto_id": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAJgr0v4xetUXjlLcPcyKv/rzjtWOKp9CZJcm23Noglu5RR/rXJS0qKI+W9MmJ64TMf27KvaJ0UXwfTRrvOC1plCg==", - "subType": "06" - } - } - }, - "kmip_date_rand_auto_altname": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAJoeysAaiPsVK+JL1P1vD/9xF92m5kKidUdn6yklPlSKN4VVEBTymDetTLujULs1u1TlrS71jVLxo3xEwpG/KQvg==", - "subType": "06" - } - } - }, - "kmip_date_rand_explicit_id": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAJVwu4+Su0DktpnZvzTBHYpWbWTq5gho/SLijrcIrFJcvq4YrjjPCXv+odCl95tkH+J1RlJdQ5Cr0umEIazLa6GA==", - "subType": "06" - } - } - }, - "kmip_date_rand_explicit_altname": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAJWTYpjbDkIf82QXHMGrvd0SqhP8cBIakfYJf5aNcNrs86vxRhiG3KwETWPeOOlPZ6n1WjE2bOLB+DJTAxmJvahA==", - "subType": "06" - } - } - }, - "kmip_date_det_auto_id": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", - "subType": "06" - } - } - }, - "kmip_date_det_explicit_id": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", - "subType": "06" - } - } - }, - "kmip_date_det_explicit_altname": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAJ/+sQrUqQh+JADSVIKM0d68gDUhDy37M1z1uvROzQw6hUAbQeD0DWdztADKg560UTPM4uOgH4NAyhLyBLMrWWHg==", - "subType": "06" - } - } - }, - "kmip_null_rand_explicit_id": { - "kms": "kmip", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "kmip_null_rand_explicit_altname": { - "kms": "kmip", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "kmip_null_det_explicit_id": { - "kms": "kmip", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "kmip_null_det_explicit_altname": { - "kms": "kmip", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "kmip_regex_rand_auto_id": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAALi8avMfpxSlDsSTqdxO8O2B1M79gOElyUIdXySQo7mvgHlf4oHQ7r94lL9dnsA2t/jmUmBKoGypaUQUSQE+9x+A==", - "subType": "06" - } - } - }, - "kmip_regex_rand_auto_altname": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAALfHerZ/KolaBrb5qi3SpeNVW+i/nh5mkcdtQg5f1pHePr68KryHucM/XDAzbMqrPlag2/41STGYdJqzYO7Mbppg==", - "subType": "06" - } - } - }, - "kmip_regex_rand_explicit_id": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAALOhKDVAN5cuDyB1EuRFWgKKt0wGJ63E5pPY8Tq2TXMNgCxUUc5O+TE+Ux4ls/uMyOBA3gPzND0CZKiru0i7ACUQ==", - "subType": "06" - } - } - }, - "kmip_regex_rand_explicit_altname": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAALK3Hg8xX9gX+d3vKh7aosRP9CS2CIFeG9sapZv3OAPv1eWjY62Cp/G16kJ0BQt33RYD+DzD3gWupfUSyNZR0gng==", - "subType": "06" - } - } - }, - "kmip_regex_det_auto_id": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", - "subType": "06" - } - } - }, - "kmip_regex_det_explicit_id": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", - "subType": "06" - } - } - }, - "kmip_regex_det_explicit_altname": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAALaQXA8rItT7ELVxO8XtAWdHuiXFFPmnMhS5PMrUy/6mRtbq4fvU9dascW7ozonKOh8ad6+MIT7B/STv9dVBF4Kw==", - "subType": "06" - } - } - }, - "kmip_dbPointer_rand_auto_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAMoGkfmmUWTI+0aW7jVyCJ5Dgru1SCXBUmJSRzDL0D57pNruQ+79tVVcI6Uz5j87DhZFxShHbPjj583vLOOBNM3WGzZCpqH3serhHTWvXK+NM=", - "subType": "06" - } - } - }, - "kmip_dbPointer_rand_auto_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAMwu1WaRhhv43xgxLNxuenbND9M6mxGtCs9o4J5+yfL95XNB9Daie3RcLlyngz0pncBie6IqjhTycXsxTLQ94Jdg6m5GD5cU541LYKvhbv5f4=", - "subType": "06" - } - } - }, - "kmip_dbPointer_rand_explicit_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAM+CIoCAisUwhhJtWQLolxQGQWafniwYyvaJQHmJC94Uwbf1gPfhMR42v2VtrmIVP0J0BaP/xf0cco2/qWRdKGZpgkK2CK6M972NtnZ/2x03A=", - "subType": "06" - } - } - }, - "kmip_dbPointer_rand_explicit_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAMjbeE9+EaJYjGfeAuxsV8teOdsW8bfnlkvji/tE11Zq89UMGx+oUsZzeLjUgVZ5nxsZKCZjEAq+DPnwFVC+MgqNeqWL7fRChODFlPGH2ZC+8=", - "subType": "06" - } - } - }, - "kmip_dbPointer_det_auto_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", - "subType": "06" - } - } - }, - "kmip_dbPointer_det_explicit_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", - "subType": "06" - } - } - }, - "kmip_dbPointer_det_explicit_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAM5B+fjbjYCZzCYUu4N/pJI3srCCXN+OCCHweeweqmpIEmB7yw87bQRIMGtCm6HuekcZ5J5q+nY5AQb0du/wh1YIoOrC3u4w7ZcLHkDmuAJPg=", - "subType": "06" - } - } - }, - "kmip_javascript_rand_auto_id": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAANuzlkWs/c8xArrAxPgYuCeShjj1zCfIMHOTPohspcyNofo9iY3P5MlhEOprZDiS8dBFg6EB7fZDzDdczx6VCN2A==", - "subType": "06" - } - } - }, - "kmip_javascript_rand_auto_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAANwJ72y7UqCBJh1NwVRiE3vU1ex7FMv/X5YWCMuO9MHPMo4g1V5eaO4KfOr+K8+9NtkflgMpeDkvwP92rfR5ud5Q==", - "subType": "06" - } - } - }, - "kmip_javascript_rand_explicit_id": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAANj5q+888itRnLsw9PNGsBLhgqpvem5IJBOE2292r6zwjVueoEK/2I2PesRnn0esnkwdia1ADoMkcLUegwcFRkWQ==", - "subType": "06" - } - } - }, - "kmip_javascript_rand_explicit_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAANnvbnmApys7OIe8LGTsZKDG1F1G1SI/rfZVmF6q1fq5U7feYPp1ejb2t2S2+v7LfcOHytsQWGcYuWCDcl+vosvQ==", - "subType": "06" - } - } - }, - "kmip_javascript_det_auto_id": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", - "subType": "06" - } - } - }, - "kmip_javascript_det_explicit_id": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", - "subType": "06" - } - } - }, - "kmip_javascript_det_explicit_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAANOR9R/Da8j5iVxllLiGFlv4U/bVn/PyN9/5WeGJkGJeE/j/osKrKx6IL1igI0YVI+pKKzsINqJGIv+bJX0s7MNw==", - "subType": "06" - } - } - }, - "kmip_symbol_rand_auto_id": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAOe+vXpJSkmBM3WkxZrn4ea9/C6iNyMXWUzkQIzIYlnbkyu8od8nfOdhobUhoFxcKnvdaxN1s5NhJ1FA97RN/upGYN+AI/7cTCElmFSpdSvkI=", - "subType": "06" - } - } - }, - "kmip_symbol_rand_auto_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAOPpCgK6Hc/M2elOJkwIU9J7PZa+h1chody2yvfDu/UlB6T5sxnEZ6aEY/ISNLhJlhsRzuApSgFOmnrcG6Eg9VnSKin2yK0ll+VFxQEDHAcSA=", - "subType": "06" - } - } - }, - "kmip_symbol_rand_explicit_id": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAOVoHX9GaOn71L5D9TpZmmxkx/asr0FHCLG5ZgLLA04yIhZHsDjt2DiVGGO/Mf4KwvoBn7Cf08qMhW7rQh2LgvvSLBO3zbw5l+MZ/bSn+Jylo=", - "subType": "06" - } - } - }, - "kmip_symbol_rand_explicit_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAOPobmcO/I4QObtCUEmGWpSCJ6tlYyhbO59q78LZBucSNl7DSkf/13tOJ9t+WKXACcMKVMmfPoFsgHbVj1nKWULBT07n1OWWDTZkuMD6C2+Fc=", - "subType": "06" - } - } - }, - "kmip_symbol_det_auto_id": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", - "subType": "06" - } - } - }, - "kmip_symbol_det_explicit_id": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", - "subType": "06" - } - } - }, - "kmip_symbol_det_explicit_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAOPpwX4mafoQJYHuzYfbKW1JunpjpB7Nd2slTC3n8Hsas9wQYf9VkModQhe5M4wZHOIXpehaODRcjKKfKRmpnNBOURSLm/ORJvy+UxtSLsnqo=", - "subType": "06" - } - } - }, - "kmip_javascriptWithScope_rand_auto_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAPW2VMMm+EvsYpVtJQhsxgxgvV35kr9nxqKxP2qqIOAOQ58R/1oyYScFkNwB/tw0A1/zdvhoo+ERa7c0tjLIojFrosXhX2N/8Z4VnbZruz0Nk=", - "subType": "06" - } - } - }, - "kmip_javascriptWithScope_rand_auto_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAPjPq9BQR4EwG/CD+RthOJY04m99LCl/shY6HnaU/QL627kN1dbBAG5vs+MXfa+glg8waVTNgB94vm3j72FMV1ZOKvbl4faWF1Rl2EOpOlR9U=", - "subType": "06" - } - } - }, - "kmip_javascriptWithScope_rand_explicit_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAPtqebrCAidKzBMvp3B5/vBeetqeCoMKS+vo+hLAYooXrnBunWxwRHpr45XYUvroG3aqOMkLtVZSgw8sO6Y/3z1viO2G0sGQW1ZMoW0/PX5Uw=", - "subType": "06" - } - } - }, - "kmip_javascriptWithScope_rand_explicit_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAPtkJwXKlq8Fx1f1+9HFofM4uKi6lHQRFRyiOyUFJYxxZY1LR/2WXXTqWz3MWtrcJFCB+QSVOb1N/ieC7AZUboPgIuPJISM3Hu5VU2x/Isbdc=", - "subType": "06" - } - } - }, - "kmip_javascriptWithScope_det_explicit_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_det_explicit_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_int_rand_auto_id": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAQ50kE7Tby9od2OsmIGZhp9k/mj4vy/YdnmF6YsSPxihbjV1vXGMraI/nGCr+0H1riwzq3m4sCT7aPw2VgiuwKMA==", - "subType": "06" - } - } - }, - "kmip_int_rand_auto_altname": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAQkNL14OSMX/bJbsLtB/UumRoat6QOY7fvwZxRrkXTS3VJVHigthI1cUX7Is/uUsY8oHOfk/ZuHklQkifmfdcklQ==", - "subType": "06" - } - } - }, - "kmip_int_rand_explicit_id": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAQtN2gNVU9Itoj+vgcK/4jEB5baSUH+Qz2WqTY7m0XaA3bPWGFCiWY4Sdw+qovednrSSSbC+azWi1QYclFRraldQ==", - "subType": "06" - } - } - }, - "kmip_int_rand_explicit_altname": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAQk6uBqwXXFF9zEM4bc124goI3pBy2Jdi8Cd0ycKkjXrPG7GVCUm2UMbO+zEzYODeVo35N11g2yMXcv9RVgjWtNA==", - "subType": "06" - } - } - }, - "kmip_int_det_auto_id": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", - "subType": "06" - } - } - }, - "kmip_int_det_explicit_id": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", - "subType": "06" - } - } - }, - "kmip_int_det_explicit_altname": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAQgrkPEf+RBZMn/J7HZObqEfus8icYls6ecaUrlabI6v1ALgxLuv23WSIfTr6mqpQCounqdA14DWS/Wl3kSkVC0w==", - "subType": "06" - } - } - }, - "kmip_timestamp_rand_auto_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAAR2Cu3o2e/u5o69MndeZPJU5ngVA1G2MNYn00t+up/GlmaUC1ni1CVl0ZR0EVZ0gCDUrfxwPISPib8y23tNjbsog==", - "subType": "06" - } - } - }, - "kmip_timestamp_rand_auto_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAARgi8stgSQwqnN4Ws2ZBILOREsjreZcS1MBerL7dbGLVfzW99tqECglhGokkrE0aY69L0xMgcAUIaFRN4GanQAPg==", - "subType": "06" - } - } - }, - "kmip_timestamp_rand_explicit_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAARPxEEI8L5Q3Jybu88BLdf31T3uYEUbijgSlKlkTt141RYrlE8nxtiYU5/5H9GXBis0Qq1s2C+MauD2h/cNijTCA==", - "subType": "06" - } - } - }, - "kmip_timestamp_rand_explicit_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAARh/QaU1dnGbii4LtXCpT5o6vencc8E2fzarjJFbSEd0ixW/UV1ppZdvD729d0umkaIwIEVA4q+XVvHfl/ckKPFg==", - "subType": "06" - } - } - }, - "kmip_timestamp_det_auto_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", - "subType": "06" - } - } - }, - "kmip_timestamp_det_explicit_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", - "subType": "06" - } - } - }, - "kmip_timestamp_det_explicit_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAARqdpLb72mmzb75QBrE+ATMfS5LLqzAD/1g5ScT8zfgh0IHsZZBWCJlSVRNC12Sgr3zdXHMtYp8C3OZT6/tPkQGg==", - "subType": "06" - } - } - }, - "kmip_long_rand_auto_id": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAASVv+ClXkh9spIaXWJYRV/o8UZjG+WWWrNpIjZ9LQn2bXakrKJ3REvdkrzGuxASmBhBYTplEyvxVCJwXuWRAGGYw==", - "subType": "06" - } - } - }, - "kmip_long_rand_auto_altname": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAASeAz/dK+Gc4/jx3W07B2rNFvQ0LoyCllFRvRVGu1Xf1NByc4cRZLOMzlr99syz/fifF6WY30bOi5Pani9QtFuGg==", - "subType": "06" - } - } - }, - "kmip_long_rand_explicit_id": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAASP1HD9uoDlwTldaznKxW71JUQcLsa4/cUWzeTnelQwdpohCbZsM8fBZBqgwwTWnjpYY/LBUipC6yhwLKfUXBoBQ==", - "subType": "06" - } - } - }, - "kmip_long_rand_explicit_altname": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAASnGPH77bS/ETB1hn+VTvsBrxEvIHA6EAb8Z2SEz6BHt7SVeI+I7DLERvRVpV5kNJFcKgXDrvRmD+Et0rhSmk9sw==", - "subType": "06" - } - } - }, - "kmip_long_det_auto_id": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", - "subType": "06" - } - } - }, - "kmip_long_det_explicit_id": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", - "subType": "06" - } - } - }, - "kmip_long_det_explicit_altname": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "ASjCDwAAAAAAAAAAAAAAAAAS+zKmtijSTPOEVlpwmaeMIOuzVNuZpV4Jw9zP8Yqa1xYtlItXDozqdibacRaA74KU49KNySdR1T7fxwxa2OOTrQ==", - "subType": "06" - } - } - }, - "kmip_decimal_rand_auto_id": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAATu/BbCc5Ti9SBlMR2B8zj3Q1yQ16Uob+10LWaT5QKS192IcnBGy4wmmNkIsTys060xUby9KKQF80dVPnjYfqJwEXCe/pVaPQZftE0DolKv78=", - "subType": "06" - } - } - }, - "kmip_decimal_rand_auto_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAATpq6/dtxq2ZUZHrK10aB0YjjPalEaXYcyAyRZjfXWAYCLZdT9sIybjX3Axjxisim+VSHx0QU7oXkKUfcbLgHyjUXj8g9059FHxKFkUsNv4Z8=", - "subType": "06" - } - } - }, - "kmip_decimal_rand_explicit_id": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAATS++9KcfM7uiShZYxRpFPrBJquKv7dyvFRTjnxs6aaaPo0fiqpv6bco/cMLsldEVpWDEA/Tc2HtSXYPp4UJsMfASyBjoxCloL5SaRWyD9Ye8=", - "subType": "06" - } - } - }, - "kmip_decimal_rand_explicit_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AijCDwAAAAAAAAAAAAAAAAATREcETS5KoAGyj/P45owPrdFfy5ng8Z1ND+F+780lLddOyPeDnIsa7yg6uvhTZ65mHfGLvKcFocclYenq/AX1dY4xdjLRg/AfT088A27ORUA=", - "subType": "06" - } - } - }, - "kmip_decimal_det_explicit_id": { - "kms": "kmip", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_det_explicit_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_minKey_rand_explicit_id": { - "kms": "kmip", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_rand_explicit_altname": { - "kms": "kmip", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_det_explicit_id": { - "kms": "kmip", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_det_explicit_altname": { - "kms": "kmip", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_maxKey_rand_explicit_id": { - "kms": "kmip", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_rand_explicit_altname": { - "kms": "kmip", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_det_explicit_id": { - "kms": "kmip", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_det_explicit_altname": { - "kms": "kmip", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - } -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json deleted file mode 100644 index eca6cf912..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-aws.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "status": { - "$numberInt": "1" - }, - "_id": { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "region": "us-east-1", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "provider": "aws" - }, - "updateDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyAltNames": ["aws"] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json deleted file mode 100644 index 31a564edb..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-azure.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "_id": { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyAltNames": ["azure"] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json deleted file mode 100644 index 79d6999b0..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-gcp.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "_id": { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyAltNames": ["gcp"] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json deleted file mode 100644 index 7c7069700..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-kmip.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "_id": { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "kmip", - "keyId": "1" - }, - "keyAltNames": ["kmip"] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json b/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json deleted file mode 100644 index b3fe0723b..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-key-local.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "status": { - "$numberInt": "1" - }, - "_id": { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - }, - "updateDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyMaterial": { - "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyAltNames": [ "local" ] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json b/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json deleted file mode 100644 index e74bc914f..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus-schema.json +++ /dev/null @@ -1,6335 +0,0 @@ -{ - "bsonType": "object", - "properties": { - "aws_double_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "aws_double_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "aws_double_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_double_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_string_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "aws_string_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "aws_string_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_string_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_string_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "string" - } - } - } - }, - "aws_string_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_string_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_object_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "aws_object_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "aws_object_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_object_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_array_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "aws_array_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "aws_array_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_array_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=00_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "aws_binData=00_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "aws_binData=00_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=00_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=00_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "aws_binData=00_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=00_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=04_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "aws_binData=04_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "aws_binData=04_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=04_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=04_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "aws_binData=04_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_binData=04_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_objectId_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "aws_objectId_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "aws_objectId_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_objectId_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_objectId_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "objectId" - } - } - } - }, - "aws_objectId_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_objectId_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_bool_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "aws_bool_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "aws_bool_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_bool_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_date_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "aws_date_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "aws_date_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_date_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_date_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "date" - } - } - } - }, - "aws_date_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_date_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_regex_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "aws_regex_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "aws_regex_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_regex_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_regex_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "regex" - } - } - } - }, - "aws_regex_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_regex_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_dbPointer_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "aws_dbPointer_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "aws_dbPointer_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_dbPointer_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_dbPointer_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "dbPointer" - } - } - } - }, - "aws_dbPointer_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_dbPointer_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascript_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "aws_javascript_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "aws_javascript_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascript_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascript_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "javascript" - } - } - } - }, - "aws_javascript_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascript_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_symbol_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "aws_symbol_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "aws_symbol_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_symbol_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_symbol_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "symbol" - } - } - } - }, - "aws_symbol_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_symbol_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascriptWithScope_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "aws_javascriptWithScope_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "aws_javascriptWithScope_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_javascriptWithScope_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_int_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "aws_int_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "aws_int_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_int_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_int_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "int" - } - } - } - }, - "aws_int_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_int_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_timestamp_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "aws_timestamp_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "aws_timestamp_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_timestamp_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_timestamp_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "timestamp" - } - } - } - }, - "aws_timestamp_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_timestamp_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_long_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "aws_long_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "aws_long_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_long_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_long_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "long" - } - } - } - }, - "aws_long_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_long_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_decimal_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AWSAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "aws_decimal_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_aws", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "aws_decimal_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "aws_decimal_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_double_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "local_double_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "local_double_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_double_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_string_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "local_string_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "local_string_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_string_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_string_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "string" - } - } - } - }, - "local_string_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_string_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_object_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "local_object_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "local_object_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_object_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_array_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "local_array_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "local_array_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_array_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=00_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "local_binData=00_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "local_binData=00_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=00_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=00_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "local_binData=00_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=00_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=04_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "local_binData=04_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "local_binData=04_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=04_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=04_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "local_binData=04_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_binData=04_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_objectId_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "local_objectId_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "local_objectId_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_objectId_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_objectId_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "objectId" - } - } - } - }, - "local_objectId_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_objectId_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_bool_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "local_bool_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "local_bool_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_bool_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_date_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "local_date_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "local_date_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_date_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_date_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "date" - } - } - } - }, - "local_date_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_date_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_regex_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "local_regex_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "local_regex_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_regex_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_regex_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "regex" - } - } - } - }, - "local_regex_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_regex_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_dbPointer_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "local_dbPointer_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "local_dbPointer_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_dbPointer_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_dbPointer_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "dbPointer" - } - } - } - }, - "local_dbPointer_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_dbPointer_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascript_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "local_javascript_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "local_javascript_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascript_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascript_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "javascript" - } - } - } - }, - "local_javascript_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascript_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_symbol_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "local_symbol_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "local_symbol_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_symbol_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_symbol_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "symbol" - } - } - } - }, - "local_symbol_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_symbol_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascriptWithScope_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "local_javascriptWithScope_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "local_javascriptWithScope_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_javascriptWithScope_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_int_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "local_int_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "local_int_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_int_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_int_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "int" - } - } - } - }, - "local_int_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_int_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_timestamp_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "local_timestamp_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "local_timestamp_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_timestamp_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_timestamp_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "timestamp" - } - } - } - }, - "local_timestamp_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_timestamp_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_long_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "local_long_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "local_long_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_long_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_long_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "long" - } - } - } - }, - "local_long_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_long_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_decimal_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "local_decimal_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_local", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "local_decimal_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "local_decimal_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_double_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "azure_double_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "azure_double_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_double_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_string_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "azure_string_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "azure_string_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_string_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_string_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "string" - } - } - } - }, - "azure_string_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_string_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_object_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "azure_object_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "azure_object_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_object_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_array_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "azure_array_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "azure_array_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_array_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=00_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "azure_binData=00_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "azure_binData=00_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=00_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=00_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "azure_binData=00_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=00_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=04_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "azure_binData=04_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "azure_binData=04_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=04_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=04_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "azure_binData=04_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_binData=04_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_objectId_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "azure_objectId_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "azure_objectId_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_objectId_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_objectId_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "objectId" - } - } - } - }, - "azure_objectId_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_objectId_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_bool_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "azure_bool_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "azure_bool_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_bool_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_date_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "azure_date_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "azure_date_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_date_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_date_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "date" - } - } - } - }, - "azure_date_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_date_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_regex_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "azure_regex_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "azure_regex_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_regex_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_regex_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "regex" - } - } - } - }, - "azure_regex_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_regex_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_dbPointer_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "azure_dbPointer_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "azure_dbPointer_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_dbPointer_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_dbPointer_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "dbPointer" - } - } - } - }, - "azure_dbPointer_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_dbPointer_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascript_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "azure_javascript_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "azure_javascript_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascript_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascript_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "javascript" - } - } - } - }, - "azure_javascript_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascript_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_symbol_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "azure_symbol_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "azure_symbol_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_symbol_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_symbol_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "symbol" - } - } - } - }, - "azure_symbol_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_symbol_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascriptWithScope_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "azure_javascriptWithScope_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "azure_javascriptWithScope_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_javascriptWithScope_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_int_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "azure_int_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "azure_int_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_int_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_int_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "int" - } - } - } - }, - "azure_int_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_int_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_timestamp_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "azure_timestamp_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "azure_timestamp_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_timestamp_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_timestamp_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "timestamp" - } - } - } - }, - "azure_timestamp_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_timestamp_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_long_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "azure_long_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "azure_long_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_long_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_long_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "long" - } - } - } - }, - "azure_long_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_long_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_decimal_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZUREAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "azure_decimal_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_azure", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "azure_decimal_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "azure_decimal_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_double_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "gcp_double_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "gcp_double_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_double_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_string_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "gcp_string_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "gcp_string_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_string_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_string_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "string" - } - } - } - }, - "gcp_string_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_string_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_object_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "gcp_object_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "gcp_object_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_object_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_array_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "gcp_array_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "gcp_array_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_array_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=00_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=00_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=00_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=00_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=00_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=00_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=00_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=04_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=04_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=04_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=04_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=04_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "gcp_binData=04_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_binData=04_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_objectId_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "gcp_objectId_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "gcp_objectId_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_objectId_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_objectId_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "objectId" - } - } - } - }, - "gcp_objectId_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_objectId_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_bool_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "gcp_bool_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "gcp_bool_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_bool_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_date_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "gcp_date_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "gcp_date_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_date_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_date_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "date" - } - } - } - }, - "gcp_date_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_date_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_regex_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "gcp_regex_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "gcp_regex_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_regex_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_regex_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "regex" - } - } - } - }, - "gcp_regex_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_regex_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_dbPointer_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "gcp_dbPointer_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "gcp_dbPointer_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_dbPointer_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_dbPointer_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "dbPointer" - } - } - } - }, - "gcp_dbPointer_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_dbPointer_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascript_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "gcp_javascript_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "gcp_javascript_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascript_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascript_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "javascript" - } - } - } - }, - "gcp_javascript_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascript_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_symbol_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "gcp_symbol_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "gcp_symbol_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_symbol_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_symbol_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "symbol" - } - } - } - }, - "gcp_symbol_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_symbol_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascriptWithScope_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "gcp_javascriptWithScope_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "gcp_javascriptWithScope_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_javascriptWithScope_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_int_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "gcp_int_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "gcp_int_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_int_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_int_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "int" - } - } - } - }, - "gcp_int_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_int_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_timestamp_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "gcp_timestamp_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "gcp_timestamp_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_timestamp_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_timestamp_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "timestamp" - } - } - } - }, - "gcp_timestamp_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_timestamp_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_long_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "gcp_long_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "gcp_long_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_long_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_long_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "long" - } - } - } - }, - "gcp_long_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_long_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_decimal_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCPAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "gcp_decimal_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_gcp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "gcp_decimal_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "gcp_decimal_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_double_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "kmip_double_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "double" - } - } - } - }, - "kmip_double_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_double_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_string_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "kmip_string_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "string" - } - } - } - }, - "kmip_string_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_string_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_string_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "string" - } - } - } - }, - "kmip_string_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_string_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_object_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "kmip_object_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "object" - } - } - } - }, - "kmip_object_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_object_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_array_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "kmip_array_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "array" - } - } - } - }, - "kmip_array_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_array_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=00_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=00_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=00_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=00_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=00_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=00_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=00_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=04_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=04_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=04_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=04_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=04_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "binData" - } - } - } - }, - "kmip_binData=04_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_binData=04_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_objectId_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "kmip_objectId_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "objectId" - } - } - } - }, - "kmip_objectId_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_objectId_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_objectId_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "objectId" - } - } - } - }, - "kmip_objectId_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_objectId_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_bool_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "kmip_bool_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "bool" - } - } - } - }, - "kmip_bool_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_bool_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_date_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "kmip_date_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "date" - } - } - } - }, - "kmip_date_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_date_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_date_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "date" - } - } - } - }, - "kmip_date_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_date_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_regex_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "kmip_regex_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "regex" - } - } - } - }, - "kmip_regex_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_regex_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_regex_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "regex" - } - } - } - }, - "kmip_regex_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_regex_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_dbPointer_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "kmip_dbPointer_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "dbPointer" - } - } - } - }, - "kmip_dbPointer_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_dbPointer_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_dbPointer_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "dbPointer" - } - } - } - }, - "kmip_dbPointer_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_dbPointer_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascript_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "kmip_javascript_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascript" - } - } - } - }, - "kmip_javascript_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascript_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascript_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "javascript" - } - } - } - }, - "kmip_javascript_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascript_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_symbol_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "kmip_symbol_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "symbol" - } - } - } - }, - "kmip_symbol_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_symbol_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_symbol_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "symbol" - } - } - } - }, - "kmip_symbol_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_symbol_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascriptWithScope_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "kmip_javascriptWithScope_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "javascriptWithScope" - } - } - } - }, - "kmip_javascriptWithScope_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_javascriptWithScope_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_int_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "kmip_int_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "int" - } - } - } - }, - "kmip_int_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_int_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_int_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "int" - } - } - } - }, - "kmip_int_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_int_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_timestamp_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "kmip_timestamp_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "timestamp" - } - } - } - }, - "kmip_timestamp_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_timestamp_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_timestamp_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "timestamp" - } - } - } - }, - "kmip_timestamp_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_timestamp_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_long_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "kmip_long_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "long" - } - } - } - }, - "kmip_long_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_long_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_long_det_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic", - "bsonType": "long" - } - } - } - }, - "kmip_long_det_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_long_det_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_decimal_rand_auto_id": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "KMIPAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "kmip_decimal_rand_auto_altname": { - "bsonType": "object", - "properties": { - "value": { - "encrypt": { - "keyId": "/altname_kmip", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random", - "bsonType": "decimal" - } - } - } - }, - "kmip_decimal_rand_explicit_id": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - }, - "kmip_decimal_rand_explicit_altname": { - "bsonType": "object", - "properties": { - "value": { - "bsonType": "binData" - } - } - } - } -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/corpus/corpus.json b/tests/SpecTests/client-side-encryption/corpus/corpus.json deleted file mode 100644 index 559711b34..000000000 --- a/tests/SpecTests/client-side-encryption/corpus/corpus.json +++ /dev/null @@ -1,8619 +0,0 @@ -{ - "_id": "client_side_encryption_corpus", - "altname_aws": "aws", - "altname_local": "local", - "altname_azure": "azure", - "altname_gcp": "gcp", - "altname_kmip": "kmip", - "aws_double_rand_auto_id": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_double_rand_auto_altname": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_double_rand_explicit_id": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_double_rand_explicit_altname": { - "kms": "aws", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_double_det_explicit_id": { - "kms": "aws", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_double_det_explicit_altname": { - "kms": "aws", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "aws_string_rand_auto_id": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "aws_string_rand_auto_altname": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "aws_string_rand_explicit_id": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "aws_string_rand_explicit_altname": { - "kms": "aws", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "aws_string_det_auto_id": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "aws_string_det_explicit_id": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "aws_string_det_explicit_altname": { - "kms": "aws", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "aws_object_rand_auto_id": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_object_rand_auto_altname": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_object_rand_explicit_id": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_object_rand_explicit_altname": { - "kms": "aws", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_object_det_explicit_id": { - "kms": "aws", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_object_det_explicit_altname": { - "kms": "aws", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "aws_array_rand_auto_id": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_array_rand_auto_altname": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_array_rand_explicit_id": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_array_rand_explicit_altname": { - "kms": "aws", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_array_det_explicit_id": { - "kms": "aws", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_array_det_explicit_altname": { - "kms": "aws", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "aws_binData=00_rand_auto_id": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_rand_auto_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_rand_explicit_id": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_rand_explicit_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_det_auto_id": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_det_explicit_id": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=00_det_explicit_altname": { - "kms": "aws", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "aws_binData=04_rand_auto_id": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_rand_auto_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_rand_explicit_id": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_rand_explicit_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_det_auto_id": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_det_explicit_id": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_binData=04_det_explicit_altname": { - "kms": "aws", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "aws_undefined_rand_explicit_id": { - "kms": "aws", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "aws_undefined_rand_explicit_altname": { - "kms": "aws", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "aws_undefined_det_explicit_id": { - "kms": "aws", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "aws_undefined_det_explicit_altname": { - "kms": "aws", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "aws_objectId_rand_auto_id": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_rand_auto_altname": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_rand_explicit_id": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_rand_explicit_altname": { - "kms": "aws", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_det_auto_id": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_det_explicit_id": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_objectId_det_explicit_altname": { - "kms": "aws", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "aws_bool_rand_auto_id": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": true - }, - "aws_bool_rand_auto_altname": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": true - }, - "aws_bool_rand_explicit_id": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": true - }, - "aws_bool_rand_explicit_altname": { - "kms": "aws", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": true - }, - "aws_bool_det_explicit_id": { - "kms": "aws", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "aws_bool_det_explicit_altname": { - "kms": "aws", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "aws_date_rand_auto_id": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_rand_auto_altname": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_rand_explicit_id": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_rand_explicit_altname": { - "kms": "aws", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_det_auto_id": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_det_explicit_id": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_date_det_explicit_altname": { - "kms": "aws", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "aws_null_rand_explicit_id": { - "kms": "aws", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "aws_null_rand_explicit_altname": { - "kms": "aws", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "aws_null_det_explicit_id": { - "kms": "aws", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "aws_null_det_explicit_altname": { - "kms": "aws", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "aws_regex_rand_auto_id": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_rand_auto_altname": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_rand_explicit_id": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_rand_explicit_altname": { - "kms": "aws", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_det_auto_id": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_det_explicit_id": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_regex_det_explicit_altname": { - "kms": "aws", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "aws_dbPointer_rand_auto_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_rand_auto_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_rand_explicit_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_rand_explicit_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_det_auto_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_det_explicit_id": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_dbPointer_det_explicit_altname": { - "kms": "aws", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "aws_javascript_rand_auto_id": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_rand_auto_altname": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_rand_explicit_id": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_rand_explicit_altname": { - "kms": "aws", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_det_auto_id": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_det_explicit_id": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_javascript_det_explicit_altname": { - "kms": "aws", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "aws_symbol_rand_auto_id": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_rand_auto_altname": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_rand_explicit_id": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_rand_explicit_altname": { - "kms": "aws", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_det_auto_id": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_det_explicit_id": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_symbol_det_explicit_altname": { - "kms": "aws", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "aws_javascriptWithScope_rand_auto_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_javascriptWithScope_rand_auto_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_javascriptWithScope_rand_explicit_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_javascriptWithScope_rand_explicit_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_javascriptWithScope_det_explicit_id": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_javascriptWithScope_det_explicit_altname": { - "kms": "aws", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "aws_int_rand_auto_id": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_rand_auto_altname": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_rand_explicit_id": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_rand_explicit_altname": { - "kms": "aws", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_det_auto_id": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_det_explicit_id": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_int_det_explicit_altname": { - "kms": "aws", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "aws_timestamp_rand_auto_id": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_rand_auto_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_rand_explicit_id": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_rand_explicit_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_det_auto_id": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_det_explicit_id": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_timestamp_det_explicit_altname": { - "kms": "aws", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "aws_long_rand_auto_id": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_rand_auto_altname": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_rand_explicit_id": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_rand_explicit_altname": { - "kms": "aws", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_det_auto_id": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_det_explicit_id": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_long_det_explicit_altname": { - "kms": "aws", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "aws_decimal_rand_auto_id": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_decimal_rand_auto_altname": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_decimal_rand_explicit_id": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_decimal_rand_explicit_altname": { - "kms": "aws", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_decimal_det_explicit_id": { - "kms": "aws", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_decimal_det_explicit_altname": { - "kms": "aws", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "aws_minKey_rand_explicit_id": { - "kms": "aws", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "aws_minKey_rand_explicit_altname": { - "kms": "aws", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "aws_minKey_det_explicit_id": { - "kms": "aws", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "aws_minKey_det_explicit_altname": { - "kms": "aws", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "aws_maxKey_rand_explicit_id": { - "kms": "aws", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "aws_maxKey_rand_explicit_altname": { - "kms": "aws", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "aws_maxKey_det_explicit_id": { - "kms": "aws", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "aws_maxKey_det_explicit_altname": { - "kms": "aws", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "local_double_rand_auto_id": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "local_double_rand_auto_altname": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "local_double_rand_explicit_id": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "local_double_rand_explicit_altname": { - "kms": "local", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "local_double_det_explicit_id": { - "kms": "local", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "local_double_det_explicit_altname": { - "kms": "local", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "local_string_rand_auto_id": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "local_string_rand_auto_altname": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "local_string_rand_explicit_id": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "local_string_rand_explicit_altname": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "local_string_det_auto_id": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "local_string_det_explicit_id": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "local_string_det_explicit_altname": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "local_object_rand_auto_id": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_object_rand_auto_altname": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_object_rand_explicit_id": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_object_rand_explicit_altname": { - "kms": "local", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_object_det_explicit_id": { - "kms": "local", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_object_det_explicit_altname": { - "kms": "local", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "local_array_rand_auto_id": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_array_rand_auto_altname": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_array_rand_explicit_id": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_array_rand_explicit_altname": { - "kms": "local", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_array_det_explicit_id": { - "kms": "local", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_array_det_explicit_altname": { - "kms": "local", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "local_binData=00_rand_auto_id": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_rand_auto_altname": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_rand_explicit_id": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_rand_explicit_altname": { - "kms": "local", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_det_auto_id": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_det_explicit_id": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=00_det_explicit_altname": { - "kms": "local", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "local_binData=04_rand_auto_id": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_rand_auto_altname": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_rand_explicit_id": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_rand_explicit_altname": { - "kms": "local", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_det_auto_id": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_det_explicit_id": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_binData=04_det_explicit_altname": { - "kms": "local", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "local_undefined_rand_explicit_id": { - "kms": "local", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "local_undefined_rand_explicit_altname": { - "kms": "local", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "local_undefined_det_explicit_id": { - "kms": "local", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "local_undefined_det_explicit_altname": { - "kms": "local", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "local_objectId_rand_auto_id": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_rand_auto_altname": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_rand_explicit_id": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_rand_explicit_altname": { - "kms": "local", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_det_auto_id": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_det_explicit_id": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_objectId_det_explicit_altname": { - "kms": "local", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "local_bool_rand_auto_id": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": true - }, - "local_bool_rand_auto_altname": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": true - }, - "local_bool_rand_explicit_id": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": true - }, - "local_bool_rand_explicit_altname": { - "kms": "local", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": true - }, - "local_bool_det_explicit_id": { - "kms": "local", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "local_bool_det_explicit_altname": { - "kms": "local", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "local_date_rand_auto_id": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_rand_auto_altname": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_rand_explicit_id": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_rand_explicit_altname": { - "kms": "local", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_det_auto_id": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_det_explicit_id": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_date_det_explicit_altname": { - "kms": "local", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "local_null_rand_explicit_id": { - "kms": "local", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "local_null_rand_explicit_altname": { - "kms": "local", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "local_null_det_explicit_id": { - "kms": "local", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "local_null_det_explicit_altname": { - "kms": "local", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "local_regex_rand_auto_id": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_rand_auto_altname": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_rand_explicit_id": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_rand_explicit_altname": { - "kms": "local", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_det_auto_id": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_det_explicit_id": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_regex_det_explicit_altname": { - "kms": "local", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "local_dbPointer_rand_auto_id": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_rand_auto_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_rand_explicit_id": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_rand_explicit_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_det_auto_id": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_det_explicit_id": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_dbPointer_det_explicit_altname": { - "kms": "local", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "local_javascript_rand_auto_id": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_rand_auto_altname": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_rand_explicit_id": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_rand_explicit_altname": { - "kms": "local", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_det_auto_id": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_det_explicit_id": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_javascript_det_explicit_altname": { - "kms": "local", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "local_symbol_rand_auto_id": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_rand_auto_altname": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_rand_explicit_id": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_rand_explicit_altname": { - "kms": "local", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_det_auto_id": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_det_explicit_id": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_symbol_det_explicit_altname": { - "kms": "local", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "local_javascriptWithScope_rand_auto_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_javascriptWithScope_rand_auto_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_javascriptWithScope_rand_explicit_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_javascriptWithScope_rand_explicit_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_javascriptWithScope_det_explicit_id": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_javascriptWithScope_det_explicit_altname": { - "kms": "local", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "local_int_rand_auto_id": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_rand_auto_altname": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_rand_explicit_id": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_rand_explicit_altname": { - "kms": "local", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_det_auto_id": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_det_explicit_id": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_int_det_explicit_altname": { - "kms": "local", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "local_timestamp_rand_auto_id": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_rand_auto_altname": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_rand_explicit_id": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_rand_explicit_altname": { - "kms": "local", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_det_auto_id": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_det_explicit_id": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_timestamp_det_explicit_altname": { - "kms": "local", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "local_long_rand_auto_id": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_rand_auto_altname": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_rand_explicit_id": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_rand_explicit_altname": { - "kms": "local", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_det_auto_id": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_det_explicit_id": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_long_det_explicit_altname": { - "kms": "local", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "local_decimal_rand_auto_id": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_decimal_rand_auto_altname": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_decimal_rand_explicit_id": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_decimal_rand_explicit_altname": { - "kms": "local", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_decimal_det_explicit_id": { - "kms": "local", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_decimal_det_explicit_altname": { - "kms": "local", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "local_minKey_rand_explicit_id": { - "kms": "local", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "local_minKey_rand_explicit_altname": { - "kms": "local", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "local_minKey_det_explicit_id": { - "kms": "local", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "local_minKey_det_explicit_altname": { - "kms": "local", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "local_maxKey_rand_explicit_id": { - "kms": "local", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "local_maxKey_rand_explicit_altname": { - "kms": "local", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "local_maxKey_det_explicit_id": { - "kms": "local", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "local_maxKey_det_explicit_altname": { - "kms": "local", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_double_rand_auto_id": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_double_rand_auto_altname": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_double_rand_explicit_id": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_double_rand_explicit_altname": { - "kms": "azure", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_double_det_explicit_id": { - "kms": "azure", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_double_det_explicit_altname": { - "kms": "azure", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "azure_string_rand_auto_id": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "azure_string_rand_auto_altname": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "azure_string_rand_explicit_id": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "azure_string_rand_explicit_altname": { - "kms": "azure", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "azure_string_det_auto_id": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "azure_string_det_explicit_id": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "azure_string_det_explicit_altname": { - "kms": "azure", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "azure_object_rand_auto_id": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_rand_auto_altname": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_rand_explicit_id": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_rand_explicit_altname": { - "kms": "azure", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_det_explicit_id": { - "kms": "azure", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_object_det_explicit_altname": { - "kms": "azure", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "azure_array_rand_auto_id": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_rand_auto_altname": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_rand_explicit_id": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_rand_explicit_altname": { - "kms": "azure", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_det_explicit_id": { - "kms": "azure", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_array_det_explicit_altname": { - "kms": "azure", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "azure_binData=00_rand_auto_id": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_rand_auto_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_rand_explicit_id": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_rand_explicit_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_det_auto_id": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_det_explicit_id": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=00_det_explicit_altname": { - "kms": "azure", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "azure_binData=04_rand_auto_id": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_rand_auto_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_rand_explicit_id": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_rand_explicit_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_det_auto_id": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_det_explicit_id": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_binData=04_det_explicit_altname": { - "kms": "azure", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "azure_undefined_rand_explicit_id": { - "kms": "azure", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_rand_explicit_altname": { - "kms": "azure", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_det_explicit_id": { - "kms": "azure", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_undefined_det_explicit_altname": { - "kms": "azure", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "azure_objectId_rand_auto_id": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_rand_auto_altname": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_rand_explicit_id": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_rand_explicit_altname": { - "kms": "azure", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_det_auto_id": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_det_explicit_id": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_objectId_det_explicit_altname": { - "kms": "azure", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "azure_bool_rand_auto_id": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": true - }, - "azure_bool_rand_auto_altname": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": true - }, - "azure_bool_rand_explicit_id": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": true - }, - "azure_bool_rand_explicit_altname": { - "kms": "azure", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": true - }, - "azure_bool_det_explicit_id": { - "kms": "azure", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "azure_bool_det_explicit_altname": { - "kms": "azure", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "azure_date_rand_auto_id": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_rand_auto_altname": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_rand_explicit_id": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_rand_explicit_altname": { - "kms": "azure", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_det_auto_id": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_det_explicit_id": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_date_det_explicit_altname": { - "kms": "azure", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "azure_null_rand_explicit_id": { - "kms": "azure", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "azure_null_rand_explicit_altname": { - "kms": "azure", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "azure_null_det_explicit_id": { - "kms": "azure", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "azure_null_det_explicit_altname": { - "kms": "azure", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "azure_regex_rand_auto_id": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_rand_auto_altname": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_rand_explicit_id": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_rand_explicit_altname": { - "kms": "azure", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_det_auto_id": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_det_explicit_id": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_regex_det_explicit_altname": { - "kms": "azure", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "azure_dbPointer_rand_auto_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_rand_auto_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_rand_explicit_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_rand_explicit_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_det_auto_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_det_explicit_id": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_dbPointer_det_explicit_altname": { - "kms": "azure", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "azure_javascript_rand_auto_id": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_rand_auto_altname": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_rand_explicit_id": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_rand_explicit_altname": { - "kms": "azure", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_det_auto_id": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_det_explicit_id": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_javascript_det_explicit_altname": { - "kms": "azure", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "azure_symbol_rand_auto_id": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_rand_auto_altname": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_rand_explicit_id": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_rand_explicit_altname": { - "kms": "azure", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_det_auto_id": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_det_explicit_id": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_symbol_det_explicit_altname": { - "kms": "azure", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "azure_javascriptWithScope_rand_auto_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_rand_auto_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_rand_explicit_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_rand_explicit_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_det_explicit_id": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_javascriptWithScope_det_explicit_altname": { - "kms": "azure", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "azure_int_rand_auto_id": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_rand_auto_altname": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_rand_explicit_id": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_rand_explicit_altname": { - "kms": "azure", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_det_auto_id": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_det_explicit_id": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_int_det_explicit_altname": { - "kms": "azure", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "azure_timestamp_rand_auto_id": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_rand_auto_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_rand_explicit_id": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_rand_explicit_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_det_auto_id": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_det_explicit_id": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_timestamp_det_explicit_altname": { - "kms": "azure", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "azure_long_rand_auto_id": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_rand_auto_altname": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_rand_explicit_id": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_rand_explicit_altname": { - "kms": "azure", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_det_auto_id": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_det_explicit_id": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_long_det_explicit_altname": { - "kms": "azure", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "azure_decimal_rand_auto_id": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_rand_auto_altname": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_rand_explicit_id": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_rand_explicit_altname": { - "kms": "azure", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_det_explicit_id": { - "kms": "azure", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_decimal_det_explicit_altname": { - "kms": "azure", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "azure_minKey_rand_explicit_id": { - "kms": "azure", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_rand_explicit_altname": { - "kms": "azure", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_det_explicit_id": { - "kms": "azure", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_minKey_det_explicit_altname": { - "kms": "azure", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "azure_maxKey_rand_explicit_id": { - "kms": "azure", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_rand_explicit_altname": { - "kms": "azure", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_det_explicit_id": { - "kms": "azure", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "azure_maxKey_det_explicit_altname": { - "kms": "azure", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_double_rand_auto_id": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_double_rand_auto_altname": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_double_rand_explicit_id": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_double_rand_explicit_altname": { - "kms": "gcp", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_double_det_explicit_id": { - "kms": "gcp", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_double_det_explicit_altname": { - "kms": "gcp", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "gcp_string_rand_auto_id": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_rand_auto_altname": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_rand_explicit_id": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_rand_explicit_altname": { - "kms": "gcp", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_det_auto_id": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_det_explicit_id": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "gcp_string_det_explicit_altname": { - "kms": "gcp", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "gcp_object_rand_auto_id": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_rand_auto_altname": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_rand_explicit_id": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_rand_explicit_altname": { - "kms": "gcp", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_det_explicit_id": { - "kms": "gcp", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_object_det_explicit_altname": { - "kms": "gcp", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "gcp_array_rand_auto_id": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_rand_auto_altname": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_rand_explicit_id": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_rand_explicit_altname": { - "kms": "gcp", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_det_explicit_id": { - "kms": "gcp", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_array_det_explicit_altname": { - "kms": "gcp", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "gcp_binData=00_rand_auto_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_rand_auto_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_rand_explicit_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_rand_explicit_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_det_auto_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_det_explicit_id": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=00_det_explicit_altname": { - "kms": "gcp", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "gcp_binData=04_rand_auto_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_rand_auto_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_rand_explicit_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_rand_explicit_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_det_auto_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_det_explicit_id": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_binData=04_det_explicit_altname": { - "kms": "gcp", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "gcp_undefined_rand_explicit_id": { - "kms": "gcp", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_rand_explicit_altname": { - "kms": "gcp", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_det_explicit_id": { - "kms": "gcp", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_undefined_det_explicit_altname": { - "kms": "gcp", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "gcp_objectId_rand_auto_id": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_rand_auto_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_rand_explicit_id": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_rand_explicit_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_det_auto_id": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_det_explicit_id": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_objectId_det_explicit_altname": { - "kms": "gcp", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "gcp_bool_rand_auto_id": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": true - }, - "gcp_bool_rand_auto_altname": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": true - }, - "gcp_bool_rand_explicit_id": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": true - }, - "gcp_bool_rand_explicit_altname": { - "kms": "gcp", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": true - }, - "gcp_bool_det_explicit_id": { - "kms": "gcp", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "gcp_bool_det_explicit_altname": { - "kms": "gcp", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "gcp_date_rand_auto_id": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_rand_auto_altname": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_rand_explicit_id": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_rand_explicit_altname": { - "kms": "gcp", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_det_auto_id": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_det_explicit_id": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_date_det_explicit_altname": { - "kms": "gcp", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "gcp_null_rand_explicit_id": { - "kms": "gcp", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "gcp_null_rand_explicit_altname": { - "kms": "gcp", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "gcp_null_det_explicit_id": { - "kms": "gcp", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "gcp_null_det_explicit_altname": { - "kms": "gcp", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "gcp_regex_rand_auto_id": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_rand_auto_altname": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_rand_explicit_id": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_rand_explicit_altname": { - "kms": "gcp", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_det_auto_id": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_det_explicit_id": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_regex_det_explicit_altname": { - "kms": "gcp", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "gcp_dbPointer_rand_auto_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_rand_auto_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_rand_explicit_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_rand_explicit_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_det_auto_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_det_explicit_id": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_dbPointer_det_explicit_altname": { - "kms": "gcp", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "gcp_javascript_rand_auto_id": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_rand_auto_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_rand_explicit_id": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_rand_explicit_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_det_auto_id": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_det_explicit_id": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_javascript_det_explicit_altname": { - "kms": "gcp", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "gcp_symbol_rand_auto_id": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_rand_auto_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_rand_explicit_id": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_rand_explicit_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_det_auto_id": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_det_explicit_id": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_symbol_det_explicit_altname": { - "kms": "gcp", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "gcp_javascriptWithScope_rand_auto_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_rand_auto_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_rand_explicit_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_rand_explicit_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_det_explicit_id": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_javascriptWithScope_det_explicit_altname": { - "kms": "gcp", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "gcp_int_rand_auto_id": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_rand_auto_altname": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_rand_explicit_id": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_rand_explicit_altname": { - "kms": "gcp", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_det_auto_id": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_det_explicit_id": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_int_det_explicit_altname": { - "kms": "gcp", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "gcp_timestamp_rand_auto_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_rand_auto_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_rand_explicit_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_rand_explicit_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_det_auto_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_det_explicit_id": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_timestamp_det_explicit_altname": { - "kms": "gcp", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "gcp_long_rand_auto_id": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_rand_auto_altname": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_rand_explicit_id": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_rand_explicit_altname": { - "kms": "gcp", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_det_auto_id": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_det_explicit_id": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_long_det_explicit_altname": { - "kms": "gcp", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "gcp_decimal_rand_auto_id": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_rand_auto_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_rand_explicit_id": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_rand_explicit_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_det_explicit_id": { - "kms": "gcp", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_decimal_det_explicit_altname": { - "kms": "gcp", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "gcp_minKey_rand_explicit_id": { - "kms": "gcp", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_rand_explicit_altname": { - "kms": "gcp", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_det_explicit_id": { - "kms": "gcp", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_minKey_det_explicit_altname": { - "kms": "gcp", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "gcp_maxKey_rand_explicit_id": { - "kms": "gcp", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_rand_explicit_altname": { - "kms": "gcp", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_det_explicit_id": { - "kms": "gcp", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "gcp_maxKey_det_explicit_altname": { - "kms": "gcp", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_double_rand_auto_id": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_double_rand_auto_altname": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_double_rand_explicit_id": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_double_rand_explicit_altname": { - "kms": "kmip", - "type": "double", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_double_det_explicit_id": { - "kms": "kmip", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_double_det_explicit_altname": { - "kms": "kmip", - "type": "double", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDouble": "1.234" - } - }, - "kmip_string_rand_auto_id": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_rand_auto_altname": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_rand_explicit_id": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_rand_explicit_altname": { - "kms": "kmip", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_det_auto_id": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_det_explicit_id": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "mongodb" - }, - "kmip_string_det_explicit_altname": { - "kms": "kmip", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": "mongodb" - }, - "kmip_object_rand_auto_id": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_rand_auto_altname": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_rand_explicit_id": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_rand_explicit_altname": { - "kms": "kmip", - "type": "object", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_det_explicit_id": { - "kms": "kmip", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_object_det_explicit_altname": { - "kms": "kmip", - "type": "object", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "x": { - "$numberInt": "1" - } - } - }, - "kmip_array_rand_auto_id": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_rand_auto_altname": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_rand_explicit_id": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_rand_explicit_altname": { - "kms": "kmip", - "type": "array", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_det_explicit_id": { - "kms": "kmip", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_array_det_explicit_altname": { - "kms": "kmip", - "type": "array", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": [ - { - "$numberInt": "1" - }, - { - "$numberInt": "2" - }, - { - "$numberInt": "3" - } - ] - }, - "kmip_binData=00_rand_auto_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_rand_auto_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_rand_explicit_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_rand_explicit_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_det_auto_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_det_explicit_id": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=00_det_explicit_altname": { - "kms": "kmip", - "type": "binData=00", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AQIDBA==", - "subType": "00" - } - } - }, - "kmip_binData=04_rand_auto_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_rand_auto_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_rand_explicit_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_rand_explicit_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_det_auto_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_det_explicit_id": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_binData=04_det_explicit_altname": { - "kms": "kmip", - "type": "binData=04", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$binary": { - "base64": "AAECAwQFBgcICQoLDA0ODw==", - "subType": "04" - } - } - }, - "kmip_undefined_rand_explicit_id": { - "kms": "kmip", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_rand_explicit_altname": { - "kms": "kmip", - "type": "undefined", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_det_explicit_id": { - "kms": "kmip", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_undefined_det_explicit_altname": { - "kms": "kmip", - "type": "undefined", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$undefined": true - } - }, - "kmip_objectId_rand_auto_id": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_rand_auto_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_rand_explicit_id": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_rand_explicit_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_det_auto_id": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_det_explicit_id": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_objectId_det_explicit_altname": { - "kms": "kmip", - "type": "objectId", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$oid": "01234567890abcdef0123456" - } - }, - "kmip_bool_rand_auto_id": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": true - }, - "kmip_bool_rand_auto_altname": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": true - }, - "kmip_bool_rand_explicit_id": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": true - }, - "kmip_bool_rand_explicit_altname": { - "kms": "kmip", - "type": "bool", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": true - }, - "kmip_bool_det_explicit_id": { - "kms": "kmip", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": true - }, - "kmip_bool_det_explicit_altname": { - "kms": "kmip", - "type": "bool", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": true - }, - "kmip_date_rand_auto_id": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_rand_auto_altname": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_rand_explicit_id": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_rand_explicit_altname": { - "kms": "kmip", - "type": "date", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_det_auto_id": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_det_explicit_id": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_date_det_explicit_altname": { - "kms": "kmip", - "type": "date", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$date": { - "$numberLong": "12345" - } - } - }, - "kmip_null_rand_explicit_id": { - "kms": "kmip", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "kmip_null_rand_explicit_altname": { - "kms": "kmip", - "type": "null", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "kmip_null_det_explicit_id": { - "kms": "kmip", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": null - }, - "kmip_null_det_explicit_altname": { - "kms": "kmip", - "type": "null", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": null - }, - "kmip_regex_rand_auto_id": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_rand_auto_altname": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_rand_explicit_id": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_rand_explicit_altname": { - "kms": "kmip", - "type": "regex", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_det_auto_id": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_det_explicit_id": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_regex_det_explicit_altname": { - "kms": "kmip", - "type": "regex", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$regularExpression": { - "pattern": ".*", - "options": "" - } - } - }, - "kmip_dbPointer_rand_auto_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_rand_auto_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_rand_explicit_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_rand_explicit_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_det_auto_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_det_explicit_id": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_dbPointer_det_explicit_altname": { - "kms": "kmip", - "type": "dbPointer", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$dbPointer": { - "$ref": "db.example", - "$id": { - "$oid": "01234567890abcdef0123456" - } - } - } - }, - "kmip_javascript_rand_auto_id": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_rand_auto_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_rand_explicit_id": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_rand_explicit_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_det_auto_id": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_det_explicit_id": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_javascript_det_explicit_altname": { - "kms": "kmip", - "type": "javascript", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1" - } - }, - "kmip_symbol_rand_auto_id": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_rand_auto_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_rand_explicit_id": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_rand_explicit_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_det_auto_id": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_det_explicit_id": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_symbol_det_explicit_altname": { - "kms": "kmip", - "type": "symbol", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$symbol": "mongodb-symbol" - } - }, - "kmip_javascriptWithScope_rand_auto_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_rand_auto_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_rand_explicit_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_rand_explicit_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_det_explicit_id": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_javascriptWithScope_det_explicit_altname": { - "kms": "kmip", - "type": "javascriptWithScope", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$code": "x=1", - "$scope": {} - } - }, - "kmip_int_rand_auto_id": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_rand_auto_altname": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_rand_explicit_id": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_rand_explicit_altname": { - "kms": "kmip", - "type": "int", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_det_auto_id": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_det_explicit_id": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_int_det_explicit_altname": { - "kms": "kmip", - "type": "int", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberInt": "123" - } - }, - "kmip_timestamp_rand_auto_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_rand_auto_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_rand_explicit_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_rand_explicit_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_det_auto_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_det_explicit_id": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_timestamp_det_explicit_altname": { - "kms": "kmip", - "type": "timestamp", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$timestamp": { - "t": 0, - "i": 12345 - } - } - }, - "kmip_long_rand_auto_id": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_rand_auto_altname": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_rand_explicit_id": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_rand_explicit_altname": { - "kms": "kmip", - "type": "long", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_det_auto_id": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_det_explicit_id": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_long_det_explicit_altname": { - "kms": "kmip", - "type": "long", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberLong": "456" - } - }, - "kmip_decimal_rand_auto_id": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_rand_auto_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "auto", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_rand_explicit_id": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_rand_explicit_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": true, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_det_explicit_id": { - "kms": "kmip", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_decimal_det_explicit_altname": { - "kms": "kmip", - "type": "decimal", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$numberDecimal": "1.234" - } - }, - "kmip_minKey_rand_explicit_id": { - "kms": "kmip", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_rand_explicit_altname": { - "kms": "kmip", - "type": "minKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_det_explicit_id": { - "kms": "kmip", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_minKey_det_explicit_altname": { - "kms": "kmip", - "type": "minKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$minKey": 1 - } - }, - "kmip_maxKey_rand_explicit_id": { - "kms": "kmip", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_rand_explicit_altname": { - "kms": "kmip", - "type": "maxKey", - "algo": "rand", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_det_explicit_id": { - "kms": "kmip", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "kmip_maxKey_det_explicit_altname": { - "kms": "kmip", - "type": "maxKey", - "algo": "det", - "method": "explicit", - "identifier": "altname", - "allowed": false, - "value": { - "$maxKey": 1 - } - }, - "payload=0,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "" - }, - "payload=1,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "a" - }, - "payload=2,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aa" - }, - "payload=3,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaa" - }, - "payload=4,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaa" - }, - "payload=5,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaa" - }, - "payload=6,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaa" - }, - "payload=7,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaa" - }, - "payload=8,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaa" - }, - "payload=9,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaa" - }, - "payload=10,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaa" - }, - "payload=11,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaa" - }, - "payload=12,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaa" - }, - "payload=13,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaa" - }, - "payload=14,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaa" - }, - "payload=15,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaaa" - }, - "payload=16,algo=rand": { - "kms": "local", - "type": "string", - "algo": "rand", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaaaa" - }, - "payload=0,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "" - }, - "payload=1,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "a" - }, - "payload=2,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aa" - }, - "payload=3,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaa" - }, - "payload=4,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaa" - }, - "payload=5,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaa" - }, - "payload=6,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaa" - }, - "payload=7,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaa" - }, - "payload=8,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaa" - }, - "payload=9,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaa" - }, - "payload=10,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaa" - }, - "payload=11,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaa" - }, - "payload=12,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaa" - }, - "payload=13,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaa" - }, - "payload=14,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaa" - }, - "payload=15,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaaa" - }, - "payload=16,algo=det": { - "kms": "local", - "type": "string", - "algo": "det", - "method": "explicit", - "identifier": "id", - "allowed": true, - "value": "aaaaaaaaaaaaaaaa" - } -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json b/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json deleted file mode 100644 index 2364590e4..000000000 --- a/tests/SpecTests/client-side-encryption/etc/data/encryptedFields.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json deleted file mode 100644 index 566b56c35..000000000 --- a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-document.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } -} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json deleted file mode 100644 index 7d18f52eb..000000000 --- a/tests/SpecTests/client-side-encryption/etc/data/keys/key1-id.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } -} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json deleted file mode 100644 index a654d980b..000000000 --- a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-document.json +++ /dev/null @@ -1,30 +0,0 @@ -{ - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } -} diff --git a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json b/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json deleted file mode 100644 index 6e9b87bbc..000000000 --- a/tests/SpecTests/client-side-encryption/etc/data/keys/key2-id.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } -} diff --git a/tests/SpecTests/client-side-encryption/external/external-key.json b/tests/SpecTests/client-side-encryption/external/external-key.json deleted file mode 100644 index b3fe0723b..000000000 --- a/tests/SpecTests/client-side-encryption/external/external-key.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "status": { - "$numberInt": "1" - }, - "_id": { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - }, - "updateDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyMaterial": { - "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyAltNames": [ "local" ] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/external/external-schema.json b/tests/SpecTests/client-side-encryption/external/external-schema.json deleted file mode 100644 index 7d8cad8c3..000000000 --- a/tests/SpecTests/client-side-encryption/external/external-schema.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "properties": { - "encrypted": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" -} diff --git a/tests/SpecTests/client-side-encryption/limits/limits-doc.json b/tests/SpecTests/client-side-encryption/limits/limits-doc.json deleted file mode 100644 index 53de52326..000000000 --- a/tests/SpecTests/client-side-encryption/limits/limits-doc.json +++ /dev/null @@ -1,102 +0,0 @@ -{ - "00": "a", - "01": "a", - "02": "a", - "03": "a", - "04": "a", - "05": "a", - "06": "a", - "07": "a", - "08": "a", - "09": "a", - "10": "a", - "11": "a", - "12": "a", - "13": "a", - "14": "a", - "15": "a", - "16": "a", - "17": "a", - "18": "a", - "19": "a", - "20": "a", - "21": "a", - "22": "a", - "23": "a", - "24": "a", - "25": "a", - "26": "a", - "27": "a", - "28": "a", - "29": "a", - "30": "a", - "31": "a", - "32": "a", - "33": "a", - "34": "a", - "35": "a", - "36": "a", - "37": "a", - "38": "a", - "39": "a", - "40": "a", - "41": "a", - "42": "a", - "43": "a", - "44": "a", - "45": "a", - "46": "a", - "47": "a", - "48": "a", - "49": "a", - "50": "a", - "51": "a", - "52": "a", - "53": "a", - "54": "a", - "55": "a", - "56": "a", - "57": "a", - "58": "a", - "59": "a", - "60": "a", - "61": "a", - "62": "a", - "63": "a", - "64": "a", - "65": "a", - "66": "a", - "67": "a", - "68": "a", - "69": "a", - "70": "a", - "71": "a", - "72": "a", - "73": "a", - "74": "a", - "75": "a", - "76": "a", - "77": "a", - "78": "a", - "79": "a", - "80": "a", - "81": "a", - "82": "a", - "83": "a", - "84": "a", - "85": "a", - "86": "a", - "87": "a", - "88": "a", - "89": "a", - "90": "a", - "91": "a", - "92": "a", - "93": "a", - "94": "a", - "95": "a", - "96": "a", - "97": "a", - "98": "a", - "99": "a" -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/limits/limits-key.json b/tests/SpecTests/client-side-encryption/limits/limits-key.json deleted file mode 100644 index b3fe0723b..000000000 --- a/tests/SpecTests/client-side-encryption/limits/limits-key.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "status": { - "$numberInt": "1" - }, - "_id": { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - }, - "updateDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyMaterial": { - "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1557827033449" - } - }, - "keyAltNames": [ "local" ] -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/limits/limits-schema.json b/tests/SpecTests/client-side-encryption/limits/limits-schema.json deleted file mode 100644 index c06908d9c..000000000 --- a/tests/SpecTests/client-side-encryption/limits/limits-schema.json +++ /dev/null @@ -1,1405 +0,0 @@ -{ - "properties": { - "00": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "01": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "02": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "03": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "04": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "05": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "06": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "07": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "08": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "09": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "10": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "11": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "12": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "13": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "14": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "15": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "16": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "17": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "18": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "19": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "20": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "21": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "22": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "23": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "24": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "25": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "26": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "27": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "28": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "29": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "30": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "31": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "32": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "33": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "34": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "35": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "36": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "37": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "38": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "39": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "40": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "41": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "42": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "43": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "44": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "45": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "46": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "47": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "48": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "49": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "50": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "51": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "52": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "53": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "54": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "55": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "56": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "57": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "58": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "59": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "60": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "61": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "62": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "63": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "64": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "65": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "66": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "67": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "68": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "69": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "70": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "71": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "72": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "73": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "74": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "75": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "76": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "77": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "78": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "79": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "80": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "81": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "82": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "83": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "84": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "85": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "86": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "87": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "88": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "89": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "90": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "91": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "92": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "93": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "94": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "95": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "96": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "97": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "98": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "99": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "LOCALAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" -} \ No newline at end of file diff --git a/tests/SpecTests/client-side-encryption/tests/aggregate.json b/tests/SpecTests/client-side-encryption/tests/aggregate.json deleted file mode 100644 index 7de725b71..000000000 --- a/tests/SpecTests/client-side-encryption/tests/aggregate.json +++ /dev/null @@ -1,390 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Aggregate with deterministic encryption", - "skipReason": "SERVER-39395", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "encrypted_string": "457-55-5642" - } - } - ] - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "default", - "pipeline": [ - { - "$match": { - "encrypted_string": "457-55-5642" - } - } - ] - }, - "command_name": "aggregate" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Aggregate with empty pipeline", - "skipReason": "SERVER-40829 hides agg support behind enableTestCommands flag.", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [] - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "default", - "pipeline": [], - "cursor": {} - }, - "command_name": "aggregate" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Aggregate should fail with random encryption", - "skipReason": "SERVER-39395", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "random": "abc" - } - } - ] - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - }, - { - "description": "Database aggregate should fail", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "aggregate", - "object": "database", - "arguments": { - "pipeline": [ - { - "$currentOp": { - "allUsers": false, - "idleConnections": false, - "localOps": true - } - }, - { - "$match": { - "command.aggregate": { - "$eq": 1 - } - } - }, - { - "$project": { - "command": 1 - } - }, - { - "$project": { - "command.lsid": 0 - } - } - ] - }, - "result": { - "errorContains": "non-collection command not supported for auto encryption: aggregate" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/awsTemporary.json b/tests/SpecTests/client-side-encryption/tests/awsTemporary.json deleted file mode 100644 index 10eb85fee..000000000 --- a/tests/SpecTests/client-side-encryption/tests/awsTemporary.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using the AWS provider with temporary credentials", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "awsTemporary": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Insert with invalid temporary credentials", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "awsTemporaryNoSessionToken": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "security token" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/azureKMS.json b/tests/SpecTests/client-side-encryption/tests/azureKMS.json deleted file mode 100644 index afecf40b0..000000000 --- a/tests/SpecTests/client-side-encryption/tests/azureKMS.json +++ /dev/null @@ -1,224 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "n+HWZ0ZSVOYA3cvQgP7inN4JSXfOH85IngmeQxRpQHjCCcqT3IFqEWNlrsVHiz3AELimHhX4HKqOLWMUeSIT6emUDDoQX9BAv8DR1+E1w4nGs/NyEneac78EYFkK3JysrFDOgl2ypCCTKAypkn9CkAx1if4cfgQE93LW4kczcyHdGiH36CIxrCDGv1UzAvERN5Qa47DVwsM6a+hWsF2AAAJVnF0wYLLJU07TuRHdMrrphPWXZsFgyV+lRqJ7DDpReKNO8nMPLV/mHqHBHGPGQiRdb9NoJo8CvokGz4+KE8oLwzKf6V24dtwZmRkrsDV4iOhvROAzz+Euo1ypSkL3mw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601573901680" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyAltNames": [ - "altname", - "azure_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using Azure KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "azure": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_azure": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_azure": { - "$binary": { - "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_azure": { - "$binary": { - "base64": "AQGVERPgAAAAAAAAAAAAAAAC5DbBSwPwfSlBrDtRuglvNvCXD1KzDuCKY2P+4bRFtHDjpTOE2XuytPAUaAbXf1orsPq59PVZmsbTZbt2CB8qaQ==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/badQueries.json b/tests/SpecTests/client-side-encryption/tests/badQueries.json deleted file mode 100644 index 4968307ba..000000000 --- a/tests/SpecTests/client-side-encryption/tests/badQueries.json +++ /dev/null @@ -1,1446 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "$text unconditionally fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "$text": { - "$search": "search text" - } - } - }, - "result": { - "errorContains": "Unsupported match expression operator for encryption" - } - } - ] - }, - { - "description": "$where unconditionally fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "$where": { - "$code": "function() { return true }" - } - } - }, - "result": { - "errorContains": "Unsupported match expression operator for encryption" - } - } - ] - }, - { - "description": "$bit operators succeed on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$bitsAllClear": 35 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$bitsAllClear": 35 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$bitsAllSet": 35 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$bitsAllSet": 35 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$bitsAnyClear": 35 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$bitsAnyClear": 35 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$bitsAnySet": 35 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$bitsAnySet": 35 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - } - ] - }, - { - "description": "geo operators succeed on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$near": [ - 0, - 0 - ] - } - } - }, - "result": { - "errorContains": "unable to find index" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$near": [ - 0, - 0 - ] - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$nearSphere": [ - 0, - 0 - ] - } - } - }, - "result": { - "errorContains": "unable to find index" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$nearSphere": [ - 0, - 0 - ] - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$geoIntersects": { - "$geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - 0, - 0 - ], - [ - 1, - 0 - ], - [ - 1, - 1 - ], - [ - 0, - 0 - ] - ] - ] - } - } - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$geoIntersects": { - "$geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - 0, - 0 - ], - [ - 1, - 0 - ], - [ - 1, - 1 - ], - [ - 0, - 0 - ] - ] - ] - } - } - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$geoWithin": { - "$geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - 0, - 0 - ], - [ - 1, - 0 - ], - [ - 1, - 1 - ], - [ - 0, - 0 - ] - ] - ] - } - } - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$geoWithin": { - "$geometry": { - "type": "Polygon", - "coordinates": [ - [ - [ - 0, - 0 - ], - [ - 1, - 0 - ], - [ - 1, - 1 - ], - [ - 0, - 0 - ] - ] - ] - } - } - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - } - ] - }, - { - "description": "inequality operators succeed on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$gt": 1 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$gt": 1 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$lt": 1 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$lt": 1 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$gte": 1 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$gte": 1 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$lte": 1 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$lte": 1 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - } - ] - }, - { - "description": "other misc operators succeed on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$mod": [ - 3, - 1 - ] - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$mod": [ - 3, - 1 - ] - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$regex": "pattern", - "$options": "" - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$regex": "pattern", - "$options": "" - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$size": 2 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$size": 2 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$type": 2 - } - } - }, - "result": [] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$type": 2 - } - } - }, - "result": { - "errorContains": "Invalid match expression operator on encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$eq": null - } - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string1" - } - ] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$eq": null - } - } - }, - "result": { - "errorContains": "Illegal equality to null predicate for encrypted field" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "unencrypted": { - "$in": [ - null - ] - } - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string1" - } - ] - }, - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$in": [ - null - ] - } - } - }, - "result": { - "errorContains": "Illegal equality to null inside $in against an encrypted field" - } - } - ] - }, - { - "description": "$addToSet succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$addToSet": { - "unencrypted": [ - "a" - ] - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$addToSet": { - "encrypted_string": [ - "a" - ] - } - } - }, - "result": { - "errorContains": "$addToSet not allowed on encrypted values" - } - } - ] - }, - { - "description": "$inc succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$inc": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$inc": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$inc and $mul not allowed on encrypted values" - } - } - ] - }, - { - "description": "$mul succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$mul": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$mul": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$inc and $mul not allowed on encrypted values" - } - } - ] - }, - { - "description": "$max succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$max": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$max": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$max and $min not allowed on encrypted values" - } - } - ] - }, - { - "description": "$min succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$min": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$min": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$max and $min not allowed on encrypted values" - } - } - ] - }, - { - "description": "$currentDate succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$currentDate": { - "unencrypted": true - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$currentDate": { - "encrypted_string": true - } - } - }, - "result": { - "errorContains": "$currentDate not allowed on encrypted values" - } - } - ] - }, - { - "description": "$pop succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pop": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pop": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$pop not allowed on encrypted values" - } - } - ] - }, - { - "description": "$pull succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pull": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pull": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$pull not allowed on encrypted values" - } - } - ] - }, - { - "description": "$pullAll succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pullAll": { - "unencrypted": [ - 1 - ] - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 0, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$pullAll": { - "encrypted_string": [ - 1 - ] - } - } - }, - "result": { - "errorContains": "$pullAll not allowed on encrypted values" - } - } - ] - }, - { - "description": "$push succeeds on unencrypted, error on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$push": { - "unencrypted": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$push": { - "encrypted_string": 1 - } - } - }, - "result": { - "errorContains": "$push not allowed on encrypted values" - } - } - ] - }, - { - "description": "array filters on encrypted fields does not error in mongocryptd, but errors in mongod", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "encrypted_string.$[i].x": 1 - } - }, - "arrayFilters": [ - { - "i.x": 1 - } - ] - }, - "result": { - "errorContains": "Array update operations not allowed on encrypted values" - } - } - ] - }, - { - "description": "positional operator succeeds on unencrypted, errors on encrypted", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "unencrypted": 1 - }, - "update": { - "$set": { - "unencrypted.$": 1 - } - } - }, - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0 - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "encrypted_string": "abc" - }, - "update": { - "$set": { - "encrypted_string.$": "abc" - } - } - }, - "result": { - "errorContains": "Cannot encrypt fields below '$' positional update operator" - } - } - ] - }, - { - "description": "an update that would produce an array on an encrypted field errors", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "encrypted_string": [ - 1, - 2 - ] - } - } - }, - "result": { - "errorContains": "Cannot encrypt element of type" - } - } - ] - }, - { - "description": "an insert with encrypted field on _id errors", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "schemaMap": { - "default.default": { - "properties": { - "_id": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - }, - "result": { - "errorContains": "Invalid schema containing the 'encrypt' keyword." - } - } - ] - }, - { - "description": "an insert with an array value for an encrypted field fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "encrypted_string": [ - "123", - "456" - ] - } - }, - "result": { - "errorContains": "Cannot encrypt element of type" - } - } - ] - }, - { - "description": "an insert with a Timestamp(0,0) value in the top-level fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "random": { - "$timestamp": { - "t": 0, - "i": 0 - } - } - } - }, - "result": { - "errorContains": "A command that inserts cannot supply Timestamp(0, 0) for an encrypted" - } - } - ] - }, - { - "description": "distinct with the key referring to a field where the keyID is a JSON Pointer errors", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "distinct", - "arguments": { - "filter": {}, - "fieldName": "encrypted_w_altname" - }, - "result": { - "errorContains": "The distinct key is not allowed to be marked for encryption with a non-UUID keyId" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/badSchema.json b/tests/SpecTests/client-side-encryption/tests/badSchema.json deleted file mode 100644 index 1fd0f8ed3..000000000 --- a/tests/SpecTests/client-side-encryption/tests/badSchema.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Schema with an encrypted field in an array", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - } - }, - "bsonType": "array" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "Invalid schema" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "Schema without specifying parent object types", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "foo": { - "properties": { - "bar": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - } - } - } - } - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "Invalid schema" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "Schema with siblings of encrypt document", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - }, - "bsonType": "object" - } - } - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "'encrypt' cannot be used in conjunction with 'bsonType'" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "Schema with logical keywords", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "anyOf": [ - { - "properties": { - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - } - } - } - ] - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "Invalid schema" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/basic.json b/tests/SpecTests/client-side-encryption/tests/basic.json deleted file mode 100644 index 3ed066f53..000000000 --- a/tests/SpecTests/client-side-encryption/tests/basic.json +++ /dev/null @@ -1,350 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Insert with deterministic encryption, then find it", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Insert with randomized encryption, then find it", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "random": "123" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "random": "123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "random": { - "$$type": "binData" - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "random": { - "$$type": "binData" - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/bulk.json b/tests/SpecTests/client-side-encryption/tests/bulk.json deleted file mode 100644 index 1b62e5e8a..000000000 --- a/tests/SpecTests/client-side-encryption/tests/bulk.json +++ /dev/null @@ -1,333 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Bulk write with encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "encrypted_string": "string1" - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "update": { - "$set": { - "encrypted_string": "string1" - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "$and": [ - { - "encrypted_string": "string1" - }, - { - "_id": 2 - } - ] - } - } - } - ], - "options": { - "ordered": true - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - } - } - ], - "ordered": true - }, - "command_name": "update" - } - }, - { - "command_started_event": { - "command": { - "delete": "default", - "deletes": [ - { - "q": { - "$and": [ - { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - }, - { - "_id": { - "$eq": 2 - } - } - ] - }, - "limit": 1 - } - ], - "ordered": true - }, - "command_name": "delete" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/bypassAutoEncryption.json b/tests/SpecTests/client-side-encryption/tests/bypassAutoEncryption.json deleted file mode 100644 index 9d09cb3fa..000000000 --- a/tests/SpecTests/client-side-encryption/tests/bypassAutoEncryption.json +++ /dev/null @@ -1,402 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Insert with bypassAutoEncryption", - "clientOptions": { - "autoEncryptOpts": { - "bypassAutoEncryption": true, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "encrypted_string": "string0" - }, - "bypassDocumentValidation": true - } - }, - { - "name": "find", - "arguments": { - "filter": {} - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 2, - "encrypted_string": "string0" - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": {} - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": "string0" - } - ] - } - } - }, - { - "description": "Insert with bypassAutoEncryption for local schema", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "bypassAutoEncryption": true, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "encrypted_string": "string0" - }, - "bypassDocumentValidation": true - } - }, - { - "name": "find", - "arguments": { - "filter": {} - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 2, - "encrypted_string": "string0" - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": {} - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": "string0" - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json b/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json deleted file mode 100644 index bd0b1c565..000000000 --- a/tests/SpecTests/client-side-encryption/tests/bypassedCommand.json +++ /dev/null @@ -1,106 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": {}, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "ping is bypassed", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "ping", - "arguments": { - "command": { - "ping": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "ping": 1 - }, - "command_name": "ping" - } - } - ] - }, - { - "description": "current op is not bypassed", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "currentOp", - "arguments": { - "command": { - "currentOp": 1 - } - }, - "result": { - "errorContains": "command not supported for auto encryption: currentOp" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/count.json b/tests/SpecTests/client-side-encryption/tests/count.json deleted file mode 100644 index 9df8cd639..000000000 --- a/tests/SpecTests/client-side-encryption/tests/count.json +++ /dev/null @@ -1,229 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Count with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "count", - "arguments": { - "filter": { - "encrypted_string": "string0" - } - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "count": "default", - "query": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - } - }, - "command_name": "count" - } - } - ] - }, - { - "description": "Count fails when filtering on a random encrypted field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "count", - "arguments": { - "filter": { - "random": "abc" - } - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/countDocuments.json b/tests/SpecTests/client-side-encryption/tests/countDocuments.json deleted file mode 100644 index 07ff97f26..000000000 --- a/tests/SpecTests/client-side-encryption/tests/countDocuments.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "countDocuments with deterministic encryption", - "skipReason": "waiting on SERVER-39395", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "countDocuments", - "arguments": { - "filter": { - "encrypted_string": "string0" - } - }, - "result": 1 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "default", - "pipeline": [ - { - "$match": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "command_name": "aggregate" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json b/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json deleted file mode 100644 index 48638a97c..000000000 --- a/tests/SpecTests/client-side-encryption/tests/create-and-createIndexes.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "tests": [ - { - "description": "create is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "unencryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "unencryptedCollection", - "validator": { - "unencrypted_string": "foo" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "unencryptedCollection" - } - } - ] - }, - { - "description": "createIndexes is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "unencryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "unencryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "createIndexes": "unencryptedCollection", - "indexes": [ - { - "name": "name", - "key": { - "name": 1 - } - } - ] - } - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "unencryptedCollection", - "index": "name" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/delete.json b/tests/SpecTests/client-side-encryption/tests/delete.json deleted file mode 100644 index a6f4ffde9..000000000 --- a/tests/SpecTests/client-side-encryption/tests/delete.json +++ /dev/null @@ -1,340 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "deleteOne with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "encrypted_string": "string0" - } - }, - "result": { - "deletedCount": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "delete": "default", - "deletes": [ - { - "q": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ], - "ordered": true - }, - "command_name": "delete" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "deleteMany with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "encrypted_string": { - "$in": [ - "string0", - "string1" - ] - } - } - }, - "result": { - "deletedCount": 2 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "delete": "default", - "deletes": [ - { - "q": { - "encrypted_string": { - "$in": [ - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - ] - } - }, - "limit": 0 - } - ], - "ordered": true - }, - "command_name": "delete" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/distinct.json b/tests/SpecTests/client-side-encryption/tests/distinct.json deleted file mode 100644 index 9786b0781..000000000 --- a/tests/SpecTests/client-side-encryption/tests/distinct.json +++ /dev/null @@ -1,276 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 3, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "distinct with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "distinct", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "fieldName": "encrypted_string" - }, - "result": [ - "string0" - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "distinct": "default", - "key": "encrypted_string", - "query": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - } - }, - "command_name": "distinct" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 3, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Distinct fails when filtering on a random encrypted field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "distinct", - "arguments": { - "filter": { - "random": "abc" - }, - "fieldName": "encrypted_string" - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/explain.json b/tests/SpecTests/client-side-encryption/tests/explain.json deleted file mode 100644 index 0e451e481..000000000 --- a/tests/SpecTests/client-side-encryption/tests/explain.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Explain a find with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "explain", - "arguments": { - "command": { - "explain": { - "find": "default", - "filter": { - "encrypted_string": "string1" - } - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "explain": { - "find": "default", - "filter": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - } - }, - "verbosity": "allPlansExecution" - }, - "command_name": "explain" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/find.json b/tests/SpecTests/client-side-encryption/tests/find.json deleted file mode 100644 index 1feddab0e..000000000 --- a/tests/SpecTests/client-side-encryption/tests/find.json +++ /dev/null @@ -1,408 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$binary": { - "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Find with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": "string0" - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$binary": { - "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Find with $in with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "encrypted_string": { - "$in": [ - "string0", - "string1" - ] - } - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string1", - "random": "abc" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "encrypted_string": { - "$in": [ - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - ] - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$binary": { - "base64": "AgAAAAAAAAAAAAAAAAAAAAACyfp+lXvKOi7f5vh6ZsCijLEaXFKq1X06RmyS98ZvmMQGixTw8HM1f/bGxZjGwvYwjXOkIEb7Exgb8p2KCDI5TQ==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "Find fails when filtering on a random encrypted field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "random": "abc" - } - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json b/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json deleted file mode 100644 index e418a4581..000000000 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndDelete.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "findOneAndDelete with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "encrypted_string": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default", - "query": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "remove": true - }, - "command_name": "findAndModify" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json b/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json deleted file mode 100644 index 78baca843..000000000 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndReplace.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "findOneAndReplace with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "replacement": { - "encrypted_string": "string1" - }, - "returnDocument": "Before" - }, - "result": { - "_id": 1, - "encrypted_string": "string0" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default", - "query": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "update": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - }, - "command_name": "findAndModify" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json deleted file mode 100644 index 1d8585115..000000000 --- a/tests/SpecTests/client-side-encryption/tests/findOneAndUpdate.json +++ /dev/null @@ -1,231 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "findOneAndUpdate with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "update": { - "$set": { - "encrypted_string": "string1" - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 1, - "encrypted_string": "string0" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default", - "query": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "update": { - "$set": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - } - }, - "command_name": "findAndModify" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-BypassQueryAnalysis.json b/tests/SpecTests/client-side-encryption/tests/fle2-BypassQueryAnalysis.json deleted file mode 100644 index 4272e16a8..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-BypassQueryAnalysis.json +++ /dev/null @@ -1,291 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - }, - { - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "BypassQueryAnalysis decrypts", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "bypassQueryAnalysis": true - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": { - "$binary": { - "base64": "BHEBAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVjACAAAAAAsZXWOWA+UiCBbrJNB6bHflB/cn7pWSvwWN2jw4FPeIUFcABQAAAAAMdD1nV2nqeI1eXEQNskDflCy8I7/HvvqDKJ6XxjhrPQWdLqjz+8GosGUsB7A8ee/uG9/guENuL25XD+Fxxkv1LLXtavHOlLF7iW0u9yabqqBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AE0AAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSFtVvkUcnHWj/rfPW7iJ0G3UJ8zpuBmUM/VjOMJCY4+eDqdTiPIwX+/vNXegc8FZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsAA==", - "subType": "06" - } - } - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encryptedIndexed": "value123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$binary": { - "base64": "BHEBAAAFZAAgAAAAAHb62aV7+mqmaGcotPLdG3KP7S8diFwWMLM/5rYtqLrEBXMAIAAAAAAVJ6OWHRv3OtCozHpt3ZzfBhaxZirLv3B+G8PuaaO4EgVjACAAAAAAsZXWOWA+UiCBbrJNB6bHflB/cn7pWSvwWN2jw4FPeIUFcABQAAAAAMdD1nV2nqeI1eXEQNskDflCy8I7/HvvqDKJ6XxjhrPQWdLqjz+8GosGUsB7A8ee/uG9/guENuL25XD+Fxxkv1LLXtavHOlLF7iW0u9yabqqBXUAEAAAAAQSNFZ4EjSYdhI0EjRWeJASEHQAAgAAAAV2AE0AAAAAq83vqxI0mHYSNBI0VniQEkzZZBBDgeZh+h+gXEmOrSFtVvkUcnHWj/rfPW7iJ0G3UJ8zpuBmUM/VjOMJCY4+eDqdTiPIwX+/vNXegc8FZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsAA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "__safeContent__": [ - { - "$binary": { - "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", - "subType": "00" - } - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-Compact.json b/tests/SpecTests/client-side-encryption/tests/fle2-Compact.json deleted file mode 100644 index b13e91b1e..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-Compact.json +++ /dev/null @@ -1,234 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - }, - { - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Compact works", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "compactStructuredEncryptionData", - "arguments": { - "command": { - "compactStructuredEncryptionData": "default" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "compactStructuredEncryptionData": "default", - "compactionTokens": { - "encryptedIndexed": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - }, - "encryptedUnindexed": { - "$binary": { - "base64": "SWO8WEoZ2r2Kx/muQKb7+COizy85nIIUFiHh4K9kcvA=", - "subType": "00" - } - } - } - }, - "command_name": "compactStructuredEncryptionData" - } - } - ] - }, - { - "description": "Compact errors on an unencrypted client", - "operations": [ - { - "name": "runCommand", - "object": "database", - "command_name": "compactStructuredEncryptionData", - "arguments": { - "command": { - "compactStructuredEncryptionData": "default" - } - }, - "result": { - "errorContains": "'compactStructuredEncryptionData.compactionTokens' is missing" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-CreateCollection.json b/tests/SpecTests/client-side-encryption/tests/fle2-CreateCollection.json deleted file mode 100644 index 19bd54e0d..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-CreateCollection.json +++ /dev/null @@ -1,2241 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "tests": [ - { - "description": "state collections and index are created", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - } - ] - }, - { - "description": "default state collection names are applied", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - }, - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - }, - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - } - ] - }, - { - "description": "drop removes all state collections", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - }, - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - }, - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - }, - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - } - ] - }, - { - "description": "encryptedFieldsMap with cyclic entries does not loop", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - }, - "default.encryptedCollection.esc": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - } - ] - }, - { - "description": "CreateCollection without encryptedFields.", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "plaintextCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "plaintextCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "plaintextCollection" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "plaintextCollection" - } - }, - "command_name": "listCollections", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "plaintextCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "plaintextCollection" - }, - "command_name": "create", - "database_name": "default" - } - } - ] - }, - { - "description": "CreateCollection from encryptedFieldsMap.", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - } - ] - }, - { - "description": "CreateCollection from encryptedFields.", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "encryptedCollection" - } - }, - "command_name": "listCollections", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - } - ] - }, - { - "description": "DropCollection from encryptedFieldsMap", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - } - ] - }, - { - "description": "DropCollection from encryptedFields", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": {} - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - }, - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "encryptedCollection" - } - }, - "command_name": "listCollections", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - } - ] - }, - { - "description": "DropCollection from remote encryptedFields", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "encryptedFieldsMap": {} - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "__safeContent___1" - } - }, - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.esc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "enxcol_.encryptedCollection.ecoc" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.esc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "enxcol_.encryptedCollection.ecoc", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "create": "encryptedCollection", - "encryptedFields": { - "escCollection": "enxcol_.encryptedCollection.esc", - "eccCollection": "enxcol_.encryptedCollection.ecc", - "ecocCollection": "enxcol_.encryptedCollection.ecoc", - "fields": [ - { - "path": "firstName", - "bsonType": "string", - "keyId": { - "$binary": { - "subType": "04", - "base64": "AAAAAAAAAAAAAAAAAAAAAA==" - } - } - } - ] - } - }, - "command_name": "create", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "encryptedCollection" - } - }, - "command_name": "listCollections", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "__safeContent___1", - "key": { - "__safeContent__": 1 - } - } - ] - }, - "command_name": "createIndexes", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "encryptedCollection" - } - }, - "command_name": "listCollections", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.esc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "enxcol_.encryptedCollection.ecoc" - }, - "command_name": "drop", - "database_name": "default" - } - }, - { - "command_started_event": { - "command": { - "drop": "encryptedCollection" - }, - "command_name": "drop", - "database_name": "default" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-DecryptExistingData.json b/tests/SpecTests/client-side-encryption/tests/fle2-DecryptExistingData.json deleted file mode 100644 index 23cdab93e..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-DecryptExistingData.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encryptedUnindexed": { - "$binary": { - "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", - "subType": "06" - } - } - } - ], - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "FLE2 decrypt of existing data succeeds", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encryptedUnindexed": "value123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-Delete.json b/tests/SpecTests/client-side-encryption/tests/fle2-Delete.json deleted file mode 100644 index 8e04dc5d8..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-Delete.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Delete can query an FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "encryptedIndexed": "value123" - } - }, - "result": { - "deletedCount": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "delete": "default", - "deletes": [ - { - "q": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVjACAAAAAA19X9v9NlWidu/wR5/C/7WUV54DfL5CkNmT5WYrhxdDcFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "limit": 1 - } - ], - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - }, - "deleteTokens": { - "default.default": { - "encryptedIndexed": { - "e": { - "$binary": { - "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", - "subType": "00" - } - }, - "o": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - } - } - } - } - } - }, - "command_name": "delete" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-EncryptedFieldsMap.json b/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-EncryptedFieldsMap.json deleted file mode 100644 index c44bf9d2e..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-EncryptedFieldsMap.json +++ /dev/null @@ -1,219 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "encryptedFieldsMap is preferred over remote encryptedFields", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [] - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedUnindexed": { - "$binary": { - "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", - "subType": "06" - } - } - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encryptedUnindexed": "value123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedUnindexed": { - "$binary": { - "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedUnindexed": { - "$binary": { - "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-jsonSchema.json b/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-jsonSchema.json deleted file mode 100644 index 0e56912e3..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFields-vs-jsonSchema.json +++ /dev/null @@ -1,306 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": {}, - "bsonType": "object" - }, - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "encryptedFields is preferred over jsonSchema", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "123" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "encryptedIndexed": "123" - } - }, - "result": [ - { - "_id": 1, - "encryptedIndexed": "123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVjACAAAAAAWuidNu47c9A4Clic3DvFhn1AQJVC+FJtoE5bGZuz6PsFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "__safeContent__": [ - { - "$binary": { - "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", - "subType": "00" - } - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFieldsMap-defaults.json b/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFieldsMap-defaults.json deleted file mode 100644 index 4e13934b7..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-EncryptedFieldsMap-defaults.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "key_vault_data": [], - "tests": [ - { - "description": "default state collections are applied to encryptionInformation", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.default": { - "fields": [] - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "foo": { - "$binary": { - "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", - "subType": "06" - } - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "foo": { - "$binary": { - "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", - "subType": "06" - } - } - } - ], - "encryptionInformation": { - "type": { - "$numberInt": "1" - }, - "schema": { - "default.default": { - "fields": [], - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc" - } - } - }, - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "foo": { - "$binary": { - "base64": "BYkAAAAFZAAgAAAAAE8KGPgq7h3n9nH5lfHcia8wtOTLwGkZNLBesb6PULqbBXMAIAAAAACq0558QyD3c3jkR5k0Zc9UpQK8ByhXhtn2d1xVQnuJ3AVjACAAAAAA1003zUWGwD4zVZ0KeihnZOthS3V6CEHUfnJZcIYHefISY20AAAAAAAAAAAAA", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-FindOneAndUpdate.json b/tests/SpecTests/client-side-encryption/tests/fle2-FindOneAndUpdate.json deleted file mode 100644 index e929c823a..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-FindOneAndUpdate.json +++ /dev/null @@ -1,604 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "findOneAndUpdate can query an FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - }, - { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "encryptedIndexed": "value123" - }, - "update": { - "$set": { - "foo": "bar" - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default", - "query": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVjACAAAAAA19X9v9NlWidu/wR5/C/7WUV54DfL5CkNmT5WYrhxdDcFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "update": { - "$set": { - "foo": "bar" - } - }, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - }, - "deleteTokens": { - "default.default": { - "encryptedIndexed": { - "e": { - "$binary": { - "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", - "subType": "00" - } - }, - "o": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - } - } - } - } - } - }, - "command_name": "findAndModify" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "foo": "bar", - "__safeContent__": [ - { - "$binary": { - "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", - "subType": "00" - } - } - ] - } - ] - } - } - }, - { - "description": "findOneAndUpdate can modify an FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - }, - { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "encryptedIndexed": "value123" - }, - "update": { - "$set": { - "encryptedIndexed": "value456" - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 1, - "encryptedIndexed": "value123" - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "encryptedIndexed": "value456" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default", - "query": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVjACAAAAAA19X9v9NlWidu/wR5/C/7WUV54DfL5CkNmT5WYrhxdDcFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "update": { - "$set": { - "encryptedIndexed": { - "$$type": "binData" - } - } - }, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - }, - "deleteTokens": { - "default.default": { - "encryptedIndexed": { - "e": { - "$binary": { - "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", - "subType": "00" - } - }, - "o": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - } - } - } - } - } - }, - "command_name": "findAndModify" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": { - "$eq": 1 - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "__safeContent__": [ - { - "$binary": { - "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", - "subType": "00" - } - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Indexed.json b/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Indexed.json deleted file mode 100644 index 53857cac1..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Indexed.json +++ /dev/null @@ -1,302 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Insert and find FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "123" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "encryptedIndexed": "123" - } - }, - "result": [ - { - "_id": 1, - "encryptedIndexed": "123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPGmZcUzdE/FPILvRSyAScGvZparGI2y9rJ/vSBxgCujBXMAIAAAAACi1RjmndKqgnXy7xb22RzUbnZl1sOZRXPOC0KcJkAxmQVjACAAAAAAWuidNu47c9A4Clic3DvFhn1AQJVC+FJtoE5bGZuz6PsFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "__safeContent__": [ - { - "$binary": { - "base64": "31eCYlbQoVboc5zwC8IoyJVSkag9PxREka8dkmbXJeY=", - "subType": "00" - } - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Unindexed.json b/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Unindexed.json deleted file mode 100644 index b2a3bd9d9..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-InsertFind-Unindexed.json +++ /dev/null @@ -1,252 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "HBk9BWihXExNDvTp1lUxOuxuZK2Pe2ZdVdlsxPEBkiO1bS4mG5NNDsQ7zVxJAH8BtdOYp72Ku4Y3nwc0BUpIKsvAKX4eYXtlhv5zUQxWdeNFhg9qK7qb8nqhnnLeT0f25jFSqzWJoT379hfwDeu0bebJHr35QrJ8myZdPMTEDYF08QYQ48ShRBli0S+QzBHHAQiM2iJNr4svg2WR8JSeWQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Insert and find FLE2 unindexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedUnindexed": "value123" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encryptedUnindexed": "value123" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedUnindexed": { - "$$type": "binData" - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": { - "$eq": 1 - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedUnindexed": { - "$$type": "binData" - } - } - ] - } - } - }, - { - "description": "Query with an unindexed field fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedUnindexed": "value123" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "encryptedUnindexed": "value123" - } - }, - "result": { - "errorContains": "encrypt" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-MissingKey.json b/tests/SpecTests/client-side-encryption/tests/fle2-MissingKey.json deleted file mode 100644 index ce5c1677f..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-MissingKey.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "encryptedUnindexed": { - "$binary": { - "base64": "BqvN76sSNJh2EjQSNFZ4kBICTQaVZPWgXp41I7mPV1rLFTtw1tXzjcdSEyxpKKqujlko5TeizkB9hHQ009dVY1+fgIiDcefh+eQrm3CkhQ==", - "subType": "06" - } - } - } - ], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [], - "tests": [ - { - "description": "FLE2 encrypt fails with mising key", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "123" - } - }, - "result": { - "errorContains": "not all keys requested were satisfied" - } - } - ] - }, - { - "description": "FLE2 decrypt fails with mising key", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": {} - }, - "result": { - "errorContains": "not all keys requested were satisfied" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-NoEncryption.json b/tests/SpecTests/client-side-encryption/tests/fle2-NoEncryption.json deleted file mode 100644 index 39ffcf17f..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-NoEncryption.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "key_vault_data": [], - "encrypted_fields": { - "fields": [] - }, - "tests": [ - { - "description": "insert with no encryption succeeds", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "foo": "bar" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "foo": "bar" - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "foo": "bar" - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-Update.json b/tests/SpecTests/client-side-encryption/tests/fle2-Update.json deleted file mode 100644 index 581cebbc7..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-Update.json +++ /dev/null @@ -1,612 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "encrypted_fields": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "sHe0kz57YW7v8g9VP9sf/+K1ex4JqKc5rf/URX3n3p8XdZ6+15uXPaSayC6adWbNxkFskuMCOifDoTT+rkqMtFkDclOy884RuGGtUysq3X7zkAWYTKi8QAfKkajvVbZl2y23UqgVasdQu3OVBQCrH/xY00nNAs/52e958nVjBuzQkSb1T8pKJAyjZsHJ60+FtnfafDZSTAIBJYn7UWBCwQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1648914851981" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Update can query an FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "encryptedIndexed": "value123" - }, - "update": { - "$set": { - "foo": "bar" - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVjACAAAAAA19X9v9NlWidu/wR5/C/7WUV54DfL5CkNmT5WYrhxdDcFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "foo": "bar" - } - } - } - ], - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - }, - "deleteTokens": { - "default.default": { - "encryptedIndexed": { - "e": { - "$binary": { - "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", - "subType": "00" - } - }, - "o": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - } - } - } - } - } - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "foo": "bar", - "__safeContent__": [ - { - "$binary": { - "base64": "ThpoKfQ8AkOzkFfNC1+9PF0pY2nIzfXvRdxQgjkNbBw=", - "subType": "00" - } - } - ] - } - ] - } - } - }, - { - "description": "Update can modify an FLE2 indexed field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encryptedIndexed": "value123" - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "encryptedIndexed": "value123" - }, - "update": { - "$set": { - "encryptedIndexed": "value456" - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "encryptedIndexed": "value456" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - } - } - ], - "ordered": true, - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encryptedIndexed": { - "$eq": { - "$binary": { - "base64": "BbEAAAAFZAAgAAAAAPtVteJQAlgb2YMa/+7YWH00sbQPyt7L6Rb8OwBdMmL2BXMAIAAAAAAd44hgVKnEnTFlwNVC14oyc9OZOTspeymusqkRQj57nAVjACAAAAAA19X9v9NlWidu/wR5/C/7WUV54DfL5CkNmT5WYrhxdDcFZQAgAAAAAOuac/eRLYakKX6B0vZ1r3QodOQFfjqJD+xlGiPu4/PsEmNtAAAAAAAAAAAAAA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encryptedIndexed": { - "$$type": "binData" - } - } - } - } - ], - "encryptionInformation": { - "type": 1, - "schema": { - "default.default": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - }, - "deleteTokens": { - "default.default": { - "encryptedIndexed": { - "e": { - "$binary": { - "base64": "65pz95EthqQpfoHS9nWvdCh05AV+OokP7GUaI+7j8+w=", - "subType": "00" - } - }, - "o": { - "$binary": { - "base64": "noN+05JsuO1oDg59yypIGj45i+eFH6HOTXOPpeZ//Mk=", - "subType": "00" - } - } - } - } - } - } - }, - "command_name": "update" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": { - "$eq": 1 - } - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encryptedIndexed": { - "$$type": "binData" - }, - "__safeContent__": [ - { - "$binary": { - "base64": "rhe7/w8Ob8Unl44rGr/moScx6m5VODQnscDhF4Nkn6g=", - "subType": "00" - } - } - ] - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/fle2-validatorAndPartialFieldExpression.json b/tests/SpecTests/client-side-encryption/tests/fle2-validatorAndPartialFieldExpression.json deleted file mode 100644 index 286fbb6b0..000000000 --- a/tests/SpecTests/client-side-encryption/tests/fle2-validatorAndPartialFieldExpression.json +++ /dev/null @@ -1,522 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "6.2.99", - "minServerVersion": "6.0.0", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "tests": [ - { - "description": "create with a validator on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "validator": { - "unencrypted_string": "foo" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - } - ] - }, - { - "description": "create with a validator on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "validator": { - "encryptedIndexed": "foo" - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - }, - { - "description": "collMod with a validator on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "collMod": "encryptedCollection", - "validator": { - "unencrypted_string": "foo" - } - } - } - } - ] - }, - { - "description": "collMod with a validator on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "collMod": "encryptedCollection", - "validator": { - "encryptedIndexed": "foo" - } - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - }, - { - "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "name", - "key": { - "name": 1 - }, - "partialFilterExpression": { - "unencrypted_string": "foo" - } - } - ] - } - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "name" - } - } - ] - }, - { - "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "encryptedFieldsMap": { - "default.encryptedCollection": { - "escCollection": "enxcol_.default.esc", - "eccCollection": "enxcol_.default.ecc", - "ecocCollection": "enxcol_.default.ecoc", - "fields": [ - { - "keyId": { - "$binary": { - "base64": "EjRWeBI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedIndexed", - "bsonType": "string", - "queries": { - "queryType": "equality", - "contention": { - "$numberLong": "0" - } - } - }, - { - "keyId": { - "$binary": { - "base64": "q83vqxI0mHYSNBI0VniQEg==", - "subType": "04" - } - }, - "path": "encryptedUnindexed", - "bsonType": "string" - } - ] - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "name", - "key": { - "name": 1 - }, - "partialFilterExpression": { - "encryptedIndexed": "foo" - } - } - ] - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/gcpKMS.json b/tests/SpecTests/client-side-encryption/tests/gcpKMS.json deleted file mode 100644 index c2c08b8a2..000000000 --- a/tests/SpecTests/client-side-encryption/tests/gcpKMS.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "CiQAIgLj0WyktnB4dfYHo5SLZ41K4ASQrjJUaSzl5vvVH0G12G0SiQEAjlV8XPlbnHDEDFbdTO4QIe8ER2/172U1ouLazG0ysDtFFIlSvWX5ZnZUrRMmp/R2aJkzLXEt/zf8Mn4Lfm+itnjgo5R9K4pmPNvvPKNZX5C16lrPT+aA+rd+zXFSmlMg3i5jnxvTdLHhg3G7Q/Uv1ZIJskKt95bzLoe0tUVzRWMYXLIEcohnQg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1601574333107" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyAltNames": [ - "altname", - "gcp_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using GCP KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "gcp": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_gcp": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_gcp": { - "$binary": { - "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_gcp": { - "$binary": { - "base64": "ARgj/gAAAAAAAAAAAAAAAAACwFd+Y5Ojw45GUXNvbcIpN9YkRdoHDHkR4kssdn0tIMKlDQOLFkWFY9X07IRlXsxPD8DcTiKnl6XINK28vhcGlg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/getMore.json b/tests/SpecTests/client-side-encryption/tests/getMore.json deleted file mode 100644 index ee99bf753..000000000 --- a/tests/SpecTests/client-side-encryption/tests/getMore.json +++ /dev/null @@ -1,263 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - }, - { - "_id": 3, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "getMore with encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "batchSize": 2, - "filter": {} - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - }, - { - "_id": 2, - "encrypted_string": "string1" - }, - { - "_id": 3, - "encrypted_string": "string2" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "batchSize": 2 - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "default", - "batchSize": 2 - }, - "command_name": "getMore" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - }, - { - "_id": 3, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/insert.json b/tests/SpecTests/client-side-encryption/tests/insert.json deleted file mode 100644 index cf2910fd7..000000000 --- a/tests/SpecTests/client-side-encryption/tests/insert.json +++ /dev/null @@ -1,344 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "insertOne with encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - }, - { - "description": "insertMany with encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - }, - { - "_id": 2, - "encrypted_string": "string1" - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/keyAltName.json b/tests/SpecTests/client-side-encryption/tests/keyAltName.json deleted file mode 100644 index 7f71b9dbe..000000000 --- a/tests/SpecTests/client-side-encryption/tests/keyAltName.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Insert with encryption using key alt name", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_w_altname": "string0", - "altname": "altname" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [] - } - }, - { - "keyAltNames": { - "$in": [ - "altname" - ] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_w_altname": { - "$$type": "binData" - }, - "altname": "altname" - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_w_altname": { - "$$type": "binData" - }, - "altname": "altname" - } - ] - } - } - }, - { - "description": "Replace with key alt name fails", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$set": { - "encrypted_w_altname": "string0" - } - }, - "upsert": true - }, - "result": { - "errorContains": "A non-static (JSONPointer) keyId is not supported" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/kmipKMS.json b/tests/SpecTests/client-side-encryption/tests/kmipKMS.json deleted file mode 100644 index 5749d21ab..000000000 --- a/tests/SpecTests/client-side-encryption/tests/kmipKMS.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string_aws": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_azure": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AZURE+AAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_gcp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "GCP+AAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_local": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "encrypted_string_kmip": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "eUYDyB0HuWb+lQgUwO+6qJQyTTDTY2gp9FbemL7ZFo0pvr0x6rm6Ff9OVUTGH6HyMKipaeHdiIJU1dzsLwvqKvi7Beh+U4iaIWX/K0oEg1GOsJc0+Z/in8gNHbGUYLmycHViM3LES3kdt7FdFSUl5rEBHrM71yoNEXImz17QJWMGOuT4x6yoi2pvnaRJwfrI4DjpmnnTrDMac92jgZehbg==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1634220190041" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "kmip", - "keyId": "1" - }, - "keyAltNames": [ - "altname", - "kmip_altname" - ] - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using KMIP KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "kmip": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string_kmip": "string0" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "dBHpr8aITfeBQ15grpbLpQ==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string_kmip": { - "$binary": { - "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_kmip": { - "$binary": { - "base64": "AXQR6a/GiE33gUNeYK6Wy6UCKCwtKFIsL8eKObDVxvqGupJNUk7kXswHhB7G5j/C1D+6no+Asra0KgSU43bTL3ooIBLVyIzbV5CDJYqzAsa4WQ==", - "subType": "06" - } - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/localKMS.json b/tests/SpecTests/client-side-encryption/tests/localKMS.json deleted file mode 100644 index 67c4ba130..000000000 --- a/tests/SpecTests/client-side-encryption/tests/localKMS.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "Ce9HSz/HKKGkIt4uyy+jDuKGA+rLC2cycykMo6vc8jXxqa1UVDYHWq1r+vZKbnnSRBfB981akzRKZCFpC05CTyFqDhXv6OnMjpG97OZEREGIsHEYiJkBW0jJJvfLLgeLsEpBzsro9FztGGXASxyxFRZFhXvHxyiLOKrdWfs7X1O/iK3pEoHMx6uSNSfUOgbebLfIqW7TO++iQS5g1xovXA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "status": { - "$numberInt": "0" - }, - "masterKey": { - "provider": "local" - } - } - ], - "tests": [ - { - "description": "Insert a document with auto encryption using local KMS provider", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {}, - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault" - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACV/+zJmpqMU47yxS/xIVAviGi7wHDuFwaULAixEAoIh0xHz73UYOM3D8D44gcJn67EROjbz4ITpYzzlCJovDL0Q==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/localSchema.json b/tests/SpecTests/client-side-encryption/tests/localSchema.json deleted file mode 100644 index 4698520f6..000000000 --- a/tests/SpecTests/client-side-encryption/tests/localSchema.json +++ /dev/null @@ -1,258 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": {}, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "A local schema should override", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - } - }, - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": "string0" - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "A local schema with no encryption is an error", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "test": { - "bsonType": "string" - } - }, - "bsonType": "object", - "required": [ - "test" - ] - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "JSON schema keyword 'required' is only allowed with a remote schema" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/malformedCiphertext.json b/tests/SpecTests/client-side-encryption/tests/malformedCiphertext.json deleted file mode 100644 index c81330ce8..000000000 --- a/tests/SpecTests/client-side-encryption/tests/malformedCiphertext.json +++ /dev/null @@ -1,321 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "00" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQ==", - "subType": "06" - } - } - }, - { - "_id": 3, - "encrypted_string": { - "$binary": { - "base64": "AQAAa2V2aW4gYWxiZXJ0c29uCg==", - "subType": "06" - } - } - } - ], - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Wrong subtype", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "Empty data", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "_id": 2 - } - }, - "result": { - "errorContains": "malformed ciphertext" - } - } - ] - }, - { - "description": "Malformed data", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "_id": 3 - } - }, - "result": { - "errorContains": "not all keys requested were satisfied" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json b/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json deleted file mode 100644 index f04f58dff..000000000 --- a/tests/SpecTests/client-side-encryption/tests/maxWireVersion.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "runOn": [ - { - "maxServerVersion": "4.0.99" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "operation fails with maxWireVersion < 8", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - }, - "extraOptions": { - "mongocryptdBypassSpawn": true - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "encrypted_string": "string0" - } - }, - "result": { - "errorContains": "Auto-encryption requires a minimum MongoDB version of 4.2" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/missingKey.json b/tests/SpecTests/client-side-encryption/tests/missingKey.json deleted file mode 100644 index 275147bb7..000000000 --- a/tests/SpecTests/client-side-encryption/tests/missingKey.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "Insert with encryption on a missing key", - "clientOptions": { - "autoEncryptOpts": { - "keyVaultNamespace": "keyvault.different", - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - }, - "result": { - "errorContains": "not all keys requested were satisfied" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "different", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/noSchema.json b/tests/SpecTests/client-side-encryption/tests/noSchema.json deleted file mode 100644 index 095434f88..000000000 --- a/tests/SpecTests/client-side-encryption/tests/noSchema.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "unencrypted", - "tests": [ - { - "description": "Insert on an unencrypted collection", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "unencrypted" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "insert": "unencrypted", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true - }, - "command_name": "insert" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/replaceOne.json b/tests/SpecTests/client-side-encryption/tests/replaceOne.json deleted file mode 100644 index 975768681..000000000 --- a/tests/SpecTests/client-side-encryption/tests/replaceOne.json +++ /dev/null @@ -1,239 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "replaceOne with encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "replaceOne", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "replacement": { - "encrypted_string": "string1", - "random": "abc" - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "u": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - } - ], - "ordered": true - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/timeoutMS.json b/tests/SpecTests/client-side-encryption/tests/timeoutMS.json deleted file mode 100644 index 443aa0aa2..000000000 --- a/tests/SpecTests/client-side-encryption/tests/timeoutMS.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.4" - } - ], - "database_name": "cse-timeouts-db", - "collection_name": "cse-timeouts-coll", - "data": [], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "timeoutMS applied to listCollections to get collection schema", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "blockConnection": true, - "blockTimeMS": 60 - } - }, - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - }, - "timeoutMS": 50 - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - }, - "result": { - "isTimeoutError": true - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "cse-timeouts-coll" - }, - "maxTimeMS": { - "$$type": [ - "int", - "long" - ] - } - }, - "command_name": "listCollections" - } - } - ] - }, - { - "description": "remaining timeoutMS applied to find to get keyvault data", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 3 - }, - "data": { - "failCommands": [ - "listCollections", - "find" - ], - "blockConnection": true, - "blockTimeMS": 20 - } - }, - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - }, - "timeoutMS": 50 - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_string": "string0", - "random": "abc" - } - }, - "result": { - "isTimeoutError": true - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/types.json b/tests/SpecTests/client-side-encryption/tests/types.json deleted file mode 100644 index a6c6507e9..000000000 --- a/tests/SpecTests/client-side-encryption/tests/types.json +++ /dev/null @@ -1,1646 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "json_schema": {}, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "type=objectId", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_objectId": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "objectId", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_objectId": { - "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_objectId": { - "$oid": "AAAAAAAAAAAAAAAAAAAAAAAA" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_objectId": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_objectId": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAHmkTPqvzfHMWpvS1mEsrjOxVQ2dyihEgIFWD5E0eNEsiMBQsC0GuvjdqYRL5DHLFI1vKuGek7EYYp0Qyii/tHqA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=symbol", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_symbol": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "symbol", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_symbol": { - "$symbol": "test" - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_symbol": { - "$symbol": "test" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_symbol": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_symbol": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAOOmvDmWjcuKsSCO7U/7t9HJ8eI73B6wduyMbdkvn7n7V4uTJes/j+BTtneSdyG2JHKHGkevWAJSIU2XoO66BSXw==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=int", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_int": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "int", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_int": { - "$numberInt": "123" - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_int": { - "$numberInt": "123" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_int": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_int": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAQPNXJVXMEjGZnftMuf2INKufXCtQIRHdw5wTgn6QYt3ejcoAXyiwI4XIUizkpsob494qpt2in4tWeiO7b9zkA8Q==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=double", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_double": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "double", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_double": { - "$numberDouble": "1.23" - } - } - }, - "result": { - "errorContains": "element of type: double" - } - } - ] - }, - { - "description": "type=decimal", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_decimal": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "decimal", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_decimal": { - "$numberDecimal": "1.23" - } - } - }, - "result": { - "errorContains": "element of type: decimal" - } - } - ] - }, - { - "description": "type=binData", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_binData": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "binData", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_binData": { - "$binary": { - "base64": "AAAA", - "subType": "00" - } - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_binData": { - "$binary": { - "base64": "AAAA", - "subType": "00" - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_binData": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_binData": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAFB/KHZQHaHHo8fctcl7v6kR+sLkJoTRx2cPSSck9ya+nbGROSeFhdhDRHaCzhV78fDEqnMDSVPNi+ZkbaIh46GQ==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=javascript", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_javascript": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "javascript", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_javascript": { - "$code": "var x = 1;" - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_javascript": { - "$code": "var x = 1;" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_javascript": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_javascript": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAANrvMgJkTKWGMc9wt3E2RBR2Hu5gL9p+vIIdHe9FcOm99t1W480/oX1Gnd87ON3B399DuFaxi/aaIiQSo7gTX6Lw==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=javascriptWithScope", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_javascriptWithScope": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "javascriptWithScope", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_javascriptWithScope": { - "$code": "var x = 1;", - "$scope": {} - } - } - }, - "result": { - "errorContains": "element of type: javascriptWithScope" - } - } - ] - }, - { - "description": "type=object", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_object": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "object", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_object": {} - } - }, - "result": { - "errorContains": "element of type: object" - } - } - ] - }, - { - "description": "type=timestamp", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_timestamp": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "timestamp", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_timestamp": { - "$timestamp": { - "t": 123, - "i": 456 - } - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_timestamp": { - "$timestamp": { - "t": 123, - "i": 456 - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_timestamp": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_timestamp": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAARJHaM4Gq3MpDTdBasBsEolQaOmxJQU1wsZVaSFAOLpEh1QihDglXI95xemePFMKhg+KNpFg7lw1ChCs2Wn/c26Q==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=regex", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_regex": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "regex", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_regex": { - "$regularExpression": { - "pattern": "test", - "options": "" - } - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_regex": { - "$regularExpression": { - "pattern": "test", - "options": "" - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_regex": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_regex": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAALVnxM4UqGhqf5eXw6nsS08am3YJrTf1EvjKitT8tyyMAbHsICIU3GUjuC7EBofCHbusvgo7pDyaClGostFz44nA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=date", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_date": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "date", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_date": { - "$date": { - "$numberLong": "123" - } - } - } - } - }, - { - "name": "findOne", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "encrypted_date": { - "$date": { - "$numberLong": "123" - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "insert": "default", - "documents": [ - { - "_id": 1, - "encrypted_date": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", - "subType": "06" - } - } - } - ], - "ordered": true - }, - "command_name": "insert" - } - }, - { - "command_started_event": { - "command": { - "find": "default", - "filter": { - "_id": 1 - } - }, - "command_name": "find" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_date": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAAJ5sN7u6l97+DswfKTqZAijSTSOo5htinGKQKUD7pHNJYlLXGOkB4glrCu7ibu0g3344RHQ5yUp4YxMEa8GD+Snw==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "type=minKey", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_minKey": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "minKey", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_minKey": { - "$minKey": 1 - } - } - }, - "result": { - "errorContains": "Cannot encrypt element of type: minKey" - } - } - ] - }, - { - "description": "type=maxKey", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_maxKey": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "maxKey", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_maxKey": { - "$maxKey": 1 - } - } - }, - "result": { - "errorContains": "Cannot encrypt element of type: maxKey" - } - } - ] - }, - { - "description": "type=undefined", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_undefined": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "undefined", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_undefined": { - "$undefined": true - } - } - }, - "result": { - "errorContains": "Cannot encrypt element of type: undefined" - } - } - ] - }, - { - "description": "type=array", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_array": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "array", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_array": [] - } - }, - "result": { - "errorContains": "element of type: array" - } - } - ] - }, - { - "description": "type=bool", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_bool": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "bool", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_bool": true - } - }, - "result": { - "errorContains": "element of type: bool" - } - } - ] - }, - { - "description": "type=null", - "clientOptions": { - "autoEncryptOpts": { - "schemaMap": { - "default.default": { - "properties": { - "encrypted_null": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "null", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - }, - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "encrypted_null": true - } - }, - "result": { - "errorContains": "Cannot encrypt element of type: null" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/unsupportedCommand.json b/tests/SpecTests/client-side-encryption/tests/unsupportedCommand.json deleted file mode 100644 index 318871511..000000000 --- a/tests/SpecTests/client-side-encryption/tests/unsupportedCommand.json +++ /dev/null @@ -1,152 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "x": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "x": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "mapReduce deterministic encryption (unsupported)", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "mapReduce", - "arguments": { - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "result": { - "errorContains": "command not supported for auto encryption: mapreduce" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/updateMany.json b/tests/SpecTests/client-side-encryption/tests/updateMany.json deleted file mode 100644 index 823909044..000000000 --- a/tests/SpecTests/client-side-encryption/tests/updateMany.json +++ /dev/null @@ -1,307 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "updateMany with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "encrypted_string": { - "$in": [ - "string0", - "string1" - ] - } - }, - "update": { - "$set": { - "encrypted_string": "string2", - "random": "abc" - } - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encrypted_string": { - "$in": [ - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - }, - { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - } - ] - } - }, - "u": { - "$set": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - }, - "multi": true, - "upsert": false - } - ], - "ordered": true - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - }, - { - "_id": 2, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACQ76HWOut3DZtQuV90hp1aaCpZn95vZIaWmn+wrBehcEtcFwyJlBdlyzDzZTWPZCPgiFq72Wvh6Y7VbpU9NAp3A==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - }, - { - "description": "updateMany fails when filtering on a random field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "random": "abc" - }, - "update": { - "$set": { - "encrypted_string": "string1" - } - } - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/updateOne.json b/tests/SpecTests/client-side-encryption/tests/updateOne.json deleted file mode 100644 index 23bada964..000000000 --- a/tests/SpecTests/client-side-encryption/tests/updateOne.json +++ /dev/null @@ -1,465 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.10" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ], - "json_schema": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - }, - "key_vault_data": [ - { - "status": 1, - "_id": { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - }, - "updateDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gEqnsxXlR51T5EbEVezUqqKAAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDHa4jo6yp0Z18KgbUgIBEIB74sKxWtV8/YHje5lv5THTl0HIbhSwM6EqRlmBiFFatmEWaeMk4tO4xBX65eq670I5TWPSLMzpp8ncGHMmvHqRajNBnmFtbYxN3E3/WjxmdbOOe+OXpnGJPcGsftc7cB2shRfA4lICPnE26+oVNXT6p0Lo20nY5XC7jyCO", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1552949630483" - } - }, - "keyAltNames": [ - "altname", - "another_altname" - ] - } - ], - "tests": [ - { - "description": "updateOne with deterministic encryption", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "encrypted_string": "string0" - }, - "update": { - "$set": { - "encrypted_string": "string1", - "random": "abc" - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "find": "datakeys", - "filter": { - "$or": [ - { - "_id": { - "$in": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ] - } - }, - { - "keyAltNames": { - "$in": [] - } - } - ] - }, - "$db": "keyvault", - "readConcern": { - "level": "majority" - } - }, - "command_name": "find" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": { - "encrypted_string": { - "$eq": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - }, - "u": { - "$set": { - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - } - } - ], - "ordered": true - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACDdw4KFz3ZLquhsbt7RmDjD0N67n0uSXx7IGnQNCLeIKvot6s/ouI21Eo84IOtb6lhwUNPlSEBNY0/hbszWAKJg==", - "subType": "06" - } - }, - "random": { - "$$type": "binData" - } - } - ] - } - } - }, - { - "description": "updateOne fails when filtering on a random field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "random": "abc" - }, - "update": { - "$set": { - "encrypted_string": "string1" - } - } - }, - "result": { - "errorContains": "Cannot query on fields encrypted with the randomized encryption" - } - } - ] - }, - { - "description": "$unset works with an encrypted field", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$unset": { - "encrypted_string": "" - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": {}, - "u": { - "$unset": { - "encrypted_string": "" - } - } - } - ], - "ordered": true - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "$rename works if target value has same encryption options", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$rename": { - "encrypted_string": "encrypted_string_equivalent" - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1, - "filter": { - "name": "default" - } - }, - "command_name": "listCollections" - } - }, - { - "command_started_event": { - "command": { - "update": "default", - "updates": [ - { - "q": {}, - "u": { - "$rename": { - "encrypted_string": "encrypted_string_equivalent" - } - } - } - ], - "ordered": true - }, - "command_name": "update" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "encrypted_string_equivalent": { - "$binary": { - "base64": "AQAAAAAAAAAAAAAAAAAAAAACwj+3zkv2VM+aTfk60RqhXq6a/77WlLwu/BxXFkL7EppGsju/m8f0x5kBDD3EZTtGALGXlym5jnpZAoSIkswHoA==", - "subType": "06" - } - } - } - ] - } - } - }, - { - "description": "$rename fails if target value has different encryption options", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "$rename": { - "encrypted_string": "random" - } - } - }, - "result": { - "errorContains": "$rename between two encrypted fields must have the same metadata or both be unencrypted" - } - } - ] - }, - { - "description": "an invalid update (no $ operators) is validated and errors", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "aws": {} - } - } - }, - "operations": [ - { - "name": "updateOne", - "arguments": { - "filter": {}, - "update": { - "encrypted_string": "random" - } - }, - "result": { - "errorContains": "" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json b/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json deleted file mode 100644 index e07137ce1..000000000 --- a/tests/SpecTests/client-side-encryption/tests/validatorAndPartialFieldExpression.json +++ /dev/null @@ -1,642 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "6.0.0" - } - ], - "database_name": "default", - "collection_name": "default", - "data": [], - "tests": [ - { - "description": "create with a validator on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "validator": { - "unencrypted_string": "foo" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection" - } - } - ] - }, - { - "description": "create with a validator on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection", - "validator": { - "encrypted_string": "foo" - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - }, - { - "description": "collMod with a validator on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "collMod": "encryptedCollection", - "validator": { - "unencrypted_string": "foo" - } - } - } - } - ] - }, - { - "description": "collMod with a validator on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "collMod": "encryptedCollection", - "validator": { - "encrypted_string": "foo" - } - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - }, - { - "description": "createIndexes with a partialFilterExpression on an unencrypted field is OK", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "name", - "key": { - "name": 1 - }, - "partialFilterExpression": { - "unencrypted_string": "foo" - } - } - ] - } - } - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "default", - "collection": "encryptedCollection", - "index": "name" - } - } - ] - }, - { - "description": "createIndexes with a partialFilterExpression on an encrypted field is an error", - "clientOptions": { - "autoEncryptOpts": { - "kmsProviders": { - "local": { - "key": { - "$binary": { - "base64": "Mng0NCt4ZHVUYUJCa1kxNkVyNUR1QURhZ2h2UzR2d2RrZzh0cFBwM3R6NmdWMDFBMUN3YkQ5aXRRMkhGRGdQV09wOGVNYUMxT2k3NjZKelhaQmRCZGJkTXVyZG9uSjFk", - "subType": "00" - } - } - } - }, - "schemaMap": { - "default.encryptedCollection": { - "properties": { - "encrypted_w_altname": { - "encrypt": { - "keyId": "/altname", - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - }, - "random": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Random" - } - }, - "encrypted_string_equivalent": { - "encrypt": { - "keyId": [ - { - "$binary": { - "base64": "AAAAAAAAAAAAAAAAAAAAAA==", - "subType": "04" - } - } - ], - "bsonType": "string", - "algorithm": "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - } - } - }, - "bsonType": "object" - } - } - } - }, - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "collection": "encryptedCollection" - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "createIndexes": "encryptedCollection", - "indexes": [ - { - "name": "name", - "key": { - "name": 1 - }, - "partialFilterExpression": { - "encrypted_string": "foo" - } - } - ] - } - }, - "result": { - "errorContains": "Comparison to encrypted fields not supported" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/read-write-concern/operation/default-write-concern-2.6.json b/tests/SpecTests/read-write-concern/operation/default-write-concern-2.6.json deleted file mode 100644 index c623298cd..000000000 --- a/tests/SpecTests/read-write-concern/operation/default-write-concern-2.6.json +++ /dev/null @@ -1,544 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "default_write_concern_coll", - "database_name": "default_write_concern_db", - "runOn": [ - { - "minServerVersion": "2.6" - } - ], - "tests": [ - { - "description": "DeleteOne omits default write concern", - "operations": [ - { - "name": "deleteOne", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": {} - }, - "result": { - "deletedCount": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": {}, - "limit": 1 - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "DeleteMany omits default write concern", - "operations": [ - { - "name": "deleteMany", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": {} - }, - "result": { - "deletedCount": 2 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": {}, - "limit": 0 - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "BulkWrite with all models omits default write concern", - "operations": [ - { - "name": "bulkWrite", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "ordered": true, - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": {} - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 2 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3 - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 3 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 3 - } - } - } - ] - } - } - ], - "outcome": { - "collection": { - "name": "default_write_concern_coll", - "data": [ - { - "_id": 1, - "x": 3 - }, - { - "_id": 2 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": {}, - "limit": 0 - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "insert": "default_write_concern_coll", - "documents": [ - { - "_id": 1 - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 1 - } - } - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "insert": "default_write_concern_coll", - "documents": [ - { - "_id": 2 - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "x": 2 - } - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "insert": "default_write_concern_coll", - "documents": [ - { - "_id": 3 - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 3 - } - }, - "multi": true - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": { - "_id": 3 - }, - "limit": 1 - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "InsertOne and InsertMany omit default write concern", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "document": { - "_id": 3 - } - } - }, - { - "name": "insertMany", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - ], - "outcome": { - "collection": { - "name": "default_write_concern_coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "default_write_concern_coll", - "documents": [ - { - "_id": 3 - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "insert": "default_write_concern_coll", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "UpdateOne, UpdateMany, and ReplaceOne omit default write concern", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "updateMany", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": 2 - } - } - } - }, - { - "name": "replaceOne", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 3 - } - } - } - ], - "outcome": { - "collection": { - "name": "default_write_concern_coll", - "data": [ - { - "_id": 1, - "x": 1 - }, - { - "_id": 2, - "x": 3 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 1 - } - } - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "$set": { - "x": 2 - } - }, - "multi": true - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "update": "default_write_concern_coll", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "x": 3 - } - } - ], - "writeConcern": null - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/read-write-concern/operation/default-write-concern-3.2.json b/tests/SpecTests/read-write-concern/operation/default-write-concern-3.2.json deleted file mode 100644 index 04dd231f0..000000000 --- a/tests/SpecTests/read-write-concern/operation/default-write-concern-3.2.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "default_write_concern_coll", - "database_name": "default_write_concern_db", - "runOn": [ - { - "minServerVersion": "3.2" - } - ], - "tests": [ - { - "description": "findAndModify operations omit default write concern", - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "x": 2 - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "filter": { - "_id": 2 - } - } - } - ], - "outcome": { - "collection": { - "name": "default_write_concern_coll", - "data": [ - { - "_id": 1, - "x": 1 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "default_write_concern_coll", - "query": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - }, - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default_write_concern_coll", - "query": { - "_id": 2 - }, - "update": { - "x": 2 - }, - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "default_write_concern_coll", - "query": { - "_id": 2 - }, - "remove": true, - "writeConcern": null - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/read-write-concern/operation/default-write-concern-3.4.json b/tests/SpecTests/read-write-concern/operation/default-write-concern-3.4.json deleted file mode 100644 index 6519f6f08..000000000 --- a/tests/SpecTests/read-write-concern/operation/default-write-concern-3.4.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "default_write_concern_coll", - "database_name": "default_write_concern_db", - "runOn": [ - { - "minServerVersion": "3.4" - } - ], - "tests": [ - { - "description": "Aggregate with $out omits default write concern", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_collection_name" - } - ] - } - } - ], - "outcome": { - "collection": { - "name": "other_collection_name", - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - }, - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "default_write_concern_coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_collection_name" - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "RunCommand with a write command omits default write concern (runCommand should never inherit write concern)", - "operations": [ - { - "object": "database", - "databaseOptions": { - "writeConcern": {} - }, - "name": "runCommand", - "command_name": "delete", - "arguments": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": {}, - "limit": 1 - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "default_write_concern_coll", - "deletes": [ - { - "q": {}, - "limit": 1 - } - ], - "writeConcern": null - } - } - } - ] - }, - { - "description": "CreateIndex and dropIndex omits default write concern", - "operations": [ - { - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "name": "createIndex", - "arguments": { - "keys": { - "x": 1 - } - } - }, - { - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "name": "dropIndex", - "arguments": { - "name": "x_1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "createIndexes": "default_write_concern_coll", - "indexes": [ - { - "name": "x_1", - "key": { - "x": 1 - } - } - ], - "writeConcern": null - } - } - }, - { - "command_started_event": { - "command": { - "dropIndexes": "default_write_concern_coll", - "index": "x_1", - "writeConcern": null - } - } - } - ] - }, - { - "description": "MapReduce omits default write concern", - "operations": [ - { - "name": "mapReduce", - "object": "collection", - "collectionOptions": { - "writeConcern": {} - }, - "arguments": { - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "mapReduce": "default_write_concern_coll", - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - }, - "writeConcern": null - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/read-write-concern/operation/default-write-concern-4.2.json b/tests/SpecTests/read-write-concern/operation/default-write-concern-4.2.json deleted file mode 100644 index fef192d1a..000000000 --- a/tests/SpecTests/read-write-concern/operation/default-write-concern-4.2.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "collection_name": "default_write_concern_coll", - "database_name": "default_write_concern_db", - "runOn": [ - { - "minServerVersion": "4.2" - } - ], - "tests": [ - { - "description": "Aggregate with $merge omits default write concern", - "operations": [ - { - "object": "collection", - "databaseOptions": { - "writeConcern": {} - }, - "collectionOptions": { - "writeConcern": {} - }, - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_collection_name" - } - } - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "default_write_concern_coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_collection_name" - } - } - ], - "writeConcern": null - } - } - } - ], - "outcome": { - "collection": { - "name": "other_collection_name", - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-reads/aggregate-merge.json b/tests/SpecTests/retryable-reads/aggregate-merge.json deleted file mode 100644 index b401d741b..000000000 --- a/tests/SpecTests/retryable-reads/aggregate-merge.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.11" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Aggregate with $merge does not retry", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "object": "collection", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "output-collection" - } - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "output-collection" - } - } - ] - }, - "command_name": "aggregate", - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/aggregate-serverErrors.json b/tests/SpecTests/retryable-reads/aggregate-serverErrors.json deleted file mode 100644 index 1155f808d..000000000 --- a/tests/SpecTests/retryable-reads/aggregate-serverErrors.json +++ /dev/null @@ -1,1208 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Aggregate succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/aggregate.json b/tests/SpecTests/retryable-reads/aggregate.json deleted file mode 100644 index f23d5c679..000000000 --- a/tests/SpecTests/retryable-reads/aggregate.json +++ /dev/null @@ -1,406 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Aggregate succeeds on first attempt", - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "result": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Aggregate with $out does not retry", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "output-collection" - } - ] - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "output-collection" - } - ] - }, - "command_name": "aggregate", - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json deleted file mode 100644 index 73dbfee91..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-client.watch-serverErrors.json +++ /dev/null @@ -1,740 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "client.watch succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-client.watch.json b/tests/SpecTests/retryable-reads/changeStreams-client.watch.json deleted file mode 100644 index 30a53037a..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-client.watch.json +++ /dev/null @@ -1,209 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "client.watch succeeds on first attempt", - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - }, - { - "description": "client.watch fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json deleted file mode 100644 index 77b3af04f..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch-serverErrors.json +++ /dev/null @@ -1,690 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "db.coll.watch succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json b/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json deleted file mode 100644 index 27f6105a4..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-db.coll.watch.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "db.coll.watch succeeds on first attempt", - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.coll.watch fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json b/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json deleted file mode 100644 index 7a8753450..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-db.watch-serverErrors.json +++ /dev/null @@ -1,690 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "db.watch succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "watch", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/changeStreams-db.watch.json b/tests/SpecTests/retryable-reads/changeStreams-db.watch.json deleted file mode 100644 index e6b0b9b78..000000000 --- a/tests/SpecTests/retryable-reads/changeStreams-db.watch.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "db.watch succeeds on first attempt", - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "db.watch fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "watch", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/count-serverErrors.json b/tests/SpecTests/retryable-reads/count-serverErrors.json deleted file mode 100644 index 36a0c17ca..000000000 --- a/tests/SpecTests/retryable-reads/count-serverErrors.json +++ /dev/null @@ -1,586 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "Count succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/count.json b/tests/SpecTests/retryable-reads/count.json deleted file mode 100644 index 139a54513..000000000 --- a/tests/SpecTests/retryable-reads/count.json +++ /dev/null @@ -1,179 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "Count succeeds on first attempt", - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Count fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "count", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json b/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json deleted file mode 100644 index 782ea5e4f..000000000 --- a/tests/SpecTests/retryable-reads/countDocuments-serverErrors.json +++ /dev/null @@ -1,911 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "CountDocuments succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/countDocuments.json b/tests/SpecTests/retryable-reads/countDocuments.json deleted file mode 100644 index 57a64e45b..000000000 --- a/tests/SpecTests/retryable-reads/countDocuments.json +++ /dev/null @@ -1,257 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "CountDocuments succeeds on first attempt", - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "CountDocuments fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": {} - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ] - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/distinct-serverErrors.json b/tests/SpecTests/retryable-reads/distinct-serverErrors.json deleted file mode 100644 index d7c6018a6..000000000 --- a/tests/SpecTests/retryable-reads/distinct-serverErrors.json +++ /dev/null @@ -1,838 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Distinct succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/distinct.json b/tests/SpecTests/retryable-reads/distinct.json deleted file mode 100644 index 1fd415da8..000000000 --- a/tests/SpecTests/retryable-reads/distinct.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "tests": [ - { - "description": "Distinct succeeds on first attempt", - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "result": [ - 22, - 33 - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Distinct fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "distinct" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": { - "_id": { - "$gt": 1 - } - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "coll", - "key": "x", - "query": { - "_id": { - "$gt": 1 - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json b/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json deleted file mode 100644 index 6bb128f5f..000000000 --- a/tests/SpecTests/retryable-reads/estimatedDocumentCount-serverErrors.json +++ /dev/null @@ -1,546 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "EstimatedDocumentCount succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/estimatedDocumentCount.json b/tests/SpecTests/retryable-reads/estimatedDocumentCount.json deleted file mode 100644 index 8dfa15a2c..000000000 --- a/tests/SpecTests/retryable-reads/estimatedDocumentCount.json +++ /dev/null @@ -1,166 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "EstimatedDocumentCount succeeds on first attempt", - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "result": 2 - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "EstimatedDocumentCount fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "count": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/find-serverErrors.json b/tests/SpecTests/retryable-reads/find-serverErrors.json deleted file mode 100644 index f6b96c6dc..000000000 --- a/tests/SpecTests/retryable-reads/find-serverErrors.json +++ /dev/null @@ -1,962 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "Find succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/find.json b/tests/SpecTests/retryable-reads/find.json deleted file mode 100644 index 00d419c0d..000000000 --- a/tests/SpecTests/retryable-reads/find.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "Find succeeds on first attempt", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds on second attempt with explicit clientOptions", - "clientOptions": { - "retryReads": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "result": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Find fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 4 - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/findOne-serverErrors.json b/tests/SpecTests/retryable-reads/findOne-serverErrors.json deleted file mode 100644 index d039ef247..000000000 --- a/tests/SpecTests/retryable-reads/findOne-serverErrors.json +++ /dev/null @@ -1,732 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "FindOne succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/findOne.json b/tests/SpecTests/retryable-reads/findOne.json deleted file mode 100644 index b9deb73d2..000000000 --- a/tests/SpecTests/retryable-reads/findOne.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ], - "tests": [ - { - "description": "FindOne succeeds on first attempt", - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": { - "_id": 1, - "x": 11 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "FindOne fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "findOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "coll", - "filter": { - "_id": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json b/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json deleted file mode 100644 index cec3a5016..000000000 --- a/tests/SpecTests/retryable-reads/gridfs-download-serverErrors.json +++ /dev/null @@ -1,925 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "bucket_name": "fs", - "data": { - "fs.files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "filename": "abc", - "metadata": {} - } - ], - "fs.chunks": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - }, - "tests": [ - { - "description": "Download succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/gridfs-download.json b/tests/SpecTests/retryable-reads/gridfs-download.json deleted file mode 100644 index 4d0d5a17e..000000000 --- a/tests/SpecTests/retryable-reads/gridfs-download.json +++ /dev/null @@ -1,270 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "bucket_name": "fs", - "data": { - "fs.files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "filename": "abc", - "metadata": {} - } - ], - "fs.chunks": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - }, - "tests": [ - { - "description": "Download succeeds on first attempt", - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "Download fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download", - "object": "gridfsbucket", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "_id": { - "$oid": "000000000000000000000001" - } - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json b/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json deleted file mode 100644 index a64230d38..000000000 --- a/tests/SpecTests/retryable-reads/gridfs-downloadByName-serverErrors.json +++ /dev/null @@ -1,849 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "bucket_name": "fs", - "data": { - "fs.files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "filename": "abc", - "metadata": {} - } - ], - "fs.chunks": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - }, - "tests": [ - { - "description": "DownloadByName succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/gridfs-downloadByName.json b/tests/SpecTests/retryable-reads/gridfs-downloadByName.json deleted file mode 100644 index 48f2168cf..000000000 --- a/tests/SpecTests/retryable-reads/gridfs-downloadByName.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "bucket_name": "fs", - "data": { - "fs.files": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "filename": "abc", - "metadata": {} - } - ], - "fs.chunks": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - }, - "tests": [ - { - "description": "DownloadByName succeeds on first attempt", - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.chunks", - "filter": { - "files_id": { - "$oid": "000000000000000000000001" - } - }, - "sort": { - "n": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "DownloadByName fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "download_by_name", - "object": "gridfsbucket", - "arguments": { - "filename": "abc" - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "fs.files", - "filter": { - "filename": "abc" - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json b/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json deleted file mode 100644 index bbdce625a..000000000 --- a/tests/SpecTests/retryable-reads/listCollectionNames-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollectionNames succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollectionNames.json b/tests/SpecTests/retryable-reads/listCollectionNames.json deleted file mode 100644 index 73d96a3cf..000000000 --- a/tests/SpecTests/retryable-reads/listCollectionNames.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollectionNames succeeds on first attempt", - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionNames fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionNames", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json b/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json deleted file mode 100644 index ab469dfe3..000000000 --- a/tests/SpecTests/retryable-reads/listCollectionObjects-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollectionObjects succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollectionObjects.json b/tests/SpecTests/retryable-reads/listCollectionObjects.json deleted file mode 100644 index 1fb0f1843..000000000 --- a/tests/SpecTests/retryable-reads/listCollectionObjects.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollectionObjects succeeds on first attempt", - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollectionObjects fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollectionObjects", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollections-serverErrors.json b/tests/SpecTests/retryable-reads/listCollections-serverErrors.json deleted file mode 100644 index def9ac459..000000000 --- a/tests/SpecTests/retryable-reads/listCollections-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollections succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listCollections.json b/tests/SpecTests/retryable-reads/listCollections.json deleted file mode 100644 index 242788362..000000000 --- a/tests/SpecTests/retryable-reads/listCollections.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListCollections succeeds on first attempt", - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - }, - { - "description": "ListCollections fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listCollections" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listCollections", - "object": "database", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listCollections": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json deleted file mode 100644 index 1dd8e4415..000000000 --- a/tests/SpecTests/retryable-reads/listDatabaseNames-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabaseNames succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabaseNames.json b/tests/SpecTests/retryable-reads/listDatabaseNames.json deleted file mode 100644 index b431f5701..000000000 --- a/tests/SpecTests/retryable-reads/listDatabaseNames.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabaseNames succeeds on first attempt", - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseNames fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseNames", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json deleted file mode 100644 index bc497bb08..000000000 --- a/tests/SpecTests/retryable-reads/listDatabaseObjects-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabaseObjects succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabaseObjects.json b/tests/SpecTests/retryable-reads/listDatabaseObjects.json deleted file mode 100644 index 267fe921c..000000000 --- a/tests/SpecTests/retryable-reads/listDatabaseObjects.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabaseObjects succeeds on first attempt", - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabaseObjects fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabaseObjects", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json b/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json deleted file mode 100644 index ed7bcbc39..000000000 --- a/tests/SpecTests/retryable-reads/listDatabases-serverErrors.json +++ /dev/null @@ -1,502 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabases succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listDatabases.json b/tests/SpecTests/retryable-reads/listDatabases.json deleted file mode 100644 index 69ef9788f..000000000 --- a/tests/SpecTests/retryable-reads/listDatabases.json +++ /dev/null @@ -1,150 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListDatabases succeeds on first attempt", - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - }, - { - "description": "ListDatabases fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listDatabases", - "object": "client", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - }, - { - "command_started_event": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json b/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json deleted file mode 100644 index 2d3265ec8..000000000 --- a/tests/SpecTests/retryable-reads/listIndexNames-serverErrors.json +++ /dev/null @@ -1,527 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListIndexNames succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listIndexNames.json b/tests/SpecTests/retryable-reads/listIndexNames.json deleted file mode 100644 index fbdb420f8..000000000 --- a/tests/SpecTests/retryable-reads/listIndexNames.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListIndexNames succeeds on first attempt", - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexNames fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexNames", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json b/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json deleted file mode 100644 index 25c5b0e44..000000000 --- a/tests/SpecTests/retryable-reads/listIndexes-serverErrors.json +++ /dev/null @@ -1,527 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListIndexes succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 11600 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 11602 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 13435 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 13436 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 189 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 91 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 7 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 6 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 89 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 9001 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes fails after two NotWritablePrimary errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes fails after NotWritablePrimary when retryReads is false", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/listIndexes.json b/tests/SpecTests/retryable-reads/listIndexes.json deleted file mode 100644 index 5cb620ae4..000000000 --- a/tests/SpecTests/retryable-reads/listIndexes.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [], - "tests": [ - { - "description": "ListIndexes succeeds on first attempt", - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes succeeds on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes fails on first attempt", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "ListIndexes fails on second attempt", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "listIndexes" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "listIndexes", - "object": "collection", - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - }, - { - "command_started_event": { - "command": { - "listIndexes": "coll" - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-reads/mapReduce.json b/tests/SpecTests/retryable-reads/mapReduce.json deleted file mode 100644 index 9327a2305..000000000 --- a/tests/SpecTests/retryable-reads/mapReduce.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "database_name": "retryable-reads-tests", - "collection_name": "coll", - "data": [ - { - "_id": 1, - "x": 0 - }, - { - "_id": 2, - "x": 1 - }, - { - "_id": 3, - "x": 2 - } - ], - "tests": [ - { - "description": "MapReduce succeeds with retry on", - "operations": [ - { - "name": "mapReduce", - "object": "collection", - "arguments": { - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "result": [ - { - "_id": 0, - "value": 6 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "mapReduce": "coll", - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "MapReduce fails with retry on", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "mapReduce" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "mapReduce", - "object": "collection", - "arguments": { - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "mapReduce": "coll", - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - }, - { - "description": "MapReduce fails with retry off", - "clientOptions": { - "retryReads": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "mapReduce" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "mapReduce", - "object": "collection", - "arguments": { - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "error": true - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "mapReduce": "coll", - "map": { - "$code": "function inc() { return emit(0, this.x + 1) }" - }, - "reduce": { - "$code": "function sum(key, values) { return values.reduce((acc, x) => acc + x); }" - }, - "out": { - "inline": 1 - } - }, - "database_name": "retryable-reads-tests" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json b/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json deleted file mode 100644 index 66c3ecb33..000000000 --- a/tests/SpecTests/retryable-writes/bulkWrite-errorLabels.json +++ /dev/null @@ -1,183 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "BulkWrite succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 3 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json b/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json deleted file mode 100644 index 1e6cc74c0..000000000 --- a/tests/SpecTests/retryable-writes/bulkWrite-serverErrors.json +++ /dev/null @@ -1,273 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "BulkWrite succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 3 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 3 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "BulkWrite fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/bulkWrite.json b/tests/SpecTests/retryable-writes/bulkWrite.json deleted file mode 100644 index 72a8d0189..000000000 --- a/tests/SpecTests/retryable-writes/bulkWrite.json +++ /dev/null @@ -1,806 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "First command is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "All commands are retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 7 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 4, - "x": 44 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 5, - "x": 55 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 3 - }, - "replacement": { - "_id": 3, - "x": 333 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 3, - "insertedIds": { - "0": 2, - "2": 3, - "4": 5 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 1, - "upsertedIds": { - "3": 4 - } - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 333 - }, - { - "_id": 4, - "x": 45 - }, - { - "_id": 5, - "x": 55 - } - ] - } - } - }, - { - "description": "Both commands are retried after their first statement fails", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "Second command is retried after its second statement fails", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - }, - { - "description": "BulkWrite with unordered execution", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 2, - "insertedIds": { - "0": 2, - "1": 3 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "First insertOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": {}, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "Second updateOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Third updateOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "skip": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following deleteMany is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "deleteMany", - "arguments": { - "filter": { - "x": 11 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "Single-document write following updateMany is retried", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "name": "updateMany", - "arguments": { - "filter": { - "x": 11 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "1": 2 - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/deleteMany.json b/tests/SpecTests/retryable-writes/deleteMany.json deleted file mode 100644 index faa21c44f..000000000 --- a/tests/SpecTests/retryable-writes/deleteMany.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteMany ignores retryWrites", - "useMultipleMongoses": true, - "operation": { - "name": "deleteMany", - "arguments": { - "filter": {} - } - }, - "outcome": { - "result": { - "deletedCount": 2 - }, - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json b/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json deleted file mode 100644 index c14692fd1..000000000 --- a/tests/SpecTests/retryable-writes/deleteOne-errorLabels.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteOne succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json b/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json deleted file mode 100644 index a1a27838d..000000000 --- a/tests/SpecTests/retryable-writes/deleteOne-serverErrors.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne fails with RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "delete" - ], - "closeConnection": true - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/deleteOne.json b/tests/SpecTests/retryable-writes/deleteOne.json deleted file mode 100644 index 592937ace..000000000 --- a/tests/SpecTests/retryable-writes/deleteOne.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "DeleteOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "result": { - "deletedCount": 1 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "DeleteOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json deleted file mode 100644 index 60e6e0a7b..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndDelete-errorLabels.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndDelete succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json deleted file mode 100644 index c18b63f45..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndDelete-serverErrors.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndDelete succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndDelete.json b/tests/SpecTests/retryable-writes/findOneAndDelete.json deleted file mode 100644 index 0cbe18108..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndDelete.json +++ /dev/null @@ -1,137 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndDelete is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndDelete is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndDelete", - "arguments": { - "filter": { - "x": { - "$gte": 11 - } - }, - "sort": { - "x": 1 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json deleted file mode 100644 index afa2f47af..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndReplace-errorLabels.json +++ /dev/null @@ -1,122 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndReplace succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json deleted file mode 100644 index 944a3af84..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndReplace-serverErrors.json +++ /dev/null @@ -1,178 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndReplace succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndReplace.json b/tests/SpecTests/retryable-writes/findOneAndReplace.json deleted file mode 100644 index e1f9ab7f8..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndReplace.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndReplace is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndReplace is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json b/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json deleted file mode 100644 index 19b3a9e77..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndUpdate-errorLabels.json +++ /dev/null @@ -1,124 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json b/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json deleted file mode 100644 index e83a61061..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndUpdate-serverErrors.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/findOneAndUpdate.json b/tests/SpecTests/retryable-writes/findOneAndUpdate.json deleted file mode 100644 index 9ae2d87d8..000000000 --- a/tests/SpecTests/retryable-writes/findOneAndUpdate.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "FindOneAndUpdate is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - } - }, - "outcome": { - "result": { - "_id": 1, - "x": 11 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "FindOneAndUpdate is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertMany-errorLabels.json b/tests/SpecTests/retryable-writes/insertMany-errorLabels.json deleted file mode 100644 index 65fd377fa..000000000 --- a/tests/SpecTests/retryable-writes/insertMany-errorLabels.json +++ /dev/null @@ -1,130 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertMany-serverErrors.json b/tests/SpecTests/retryable-writes/insertMany-serverErrors.json deleted file mode 100644 index fe8dbf4a6..000000000 --- a/tests/SpecTests/retryable-writes/insertMany-serverErrors.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertMany.json b/tests/SpecTests/retryable-writes/insertMany.json deleted file mode 100644 index 0ad326e2d..000000000 --- a/tests/SpecTests/retryable-writes/insertMany.json +++ /dev/null @@ -1,163 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - } - ], - "tests": [ - { - "description": "InsertMany succeeds after one network error", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany with unordered execution", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "options": { - "ordered": false - } - } - }, - "outcome": { - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertMany fails after multiple network errors", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": "alwaysOn", - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertMany", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "options": { - "ordered": true - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertOne-errorLabels.json b/tests/SpecTests/retryable-writes/insertOne-errorLabels.json deleted file mode 100644 index d90ac5dfb..000000000 --- a/tests/SpecTests/retryable-writes/insertOne-errorLabels.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [], - "tests": [ - { - "description": "InsertOne succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "x": 11 - } - } - }, - "outcome": { - "result": { - "insertedId": 1 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - } - ] - } - } - }, - { - "description": "InsertOne fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1, - "x": 11 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertOne-serverErrors.json b/tests/SpecTests/retryable-writes/insertOne-serverErrors.json deleted file mode 100644 index 5179a6ab7..000000000 --- a/tests/SpecTests/retryable-writes/insertOne-serverErrors.json +++ /dev/null @@ -1,1162 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "InsertOne succeeds after connection failure", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after connection failure when retryWrites option is false", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 10107, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13436, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 13435, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11602, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 7, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 6, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 9001, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 89, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after ExceededTimeLimit", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 262, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after multiple retryable writeConcernErrors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails after WriteConcernError WriteConcernFailed", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": true - } - } - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/insertOne.json b/tests/SpecTests/retryable-writes/insertOne.json deleted file mode 100644 index 04dee6dd6..000000000 --- a/tests/SpecTests/retryable-writes/insertOne.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "InsertOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "result": { - "insertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - } - }, - { - "description": "InsertOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json b/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json deleted file mode 100644 index 6029b875d..000000000 --- a/tests/SpecTests/retryable-writes/replaceOne-errorLabels.json +++ /dev/null @@ -1,121 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "ReplaceOne succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json b/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json deleted file mode 100644 index 6b35722e1..000000000 --- a/tests/SpecTests/retryable-writes/replaceOne-serverErrors.json +++ /dev/null @@ -1,177 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "ReplaceOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/replaceOne.json b/tests/SpecTests/retryable-writes/replaceOne.json deleted file mode 100644 index e5b8cf8ea..000000000 --- a/tests/SpecTests/retryable-writes/replaceOne.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "ReplaceOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 111 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "ReplaceOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/updateMany.json b/tests/SpecTests/retryable-writes/updateMany.json deleted file mode 100644 index 46fef73e7..000000000 --- a/tests/SpecTests/retryable-writes/updateMany.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateMany ignores retryWrites", - "useMultipleMongoses": true, - "operation": { - "name": "updateMany", - "arguments": { - "filter": {}, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 23 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/updateOne-errorLabels.json b/tests/SpecTests/retryable-writes/updateOne-errorLabels.json deleted file mode 100644 index 5bd00cde9..000000000 --- a/tests/SpecTests/retryable-writes/updateOne-errorLabels.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateOne succeeds with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne fails if server does not return RetryableWriteError", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/updateOne-serverErrors.json b/tests/SpecTests/retryable-writes/updateOne-serverErrors.json deleted file mode 100644 index cf274f57e..000000000 --- a/tests/SpecTests/retryable-writes/updateOne-serverErrors.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topology": [ - "sharded", - "load-balanced" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateOne succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne fails with a RetryableWriteError label after two connection failures", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "result": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/retryable-writes/updateOne.json b/tests/SpecTests/retryable-writes/updateOne.json deleted file mode 100644 index 0f806dc3d..000000000 --- a/tests/SpecTests/retryable-writes/updateOne.json +++ /dev/null @@ -1,288 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "3.6", - "topology": [ - "replicaset" - ] - } - ], - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ], - "tests": [ - { - "description": "UpdateOne is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is not committed on first attempt", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 3 - }, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 34 - } - ] - } - } - }, - { - "description": "UpdateOne with upsert is never committed", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - }, - "operation": { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 3, - "x": 33 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - }, - "outcome": { - "error": true, - "collection": { - "data": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/callback-aborts.json b/tests/SpecTests/transactions-convenient-api/callback-aborts.json deleted file mode 100644 index 2a3038e8b..000000000 --- a/tests/SpecTests/transactions-convenient-api/callback-aborts.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "withTransaction succeeds if callback aborts", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "withTransaction succeeds if callback aborts with no ops", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "abortTransaction", - "object": "session0" - } - ] - } - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "withTransaction still succeeds if callback aborts and runs extra op", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "autocommit": null, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/callback-commits.json b/tests/SpecTests/transactions-convenient-api/callback-commits.json deleted file mode 100644 index 4abbbdd0e..000000000 --- a/tests/SpecTests/transactions-convenient-api/callback-commits.json +++ /dev/null @@ -1,303 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "withTransaction succeeds if callback commits", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "withTransaction still succeeds if callback commits and runs extra op", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session0", - "autocommit": null, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/callback-retry.json b/tests/SpecTests/transactions-convenient-api/callback-retry.json deleted file mode 100644 index a0391c1b5..000000000 --- a/tests/SpecTests/transactions-convenient-api/callback-retry.json +++ /dev/null @@ -1,315 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "callback succeeds after multiple connection errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "callback is not retried after non-transient error (DuplicateKeyError)", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ] - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ], - "errorContains": "E11000" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/commit-retry.json b/tests/SpecTests/transactions-convenient-api/commit-retry.json deleted file mode 100644 index 02e38460d..000000000 --- a/tests/SpecTests/transactions-convenient-api/commit-retry.json +++ /dev/null @@ -1,531 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction succeeds after multiple connection errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction retry only overwrites write concern w option", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - }, - "options": { - "writeConcern": { - "w": 2, - "j": true, - "wtimeout": 5000 - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 2, - "j": true, - "wtimeout": 5000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "j": true, - "wtimeout": 5000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "j": true, - "wtimeout": 5000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit is retried after commitTransaction UnknownTransactionCommitResult (NotWritablePrimary)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 10107, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit is not retried after MaxTimeMSExpired error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 50 - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - }, - "options": { - "maxCommitTimeMS": 60000 - } - }, - "result": { - "errorCodeName": "MaxTimeMSExpired", - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "maxTimeMS": 60000, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror-4.2.json b/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror-4.2.json deleted file mode 100644 index 7663bb54e..000000000 --- a/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror-4.2.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.6", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "transaction is retried after commitTransaction TransientTransactionError (PreparedTransactionInProgress)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 267, - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror.json b/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror.json deleted file mode 100644 index 18becbe09..000000000 --- a/tests/SpecTests/transactions-convenient-api/commit-transienttransactionerror.json +++ /dev/null @@ -1,725 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "transaction is retried after commitTransaction TransientTransactionError (LockTimeout)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 24, - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction is retried after commitTransaction TransientTransactionError (WriteConflict)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 112, - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction is retried after commitTransaction TransientTransactionError (SnapshotUnavailable)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 246, - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction is retried after commitTransaction TransientTransactionError (NoSuchTransaction)", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 251, - "closeConnection": false - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/commit-writeconcernerror.json b/tests/SpecTests/transactions-convenient-api/commit-writeconcernerror.json deleted file mode 100644 index fbad64554..000000000 --- a/tests/SpecTests/transactions-convenient-api/commit-writeconcernerror.json +++ /dev/null @@ -1,602 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction is retried after WriteConcernFailed timeout error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": true - } - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction is retried after WriteConcernFailed non-timeout error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "multiple errors reported" - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction is not retried after UnknownReplWriteConcern error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 79, - "codeName": "UnknownReplWriteConcern", - "errmsg": "No write concern mode named 'foo' found in replica set configuration" - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - }, - "result": { - "errorCodeName": "UnknownReplWriteConcern", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction is not retried after UnsatisfiableWriteConcern error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 100, - "codeName": "UnsatisfiableWriteConcern", - "errmsg": "Not enough data-bearing nodes" - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - }, - "result": { - "errorCodeName": "UnsatisfiableWriteConcern", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction is not retried after MaxTimeMSExpired error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 50, - "codeName": "MaxTimeMSExpired", - "errmsg": "operation exceeded time limit" - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - }, - "result": { - "errorCodeName": "MaxTimeMSExpired", - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/commit.json b/tests/SpecTests/transactions-convenient-api/commit.json deleted file mode 100644 index 0a7451db9..000000000 --- a/tests/SpecTests/transactions-convenient-api/commit.json +++ /dev/null @@ -1,286 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "withTransaction commits after callback returns", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "withTransaction commits after callback returns (second transaction)", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": { - "afterClusterTime": 42 - }, - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions-convenient-api/transaction-options.json b/tests/SpecTests/transactions-convenient-api/transaction-options.json deleted file mode 100644 index 6deff43cf..000000000 --- a/tests/SpecTests/transactions-convenient-api/transaction-options.json +++ /dev/null @@ -1,577 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "withTransaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "withTransaction and no transaction options set", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "readConcern": null, - "startTransaction": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "withTransaction inherits transaction options from client", - "useMultipleMongoses": true, - "clientOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "withTransaction inherits transaction options from defaultTransactionOptions", - "useMultipleMongoses": true, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "withTransaction explicit transaction options", - "useMultipleMongoses": true, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - }, - "options": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "withTransaction explicit transaction options override defaultTransactionOptions", - "useMultipleMongoses": true, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - }, - "options": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "withTransaction explicit transaction options override client options", - "useMultipleMongoses": true, - "clientOptions": { - "readConcernLevel": "local", - "w": "majority" - }, - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": { - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ] - }, - "options": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "withTransaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": null, - "startTransaction": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/abort.json b/tests/SpecTests/transactions/abort.json deleted file mode 100644 index 3729a9829..000000000 --- a/tests/SpecTests/transactions/abort.json +++ /dev/null @@ -1,621 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "implicit abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "two aborts", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "cannot call abortTransaction twice" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort without start", - "operations": [ - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort directly after no-op commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call abortTransaction after calling commitTransaction" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort directly after commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call abortTransaction after calling commitTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "abort ignores TransactionAborted", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ], - "errorContains": "E11000" - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "NoSuchTransaction", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abort does not apply writeConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 10 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/bulk.json b/tests/SpecTests/transactions/bulk.json deleted file mode 100644 index 8a9793b8b..000000000 --- a/tests/SpecTests/transactions/bulk.json +++ /dev/null @@ -1,531 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "bulk", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": 2 - } - }, - "upsert": true - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 3 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 4 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 5 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 6 - } - } - }, - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 7 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - } - } - }, - { - "name": "replaceOne", - "arguments": { - "filter": { - "_id": 2 - }, - "replacement": { - "y": 2 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 3 - } - } - }, - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 4 - } - } - }, - { - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gte": 2 - } - }, - "update": { - "$set": { - "z": 1 - } - } - } - }, - { - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gte": 6 - } - } - } - } - ] - }, - "result": { - "deletedCount": 4, - "insertedCount": 6, - "insertedIds": { - "0": 1, - "3": 3, - "4": 4, - "5": 5, - "6": 6, - "7": 7 - }, - "matchedCount": 7, - "modifiedCount": 7, - "upsertedCount": 1, - "upsertedIds": { - "2": 2 - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 1 - } - } - }, - { - "q": { - "_id": 2 - }, - "u": { - "$set": { - "x": 2 - } - }, - "upsert": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - }, - { - "_id": 6 - }, - { - "_id": 7 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "y": 1 - } - }, - { - "q": { - "_id": 2 - }, - "u": { - "y": 2 - } - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 3 - }, - "limit": 1 - }, - { - "q": { - "_id": 4 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 2 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gte": 6 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "y": 1 - }, - { - "_id": 2, - "y": 2, - "z": 1 - }, - { - "_id": 5, - "z": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/causal-consistency.json b/tests/SpecTests/transactions/causal-consistency.json deleted file mode 100644 index 0e81bf2ff..000000000 --- a/tests/SpecTests/transactions/causal-consistency.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1, - "count": 0 - } - ], - "tests": [ - { - "description": "causal consistency", - "clientOptions": { - "retryWrites": false - }, - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - } - } - ], - "ordered": true, - "lsid": "session0", - "readConcern": null, - "txnNumber": null, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - } - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "count": 2 - } - ] - } - } - }, - { - "description": "causal consistency disabled", - "clientOptions": { - "retryWrites": false - }, - "sessionOptions": { - "session0": { - "causalConsistency": false - } - }, - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "count": 1 - } - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "count": 1 - } - } - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1, - "count": 1 - }, - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/commit.json b/tests/SpecTests/transactions/commit.json deleted file mode 100644 index faa39a65f..000000000 --- a/tests/SpecTests/transactions/commit.json +++ /dev/null @@ -1,925 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "rerun commit after empty transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "multiple commits in a row", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "write concern error on commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 10 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit without start", - "operations": [ - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commit after no-op abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call commitTransaction after calling abortTransaction" - } - } - ], - "expectations": [], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commit after abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "Cannot call commitTransaction after calling abortTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "multiple commits after empty transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "reset session state commit", - "clientOptions": { - "retryWrites": false - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "reset session state abort", - "clientOptions": { - "retryWrites": false - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0", - "result": { - "errorContains": "no transaction started" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/count.json b/tests/SpecTests/transactions/count.json deleted file mode 100644 index 169296416..000000000 --- a/tests/SpecTests/transactions/count.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0.2", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "tests": [ - { - "description": "count", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "count", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "OperationNotSupportedInTransaction", - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "count": "test", - "query": { - "_id": 1 - }, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "count", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/create-collection.json b/tests/SpecTests/transactions/create-collection.json deleted file mode 100644 index 9071c59c4..000000000 --- a/tests/SpecTests/transactions/create-collection.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4", - "topology": [ - "replicaset", - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "explicitly create collection using create command", - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "session": "session0", - "collection": "test" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "test", - "writeConcern": null - }, - "command_name": "drop", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "create": "test", - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "create", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "implicitly create collection using insert", - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "test", - "writeConcern": null - }, - "command_name": "drop", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/create-index.json b/tests/SpecTests/transactions/create-index.json deleted file mode 100644 index 2ff09c928..000000000 --- a/tests/SpecTests/transactions/create-index.json +++ /dev/null @@ -1,237 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.4", - "topology": [ - "replicaset", - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "create index on a non-existing collection", - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "createIndex", - "object": "collection", - "arguments": { - "session": "session0", - "name": "t_1", - "keys": { - "x": 1 - } - } - }, - { - "name": "assertIndexNotExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test", - "index": "t_1" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test", - "index": "t_1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "test", - "writeConcern": null - }, - "command_name": "drop", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "test", - "indexes": [ - { - "name": "t_1", - "key": { - "x": 1 - } - } - ], - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "createIndexes", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "create index on a collection created within the same transaction", - "operations": [ - { - "name": "dropCollection", - "object": "database", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "createCollection", - "object": "database", - "arguments": { - "session": "session0", - "collection": "test" - } - }, - { - "name": "createIndex", - "object": "collection", - "arguments": { - "session": "session0", - "name": "t_1", - "keys": { - "x": 1 - } - } - }, - { - "name": "assertIndexNotExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test", - "index": "t_1" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "database": "transaction-tests", - "collection": "test", - "index": "t_1" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "drop": "test", - "writeConcern": null - }, - "command_name": "drop", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "create": "test", - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "create", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "createIndexes": "test", - "indexes": [ - { - "name": "t_1", - "key": { - "x": 1 - } - } - ], - "lsid": "session0", - "writeConcern": null - }, - "command_name": "createIndexes", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/delete.json b/tests/SpecTests/transactions/delete.json deleted file mode 100644 index 65b832703..000000000 --- a/tests/SpecTests/transactions/delete.json +++ /dev/null @@ -1,327 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "tests": [ - { - "description": "delete", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$lte": 3 - } - } - }, - "result": { - "deletedCount": 2 - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$lte": 3 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 4 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 5 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for delete", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "deleteOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "deleteMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$lte": 3 - } - } - }, - "result": { - "deletedCount": 2 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$lte": 3 - } - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/error-labels.json b/tests/SpecTests/transactions/error-labels.json deleted file mode 100644 index 0be19c731..000000000 --- a/tests/SpecTests/transactions/error-labels.json +++ /dev/null @@ -1,2086 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ], - "serverless": "forbid" - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "DuplicateKey errors do not contain transient label", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 1 - }, - { - "_id": 1 - } - ] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ], - "errorContains": "E11000" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - }, - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NotWritablePrimary errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 10107 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "WriteConflict errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 112 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NoSuchTransaction errors contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 251 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "NoSuchTransaction errors on commit contain transient label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 251 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add TransientTransactionError label to connection errors, but do not add RetryableWriteError label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 4 - }, - "data": { - "failCommands": [ - "insert", - "find", - "aggregate", - "distinct" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": {}, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to connection errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to retryable commit errors", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11602, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add RetryableWriteError and UnknownTransactionCommitResult labels to writeConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "do not add RetryableWriteError label to writeConcernError ShutdownInProgress that occurs within transaction", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [], - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernFailed", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "errmsg": "multiple errors reported" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add UnknownTransactionCommitResult label to writeConcernError WriteConcernFailed with wtimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 64, - "codeName": "WriteConcernFailed", - "errmsg": "waiting for replication timed out", - "errInfo": { - "wtimeout": true - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "omit UnknownTransactionCommitResult label from writeConcernError UnsatisfiableWriteConcern", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 100, - "errmsg": "Not enough data-bearing nodes" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "omit UnknownTransactionCommitResult label from writeConcernError UnknownReplWriteConcern", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 79, - "errmsg": "No write concern mode named 'blah' found in replica set configuration" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "RetryableWriteConcern", - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "do not add UnknownTransactionCommitResult label to MaxTimeMSExpired inside transactions", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 50 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "maxTimeMS": 60000, - "session": "session0" - }, - "result": { - "errorLabelsOmit": [ - "RetryableWriteError", - "UnknownTransactionCommitResult", - "TransientTransactionError" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": {}, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "maxTimeMS": 60000 - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "add UnknownTransactionCommitResult label to MaxTimeMSExpired", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 50 - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - }, - "maxCommitTimeMS": 60000 - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "add UnknownTransactionCommitResult label to writeConcernError MaxTimeMSExpired", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 50, - "errmsg": "operation exceeded time limit" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - }, - "maxCommitTimeMS": 60000 - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/errors-client.json b/tests/SpecTests/transactions/errors-client.json deleted file mode 100644 index 15fae96fe..000000000 --- a/tests/SpecTests/transactions/errors-client.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "Client side error in command starting transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "x": 1 - } - }, - "error": true - }, - { - "name": "assertSessionTransactionState", - "object": "testRunner", - "arguments": { - "session": "session0", - "state": "starting" - } - } - ] - }, - { - "description": "Client side error when transaction is in progress", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "x": 1 - } - }, - "error": true - }, - { - "name": "assertSessionTransactionState", - "object": "testRunner", - "arguments": { - "session": "session0", - "state": "in_progress" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/errors.json b/tests/SpecTests/transactions/errors.json deleted file mode 100644 index 5fc4905e8..000000000 --- a/tests/SpecTests/transactions/errors.json +++ /dev/null @@ -1,222 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "start insert start", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ] - }, - { - "description": "start twice", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - } - ] - }, - { - "description": "commit and start twice", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transaction already in progress" - } - } - ] - }, - { - "description": "write conflict commit", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "WriteConflict", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session1", - "result": { - "errorCodeName": "NoSuchTransaction", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - } - ] - }, - { - "description": "write conflict abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "result": { - "errorCodeName": "WriteConflict", - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "abortTransaction", - "object": "session1" - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/findOneAndDelete.json b/tests/SpecTests/transactions/findOneAndDelete.json deleted file mode 100644 index d82657a9f..000000000 --- a/tests/SpecTests/transactions/findOneAndDelete.json +++ /dev/null @@ -1,221 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndDelete", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - } - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - } - }, - "result": null - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndDelete", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - } - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/findOneAndReplace.json b/tests/SpecTests/transactions/findOneAndReplace.json deleted file mode 100644 index 7a54ca343..000000000 --- a/tests/SpecTests/transactions/findOneAndReplace.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndReplace", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "replacement": { - "x": 1 - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "replacement": { - "x": 1 - }, - "upsert": true, - "returnDocument": "After" - }, - "result": { - "_id": 4, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "x": 1 - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "update": { - "x": 1 - }, - "new": true, - "upsert": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "x": 1 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndReplace", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "replacement": { - "x": 1 - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "x": 1 - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/findOneAndUpdate.json b/tests/SpecTests/transactions/findOneAndUpdate.json deleted file mode 100644 index 7af54ba80..000000000 --- a/tests/SpecTests/transactions/findOneAndUpdate.json +++ /dev/null @@ -1,413 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "findOneAndUpdate", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true, - "returnDocument": "After" - }, - "result": { - "_id": 4, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3, - "x": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3, - "x": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": true, - "upsert": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "x": 2 - }, - { - "_id": 4, - "x": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for findOneAndUpdate", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 3 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 3 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/insert.json b/tests/SpecTests/transactions/insert.json deleted file mode 100644 index f26e7c2a7..000000000 --- a/tests/SpecTests/transactions/insert.json +++ /dev/null @@ -1,648 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "insert", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "insertedId": 4 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 5 - } - }, - "result": { - "insertedId": 5 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 5 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - }, - { - "description": "insert with session1", - "operations": [ - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "session": "session1" - }, - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - } - }, - { - "name": "commitTransaction", - "object": "session1" - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session1", - "document": { - "_id": 4 - } - }, - "result": { - "insertedId": 4 - } - }, - { - "name": "abortTransaction", - "object": "session1" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session1", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session1", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session1", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session1", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session1", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - } - }, - { - "description": "collection writeConcern without transaction", - "clientOptions": { - "retryWrites": false - }, - "operations": [ - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "collection writeConcern ignored for insert", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "insertMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 2, - "1": 3 - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/isolation.json b/tests/SpecTests/transactions/isolation.json deleted file mode 100644 index f16b28a5e..000000000 --- a/tests/SpecTests/transactions/isolation.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "one transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "two transactions", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session1" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "result": [] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - }, - "result": [ - { - "_id": 1 - } - ] - }, - { - "name": "commitTransaction", - "object": "session1" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/mongos-pin-auto.json b/tests/SpecTests/transactions/mongos-pin-auto.json deleted file mode 100644 index 037f212f4..000000000 --- a/tests/SpecTests/transactions/mongos-pin-auto.json +++ /dev/null @@ -1,4815 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ], - "serverless": "forbid" - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ], - "tests": [ - { - "description": "remain pinned after non-transient Interrupted error on insertOne", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ], - "errorCodeName": "Interrupted" - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - } - }, - { - "description": "unpin after transient error within a transaction", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on insertOne insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on insertMany insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on updateOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on replaceOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on updateMany update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "updateMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on deleteOne delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on deleteMany delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on findOneAndDelete findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on findOneAndUpdate findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on findOneAndReplace findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on bulkWrite insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on bulkWrite update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - } - ] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on bulkWrite delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on find find", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on countDocuments aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "session": "session0", - "filter": {} - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on aggregate aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "session": "session0", - "pipeline": [] - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on distinct distinct", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient Interrupted error on runCommand insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on insertOne insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on insertOne insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on insertMany insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on insertMany insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on updateOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - } - } - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on updateOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on replaceOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - } - } - }, - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on replaceOne update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on updateMany update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - } - } - }, - { - "name": "updateMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on updateMany update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "updateMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on deleteOne delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "closeConnection": true - } - } - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on deleteOne delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on deleteMany delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "closeConnection": true - } - } - } - }, - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on deleteMany delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 1 - } - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on findOneAndDelete findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on findOneAndDelete findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on findOneAndUpdate findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on findOneAndUpdate findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on findOneAndReplace findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on findOneAndReplace findAndModify", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "replacement": { - "y": 1 - }, - "returnDocument": "Before" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on bulkWrite insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on bulkWrite insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on bulkWrite update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "closeConnection": true - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on bulkWrite update", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on bulkWrite delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "closeConnection": true - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on bulkWrite delete", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "session": "session0", - "requests": [ - { - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on find find", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on find find", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on countDocuments aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - } - } - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "session": "session0", - "filter": {} - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on countDocuments aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "session": "session0", - "filter": {} - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on aggregate aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - } - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "session": "session0", - "pipeline": [] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on aggregate aggregate", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "session": "session0", - "pipeline": [] - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on distinct distinct", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "closeConnection": true - } - } - } - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on distinct distinct", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "distinct" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient connection error on runCommand insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient ShutdownInProgress error on runCommand insert", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/mongos-recovery-token.json b/tests/SpecTests/transactions/mongos-recovery-token.json deleted file mode 100644 index da4e9861d..000000000 --- a/tests/SpecTests/transactions/mongos-recovery-token.json +++ /dev/null @@ -1,511 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ], - "serverless": "forbid" - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction explicit retries include recoveryToken", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction retry succeeds on new mongos", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - }, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction retry fails on new mongos", - "useMultipleMongoses": true, - "clientOptions": { - "heartbeatFrequencyMS": 30000 - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 7 - }, - "data": { - "failCommands": [ - "commitTransaction", - "isMaster", - "hello" - ], - "closeConnection": true - } - } - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ], - "errorCodeName": "NoSuchTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - }, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction sends recoveryToken", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - } - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/pin-mongos.json b/tests/SpecTests/transactions/pin-mongos.json deleted file mode 100644 index 485a3d932..000000000 --- a/tests/SpecTests/transactions/pin-mongos.json +++ /dev/null @@ -1,1229 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ], - "serverless": "forbid" - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ], - "tests": [ - { - "description": "countDocuments", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "distinct", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "_id", - "session": "session0" - }, - "result": [ - 1, - 2 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "find", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "session": "session0" - }, - "result": [ - { - "_id": 2 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "insertOne", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 3 - }, - "session": "session0" - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 4 - }, - "session": "session0" - }, - "result": { - "insertedId": 4 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 5 - }, - "session": "session0" - }, - "result": { - "insertedId": 5 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 6 - }, - "session": "session0" - }, - "result": { - "insertedId": 6 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 7 - }, - "session": "session0" - }, - "result": { - "insertedId": 7 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 8 - }, - "session": "session0" - }, - "result": { - "insertedId": 8 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 9 - }, - "session": "session0" - }, - "result": { - "insertedId": 9 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 10 - }, - "session": "session0" - }, - "result": { - "insertedId": 10 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - }, - { - "_id": 6 - }, - { - "_id": 7 - }, - { - "_id": 8 - }, - { - "_id": 9 - }, - { - "_id": 10 - } - ] - } - } - }, - { - "description": "mixed read write operations", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 3 - }, - "session": "session0" - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 3 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 3 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 3 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 3 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "_id": 3 - }, - "session": "session0" - }, - "result": 1 - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 4 - }, - "session": "session0" - }, - "result": { - "insertedId": 4 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 5 - }, - "session": "session0" - }, - "result": { - "insertedId": 5 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 6 - }, - "session": "session0" - }, - "result": { - "insertedId": 6 - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 7 - }, - "session": "session0" - }, - "result": { - "insertedId": 7 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - }, - { - "_id": 6 - }, - { - "_id": 7 - } - ] - } - } - }, - { - "description": "multiple commits", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 3, - "1": 4 - } - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "remain pinned after non-transient error on commit", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 3, - "1": 4 - } - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 51 - } - } - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "TransientTransactionError" - ], - "errorCode": 51 - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "unpin after transient error within a transaction", - "useMultipleMongoses": true, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unpin after transient error within a transaction and commit", - "useMultipleMongoses": true, - "clientOptions": { - "heartbeatFrequencyMS": 30000 - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 7 - }, - "data": { - "failCommands": [ - "insert", - "isMaster", - "hello" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ], - "errorCodeName": "NoSuchTransaction" - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null, - "recoveryToken": 42 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/read-concern.json b/tests/SpecTests/transactions/read-concern.json deleted file mode 100644 index dd9243e2f..000000000 --- a/tests/SpecTests/transactions/read-concern.json +++ /dev/null @@ -1,1628 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "tests": [ - { - "description": "only first countDocuments includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": { - "level": "majority" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first find includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": { - "level": "majority" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first aggregate includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": { - "level": "majority" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first distinct includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": { - "level": "majority" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "only first runCommand includes readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": { - "level": "majority" - }, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "countDocuments ignores collection readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "countDocuments", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 2 - } - } - }, - "result": 3 - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "_id": { - "$gte": 2 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "cursor": {}, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "find ignores collection readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "aggregate ignores collection readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "distinct ignores collection readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "distinct", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "runCommand ignores database readConcern", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "databaseOptions": { - "readConcern": { - "level": "majority" - } - }, - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/read-pref.json b/tests/SpecTests/transactions/read-pref.json deleted file mode 100644 index bf1f1970e..000000000 --- a/tests/SpecTests/transactions/read-pref.json +++ /dev/null @@ -1,720 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "default readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": [ - { - "count": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "primary readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": [ - { - "count": 1 - } - ] - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Secondary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "secondary readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "primaryPreferred readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "PrimaryPreferred" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "nearest readPreference", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Nearest" - } - } - } - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2, - "2": 3, - "3": 4 - } - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - }, - { - "$count": "count" - } - ] - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "aggregate", - "object": "collection", - "collectionOptions": { - "readPreference": { - "mode": "Primary" - } - }, - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "secondary write only", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/reads.json b/tests/SpecTests/transactions/reads.json deleted file mode 100644 index 9fc587f48..000000000 --- a/tests/SpecTests/transactions/reads.json +++ /dev/null @@ -1,543 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ], - "tests": [ - { - "description": "collection readConcern without transaction", - "operations": [ - { - "name": "find", - "object": "collection", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - }, - "arguments": { - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "readConcern": { - "level": "majority" - }, - "lsid": "session0", - "txnNumber": null, - "startTransaction": null, - "autocommit": null - }, - "command_name": "find", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "find", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "batchSize": 3 - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "find": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "find", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "aggregate", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "batchSize": 3, - "session": "session0" - }, - "result": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "cursor": { - "batchSize": 3 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "aggregate", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "getMore": { - "$numberLong": "42" - }, - "collection": "test", - "batchSize": 3, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false - }, - "command_name": "getMore", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - }, - { - "description": "distinct", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "distinct", - "object": "collection", - "arguments": { - "session": "session0", - "fieldName": "_id" - }, - "result": [ - 1, - 2, - 3, - 4 - ] - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "distinct": "test", - "key": "_id", - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "distinct", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "readConcern": null, - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/retryable-abort-errorLabels.json b/tests/SpecTests/transactions/retryable-abort-errorLabels.json deleted file mode 100644 index 1110ce2c3..000000000 --- a/tests/SpecTests/transactions/retryable-abort-errorLabels.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "abortTransaction only retries once with RetryableWriteError from server", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction does not retry without RetryableWriteError label", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/retryable-abort.json b/tests/SpecTests/transactions/retryable-abort.json deleted file mode 100644 index 13cc7c88f..000000000 --- a/tests/SpecTests/transactions/retryable-abort.json +++ /dev/null @@ -1,2017 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "abortTransaction only performs a single retry", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction does not retry after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction does not retry after WriteConcernError Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "writeConcernError": { - "code": 11601, - "errmsg": "operation was interrupted" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after connection error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 10107, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 13436, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 13435, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11602, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 11600, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 91, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 7, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 6, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 9001, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 89, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "abortTransaction succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/retryable-commit-errorLabels.json b/tests/SpecTests/transactions/retryable-commit-errorLabels.json deleted file mode 100644 index e0818f237..000000000 --- a/tests/SpecTests/transactions/retryable-commit-errorLabels.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.3.1", - "topology": [ - "replicaset", - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction does not retry error without RetryableWriteError label", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11600, - "errorLabels": [] - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commitTransaction retries once with RetryableWriteError from server", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 112, - "errorLabels": [ - "RetryableWriteError" - ] - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/retryable-commit.json b/tests/SpecTests/transactions/retryable-commit.json deleted file mode 100644 index 49148c62d..000000000 --- a/tests/SpecTests/transactions/retryable-commit.json +++ /dev/null @@ -1,2336 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "commitTransaction fails after two errors", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction applies majority write concern on retries", - "clientOptions": { - "retryWrites": false - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 2, - "j": true, - "wtimeout": 5000 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsContain": [ - "RetryableWriteError", - "UnknownTransactionCommitResult" - ], - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": 2, - "j": true, - "wtimeout": 5000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "j": true, - "wtimeout": 5000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "j": true, - "wtimeout": 5000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction fails after Interrupted", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11601, - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorCodeName": "Interrupted", - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "commitTransaction is not retried after UnsatisfiableWriteConcern error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "writeConcernError": { - "code": 100, - "errmsg": "Not enough data-bearing nodes" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0", - "result": { - "errorLabelsOmit": [ - "RetryableWriteError", - "TransientTransactionError", - "UnknownTransactionCommitResult" - ] - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after connection error", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotWritablePrimary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 10107, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotPrimaryOrSecondary", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 13436, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NotPrimaryNoSecondaryOk", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 13435, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11602, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 11600, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 91, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after HostNotFound", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 7, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after HostUnreachable", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 6, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after SocketException", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 9001, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after NetworkTimeout", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 89, - "errorLabels": [ - "RetryableWriteError" - ], - "closeConnection": false - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError InterruptedAtShutdown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11600, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError InterruptedDueToReplStateChange", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 11602, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError PrimarySteppedDown", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 189, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commitTransaction succeeds after WriteConcernError ShutdownInProgress", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorLabels": [ - "RetryableWriteError" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority", - "wtimeout": 10000 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/retryable-writes.json b/tests/SpecTests/transactions/retryable-writes.json deleted file mode 100644 index c932893b5..000000000 --- a/tests/SpecTests/transactions/retryable-writes.json +++ /dev/null @@ -1,343 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "increment txnNumber", - "clientOptions": { - "retryWrites": true - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "result": { - "insertedId": 3 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "session": "session0" - }, - "result": { - "insertedIds": { - "0": 4, - "1": 5 - } - } - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": { - "afterClusterTime": 42 - }, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "3" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - }, - { - "_id": 5 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "4" - }, - "startTransaction": null, - "autocommit": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - } - }, - { - "description": "writes are not retried", - "clientOptions": { - "retryWrites": true - }, - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/run-command.json b/tests/SpecTests/transactions/run-command.json deleted file mode 100644 index 2f2a3a881..000000000 --- a/tests/SpecTests/transactions/run-command.json +++ /dev/null @@ -1,306 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "run command with default read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command with secondary read preference in client option and primary read preference in transaction options", - "clientOptions": { - "readPreference": "secondary" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command with explicit primary read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "insert", - "arguments": { - "session": "session0", - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ] - }, - "readPreference": { - "mode": "Primary" - } - }, - "result": { - "n": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - }, - { - "description": "run command fails with explicit secondary read preference", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - }, - "readPreference": { - "mode": "Secondary" - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - } - ] - }, - { - "description": "run command fails with secondary read preference from transaction options", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "command_name": "find", - "arguments": { - "session": "session0", - "command": { - "find": "test" - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/transaction-options-repl.json b/tests/SpecTests/transactions/transaction-options-repl.json deleted file mode 100644 index 33324debb..000000000 --- a/tests/SpecTests/transactions/transaction-options-repl.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "readConcern snapshot in startTransaction options", - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "snapshot" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "snapshot", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/transaction-options.json b/tests/SpecTests/transactions/transaction-options.json deleted file mode 100644 index 25d245dca..000000000 --- a/tests/SpecTests/transactions/transaction-options.json +++ /dev/null @@ -1,1404 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [], - "tests": [ - { - "description": "no transaction options set", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "afterClusterTime": 42 - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction options inherited from client", - "clientOptions": { - "w": 1, - "readConcernLevel": "local" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - }, - "maxTimeMS": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local", - "afterClusterTime": 42 - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - }, - "maxTimeMS": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "transaction options inherited from defaultTransactionOptions", - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - }, - "maxCommitTimeMS": 60000 - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "startTransaction options override defaults", - "clientOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "snapshot" - }, - "writeConcern": { - "w": 1 - }, - "maxCommitTimeMS": 30000 - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": "majority" - }, - "maxCommitTimeMS": 60000 - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority", - "afterClusterTime": 42 - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "defaultTransactionOptions override client options", - "clientOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": "majority" - }, - "maxCommitTimeMS": 60000 - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": 60000 - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority", - "afterClusterTime": 42 - }, - "writeConcern": null, - "maxTimeMS": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": "majority" - }, - "maxTimeMS": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readConcern local in defaultTransactionOptions", - "clientOptions": { - "w": 1 - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readConcern": { - "level": "local" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "result": { - "insertedId": 2 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - }, - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local", - "afterClusterTime": 42 - }, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "2" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": { - "w": 1 - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "client writeConcern ignored for bulk", - "clientOptions": { - "w": "majority" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 1 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "session": "session0" - }, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 1 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": 1 - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readPreference inherited from client", - "clientOptions": { - "readPreference": "secondary" - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "readPreference inherited from defaultTransactionOptions", - "clientOptions": { - "readPreference": "primary" - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - }, - { - "description": "startTransaction overrides readPreference", - "clientOptions": { - "readPreference": "primary" - }, - "sessionOptions": { - "session0": { - "defaultTransactionOptions": { - "readPreference": { - "mode": "Primary" - } - } - } - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "readPreference": { - "mode": "Secondary" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "result": { - "errorContains": "read preference in a transaction must be primary" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - } - ] - } - } - } - ] -} diff --git a/tests/SpecTests/transactions/update.json b/tests/SpecTests/transactions/update.json deleted file mode 100644 index e33bf5b81..000000000 --- a/tests/SpecTests/transactions/update.json +++ /dev/null @@ -1,442 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ], - "tests": [ - { - "description": "update", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - } - }, - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "x": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateMany", - "object": "collection", - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 3 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "x": 1 - }, - "u": { - "y": 1 - } - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 3 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3, - "z": 1 - }, - { - "_id": 4, - "y": 1, - "z": 1 - } - ] - } - } - }, - { - "description": "collections writeConcern ignored for update", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "updateOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 1, - "upsertedId": 4 - } - }, - { - "name": "replaceOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "x": 1 - }, - "replacement": { - "y": 1 - } - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "updateMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": { - "$gte": 3 - } - }, - "update": { - "$set": { - "z": 1 - } - } - }, - "result": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - ], - "ordered": true, - "readConcern": null, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "x": 1 - }, - "u": { - "y": 1 - } - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gte": 3 - } - }, - "u": { - "$set": { - "z": 1 - } - }, - "multi": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ] - } - ] -} diff --git a/tests/SpecTests/transactions/write-concern.json b/tests/SpecTests/transactions/write-concern.json deleted file mode 100644 index 84b1ea365..000000000 --- a/tests/SpecTests/transactions/write-concern.json +++ /dev/null @@ -1,1278 +0,0 @@ -{ - "runOn": [ - { - "minServerVersion": "4.0", - "topology": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topology": [ - "sharded" - ] - } - ], - "database_name": "transaction-tests", - "collection_name": "test", - "data": [ - { - "_id": 0 - } - ], - "tests": [ - { - "description": "commit with majority", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - }, - { - "_id": 1 - } - ] - } - } - }, - { - "description": "commit with default", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - }, - { - "_id": 1 - } - ] - } - } - }, - { - "description": "abort with majority", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": { - "w": "majority" - } - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - } - ] - } - } - }, - { - "description": "abort with default", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "abortTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "abortTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - } - ] - } - } - }, - { - "description": "start with unacknowledged write concern", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "arguments": { - "options": { - "writeConcern": { - "w": 0 - } - } - }, - "result": { - "errorContains": "transactions do not support unacknowledged write concern" - } - } - ] - }, - { - "description": "start with implicit unacknowledged write concern", - "clientOptions": { - "w": 0 - }, - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "result": { - "errorContains": "transactions do not support unacknowledged write concern" - } - } - ] - }, - { - "description": "unacknowledged write concern coll insertOne", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "result": { - "insertedId": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - }, - { - "_id": 1 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll insertMany", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - }, - "result": { - "insertedIds": { - "0": 1, - "1": 2 - } - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - }, - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll bulkWrite", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "bulkWrite", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "requests": [ - { - "name": "insertOne", - "arguments": { - "document": { - "_id": 1 - } - } - } - ] - }, - "result": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "0": 1 - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "insert", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0 - }, - { - "_id": 1 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll deleteOne", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "deleteOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 0 - }, - "limit": 1 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "unacknowledged write concern coll deleteMany", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "deleteMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - } - }, - "result": { - "deletedCount": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 0 - }, - "limit": 0 - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "delete", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "unacknowledged write concern coll updateOne", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 0 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0, - "x": 1 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll updateMany", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateMany", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - }, - "result": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 0 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": true - } - ], - "ordered": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "update", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0, - "x": 1 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll findOneAndDelete", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndDelete", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - } - }, - "result": { - "_id": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 0 - }, - "remove": true, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [] - } - } - }, - { - "description": "unacknowledged write concern coll findOneAndReplace", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndReplace", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - }, - "replacement": { - "x": 1 - }, - "returnDocument": "Before" - }, - "result": { - "_id": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 0 - }, - "update": { - "x": 1 - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0, - "x": 1 - } - ] - } - } - }, - { - "description": "unacknowledged write concern coll findOneAndUpdate", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "findOneAndUpdate", - "object": "collection", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - }, - "arguments": { - "session": "session0", - "filter": { - "_id": 0 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "result": { - "_id": 0 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectations": [ - { - "command_started_event": { - "command": { - "findAndModify": "test", - "query": { - "_id": 0 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": true, - "autocommit": false, - "readConcern": null, - "writeConcern": null - }, - "command_name": "findAndModify", - "database_name": "transaction-tests" - } - }, - { - "command_started_event": { - "command": { - "commitTransaction": 1, - "lsid": "session0", - "txnNumber": { - "$numberLong": "1" - }, - "startTransaction": null, - "autocommit": false, - "writeConcern": null - }, - "command_name": "commitTransaction", - "database_name": "admin" - } - } - ], - "outcome": { - "collection": { - "data": [ - { - "_id": 0, - "x": 1 - } - ] - } - } - } - ] -} diff --git a/tests/TestCase.php b/tests/TestCase.php index 7ed378e11..2b6b86eb2 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,32 +3,39 @@ namespace MongoDB\Tests; use InvalidArgumentException; +use MongoDB\BSON\Document; +use MongoDB\BSON\PackedArray; +use MongoDB\Codec\Codec; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; use MongoDB\Driver\WriteConcern; use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; +use MongoDB\Tests\SpecTests\DocumentsMatchConstraint; use PHPUnit\Framework\TestCase as BaseTestCase; use ReflectionClass; use stdClass; use Traversable; use function array_map; -use function array_merge; use function array_values; use function call_user_func; +use function get_debug_type; use function getenv; use function hash; use function is_array; +use function is_int; use function is_object; use function is_string; use function iterator_to_array; -use function MongoDB\BSON\fromPHP; -use function MongoDB\BSON\toJSON; +use function preg_match; +use function preg_replace; use function restore_error_handler; use function set_error_handler; use function sprintf; +use function strtr; +use const E_DEPRECATED; use const E_USER_DEPRECATED; abstract class TestCase extends BaseTestCase @@ -41,38 +48,53 @@ public static function getUri(): string return getenv('MONGODB_URI') ?: 'mongodb://127.0.0.1:27017'; } + public static function printConfiguration(): void + { + $template = <<<'OUTPUT' +Test configuration: + URI: %uri% + Database: %database% + API version: %apiVersion% + + crypt_shared: %cryptSharedAvailable% + mongocryptd: %mongocryptdAvailable% + +OUTPUT; + + // Redact credentials in the URI to be safe + $uri = static::getUri(); + if (preg_match('#://.+:.+@#', $uri)) { + $uri = preg_replace('#(.+://).+:.+@(.+)$#', '$1@$2', $uri); + } + + $configuration = [ + '%uri%' => $uri, + '%database%' => static::getDatabaseName(), + '%apiVersion%' => getenv('API_VERSION') ?: 'Not configured', + '%cryptSharedAvailable%' => FunctionalTestCase::isCryptSharedLibAvailable() ? 'Available' : 'Not available', + '%mongocryptdAvailable%' => FunctionalTestCase::isMongocryptdAvailable() ? 'Available' : 'Not available', + ]; + + echo strtr($template, $configuration) . "\n"; + } + + /** + * Return the test database name. + */ + protected static function getDatabaseName(): string + { + return getenv('MONGODB_DATABASE') ?: 'phplib_test'; + } + /** * Asserts that a document has expected values for some fields. * * Only fields in the expected document will be checked. The actual document * may contain additional fields. - * - * @param array|object $expectedDocument - * @param array|object $actualDocument */ - public function assertMatchesDocument($expectedDocument, $actualDocument): void + public function assertMatchesDocument(array|object $expectedDocument, array|object $actualDocument): void { - $normalizedExpectedDocument = $this->normalizeBSON($expectedDocument); - $normalizedActualDocument = $this->normalizeBSON($actualDocument); - - $extraKeys = []; - - /* Avoid unsetting fields while we're iterating on the ArrayObject to - * work around https://bugs.php.net/bug.php?id=70246 */ - foreach ($normalizedActualDocument as $key => $value) { - if (! $normalizedExpectedDocument->offsetExists($key)) { - $extraKeys[] = $key; - } - } - - foreach ($extraKeys as $key) { - $normalizedActualDocument->offsetUnset($key); - } - - $this->assertEquals( - toJSON(fromPHP($normalizedExpectedDocument)), - toJSON(fromPHP($normalizedActualDocument)) - ); + (new DocumentsMatchConstraint($expectedDocument, true, true))->evaluate($actualDocument); } /** @@ -80,15 +102,12 @@ public function assertMatchesDocument($expectedDocument, $actualDocument): void * * The actual document will be compared directly with the expected document * and may not contain extra fields. - * - * @param array|object $expectedDocument - * @param array|object $actualDocument */ - public function assertSameDocument($expectedDocument, $actualDocument): void + public function assertSameDocument(array|object $expectedDocument, array|object $actualDocument): void { $this->assertEquals( - toJSON(fromPHP($this->normalizeBSON($expectedDocument))), - toJSON(fromPHP($this->normalizeBSON($actualDocument))) + Document::fromPHP($this->normalizeBSON($expectedDocument))->toRelaxedExtendedJSON(), + Document::fromPHP($this->normalizeBSON($actualDocument))->toRelaxedExtendedJSON(), ); } @@ -102,13 +121,11 @@ public function assertSameDocuments(array $expectedDocuments, $actualDocuments): throw new InvalidArgumentException('$actualDocuments is not an array or Traversable'); } - $normalizeRootDocuments = function ($document) { - return toJSON(fromPHP($this->normalizeBSON($document))); - }; + $normalizeRootDocuments = fn ($document) => Document::fromPHP($this->normalizeBSON($document))->toRelaxedExtendedJSON(); $this->assertEquals( array_map($normalizeRootDocuments, $expectedDocuments), - array_map($normalizeRootDocuments, $actualDocuments) + array_map($normalizeRootDocuments, $actualDocuments), ); } @@ -122,36 +139,66 @@ public function dataDescription(): string return is_string($dataName) ? $dataName : ''; } - public function provideInvalidArrayValues() + final public static function provideInvalidArrayValues(): array + { + return self::wrapValuesForDataProvider(self::getInvalidArrayValues()); + } + + final public static function provideInvalidDocumentValues(): array { - return $this->wrapValuesForDataProvider($this->getInvalidArrayValues()); + return self::wrapValuesForDataProvider(self::getInvalidDocumentValues()); } - public function provideInvalidDocumentValues() + final public static function provideInvalidIntegerValues(): array { - return $this->wrapValuesForDataProvider($this->getInvalidDocumentValues()); + return self::wrapValuesForDataProvider(self::getInvalidIntegerValues()); } - public function provideInvalidIntegerValues() + final public static function provideInvalidStringValues(): array { - return $this->wrapValuesForDataProvider($this->getInvalidIntegerValues()); + return self::wrapValuesForDataProvider(self::getInvalidStringValues()); } - protected function assertDeprecated(callable $execution): void + protected function assertDeprecated(callable $execution): mixed + { + return $this->assertError(E_USER_DEPRECATED | E_DEPRECATED, $execution); + } + + protected function assertError(int $levels, callable $execution): mixed { $errors = []; set_error_handler(function ($errno, $errstr) use (&$errors): void { $errors[] = $errstr; - }, E_USER_DEPRECATED); + }, $levels); try { - call_user_func($execution); + $result = call_user_func($execution); } finally { restore_error_handler(); } $this->assertCount(1, $errors); + + return $result; + } + + protected static function createOptionDataProvider(array $options): array + { + $data = []; + + foreach ($options as $option => $values) { + foreach ($values as $key => $value) { + $dataKey = $option . '_' . $key; + if (is_int($key)) { + $dataKey .= '_' . get_debug_type($value); + } + + $data[$dataKey] = [[$option => $value]]; + } + } + + return $data; } /** @@ -161,87 +208,138 @@ protected function getCollectionName(): string { $class = new ReflectionClass($this); - return sprintf('%s.%s', $class->getShortName(), hash('crc32b', $this->getName())); + return sprintf('%s.%s', $class->getShortName(), hash('xxh3', $this->name())); } /** - * Return the test database name. + * Return a list of invalid array values. */ - protected function getDatabaseName(): string + protected static function getInvalidArrayValues(bool $includeNull = false): array { - return getenv('MONGODB_DATABASE') ?: 'phplib_test'; + return [123, 3.14, 'foo', true, new stdClass(), ...($includeNull ? [null] : [])]; } /** - * Return a list of invalid array values. + * Return a list of invalid boolean values. */ - protected function getInvalidArrayValues(bool $includeNull = false): array + protected static function getInvalidBooleanValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, new stdClass()], $includeNull ? [null] : []); + return [123, 3.14, 'foo', [], new stdClass(), ...($includeNull ? [null] : [])]; } /** - * Return a list of invalid boolean values. + * Return a list of invalid document values. */ - protected function getInvalidBooleanValues(bool $includeNull = false): array + protected static function getInvalidDocumentValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', [], new stdClass()], $includeNull ? [null] : []); + return [123, 3.14, 'foo', true, PackedArray::fromPHP([]), ...($includeNull ? [null] : [])]; + } + + protected static function getInvalidObjectValues(bool $includeNull = false): array + { + return [123, 3.14, 'foo', true, [], new stdClass(), ...($includeNull ? [null] : [])]; + } + + protected static function getInvalidDocumentCodecValues(): array + { + return [123, 3.14, 'foo', true, [], new stdClass(), self::createStub(Codec::class)]; } /** - * Return a list of invalid document values. + * Return a list of invalid hint values. */ - protected function getInvalidDocumentValues(bool $includeNull = false): array + protected static function getInvalidHintValues(): array { - return array_merge([123, 3.14, 'foo', true], $includeNull ? [null] : []); + return [123, 3.14, true, PackedArray::fromPHP([])]; } /** * Return a list of invalid integer values. */ - protected function getInvalidIntegerValues(bool $includeNull = false): array + protected static function getInvalidIntegerValues(bool $includeNull = false): array { - return array_merge([3.14, 'foo', true, [], new stdClass()], $includeNull ? [null] : []); + return [3.14, 'foo', true, [], new stdClass(), ...($includeNull ? [null] : [])]; } /** * Return a list of invalid ReadPreference values. */ - protected function getInvalidReadConcernValues(bool $includeNull = false): array + protected static function getInvalidReadConcernValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadPreference(ReadPreference::RP_PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); + return [ + 123, + 3.14, + 'foo', + true, + [], + new stdClass(), + new ReadPreference(ReadPreference::PRIMARY), + new WriteConcern(1), + ...($includeNull ? ['null' => null] : []), + ]; } /** * Return a list of invalid ReadPreference values. */ - protected function getInvalidReadPreferenceValues(bool $includeNull = false): array + protected static function getInvalidReadPreferenceValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new WriteConcern(1)], $includeNull ? [null] : []); + return [ + 123, + 3.14, + 'foo', + true, + [], + new stdClass(), + new ReadConcern(), + new WriteConcern(1), + ...($includeNull ? ['null' => null] : []), + ]; } /** * Return a list of invalid Session values. */ - protected function getInvalidSessionValues(bool $includeNull = false): array + protected static function getInvalidSessionValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::RP_PRIMARY), new WriteConcern(1)], $includeNull ? [null] : []); + return [ + 123, + 3.14, + 'foo', + true, + [], + new stdClass(), + new ReadConcern(), + new ReadPreference(ReadPreference::PRIMARY), + new WriteConcern(1), + ...($includeNull ? ['null' => null] : []), + ]; } /** * Return a list of invalid string values. */ - protected function getInvalidStringValues(bool $includeNull = false): array + protected static function getInvalidStringValues(bool $includeNull = false): array { - return array_merge([123, 3.14, true, [], new stdClass()], $includeNull ? [null] : []); + return [123, 3.14, true, [], new stdClass(), ...($includeNull ? [null] : [])]; } /** * Return a list of invalid WriteConcern values. */ - protected function getInvalidWriteConcernValues(bool $includeNull = false): array + protected static function getInvalidWriteConcernValues(bool $includeNull = false): array { - return array_merge([123, 3.14, 'foo', true, [], new stdClass(), new ReadConcern(), new ReadPreference(ReadPreference::RP_PRIMARY)], $includeNull ? [null] : []); + return [ + 123, + 3.14, + 'foo', + true, + [], + new stdClass(), + new ReadConcern(), + new ReadPreference(ReadPreference::PRIMARY), + ...($includeNull ? ['null' => null] : []), + ]; } /** @@ -257,11 +355,9 @@ protected function getNamespace(): string * * @param array $values List of values */ - protected function wrapValuesForDataProvider(array $values): array + final protected static function wrapValuesForDataProvider(array $values): array { - return array_map(function ($value) { - return [$value]; - }, $values); + return array_map(fn ($value) => [$value], $values); } /** @@ -271,11 +367,9 @@ protected function wrapValuesForDataProvider(array $values): array * its type and keys. Document fields will be sorted alphabetically. Each * value within the array or document will then be normalized recursively. * - * @param array|object $bson - * @return BSONDocument|BSONArray * @throws InvalidArgumentException if $bson is not an array or object */ - private function normalizeBSON($bson) + private function normalizeBSON(array|object $bson): BSONDocument|BSONArray { if (! is_array($bson) && ! is_object($bson)) { throw new InvalidArgumentException('$bson is not an array or object'); diff --git a/tests/UnifiedSpecTests/CollectionData.php b/tests/UnifiedSpecTests/CollectionData.php index 46d7e2ec2..b7ca2f353 100644 --- a/tests/UnifiedSpecTests/CollectionData.php +++ b/tests/UnifiedSpecTests/CollectionData.php @@ -6,6 +6,7 @@ use MongoDB\Client; use MongoDB\Driver\ReadConcern; use MongoDB\Driver\ReadPreference; +use MongoDB\Driver\Session; use MongoDB\Driver\WriteConcern; use MongoDB\Tests\UnifiedSpecTests\Constraint\Matches; use MultipleIterator; @@ -13,21 +14,22 @@ use function PHPUnit\Framework\assertContainsOnly; use function PHPUnit\Framework\assertIsArray; +use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotNull; +use function PHPUnit\Framework\assertObjectNotHasProperty; use function PHPUnit\Framework\assertThat; use function sprintf; class CollectionData { - /** @var string */ - private $collectionName; + private string $collectionName; - /** @var string */ - private $databaseName; + private string $databaseName; - /** @var array */ - private $documents; + private array $documents; + + private array $createOptions = []; public function __construct(stdClass $o) { @@ -40,24 +42,33 @@ public function __construct(stdClass $o) assertIsArray($o->documents); assertContainsOnly('object', $o->documents); $this->documents = $o->documents; + + if (isset($o->createOptions)) { + assertIsObject($o->createOptions); + /* The writeConcern option is prohibited here, as prepareInitialData() applies w:majority. Since a session + * option would be ignored by prepareInitialData() we can assert that it is also omitted. */ + assertObjectNotHasProperty('writeConcern', $o->createOptions); + assertObjectNotHasProperty('session', $o->createOptions); + $this->createOptions = (array) $o->createOptions; + } } - public function prepareInitialData(Client $client): void + public function prepareInitialData(Client $client, ?Session $session = null): void { $database = $client->selectDatabase( $this->databaseName, - ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)] + ['writeConcern' => new WriteConcern(WriteConcern::MAJORITY)], ); - $database->dropCollection($this->collectionName); + $database->dropCollection($this->collectionName, ['session' => $session]); - if (empty($this->documents)) { - $database->createCollection($this->collectionName); - - return; + if (empty($this->documents) || ! empty($this->createOptions)) { + $database->createCollection($this->collectionName, ['session' => $session] + $this->createOptions); } - $database->selectCollection($this->collectionName)->insertMany($this->documents); + if (! empty($this->documents)) { + $database->selectCollection($this->collectionName)->insertMany($this->documents, ['session' => $session]); + } } public function assertOutcome(Client $client): void @@ -68,7 +79,7 @@ public function assertOutcome(Client $client): void [ 'readConcern' => new ReadConcern(ReadConcern::LOCAL), 'readPreference' => new ReadPreference(ReadPreference::PRIMARY), - ] + ], ); $cursor = $collection->find([], ['sort' => ['_id' => 1]]); diff --git a/tests/UnifiedSpecTests/Constraint/IsBsonType.php b/tests/UnifiedSpecTests/Constraint/IsBsonType.php index 16cbdb5bc..e81ccb2a3 100644 --- a/tests/UnifiedSpecTests/Constraint/IsBsonType.php +++ b/tests/UnifiedSpecTests/Constraint/IsBsonType.php @@ -23,7 +23,6 @@ use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\Constraint\LogicalOr; use RuntimeException; -use Symfony\Bridge\PhpUnit\ConstraintTrait; use function array_keys; use function array_map; @@ -40,10 +39,7 @@ final class IsBsonType extends Constraint { - use ConstraintTrait; - - /** @var array */ - private static $types = [ + private static array $types = [ 'double', 'string', 'object', @@ -68,16 +64,11 @@ final class IsBsonType extends Constraint 'number', ]; - /** @var string */ - private $type; - - public function __construct(string $type) + public function __construct(private string $type) { if (! in_array($type, self::$types)) { throw new RuntimeException(sprintf('Type specified for %s <%s> is not a valid type', self::class, $type)); } - - $this->type = $type; } public static function any(): LogicalOr @@ -91,87 +82,40 @@ public static function anyOf(string ...$types): Constraint return new self(...$types); } - return LogicalOr::fromConstraints(...array_map(function ($type) { - return new self($type); - }, $types)); + return LogicalOr::fromConstraints(...array_map(fn ($type) => new self($type), $types)); } - private function doMatches($other): bool + protected function matches($other): bool { - switch ($this->type) { - case 'double': - return is_float($other); - - case 'string': - return is_string($other); - - case 'object': - return self::isObject($other); - - case 'array': - return self::isArray($other); - - case 'binData': - return $other instanceof BinaryInterface; - - case 'undefined': - return $other instanceof Undefined; - - case 'objectId': - return $other instanceof ObjectIdInterface; - - case 'bool': - return is_bool($other); - - case 'date': - return $other instanceof UTCDateTimeInterface; - - case 'null': - return $other === null; - - case 'regex': - return $other instanceof RegexInterface; - - case 'dbPointer': - return $other instanceof DBPointer; - - case 'javascript': - return $other instanceof JavascriptInterface && $other->getScope() === null; - - case 'symbol': - return $other instanceof Symbol; - - case 'javascriptWithScope': - return $other instanceof JavascriptInterface && $other->getScope() !== null; - - case 'int': - return is_int($other); - - case 'timestamp': - return $other instanceof TimestampInterface; - - case 'long': - return is_int($other) || $other instanceof Int64; - - case 'decimal': - return $other instanceof Decimal128Interface; - - case 'minKey': - return $other instanceof MinKeyInterface; - - case 'maxKey': - return $other instanceof MaxKeyInterface; - - case 'number': - return is_int($other) || $other instanceof Int64 || is_float($other) || $other instanceof Decimal128Interface; - - default: - // This should already have been caught in the constructor - throw new LogicException('Unsupported type: ' . $this->type); - } + return match ($this->type) { + 'double' => is_float($other), + 'string' => is_string($other), + 'object' => self::isObject($other), + 'array' => self::isArray($other), + 'binData' => $other instanceof BinaryInterface, + 'undefined' => $other instanceof Undefined, + 'objectId' => $other instanceof ObjectIdInterface, + 'bool' => is_bool($other), + 'date' => $other instanceof UTCDateTimeInterface, + 'null' => $other === null, + 'regex' => $other instanceof RegexInterface, + 'dbPointer' => $other instanceof DBPointer, + 'javascript' => $other instanceof JavascriptInterface && $other->getScope() === null, + 'symbol' => $other instanceof Symbol, + 'javascriptWithScope' => $other instanceof JavascriptInterface && $other->getScope() !== null, + 'int' => is_int($other), + 'timestamp' => $other instanceof TimestampInterface, + 'long' => is_int($other) || $other instanceof Int64, + 'decimal' => $other instanceof Decimal128Interface, + 'minKey' => $other instanceof MinKeyInterface, + 'maxKey' => $other instanceof MaxKeyInterface, + 'number' => is_int($other) || $other instanceof Int64 || is_float($other) || $other instanceof Decimal128Interface, + // This should already have been caught in the constructor + default => throw new LogicException('Unsupported type: ' . $this->type), + }; } - private function doToString(): string + public function toString(): string { return sprintf('is of BSON type "%s"', $this->type); } diff --git a/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php b/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php index ae0f88c2e..dd3c6e3e3 100644 --- a/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php +++ b/tests/UnifiedSpecTests/Constraint/IsBsonTypeTest.php @@ -4,6 +4,8 @@ use MongoDB\BSON\Binary; use MongoDB\BSON\Decimal128; +use MongoDB\BSON\Document; +use MongoDB\BSON\Int64; use MongoDB\BSON\Javascript; use MongoDB\BSON\MaxKey; use MongoDB\BSON\MinKey; @@ -15,34 +17,30 @@ use MongoDB\Model\BSONArray; use MongoDB\Model\BSONDocument; use MongoDB\Tests\TestCase; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Constraint\Constraint; use PHPUnit\Framework\ExpectationFailedException; use stdClass; use function fopen; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; -use function unserialize; use const PHP_INT_SIZE; class IsBsonTypeTest extends TestCase { - /** - * @dataProvider provideTypes - */ + #[DataProvider('provideTypes')] public function testConstraint($type, $value): void { $this->assertResult(true, new IsBsonType($type), $value, $this->dataName() . ' is ' . $type); } - public function provideTypes() + public static function provideTypes() { - $undefined = toPHP(fromJSON('{ "x": {"$undefined": true} }'))->x; - $symbol = toPHP(fromJSON('{ "x": {"$symbol": "test"} }'))->x; - $dbPointer = toPHP(fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }'))->x; - $int64 = unserialize('C:18:"MongoDB\BSON\Int64":28:{a:1:{s:7:"integer";s:1:"1";}}'); - $long = PHP_INT_SIZE == 4 ? unserialize('C:18:"MongoDB\BSON\Int64":38:{a:1:{s:7:"integer";s:10:"4294967296";}}') : 4294967296; + $undefined = Document::fromJSON('{ "x": {"$undefined": true} }')->toPHP()->x; + $symbol = Document::fromJSON('{ "x": {"$symbol": "test"} }')->toPHP()->x; + $dbPointer = Document::fromJSON('{ "x": {"$dbPointer": {"$ref": "db.coll", "$id" : { "$oid" : "5a2e78accd485d55b405ac12" } }} }')->toPHP()->x; + $int64 = new Int64(1); + $long = PHP_INT_SIZE == 4 ? new Int64('4294967296') : 4_294_967_296; return [ 'double' => ['double', 1.4], @@ -50,10 +48,10 @@ public function provideTypes() // Note: additional tests in testTypeObject 'object(stdClass)' => ['object', new stdClass()], 'object(BSONDocument)' => ['object', new BSONDocument()], - // Note: additional tests tests in testTypeArray + // Note: additional tests in testTypeArray 'array(indexed array)' => ['array', ['foo']], 'array(BSONArray)' => ['array', new BSONArray()], - 'binData' => ['binData', new Binary('', 0)], + 'binData' => ['binData', new Binary('')], 'undefined' => ['undefined', $undefined], 'objectId' => ['objectId', new ObjectId()], 'bool' => ['bool', true], @@ -79,9 +77,7 @@ public function provideTypes() ]; } - /** - * @dataProvider provideTypes - */ + #[DataProvider('provideTypes')] public function testAny($type, $value): void { $this->assertResult(true, IsBsonType::any(), $value, $this->dataName() . ' is a BSON type'); diff --git a/tests/UnifiedSpecTests/Constraint/Matches.php b/tests/UnifiedSpecTests/Constraint/Matches.php index eea67a153..a6bda1420 100644 --- a/tests/UnifiedSpecTests/Constraint/Matches.php +++ b/tests/UnifiedSpecTests/Constraint/Matches.php @@ -3,6 +3,9 @@ namespace MongoDB\Tests\UnifiedSpecTests\Constraint; use LogicException; +use MongoDB\BSON\Document; +use MongoDB\BSON\Int64; +use MongoDB\BSON\PackedArray; use MongoDB\BSON\Serializable; use MongoDB\BSON\Type; use MongoDB\Model\BSONArray; @@ -13,7 +16,7 @@ use RuntimeException; use SebastianBergmann\Comparator\ComparisonFailure; use SebastianBergmann\Comparator\Factory; -use Symfony\Bridge\PhpUnit\ConstraintTrait; +use SebastianBergmann\Exporter\Exporter; use function array_keys; use function count; @@ -25,11 +28,16 @@ use function is_int; use function is_object; use function ltrim; +use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsBool; use function PHPUnit\Framework\assertIsString; +use function PHPUnit\Framework\assertJson; +use function PHPUnit\Framework\assertLessThanOrEqual; use function PHPUnit\Framework\assertMatchesRegularExpression; use function PHPUnit\Framework\assertNotNull; +use function PHPUnit\Framework\assertStringStartsWith; use function PHPUnit\Framework\assertThat; +use function PHPUnit\Framework\assertTrue; use function PHPUnit\Framework\containsOnly; use function PHPUnit\Framework\isInstanceOf; use function PHPUnit\Framework\isType; @@ -37,10 +45,9 @@ use function PHPUnit\Framework\logicalOr; use function range; use function sprintf; -use function strpos; +use function str_starts_with; use function strrchr; - -use const PHP_INT_SIZE; +use function trim; /** * Constraint that checks if one value matches another. @@ -51,36 +58,25 @@ */ class Matches extends Constraint { - use ConstraintTrait; - - /** @var EntityMap */ - private $entityMap; + private mixed $value; - /** @var mixed */ - private $value; + private bool $allowExtraRootKeys; - /** @var bool */ - private $allowExtraRootKeys; + private bool $allowOperators; - /** @var bool */ - private $allowOperators; + private ?ComparisonFailure $lastFailure = null; - /** @var ComparisonFailure|null */ - private $lastFailure; + private Factory $comparatorFactory; - /** @var Factory */ - private $comparatorFactory; - - public function __construct($value, ?EntityMap $entityMap = null, $allowExtraRootKeys = true, $allowOperators = true) + public function __construct($value, private ?EntityMap $entityMap = null, $allowExtraRootKeys = true, $allowOperators = true) { $this->value = self::prepare($value); - $this->entityMap = $entityMap; $this->allowExtraRootKeys = $allowExtraRootKeys; $this->allowOperators = $allowOperators; $this->comparatorFactory = Factory::getInstance(); } - private function doEvaluate($other, $description = '', $returnResult = false) + public function evaluate($other, $description = '', $returnResult = false): ?bool { $other = self::prepare($other); $success = false; @@ -96,15 +92,15 @@ private function doEvaluate($other, $description = '', $returnResult = false) } catch (RuntimeException $e) { /* This will generally catch internal errors from failAt(), which * include a key path to pinpoint the failure. */ + $exporter = new Exporter(); $this->lastFailure = new ComparisonFailure( $this->value, $other, /* TODO: Improve the exporter to canonicalize documents by * sorting keys and remove spl_object_hash from output. */ - $this->exporter()->export($this->value), - $this->exporter()->export($other), - false, - $e->getMessage() + $exporter->export($this->value), + $exporter->export($other), + $e->getMessage(), ); } @@ -115,6 +111,8 @@ private function doEvaluate($other, $description = '', $returnResult = false) if (! $success) { $this->fail($other, $description, $this->lastFailure); } + + return null; } private function assertEquals($expected, $actual, string $keyPath): void @@ -170,7 +168,7 @@ private function assertMatchesArray(BSONArray $expected, $actual, string $keyPat $this->assertMatches( $expectedValue, $actual[$key], - (empty($keyPath) ? $key : $keyPath . '.' . $key) + (empty($keyPath) ? $key : $keyPath . '.' . $key), ); } } @@ -224,7 +222,7 @@ private function assertMatchesDocument(BSONDocument $expected, $actual, string $ $this->assertMatches( $expectedValue, $actual[$key], - (empty($keyPath) ? $key : $keyPath . '.' . $key) + (empty($keyPath) ? $key : $keyPath . '.' . $key), ); } @@ -262,13 +260,42 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $ assertThat( $operator['$$type'], logicalOr(isType('string'), logicalAnd(isInstanceOf(BSONArray::class), containsOnly('string'))), - '$$type requires string or string[]' + '$$type requires string or string[]', ); $constraint = IsBsonType::anyOf(...(array) $operator['$$type']); if (! $constraint->evaluate($actual, '', true)) { - self::failAt(sprintf('%s is not an expected BSON type: %s', $this->exporter()->shortenedExport($actual), implode(', ', $types)), $keyPath); + self::failAt(sprintf('%s is not an expected BSON type: %s', (new Exporter())->shortenedExport($actual), implode(', ', (array) $operator['$$type'])), $keyPath); + } + + return; + } + + if ($name === '$$matchAsDocument') { + assertInstanceOf(BSONDocument::class, $operator['$$matchAsDocument'], '$$matchAsDocument requires a BSON document'); + assertIsString($actual, '$$matchAsDocument requires actual value to be a JSON string'); + assertJson($actual, '$$matchAsDocument requires actual value to be a JSON string'); + + /* Note: assertJson() accepts array and scalar values, but the spec + * assumes that the JSON string will yield a document. */ + assertStringStartsWith('{', trim($actual), '$$matchAsDocument requires actual value to be a JSON string denoting an object'); + + $actualDocument = Document::fromJSON($actual)->toPHP(); + $constraint = new Matches($operator['$$matchAsDocument'], $this->entityMap, allowExtraRootKeys: false); + + if (! $constraint->evaluate($actualDocument, '', true)) { + self::failAt(sprintf('%s did not match: %s', (new Exporter())->shortenedExport($actual), $constraint->additionalFailureDescription(null)), $keyPath); + } + + return; + } + + if ($name === '$$matchAsRoot') { + $constraint = new Matches($operator['$$matchAsRoot'], $this->entityMap, allowExtraRootKeys: true); + + if (! $constraint->evaluate($actual, '', true)) { + self::failAt(sprintf('$actual did not match as root-level document: %s', $constraint->additionalFailureDescription(null)), $keyPath); } return; @@ -284,7 +311,7 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $ $this->assertMatches( self::prepare($this->entityMap[$operator['$$matchesEntity']]), $actual, - $keyPath + $keyPath, ); return; @@ -296,7 +323,7 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $ assertIsString($actual); if ($actual !== hex2bin($operator['$$matchesHexBytes'])) { - self::failAt(sprintf('%s does not match expected hex bytes: %s', $this->exporter()->shortenedExport($actual), $operator['$$matchesHexBytes']), $keyPath); + self::failAt(sprintf('%s does not match expected hex bytes: %s', (new Exporter())->shortenedExport($actual), $operator['$$matchesHexBytes']), $keyPath); } return; @@ -313,7 +340,7 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $ $this->assertMatches( self::prepare($operator['$$unsetOrMatches']), $actual, - $keyPath + $keyPath, ); return; @@ -329,11 +356,18 @@ private function assertMatchesOperator(BSONDocument $operator, $actual, string $ return; } + if ($name === '$$lte') { + assertTrue(self::isNumeric($operator['$$lte']), '$$lte requires number'); + assertTrue(self::isNumeric($actual), '$actual operand for $$lte should be a number'); + assertLessThanOrEqual($operator['$$lte'], $actual); + + return; + } + throw new LogicException('unsupported operator: ' . $name); } - /** @see ConstraintTrait */ - private function doAdditionalFailureDescription($other) + protected function additionalFailureDescription($other): string { if ($this->lastFailure === null) { return ''; @@ -342,32 +376,30 @@ private function doAdditionalFailureDescription($other) return $this->lastFailure->getMessage(); } - /** @see ConstraintTrait */ - private function doFailureDescription($other) + protected function failureDescription($other): string { return 'expected value matches actual value'; } - /** @see ConstraintTrait */ - private function doMatches($other) + protected function matches($other): bool { $other = self::prepare($other); try { $this->assertMatches($this->value, $other); - } catch (RuntimeException $e) { + } catch (RuntimeException) { return false; } return true; } - /** @see ConstraintTrait */ - private function doToString() + public function toString(): string { - return 'matches ' . $this->exporter()->export($this->value); + return 'matches ' . (new Exporter())->export($this->value); } + /** @psalm-return never-return */ private static function failAt(string $message, string $keyPath): void { $prefix = empty($keyPath) ? '' : sprintf('Field path "%s": ', $keyPath); @@ -379,7 +411,7 @@ private static function getOperatorName(BSONDocument $document): string { // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps foreach ($document as $key => $_) { - if (strpos((string) $key, '$$') === 0) { + if (str_starts_with((string) $key, '$$')) { return $key; } } @@ -400,7 +432,7 @@ private static function isOperator(BSONDocument $document): bool // phpcs:ignore Squiz.NamingConventions.ValidVariableName.NotCamelCaps foreach ($document as $key => $_) { - return strpos((string) $key, '$$') === 0; + return str_starts_with((string) $key, '$$'); } throw new LogicException('should not reach this point'); @@ -414,34 +446,26 @@ private static function isOperator(BSONDocument $document): bool * converted to a BSONDocument; otherwise, it will be converted to a * BSONArray or BSONDocument based on its keys. Each value within an array * or document will then be prepared recursively. - * - * @param mixed $bson - * @return mixed */ - private static function prepare($bson) + private static function prepare(mixed $bson): mixed { if (! is_array($bson) && ! is_object($bson)) { return $bson; } - /* Convert Int64 objects to integers on 64-bit platforms for - * compatibility reasons. */ - if ($bson instanceof Int64 && PHP_INT_SIZE != 4) { - return (int) ((string) $bson); - } - - /* TODO: Convert Int64 objects to integers on 32-bit platforms if they - * can be expressed as such. This is necessary to handle flexible - * numeric comparisons if the server returns 32-bit value as a 64-bit - * integer (e.g. cursor ID). */ - // Serializable can produce an array or object, so recurse on its output if ($bson instanceof Serializable) { return self::prepare($bson->bsonSerialize()); } - /* Serializable has already been handled, so any remaining instances of - * Type will not serialize as BSON arrays or objects */ + // Recurse on the PHP representation of Document and PackedArray types + if ($bson instanceof Document || $bson instanceof PackedArray) { + return self::prepare($bson->toPHP()); + } + + /* Serializable, Document, and PackedArray have already been handled. + * Any remaining Type instances will not serialize as BSON arrays or + * objects. */ if ($bson instanceof Type) { return $bson; } diff --git a/tests/UnifiedSpecTests/Constraint/MatchesTest.php b/tests/UnifiedSpecTests/Constraint/MatchesTest.php index 72433b3e0..41b57fad1 100644 --- a/tests/UnifiedSpecTests/Constraint/MatchesTest.php +++ b/tests/UnifiedSpecTests/Constraint/MatchesTest.php @@ -5,6 +5,7 @@ use MongoDB\BSON\Binary; use MongoDB\Tests\FunctionalTestCase; use MongoDB\Tests\UnifiedSpecTests\EntityMap; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\ExpectationFailedException; use stdClass; @@ -29,6 +30,12 @@ public function testFlexibleNumericComparison(): void $this->assertResult(true, $c, ['x' => 1.0, 'y' => 1.0], 'Float instead of expected int matches'); $this->assertResult(true, $c, ['x' => 1, 'y' => 1], 'Int instead of expected float matches'); $this->assertResult(false, $c, ['x' => 'foo', 'y' => 1.0], 'Different type does not match'); + + /* Matches uses PHPUnit's comparators, which follow PHP behavior. This + * is more liberal than the comparison logic called for by the unified + * test format. This test can be removed when PHPLIB-1577 is addressed. + */ + $this->assertResult(true, $c, ['x' => '1.0', 'y' => '1'], 'Numeric strings may match ints and floats'); } public function testDoNotAllowExtraRootKeys(): void @@ -170,9 +177,38 @@ public function testOperatorSessionLsid(): void $this->assertResult(false, $c, ['x' => 1], 'session LSID does not match (embedded)'); } - /** - * @dataProvider errorMessageProvider - */ + public function testOperatorMatchAsDocument(): void + { + $c = new Matches(['json' => ['$$matchAsDocument' => ['x' => 1]]]); + $this->assertResult(true, $c, ['json' => '{"x": 1}'], 'JSON document matches'); + $this->assertResult(false, $c, ['json' => '{"x": 2}'], 'JSON document does not match'); + $this->assertResult(false, $c, ['json' => '{"x": 1, "y": 2}'], 'JSON document cannot contain extra fields'); + + $c = new Matches(['json' => ['$$matchAsDocument' => ['x' => 1.0]]]); + $this->assertResult(true, $c, ['json' => '{"x": 1}'], 'JSON document matches (flexible numeric comparison)'); + + $c = new Matches(['json' => ['$$matchAsDocument' => ['x' => ['$$exists' => true]]]]); + $this->assertResult(true, $c, ['json' => '{"x": 1}'], 'JSON document matches (special operators)'); + $this->assertResult(false, $c, ['json' => '{"y": 1}'], 'JSON document does not match (special operators)'); + + $c = new Matches(['json' => ['$$matchAsDocument' => ['x' => ['$$type' => 'objectId']]]]); + $this->assertResult(true, $c, ['json' => '{"x": {"$oid": "57e193d7a9cc81b4027498b5"}}'], 'JSON document matches (extended JSON)'); + $this->assertResult(false, $c, ['json' => '{"x": {"$numberDecimal": "1234.5"}}'], 'JSON document does not match (extended JSON)'); + } + + public function testOperatorMatchAsRoot(): void + { + $c = new Matches(['x' => ['$$matchAsRoot' => ['y' => 2]]]); + $this->assertResult(true, $c, ['x' => ['y' => 2, 'z' => 3]], 'Nested document matches (allow extra fields)'); + $this->assertResult(true, $c, ['x' => ['y' => 2.0, 'z' => 3.0]], 'Nested document matches (flexible numeric comparison)'); + $this->assertResult(false, $c, ['x' => ['y' => 3, 'z' => 3]], 'Nested document does not match'); + + $c = new Matches(['x' => ['$$matchAsRoot' => ['y' => ['$$exists' => true]]]]); + $this->assertResult(true, $c, ['x' => ['y' => 2, 'z' => 3]], 'Nested document matches (special operators)'); + $this->assertResult(false, $c, ['x' => ['z' => 3]], 'Nested document matches (special operators)'); + } + + #[DataProvider('errorMessageProvider')] public function testErrorMessages($expectedMessageRegex, Matches $constraint, $actualValue): void { try { @@ -184,7 +220,7 @@ public function testErrorMessages($expectedMessageRegex, Matches $constraint, $a } } - public function errorMessageProvider() + public static function errorMessageProvider() { return [ 'assertEquals: type check (root-level)' => [ @@ -255,9 +291,7 @@ public function errorMessageProvider() ]; } - /** - * @dataProvider operatorErrorMessageProvider - */ + #[DataProvider('operatorErrorMessageProvider')] public function testOperatorSyntaxValidation($expectedMessage, Matches $constraint): void { $this->expectException(ExpectationFailedException::class); @@ -266,7 +300,7 @@ public function testOperatorSyntaxValidation($expectedMessage, Matches $constrai $constraint->evaluate(['x' => 1], '', true); } - public function operatorErrorMessageProvider() + public static function operatorErrorMessageProvider() { return [ '$$exists type' => [ @@ -305,6 +339,10 @@ public function operatorErrorMessageProvider() '$$sessionLsid requires string', new Matches(['x' => ['$$sessionLsid' => 1]], new EntityMap()), ], + '$$matchAsDocument type' => [ + '$$matchAsDocument requires a BSON document', + new Matches(['x' => ['$$matchAsDocument' => 'foo']]), + ], ]; } diff --git a/tests/UnifiedSpecTests/Context.php b/tests/UnifiedSpecTests/Context.php index 2300965f3..0faa0611f 100644 --- a/tests/UnifiedSpecTests/Context.php +++ b/tests/UnifiedSpecTests/Context.php @@ -38,38 +38,29 @@ */ final class Context { - /** @var string */ - private $activeClient; + use ManagesFailPointsTrait; - /** @var EntityMap */ - private $entityMap; + private ?string $activeClient = null; - /** @var EventCollector[] */ - private $eventCollectors = []; + private EntityMap $entityMap; - /** @var EventObserver[] */ - private $eventObserversByClient = []; + /** @var list */ + private array $eventCollectors = []; - /** @var Client */ - private $internalClient; + /** @var array */ + private array $eventObserversByClient = []; - /** @var boolean */ - private $inLoop = false; + private bool $inLoop = false; - /** @var string */ - private $uri; + private string $singleMongosUri; - /** @var string */ - private $singleMongosUri; + private string $multiMongosUri; - /** @var string */ - private $multiMongosUri; + private ?object $advanceClusterTime = null; - public function __construct(Client $internalClient, string $uri) + public function __construct(private Client $internalClient, private string $uri) { $this->entityMap = new EntityMap(); - $this->internalClient = $internalClient; - $this->uri = $uri; /* TODO: Consider leaving these unset, although that might require * redundant topology/serverless checks in Context::createClient(). */ @@ -106,34 +97,15 @@ public function createEntities(array $entities): void $id = $def->id ?? null; assertIsString($id); - switch ($type) { - case 'client': - $this->createClient($id, $def); - break; - - case 'clientEncryption': - $this->createClientEncryption($id, $def); - break; - - case 'database': - $this->createDatabase($id, $def); - break; - - case 'collection': - $this->createCollection($id, $def); - break; - - case 'session': - $this->createSession($id, $def); - break; - - case 'bucket': - $this->createBucket($id, $def); - break; - - default: - throw new LogicException('Unsupported entity type: ' . $type); - } + match ($type) { + 'client' => $this->createClient($id, $def), + 'clientEncryption' => $this->createClientEncryption($id, $def), + 'database' => $this->createDatabase($id, $def), + 'collection' => $this->createCollection($id, $def), + 'session' => $this->createSession($id, $def), + 'bucket' => $this->createBucket($id, $def), + default => throw new LogicException('Unsupported entity type: ' . $type), + }; } } @@ -157,6 +129,17 @@ public function setActiveClient(?string $clientId = null): void $this->activeClient = $clientId; } + /** + * Set a cluster time to use for advancing newly created session entities. + * + * This is used to ensure causal consistency with initialData collections + * in sharded environments (see: DRIVERS-2816). + */ + public function setAdvanceClusterTime(?object $clusterTime): void + { + $this->advanceClusterTime = $clusterTime; + } + public function isInLoop(): bool { return $this->inLoop; @@ -226,8 +209,7 @@ public function stopEventCollectors(): void } } - /** @param string|array $readPreferenceTags */ - private function convertReadPreferenceTags($readPreferenceTags): array + private function convertReadPreferenceTags(string|array $readPreferenceTags): array { return array_map( static function (string $readPreferenceTagSet): array { @@ -239,10 +221,10 @@ static function (string $tag): array { return [$key => $value]; }, - $tags + $tags, ); }, - (array) $readPreferenceTags + (array) $readPreferenceTags, ); } @@ -316,7 +298,7 @@ private function createClient(string $id, stdClass $o): void $driverOptions['serverApi'] = new ServerApi( $serverApi->version, $serverApi->strict ?? null, - $serverApi->deprecationErrors ?? null + $serverApi->deprecationErrors ?? null, ); } @@ -470,7 +452,13 @@ private function createSession(string $id, stdClass $o): void $options = self::prepareSessionOptions((array) $o->sessionOptions); } - $this->entityMap->set($id, $client->startSession($options), $clientId); + $session = $client->startSession($options); + + if ($this->advanceClusterTime !== null) { + $session->advanceClusterTime($this->advanceClusterTime); + } + + $this->entityMap->set($id, $session, $clientId); } private function createBucket(string $id, stdClass $o): void @@ -504,6 +492,10 @@ private static function getEnv(string $name): string private static function prepareCollectionOrDatabaseOptions(array $options): array { + if (array_key_exists('timeoutMS', $options)) { + Assert::markTestIncomplete('CSOT is not yet implemented (PHPC-1760)'); + } + Util::assertHasOnlyKeys($options, ['readConcern', 'readPreference', 'writeConcern']); return Util::prepareCommonOptions($options); @@ -521,10 +513,6 @@ private static function prepareBucketOptions(array $options): array assertIsInt($options['chunkSizeBytes']); } - if (array_key_exists('disableMD5', $options)) { - assertIsBool($options['disableMD5']); - } - return Util::prepareCommonOptions($options); } diff --git a/tests/UnifiedSpecTests/EntityMap.php b/tests/UnifiedSpecTests/EntityMap.php index 3e50f574b..32b99ffd0 100644 --- a/tests/UnifiedSpecTests/EntityMap.php +++ b/tests/UnifiedSpecTests/EntityMap.php @@ -29,19 +29,17 @@ class EntityMap implements ArrayAccess { - /** @var array */ - private $map = []; + private array $map = []; /** * Track lsids so they can be accessed after Session::getLogicalSessionId() * has been called. * - * @var stdClass[] + * @var array */ - private $lsidsBySession = []; + private array $lsidsBySession = []; - /** @var Constraint */ - private static $isSupportedType; + private static Constraint $isSupportedType; public function __destruct() { @@ -57,9 +55,7 @@ public function __destruct() } } - /** - * @see https://php.net/arrayaccess.offsetexists - */ + /** @see https://php.net/arrayaccess.offsetexists */ public function offsetExists($id): bool { assertIsString($id); @@ -67,12 +63,9 @@ public function offsetExists($id): bool return array_key_exists($id, $this->map); } - /** - * @see https://php.net/arrayaccess.offsetget - * @return mixed - */ + /** @see https://php.net/arrayaccess.offsetget */ #[ReturnTypeWillChange] - public function offsetGet($id) + public function offsetGet($id): mixed { assertIsString($id); assertArrayHasKey($id, $this->map, sprintf('No entity is defined for "%s"', $id)); @@ -80,17 +73,13 @@ public function offsetGet($id) return $this->map[$id]->value; } - /** - * @see https://php.net/arrayaccess.offsetset - */ + /** @see https://php.net/arrayaccess.offsetset */ public function offsetSet($id, $value): void { Assert::fail('Entities can only be set via set()'); } - /** - * @see https://php.net/arrayaccess.offsetunset - */ + /** @see https://php.net/arrayaccess.offsetunset */ public function offsetUnset($id): void { Assert::fail('Entities cannot be removed from the map'); @@ -108,18 +97,11 @@ public function set(string $id, $value, ?string $parentId = null): void $parent = $parentId === null ? null : $this->map[$parentId]; $this->map[$id] = new class ($id, $value, $parent) { - /** @var string */ - public $id; - /** @var mixed */ - public $value; - /** @var self */ - public $parent; - - public function __construct(string $id, $value, ?self $parent = null) + public mixed $value; + + public function __construct(public string $id, $value, public ?self $parent = null) { - $this->id = $id; $this->value = $value; - $this->parent = $parent; } public function getRoot(): self @@ -180,19 +162,17 @@ public function getRootClientIdOf(string $id) private static function isSupportedType(): Constraint { - if (self::$isSupportedType === null) { - self::$isSupportedType = logicalOr( - isInstanceOf(Client::class), - isInstanceOf(ClientEncryption::class), - isInstanceOf(Database::class), - isInstanceOf(Collection::class), - isInstanceOf(Session::class), - isInstanceOf(Bucket::class), - isInstanceOf(ChangeStream::class), - isInstanceOf(Cursor::class), - IsBsonType::any() - ); - } + self::$isSupportedType ??= logicalOr( + isInstanceOf(Client::class), + isInstanceOf(ClientEncryption::class), + isInstanceOf(Database::class), + isInstanceOf(Collection::class), + isInstanceOf(Session::class), + isInstanceOf(Bucket::class), + isInstanceOf(ChangeStream::class), + isInstanceOf(Cursor::class), + IsBsonType::any(), + ); return self::$isSupportedType; } diff --git a/tests/UnifiedSpecTests/EventCollector.php b/tests/UnifiedSpecTests/EventCollector.php index 98465a1a3..0ecd622dc 100644 --- a/tests/UnifiedSpecTests/EventCollector.php +++ b/tests/UnifiedSpecTests/EventCollector.php @@ -10,7 +10,6 @@ use function array_filter; use function array_flip; -use function get_class; use function microtime; use function MongoDB\Driver\Monitoring\addSubscriber; use function MongoDB\Driver\Monitoring\removeSubscriber; @@ -18,7 +17,6 @@ use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotEmpty; -use function sprintf; /** * EventCollector handles "storeEventsAsEntities" for client entities. @@ -29,8 +27,7 @@ */ final class EventCollector implements CommandSubscriber { - /** @var array */ - private static $supportedEvents = [ + private static array $supportedEvents = [ 'PoolCreatedEvent' => null, 'PoolReadyEvent' => null, 'PoolClearedEvent' => null, @@ -47,19 +44,9 @@ final class EventCollector implements CommandSubscriber 'CommandFailedEvent' => CommandFailedEvent::class, ]; - /** @var string */ - private $clientId; + private array $collectEvents = []; - /** @var Context */ - private $context; - - /** @var array */ - private $collectEvents = []; - - /** @var BSONArray */ - private $eventList; - - public function __construct(BSONArray $eventList, array $collectEvents, string $clientId, Context $context) + public function __construct(private BSONArray $eventList, array $collectEvents, private string $clientId, private Context $context) { assertNotEmpty($collectEvents); @@ -74,31 +61,21 @@ public function __construct(BSONArray $eventList, array $collectEvents, string $ $this->collectEvents[self::$supportedEvents[$event]] = 1; } } - - $this->clientId = $clientId; - $this->context = $context; - $this->eventList = $eventList; } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ public function commandFailed(CommandFailedEvent $event): void { $this->handleCommandMonitoringEvent($event); } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ public function commandStarted(CommandStartedEvent $event): void { $this->handleCommandMonitoringEvent($event); } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ public function commandSucceeded(CommandSucceededEvent $event): void { $this->handleCommandMonitoringEvent($event); @@ -114,8 +91,7 @@ public function stop(): void removeSubscriber($this); } - /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ - private function handleCommandMonitoringEvent($event): void + private function handleCommandMonitoringEvent(CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event): void { assertIsObject($event); @@ -123,7 +99,7 @@ private function handleCommandMonitoringEvent($event): void return; } - if (! isset($this->collectEvents[get_class($event)])) { + if (! isset($this->collectEvents[$event::class])) { return; } @@ -131,18 +107,17 @@ private function handleCommandMonitoringEvent($event): void 'name' => self::getEventName($event), 'observedAt' => microtime(true), 'commandName' => $event->getCommandName(), - 'connectionId' => self::getConnectionId($event), - 'requestId' => $event->getRequestId(), + 'databaseName' => $event->getDatabaseName(), 'operationId' => $event->getOperationId(), + 'requestId' => $event->getRequestId(), + 'server' => $event->getHost() . ':' . $event->getPort(), + 'serverConnectionId' => $event->getServerConnectionId(), + 'serviceId' => $event->getServiceId(), ]; /* Note: CommandStartedEvent.command and CommandSucceededEvent.reply can * be omitted from logged events. */ - if ($event instanceof CommandStartedEvent) { - $log['databaseName'] = $event->getDatabaseName(); - } - if ($event instanceof CommandSucceededEvent) { $log['duration'] = $event->getDurationMicros(); } @@ -155,14 +130,6 @@ private function handleCommandMonitoringEvent($event): void $this->eventList[] = $log; } - /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ - private static function getConnectionId($event): string - { - $server = $event->getServer(); - - return sprintf('%s:%d', $server->getHost(), $server->getPort()); - } - private static function getEventName(object $event): string { static $eventNamesByClass = null; @@ -171,6 +138,6 @@ private static function getEventName(object $event): string $eventNamesByClass = array_flip(array_filter(self::$supportedEvents)); } - return $eventNamesByClass[get_class($event)]; + return $eventNamesByClass[$event::class]; } } diff --git a/tests/UnifiedSpecTests/EventObserver.php b/tests/UnifiedSpecTests/EventObserver.php index 842dfb89a..aa057fbfd 100644 --- a/tests/UnifiedSpecTests/EventObserver.php +++ b/tests/UnifiedSpecTests/EventObserver.php @@ -15,7 +15,6 @@ use function array_reverse; use function count; use function current; -use function get_class; use function is_object; use function key; use function MongoDB\Driver\Monitoring\addSubscriber; @@ -28,7 +27,7 @@ use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotEmpty; -use function PHPUnit\Framework\assertObjectHasAttribute; +use function PHPUnit\Framework\assertObjectHasProperty; use function PHPUnit\Framework\assertSame; use function PHPUnit\Framework\assertThat; use function sprintf; @@ -43,10 +42,9 @@ final class EventObserver implements CommandSubscriber * These commands are always considered sensitive (i.e. command and reply * documents should be redacted). * - * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security - * @var array + * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.md#security */ - private static $sensitiveCommands = [ + private static array $sensitiveCommands = [ 'authenticate' => 1, 'saslStart' => 1, 'saslContinue' => 1, @@ -62,17 +60,15 @@ final class EventObserver implements CommandSubscriber * These commands are only considered sensitive when the command or reply * document includes a speculativeAuthenticate field. * - * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.rst#security - * @var array + * @see https://github.com/mongodb/specifications/blob/master/source/command-monitoring/command-monitoring.md#security */ - private static $sensitiveCommandsWithSpeculativeAuthenticate = [ + private static array $sensitiveCommandsWithSpeculativeAuthenticate = [ 'ismaster' => 1, 'isMaster' => 1, 'hello' => 1, ]; - /** @var array */ - private static $supportedEvents = [ + private static array $supportedEvents = [ 'commandStartedEvent' => CommandStartedEvent::class, 'commandSucceededEvent' => CommandSucceededEvent::class, 'commandFailedEvent' => CommandFailedEvent::class, @@ -81,10 +77,8 @@ final class EventObserver implements CommandSubscriber /** * These events are defined in the specification but unsupported by PHPLIB * (e.g. CMAP events). - * - * @var array */ - private static $unsupportedEvents = [ + private static array $unsupportedEvents = [ 'poolCreatedEvent' => 1, 'poolReadyEvent' => 1, 'poolClearedEvent' => 1, @@ -98,30 +92,17 @@ final class EventObserver implements CommandSubscriber 'connectionCheckedInEvent' => 1, ]; - /** @var array */ - private $actualEvents = []; - - /** @var string */ - private $clientId; - - /** @var Context */ - private $context; + private array $actualEvents = []; /** * The configureFailPoint command (used by failPoint and targetedFailPoint * operations) is always ignored. - * - * @var array */ - private $ignoreCommands = ['configureFailPoint' => 1]; + private array $ignoreCommands = ['configureFailPoint' => 1]; - /** @var array */ - private $observeEvents = []; + private array $observeEvents = []; - /** @var bool */ - private $observeSensitiveCommands; - - public function __construct(array $observeEvents, array $ignoreCommands, bool $observeSensitiveCommands, string $clientId, Context $context) + public function __construct(array $observeEvents, array $ignoreCommands, private bool $observeSensitiveCommands, private string $clientId, private Context $context) { assertNotEmpty($observeEvents); @@ -144,31 +125,21 @@ public function __construct(array $observeEvents, array $ignoreCommands, bool $o assertIsString($command); $this->ignoreCommands[$command] = 1; } - - $this->observeSensitiveCommands = $observeSensitiveCommands; - $this->clientId = $clientId; - $this->context = $context; } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandfailed.php */ public function commandFailed(CommandFailedEvent $event): void { $this->handleEvent($event); } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandstarted.php */ public function commandStarted(CommandStartedEvent $event): void { $this->handleEvent($event); } - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ + /** @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php */ public function commandSucceeded(CommandSucceededEvent $event): void { $this->handleEvent($event); @@ -194,7 +165,7 @@ public function getLsidsOnLastTwoCommands(): array } $command = $event->getCommand(); - assertObjectHasAttribute('lsid', $command); + assertObjectHasProperty('lsid', $command); $lsids[] = $command->lsid; if (count($lsids) === 2) { @@ -241,23 +212,16 @@ public function assert(array $expectedEvents, bool $ignoreExtraEvents): void } } - private function assertEvent($actual, stdClass $expected, string $message) + private function assertEvent($actual, stdClass $expected, string $message): void { assertIsObject($actual); - switch (get_class($actual)) { - case CommandStartedEvent::class: - return $this->assertCommandStartedEvent($actual, $expected, $message); - - case CommandSucceededEvent::class: - return $this->assertCommandSucceededEvent($actual, $expected, $message); - - case CommandFailedEvent::class: - return $this->assertCommandFailedEvent($actual, $expected, $message); - - default: - Assert::fail($message . ': Unsupported event type: ' . get_class($actual)); - } + match ($actual::class) { + CommandStartedEvent::class => $this->assertCommandStartedEvent($actual, $expected, $message), + CommandSucceededEvent::class => $this->assertCommandSucceededEvent($actual, $expected, $message), + CommandFailedEvent::class => $this->assertCommandFailedEvent($actual, $expected, $message), + default => Assert::fail($message . ': Unsupported event type: ' . $actual::class) + }; } private function assertCommandStartedEvent(CommandStartedEvent $actual, stdClass $expected, string $message): void @@ -293,7 +257,7 @@ private function assertCommandStartedEvent(CommandStartedEvent $actual, stdClass private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdClass $expected, string $message): void { - Util::assertHasOnlyKeys($expected, ['reply', 'commandName', 'hasServiceId', 'hasServerConnectionId']); + Util::assertHasOnlyKeys($expected, ['reply', 'commandName', 'databaseName', 'hasServiceId', 'hasServerConnectionId']); if (isset($expected->reply)) { assertIsObject($expected->reply); @@ -306,6 +270,11 @@ private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdC assertSame($actual->getCommandName(), $expected->commandName, $message . ': commandName matches'); } + if (isset($expected->databaseName)) { + assertIsString($expected->databaseName); + assertSame($actual->getDatabaseName(), $expected->databaseName, $message . ': databaseName matches'); + } + if (isset($expected->hasServiceId)) { assertIsBool($expected->hasServiceId); assertSame($actual->getServiceId() !== null, $expected->hasServiceId, $message . ': hasServiceId matches'); @@ -319,13 +288,18 @@ private function assertCommandSucceededEvent(CommandSucceededEvent $actual, stdC private function assertCommandFailedEvent(CommandFailedEvent $actual, stdClass $expected, string $message): void { - Util::assertHasOnlyKeys($expected, ['commandName', 'hasServiceId', 'hasServerConnectionId']); + Util::assertHasOnlyKeys($expected, ['commandName', 'databaseName', 'hasServiceId', 'hasServerConnectionId']); if (isset($expected->commandName)) { assertIsString($expected->commandName); assertSame($actual->getCommandName(), $expected->commandName, $message . ': commandName matches'); } + if (isset($expected->databaseName)) { + assertIsString($expected->databaseName); + assertSame($actual->getDatabaseName(), $expected->databaseName, $message . ': databaseName matches'); + } + if (isset($expected->hasServiceId)) { assertIsBool($expected->hasServiceId); assertSame($actual->getServiceId() !== null, $expected->hasServiceId, $message . ': hasServiceId matches'); @@ -337,8 +311,7 @@ private function assertCommandFailedEvent(CommandFailedEvent $actual, stdClass $ } } - /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ - private function handleEvent($event): void + private function handleEvent(CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event): void { if (! $this->context->isActiveClient($this->clientId)) { return; @@ -348,7 +321,7 @@ private function handleEvent($event): void return; } - if (! isset($this->observeEvents[get_class($event)])) { + if (! isset($this->observeEvents[$event::class])) { return; } @@ -363,8 +336,7 @@ private function handleEvent($event): void $this->actualEvents[] = $event; } - /** @param CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event */ - private function isSensitiveCommand($event): bool + private function isSensitiveCommand(CommandStartedEvent|CommandSucceededEvent|CommandFailedEvent $event): bool { if (isset(self::$sensitiveCommands[$event->getCommandName()])) { return true; diff --git a/tests/UnifiedSpecTests/ExpectedError.php b/tests/UnifiedSpecTests/ExpectedError.php index b99a9f2ac..092701479 100644 --- a/tests/UnifiedSpecTests/ExpectedError.php +++ b/tests/UnifiedSpecTests/ExpectedError.php @@ -2,6 +2,7 @@ namespace MongoDB\Tests\UnifiedSpecTests; +use MongoDB\Driver\Exception\BulkWriteCommandException; use MongoDB\Driver\Exception\BulkWriteException; use MongoDB\Driver\Exception\CommandException; use MongoDB\Driver\Exception\ExecutionTimeoutException; @@ -12,9 +13,10 @@ use stdClass; use Throwable; -use function get_class; +use function count; use function PHPUnit\Framework\assertArrayHasKey; use function PHPUnit\Framework\assertContainsOnly; +use function PHPUnit\Framework\assertCount; use function PHPUnit\Framework\assertFalse; use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsArray; @@ -25,21 +27,20 @@ use function PHPUnit\Framework\assertNotInstanceOf; use function PHPUnit\Framework\assertNotNull; use function PHPUnit\Framework\assertNull; -use function PHPUnit\Framework\assertObjectHasAttribute; +use function PHPUnit\Framework\assertObjectHasProperty; use function PHPUnit\Framework\assertSame; use function PHPUnit\Framework\assertStringContainsStringIgnoringCase; use function PHPUnit\Framework\assertThat; use function PHPUnit\Framework\assertTrue; +use function PHPUnit\Framework\isInstanceOf; +use function PHPUnit\Framework\logicalOr; use function property_exists; use function sprintf; final class ExpectedError { - /** - * @see https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.err - * @var array - */ - private static $codeNameMap = [ + /** @see https://github.com/mongodb/mongo/blob/master/src/mongo/base/error_codes.err */ + private static array $codeNameMap = [ 'Interrupted' => 11601, 'MaxTimeMSExpired' => 50, 'NoSuchTransaction' => 251, @@ -47,32 +48,27 @@ final class ExpectedError 'WriteConflict' => 112, ]; - /** @var bool */ - private $isError = false; + private bool $isError = false; + + private ?bool $isClientError = null; - /** @var bool|null */ - private $isClientError; + private ?string $messageContains = null; - /** @var string|null */ - private $messageContains; + private ?int $code = null; - /** @var int|null */ - private $code; + private ?string $codeName = null; - /** @var string|null */ - private $codeName; + private ?Matches $matchesErrorResponse = null; - /** @var Matches|null */ - private $matchesResultDocument; + private array $includedLabels = []; - /** @var array */ - private $includedLabels = []; + private array $excludedLabels = []; - /** @var array */ - private $excludedLabels = []; + private ?ExpectedResult $expectedResult = null; - /** @var ExpectedResult|null */ - private $expectedResult; + private ?array $writeErrors = null; + + private ?array $writeConcernErrors = null; public function __construct(?stdClass $o, EntityMap $entityMap) { @@ -91,6 +87,10 @@ public function __construct(?stdClass $o, EntityMap $entityMap) $this->isClientError = $o->isClientError; } + if (property_exists($o, 'isTimeoutError')) { + Assert::markTestIncomplete('CSOT is not yet implemented (PHPC-1760)'); + } + if (isset($o->errorContains)) { assertIsString($o->errorContains); $this->messageContains = $o->errorContains; @@ -108,7 +108,7 @@ public function __construct(?stdClass $o, EntityMap $entityMap) if (isset($o->errorResponse)) { assertIsObject($o->errorResponse); - $this->matchesResultDocument = new Matches($o->errorResponse, $entityMap); + $this->matchesErrorResponse = new Matches($o->errorResponse, $entityMap); } if (isset($o->errorLabelsContain)) { @@ -126,6 +126,24 @@ public function __construct(?stdClass $o, EntityMap $entityMap) if (property_exists($o, 'expectResult')) { $this->expectedResult = new ExpectedResult($o, $entityMap); } + + if (isset($o->writeErrors)) { + assertIsObject($o->writeErrors); + assertContainsOnly('object', (array) $o->writeErrors); + + foreach ($o->writeErrors as $i => $writeError) { + $this->writeErrors[$i] = new Matches($writeError, $entityMap); + } + } + + if (isset($o->writeConcernErrors)) { + assertIsArray($o->writeConcernErrors); + assertContainsOnly('object', $o->writeConcernErrors); + + foreach ($o->writeConcernErrors as $i => $writeConcernError) { + $this->writeConcernErrors[$i] = new Matches($writeConcernError, $entityMap); + } + } } /** @@ -136,7 +154,7 @@ public function __construct(?stdClass $o, EntityMap $entityMap) public function assert(?Throwable $e = null): void { if (! $this->isError && $e !== null) { - Assert::fail(sprintf("Operation threw unexpected %s: %s\n%s", get_class($e), $e->getMessage(), $e->getTraceAsString())); + Assert::fail(sprintf("Operation threw unexpected %s: %s\n%s", $e::class, $e->getMessage(), $e->getTraceAsString())); } if (! $this->isError) { @@ -165,9 +183,22 @@ public function assert(?Throwable $e = null): void $this->assertCodeName($e); } - if (isset($this->matchesResultDocument)) { - assertInstanceOf(CommandException::class, $e); - assertThat($e->getResultDocument(), $this->matchesResultDocument, 'CommandException result document matches'); + if (isset($this->matchesErrorResponse)) { + assertThat($e, logicalOr( + isInstanceOf(CommandException::class), + isInstanceOf(BulkWriteException::class), + isInstanceOf(BulkWriteCommandException::class), + )); + + if ($e instanceof CommandException) { + assertThat($e->getResultDocument(), $this->matchesErrorResponse, 'CommandException result document matches expected errorResponse'); + } elseif ($e instanceof BulkWriteCommandException) { + assertThat($e->getErrorReply(), $this->matchesErrorResponse, 'BulkWriteCommandException error reply matches expected errorResponse'); + } elseif ($e instanceof BulkWriteException) { + $writeErrors = $e->getWriteResult()->getErrorReplies(); + assertCount(1, $writeErrors); + assertThat($writeErrors[0], $this->matchesErrorResponse, 'BulkWriteException first error reply matches expected errorResponse'); + } } if (! empty($this->excludedLabels) || ! empty($this->includedLabels)) { @@ -183,16 +214,34 @@ public function assert(?Throwable $e = null): void } if (isset($this->expectedResult)) { - assertInstanceOf(BulkWriteException::class, $e); - $this->expectedResult->assert($e->getWriteResult()); + assertThat($e, logicalOr( + isInstanceOf(BulkWriteException::class), + isInstanceOf(BulkWriteCommandException::class), + )); + + if ($e instanceof BulkWriteCommandException) { + $this->expectedResult->assert($e->getPartialResult()); + } elseif ($e instanceof BulkWriteException) { + $this->expectedResult->assert($e->getWriteResult()); + } + } + + if (isset($this->writeErrors)) { + assertInstanceOf(BulkWriteCommandException::class, $e); + $this->assertWriteErrors($e->getWriteErrors()); + } + + if (isset($this->writeConcernErrors)) { + assertInstanceOf(BulkWriteCommandException::class, $e); + $this->assertWriteConcernErrors($e->getWriteConcernErrors()); } } private function assertIsClientError(Throwable $e): void { - /* Note: BulkWriteException may proxy a previous exception. Unwrap it - * to check the original error. */ - if ($e instanceof BulkWriteException && $e->getPrevious() !== null) { + /* Note: BulkWriteException and BulkWriteCommandException may proxy a + * previous exception. Unwrap it to check the original error. */ + if (($e instanceof BulkWriteException || $e instanceof BulkWriteCommandException) && $e->getPrevious() !== null) { $e = $e->getPrevious(); } @@ -220,13 +269,56 @@ private function assertCodeName(ServerException $e): void $result = $e->getResultDocument(); if (isset($result->writeConcernError)) { - assertObjectHasAttribute('codeName', $result->writeConcernError); + assertObjectHasProperty('codeName', $result->writeConcernError); assertSame($this->codeName, $result->writeConcernError->codeName); return; } - assertObjectHasAttribute('codeName', $result); + assertObjectHasProperty('codeName', $result); assertSame($this->codeName, $result->codeName); } + + private function assertWriteErrors(array $writeErrors): void + { + assertCount(count($this->writeErrors), $writeErrors); + + foreach ($this->writeErrors as $i => $matchesWriteError) { + assertArrayHasKey($i, $writeErrors); + $writeError = $writeErrors[$i]; + + // Not required by the spec test, but asserts PHPC correctness + assertSame((int) $i, $writeError->getIndex()); + + /* Convert the WriteError into a document for matching. These + * field names are derived from the CRUD spec. */ + $writeErrorDocument = [ + 'code' => $writeError->getCode(), + 'message' => $writeError->getMessage(), + 'details' => $writeError->getInfo(), + ]; + + assertThat($writeErrorDocument, $matchesWriteError); + } + } + + private function assertWriteConcernErrors(array $writeConcernErrors): void + { + assertCount(count($this->writeConcernErrors), $writeConcernErrors); + + foreach ($this->writeConcernErrors as $i => $matchesWriteConcernError) { + assertArrayHasKey($i, $writeConcernErrors); + $writeConcernError = $writeConcernErrors[$i]; + + /* Convert the WriteConcernError into a document for matching. + * These field names are derived from the CRUD spec. */ + $writeConcernErrorDocument = [ + 'code' => $writeConcernError->getCode(), + 'message' => $writeConcernError->getMessage(), + 'details' => $writeConcernError->getInfo(), + ]; + + assertThat($writeConcernErrorDocument, $matchesWriteConcernError); + } + } } diff --git a/tests/UnifiedSpecTests/ExpectedResult.php b/tests/UnifiedSpecTests/ExpectedResult.php index 618a2983d..29871c289 100644 --- a/tests/UnifiedSpecTests/ExpectedResult.php +++ b/tests/UnifiedSpecTests/ExpectedResult.php @@ -4,6 +4,7 @@ use MongoDB\BulkWriteResult; use MongoDB\DeleteResult; +use MongoDB\Driver\BulkWriteCommandResult; use MongoDB\Driver\WriteResult; use MongoDB\InsertManyResult; use MongoDB\InsertOneResult; @@ -11,33 +12,27 @@ use MongoDB\UpdateResult; use stdClass; +use function array_filter; use function is_object; use function PHPUnit\Framework\assertThat; use function property_exists; final class ExpectedResult { - /** @var Matches */ - private $constraint; - - /** @var EntityMap */ - private $entityMap; + private ?Matches $constraint = null; /** * ID of the entity yielding the result. This is mainly used to associate * entities with a root client for collation of observed events. - * - * @var ?string */ - private $yieldingEntityId; + private ?string $yieldingEntityId = null; - public function __construct(stdClass $o, EntityMap $entityMap, ?string $yieldingEntityId = null) + public function __construct(stdClass $o, private EntityMap $entityMap, ?string $yieldingEntityId = null) { if (property_exists($o, 'expectResult')) { $this->constraint = new Matches($o->expectResult, $entityMap); } - $this->entityMap = $entityMap; $this->yieldingEntityId = $yieldingEntityId; } @@ -64,6 +59,10 @@ private static function prepare($value) return $value; } + if ($value instanceof BulkWriteCommandResult) { + return self::prepareBulkWriteCommandResult($value); + } + if ( $value instanceof BulkWriteResult || $value instanceof WriteResult || @@ -78,7 +77,37 @@ private static function prepare($value) return $value; } - private static function prepareWriteResult($value) + private static function prepareBulkWriteCommandResult(BulkWriteCommandResult $result): array + { + $retval = ['acknowledged' => $result->isAcknowledged()]; + + if (! $retval['acknowledged']) { + return $retval; + } + + $retval = [ + 'deletedCount' => $result->getDeletedCount(), + 'insertedCount' => $result->getInsertedCount(), + 'matchedCount' => $result->getMatchedCount(), + 'modifiedCount' => $result->getModifiedCount(), + 'upsertedCount' => $result->getUpsertedCount(), + ]; + + /* Tests use $$unsetOrMatches to expect either no key or an empty + * document when verboseResults=false, so filter out null values. */ + $retval += array_filter( + [ + 'deleteResults' => $result->getDeleteResults()?->toPHP(), + 'insertResults' => $result->getInsertResults()?->toPHP(), + 'updateResults' => $result->getUpdateResults()?->toPHP(), + ], + fn ($value) => $value !== null, + ); + + return $retval; + } + + private static function prepareWriteResult($value): array { $result = ['acknowledged' => $value->isAcknowledged()]; diff --git a/tests/UnifiedSpecTests/FailPointObserver.php b/tests/UnifiedSpecTests/FailPointObserver.php deleted file mode 100644 index 76d3b9633..000000000 --- a/tests/UnifiedSpecTests/FailPointObserver.php +++ /dev/null @@ -1,70 +0,0 @@ -getCommand(); - - if (! isset($command->configureFailPoint)) { - return; - } - - if (isset($command->mode) && $command->mode === 'off') { - return; - } - - $this->failPointsAndServers[] = [$command->configureFailPoint, $event->getServer()]; - } - - /** - * @see https://php.net/manual/en/mongodb-driver-monitoring-commandsubscriber.commandsucceeded.php - */ - public function commandSucceeded(CommandSucceededEvent $event): void - { - } - - public function disableFailPoints(): void - { - foreach ($this->failPointsAndServers as [$failPoint, $server]) { - $operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']); - $operation->execute($server); - } - - $this->failPointsAndServers = []; - } - - public function start(): void - { - addSubscriber($this); - } - - public function stop(): void - { - removeSubscriber($this); - } -} diff --git a/tests/UnifiedSpecTests/Loop.php b/tests/UnifiedSpecTests/Loop.php index 3351e22b8..62848596e 100644 --- a/tests/UnifiedSpecTests/Loop.php +++ b/tests/UnifiedSpecTests/Loop.php @@ -22,37 +22,22 @@ final class Loop { - /** @var boolean */ - private static $allowIteration = true; + private static bool $allowIteration = true; - /** @var integer */ - private static $sleepUsecBetweenIterations = 0; + private static int $sleepUsecBetweenIterations = 0; - /** @var Context */ - private $context; + private ?BSONArray $errorList = null; - /** @var array */ - private $operations = []; + private ?BSONArray $failureList = null; - /** @var BSONArray */ - private $errorList; + private string $numSuccessfulOperationsEntityId; - /** @var BSONArray */ - private $failureList; + private string $numIterationsEntityId; - /** @var string */ - private $numSuccessfulOperationsEntityId; - - /** @var string */ - private $numIterationsEntityId; - - public function __construct(array $operations, Context $context, array $options = []) + public function __construct(private array $operations, private Context $context, array $options = []) { assertContainsOnly(Operation::class, $operations); - $this->operations = $operations; - $this->context = $context; - foreach (['storeErrorsAsEntity', 'storeFailuresAsEntity', 'storeSuccessesAsEntity', 'storeIterationsAsEntity'] as $option) { if (array_key_exists($option, $options)) { assertIsString($options[$option]); diff --git a/tests/UnifiedSpecTests/ManagesFailPointsTrait.php b/tests/UnifiedSpecTests/ManagesFailPointsTrait.php new file mode 100644 index 000000000..2680e89ac --- /dev/null +++ b/tests/UnifiedSpecTests/ManagesFailPointsTrait.php @@ -0,0 +1,47 @@ + */ + private array $failPointsAndServers = []; + + public function configureFailPoint(stdClass $failPoint, Server $server): void + { + assertObjectHasProperty('configureFailPoint', $failPoint); + assertIsString($failPoint->configureFailPoint); + assertObjectHasProperty('mode', $failPoint); + + $operation = new DatabaseCommand('admin', $failPoint); + $operation->execute($server); + + if ($failPoint->mode !== 'off') { + $this->failPointsAndServers[] = [$failPoint->configureFailPoint, $server]; + } + } + + public function disableFailPoints(): void + { + foreach ($this->failPointsAndServers as [$failPoint, $server]) { + try { + $operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']); + $operation->execute($server); + } catch (ConnectionException) { + // Retry once in case the connection was dropped by the last operation + $operation = new DatabaseCommand('admin', ['configureFailPoint' => $failPoint, 'mode' => 'off']); + $operation->execute($server); + } + } + + $this->failPointsAndServers = []; + } +} diff --git a/tests/UnifiedSpecTests/Operation.php b/tests/UnifiedSpecTests/Operation.php index 54f634b02..8cb4509dd 100644 --- a/tests/UnifiedSpecTests/Operation.php +++ b/tests/UnifiedSpecTests/Operation.php @@ -3,11 +3,11 @@ namespace MongoDB\Tests\UnifiedSpecTests; use Error; -use MongoDB\BSON\Javascript; use MongoDB\ChangeStream; use MongoDB\Client; use MongoDB\Collection; use MongoDB\Database; +use MongoDB\Driver\BulkWriteCommand; use MongoDB\Driver\ClientEncryption; use MongoDB\Driver\Cursor; use MongoDB\Driver\Server; @@ -16,7 +16,6 @@ use MongoDB\Model\CollectionInfo; use MongoDB\Model\DatabaseInfo; use MongoDB\Model\IndexInfo; -use MongoDB\Operation\DatabaseCommand; use MongoDB\Operation\FindOneAndReplace; use MongoDB\Operation\FindOneAndUpdate; use PHPUnit\Framework\Assert; @@ -32,7 +31,6 @@ use function current; use function fopen; use function fwrite; -use function get_class; use function hex2bin; use function iterator_to_array; use function key; @@ -52,7 +50,7 @@ use function PHPUnit\Framework\assertNotEquals; use function PHPUnit\Framework\assertNotNull; use function PHPUnit\Framework\assertNull; -use function PHPUnit\Framework\assertObjectHasAttribute; +use function PHPUnit\Framework\assertObjectHasProperty; use function PHPUnit\Framework\assertSame; use function PHPUnit\Framework\assertThat; use function PHPUnit\Framework\assertTrue; @@ -67,39 +65,41 @@ final class Operation { public const OBJECT_TEST_RUNNER = 'testRunner'; - /** @var bool */ - private $isTestRunnerOperation; + private bool $isTestRunnerOperation; - /** @var string */ - private $name; + private string $name; - /** @var ?string */ - private $object; + private ?string $object = null; - /** @var array */ - private $arguments = []; + private array $arguments = []; - /** @var Context */ - private $context; + private EntityMap $entityMap; - /** @var EntityMap */ - private $entityMap; + private ExpectedError $expectError; - /** @var ExpectedError */ - private $expectError; + private ExpectedResult $expectResult; - /** @var ExpectedResult */ - private $expectResult; + private bool $ignoreResultAndError; - /** @var bool */ - private $ignoreResultAndError; + private ?string $saveResultAsEntity = null; - /** @var string */ - private $saveResultAsEntity; + private static array $unsupportedOperations = [ + self::OBJECT_TEST_RUNNER => [ + 'assertNumberConnectionsCheckedOut' => 'PHP does not implement CMAP', + 'createEntities' => 'createEntities is not implemented (PHPC-1760)', + ], + Client::class => ['listDatabaseObjects' => 'listDatabaseObjects is not implemented'], + Cursor::class => ['iterateOnce' => 'iterateOnce is not implemented (PHPC-1760)'], + Database::class => [ + 'createCommandCursor' => 'commandCursor API is not yet implemented (PHPLIB-1077)', + 'listCollectionObjects' => 'listCollectionObjects is not implemented', + 'runCursorCommand' => 'commandCursor API is not yet implemented (PHPLIB-1077)', + ], + Collection::class => ['listIndexNames' => 'listIndexNames is not implemented'], + ]; - public function __construct(stdClass $o, Context $context) + public function __construct(stdClass $o, private Context $context) { - $this->context = $context; $this->entityMap = $context->getEntityMap(); assertIsString($o->name); @@ -181,9 +181,10 @@ private function execute() $object = $this->entityMap[$this->object]; assertIsObject($object); + $this->skipIfOperationIsNotSupported($object::class); $this->context->setActiveClient($this->entityMap->getRootClientIdOf($this->object)); - switch (get_class($object)) { + switch ($object::class) { case Client::class: $result = $this->executeForClient($object); break; @@ -209,7 +210,7 @@ private function execute() $result = $this->executeForBucket($object); break; default: - Assert::fail('Unsupported entity type: ' . get_class($object)); + Assert::fail('Unsupported entity type: ' . $object::class); } return $result; @@ -254,13 +255,25 @@ private function executeForClient(Client $client) Util::assertArgumentsBySchema(Client::class, $this->name, $args); switch ($this->name) { + case 'clientBulkWrite': + assertArrayHasKey('models', $args); + assertIsArray($args['models']); + + // Options for ClientBulkWriteCommand and Server::executeBulkWriteCommand() will be mixed + $options = array_diff_key($args, ['models' => 1]); + + return $client->bulkWrite( + self::prepareBulkWriteCommand($args['models'], $options), + $options, + ); + case 'createChangeStream': assertArrayHasKey('pipeline', $args); assertIsArray($args['pipeline']); return $client->watch( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); case 'listDatabaseNames': @@ -268,10 +281,8 @@ private function executeForClient(Client $client) case 'listDatabases': return array_map( - function (DatabaseInfo $info) { - return $info->__debugInfo(); - }, - iterator_to_array($client->listDatabases($args)) + fn (DatabaseInfo $info) => $info->__debugInfo(), + iterator_to_array($client->listDatabases($args)), ); default: @@ -346,7 +357,7 @@ private function executeForCollection(Collection $collection) return iterator_to_array($collection->aggregate( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), )); case 'bulkWrite': @@ -355,12 +366,10 @@ private function executeForCollection(Collection $collection) return $collection->bulkWrite( array_map( - static function ($request) { - return self::prepareBulkWriteRequest($request); - }, - $args['requests'] + static fn ($request) => self::prepareBulkWriteRequest($request), + $args['requests'], ), - array_diff_key($args, ['requests' => 1]) + array_diff_key($args, ['requests' => 1]), ); case 'createChangeStream': @@ -369,7 +378,7 @@ static function ($request) { return $collection->watch( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); case 'createFindCursor': @@ -378,7 +387,7 @@ static function ($request) { return $collection->find( $args['filter'], - array_diff_key($args, ['filter' => 1]) + array_diff_key($args, ['filter' => 1]), ); case 'createIndex': @@ -387,7 +396,7 @@ static function ($request) { return $collection->createIndex( $args['keys'], - array_diff_key($args, ['keys' => 1]) + array_diff_key($args, ['keys' => 1]), ); case 'dropIndex': @@ -396,7 +405,7 @@ static function ($request) { return $collection->dropIndex( $args['name'], - array_diff_key($args, ['name' => 1]) + array_diff_key($args, ['name' => 1]), ); case 'count': @@ -424,11 +433,6 @@ static function ($request) { ); case 'distinct': - if (isset($args['session']) && $args['session']->isInTransaction()) { - // Transaction, but sharded cluster? - $collection->distinct('foo'); - } - assertArrayHasKey('fieldName', $args); assertArrayHasKey('filter', $args); assertIsString($args['fieldName']); @@ -437,7 +441,7 @@ static function ($request) { return $collection->distinct( $args['fieldName'], $args['filter'], - array_diff_key($args, ['fieldName' => 1, 'filter' => 1]) + array_diff_key($args, ['fieldName' => 1, 'filter' => 1]), ); case 'drop': @@ -449,7 +453,7 @@ static function ($request) { return iterator_to_array($collection->find( $args['filter'], - array_diff_key($args, ['filter' => 1]) + array_diff_key($args, ['filter' => 1]), )); case 'findOne': @@ -458,7 +462,7 @@ static function ($request) { return $collection->findOne( $args['filter'], - array_diff_key($args, ['filter' => 1]) + array_diff_key($args, ['filter' => 1]), ); case 'findOneAndReplace': @@ -512,7 +516,7 @@ static function ($request) { return $collection->insertMany( $args['documents'], - array_diff_key($args, ['documents' => 1]) + array_diff_key($args, ['documents' => 1]), ); case 'insertOne': @@ -521,31 +525,18 @@ static function ($request) { return $collection->insertOne( $args['document'], - array_diff_key($args, ['document' => 1]) + array_diff_key($args, ['document' => 1]), ); case 'listIndexes': return array_map( - function (IndexInfo $info) { - return $info->__debugInfo(); - }, - iterator_to_array($collection->listIndexes($args)) + fn (IndexInfo $info) => $info->__debugInfo(), + iterator_to_array($collection->listIndexes($args)), ); case 'mapReduce': - assertArrayHasKey('map', $args); - assertArrayHasKey('reduce', $args); - assertArrayHasKey('out', $args); - assertInstanceOf(Javascript::class, $args['map']); - assertInstanceOf(Javascript::class, $args['reduce']); - assertIsString($args['out']); - - return $collection->mapReduce( - $args['map'], - $args['reduce'], - $args['out'], - array_diff_key($args, ['map' => 1, 'reduce' => 1, 'out' => 1]) - ); + Assert::markTestSkipped('mapReduce operation is not supported'); + break; case 'rename': assertArrayHasKey('to', $args); @@ -554,9 +545,56 @@ function (IndexInfo $info) { return $collection->rename( $args['to'], null, /* $toDatabaseName */ - array_diff_key($args, ['to' => 1]) + array_diff_key($args, ['to' => 1]), ); + case 'createSearchIndex': + assertArrayHasKey('model', $args); + assertIsObject($args['model']); + assertObjectHasProperty('definition', $args['model']); + assertInstanceOf(stdClass::class, $args['model']->definition); + + /* Note: tests specify options within "model". A top-level + * "options" key (CreateSearchIndexOptions) is not used. */ + $definition = $args['model']->definition; + $options = array_diff_key((array) $args['model'], ['definition' => 1]); + + return $collection->createSearchIndex($definition, $options); + + case 'createSearchIndexes': + assertArrayHasKey('models', $args); + assertIsArray($args['models']); + + $indexes = array_map(function ($index) { + $index = (array) $index; + assertArrayHasKey('definition', $index); + assertInstanceOf(stdClass::class, $index['definition']); + + return $index; + }, $args['models']); + + return $collection->createSearchIndexes($indexes); + + case 'dropSearchIndex': + assertArrayHasKey('name', $args); + assertIsString($args['name']); + + return $collection->dropSearchIndex($args['name']); + + case 'updateSearchIndex': + assertArrayHasKey('name', $args); + assertArrayHasKey('definition', $args); + assertIsString($args['name']); + assertInstanceOf(stdClass::class, $args['definition']); + + return $collection->updateSearchIndex($args['name'], $args['definition']); + + case 'listSearchIndexes': + $args += (array) ($args['aggregationOptions'] ?? []); + unset($args['aggregationOptions']); + + return $collection->listSearchIndexes($args); + default: Assert::fail('Unsupported collection operation: ' . $this->name); } @@ -622,7 +660,7 @@ private function executeForDatabase(Database $database) return iterator_to_array($database->aggregate( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), )); case 'createChangeStream': @@ -631,7 +669,7 @@ private function executeForDatabase(Database $database) return $database->watch( $args['pipeline'], - array_diff_key($args, ['pipeline' => 1]) + array_diff_key($args, ['pipeline' => 1]), ); case 'createCollection': @@ -640,7 +678,7 @@ private function executeForDatabase(Database $database) return $database->createCollection( $args['collection'], - array_diff_key($args, ['collection' => 1]) + array_diff_key($args, ['collection' => 1]), ); case 'dropCollection': @@ -649,7 +687,7 @@ private function executeForDatabase(Database $database) return $database->dropCollection( $args['collection'], - array_diff_key($args, ['collection' => 1]) + array_diff_key($args, ['collection' => 1]), ); case 'listCollectionNames': @@ -657,10 +695,8 @@ private function executeForDatabase(Database $database) case 'listCollections': return array_map( - function (CollectionInfo $info) { - return $info->__debugInfo(); - }, - iterator_to_array($database->listCollections($args)) + fn (CollectionInfo $info) => $info->__debugInfo(), + iterator_to_array($database->listCollections($args)), ); case 'modifyCollection': @@ -683,10 +719,16 @@ function (CollectionInfo $info) { assertArrayHasKey('command', $args); assertInstanceOf(stdClass::class, $args['command']); - return $database->command( - $args['command'], - array_diff_key($args, ['command' => 1]) - )->toArray()[0]; + // Note: commandName is not used by PHP + $options = array_diff_key($args, ['command' => 1, 'commandName' => 1]); + + /* runCommand spec tests may execute commands that return a + * cursor (e.g. aggregate). PHPC creates a cursor automatically + * so cannot return the original command result. Existing spec + * tests do not evaluate the result, so we can return null here. + * If that changes down the line, we can use command monitoring + * to capture and return the command result. */ + return $database->command($args['command'], $options)->toArray()[0] ?? null; default: Assert::fail('Unsupported database operation: ' . $this->name); @@ -745,13 +787,19 @@ private function executeForBucket(Bucket $bucket) return $bucket->delete($args['id']); + case 'deleteByName': + assertArrayHasKey('filename', $args); + assertIsString($args['filename']); + + return $bucket->deleteByName($args['filename']); + case 'downloadByName': assertArrayHasKey('filename', $args); assertIsString($args['filename']); return stream_get_contents($bucket->openDownloadStreamByName( $args['filename'], - array_diff_key($args, ['filename' => 1]) + array_diff_key($args, ['filename' => 1]), )); case 'download': @@ -759,6 +807,23 @@ private function executeForBucket(Bucket $bucket) return stream_get_contents($bucket->openDownloadStream($args['id'])); + case 'rename': + assertArrayHasKey('id', $args); + assertArrayHasKey('newFilename', $args); + assertIsString($args['newFilename']); + + $bucket->rename($args['id'], $args['newFilename']); + + return null; + + case 'renameByName': + assertArrayHasKey('filename', $args); + assertArrayHasKey('newFilename', $args); + assertIsString($args['filename']); + assertIsString($args['newFilename']); + + return $bucket->renameByName($args['filename'], $args['newFilename']); + case 'uploadWithId': assertArrayHasKey('id', $args); $args['_id'] = $args['id']; @@ -772,7 +837,7 @@ private function executeForBucket(Bucket $bucket) return $bucket->uploadFromStream( $args['filename'], $args['source'], - array_diff_key($args, ['filename' => 1, 'source' => 1]) + array_diff_key($args, ['filename' => 1, 'source' => 1]), ); default: @@ -782,6 +847,8 @@ private function executeForBucket(Bucket $bucket) private function executeForTestRunner() { + $this->skipIfOperationIsNotSupported(self::OBJECT_TEST_RUNNER); + $args = $this->prepareArguments(); Util::assertArgumentsBySchema(self::OBJECT_TEST_RUNNER, $this->name, $args); @@ -865,12 +932,22 @@ private function executeForTestRunner() assertInstanceOf(Session::class, $args['session']); assertNull($args['session']->getServer()); break; + case 'createEntities': + assertArrayHasKey('entities', $args); + assertIsArray($args['entities']); + $this->context->createEntities($args['entities']); + /* Ensure EventObserver and EventCollector for any new clients + * are subscribed. This is a NOP for existing clients. */ + $this->context->startEventObservers(); + $this->context->startEventCollectors(); + break; case 'failPoint': assertArrayHasKey('client', $args); assertArrayHasKey('failPoint', $args); assertInstanceOf(Client::class, $args['client']); assertInstanceOf(stdClass::class, $args['failPoint']); - $args['client']->selectDatabase('admin')->command($args['failPoint']); + // Configure the fail point via the Context so it can later be disabled + $this->context->configureFailPoint($args['failPoint'], $args['client']->getManager()->selectServer()); break; case 'targetedFailPoint': assertArrayHasKey('session', $args); @@ -878,8 +955,8 @@ private function executeForTestRunner() assertInstanceOf(Session::class, $args['session']); assertInstanceOf(stdClass::class, $args['failPoint']); assertNotNull($args['session']->getServer(), 'Session is pinned'); - $operation = new DatabaseCommand('admin', $args['failPoint']); - $operation->execute($args['session']->getServer()); + // Configure the fail point via the Context so it can later be disabled + $this->context->configureFailPoint($args['failPoint'], $args['session']->getServer()); break; case 'loop': assertArrayHasKey('operations', $args); @@ -901,10 +978,8 @@ private function executeForTestRunner() private function getIndexNames(string $databaseName, string $collectionName): array { return array_map( - function (IndexInfo $indexInfo) { - return $indexInfo->getName(); - }, - iterator_to_array($this->context->getInternalClient()->selectCollection($databaseName, $collectionName)->listIndexes()) + fn (IndexInfo $indexInfo) => $indexInfo->getName(), + iterator_to_array($this->context->getInternalClient()->selectCollection($databaseName, $collectionName)->listIndexes()), ); } @@ -926,6 +1001,92 @@ private function prepareArguments(): array return Util::prepareCommonOptions($args); } + private function skipIfOperationIsNotSupported(string $executingObjectName): void + { + $skipReason = self::$unsupportedOperations[$executingObjectName][$this->name] ?? null; + if (! $skipReason) { + return; + } + + Assert::markTestSkipped($skipReason); + } + + private static function prepareBulkWriteCommand(array $models, array $options): BulkWriteCommand + { + $bulk = new BulkWriteCommand($options); + + foreach ($models as $model) { + $model = (array) $model; + assertCount(1, $model); + + $type = key($model); + $args = current($model); + assertIsObject($args); + $args = (array) $args; + + assertArrayHasKey('namespace', $args); + assertIsString($args['namespace']); + + switch ($type) { + case 'deleteMany': + case 'deleteOne': + assertArrayHasKey('filter', $args); + assertInstanceOf(stdClass::class, $args['filter']); + + $bulk->{$type}( + $args['namespace'], + $args['filter'], + array_diff_key($args, ['namespace' => 1, 'filter' => 1]), + ); + break; + + case 'insertOne': + assertArrayHasKey('document', $args); + assertInstanceOf(stdClass::class, $args['document']); + + $bulk->insertOne( + $args['namespace'], + $args['document'], + ); + break; + + case 'replaceOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('replacement', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertInstanceOf(stdClass::class, $args['replacement']); + + $bulk->replaceOne( + $args['namespace'], + $args['filter'], + $args['replacement'], + array_diff_key($args, ['namespace' => 1, 'filter' => 1, 'replacement' => 1]), + ); + break; + + case 'updateMany': + case 'updateOne': + assertArrayHasKey('filter', $args); + assertArrayHasKey('update', $args); + assertInstanceOf(stdClass::class, $args['filter']); + assertThat($args['update'], logicalOr(new IsType('array'), new IsType('object'))); + + $bulk->{$type}( + $args['namespace'], + $args['filter'], + $args['update'], + array_diff_key($args, ['namespace' => 1, 'filter' => 1, 'update' => 1]), + ); + break; + + default: + Assert::fail('Unsupported bulk write model: ' . $type); + } + } + + return $bulk; + } + private static function prepareBulkWriteRequest(stdClass $request): array { $request = (array) $request; @@ -951,6 +1112,7 @@ private static function prepareBulkWriteRequest(stdClass $request): array case 'insertOne': assertArrayHasKey('document', $args); + assertInstanceOf(stdClass::class, $args['document']); return ['insertOne' => [$args['document']]]; @@ -1017,7 +1179,7 @@ private static function prepareUploadArguments(array $args): array { $source = $args['source'] ?? null; assertIsObject($source); - assertObjectHasAttribute('$$hexBytes', $source); + assertObjectHasProperty('$$hexBytes', $source); Util::assertHasOnlyKeys($source, ['$$hexBytes']); $hexBytes = $source->{'$$hexBytes'}; assertIsString($hexBytes); diff --git a/tests/UnifiedSpecTests/RunOnRequirement.php b/tests/UnifiedSpecTests/RunOnRequirement.php index 4290bda9d..9dff581db 100644 --- a/tests/UnifiedSpecTests/RunOnRequirement.php +++ b/tests/UnifiedSpecTests/RunOnRequirement.php @@ -10,9 +10,9 @@ use function PHPUnit\Framework\assertContains; use function PHPUnit\Framework\assertContainsOnly; use function PHPUnit\Framework\assertEmpty; +use function PHPUnit\Framework\assertInstanceOf; use function PHPUnit\Framework\assertIsArray; use function PHPUnit\Framework\assertIsBool; -use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertMatchesRegularExpression; use function version_compare; @@ -31,29 +31,21 @@ class RunOnRequirement public const VERSION_PATTERN = '/^[0-9]+(\\.[0-9]+){1,2}$/'; - /** @var string */ - private $minServerVersion; + private ?string $minServerVersion = null; - /** @var string */ - private $maxServerVersion; + private ?string $maxServerVersion = null; - /** @var array */ - private $topologies; + private ?array $topologies = null; - /** @var stdClass */ - private $serverParameters; + private ?stdClass $serverParameters = null; - /** @var bool */ - private $auth; + private ?bool $auth = null; - /** @var string */ - private $serverless; + private ?string $serverless = null; - /** @var bool */ - private $csfle; + private ?bool $csfle = null; - /** @var array */ - private static $supportedTopologies = [ + private static array $supportedTopologies = [ self::TOPOLOGY_SINGLE, self::TOPOLOGY_REPLICASET, self::TOPOLOGY_SHARDED, @@ -61,8 +53,7 @@ class RunOnRequirement self::TOPOLOGY_LOAD_BALANCED, ]; - /** @var array */ - private static $supportedServerless = [ + private static array $supportedServerless = [ self::SERVERLESS_REQUIRE, self::SERVERLESS_FORBID, self::SERVERLESS_ALLOW, @@ -92,7 +83,7 @@ public function __construct(stdClass $o) } if (isset($o->serverParameters)) { - assertIsObject($o->serverParameters); + assertInstanceOf(stdClass::class, $o->serverParameters); $this->serverParameters = $o->serverParameters; } @@ -113,7 +104,7 @@ public function __construct(stdClass $o) } } - public function isSatisfied(string $serverVersion, string $topology, ServerParameterHelper $serverParameters, bool $isAuthenticated, bool $isServerless, bool $isClientSideEncryptionSupported): bool + public function isSatisfied(string $serverVersion, string $topology, ServerParameterHelper $serverParameters, bool $isAuthenticated, bool $isClientSideEncryptionSupported): bool { if (isset($this->minServerVersion) && version_compare($serverVersion, $this->minServerVersion, '<')) { return false; @@ -140,14 +131,8 @@ public function isSatisfied(string $serverVersion, string $topology, ServerParam return false; } - if (isset($this->serverless)) { - if (! $isServerless && $this->serverless === self::SERVERLESS_REQUIRE) { - return false; - } - - if ($isServerless && $this->serverless === self::SERVERLESS_FORBID) { - return false; - } + if (isset($this->serverless) && $this->serverless === self::SERVERLESS_REQUIRE) { + return false; } if (isset($this->csfle) && $isClientSideEncryptionSupported !== $this->csfle) { diff --git a/tests/UnifiedSpecTests/ServerParameterHelper.php b/tests/UnifiedSpecTests/ServerParameterHelper.php index 6e14e8faa..b0afd5684 100644 --- a/tests/UnifiedSpecTests/ServerParameterHelper.php +++ b/tests/UnifiedSpecTests/ServerParameterHelper.php @@ -10,25 +10,18 @@ final class ServerParameterHelper { - /** @var Client */ - private $client; - /** @var array */ - private $parameters = []; + private array $parameters = []; - /** @var bool */ - private $fetchAllParametersFailed = false; + private bool $fetchAllParametersFailed = false; - /** @var bool */ - private $allParametersFetched = false; + private bool $allParametersFetched = false; - public function __construct(Client $client) + public function __construct(private Client $client) { - $this->client = $client; } - /** @return mixed */ - public function __get(string $parameter) + public function __get(string $parameter): mixed { if (! array_key_exists($parameter, $this->parameters)) { $this->fetchParameter($parameter); @@ -66,12 +59,12 @@ private function fetchAllParameters(): void 'document' => 'array', 'array' => 'array', ], - ] + ], ); $this->parameters = $cursor->toArray()[0]; $this->allParametersFetched = true; - } catch (CommandException $e) { + } catch (CommandException) { $this->fetchAllParametersFailed = true; } } @@ -91,7 +84,7 @@ private function fetchSingleParameter(string $parameter): void 'document' => 'array', 'array' => 'array', ], - ] + ], ); $this->parameters[$parameter] = $cursor->toArray()[0][$parameter]; diff --git a/tests/UnifiedSpecTests/UnifiedSpecTest.php b/tests/UnifiedSpecTests/UnifiedSpecTest.php index cd5a140da..cbf504bc5 100644 --- a/tests/UnifiedSpecTests/UnifiedSpecTest.php +++ b/tests/UnifiedSpecTests/UnifiedSpecTest.php @@ -5,75 +5,114 @@ use Exception; use Generator; use MongoDB\Tests\FunctionalTestCase; +use PHPUnit\Framework\Attributes\DataProvider; +use PHPUnit\Framework\Attributes\Group; use PHPUnit\Framework\SkippedTest; use PHPUnit\Framework\Warning; -use function basename; -use function dirname; +use function array_flip; use function glob; +use function str_starts_with; +use function strtolower; /** * Unified test format spec tests. * - * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst - * @group serverless + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md */ class UnifiedSpecTest extends FunctionalTestCase { - /** @var array */ - private static $incompleteTests = [ + /** + * Incomplete test groups are listed here. Any data set that starts with a + * string listed in this index will be skipped with the message given as + * value. + * + * @var array + */ + private static array $incompleteTestGroups = [ + // Spec tests for named KMS providers depends on unimplemented functionality from UTF schema 1.18 + 'client-side-encryption/namedKMS' => 'UTF schema 1.18 is not supported (PHPLIB-1328)', + // Many load balancer tests use CMAP events and/or assertNumberConnectionsCheckedOut + 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters' => 'PHPC does not implement CMAP', + 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters' => 'PHPC does not implement CMAP', + 'load-balancers/state change errors are correctly handled' => 'PHPC does not implement CMAP', + 'load-balancers/wait queue timeout errors include details about checked out connections' => 'PHPC does not implement CMAP', + // mongoc_cluster_stream_for_server does not retry handshakes (CDRIVER-4532, PHPLIB-1033, PHPLIB-1042) + 'retryable-reads/retryable reads handshake failures' => 'Handshakes are not retried (CDRIVER-4532)', + 'retryable-writes/retryable writes handshake failures' => 'Handshakes are not retried (CDRIVER-4532)', + 'crud/bypassDocumentValidation' => 'bypassDocumentValidation is handled by libmongoc (PHPLIB-1576)', + // The rawData option will not be implemented + 'collection-management/listCollections-rawData' => 'rawData option will not be implemented', + 'crud/aggregate-rawData' => 'rawData option will not be implemented', + 'crud/BulkWrite deleteMany-rawData' => 'rawData option will not be implemented', + 'crud/BulkWrite deleteOne-rawData' => 'rawData option will not be implemented', + 'crud/BulkWrite replaceOne-rawData' => 'rawData option will not be implemented', + 'crud/BulkWrite updateMany-rawData' => 'rawData option will not be implemented', + 'crud/BulkWrite updateOne-rawData' => 'rawData option will not be implemented', + 'crud/client bulkWrite delete-rawData' => 'rawData option will not be implemented', + 'crud/client bulkWrite replaceOne-rawData' => 'rawData option will not be implemented', + 'crud/client bulkWrite update-rawData' => 'rawData option will not be implemented', + 'crud/count-rawData' => 'rawData option will not be implemented', + 'crud/countDocuments-rawData' => 'rawData option will not be implemented', + 'crud/db-aggregate-rawData' => 'rawData option will not be implemented', + 'crud/deleteMany-rawData' => 'rawData option will not be implemented', + 'crud/deleteOne-rawData' => 'rawData option will not be implemented', + 'crud/distinct-rawData' => 'rawData option will not be implemented', + 'crud/estimatedDocumentCount-rawData' => 'rawData option will not be implemented', + 'crud/find-rawData' => 'rawData option will not be implemented', + 'crud/findOneAndDelete-rawData' => 'rawData option will not be implemented', + 'crud/findOneAndReplace-rawData' => 'rawData option will not be implemented', + 'crud/findOneAndUpdate-rawData' => 'rawData option will not be implemented', + 'crud/insertMany-rawData' => 'rawData option will not be implemented', + 'crud/insertOne-rawData' => 'rawData option will not be implemented', + 'crud/replaceOne-rawData' => 'rawData option will not be implemented', + 'crud/updateMany-rawData' => 'rawData option will not be implemented', + 'crud/updateOne-rawData' => 'rawData option will not be implemented', + 'index-management/index management-rawData' => 'rawData option will not be implemented', + ]; + + /** @var array */ + private static array $incompleteTests = [ // Many load balancer tests use CMAP events and/or assertNumberConnectionsCheckedOut - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: no connection is pinned if all documents are returned in the initial batch' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned when the cursor is drained' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned to the pool when the cursor is closed' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are not returned after an network error during getMore' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are returned after a network error during a killCursors request' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: pinned connections are not returned to the pool after a non-network error on getMore' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: aggregate pins the cursor to a connection' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: listCollections pins the cursor to a connection' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: listIndexes pins the cursor to a connection' => 'PHPC does not implement CMAP', - 'load-balancers/cursors are correctly pinned to connections for load-balanced clusters: change streams pin to a connection' => 'PHPC does not implement CMAP', 'load-balancers/monitoring events include correct fields: poolClearedEvent events include serviceId' => 'PHPC does not implement CMAP', - 'load-balancers/state change errors are correctly handled: only connections for a specific serviceId are closed when pools are cleared' => 'PHPC does not implement CMAP', - 'load-balancers/state change errors are correctly handled: errors during the initial connection hello are ignored' => 'PHPC does not implement CMAP', - 'load-balancers/state change errors are correctly handled: errors during authentication are processed' => 'PHPC does not implement CMAP', - 'load-balancers/state change errors are correctly handled: stale errors are ignored' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: all operations go to the same mongos' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: transaction can be committed multiple times' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is not released after a non-transient CRUD error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is not released after a non-transient commit error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a non-transient abort error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network CRUD error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network CRUD error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network commit error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network commit error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient non-network abort error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released after a transient network abort error' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is released on successful abort' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is returned when a new transaction is started' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: pinned connection is returned when a non-transaction operation uses the session' => 'PHPC does not implement CMAP', - 'load-balancers/transactions are correctly pinned to connections for load-balanced clusters: a connection can be shared by a transaction and a cursor' => 'PHPC does not implement CMAP', - 'load-balancers/wait queue timeout errors include details about checked out connections: wait queue timeout errors include cursor statistics' => 'PHPC does not implement CMAP', - 'load-balancers/wait queue timeout errors include details about checked out connections: wait queue timeout errors include transaction statistics' => 'PHPC does not implement CMAP', + // Skips dating back to legacy transaction tests + 'transactions/mongos-recovery-token: commitTransaction retry fails on new mongos' => 'isMaster failpoints cannot be disabled', + 'transactions/pin-mongos: remain pinned after non-transient error on commit' => 'Blocked on DRIVERS-2104', + 'transactions/pin-mongos: unpin after transient error within a transaction and commit' => 'isMaster failpoints cannot be disabled', // PHPC does not implement CMAP 'valid-pass/assertNumberConnectionsCheckedOut: basic assertion succeeds' => 'PHPC does not implement CMAP', 'valid-pass/entity-client-cmap-events: events are captured during an operation' => 'PHPC does not implement CMAP', 'valid-pass/expectedEventsForClient-eventType: eventType can be set to command and cmap' => 'PHPC does not implement CMAP', 'valid-pass/expectedEventsForClient-eventType: eventType defaults to command if unset' => 'PHPC does not implement CMAP', - // CSOT is not yet implemented - 'valid-pass/collectionData-createOptions: collection is created with the correct options' => 'CSOT is not yet implemented (PHPC-1760)', - 'valid-pass/createEntities-operation: createEntities operation' => 'CSOT is not yet implemented (PHPC-1760)', - 'valid-pass/entity-cursor-iterateOnce: iterateOnce' => 'CSOT is not yet implemented (PHPC-1760)', - 'valid-pass/matches-lte-operator: special lte matching operator' => 'CSOT is not yet implemented (PHPC-1760)', - // BulkWriteException cannot access server response - 'crud/bulkWrite-errorResponse: bulkWrite operations support errorResponse assertions' => 'BulkWriteException cannot access server response', - 'crud/deleteOne-errorResponse: delete operations support errorResponse assertions' => 'BulkWriteException cannot access server response', - 'crud/insertOne-errorResponse: insert operations support errorResponse assertions' => 'BulkWriteException cannot access server response', - 'crud/updateOne-errorResponse: update operations support errorResponse assertions' => 'BulkWriteException cannot access server response', + // libmongoc always adds readConcern to aggregate command + 'index-management/search index operations ignore read and write concern: listSearchIndexes ignores read and write concern' => 'libmongoc appends readConcern to aggregate command', + // Uses an invalid object name + 'run-command/runCursorCommand: does not close the cursor when receiving an empty batch' => 'Uses an invalid object name', + ]; + + /** + * Any tests with duplicate names are skipped here. While test names should + * not be reused in spec tests, this offers a way to skip such tests until + * the name is changed. + * + * @var array + */ + private static array $duplicateTests = []; + + /** + * Any tests that rely on session pinning (including targetedFailPoint) must + * be skipped since libmongoc does not pin on load-balanced toplogies. + * + * @var array + */ + private static array $incompleteLoadBalancerTests = [ + 'transactions/mongos-recovery-token: commitTransaction explicit retries include recoveryToken' => 'libmongoc omits recoveryToken for load-balanced topology (CDRIVER-4718)', + 'transactions/pin-mongos: multiple commits' => 'libmongoc does not pin for load-balanced topology', ]; - /** @var UnifiedTestRunner */ - private static $runner; + private static UnifiedTestRunner $runner; + + private static string $testDir = __DIR__ . '/../specifications/source'; public static function setUpBeforeClass(): void { @@ -91,155 +130,197 @@ public function setUp(): void if (isset(self::$incompleteTests[$this->dataDescription()])) { $this->markTestIncomplete(self::$incompleteTests[$this->dataDescription()]); } + + if ($this->isLoadBalanced() && isset(self::$incompleteLoadBalancerTests[$this->dataDescription()])) { + $this->markTestIncomplete(self::$incompleteLoadBalancerTests[$this->dataDescription()]); + } + + foreach (self::$incompleteTestGroups as $testGroup => $reason) { + if (str_starts_with(strtolower($this->dataDescription()), strtolower($testGroup))) { + $this->markTestIncomplete($reason); + } + } } - /** - * @dataProvider provideChangeStreamsTests - */ + #[DataProvider('provideChangeStreamsTests')] public function testChangeStreams(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideChangeStreamsTests() + public static function provideChangeStreamsTests(): Generator { - return $this->provideTests(__DIR__ . '/change-streams/*.json'); + return self::provideTests('change-streams/tests/unified', 'change-streams'); } - /** - * @dataProvider provideClientSideEncryptionTests - */ + #[DataProvider('provideClientSideEncryptionTests')] + #[Group('csfle')] public function testClientSideEncryption(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideClientSideEncryptionTests() + public static function provideClientSideEncryptionTests(): Generator { - return $this->provideTests(__DIR__ . '/client-side-encryption/*.json'); + return self::provideTests('client-side-encryption/tests/unified', 'client-side-encryption'); } - /** - * @dataProvider provideCollectionManagementTests - */ + #[DataProvider('provideCollectionManagementTests')] public function testCollectionManagement(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideCollectionManagementTests() + public static function provideCollectionManagementTests(): Generator { - return $this->provideTests(__DIR__ . '/collection-management/*.json'); + return self::provideTests('collection-management/tests', 'collection-management'); } - /** - * @dataProvider provideCommandMonitoringTests - */ + #[DataProvider('provideCommandMonitoringTests')] public function testCommandMonitoring(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideCommandMonitoringTests() + public static function provideCommandMonitoringTests(): Generator { - return $this->provideTests(__DIR__ . '/command-monitoring/*.json'); + return self::provideTests('command-logging-and-monitoring/tests/monitoring', 'command-monitoring'); } - /** - * @dataProvider provideCrudTests - */ + #[DataProvider('provideCrudTests')] public function testCrud(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideCrudTests() + public static function provideCrudTests(): Generator { - return $this->provideTests(__DIR__ . '/crud/*.json'); + return self::provideTests('crud/tests/unified', 'crud'); } - /** - * @dataProvider provideGridFSTests - */ + #[DataProvider('provideGridFSTests')] public function testGridFS(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideGridFSTests() + public static function provideGridFSTests(): Generator { - return $this->provideTests(__DIR__ . '/gridfs/*.json'); + return self::provideTests('gridfs/tests', 'gridfs'); } - /** - * @dataProvider provideLoadBalancers - */ + #[DataProvider('provideLoadBalancers')] public function testLoadBalancers(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideLoadBalancers() + public static function provideLoadBalancers(): Generator { - return $this->provideTests(__DIR__ . '/load-balancers/*.json'); + return self::provideTests('load-balancers/tests', 'load-balancers'); } - /** - * @dataProvider provideSessionsTests - */ + #[DataProvider('provideReadWriteConcernTests')] + public function testReadWriteConcern(UnifiedTestCase $test): void + { + self::$runner->run($test); + } + + public static function provideReadWriteConcernTests(): Generator + { + return self::provideTests('read-write-concern/tests/operation', 'read-write-concern'); + } + + #[DataProvider('provideRetryableReadsTests')] + public function testRetryableReads(UnifiedTestCase $test): void + { + self::$runner->run($test); + } + + public static function provideRetryableReadsTests(): Generator + { + return self::provideTests('retryable-reads/tests/unified', 'retryable-reads'); + } + + #[DataProvider('provideRetryableWritesTests')] + public function testRetryableWrites(UnifiedTestCase $test): void + { + self::$runner->run($test); + } + + public static function provideRetryableWritesTests(): Generator + { + return self::provideTests('retryable-writes/tests/unified', 'retryable-writes'); + } + + #[DataProvider('provideRunCommandTests')] + public function testRunCommand(UnifiedTestCase $test): void + { + self::$runner->run($test); + } + + public static function provideRunCommandTests(): Generator + { + return self::provideTests('run-command/tests/unified', 'run-command'); + } + + #[DataProvider('provideSessionsTests')] public function testSessions(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideSessionsTests() + public static function provideSessionsTests(): Generator { - return $this->provideTests(__DIR__ . '/sessions/*.json'); + return self::provideTests('sessions/tests', 'sessions'); } - /** - * @dataProvider provideTransactionsTests - */ + #[DataProvider('provideTransactionsTests')] public function testTransactions(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideTransactionsTests() + public static function provideTransactionsTests(): Generator { - return $this->provideTests(__DIR__ . '/transactions/*.json'); + return self::provideTests('transactions/tests/unified', 'transactions'); } - /** - * @dataProvider provideVersionedApiTests - * @group versioned-api - */ + #[DataProvider('provideTransactionsConvenientApiTests')] + public function testTransactionsConvenientApi(UnifiedTestCase $test): void + { + self::$runner->run($test); + } + + public static function provideTransactionsConvenientApiTests(): Generator + { + return self::provideTests('transactions-convenient-api/tests/unified', 'transactions-convenient-api'); + } + + #[DataProvider('provideVersionedApiTests')] + #[Group('versioned-api')] public function testVersionedApi(UnifiedTestCase $test): void { self::$runner->run($test); } - public function provideVersionedApiTests() + public static function provideVersionedApiTests(): Generator { - return $this->provideTests(__DIR__ . '/versioned-api/*.json'); + return self::provideTests('versioned-api/tests', 'versioned-api'); } - /** - * @dataProvider providePassingTests - */ + #[DataProvider('providePassingTests')] public function testPassingTests(UnifiedTestCase $test): void { self::$runner->run($test); } - public function providePassingTests() + public static function providePassingTests(): Generator { - yield from $this->provideTests(__DIR__ . '/valid-pass/*.json'); + yield from self::provideTests('unified-test-format/tests/valid-pass', 'valid-pass'); } - /** - * @dataProvider provideFailingTests - */ + #[DataProvider('provideFailingTests')] public function testFailingTests(UnifiedTestCase $test): void { // Cannot use expectException(), as it ignores PHPUnit Exceptions @@ -272,18 +353,45 @@ public function testFailingTests(UnifiedTestCase $test): void $this->assertTrue($failed, 'Expected test to throw an exception'); } - public function provideFailingTests() + public static function provideFailingTests(): Generator { - yield from $this->provideTests(__DIR__ . '/valid-fail/*.json'); + yield from self::provideTests('unified-test-format/tests/valid-fail', 'valid-fail'); } - private function provideTests(string $pattern): Generator + #[DataProvider('provideIndexManagementTests')] + public function testIndexManagement(UnifiedTestCase $test): void { - foreach (glob($pattern) as $filename) { - $group = basename(dirname($filename)); + if (self::isAtlas()) { + self::markTestSkipped('Search Indexes tests must run on a non-Atlas cluster'); + } + if (! self::isEnterprise()) { + self::markTestSkipped('Specific Atlas error messages are only available on Enterprise server'); + } + + self::$runner->run($test); + } + + public static function provideIndexManagementTests(): Generator + { + yield from self::provideTests('index-management/tests', 'index-management'); + } + + private static function provideTests(string $directory, string $testGroup): Generator + { + $pattern = self::$testDir . '/' . $directory . '/*.json'; + + $duplicateTests = array_flip(self::$duplicateTests); + + foreach (glob($pattern) as $filename) { foreach (UnifiedTestCase::fromFile($filename) as $name => $test) { - yield $group . '/' . $name => [$test]; + $testKey = $testGroup . '/' . $name; + + if (isset($duplicateTests[$testKey])) { + continue; + } + + yield $testKey => [$test]; } } } diff --git a/tests/UnifiedSpecTests/UnifiedTestCase.php b/tests/UnifiedSpecTests/UnifiedTestCase.php index ced1adec0..9b12445d7 100644 --- a/tests/UnifiedSpecTests/UnifiedTestCase.php +++ b/tests/UnifiedSpecTests/UnifiedTestCase.php @@ -4,12 +4,11 @@ use Generator; use IteratorAggregate; +use MongoDB\BSON\Document; use stdClass; use Traversable; use function file_get_contents; -use function MongoDB\BSON\fromJSON; -use function MongoDB\BSON\toPHP; use function PHPUnit\Framework\assertIsArray; use function PHPUnit\Framework\assertIsObject; use function PHPUnit\Framework\assertIsString; @@ -21,32 +20,12 @@ * within a JSON object conforming to the unified test format's JSON schema. * This test case may be executed by UnifiedTestRunner::run(). * - * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md */ final class UnifiedTestCase implements IteratorAggregate { - /** @var stdClass */ - private $test; - - /** @var string */ - private $schemaVersion; - - /** @var array|null */ - private $runOnRequirements; - - /** @var array|null */ - private $createEntities; - - /** @var array|null */ - private $initialData; - - private function __construct(stdClass $test, string $schemaVersion, ?array $runOnRequirements = null, ?array $createEntities = null, ?array $initialData = null) + private function __construct(private stdClass $test, private string $schemaVersion, private ?array $runOnRequirements = null, private ?array $createEntities = null, private ?array $initialData = null) { - $this->test = $test; - $this->schemaVersion = $schemaVersion; - $this->runOnRequirements = $runOnRequirements; - $this->createEntities = $createEntities; - $this->initialData = $initialData; } /** @@ -74,7 +53,7 @@ public static function fromFile(string $filename): Generator { /* Decode the file through the driver's extended JSON parser to ensure * proper handling of special types. */ - $json = toPHP(fromJSON(file_get_contents($filename))); + $json = Document::fromJSON(file_get_contents($filename))->toPHP(); yield from static::fromJSON($json); } diff --git a/tests/UnifiedSpecTests/UnifiedTestRunner.php b/tests/UnifiedSpecTests/UnifiedTestRunner.php index 668eec997..3069636b7 100644 --- a/tests/UnifiedSpecTests/UnifiedTestRunner.php +++ b/tests/UnifiedSpecTests/UnifiedTestRunner.php @@ -2,9 +2,9 @@ namespace MongoDB\Tests\UnifiedSpecTests; +use MongoDB\Client; use MongoDB\Collection; use MongoDB\Driver\Exception\ServerException; -use MongoDB\Driver\ReadPreference; use MongoDB\Driver\Server; use MongoDB\Model\BSONArray; use MongoDB\Operation\DatabaseCommand; @@ -20,13 +20,10 @@ use function call_user_func; use function count; use function explode; -use function filter_var; use function gc_collect_cycles; use function getenv; use function implode; use function in_array; -use function is_executable; -use function is_readable; use function is_string; use function parse_url; use function PHPUnit\Framework\assertContainsOnly; @@ -35,69 +32,64 @@ use function PHPUnit\Framework\assertIsString; use function PHPUnit\Framework\assertNotEmpty; use function PHPUnit\Framework\assertNotFalse; -use function PHPUnit\Framework\assertStringContainsString; -use function PHPUnit\Framework\assertStringStartsWith; -use function preg_match; use function preg_replace; use function sprintf; +use function str_starts_with; use function strlen; use function strpos; use function substr_replace; use function version_compare; -use const DIRECTORY_SEPARATOR; -use const FILTER_VALIDATE_BOOLEAN; -use const PATH_SEPARATOR; -use const PHP_URL_HOST; - /** * Unified test runner. * - * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md */ final class UnifiedTestRunner { - public const ATLAS_TLD = 'mongodb.net'; - public const SERVER_ERROR_INTERRUPTED = 11601; public const SERVER_ERROR_UNAUTHORIZED = 13; public const MIN_SCHEMA_VERSION = '1.0'; - /* Note: This is necessary to support expectedError.errorResponse from 1.12; - * however, syntax from 1.9, 1.10, and 1.11 has not been implemented. */ - public const MAX_SCHEMA_VERSION = '1.12'; - - /** @var MongoDB\Client */ - private $internalClient; + /** + * Support for the following schema versions is incomplete: + * + * - 1.9: collectionOrDatabaseOptions.timeoutMS and expectedError.isTimeoutError are not implemented + * - 1.10: Not implemented + * - 1.11: Not implemented, but CMAP is not applicable + * - 1.13: Only $$matchAsDocument and $$matchAsRoot is implemented + * - 1.14: Not implemented + * - 1.16: Not implemented + * - 1.17: Not implemented + * - 1.18: Not implemented + * - 1.19: Not implemented + * - 1.20: Not implemented + */ + public const MAX_SCHEMA_VERSION = '1.21'; - /** @var string */ - private $internalClientUri; + private Client $internalClient; - /** @var bool */ - private $allowKillAllSessions = true; + private bool $allowKillAllSessions = true; - /** @var EntityMap */ - private $entityMap; + private ?EntityMap $entityMap = null; /** @var callable(EntityMap):void */ private $entityMapObserver; - /** @var FailPointObserver */ - private $failPointObserver; + private ServerParameterHelper $serverParameterHelper; - /** @var ServerParameterHelper */ - private $serverParameterHelper; - - public function __construct(string $internalClientUri) + public function __construct(private string $internalClientUri) { $this->internalClient = FunctionalTestCase::createTestClient($internalClientUri); - $this->internalClientUri = $internalClientUri; /* Atlas prohibits killAllSessions. Inspect the connection string to * determine if we should avoid calling killAllSessions(). This does - * mean that lingering transactions could block test execution. */ - if ($this->isServerless() || strpos($internalClientUri, self::ATLAS_TLD) !== false) { + * mean that lingering transactions could block test execution. + * + * Atlas Data Lake also does not support killAllSessions. + */ + if (FunctionalTestCase::isAtlas($internalClientUri) || $this->isAtlasDataLake()) { $this->allowKillAllSessions = false; } @@ -139,6 +131,8 @@ public function run(UnifiedTestCase $test): void * * This function is primarily used by the Atlas testing workload executor. * + * @see https://github.com/mongodb-labs/drivers-atlas-testing/ + * * @param callable(EntityMap):void $entityMapObserver */ public function setEntityMapObserver(callable $entityMapObserver): void @@ -153,9 +147,6 @@ private function doSetUp(): void * after transient error within a transaction" pinning test causes the * subsequent transaction test to block. */ $this->killAllSessions(); - - $this->failPointObserver = new FailPointObserver(); - $this->failPointObserver->start(); } private function doTearDown(bool $hasFailed): void @@ -166,9 +157,6 @@ private function doTearDown(bool $hasFailed): void $this->killAllSessions(); } - $this->failPointObserver->stop(); - $this->failPointObserver->disableFailPoints(); - /* Manually invoking garbage collection since each test is prone to * create cycles (perhaps due to EntityMap), which can leak and prevent * sessions from being released back into the pool. */ @@ -195,12 +183,14 @@ private function doTestCase(stdClass $test, string $schemaVersion, ?array $runOn $this->checkRunOnRequirements($test->runOnRequirements); } - if (isset($initialData)) { - $this->prepareInitialData($initialData); - } + assertIsArray($test->operations); $context = $this->createContext(); + if (isset($initialData)) { + $this->prepareInitialData($initialData, $context, $this->isAdvanceClusterTimeNeeded($test->operations)); + } + /* If an EntityMap observer has been configured, assign the Context's * EntityMap to a class property so it can later be accessed from run(), * irrespective of whether this test succeeds or fails. */ @@ -212,20 +202,22 @@ private function doTestCase(stdClass $test, string $schemaVersion, ?array $runOn $context->createEntities($createEntities); } - assertIsArray($test->operations); $this->preventStaleDbVersionError($test->operations, $context); $context->startEventObservers(); $context->startEventCollectors(); - foreach ($test->operations as $o) { - $operation = new Operation($o, $context); - $operation->assert(); + try { + foreach ($test->operations as $o) { + $operation = new Operation($o, $context); + $operation->assert(); + } + } finally { + $context->stopEventObservers(); + $context->stopEventCollectors(); + $context->disableFailPoints(); } - $context->stopEventObservers(); - $context->stopEventCollectors(); - if (isset($test->expectEvents)) { assertIsArray($test->expectEvents); $context->assertExpectedEventsForClients($test->expectEvents); @@ -257,7 +249,6 @@ private function checkRunOnRequirements(array $runOnRequirements): void $this->getTopology(), $this->serverParameterHelper, $this->isAuthenticated(), - $this->isServerless(), $this->isClientSideEncryptionSupported(), ]; } @@ -274,7 +265,7 @@ private function checkRunOnRequirements(array $runOnRequirements): void 'Server (version=%s, topology=%s, auth=%s) does not meet test requirements', $cachedIsSatisfiedArgs[0], $cachedIsSatisfiedArgs[1], - $cachedIsSatisfiedArgs[3] ? 'yes' : 'no' + $cachedIsSatisfiedArgs[3] ? 'yes' : 'no', )); } @@ -282,7 +273,7 @@ private function getPrimaryServer(): Server { $manager = $this->internalClient->getManager(); - return $manager->selectServer(new ReadPreference(ReadPreference::PRIMARY)); + return $manager->selectServer(); } private function getServerVersion(): string @@ -304,32 +295,28 @@ private function getServerVersion(): string */ private function getTopology(): string { - switch ($this->getPrimaryServer()->getType()) { - case Server::TYPE_STANDALONE: - return RunOnRequirement::TOPOLOGY_SINGLE; - - case Server::TYPE_RS_PRIMARY: - return RunOnRequirement::TOPOLOGY_REPLICASET; - - case Server::TYPE_MONGOS: - return $this->isShardedClusterUsingReplicasets() - ? RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET - : RunOnRequirement::TOPOLOGY_SHARDED; + return match ($this->getPrimaryServer()->getType()) { + Server::TYPE_STANDALONE => RunOnRequirement::TOPOLOGY_SINGLE, + Server::TYPE_RS_PRIMARY => RunOnRequirement::TOPOLOGY_REPLICASET, + /* Since MongoDB 3.6, all sharded clusters use replica sets. The + * unified test format deprecated use of "sharded-replicaset" in + * tests but we should still identify as such. */ + Server::TYPE_MONGOS => RunOnRequirement::TOPOLOGY_SHARDED_REPLICASET, + Server::TYPE_LOAD_BALANCER => RunOnRequirement::TOPOLOGY_LOAD_BALANCED, + default => throw new UnexpectedValueException('Topology is neither single nor RS nor sharded'), + }; + } - case Server::TYPE_LOAD_BALANCER: - return RunOnRequirement::TOPOLOGY_LOAD_BALANCED; + private function isAtlasDataLake(): bool + { + $database = $this->internalClient->selectDatabase('admin'); + $buildInfo = $database->command(['buildInfo' => 1])->toArray()[0]; - default: - throw new UnexpectedValueException('Topology is neither single nor RS nor sharded'); - } + return ! empty($buildInfo->dataLake); } /** * Return whether the connection is authenticated. - * - * Note: if the connectionStatus command is not portable for serverless, it - * may be necessary to rewrite this to instead inspect the connection string - * or consult an environment variable, as is done in libmongoc. */ private function isAuthenticated(): bool { @@ -345,6 +332,8 @@ private function isAuthenticated(): bool /** * Return whether client-side encryption is supported. + * + * @see FunctionalTestCase::skipIfClientSideEncryptionIsNotSupported() */ private function isClientSideEncryptionSupported(): bool { @@ -358,41 +347,7 @@ private function isClientSideEncryptionSupported(): bool return false; } - return static::isCryptSharedLibAvailable() || static::isMongocryptdAvailable(); - } - - private static function isCryptSharedLibAvailable(): bool - { - $cryptSharedLibPath = getenv('CRYPT_SHARED_LIB_PATH'); - - if ($cryptSharedLibPath === false) { - return false; - } - - return is_readable($cryptSharedLibPath); - } - - private static function isMongocryptdAvailable(): bool - { - $paths = explode(PATH_SEPARATOR, getenv("PATH")); - - foreach ($paths as $path) { - if (is_executable($path . DIRECTORY_SEPARATOR . 'mongocryptd')) { - return true; - } - } - - return false; - } - - /** - * Return whether serverless (i.e. proxy as mongos) is being utilized. - */ - private function isServerless(): bool - { - $isServerless = getenv('MONGODB_IS_SERVERLESS'); - - return $isServerless !== false ? filter_var($isServerless, FILTER_VALIDATE_BOOLEAN) : false; + return FunctionalTestCase::isCryptSharedLibAvailable() || FunctionalTestCase::isMongocryptdAvailable(); } /** @@ -403,23 +358,6 @@ private function isSchemaVersionSupported(string $schemaVersion): bool return version_compare($schemaVersion, self::MIN_SCHEMA_VERSION, '>=') && version_compare($schemaVersion, self::MAX_SCHEMA_VERSION, '<='); } - private function isShardedClusterUsingReplicasets(): bool - { - $collection = $this->internalClient->selectCollection('config', 'shards'); - $config = $collection->findOne(); - - if ($config === null) { - return false; - } - - /** - * Use regular expression to distinguish between standalone or replicaset: - * Without a replicaset: "host" : "localhost:4100" - * With a replicaset: "host" : "dec6d8a7-9bc1-4c0e-960c-615f860b956f/localhost:4400,localhost:4401" - */ - return preg_match('@^.*/.*:\d+@', $config['host']); - } - /** * Kill all sessions on the cluster. * @@ -441,7 +379,7 @@ private function killAllSessions(): void } $manager = $this->internalClient->getManager(); - $primary = $manager->selectServer(new ReadPreference(ReadPreference::PRIMARY)); + $primary = $manager->selectServer(); $servers = $primary->getType() === Server::TYPE_MONGOS ? $manager->getServers() : [$primary]; foreach ($servers as $server) { @@ -473,21 +411,52 @@ private function assertOutcome(array $outcome): void } } - private function prepareInitialData(array $initialData): void + private function prepareInitialData(array $initialData, Context $context, bool $isAdvanceClusterTimeNeeded): void { assertNotEmpty($initialData); assertContainsOnly('object', $initialData); + /* In order to avoid MigrationConflict errors on sharded clusters, use the cluster time obtained from creating + * collections to advance session entities. This is necessary because initialData uses an internal MongoClient, + * which will not share/gossip its cluster time via the test entities. */ + if ($isAdvanceClusterTimeNeeded) { + $session = $this->internalClient->startSession(); + } + foreach ($initialData as $data) { $collectionData = new CollectionData($data); - $collectionData->prepareInitialData($this->internalClient); + $collectionData->prepareInitialData($this->internalClient, $session ?? null); + } + + if (isset($session)) { + $context->setAdvanceClusterTime($session->getClusterTime()); + } + } + + /** + * Work around potential MigrationConflict errors on sharded clusters. + */ + private function isAdvanceClusterTimeNeeded(array $operations): bool + { + if (! in_array($this->getPrimaryServer()->getType(), [Server::TYPE_MONGOS, Server::TYPE_LOAD_BALANCER], true)) { + return false; } + + foreach ($operations as $operation) { + switch ($operation->name) { + case 'startTransaction': + case 'withTransaction': + return true; + } + } + + return false; } /** * Work around potential error executing distinct on sharded clusters. * - * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.rst#staledbversion-errors-on-sharded-clusters + * @see https://github.com/mongodb/specifications/blob/master/source/unified-test-format/unified-test-format.md#staledbversion-errors-on-sharded-clusters */ private function preventStaleDbVersionError(array $operations, Context $context): void { @@ -503,7 +472,11 @@ private function preventStaleDbVersionError(array $operations, Context $context) switch ($operation->name) { case 'distinct': $hasDistinct = true; - $collection = $context->getEntityMap()[$operation->object]; + /* TODO: If this operation references an entity that would + * be created by a createEntities test runner operation, the + * assertion below will fail; however, there is no need to + * address this until such a transaction test is created. */ + $collection = $context->getEntityMap()[$operation->object] ?? null; break; case 'startTransaction': @@ -531,26 +504,25 @@ private function createContext(): Context // We assume the internal client URI has multiple mongos hosts $multiMongosUri = $this->internalClientUri; - /* TODO: If an SRV URI is provided, we can consider connecting and - * checking the topology for multiple mongoses and then selecting a - * single mongos to reconstruct a single mongos URI; however, that - * may omit necessary URI options provided by TXT records. */ - assertStringStartsWith('mongodb://', $multiMongosUri); - assertStringContainsString(',', parse_url($multiMongosUri, PHP_URL_HOST)); - - $singleMongosUri = self::removeMultipleHosts($multiMongosUri); + if (str_starts_with($multiMongosUri, 'mongodb+srv://')) { + /* TODO: If an SRV URI is provided, we can consider connecting and + * checking the topology for multiple mongoses and then selecting a + * single mongos to reconstruct a single mongos URI; however, that + * may omit necessary URI options provided by TXT records. */ + $singleMongosUri = $multiMongosUri; + } else { + $singleMongosUri = self::removeMultipleHosts($multiMongosUri); + } $context->setUrisForUseMultipleMongoses($singleMongosUri, $multiMongosUri); } - /* TODO: Enable this logic once PHPLIB-794 is implemented. For now, load - * balancer tests will continue to use MONGODB_URI. */ - if (false && $this->getPrimaryServer()->getType() === Server::TYPE_LOAD_BALANCER && ! $this->isServerless()) { + if ($this->getPrimaryServer()->getType() === Server::TYPE_LOAD_BALANCER) { $singleMongosUri = getenv('MONGODB_SINGLE_MONGOS_LB_URI'); $multiMongosUri = getenv('MONGODB_MULTI_MONGOS_LB_URI'); - assertNotFalse($singleMongosUri); - assertNotFalse($multiMongosUri); + assertNotEmpty($singleMongosUri); + assertNotEmpty($multiMongosUri); $context->setUrisForUseMultipleMongoses($singleMongosUri, $multiMongosUri); } diff --git a/tests/UnifiedSpecTests/UnifiedTestRunnerTest.php b/tests/UnifiedSpecTests/UnifiedTestRunnerTest.php new file mode 100644 index 000000000..d9e9c37ca --- /dev/null +++ b/tests/UnifiedSpecTests/UnifiedTestRunnerTest.php @@ -0,0 +1,25 @@ +setEntityMapObserver(function (EntityMap $entityMap) use (&$calls): void { + $this->assertArrayHasKey('client0', $entityMap); + $this->assertArrayHasKey('database0', $entityMap); + $this->assertArrayHasKey('collection0', $entityMap); + $calls++; + }); + + $runner->run($test->current()); + $this->assertSame(1, $calls); + } +} diff --git a/tests/UnifiedSpecTests/Util.php b/tests/UnifiedSpecTests/Util.php index d9b3b3898..e134cb109 100644 --- a/tests/UnifiedSpecTests/Util.php +++ b/tests/UnifiedSpecTests/Util.php @@ -38,7 +38,7 @@ final class Util /** * Array to fill, which contains the schema of allowed attributes for operations. */ - private static $args = [ + private static array $args = [ Operation::OBJECT_TEST_RUNNER => [ 'assertCollectionExists' => ['databaseName', 'collectionName'], 'assertCollectionNotExists' => ['databaseName', 'collectionName'], @@ -52,11 +52,13 @@ final class Util 'assertSessionPinned' => ['session'], 'assertSessionTransactionState' => ['session', 'state'], 'assertSessionUnpinned' => ['session'], + 'createEntities' => ['entities'], 'failPoint' => ['client', 'failPoint'], 'targetedFailPoint' => ['session', 'failPoint'], 'loop' => ['operations', 'storeErrorsAsEntity', 'storeFailuresAsEntity', 'storeSuccessesAsEntity', 'storeIterationsAsEntity'], ], Client::class => [ + 'clientBulkWrite' => ['models', 'bypassDocumentValidation', 'comment', 'let', 'ordered', 'session', 'verboseResults', 'writeConcern'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'], 'listDatabaseNames' => ['authorizedDatabases', 'filter', 'maxTimeMS', 'session'], 'listDatabases' => ['authorizedDatabases', 'filter', 'maxTimeMS', 'session'], @@ -72,22 +74,24 @@ final class Util 'rewrapManyDataKey' => ['filter', 'opts'], ], Database::class => [ - 'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], + 'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'showExpandedEvents'], - 'createCollection' => ['collection', 'session', 'autoIndexId', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'flags', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'], + 'createCollection' => ['collection', 'session', 'capped', 'changeStreamPreAndPostImages', 'clusteredIndex', 'collation', 'expireAfterSeconds', 'indexOptionDefaults', 'max', 'maxTimeMS', 'pipeline', 'size', 'storageEngine', 'timeseries', 'validationAction', 'validationLevel', 'validator', 'viewOn'], 'dropCollection' => ['collection', 'session'], 'listCollectionNames' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], 'listCollections' => ['authorizedCollections', 'filter', 'maxTimeMS', 'session'], 'modifyCollection' => ['collection', 'changeStreamPreAndPostImages', 'index', 'validator'], // Note: commandName is not used by PHP - 'runCommand' => ['command', 'session', 'commandName'], + 'runCommand' => ['command', 'commandName', 'readPreference', 'session'], ], Collection::class => [ - 'aggregate' => ['pipeline', 'session', 'useCursor', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], + 'aggregate' => ['pipeline', 'session', 'allowDiskUse', 'batchSize', 'bypassDocumentValidation', 'collation', 'comment', 'explain', 'hint', 'let', 'maxAwaitTimeMS', 'maxTimeMS'], 'bulkWrite' => ['let', 'requests', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], 'createChangeStream' => ['pipeline', 'session', 'fullDocument', 'fullDocumentBeforeChange', 'resumeAfter', 'startAfter', 'startAtOperationTime', 'batchSize', 'collation', 'maxAwaitTimeMS', 'comment', 'showExpandedEvents'], - 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'createFindCursor' => ['filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'createIndex' => ['keys', 'comment', 'commitQuorum', 'maxTimeMS', 'name', 'session', 'unique'], + 'createSearchIndex' => ['model'], + 'createSearchIndexes' => ['models'], 'dropIndex' => ['name', 'session', 'maxTimeMS', 'comment'], 'count' => ['filter', 'session', 'collation', 'hint', 'limit', 'maxTimeMS', 'skip', 'comment'], 'countDocuments' => ['filter', 'session', 'limit', 'skip', 'collation', 'hint', 'maxTimeMS', 'comment'], @@ -95,19 +99,22 @@ final class Util 'deleteMany' => ['let', 'filter', 'session', 'collation', 'hint', 'comment'], 'deleteOne' => ['let', 'filter', 'session', 'collation', 'hint', 'comment'], 'findOneAndDelete' => ['let', 'filter', 'session', 'projection', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'sort', 'update', 'upsert', 'comment'], - 'distinct' => ['fieldName', 'filter', 'session', 'collation', 'maxTimeMS', 'comment'], + 'distinct' => ['fieldName', 'filter', 'session', 'collation', 'maxTimeMS', 'comment', 'hint'], 'drop' => ['session', 'comment'], - 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], - 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxScan', 'maxTimeMS', 'min', 'modifiers', 'noCursorTimeout', 'oplogReplay', 'projection', 'returnKey', 'showRecordId', 'skip', 'snapshot', 'sort'], + 'dropSearchIndex' => ['name'], + 'find' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'limit', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], + 'findOne' => ['let', 'filter', 'session', 'allowDiskUse', 'allowPartialResults', 'batchSize', 'collation', 'comment', 'cursorType', 'hint', 'max', 'maxAwaitTimeMS', 'maxTimeMS', 'min', 'noCursorTimeout', 'projection', 'returnKey', 'showRecordId', 'skip', 'sort'], 'findOneAndReplace' => ['let', 'returnDocument', 'filter', 'replacement', 'session', 'projection', 'returnDocument', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'new', 'remove', 'sort', 'comment'], 'rename' => ['to', 'comment', 'dropTarget'], - 'replaceOne' => ['let', 'filter', 'replacement', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], + 'replaceOne' => ['let', 'filter', 'replacement', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment', 'sort'], 'findOneAndUpdate' => ['let', 'returnDocument', 'filter', 'update', 'session', 'upsert', 'projection', 'remove', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'maxTimeMS', 'sort', 'comment'], 'updateMany' => ['let', 'filter', 'update', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], - 'updateOne' => ['let', 'filter', 'update', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment'], + 'updateOne' => ['let', 'filter', 'update', 'session', 'upsert', 'arrayFilters', 'bypassDocumentValidation', 'collation', 'hint', 'comment', 'sort'], + 'updateSearchIndex' => ['name', 'definition'], 'insertMany' => ['documents', 'session', 'ordered', 'bypassDocumentValidation', 'comment'], 'insertOne' => ['document', 'session', 'bypassDocumentValidation', 'comment'], 'listIndexes' => ['session', 'maxTimeMS', 'comment'], + 'listSearchIndexes' => ['name', 'aggregationOptions'], 'mapReduce' => ['map', 'reduce', 'out', 'session', 'bypassDocumentValidation', 'collation', 'finalize', 'jsMode', 'limit', 'maxTimeMS', 'query', 'scope', 'sort', 'verbose', 'comment'], ], ChangeStream::class => [ @@ -126,10 +133,14 @@ final class Util ], Bucket::class => [ 'delete' => ['id'], + 'deleteByName' => ['filename'], 'downloadByName' => ['filename', 'revision'], 'download' => ['id'], - 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], - 'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'contentType', 'metadata'], + 'rename' => ['id', 'newFilename'], + 'renameByName' => ['filename', 'newFilename'], + // "disableMD5" is ignored but allowed for backward compatibility + 'uploadWithId' => ['id', 'filename', 'source', 'chunkSizeBytes', 'disableMD5', 'metadata'], + 'upload' => ['filename', 'source', 'chunkSizeBytes', 'disableMD5', 'metadata'], ], ]; diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json b/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json deleted file mode 100644 index 55b4ae3fb..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-clusterTime.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "description": "change-streams-clusterTime", - "schemaVersion": "1.4", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - } - ], - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced", - "sharded" - ], - "serverless": "forbid" - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "description": "clusterTime is present", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "ns": { - "db": "database0", - "coll": "collection0" - }, - "clusterTime": { - "$$exists": true - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json b/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json deleted file mode 100644 index 91d8e66da..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-disambiguatedPaths.json +++ /dev/null @@ -1,252 +0,0 @@ -{ - "description": "disambiguatedPaths", - "schemaVersion": "1.4", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - } - ], - "runOnRequirements": [ - { - "minServerVersion": "6.1.0", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced", - "sharded" - ], - "serverless": "forbid" - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "description": "disambiguatedPaths is not present when showExpandedEvents is false/unset", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "1": 1 - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "a.1": 2 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": { - "$$exists": true - }, - "removedFields": { - "$$exists": true - }, - "truncatedArrays": { - "$$exists": true - }, - "disambiguatedPaths": { - "$$exists": false - } - } - } - } - ] - }, - { - "description": "disambiguatedPaths is present on updateDescription when an ambiguous path is present", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "1": 1 - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "a.1": 2 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": { - "$$exists": true - }, - "removedFields": { - "$$exists": true - }, - "truncatedArrays": { - "$$exists": true - }, - "disambiguatedPaths": { - "a.1": [ - "a", - "1" - ] - } - } - } - } - ] - }, - { - "description": "disambiguatedPaths returns array indices as integers", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": [ - { - "1": 1 - } - ] - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "a.0.1": 2 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": { - "$$exists": true - }, - "removedFields": { - "$$exists": true - }, - "truncatedArrays": { - "$$exists": true - }, - "disambiguatedPaths": { - "a.0.1": [ - "a", - { - "$$type": "int" - }, - "1" - ] - } - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-errors.json b/tests/UnifiedSpecTests/change-streams/change-streams-errors.json deleted file mode 100644 index 04fe8f04f..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-errors.json +++ /dev/null @@ -1,246 +0,0 @@ -{ - "description": "change-streams-errors", - "schemaVersion": "1.7", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "killCursors" - ], - "useMultipleMongoses": false - } - }, - { - "client": { - "id": "globalClient", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "globalDatabase0", - "client": "globalClient", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "globalCollection0", - "database": "globalDatabase0", - "collectionName": "collection0" - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "description": "The watch helper must not throw a custom exception when executed against a single server topology, but instead depend on a server error", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "topologies": [ - "single" - ] - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "expectError": { - "errorCode": 40573 - } - } - ] - }, - { - "description": "Change Stream should error when an invalid aggregation stage is passed in", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$unsupported": "foo" - } - ] - }, - "expectError": { - "errorCode": 40324 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - }, - { - "$unsupported": "foo" - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Change Stream should error when _id is projected out", - "runOnRequirements": [ - { - "minServerVersion": "4.1.11", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ] - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0 - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "errorCode": 280 - } - } - ] - }, - { - "description": "change stream errors on ElectionInProgress", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 216, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "errorCode": 216 - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json b/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json deleted file mode 100644 index 8beefb2bc..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-pre_and_post_images.json +++ /dev/null @@ -1,827 +0,0 @@ -{ - "description": "change-streams-pre_and_post_images", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "6.0.0", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "collMod", - "insert", - "update", - "getMore", - "killCursors" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "change-stream-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "change-stream-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "fullDocument:whenAvailable with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocument": "whenAvailable" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocument": { - "_id": 1, - "x": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocument": "whenAvailable" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocument:whenAvailable with changeStreamPreAndPostImages disabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocument": "whenAvailable" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocument": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocument": "whenAvailable" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocument:required with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocument": "required" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocument": { - "_id": 1, - "x": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocument": "required" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocument:required with changeStreamPreAndPostImages disabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocument": "required" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocument": "required" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "whenAvailable" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocumentBeforeChange": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "whenAvailable" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:whenAvailable with changeStreamPreAndPostImages disabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "whenAvailable" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocumentBeforeChange": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "whenAvailable" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:required with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "required" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocumentBeforeChange": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "required" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:required with changeStreamPreAndPostImages disabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "required" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "required" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:off with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "off" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocumentBeforeChange": { - "$$exists": false - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "off" - } - } - ] - } - } - } - ] - } - ] - }, - { - "description": "fullDocumentBeforeChange:off with changeStreamPreAndPostImages disabled", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collMod", - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "fullDocumentBeforeChange": "off" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "updateDescription": { - "$$type": "object" - }, - "fullDocumentBeforeChange": { - "$$exists": false - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$changeStream": { - "fullDocumentBeforeChange": "off" - } - } - ] - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json b/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json deleted file mode 100644 index b4953ec73..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-resume-allowlist.json +++ /dev/null @@ -1,2348 +0,0 @@ -{ - "description": "change-streams-resume-allowlist", - "schemaVersion": "1.7", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "killCursors" - ], - "useMultipleMongoses": false - } - }, - { - "client": { - "id": "globalClient", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "globalDatabase0", - "client": "globalClient", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "globalCollection0", - "database": "globalDatabase0", - "collectionName": "collection0" - } - } - ], - "tests": [ - { - "description": "change stream resumes after a network error", - "runOnRequirements": [ - { - "minServerVersion": "4.2" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after HostUnreachable", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 6, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after HostNotFound", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 7, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NetworkTimeout", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 89, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after ShutdownInProgress", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 91, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after PrimarySteppedDown", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 189, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after ExceededTimeLimit", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 262, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after SocketException", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 9001, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotWritablePrimary", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 10107, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after InterruptedAtShutdown", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 11600, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after InterruptedDueToReplStateChange", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 11602, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotPrimaryNoSecondaryOk", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 13435, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotPrimaryOrSecondary", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 13436, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after StaleShardVersion", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 63, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after StaleEpoch", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 150, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after RetryChangeStream", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 234, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after FailedToSatisfyReadPreference", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 133, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after CursorNotFound", - "runOnRequirements": [ - { - "minServerVersion": "4.2" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 43, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json b/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json deleted file mode 100644 index f5f4505a9..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-resume-errorLabels.json +++ /dev/null @@ -1,2130 +0,0 @@ -{ - "description": "change-streams-resume-errorlabels", - "schemaVersion": "1.7", - "runOnRequirements": [ - { - "minServerVersion": "4.3.1", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "killCursors" - ], - "useMultipleMongoses": false - } - }, - { - "client": { - "id": "globalClient", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "globalDatabase0", - "client": "globalClient", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "globalCollection0", - "database": "globalDatabase0", - "collectionName": "collection0" - } - } - ], - "tests": [ - { - "description": "change stream resumes after HostUnreachable", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 6, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after HostNotFound", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 7, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NetworkTimeout", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 89, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after ShutdownInProgress", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 91, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after PrimarySteppedDown", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 189, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after ExceededTimeLimit", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 262, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after SocketException", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 9001, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotWritablePrimary", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 10107, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after InterruptedAtShutdown", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 11600, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after InterruptedDueToReplStateChange", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 11602, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotPrimaryNoSecondaryOk", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 13435, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after NotPrimaryOrSecondary", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 13436, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after StaleShardVersion", - "runOnRequirements": [ - { - "maxServerVersion": "6.0.99" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 63, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after StaleEpoch", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 150, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after RetryChangeStream", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 234, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes after FailedToSatisfyReadPreference", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failGetMoreAfterCursorCheckout", - "mode": { - "times": 1 - }, - "data": { - "errorCode": 133, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream resumes if error contains ResumableChangeStreamError", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 50, - "closeConnection": false, - "errorLabels": [ - "ResumableChangeStreamError" - ] - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$exists": true - }, - "collection": "collection0" - }, - "commandName": "getMore", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "resumeAfter": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "change stream does not resume if error does not contain ResumableChangeStreamError", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 6, - "closeConnection": false - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "errorCode": 6 - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json b/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json deleted file mode 100644 index a59a81849..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams-showExpandedEvents.json +++ /dev/null @@ -1,518 +0,0 @@ -{ - "description": "change-streams-showExpandedEvents", - "schemaVersion": "1.7", - "runOnRequirements": [ - { - "minServerVersion": "6.0.0", - "topologies": [ - "replicaset", - "sharded-replicaset", - "sharded" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "killCursors" - ], - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "database1", - "client": "client0", - "databaseName": "database1" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "collection1" - } - }, - { - "database": { - "id": "shardedDb", - "client": "client0", - "databaseName": "shardedDb" - } - }, - { - "database": { - "id": "adminDb", - "client": "client0", - "databaseName": "admin" - } - }, - { - "collection": { - "id": "shardedCollection", - "database": "shardedDb", - "collectionName": "shardedCollection" - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "description": "when provided, showExpandedEvents is sent as a part of the aggregate command", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "showExpandedEvents": true - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "when omitted, showExpandedEvents is not sent as a part of the aggregate command", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "showExpandedEvents": { - "$$exists": false - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "when showExpandedEvents is true, new fields on change stream events are handled appropriately", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "foo" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "foo" - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "a": 1 - } - } - }, - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "name": "x_1" - } - }, - { - "name": "rename", - "object": "collection0", - "arguments": { - "to": "foo", - "dropTarget": true - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "collectionUUID": { - "$$exists": true - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "createIndexes", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "operationDescription": { - "$$exists": true - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "rename", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "to": { - "db": "database0", - "coll": "foo" - }, - "operationDescription": { - "dropTarget": { - "$$exists": true - }, - "to": { - "db": "database0", - "coll": "foo" - } - } - } - } - ] - }, - { - "description": "when showExpandedEvents is true, createIndex events are reported", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "operationType": { - "$ne": "create" - } - } - } - ], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "name": "x_1" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "createIndexes" - } - } - ] - }, - { - "description": "when showExpandedEvents is true, dropIndexes events are reported", - "operations": [ - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "name": "x_1" - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "dropIndex", - "object": "collection0", - "arguments": { - "name": "x_1" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "dropIndexes" - } - } - ] - }, - { - "description": "when showExpandedEvents is true, create events are reported", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "foo" - } - }, - { - "name": "createChangeStream", - "object": "database0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "foo" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "create" - } - } - ] - }, - { - "description": "when showExpandedEvents is true, create events on views are reported", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "foo" - } - }, - { - "name": "createChangeStream", - "object": "database0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "foo", - "viewOn": "testName" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "create" - } - } - ] - }, - { - "description": "when showExpandedEvents is true, modify events are reported", - "operations": [ - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "name": "x_2" - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "command": { - "collMod": "collection0" - }, - "commandName": "collMod" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "modify" - } - } - ] - }, - { - "description": "when showExpandedEvents is true, shardCollection events are reported", - "runOnRequirements": [ - { - "topologies": [ - "sharded-replicaset", - "sharded" - ] - } - ], - "operations": [ - { - "name": "dropCollection", - "object": "shardedDb", - "arguments": { - "collection": "shardedCollection" - } - }, - { - "name": "createCollection", - "object": "shardedDb", - "arguments": { - "collection": "shardedCollection" - } - }, - { - "name": "createChangeStream", - "object": "shardedCollection", - "arguments": { - "pipeline": [], - "showExpandedEvents": true - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "runCommand", - "object": "adminDb", - "arguments": { - "command": { - "shardCollection": "shardedDb.shardedCollection", - "key": { - "_id": 1 - } - }, - "commandName": "shardCollection" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "shardCollection" - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/change-streams/change-streams.json b/tests/UnifiedSpecTests/change-streams/change-streams.json deleted file mode 100644 index c8b60ed4e..000000000 --- a/tests/UnifiedSpecTests/change-streams/change-streams.json +++ /dev/null @@ -1,1795 +0,0 @@ -{ - "description": "change-streams", - "schemaVersion": "1.7", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "topologies": [ - "replicaset" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "killCursors" - ], - "useMultipleMongoses": false - } - }, - { - "client": { - "id": "globalClient", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "database1", - "client": "client0", - "databaseName": "database1" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "collection1" - } - }, - { - "database": { - "id": "globalDatabase0", - "client": "globalClient", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "globalCollection0", - "database": "globalDatabase0", - "collectionName": "collection0" - } - }, - { - "database": { - "id": "globalDatabase1", - "client": "globalClient", - "databaseName": "database1" - } - }, - { - "collection": { - "id": "globalCollection1", - "database": "globalDatabase1", - "collectionName": "collection1" - } - }, - { - "collection": { - "id": "globalDb1Collection0", - "database": "globalDatabase1", - "collectionName": "collection0" - } - }, - { - "collection": { - "id": "globalDb0Collection1", - "database": "globalDatabase0", - "collectionName": "collection1" - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [] - } - ], - "tests": [ - { - "description": "Test array truncation", - "runOnRequirements": [ - { - "minServerVersion": "4.7" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1, - "array": [ - "foo", - { - "a": "bar" - }, - 1, - 2, - 3 - ] - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "array": [ - "foo", - { - "a": "bar" - } - ] - } - } - ] - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": {}, - "removedFields": [], - "truncatedArrays": [ - { - "field": "array", - "newSize": 2 - } - ] - } - } - } - ] - }, - { - "description": "Test with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "comment": { - "name": "test1" - } - }, - "saveResultAsEntity": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "pipeline": [ - { - "$changeStream": {} - } - ], - "comment": { - "name": "test1" - } - } - } - } - ] - } - ] - }, - { - "description": "Test with document comment - pre 4.4", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "comment": { - "name": "test1" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "pipeline": [ - { - "$changeStream": {} - } - ], - "comment": { - "name": "test1" - } - } - } - } - ] - } - ] - }, - { - "description": "Test with string comment", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "comment": "comment" - }, - "saveResultAsEntity": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "pipeline": [ - { - "$changeStream": {} - } - ], - "comment": "comment" - } - } - } - ] - } - ] - }, - { - "description": "Test that comment is set on getMore", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "comment": { - "key": "value" - } - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "pipeline": [ - { - "$changeStream": {} - } - ], - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "collection0", - "documents": [ - { - "_id": 1, - "a": 1 - } - ] - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": { - "key": "value" - } - }, - "commandName": "getMore", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test that comment is not set on getMore - pre 4.4", - "runOnRequirements": [ - { - "maxServerVersion": "4.3.99" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "comment": "comment" - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "pipeline": [ - { - "$changeStream": {} - } - ], - "comment": "comment" - } - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "collection0", - "documents": [ - { - "_id": 1, - "a": 1 - } - ] - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "collection0", - "comment": { - "$$exists": false - } - }, - "commandName": "getMore", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "to field is set in a rename change event", - "runOnRequirements": [ - { - "minServerVersion": "4.0.1" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "collection1" - } - }, - { - "name": "rename", - "object": "collection0", - "arguments": { - "to": "collection1" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "rename", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "to": { - "db": "database0", - "coll": "collection1" - } - } - } - ] - }, - { - "description": "Test unknown operationType MUST NOT err", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "operationType": "addedInFutureMongoDBVersion", - "ns": 1 - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "addedInFutureMongoDBVersion", - "ns": { - "db": "database0", - "coll": "collection0" - } - } - } - ] - }, - { - "description": "Test newField added in response MUST NOT err", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "operationType": 1, - "ns": 1, - "newField": "newFieldValue" - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "newField": "newFieldValue" - } - } - ] - }, - { - "description": "Test new structure in ns document MUST NOT err", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "maxServerVersion": "5.2.99" - }, - { - "minServerVersion": "6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "operationType": "insert", - "ns.viewOn": "db.coll" - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "viewOn": "db.coll" - } - } - } - ] - }, - { - "description": "Test modified structure in ns document MUST NOT err", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "operationType": "insert", - "ns": { - "db": "$ns.db", - "coll": "$ns.coll", - "viewOn": "db.coll" - } - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0", - "viewOn": "db.coll" - } - } - } - ] - }, - { - "description": "Test server error on projecting out _id", - "runOnRequirements": [ - { - "minServerVersion": "4.2" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0 - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectError": { - "errorCode": 280, - "errorCodeName": "ChangeStreamFatalError", - "errorLabelsContain": [ - "NonResumableChangeStreamError" - ] - } - } - ] - }, - { - "description": "Test projection in change stream returns expected fields", - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "optype": "$operationType", - "ns": 1, - "newField": "value" - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "optype": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "newField": "value" - } - } - ] - }, - { - "description": "$changeStream must be the first stage in a change stream pipeline sent to the server", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "The server returns change stream responses in the specified server response format", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "_id": { - "$$exists": true - }, - "documentKey": { - "$$exists": true - }, - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - } - ] - }, - { - "description": "Executing a watch helper on a Collection results in notifications for changes to the specified collection", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalDb0Collection1", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "globalDb1Collection0", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "z": 3, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Change Stream should allow valid aggregate pipeline stages", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "fullDocument.z": 3 - } - } - ] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "z": 3, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - }, - { - "$match": { - "fullDocument.z": 3 - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Executing a watch helper on a Database results in notifications for changes to all collections in the specified database.", - "runOnRequirements": [ - { - "minServerVersion": "3.8.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "database0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalDb0Collection1", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "globalDb1Collection0", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection1" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "z": 3, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Executing a watch helper on a MongoClient results in notifications for changes to all collections in all databases in the cluster.", - "runOnRequirements": [ - { - "minServerVersion": "3.8.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "client0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalDb0Collection1", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "globalDb1Collection0", - "arguments": { - "document": { - "y": 2 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "z": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection1" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database1", - "coll": "collection0" - }, - "fullDocument": { - "y": 2, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "z": 3, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "admin" - } - } - ] - } - ] - }, - { - "description": "Test insert, update, replace, and delete event types", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "updateOne", - "object": "globalCollection0", - "arguments": { - "filter": { - "x": 1 - }, - "update": { - "$set": { - "x": 2 - } - } - } - }, - { - "name": "replaceOne", - "object": "globalCollection0", - "arguments": { - "filter": { - "x": 2 - }, - "replacement": { - "x": 3 - } - } - }, - { - "name": "deleteOne", - "object": "globalCollection0", - "arguments": { - "filter": { - "x": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "update", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "updateDescription": { - "updatedFields": { - "x": 2 - }, - "removedFields": [], - "truncatedArrays": { - "$$unsetOrMatches": { - "$$exists": true - } - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "replace", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 3, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "delete", - "ns": { - "db": "database0", - "coll": "collection0" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test rename and invalidate event types", - "runOnRequirements": [ - { - "minServerVersion": "4.0.1" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "collection1" - } - }, - { - "name": "rename", - "object": "globalCollection0", - "arguments": { - "to": "collection1" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "rename", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "to": { - "db": "database0", - "coll": "collection1" - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "invalidate" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test drop and invalidate event types", - "runOnRequirements": [ - { - "minServerVersion": "4.0.1" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "collection0" - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "drop", - "ns": { - "db": "database0", - "coll": "collection0" - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "invalidate" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": {}, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test consecutive resume", - "runOnRequirements": [ - { - "minServerVersion": "4.1.7" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "globalClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [], - "batchSize": 1 - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 2 - } - } - }, - { - "name": "insertOne", - "object": "globalCollection0", - "arguments": { - "document": { - "x": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 1, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 2, - "_id": { - "$$exists": true - } - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "fullDocument": { - "x": 3, - "_id": { - "$$exists": true - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "cursor": { - "batchSize": 1 - }, - "pipeline": [ - { - "$changeStream": {} - } - ] - }, - "commandName": "aggregate", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "Test wallTime field is set in a change event", - "runOnRequirements": [ - { - "minServerVersion": "6.0.0" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "database0", - "coll": "collection0" - }, - "wallTime": { - "$$exists": true - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json b/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json deleted file mode 100644 index f70bc572a..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/addKeyAltName.json +++ /dev/null @@ -1,609 +0,0 @@ -{ - "description": "addKeyAltName", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "add keyAltName to non-existent data key", - "operations": [ - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "AAAjYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "new_key_alt_name" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "AAAjYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "new_key_alt_name" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - }, - { - "description": "add new keyAltName to data key with no keyAltNames", - "operations": [ - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "projection": { - "_id": 0, - "keyAltNames": 1 - } - }, - "expectResult": [ - { - "keyAltNames": [ - "local_key" - ] - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "local_key" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "add existing keyAltName to existing data key", - "operations": [ - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "projection": { - "_id": 0, - "keyAltNames": 1 - } - }, - "expectResult": [ - { - "keyAltNames": [ - "local_key" - ] - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "local_key" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "local_key" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "add new keyAltName to data key with keyAltNames", - "operations": [ - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "addKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "another_name" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0, - "keyAltNames": "$keyAltNames" - } - }, - { - "$unwind": "$keyAltNames" - }, - { - "$sort": { - "keyAltNames": 1 - } - } - ] - }, - "expectResult": [ - { - "keyAltNames": "another_name" - }, - { - "keyAltNames": "local_key" - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "local_key" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": { - "$addToSet": { - "keyAltNames": "another_name" - } - }, - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "aggregate" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json b/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json deleted file mode 100644 index 2344a61a9..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/createDataKey-kms_providers-invalid.json +++ /dev/null @@ -1,119 +0,0 @@ -{ - "description": "createDataKey-kms_providers-invalid", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - } - } - } - } - } - ], - "tests": [ - { - "description": "create data key without required master key fields", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "aws", - "opts": { - "masterKey": {} - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "create data key with invalid master key field", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "masterKey": { - "invalid": 1 - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "create data key with invalid master key", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "aws", - "opts": { - "masterKey": { - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "invalid" - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json b/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json deleted file mode 100644 index 110c726f9..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/createDataKey.json +++ /dev/null @@ -1,711 +0,0 @@ -{ - "description": "createDataKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [] - } - ], - "tests": [ - { - "description": "create data key with AWS KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "aws", - "opts": { - "masterKey": { - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with Azure KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "azure", - "opts": { - "masterKey": { - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with GCP KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "gcp", - "opts": { - "masterKey": { - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with KMIP KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "kmip" - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with local KMS provider", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local" - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$exists": true - }, - "masterKey": { - "provider": "local" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with no keyAltName", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [] - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": { - "$$exists": false - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with single keyAltName", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [ - "local_key" - ] - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with multiple keyAltNames", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [ - "abc", - "def" - ] - } - }, - "expectResult": { - "$$type": "binData" - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 0, - "keyAltNames": 1 - } - }, - { - "$unwind": "$keyAltNames" - }, - { - "$sort": { - "keyAltNames": 1 - } - } - ] - }, - "expectResult": [ - { - "keyAltNames": "abc" - }, - { - "keyAltNames": "def" - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": { - "$$type": "array" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "aggregate" - } - } - ] - } - ] - }, - { - "description": "create datakey with custom key material", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyMaterial": { - "$binary": { - "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", - "subType": "00" - } - } - } - }, - "expectResult": { - "$$type": "binData" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "insert": "datakeys", - "documents": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "create datakey with invalid custom key material (too short)", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyMaterial": { - "$binary": { - "base64": "a2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFsa2V5X21hdGVyaWFs", - "subType": "00" - } - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json b/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json deleted file mode 100644 index 3a10fb082..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/deleteKey.json +++ /dev/null @@ -1,557 +0,0 @@ -{ - "description": "deleteKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "delete non-existent data key", - "operations": [ - { - "name": "deleteKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "deletedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "delete": "datakeys", - "deletes": [ - { - "q": { - "_id": { - "$binary": { - "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "limit": 1 - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - }, - { - "description": "delete existing AWS data key", - "operations": [ - { - "name": "deleteKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "delete": "datakeys", - "deletes": [ - { - "q": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "limit": 1 - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - }, - { - "description": "delete existing local data key", - "operations": [ - { - "name": "deleteKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "delete": "datakeys", - "deletes": [ - { - "q": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "limit": 1 - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - ] - } - ] - }, - { - "description": "delete existing data key twice", - "operations": [ - { - "name": "deleteKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "deletedCount": 1 - } - }, - { - "name": "deleteKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "deletedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "delete": "datakeys", - "deletes": [ - { - "q": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "limit": 1 - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "delete": "datakeys", - "deletes": [ - { - "q": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "limit": 1 - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKey.json b/tests/UnifiedSpecTests/client-side-encryption/getKey.json deleted file mode 100644 index 2ea3fe735..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/getKey.json +++ /dev/null @@ -1,319 +0,0 @@ -{ - "description": "getKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "get non-existent data key", - "operations": [ - { - "name": "getKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "_id": { - "$binary": { - "base64": "AAAzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "get existing AWS data key", - "operations": [ - { - "name": "getKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - } - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "get existing local data key", - "operations": [ - { - "name": "getKey", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json b/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json deleted file mode 100644 index 2505abc16..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/getKeyByAltName.json +++ /dev/null @@ -1,289 +0,0 @@ -{ - "description": "getKeyByAltName", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "get non-existent data key", - "operations": [ - { - "name": "getKeyByAltName", - "object": "clientEncryption0", - "arguments": { - "keyAltName": "does_not_exist" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": "does_not_exist" - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "get existing AWS data key", - "operations": [ - { - "name": "getKeyByAltName", - "object": "clientEncryption0", - "arguments": { - "keyAltName": "aws_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": "aws_key" - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "get existing local data key", - "operations": [ - { - "name": "getKeyByAltName", - "object": "clientEncryption0", - "arguments": { - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": "local_key" - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/getKeys.json b/tests/UnifiedSpecTests/client-side-encryption/getKeys.json deleted file mode 100644 index d94471235..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/getKeys.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "description": "getKeys", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [] - } - ], - "tests": [ - { - "description": "getKeys with zero key documents", - "operations": [ - { - "name": "getKeys", - "object": "clientEncryption0", - "expectResult": [] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "getKeys with single key documents", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local", - "opts": { - "keyAltNames": [ - "abc" - ] - } - }, - "expectResult": { - "$$type": "binData" - } - }, - { - "name": "getKeys", - "object": "clientEncryption0", - "expectResult": [ - { - "_id": { - "$$type": "binData" - }, - "keyAltNames": [ - "abc" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "getKeys with many key documents", - "operations": [ - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local" - }, - "expectResult": { - "$$type": "binData" - } - }, - { - "name": "createDataKey", - "object": "clientEncryption0", - "arguments": { - "kmsProvider": "local" - }, - "expectResult": { - "$$type": "binData" - } - }, - { - "name": "getKeys", - "object": "clientEncryption0", - "expectResult": [ - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - }, - { - "_id": { - "$$type": "binData" - }, - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": { - "$$type": "int" - }, - "masterKey": { - "$$type": "object" - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json b/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json deleted file mode 100644 index 1b7077077..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/removeKeyAltName.json +++ /dev/null @@ -1,672 +0,0 @@ -{ - "description": "removeKeyAltName", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "remove keyAltName from non-existent data key", - "operations": [ - { - "name": "removeKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "AAAjYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "does_not_exist" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "AAAjYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": [ - { - "$set": { - "keyAltNames": { - "$cond": [ - { - "$eq": [ - "$keyAltNames", - [ - "does_not_exist" - ] - ] - }, - "$$REMOVE", - { - "$filter": { - "input": "$keyAltNames", - "cond": { - "$ne": [ - "$$this", - "does_not_exist" - ] - } - } - } - ] - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - }, - { - "description": "remove non-existent keyAltName from existing data key", - "operations": [ - { - "name": "removeKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "does_not_exist" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": [ - { - "$set": { - "keyAltNames": { - "$cond": [ - { - "$eq": [ - "$keyAltNames", - [ - "does_not_exist" - ] - ] - }, - "$$REMOVE", - { - "$filter": { - "input": "$keyAltNames", - "cond": { - "$ne": [ - "$$this", - "does_not_exist" - ] - } - } - } - ] - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "datakeys", - "databaseName": "keyvault", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ] - }, - { - "description": "remove an existing keyAltName from an existing data key", - "operations": [ - { - "name": "removeKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "alternate_name" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "projection": { - "_id": 0, - "keyAltNames": 1 - } - }, - "expectResult": [ - { - "keyAltNames": [ - "local_key" - ] - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": [ - { - "$set": { - "keyAltNames": { - "$cond": [ - { - "$eq": [ - "$keyAltNames", - [ - "alternate_name" - ] - ] - }, - "$$REMOVE", - { - "$filter": { - "input": "$keyAltNames", - "cond": { - "$ne": [ - "$$this", - "alternate_name" - ] - } - } - } - ] - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "remove the last keyAltName from an existing data key", - "operations": [ - { - "name": "removeKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "alternate_name" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "alternate_name", - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - }, - { - "name": "removeKeyAltName", - "object": "clientEncryption0", - "arguments": { - "id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltName": "local_key" - }, - "expectResult": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$$type": "binData" - }, - "creationDate": { - "$$type": "date" - }, - "updateDate": { - "$$type": "date" - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": [ - { - "$set": { - "keyAltNames": { - "$cond": [ - { - "$eq": [ - "$keyAltNames", - [ - "alternate_name" - ] - ] - }, - "$$REMOVE", - { - "$filter": { - "input": "$keyAltNames", - "cond": { - "$ne": [ - "$$this", - "alternate_name" - ] - } - } - } - ] - } - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "findAndModify": "datakeys", - "query": { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - } - }, - "update": [ - { - "$set": { - "keyAltNames": { - "$cond": [ - { - "$eq": [ - "$keyAltNames", - [ - "local_key" - ] - ] - }, - "$$REMOVE", - { - "$filter": { - "input": "$keyAltNames", - "cond": { - "$ne": [ - "$$this", - "local_key" - ] - } - } - } - ] - } - } - } - ] - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json deleted file mode 100644 index 4c7d4e804..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-decrypt_failure.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "description": "rewrapManyDataKey-decrypt_failure", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-2" - } - } - ] - } - ], - "tests": [ - { - "description": "rewrap data key that fails during decryption due to invalid masterKey", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {}, - "opts": { - "provider": "local" - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json deleted file mode 100644 index cd2d20c25..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey-encrypt_failure.json +++ /dev/null @@ -1,250 +0,0 @@ -{ - "description": "rewrapManyDataKey-encrypt_failure", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "rewrap with invalid masterKey for AWS KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {}, - "opts": { - "provider": "aws", - "masterKey": { - "key": "arn:aws:kms:us-east-2:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-2" - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with invalid masterKey for Azure KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {}, - "opts": { - "provider": "azure", - "masterKey": { - "keyVaultEndpoint": "invalid-vault-csfle.vault.azure.net", - "keyName": "invalid-name-csfle" - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with invalid masterKey for GCP KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {}, - "opts": { - "provider": "gcp", - "masterKey": { - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "invalid-ring-csfle", - "keyName": "invalid-name-csfle" - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json b/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json deleted file mode 100644 index 89860de0c..000000000 --- a/tests/UnifiedSpecTests/client-side-encryption/rewrapManyDataKey.json +++ /dev/null @@ -1,1475 +0,0 @@ -{ - "description": "rewrapManyDataKey", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "keyvault" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "datakeys" - } - } - ], - "initialData": [ - { - "databaseName": "keyvault", - "collectionName": "datakeys", - "documents": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "aws_key" - ], - "keyMaterial": { - "$binary": { - "base64": "AQICAHhQNmWG2CzOm1dq3kWLM+iDUZhEqnhJwH9wZVpuZ94A8gFXJqbF0Fy872MD7xl56D/2AAAAwjCBvwYJKoZIhvcNAQcGoIGxMIGuAgEAMIGoBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDO7HPisPUlGzaio9vgIBEIB7/Qow46PMh/8JbEUbdXgTGhLfXPE+KIVW7T8s6YEMlGiRvMu7TV0QCIUJlSHPKZxzlJ2iwuz5yXeOag+EdY+eIQ0RKrsJ3b8UTisZYzGjfzZnxUKLzLoeXremtRCm3x47wCuHKd1dhh6FBbYt5TL2tDaj+vL2GBrKat2L", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "YXp1cmVhenVyZWF6dXJlYQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "azure_key" - ], - "keyMaterial": { - "$binary": { - "base64": "pr01l7qDygUkFE/0peFwpnNlv3iIy8zrQK38Q9i12UCN2jwZHDmfyx8wokiIKMb9kAleeY+vnt3Cf1MKu9kcDmI+KxbNDd+V3ytAAGzOVLDJr77CiWjF9f8ntkXRHrAY9WwnVDANYkDwXlyU0Y2GQFTiW65jiQhUtYLYH63Tk48SsJuQvnWw1Q+PzY8ga+QeVec8wbcThwtm+r2IHsCFnc72Gv73qq7weISw+O4mN08z3wOp5FOS2ZM3MK7tBGmPdBcktW7F8ODGsOQ1FU53OrWUnyX2aTi2ftFFFMWVHqQo7EYuBZHru8RRODNKMyQk0BFfKovAeTAVRv9WH9QU7g==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", - "subType": "04" - } - }, - "keyAltNames": [ - "gcp_key" - ], - "keyMaterial": { - "$binary": { - "base64": "CiQAIgLj0USbQtof/pYRLQO96yg/JEtZbD1UxKueaC37yzT5tTkSiQEAhClWB5ZCSgzHgxv8raWjNB4r7e8ePGdsmSuYTYmLC5oHHS/BdQisConzNKFaobEQZHamTCjyhy5NotKF8MWoo+dyfQApwI29+vAGyrUIQCXzKwRnNdNQ+lb3vJtS5bqvLTvSxKHpVca2kqyC9nhonV+u4qru5Q2bAqUgVFc8fL4pBuvlowZFTQ==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "a21pcGttaXBrbWlwa21pcA==", - "subType": "04" - } - }, - "keyAltNames": [ - "kmip_key" - ], - "keyMaterial": { - "$binary": { - "base64": "CklVctHzke4mcytd0TxGqvepkdkQN8NUF4+jV7aZQITAKdz6WjdDpq3lMt9nSzWGG2vAEfvRb3mFEVjV57qqGqxjq2751gmiMRHXz0btStbIK3mQ5xbY9kdye4tsixlCryEwQONr96gwlwKKI9Nubl9/8+uRF6tgYjje7Q7OjauEf1SrJwKcoQ3WwnjZmEqAug0kImCpJ/irhdqPzivRiA==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "kmip", - "keyId": "1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "keyAltNames": [ - "local_key" - ], - "keyMaterial": { - "$binary": { - "base64": "ABKBldDEoDW323yejOnIRk6YQmlD9d3eQthd16scKL75nz2LjNL9fgPDZWrFFOlqlhMCFaSrNJfGrFUjYk5JFDO7soG5Syb50k1niJoKg4ilsj0L4mpimFUtTpOr2nzZOeQtvAksEXc7gsFgq8gV7t/U3lsaXPY7I0t42DfSE8EGlPdxRjFdHnxh+OR8h7U9b8Qs5K5UuhgyeyxaBZ1Hgw==", - "subType": "00" - } - }, - "creationDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "updateDate": { - "$date": { - "$numberLong": "1641024000000" - } - }, - "status": 1, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "tests": [ - { - "description": "no keys to rewrap due to no filter matches", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": "no_matching_keys" - }, - "opts": { - "provider": "local" - } - }, - "expectResult": { - "bulkWriteResult": { - "$$exists": false - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": "no_matching_keys" - }, - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new AWS KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "aws_key" - } - }, - "opts": { - "provider": "aws", - "masterKey": { - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "aws_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/061334ae-07a8-4ceb-a813-8135540e837d", - "region": "us-east-1" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new Azure KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "azure_key" - } - }, - "opts": { - "provider": "azure", - "masterKey": { - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "azure_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new GCP KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "gcp_key" - } - }, - "opts": { - "provider": "gcp", - "masterKey": { - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "gcp_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new KMIP KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "kmip_key" - } - }, - "opts": { - "provider": "kmip" - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "kmip_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "kmip", - "keyId": { - "$$type": "string" - } - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with new local KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": { - "keyAltNames": { - "$ne": "local_key" - } - }, - "opts": { - "provider": "local" - } - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 4, - "modifiedCount": 4, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": { - "keyAltNames": { - "$ne": "local_key" - } - }, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "provider": "local" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - } - ] - } - ] - }, - { - "description": "rewrap with current KMS provider", - "operations": [ - { - "name": "rewrapManyDataKey", - "object": "clientEncryption0", - "arguments": { - "filter": {} - }, - "expectResult": { - "bulkWriteResult": { - "insertedCount": 0, - "matchedCount": 5, - "modifiedCount": 5, - "deletedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "projection": { - "masterKey": 1 - }, - "sort": { - "keyAltNames": 1 - } - }, - "expectResult": [ - { - "_id": { - "$binary": { - "base64": "YXdzYXdzYXdzYXdzYXdzYQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "aws", - "key": "arn:aws:kms:us-east-1:579766882180:key/89fcc2c4-08b0-4bd9-9f25-e30687b580d0", - "region": "us-east-1" - } - }, - { - "_id": { - "$binary": { - "base64": "YXp1cmVhenVyZWF6dXJlYQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "azure", - "keyVaultEndpoint": "key-vault-csfle.vault.azure.net", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "Z2NwZ2NwZ2NwZ2NwZ2NwZw==", - "subType": "04" - } - }, - "masterKey": { - "provider": "gcp", - "projectId": "devprod-drivers", - "location": "global", - "keyRing": "key-ring-csfle", - "keyName": "key-name-csfle" - } - }, - { - "_id": { - "$binary": { - "base64": "a21pcGttaXBrbWlwa21pcA==", - "subType": "04" - } - }, - "masterKey": { - "provider": "kmip", - "keyId": "1" - } - }, - { - "_id": { - "$binary": { - "base64": "bG9jYWxrZXlsb2NhbGtleQ==", - "subType": "04" - } - }, - "masterKey": { - "provider": "local" - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "find": "datakeys", - "filter": {}, - "readConcern": { - "level": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "databaseName": "keyvault", - "command": { - "update": "datakeys", - "ordered": true, - "updates": [ - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$$type": "binData" - } - }, - "u": { - "$set": { - "masterKey": { - "$$type": "object" - }, - "keyMaterial": { - "$$type": "binData" - } - }, - "$currentDate": { - "updateDate": true - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/collection-management/clustered-indexes.json b/tests/UnifiedSpecTests/collection-management/clustered-indexes.json deleted file mode 100644 index 9db5ff06d..000000000 --- a/tests/UnifiedSpecTests/collection-management/clustered-indexes.json +++ /dev/null @@ -1,291 +0,0 @@ -{ - "description": "clustered-indexes", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "5.3", - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "ci-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "ci-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "createCollection with clusteredIndex", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "ci-tests", - "collectionName": "test" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "ci-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - }, - "databaseName": "ci-tests" - } - } - ] - } - ] - }, - { - "description": "listCollections includes clusteredIndex", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - } - }, - { - "name": "listCollections", - "object": "database0", - "arguments": { - "filter": { - "name": { - "$eq": "test" - } - } - }, - "expectResult": [ - { - "name": "test", - "options": { - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index", - "v": { - "$$type": [ - "int", - "long" - ] - } - } - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "ci-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - }, - "databaseName": "ci-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "listCollections": 1, - "filter": { - "name": { - "$eq": "test" - } - } - }, - "databaseName": "ci-tests" - } - } - ] - } - ] - }, - { - "description": "listIndexes returns the index", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - } - }, - { - "name": "listIndexes", - "object": "collection0", - "expectResult": [ - { - "key": { - "_id": 1 - }, - "name": "test index", - "clustered": true, - "unique": true, - "v": { - "$$type": [ - "int", - "long" - ] - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "ci-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "clusteredIndex": { - "key": { - "_id": 1 - }, - "unique": true, - "name": "test index" - } - }, - "databaseName": "ci-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "listIndexes": "test" - }, - "databaseName": "ci-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json b/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json deleted file mode 100644 index f488deacd..000000000 --- a/tests/UnifiedSpecTests/collection-management/createCollection-pre_and_post_images.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "description": "createCollection-pre_and_post_images", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "6.0", - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "papi-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "createCollection with changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "papi-tests", - "collectionName": "test" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "papi-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - }, - "databaseName": "papi-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json b/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json deleted file mode 100644 index aea71eb08..000000000 --- a/tests/UnifiedSpecTests/collection-management/modifyCollection-errorResponse.json +++ /dev/null @@ -1,118 +0,0 @@ -{ - "description": "modifyCollection-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "collMod-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "collMod-tests", - "documents": [ - { - "_id": 1, - "x": 1 - }, - { - "_id": 2, - "x": 1 - } - ] - } - ], - "tests": [ - { - "description": "modifyCollection prepareUnique violations are accessible", - "runOnRequirements": [ - { - "minServerVersion": "5.2" - } - ], - "operations": [ - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - } - } - }, - { - "name": "modifyCollection", - "object": "database0", - "arguments": { - "collection": "test", - "index": { - "keyPattern": { - "x": 1 - }, - "prepareUnique": true - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 3, - "x": 1 - } - }, - "expectError": { - "errorCode": 11000 - } - }, - { - "name": "modifyCollection", - "object": "database0", - "arguments": { - "collection": "test", - "index": { - "keyPattern": { - "x": 1 - }, - "unique": true - } - }, - "expectError": { - "isClientError": false, - "errorCode": 359, - "errorResponse": { - "violations": [ - { - "ids": [ - 1, - 2 - ] - } - ] - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json b/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json deleted file mode 100644 index 8026faeb1..000000000 --- a/tests/UnifiedSpecTests/collection-management/modifyCollection-pre_and_post_images.json +++ /dev/null @@ -1,111 +0,0 @@ -{ - "description": "modifyCollection-pre_and_post_images", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "6.0", - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "papi-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "modifyCollection to changeStreamPreAndPostImages enabled", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "papi-tests", - "collectionName": "test" - } - }, - { - "name": "modifyCollection", - "object": "database0", - "arguments": { - "collection": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "papi-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "changeStreamPreAndPostImages": { - "enabled": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "collMod": "test", - "changeStreamPreAndPostImages": { - "enabled": true - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/collection-management/timeseries-collection.json b/tests/UnifiedSpecTests/collection-management/timeseries-collection.json deleted file mode 100644 index b5638fd36..000000000 --- a/tests/UnifiedSpecTests/collection-management/timeseries-collection.json +++ /dev/null @@ -1,255 +0,0 @@ -{ - "description": "timeseries-collection", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "ts-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "ts-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "createCollection with all options", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "expireAfterSeconds": 604800, - "timeseries": { - "timeField": "time", - "metaField": "meta", - "granularity": "minutes" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "ts-tests", - "collectionName": "test" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "ts-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "expireAfterSeconds": 604800, - "timeseries": { - "timeField": "time", - "metaField": "meta", - "granularity": "minutes" - } - }, - "databaseName": "ts-tests" - } - } - ] - } - ] - }, - { - "description": "insertMany with duplicate ids", - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "test", - "expireAfterSeconds": 604800, - "timeseries": { - "timeField": "time", - "metaField": "meta", - "granularity": "minutes" - } - } - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "ts-tests", - "collectionName": "test" - } - }, - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630482" - } - } - }, - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630483" - } - } - } - ] - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "sort": { - "time": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630482" - } - } - }, - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630483" - } - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test" - }, - "databaseName": "ts-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "expireAfterSeconds": 604800, - "timeseries": { - "timeField": "time", - "metaField": "meta", - "granularity": "minutes" - } - }, - "databaseName": "ts-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630482" - } - } - }, - { - "_id": 1, - "time": { - "$date": { - "$numberLong": "1552949630483" - } - } - } - ] - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": {}, - "sort": { - "time": 1 - } - }, - "databaseName": "ts-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json b/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json deleted file mode 100644 index 49c728442..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/bulkWrite.json +++ /dev/null @@ -1,154 +0,0 @@ -{ - "description": "bulkWrite", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "A successful mixed bulk write", - "operations": [ - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 4, - "x": 44 - } - } - }, - { - "updateOne": { - "filter": { - "_id": 3 - }, - "update": { - "$set": { - "x": 333 - } - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4, - "x": 44 - } - ], - "ordered": true - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "$set": { - "x": 333 - } - }, - "upsert": { - "$$unsetOrMatches": false - }, - "multi": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "update" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/command.json b/tests/UnifiedSpecTests/command-monitoring/command.json deleted file mode 100644 index c28af95fe..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/command.json +++ /dev/null @@ -1,83 +0,0 @@ -{ - "description": "command", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "A successful command", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "command": { - "ping": 1 - }, - "commandName": "ping" - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "commandName": "ping", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1 - }, - "commandName": "ping" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/deleteMany.json b/tests/UnifiedSpecTests/command-monitoring/deleteMany.json deleted file mode 100644 index 78ebad1f9..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/deleteMany.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "description": "deleteMany", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "A successful deleteMany", - "operations": [ - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 0 - } - ], - "ordered": true - }, - "commandName": "delete", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 2 - }, - "commandName": "delete" - } - } - ] - } - ] - }, - { - "description": "A successful deleteMany with write errors", - "operations": [ - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$unsupported": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$unsupported": 1 - } - }, - "limit": 0 - } - ], - "ordered": true - }, - "commandName": "delete", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "delete" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/deleteOne.json b/tests/UnifiedSpecTests/command-monitoring/deleteOne.json deleted file mode 100644 index 2420794fe..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/deleteOne.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "description": "deleteOne", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "A successful deleteOne", - "operations": [ - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 1 - } - ], - "ordered": true - }, - "commandName": "delete", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "delete" - } - } - ] - } - ] - }, - { - "description": "A successful deleteOne with write errors", - "operations": [ - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$unsupported": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": { - "$unsupported": 1 - } - }, - "limit": 1 - } - ], - "ordered": true - }, - "commandName": "delete", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "delete" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/find.json b/tests/UnifiedSpecTests/command-monitoring/find.json deleted file mode 100644 index 4b5f45ae9..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/find.json +++ /dev/null @@ -1,550 +0,0 @@ -{ - "description": "find", - "schemaVersion": "1.1", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "_yamlAnchors": { - "namespace": "command-monitoring-tests.test" - }, - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "tests": [ - { - "description": "A successful find with no options", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": 1 - } - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - } - ] - } - }, - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "A successful find with options", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "sort": { - "x": -1 - }, - "projection": { - "_id": 0, - "x": 1 - }, - "skip": 2, - "comment": "test", - "hint": { - "_id": 1 - }, - "max": { - "_id": 6 - }, - "maxTimeMS": 6000, - "min": { - "_id": 0 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gt": 1 - } - }, - "sort": { - "x": -1 - }, - "projection": { - "_id": 0, - "x": 1 - }, - "skip": 2, - "comment": "test", - "hint": { - "_id": 1 - }, - "max": { - "_id": 6 - }, - "maxTimeMS": 6000, - "min": { - "_id": 0 - } - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "x": 33 - }, - { - "x": 22 - } - ] - } - }, - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "A successful find with showRecordId and returnKey", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "showRecordId": true, - "returnKey": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "showRecordId": true, - "returnKey": true - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - }, - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "A successful find with a getMore", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3 - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3 - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$$type": [ - "int", - "long" - ] - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "test", - "batchSize": 3 - }, - "commandName": "getMore", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - }, - "commandName": "getMore" - } - } - ] - } - ] - }, - { - "description": "A successful find event with a getmore and the server kills the cursor (<= 4.4)", - "runOnRequirements": [ - { - "minServerVersion": "3.1", - "maxServerVersion": "4.4.99", - "topologies": [ - "single", - "replicaset" - ] - } - ], - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3, - "limit": 4 - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3, - "limit": 4 - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$$type": [ - "int", - "long" - ] - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "test", - "batchSize": 1 - }, - "commandName": "getMore", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - } - ] - } - }, - "commandName": "getMore" - } - } - ] - } - ] - }, - { - "description": "A failed find event", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "$or": true - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "$or": true - } - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandFailedEvent": { - "commandName": "find" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/insertMany.json b/tests/UnifiedSpecTests/command-monitoring/insertMany.json deleted file mode 100644 index a80a218c6..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/insertMany.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "description": "insertMany", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "A successful insertMany", - "operations": [ - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "ordered": true - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "A successful insertMany with write errors", - "operations": [ - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1, - "x": 11 - } - ], - "ordered": true - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "insert" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/insertOne.json b/tests/UnifiedSpecTests/command-monitoring/insertOne.json deleted file mode 100644 index 6ff732e41..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/insertOne.json +++ /dev/null @@ -1,144 +0,0 @@ -{ - "description": "insertOne", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "A successful insertOne", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 2, - "x": 22 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "ordered": true - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "A successful insertOne with write errors", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 1, - "x": 11 - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1, - "x": 11 - } - ], - "ordered": true - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "insert" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json b/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json deleted file mode 100644 index 141fbe584..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/pre-42-server-connection-id.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "description": "pre-42-server-connection-id", - "schemaVersion": "1.6", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "server-connection-id-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "databaseName": "server-connection-id-tests", - "collectionName": "coll", - "documents": [] - } - ], - "tests": [ - { - "description": "command events do not include server connection id", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "$or": true - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert", - "hasServerConnectionId": false - } - }, - { - "commandSucceededEvent": { - "commandName": "insert", - "hasServerConnectionId": false - } - }, - { - "commandStartedEvent": { - "commandName": "find", - "hasServerConnectionId": false - } - }, - { - "commandFailedEvent": { - "commandName": "find", - "hasServerConnectionId": false - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json b/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json deleted file mode 100644 index 0f85dc3e9..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/redacted-commands.json +++ /dev/null @@ -1,659 +0,0 @@ -{ - "description": "redacted-commands", - "schemaVersion": "1.5", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "auth": false - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent" - ], - "observeSensitiveCommands": true - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - } - ], - "tests": [ - { - "description": "authenticate", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "authenticate", - "command": { - "authenticate": 1, - "mechanism": "MONGODB-X509", - "user": "CN=myName,OU=myOrgUnit,O=myOrg,L=myLocality,ST=myState,C=myCountry", - "db": "$external" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "authenticate", - "command": { - "authenticate": { - "$$exists": false - }, - "mechanism": { - "$$exists": false - }, - "user": { - "$$exists": false - }, - "db": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "saslStart", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "saslStart", - "command": { - "saslStart": 1, - "payload": "definitely-invalid-payload", - "db": "admin" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "saslStart", - "command": { - "saslStart": { - "$$exists": false - }, - "payload": { - "$$exists": false - }, - "db": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "saslContinue", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "saslContinue", - "command": { - "saslContinue": 1, - "conversationId": 0, - "payload": "definitely-invalid-payload" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "saslContinue", - "command": { - "saslContinue": { - "$$exists": false - }, - "conversationId": { - "$$exists": false - }, - "payload": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "getnonce", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "getnonce", - "command": { - "getnonce": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "getnonce", - "command": { - "getnonce": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "getnonce", - "reply": { - "ok": { - "$$exists": false - }, - "nonce": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "createUser", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "createUser", - "command": { - "createUser": "private", - "pwd": {}, - "roles": [] - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "createUser", - "command": { - "createUser": { - "$$exists": false - }, - "pwd": { - "$$exists": false - }, - "roles": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "updateUser", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "updateUser", - "command": { - "updateUser": "private", - "pwd": {}, - "roles": [] - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "updateUser", - "command": { - "updateUser": { - "$$exists": false - }, - "pwd": { - "$$exists": false - }, - "roles": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "copydbgetnonce", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "copydbgetnonce", - "command": { - "copydbgetnonce": "private" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "copydbgetnonce", - "command": { - "copydbgetnonce": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "copydbsaslstart", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "copydbsaslstart", - "command": { - "copydbsaslstart": "private" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "copydbsaslstart", - "command": { - "copydbsaslstart": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "copydb", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "copydb", - "command": { - "copydb": "private" - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "copydb", - "command": { - "copydb": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "hello with speculative authenticate", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "legacy hello with speculative authenticate", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "hello without speculative authenticate is not redacted", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": true - } - } - } - } - ] - } - ] - }, - { - "description": "legacy hello without speculative authenticate is not redacted", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json b/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json deleted file mode 100644 index a8f27637f..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/server-connection-id.json +++ /dev/null @@ -1,101 +0,0 @@ -{ - "description": "server-connection-id", - "schemaVersion": "1.6", - "runOnRequirements": [ - { - "minServerVersion": "4.2" - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "server-connection-id-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "databaseName": "server-connection-id-tests", - "collectionName": "coll", - "documents": [] - } - ], - "tests": [ - { - "description": "command events include server connection id", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "find", - "object": "collection", - "arguments": { - "filter": { - "$or": true - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert", - "hasServerConnectionId": true - } - }, - { - "commandSucceededEvent": { - "commandName": "insert", - "hasServerConnectionId": true - } - }, - { - "commandStartedEvent": { - "commandName": "find", - "hasServerConnectionId": true - } - }, - { - "commandFailedEvent": { - "commandName": "find", - "hasServerConnectionId": true - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json b/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json deleted file mode 100644 index 4c16d6df1..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/unacknowledgedBulkWrite.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "description": "unacknowledgedBulkWrite", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "A successful unordered bulk write with an unacknowledged write concern", - "operations": [ - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": "unorderedBulkWriteInsertW0", - "x": 44 - } - } - } - ], - "ordered": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": "unorderedBulkWriteInsertW0", - "x": 44 - } - ], - "ordered": false, - "writeConcern": { - "w": 0 - } - }, - "commandName": "insert", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": { - "$$exists": false - } - }, - "commandName": "insert" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/updateMany.json b/tests/UnifiedSpecTests/command-monitoring/updateMany.json deleted file mode 100644 index b15434226..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/updateMany.json +++ /dev/null @@ -1,188 +0,0 @@ -{ - "description": "updateMany", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "A successful updateMany", - "operations": [ - { - "name": "updateMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": { - "$$unsetOrMatches": false - }, - "multi": true - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 2 - }, - "commandName": "update" - } - } - ] - } - ] - }, - { - "description": "A successful updateMany with write errors", - "operations": [ - { - "name": "updateMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$unsupported": { - "x": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$unsupported": { - "x": 1 - } - }, - "upsert": { - "$$unsetOrMatches": false - }, - "multi": true - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "update" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/command-monitoring/updateOne.json b/tests/UnifiedSpecTests/command-monitoring/updateOne.json deleted file mode 100644 index a0ae99e88..000000000 --- a/tests/UnifiedSpecTests/command-monitoring/updateOne.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "description": "updateOne", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "A successful updateOne", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": { - "$$unsetOrMatches": false - }, - "multi": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1 - }, - "commandName": "update" - } - } - ] - } - ] - }, - { - "description": "A successful updateOne with upsert where the upserted id is not an ObjectId", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 4 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "upsert": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "upsert": true, - "multi": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 1, - "upserted": [ - { - "index": 0, - "_id": 4 - } - ] - }, - "commandName": "update" - } - } - ] - } - ] - }, - { - "description": "A successful updateOne with write errors", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$unsupported": { - "x": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$unsupported": { - "x": 1 - } - }, - "upsert": { - "$$unsetOrMatches": false - }, - "multi": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "n": 0, - "writeErrors": { - "$$type": "array" - } - }, - "commandName": "update" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json b/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json deleted file mode 100644 index 2e54175b8..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-allowdiskuse.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "description": "aggregate-allowdiskuse", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Aggregate does not send allowDiskUse when value is not specified", - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": {} - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - } - ], - "allowDiskUse": { - "$$exists": false - } - }, - "commandName": "aggregate", - "databaseName": "crud-tests" - } - } - ] - } - ] - }, - { - "description": "Aggregate sends allowDiskUse false when false is specified", - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": {} - } - ], - "allowDiskUse": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - } - ], - "allowDiskUse": false - }, - "commandName": "aggregate", - "databaseName": "crud-tests" - } - } - ] - } - ] - }, - { - "description": "Aggregate sends allowDiskUse true when true is specified", - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": {} - } - ], - "allowDiskUse": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - } - ], - "allowDiskUse": true - }, - "commandName": "aggregate", - "databaseName": "crud-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-let.json b/tests/UnifiedSpecTests/crud/aggregate-let.json deleted file mode 100644 index 039900920..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-let.json +++ /dev/null @@ -1,376 +0,0 @@ -{ - "description": "aggregate-let", - "schemaVersion": "1.4", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - }, - { - "collectionName": "coll1", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Aggregate with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 0, - "x": "$$x", - "y": "$$y", - "rand": "$$rand" - } - } - ], - "let": { - "id": 1, - "x": "foo", - "y": { - "$literal": "$bar" - }, - "rand": { - "$rand": {} - } - } - }, - "expectResult": [ - { - "x": "foo", - "y": "$bar", - "rand": { - "$$type": "double" - } - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 0, - "x": "$$x", - "y": "$$y", - "rand": "$$rand" - } - } - ], - "let": { - "id": 1, - "x": "foo", - "y": { - "$literal": "$bar" - }, - "rand": { - "$rand": {} - } - } - } - } - } - ] - } - ] - }, - { - "description": "Aggregate with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "2.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "let": { - "x": "foo" - } - }, - "expectError": { - "errorContains": "unrecognized field 'let'", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "let": { - "x": "foo" - } - } - } - } - ] - } - ] - }, - { - "description": "Aggregate to collection with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll1" - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll1" - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Aggregate to collection with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "2.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll1" - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "unrecognized field 'let'", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll1" - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json b/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json deleted file mode 100644 index 6c7305fd9..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-merge-errorResponse.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "description": "aggregate-merge-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 1 - }, - { - "_id": 2, - "x": 1 - } - ] - } - ], - "tests": [ - { - "description": "aggregate $merge DuplicateKey error is accessible", - "runOnRequirements": [ - { - "minServerVersion": "5.1", - "topologies": [ - "single", - "replicaset" - ] - } - ], - "operations": [ - { - "name": "aggregate", - "object": "database0", - "arguments": { - "pipeline": [ - { - "$documents": [ - { - "_id": 2, - "x": 1 - } - ] - }, - { - "$merge": { - "into": "test", - "whenMatched": "fail" - } - } - ] - }, - "expectError": { - "errorCode": 11000, - "errorResponse": { - "keyPattern": { - "_id": 1 - }, - "keyValue": { - "_id": 2 - } - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-merge.json b/tests/UnifiedSpecTests/crud/aggregate-merge.json deleted file mode 100644 index ac61ceb8a..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-merge.json +++ /dev/null @@ -1,497 +0,0 @@ -{ - "description": "aggregate-merge", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.1.11" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_aggregate_merge" - } - }, - { - "collection": { - "id": "collection_readConcern_majority", - "database": "database0", - "collectionName": "test_aggregate_merge", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "collection": { - "id": "collection_readConcern_local", - "database": "database0", - "collectionName": "test_aggregate_merge", - "collectionOptions": { - "readConcern": { - "level": "local" - } - } - } - }, - { - "collection": { - "id": "collection_readConcern_available", - "database": "database0", - "collectionName": "test_aggregate_merge", - "collectionOptions": { - "readConcern": { - "level": "available" - } - } - } - } - ], - "initialData": [ - { - "collectionName": "test_aggregate_merge", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Aggregate with $merge", - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge and batch size of 0", - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "batchSize": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "cursor": {} - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge and majority readConcern", - "operations": [ - { - "object": "collection_readConcern_majority", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge and local readConcern", - "operations": [ - { - "object": "collection_readConcern_local", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "local" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge and available readConcern", - "operations": [ - { - "object": "collection_readConcern_available", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_merge", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$merge": { - "into": "other_test_collection" - } - } - ], - "readConcern": { - "level": "available" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json b/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json deleted file mode 100644 index e293457c1..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-out-readConcern.json +++ /dev/null @@ -1,407 +0,0 @@ -{ - "description": "aggregate-out-readConcern", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.1.0", - "topologies": [ - "replicaset", - "sharded" - ], - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_aggregate_out_readconcern" - } - }, - { - "collection": { - "id": "collection_readConcern_majority", - "database": "database0", - "collectionName": "test_aggregate_out_readconcern", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - } - } - }, - { - "collection": { - "id": "collection_readConcern_local", - "database": "database0", - "collectionName": "test_aggregate_out_readconcern", - "collectionOptions": { - "readConcern": { - "level": "local" - } - } - } - }, - { - "collection": { - "id": "collection_readConcern_available", - "database": "database0", - "collectionName": "test_aggregate_out_readconcern", - "collectionOptions": { - "readConcern": { - "level": "available" - } - } - } - }, - { - "collection": { - "id": "collection_readConcern_linearizable", - "database": "database0", - "collectionName": "test_aggregate_out_readconcern", - "collectionOptions": { - "readConcern": { - "level": "linearizable" - } - } - } - } - ], - "initialData": [ - { - "collectionName": "test_aggregate_out_readconcern", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "readConcern majority with out stage", - "operations": [ - { - "object": "collection_readConcern_majority", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "majority" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "readConcern local with out stage", - "operations": [ - { - "object": "collection_readConcern_local", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "local" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "readConcern available with out stage", - "operations": [ - { - "object": "collection_readConcern_available", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "available" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "other_test_collection", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "readConcern linearizable with out stage", - "operations": [ - { - "object": "collection_readConcern_linearizable", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ] - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test_aggregate_out_readconcern", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "other_test_collection" - } - ], - "readConcern": { - "level": "linearizable" - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json b/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json deleted file mode 100644 index bc887e83c..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate-write-readPreference.json +++ /dev/null @@ -1,460 +0,0 @@ -{ - "description": "aggregate-write-readPreference", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ] - } - ], - "_yamlAnchors": { - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - }, - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "uriOptions": { - "readConcernLevel": "local", - "w": 1 - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - } - } - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - }, - { - "collectionName": "coll1", - "databaseName": "db0", - "documents": [] - } - ], - "tests": [ - { - "description": "Aggregate with $out includes read preference for 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "serverless": "forbid" - } - ], - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "coll1" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "coll1" - } - ], - "$readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "db0", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $out omits read preference for pre-5.0 server", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.4.99", - "serverless": "forbid" - } - ], - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "coll1" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$out": "coll1" - } - ], - "$readPreference": { - "$$exists": false - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "db0", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge includes read preference for 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "coll1" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "coll1" - } - } - ], - "$readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "db0", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $merge omits read preference for pre-5.0 server", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "coll1" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - }, - { - "$merge": { - "into": "coll1" - } - } - ], - "$readPreference": { - "$$exists": false - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "db0", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/aggregate.json b/tests/UnifiedSpecTests/crud/aggregate.json deleted file mode 100644 index 0cbfb4e6e..000000000 --- a/tests/UnifiedSpecTests/crud/aggregate.json +++ /dev/null @@ -1,567 +0,0 @@ -{ - "description": "aggregate", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "aggregate-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "aggregate-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "tests": [ - { - "description": "aggregate with multiple batches works", - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "batchSize": 2 - }, - "object": "collection0", - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "cursor": { - "batchSize": 2 - } - }, - "commandName": "aggregate", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2 - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2 - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - } - ] - } - ] - }, - { - "description": "aggregate with a string comment", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": "comment" - }, - "object": "collection0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": "comment" - } - } - } - ] - } - ] - }, - { - "description": "aggregate with a document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": { - "content": "test" - } - }, - "object": "collection0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": { - "content": "test" - } - } - } - } - ] - } - ] - }, - { - "description": "aggregate with a document comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": { - "content": "test" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "comment": { - "content": "test" - } - }, - "commandName": "aggregate", - "databaseName": "aggregate-tests" - } - } - ] - } - ] - }, - { - "description": "aggregate with comment sets comment on getMore", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "batchSize": 2, - "comment": { - "content": "test" - } - }, - "object": "collection0", - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "cursor": { - "batchSize": 2 - }, - "comment": { - "content": "test" - } - }, - "commandName": "aggregate", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "content": "test" - } - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "content": "test" - } - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - } - ] - } - ] - }, - { - "description": "aggregate with comment does not set comment on getMore - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.3.99" - } - ], - "operations": [ - { - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "batchSize": 2, - "comment": "comment" - }, - "object": "collection0", - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "cursor": { - "batchSize": 2 - }, - "comment": "comment" - }, - "commandName": "aggregate", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "$$exists": false - } - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "$$exists": false - } - }, - "commandName": "getMore", - "databaseName": "aggregate-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json deleted file mode 100644 index 63815e323..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters-clientError.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "description": "bulkWrite-arrayFilters-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.5.5" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "crud-v2" - } - } - ], - "initialData": [ - { - "collectionName": "crud-v2", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite on server that doesn't support arrayFilters", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": {}, - "update": { - "$set": { - "y.0.b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "BulkWrite on server that doesn't support arrayFilters with arrayFilters on second op", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": {}, - "update": { - "$set": { - "y.0.b": 2 - } - } - } - }, - { - "updateMany": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json b/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json deleted file mode 100644 index 70ee014f7..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-arrayFilters.json +++ /dev/null @@ -1,279 +0,0 @@ -{ - "description": "bulkWrite-arrayFilters", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.5.6" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateOne with arrayFilters", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": { - "$set": { - "y.$[i].b": 2 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "arrayFilters": [ - { - "i.b": 3 - } - ] - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "y": [ - { - "b": 2 - }, - { - "b": 1 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 1 - } - ] - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany with arrayFilters", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": {}, - "update": { - "$set": { - "y.$[i].b": 2 - } - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": { - "$set": { - "y.$[i].b": 2 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "arrayFilters": [ - { - "i.b": 1 - } - ] - } - ], - "ordered": true - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "y": [ - { - "b": 3 - }, - { - "b": 2 - } - ] - }, - { - "_id": 2, - "y": [ - { - "b": 0 - }, - { - "b": 2 - } - ] - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-comment.json b/tests/UnifiedSpecTests/crud/bulkWrite-comment.json deleted file mode 100644 index 0b2addc85..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-comment.json +++ /dev/null @@ -1,519 +0,0 @@ -{ - "description": "bulkWrite-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "BulkWrite_comment" - } - } - ], - "initialData": [ - { - "collectionName": "BulkWrite_comment", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 5, - "x": "inserted" - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": "replaced" - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": "updated" - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 3 - } - } - } - ], - "comment": "comment" - }, - "expectResult": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 5 - } - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "BulkWrite_comment", - "documents": [ - { - "_id": 5, - "x": "inserted" - } - ], - "ordered": true, - "comment": "comment" - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "BulkWrite_comment", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "x": "replaced" - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": 2 - }, - "u": { - "$set": { - "x": "updated" - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true, - "comment": "comment" - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_comment", - "deletes": [ - { - "q": { - "_id": 3 - }, - "limit": 1 - } - ], - "ordered": true, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_comment", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": "replaced" - }, - { - "_id": 2, - "x": "updated" - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": "inserted" - } - ] - } - ] - }, - { - "description": "BulkWrite with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 5, - "x": "inserted" - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": "replaced" - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": "updated" - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 3 - } - } - } - ], - "comment": { - "key": "value" - } - }, - "expectResult": { - "deletedCount": 1, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 5 - } - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "BulkWrite_comment", - "documents": [ - { - "_id": 5, - "x": "inserted" - } - ], - "ordered": true, - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "BulkWrite_comment", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "x": "replaced" - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": 2 - }, - "u": { - "$set": { - "x": "updated" - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true, - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_comment", - "deletes": [ - { - "q": { - "_id": 3 - }, - "limit": 1 - } - ], - "ordered": true, - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_comment", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": "replaced" - }, - { - "_id": 2, - "x": "updated" - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": "inserted" - } - ] - } - ] - }, - { - "description": "BulkWrite with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 5, - "x": "inserted" - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": "replaced" - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": "updated" - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 3 - } - } - } - ], - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "BulkWrite_comment", - "documents": [ - { - "_id": 5, - "x": "inserted" - } - ], - "ordered": true, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_comment", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json deleted file mode 100644 index 2961b55dc..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-clientError.json +++ /dev/null @@ -1,193 +0,0 @@ -{ - "description": "bulkWrite-delete-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "BulkWrite_delete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite deleteOne with hints unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "deleteOne": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite deleteMany with hints unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "deleteMany": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json deleted file mode 100644 index fa9952209..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint-serverError.json +++ /dev/null @@ -1,252 +0,0 @@ -{ - "description": "bulkWrite-delete-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "BulkWrite_delete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite deleteOne with hints unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "deleteOne": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - }, - { - "q": { - "_id": 2 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite deleteMany with hints unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "deleteMany": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_", - "limit": 0 - }, - { - "q": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json b/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json deleted file mode 100644 index 9fcdecefd..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-delete-hint.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "description": "bulkWrite-delete-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "BulkWrite_delete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite deleteOne with hints", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - } - }, - { - "deleteOne": { - "filter": { - "_id": 2 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 2, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - }, - { - "q": { - "_id": 2 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite deleteMany with hints", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_" - } - }, - { - "deleteMany": { - "filter": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 3, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "BulkWrite_delete_hint", - "deletes": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "hint": "_id_", - "limit": 0 - }, - { - "q": { - "_id": { - "$gte": 4 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "BulkWrite_delete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json deleted file mode 100644 index 2dda9486e..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-hint-unacknowledged.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "description": "bulkWrite-deleteMany-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged deleteMany with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 0 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 0 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json deleted file mode 100644 index 45c20ea49..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-deleteMany-let.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "description": "BulkWrite deleteMany-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite deleteMany with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteMany": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - } - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 0 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "BulkWrite deleteMany with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - } - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'delete.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 1 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json deleted file mode 100644 index aadf6d9e9..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-hint-unacknowledged.json +++ /dev/null @@ -1,265 +0,0 @@ -{ - "description": "bulkWrite-deleteOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged deleteOne with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 1 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 1 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json deleted file mode 100644 index f3268163c..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-deleteOne-let.json +++ /dev/null @@ -1,200 +0,0 @@ -{ - "description": "BulkWrite deleteOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite deleteOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - } - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 1 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "BulkWrite deleteOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.9" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "deleteOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - } - } - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'delete.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 1 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json b/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json deleted file mode 100644 index 157637c71..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-errorResponse.json +++ /dev/null @@ -1,88 +0,0 @@ -{ - "description": "bulkWrite-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "bulkWrite operations support errorResponse assertions", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 8 - } - } - } - }, - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "errorCode": 8, - "errorResponse": { - "code": 8 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json deleted file mode 100644 index 92bbb1aaf..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-insertOne-dots_and_dollars.json +++ /dev/null @@ -1,374 +0,0 @@ -{ - "description": "bulkWrite-insertOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1, - "$a": 1 - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1, - "$a": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - }, - { - "description": "Inserting document with top-level dotted key", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1, - "a.b": 1 - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with dollar-prefixed key in embedded doc", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1, - "a": { - "$b": 1 - } - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Inserting document with dotted key in embedded doc", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 1, - "a": { - "b.c": 1 - } - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 1, - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - }, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json deleted file mode 100644 index fce647d8f..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-dots_and_dollars.json +++ /dev/null @@ -1,532 +0,0 @@ -{ - "description": "bulkWrite-replaceOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Replacing document with top-level dotted key on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a.b": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a.b": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - } - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - } - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json deleted file mode 100644 index e54cd704d..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-hint-unacknowledged.json +++ /dev/null @@ -1,293 +0,0 @@ -{ - "description": "bulkWrite-replaceOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged replaceOne with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json deleted file mode 100644 index 70f63837a..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-replaceOne-let.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "description": "BulkWrite replaceOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite replaceOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": 3 - } - } - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": { - "x": 3 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 3 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "BulkWrite replaceOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.9" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": 3 - } - } - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": { - "x": 3 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json deleted file mode 100644 index d5eb71c29..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-clientError.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "description": "bulkWrite-update-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_bulkwrite_update_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateOne with update hints unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany with update hints unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite replaceOne with update hints unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json deleted file mode 100644 index b0f7e1b38..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint-serverError.json +++ /dev/null @@ -1,422 +0,0 @@ -{ - "description": "bulkWrite-update-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_bulkwrite_update_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateOne with update hints unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_", - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany with update hints unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_", - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite replaceOne with update hints unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "x": 333 - }, - "hint": "_id_", - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - }, - { - "q": { - "_id": 4 - }, - "u": { - "x": 444 - }, - "hint": { - "_id": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json deleted file mode 100644 index 420635989..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-update-hint.json +++ /dev/null @@ -1,445 +0,0 @@ -{ - "description": "bulkWrite-update-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_bulkwrite_update_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateOne with update hints", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": "_id_" - }, - { - "q": { - "_id": 1 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 13 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany with update hints", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$lt": 3 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 4, - "modifiedCount": 4, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": "_id_" - }, - { - "q": { - "_id": { - "$lt": 3 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 13 - }, - { - "_id": 2, - "x": 24 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "BulkWrite replaceOne with update hints", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 3 - }, - "replacement": { - "x": 333 - }, - "hint": "_id_" - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "x": 444 - }, - "hint": { - "_id": 1 - } - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_bulkwrite_update_hint", - "updates": [ - { - "q": { - "_id": 3 - }, - "u": { - "x": 333 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": "_id_" - }, - { - "q": { - "_id": 4 - }, - "u": { - "x": 444 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "_id": 1 - } - } - ], - "ordered": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_bulkwrite_update_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 333 - }, - { - "_id": 4, - "x": 444 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json b/tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json deleted file mode 100644 index f9bfda0ed..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-update-validation.json +++ /dev/null @@ -1,210 +0,0 @@ -{ - "description": "bulkWrite-update-validation", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite replaceOne prohibits atomic modifiers", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "replaceOne": { - "filter": { - "_id": 1 - }, - "replacement": { - "$set": { - "x": 22 - } - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "BulkWrite updateOne requires atomic modifiers", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": { - "x": 22 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany requires atomic modifiers", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "x": 44 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json deleted file mode 100644 index 35a5cdd52..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-dots_and_dollars.json +++ /dev/null @@ -1,452 +0,0 @@ -{ - "description": "bulkWrite-updateMany-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {} - } - ] - } - ], - "tests": [ - { - "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set top-level dotted key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "$a": 1 - } - } - ] - } - ] - }, - { - "description": "Updating document to set dotted key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "a.b": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json deleted file mode 100644 index 87478918d..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-hint-unacknowledged.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "description": "bulkWrite-updateMany-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged updateMany with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json deleted file mode 100644 index fbeba1a60..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateMany-let.json +++ /dev/null @@ -1,243 +0,0 @@ -{ - "description": "BulkWrite updateMany-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 20 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateMany with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": 21 - } - } - ] - } - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": [ - { - "$set": { - "x": 21 - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 21 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ] - }, - { - "description": "BulkWrite updateMany with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.9" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": 21 - } - } - ] - } - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": [ - { - "$set": { - "x": 21 - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 20 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json deleted file mode 100644 index cbbe113ce..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-dots_and_dollars.json +++ /dev/null @@ -1,460 +0,0 @@ -{ - "description": "bulkWrite-updateOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {} - } - ] - } - ], - "tests": [ - { - "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set top-level dotted key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "$a": 1 - } - } - ] - } - ] - }, - { - "description": "Updating document to set dotted key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - } - } - ] - }, - "expectResult": { - "deletedCount": 0, - "insertedCount": 0, - "insertedIds": { - "$$unsetOrMatches": {} - }, - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0, - "upsertedIds": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "a.b": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json deleted file mode 100644 index 1345f6b53..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-hint-unacknowledged.json +++ /dev/null @@ -1,305 +0,0 @@ -{ - "description": "bulkWrite-updateOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged updateOne with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json b/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json deleted file mode 100644 index 96783c782..000000000 --- a/tests/UnifiedSpecTests/crud/bulkWrite-updateOne-let.json +++ /dev/null @@ -1,247 +0,0 @@ -{ - "description": "BulkWrite updateOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 20 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ], - "tests": [ - { - "description": "BulkWrite updateOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": 22 - } - } - ] - } - } - ], - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": [ - { - "$set": { - "x": 22 - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ] - }, - { - "description": "BulkWrite updateOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.9" - } - ], - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": 22 - } - } - ] - } - } - ], - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": [ - { - "$set": { - "x": 22 - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 20 - }, - { - "_id": 2, - "x": 21 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/countDocuments-comment.json b/tests/UnifiedSpecTests/crud/countDocuments-comment.json deleted file mode 100644 index e6c7ae817..000000000 --- a/tests/UnifiedSpecTests/crud/countDocuments-comment.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "description": "countDocuments-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "countDocuments-comments-test" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "countDocuments-comments-test", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "countDocuments with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {}, - "comment": { - "key": "value" - } - }, - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "comment": { - "key": "value" - } - }, - "commandName": "aggregate", - "databaseName": "countDocuments-comments-test" - } - } - ] - } - ] - }, - { - "description": "countDocuments with string comment", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "operations": [ - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {}, - "comment": "comment" - }, - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "comment": "comment" - }, - "commandName": "aggregate", - "databaseName": "countDocuments-comments-test" - } - } - ] - } - ] - }, - { - "description": "countDocuments with document comment on less than 4.4.0 - server error", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.3.99" - } - ], - "operations": [ - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {}, - "comment": { - "key": "value" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "comment": { - "key": "value" - } - }, - "commandName": "aggregate", - "databaseName": "countDocuments-comments-test" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json b/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json deleted file mode 100644 index 2a81282de..000000000 --- a/tests/UnifiedSpecTests/crud/db-aggregate-write-readPreference.json +++ /dev/null @@ -1,446 +0,0 @@ -{ - "description": "db-aggregate-write-readPreference", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "topologies": [ - "replicaset" - ], - "serverless": "forbid" - } - ], - "_yamlAnchors": { - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - }, - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "uriOptions": { - "readConcernLevel": "local", - "w": 1 - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0", - "databaseOptions": { - "readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - } - } - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [] - } - ], - "tests": [ - { - "description": "Database-level aggregate with $out includes read preference for 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "serverless": "forbid" - } - ], - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll0" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll0" - } - ], - "$readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Database-level aggregate with $out omits read preference for pre-5.0 server", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.4.99", - "serverless": "forbid" - } - ], - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll0" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$out": "coll0" - } - ], - "$readPreference": { - "$$exists": false - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Database-level aggregate with $merge includes read preference for 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$merge": { - "into": "coll0" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$merge": { - "into": "coll0" - } - } - ], - "$readPreference": { - "mode": "secondaryPreferred", - "maxStalenessSeconds": 600 - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Database-level aggregate with $merge omits read preference for pre-5.0 server", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$merge": { - "into": "coll0" - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "_id": 1 - } - }, - { - "$project": { - "_id": 1 - } - }, - { - "$merge": { - "into": "coll0" - } - } - ], - "$readPreference": { - "$$exists": false - }, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "w": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/db-aggregate.json b/tests/UnifiedSpecTests/crud/db-aggregate.json deleted file mode 100644 index 5015405bf..000000000 --- a/tests/UnifiedSpecTests/crud/db-aggregate.json +++ /dev/null @@ -1,107 +0,0 @@ -{ - "description": "db-aggregate", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "admin" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "crud-v2" - } - } - ], - "tests": [ - { - "description": "Aggregate with $listLocalSessions", - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "dummy": "dummy field" - } - }, - { - "$project": { - "_id": 0, - "dummy": 1 - } - } - ] - }, - "expectResult": [ - { - "dummy": "dummy field" - } - ] - } - ] - }, - { - "description": "Aggregate with $listLocalSessions and allowDiskUse", - "operations": [ - { - "object": "database0", - "name": "aggregate", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "dummy": "dummy field" - } - }, - { - "$project": { - "_id": 0, - "dummy": 1 - } - } - ], - "allowDiskUse": true - }, - "expectResult": [ - { - "dummy": "dummy field" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-comment.json b/tests/UnifiedSpecTests/crud/deleteMany-comment.json deleted file mode 100644 index 6abc5fd58..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-comment.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "description": "deleteMany-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name2" - }, - { - "_id": 3, - "name": "name3" - } - ] - } - ], - "tests": [ - { - "description": "deleteMany with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "comment": "comment" - }, - "expectResult": { - "deletedCount": 2 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 0 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "deleteMany with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "comment": { - "key": "value" - } - }, - "expectResult": { - "deletedCount": 2 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 0 - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "deleteMany with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "limit": 0 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name2" - }, - { - "_id": 3, - "name": "name3" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json deleted file mode 100644 index 66320122b..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-hint-clientError.json +++ /dev/null @@ -1,149 +0,0 @@ -{ - "description": "deleteMany-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteMany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "DeleteMany with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "DeleteMany with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json deleted file mode 100644 index 88d4a6557..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-hint-serverError.json +++ /dev/null @@ -1,190 +0,0 @@ -{ - "description": "deleteMany-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteMany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "DeleteMany with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_", - "limit": 0 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "DeleteMany with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json deleted file mode 100644 index ab7e9c7c0..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-hint-unacknowledged.json +++ /dev/null @@ -1,245 +0,0 @@ -{ - "description": "deleteMany-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged deleteMany with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 0 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged deleteMany with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 0 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-hint.json b/tests/UnifiedSpecTests/crud/deleteMany-hint.json deleted file mode 100644 index 59d903d20..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-hint.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "description": "deleteMany-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteMany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "DeleteMany with hint string", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "deletedCount": 2 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_", - "limit": 0 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - }, - { - "description": "DeleteMany with hint document", - "operations": [ - { - "object": "collection0", - "name": "deleteMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "deletedCount": 2 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteMany_hint", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - }, - "limit": 0 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteMany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteMany-let.json b/tests/UnifiedSpecTests/crud/deleteMany-let.json deleted file mode 100644 index 71bf26a01..000000000 --- a/tests/UnifiedSpecTests/crud/deleteMany-let.json +++ /dev/null @@ -1,201 +0,0 @@ -{ - "description": "deleteMany-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ], - "tests": [ - { - "description": "deleteMany with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "let": { - "name": "name" - } - }, - "expectResult": { - "deletedCount": 2 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "limit": 0 - } - ], - "let": { - "name": "name" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "deleteMany with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "deleteMany", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "let": { - "name": "name" - } - }, - "expectError": { - "errorContains": "'delete.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "limit": 0 - } - ], - "let": { - "name": "name" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-comment.json b/tests/UnifiedSpecTests/crud/deleteOne-comment.json deleted file mode 100644 index 0f42b086a..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-comment.json +++ /dev/null @@ -1,243 +0,0 @@ -{ - "description": "deleteOne-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ], - "tests": [ - { - "description": "deleteOne with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": "comment" - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ] - }, - { - "description": "deleteOne with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ] - }, - { - "description": "deleteOne with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json b/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json deleted file mode 100644 index 1f3a266f1..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-errorResponse.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "description": "deleteOne-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "delete operations support errorResponse assertions", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "delete" - ], - "errorCode": 8 - } - } - } - }, - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - } - }, - "expectError": { - "errorCode": 8, - "errorResponse": { - "code": 8 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json deleted file mode 100644 index cf629f59e..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-hint-clientError.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "description": "deleteOne-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteOne_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "DeleteOne with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "DeleteOne with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json deleted file mode 100644 index 15541ed85..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-hint-serverError.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "description": "deleteOne-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.3.3" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteOne_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "DeleteOne with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "DeleteOne with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json deleted file mode 100644 index 1782f0f52..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-hint-unacknowledged.json +++ /dev/null @@ -1,241 +0,0 @@ -{ - "description": "deleteOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged deleteOne with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 1 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged deleteOne with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "limit": 1 - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-hint.json b/tests/UnifiedSpecTests/crud/deleteOne-hint.json deleted file mode 100644 index bcc4bc234..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-hint.json +++ /dev/null @@ -1,161 +0,0 @@ -{ - "description": "deleteOne-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "DeleteOne_hint" - } - } - ], - "initialData": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "DeleteOne with hint string", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": "_id_", - "limit": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "deleteOne with hint document", - "operations": [ - { - "object": "collection0", - "name": "deleteOne", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "DeleteOne_hint", - "deletes": [ - { - "q": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "limit": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "DeleteOne_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/deleteOne-let.json b/tests/UnifiedSpecTests/crud/deleteOne-let.json deleted file mode 100644 index 971868223..000000000 --- a/tests/UnifiedSpecTests/crud/deleteOne-let.json +++ /dev/null @@ -1,191 +0,0 @@ -{ - "description": "deleteOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "deleteOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - }, - "expectResult": { - "deletedCount": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 1 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "deleteOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'delete.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll0", - "deletes": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "limit": 1 - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/distinct-comment.json b/tests/UnifiedSpecTests/crud/distinct-comment.json deleted file mode 100644 index 11bce9ac9..000000000 --- a/tests/UnifiedSpecTests/crud/distinct-comment.json +++ /dev/null @@ -1,186 +0,0 @@ -{ - "description": "distinct-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "distinct-comment-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "distinct-comment-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "distinct with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4.14" - } - ], - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "comment": { - "key": "value" - } - }, - "expectResult": [ - 11, - 22, - 33 - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "coll0", - "key": "x", - "query": {}, - "comment": { - "key": "value" - } - }, - "commandName": "distinct", - "databaseName": "distinct-comment-tests" - } - } - ] - } - ] - }, - { - "description": "distinct with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "comment": "comment" - }, - "expectResult": [ - 11, - 22, - 33 - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "coll0", - "key": "x", - "query": {}, - "comment": "comment" - }, - "commandName": "distinct", - "databaseName": "distinct-comment-tests" - } - } - ] - } - ] - }, - { - "description": "distinct with document comment - pre 4.4, server error", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.13" - } - ], - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "comment": { - "key": "value" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "coll0", - "key": "x", - "query": {}, - "comment": { - "key": "value" - } - }, - "commandName": "distinct", - "databaseName": "distinct-comment-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json b/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json deleted file mode 100644 index 6c0adacc8..000000000 --- a/tests/UnifiedSpecTests/crud/estimatedDocumentCount-comment.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "description": "estimatedDocumentCount-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "edc-comment-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "edc-comment-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "estimatedDocumentCount with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4.14" - } - ], - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection0", - "arguments": { - "comment": { - "key": "value" - } - }, - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0", - "comment": { - "key": "value" - } - }, - "commandName": "count", - "databaseName": "edc-comment-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection0", - "arguments": { - "comment": "comment" - }, - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0", - "comment": "comment" - }, - "commandName": "count", - "databaseName": "edc-comment-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount with document comment - pre 4.4.14, server error", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.13", - "topologies": [ - "single", - "replicaset" - ] - } - ], - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection0", - "arguments": { - "comment": { - "key": "value" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0", - "comment": { - "key": "value" - } - }, - "commandName": "count", - "databaseName": "edc-comment-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json b/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json deleted file mode 100644 index 1b650c1cb..000000000 --- a/tests/UnifiedSpecTests/crud/estimatedDocumentCount.json +++ /dev/null @@ -1,357 +0,0 @@ -{ - "description": "estimatedDocumentCount", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "uriOptions": { - "retryReads": false - }, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "edc-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1" - } - }, - { - "collection": { - "id": "collection0View", - "database": "database0", - "collectionName": "coll0view" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "edc-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "estimatedDocumentCount always uses count", - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection0", - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0" - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount with maxTimeMS", - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection0", - "arguments": { - "maxTimeMS": 6000 - }, - "expectResult": 3 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0", - "maxTimeMS": 6000 - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount on non-existent collection", - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection1", - "expectResult": 0 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll1" - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount errors correctly--command error", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "errorCode": 8 - } - } - } - }, - { - "name": "estimatedDocumentCount", - "object": "collection0", - "expectError": { - "errorCode": 8 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0" - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount errors correctly--socket error", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "count" - ], - "closeConnection": true - } - } - } - }, - { - "name": "estimatedDocumentCount", - "object": "collection0", - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "coll0" - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount works correctly on views", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0" - } - ], - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "coll0view" - } - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "collection": "coll0view", - "viewOn": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ] - } - }, - { - "name": "estimatedDocumentCount", - "object": "collection0View", - "expectResult": 2 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "coll0view" - }, - "commandName": "drop", - "databaseName": "edc-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "coll0view", - "viewOn": "coll0", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ] - }, - "commandName": "create", - "databaseName": "edc-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "count": "coll0view" - }, - "commandName": "count", - "databaseName": "edc-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json deleted file mode 100644 index 5bd954e79..000000000 --- a/tests/UnifiedSpecTests/crud/find-allowdiskuse-clientError.json +++ /dev/null @@ -1,79 +0,0 @@ -{ - "description": "find-allowdiskuse-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.0.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_find_allowdiskuse_clienterror" - } - } - ], - "tests": [ - { - "description": "Find fails when allowDiskUse true is specified against pre 3.2 server", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Find fails when allowDiskUse false is specified against pre 3.2 server", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json deleted file mode 100644 index dc58f8f0e..000000000 --- a/tests/UnifiedSpecTests/crud/find-allowdiskuse-serverError.json +++ /dev/null @@ -1,100 +0,0 @@ -{ - "description": "find-allowdiskuse-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.2", - "maxServerVersion": "4.3.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_find_allowdiskuse_servererror" - } - } - ], - "tests": [ - { - "description": "Find fails when allowDiskUse true is specified against pre 4.4 server (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test_find_allowdiskuse_servererror", - "filter": {}, - "allowDiskUse": true - } - } - } - ] - } - ] - }, - { - "description": "Find fails when allowDiskUse false is specified against pre 4.4 server (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test_find_allowdiskuse_servererror", - "filter": {}, - "allowDiskUse": false - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find-allowdiskuse.json b/tests/UnifiedSpecTests/crud/find-allowdiskuse.json deleted file mode 100644 index eb238ab93..000000000 --- a/tests/UnifiedSpecTests/crud/find-allowdiskuse.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "description": "find-allowdiskuse", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.1" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_find_allowdiskuse" - } - } - ], - "tests": [ - { - "description": "Find does not send allowDiskUse when value is not specified", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "Find sends allowDiskUse false when false is specified", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": false - } - } - } - ] - } - ] - }, - { - "description": "Find sends allowDiskUse true when true is specified", - "operations": [ - { - "object": "collection0", - "name": "find", - "arguments": { - "filter": {}, - "allowDiskUse": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test_find_allowdiskuse", - "allowDiskUse": true - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find-comment.json b/tests/UnifiedSpecTests/crud/find-comment.json deleted file mode 100644 index 600a3723f..000000000 --- a/tests/UnifiedSpecTests/crud/find-comment.json +++ /dev/null @@ -1,403 +0,0 @@ -{ - "description": "find-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "tests": [ - { - "description": "find with string comment", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": "comment" - }, - "expectResult": [ - { - "_id": 1 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": 1 - }, - "comment": "comment" - } - } - } - ] - } - ] - }, - { - "description": "find with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - }, - "expectResult": [ - { - "_id": 1 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - } - } - } - ] - } - ] - }, - { - "description": "find with document comment - pre 4.4", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99", - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - } - } - } - ] - } - ] - }, - { - "description": "find with comment sets comment on getMore", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2, - "comment": { - "key": "value" - } - }, - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2, - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "key": "value" - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "key": "value" - } - } - } - } - ] - } - ] - }, - { - "description": "find with comment does not set comment on getMore - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.3.99" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2, - "comment": "comment" - }, - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2, - "comment": "comment" - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2, - "comment": { - "$$exists": false - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find-let.json b/tests/UnifiedSpecTests/crud/find-let.json deleted file mode 100644 index 4e9c9c99f..000000000 --- a/tests/UnifiedSpecTests/crud/find-let.json +++ /dev/null @@ -1,148 +0,0 @@ -{ - "description": "find-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "Find with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - }, - "expectResult": [ - { - "_id": 1 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - } - } - } - ] - } - ] - }, - { - "description": "Find with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "let": { - "x": 1 - } - }, - "expectError": { - "errorContains": "Unrecognized field 'let'", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": 1 - }, - "let": { - "x": 1 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/find.json b/tests/UnifiedSpecTests/crud/find.json deleted file mode 100644 index 275d5d351..000000000 --- a/tests/UnifiedSpecTests/crud/find.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "description": "find", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "find-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "find-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "tests": [ - { - "description": "find with multiple batches works", - "operations": [ - { - "name": "find", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2 - }, - "object": "collection0", - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - }, - { - "_id": 6, - "x": 66 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": { - "_id": { - "$gt": 1 - } - }, - "batchSize": 2 - }, - "commandName": "find", - "databaseName": "find-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2 - }, - "commandName": "getMore", - "databaseName": "find-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "coll0", - "batchSize": 2 - }, - "commandName": "getMore", - "databaseName": "find-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json deleted file mode 100644 index 6853b9cc2..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-comment.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "description": "findOneAndDelete-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndDelete with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": "comment" - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "remove": true, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndDelete with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": { - "key": "value" - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "remove": true, - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndDelete with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "remove": true, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json deleted file mode 100644 index c6ff46786..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-clientError.json +++ /dev/null @@ -1,133 +0,0 @@ -{ - "description": "findOneAndDelete-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndDelete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndDelete with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndDelete with hint document", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json deleted file mode 100644 index b87410272..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-serverError.json +++ /dev/null @@ -1,162 +0,0 @@ -{ - "description": "findOneAndDelete-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.3" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndDelete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndDelete with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": "_id_", - "remove": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndDelete with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "remove": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json deleted file mode 100644 index 077f9892b..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint-unacknowledged.json +++ /dev/null @@ -1,225 +0,0 @@ -{ - "description": "findOneAndDelete-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged findOneAndDelete with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndDelete with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndDelete with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "remove": true, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged findOneAndDelete with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "remove": true, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json deleted file mode 100644 index 8b53f2bd3..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-hint.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "description": "findOneAndDelete-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndDelete_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndDelete with hint string", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": "_id_" - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": "_id_", - "remove": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndDelete with hint document", - "operations": [ - { - "object": "collection0", - "name": "findOneAndDelete", - "arguments": { - "filter": { - "_id": 1 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndDelete_hint", - "query": { - "_id": 1 - }, - "hint": { - "_id": 1 - }, - "remove": true - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndDelete_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json b/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json deleted file mode 100644 index ba8e681c0..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndDelete-let.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "description": "findOneAndDelete-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndDelete with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "remove": true, - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndDelete with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "field 'let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "remove": true, - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json deleted file mode 100644 index f817bb693..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-comment.json +++ /dev/null @@ -1,234 +0,0 @@ -{ - "description": "findOneAndReplace-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndReplace with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 5 - }, - "comment": "comment" - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "x": 5 - }, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 5 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndReplace with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 5 - }, - "comment": { - "key": "value" - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "x": 5 - }, - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 5 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndReplace with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 5 - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "x": 5 - }, - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json deleted file mode 100644 index 19ac447f8..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-dots_and_dollars.json +++ /dev/null @@ -1,430 +0,0 @@ -{ - "description": "findOneAndReplace-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Replacing document with top-level dotted key on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a.b": 1 - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a.b": 1 - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json deleted file mode 100644 index 6b07eb1f4..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-clientError.json +++ /dev/null @@ -1,139 +0,0 @@ -{ - "description": "findOneAndReplace-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndReplace_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndReplace with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndReplace with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json deleted file mode 100644 index 7fbf5a0ea..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-serverError.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "description": "findOneAndReplace-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndReplace_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndReplace with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": "_id_" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndReplace with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": { - "_id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json deleted file mode 100644 index 8228d8a2a..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint-unacknowledged.json +++ /dev/null @@ -1,248 +0,0 @@ -{ - "description": "findOneAndReplace-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged findOneAndReplace with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndReplace with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndReplace with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "update": { - "x": 111 - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged findOneAndReplace with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "update": { - "x": 111 - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json deleted file mode 100644 index d07c5921a..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-hint.json +++ /dev/null @@ -1,173 +0,0 @@ -{ - "description": "findOneAndReplace-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.1" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndReplace_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndReplace with hint string", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": "_id_" - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": "_id_" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 33 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndReplace with hint document", - "operations": [ - { - "object": "collection0", - "name": "findOneAndReplace", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndReplace_hint", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "hint": { - "_id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndReplace_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 33 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json b/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json deleted file mode 100644 index 5e5de44b3..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndReplace-let.json +++ /dev/null @@ -1,197 +0,0 @@ -{ - "description": "findOneAndReplace-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndReplace with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": "x" - }, - "let": { - "id": 1 - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": { - "x": "x" - }, - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": "x" - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndReplace with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": "x" - }, - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "field 'let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": { - "x": "x" - }, - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json deleted file mode 100644 index 6dec5b39e..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-comment.json +++ /dev/null @@ -1,228 +0,0 @@ -{ - "description": "findOneAndUpdate-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndUpdate with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": "comment" - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": "comment" - } - } - } - ] - } - ] - }, - { - "description": "findOneAndUpdate with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": { - "key": "value" - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndUpdate with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": 5 - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json deleted file mode 100644 index 40eb54739..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-dots_and_dollars.json +++ /dev/null @@ -1,380 +0,0 @@ -{ - "description": "findOneAndUpdate-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {} - } - ] - } - ], - "tests": [ - { - "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "_id": 1, - "foo": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set top-level dotted key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "_id": 1, - "foo": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "_id": 1, - "foo": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "$a": 1 - } - } - ] - } - ] - }, - { - "description": "Updating document to set dotted key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "_id": 1, - "foo": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "new": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "a.b": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json deleted file mode 100644 index 5023a450f..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-errorResponse.json +++ /dev/null @@ -1,132 +0,0 @@ -{ - "description": "findOneAndUpdate-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": "foo" - } - ] - } - ], - "tests": [ - { - "description": "findOneAndUpdate DuplicateKey error is accessible", - "runOnRequirements": [ - { - "minServerVersion": "4.2" - } - ], - "operations": [ - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "unique": true - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$set": { - "x": "foo" - } - }, - "upsert": true - }, - "expectError": { - "errorCode": 11000, - "errorResponse": { - "keyPattern": { - "x": 1 - }, - "keyValue": { - "x": "foo" - } - } - } - } - ] - }, - { - "description": "findOneAndUpdate document validation errInfo is accessible", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "modifyCollection", - "object": "database0", - "arguments": { - "collection": "test", - "validator": { - "x": { - "$type": "string" - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - }, - "expectError": { - "errorCode": 121, - "errorResponse": { - "errInfo": { - "failingDocumentId": 1, - "details": { - "$$type": "object" - } - } - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json deleted file mode 100644 index d0b51313c..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-clientError.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "description": "findOneAndUpdate-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndUpdate_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndUpdate with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json deleted file mode 100644 index 99fd9938f..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-serverError.json +++ /dev/null @@ -1,180 +0,0 @@ -{ - "description": "findOneAndUpdate-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.3.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndUpdate_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndUpdate with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json deleted file mode 100644 index d116a06d0..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint-unacknowledged.json +++ /dev/null @@ -1,253 +0,0 @@ -{ - "description": "findOneAndUpdate-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged findOneAndUpdate with hint string fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndUpdate with hint document fails with client-side error on pre-4.4 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged findOneAndUpdate with hint string on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged findOneAndUpdate with hint document on 4.4+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.4.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": null - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "$$type": [ - "string", - "object" - ] - }, - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json deleted file mode 100644 index 5be6d2b3e..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-hint.json +++ /dev/null @@ -1,181 +0,0 @@ -{ - "description": "findOneAndUpdate-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.3.1" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "findOneAndUpdate_hint" - } - } - ], - "initialData": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndUpdate with hint string", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate with hint document", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "findOneAndUpdate_hint", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "findOneAndUpdate_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json b/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json deleted file mode 100644 index 74d7d0e58..000000000 --- a/tests/UnifiedSpecTests/crud/findOneAndUpdate-let.json +++ /dev/null @@ -1,217 +0,0 @@ -{ - "description": "findOneAndUpdate-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "findOneAndUpdate with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - }, - "expectResult": { - "_id": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": "foo" - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "findOneAndUpdate with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - }, - "expectError": { - "errorContains": "field 'let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "coll0", - "query": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/insertMany-comment.json b/tests/UnifiedSpecTests/crud/insertMany-comment.json deleted file mode 100644 index 2b4c80b3f..000000000 --- a/tests/UnifiedSpecTests/crud/insertMany-comment.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "description": "insertMany-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "insertMany with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "insertMany with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": { - "key": "value" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "insertMany with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json deleted file mode 100644 index eed8997df..000000000 --- a/tests/UnifiedSpecTests/crud/insertMany-dots_and_dollars.json +++ /dev/null @@ -1,338 +0,0 @@ -{ - "description": "insertMany-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - }, - { - "description": "Inserting document with top-level dotted key", - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with dollar-prefixed key in embedded doc", - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Inserting document with dotted key in embedded doc", - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedIds": { - "$$unsetOrMatches": { - "0": 1 - } - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/insertOne-comment.json b/tests/UnifiedSpecTests/crud/insertOne-comment.json deleted file mode 100644 index dbd83d9f6..000000000 --- a/tests/UnifiedSpecTests/crud/insertOne-comment.json +++ /dev/null @@ -1,220 +0,0 @@ -{ - "description": "insertOne-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "insertOne with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2, - "x": 22 - }, - "comment": "comment" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "insertOne with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2, - "x": 22 - }, - "comment": { - "key": "value" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "insertOne with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2, - "x": 22 - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json deleted file mode 100644 index fdc17af2e..000000000 --- a/tests/UnifiedSpecTests/crud/insertOne-dots_and_dollars.json +++ /dev/null @@ -1,614 +0,0 @@ -{ - "description": "insertOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Inserting document with top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "$a": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with top-level dollar-prefixed key on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "$a": 1 - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "$a": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - }, - { - "description": "Inserting document with top-level dotted key", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a.b": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Inserting document with dollar-prefixed key in embedded doc", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Inserting document with dotted key in embedded doc", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "b.c": 1 - } - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - }, - { - "description": "Inserting document with dollar-prefixed key in _id yields server-side error", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": { - "$a": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": { - "$a": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - }, - { - "description": "Inserting document with dotted key in _id on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": { - "a.b": 1 - } - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": { - "a.b": 1 - } - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": { - "a.b": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": { - "a.b": 1 - } - } - ] - } - ] - }, - { - "description": "Inserting document with dotted key in _id on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": { - "a.b": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": { - "a.b": 1 - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - }, - { - "description": "Inserting document with DBRef-like keys", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "a": { - "$db": "foo" - } - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1, - "a": { - "$db": "foo" - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$db": "foo" - } - } - ] - } - ] - }, - { - "description": "Unacknowledged write using dollar-prefixed or dotted keys may be silently rejected on pre-5.0 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "_id": { - "$a": 1 - } - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll1", - "documents": [ - { - "_id": { - "$a": 1 - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json b/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json deleted file mode 100644 index 04ea6a745..000000000 --- a/tests/UnifiedSpecTests/crud/insertOne-errorResponse.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "description": "insertOne-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "insert operations support errorResponse assertions", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 8 - } - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - }, - "expectError": { - "errorCode": 8, - "errorResponse": { - "code": 8 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-comment.json b/tests/UnifiedSpecTests/crud/replaceOne-comment.json deleted file mode 100644 index 88bee5d7b..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-comment.json +++ /dev/null @@ -1,248 +0,0 @@ -{ - "description": "replaceOne-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "ReplaceOne with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 22 - }, - "comment": "comment" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "x": 22 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "ReplaceOne with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 22 - }, - "comment": { - "key": "value" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "x": 22 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "ReplaceOne with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 22 - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "x": 22 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json deleted file mode 100644 index d5003dc5e..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-dots_and_dollars.json +++ /dev/null @@ -1,567 +0,0 @@ -{ - "description": "replaceOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Replacing document with top-level dotted key on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a.b": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with top-level dotted key on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a.b": 1 - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a.b": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "$b": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dollar-prefixed key in embedded doc on pre-5.0 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on 3.6+ server", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "a": { - "b.c": 1 - } - } - ] - } - ] - }, - { - "description": "Replacing document with dotted key in embedded doc on pre-3.6 server yields server-side error", - "runOnRequirements": [ - { - "maxServerVersion": "3.4.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "b.c": 1 - } - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "b.c": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Unacknowledged write using dollar-prefixed or dotted keys may be silently rejected on pre-5.0 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection1", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "a": { - "$b": 1 - } - } - }, - "expectResult": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll1", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "_id": 1, - "a": { - "$b": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json deleted file mode 100644 index 5c5dec64f..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-hint-unacknowledged.json +++ /dev/null @@ -1,269 +0,0 @@ -{ - "description": "replaceOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged replaceOne with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged replaceOne with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-hint.json b/tests/UnifiedSpecTests/crud/replaceOne-hint.json deleted file mode 100644 index 6926e9d8d..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-hint.json +++ /dev/null @@ -1,203 +0,0 @@ -{ - "description": "replaceOne-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_replaceone_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_replaceone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "ReplaceOne with hint string", - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": "_id_" - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": "_id_", - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_replaceone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 111 - } - ] - } - ] - }, - { - "description": "ReplaceOne with hint document", - "operations": [ - { - "object": "collection0", - "name": "replaceOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "replacement": { - "x": 111 - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_replaceone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "x": 111 - }, - "hint": { - "_id": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_replaceone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 111 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-let.json b/tests/UnifiedSpecTests/crud/replaceOne-let.json deleted file mode 100644 index e7a7ee65a..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-let.json +++ /dev/null @@ -1,219 +0,0 @@ -{ - "description": "replaceOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "ReplaceOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": "foo" - }, - "let": { - "id": 1 - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": { - "x": "foo" - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": "foo" - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "ReplaceOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "replacement": { - "x": "foo" - }, - "let": { - "id": 1 - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": { - "x": "foo" - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1 - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/replaceOne-validation.json b/tests/UnifiedSpecTests/crud/replaceOne-validation.json deleted file mode 100644 index 6f5b173e0..000000000 --- a/tests/UnifiedSpecTests/crud/replaceOne-validation.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "description": "replaceOne-validation", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "ReplaceOne prohibits atomic modifiers", - "operations": [ - { - "name": "replaceOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "$set": { - "x": 22 - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-comment.json b/tests/UnifiedSpecTests/crud/updateMany-comment.json deleted file mode 100644 index 88b8b67f5..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-comment.json +++ /dev/null @@ -1,254 +0,0 @@ -{ - "description": "updateMany-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "UpdateMany with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": "comment" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateMany with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": { - "key": "value" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateMany with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json deleted file mode 100644 index 5d3b9d045..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-dots_and_dollars.json +++ /dev/null @@ -1,404 +0,0 @@ -{ - "description": "updateMany-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {} - } - ] - } - ], - "tests": [ - { - "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set top-level dotted key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "$a": 1 - } - } - ] - } - ] - }, - { - "description": "Updating document to set dotted key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "a.b": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json b/tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json deleted file mode 100644 index 5da878e29..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-hint-clientError.json +++ /dev/null @@ -1,159 +0,0 @@ -{ - "description": "updateMany-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updatemany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "UpdateMany with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "UpdateMany with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json b/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json deleted file mode 100644 index c81f36b13..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-hint-serverError.json +++ /dev/null @@ -1,216 +0,0 @@ -{ - "description": "updateMany-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updatemany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "UpdateMany with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_", - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "UpdateMany with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json deleted file mode 100644 index e83838aac..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-hint-unacknowledged.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "description": "updateMany-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged updateMany with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged updateMany with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-hint.json b/tests/UnifiedSpecTests/crud/updateMany-hint.json deleted file mode 100644 index 929be5299..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-hint.json +++ /dev/null @@ -1,219 +0,0 @@ -{ - "description": "updateMany-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updatemany_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "UpdateMany with hint string", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": "_id_", - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - ] - }, - { - "description": "UpdateMany with hint document", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updatemany_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "hint": { - "_id": 1 - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updatemany_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - }, - { - "_id": 3, - "x": 34 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-let.json b/tests/UnifiedSpecTests/crud/updateMany-let.json deleted file mode 100644 index cff3bd4c7..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-let.json +++ /dev/null @@ -1,249 +0,0 @@ -{ - "description": "updateMany-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ], - "tests": [ - { - "description": "updateMany with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x", - "y": "$$y" - } - } - ], - "let": { - "name": "name", - "x": "foo", - "y": { - "$literal": "bar" - } - } - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$name", - "$$name" - ] - } - }, - "u": [ - { - "$set": { - "x": "$$x", - "y": "$$y" - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "name": "name", - "x": "foo", - "y": { - "$literal": "bar" - } - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name", - "x": "foo", - "y": "bar" - }, - { - "_id": 3, - "name": "name", - "x": "foo", - "y": "bar" - } - ] - } - ] - }, - { - "description": "updateMany with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "x": "foo" - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "x": "$$x" - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "x": "foo" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2, - "name": "name" - }, - { - "_id": 3, - "name": "name" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateMany-validation.json b/tests/UnifiedSpecTests/crud/updateMany-validation.json deleted file mode 100644 index e3e46a138..000000000 --- a/tests/UnifiedSpecTests/crud/updateMany-validation.json +++ /dev/null @@ -1,98 +0,0 @@ -{ - "description": "updateMany-validation", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "UpdateMany requires atomic modifiers", - "operations": [ - { - "name": "updateMany", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "x": 44 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-comment.json b/tests/UnifiedSpecTests/crud/updateOne-comment.json deleted file mode 100644 index f4ee74db3..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-comment.json +++ /dev/null @@ -1,260 +0,0 @@ -{ - "description": "updateOne-comment", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne with string comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": "comment" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateOne with document comment", - "runOnRequirements": [ - { - "minServerVersion": "4.4" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": { - "key": "value" - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": { - "key": "value" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateOne with comment - pre 4.4", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.2.99" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 22 - } - }, - "comment": "comment" - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": { - "$set": { - "x": 22 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "comment": "comment" - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json b/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json deleted file mode 100644 index 798d522cb..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-dots_and_dollars.json +++ /dev/null @@ -1,412 +0,0 @@ -{ - "description": "updateOne-dots_and_dollars", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {} - } - ] - } - ], - "tests": [ - { - "description": "Updating document to set top-level dollar-prefixed key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "$a": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set top-level dotted key on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceWith": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$$ROOT" - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": {}, - "a.b": 1 - } - ] - } - ] - }, - { - "description": "Updating document to set dollar-prefixed key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "$a" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "$a": 1 - } - } - ] - } - ] - }, - { - "description": "Updating document to set dotted key in embedded doc on 5.0+ server", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "foo": { - "$setField": { - "field": { - "$literal": "a.b" - }, - "value": 1, - "input": "$foo" - } - } - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "foo": { - "a.b": 1 - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json b/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json deleted file mode 100644 index 0ceddbc4f..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-errorResponse.json +++ /dev/null @@ -1,87 +0,0 @@ -{ - "description": "updateOne-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "tests": [ - { - "description": "update operations support errorResponse assertions", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "update" - ], - "errorCode": 8 - } - } - } - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$set": { - "x": 1 - } - } - }, - "expectError": { - "errorCode": 8, - "errorResponse": { - "code": 8 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json b/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json deleted file mode 100644 index d4f1a5343..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-hint-clientError.json +++ /dev/null @@ -1,147 +0,0 @@ -{ - "description": "updateOne-hint-clientError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "maxServerVersion": "3.3.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updateone_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne with hint string unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateOne with hint document unsupported (client-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json b/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json deleted file mode 100644 index 05fb03331..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-hint-serverError.json +++ /dev/null @@ -1,208 +0,0 @@ -{ - "description": "updateOne-hint-serverError", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.4.0", - "maxServerVersion": "4.1.9" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updateone_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne with hint string unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_", - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "UpdateOne with hint document unsupported (server-side error)", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json b/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json deleted file mode 100644 index 859b0f92f..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-hint-unacknowledged.json +++ /dev/null @@ -1,281 +0,0 @@ -{ - "description": "updateOne-hint-unacknowledged", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "db0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "writeConcern": { - "w": 0 - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "db0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "Unacknowledged updateOne with hint string fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint document fails with client-side error on pre-4.2 server", - "runOnRequirements": [ - { - "maxServerVersion": "4.0.99" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint string on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - }, - { - "description": "Unacknowledged updateOne with hint document on 4.2+ server", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "acknowledged": { - "$$unsetOrMatches": false - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - }, - "hint": { - "$$type": [ - "string", - "object" - ] - } - } - ], - "writeConcern": { - "w": 0 - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-hint.json b/tests/UnifiedSpecTests/crud/updateOne-hint.json deleted file mode 100644 index 484e00757..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-hint.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "description": "updateOne-hint", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-v2" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test_updateone_hint" - } - } - ], - "initialData": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne with hint string", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_" - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": "_id_", - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - } - ] - } - ] - }, - { - "description": "UpdateOne with hint document", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test_updateone_hint", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "hint": { - "_id": 1 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test_updateone_hint", - "databaseName": "crud-v2", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 23 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-let.json b/tests/UnifiedSpecTests/crud/updateOne-let.json deleted file mode 100644 index e43b97935..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-let.json +++ /dev/null @@ -1,227 +0,0 @@ -{ - "description": "updateOne-let", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne with let option", - "runOnRequirements": [ - { - "minServerVersion": "5.0" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "$expr": { - "$eq": [ - "$_id", - "$$id" - ] - } - }, - "u": [ - { - "$set": { - "x": "$$x" - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "id": 1, - "x": "foo" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": "foo" - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "UpdateOne with let option unsupported (server-side error)", - "runOnRequirements": [ - { - "minServerVersion": "4.2.0", - "maxServerVersion": "4.4.99" - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$set": { - "x": "$$x" - } - } - ], - "let": { - "x": "foo" - } - }, - "expectError": { - "errorContains": "'update.let' is an unknown field", - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "coll0", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$set": { - "x": "$$x" - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "let": { - "x": "foo" - } - } - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateOne-validation.json b/tests/UnifiedSpecTests/crud/updateOne-validation.json deleted file mode 100644 index 1464642c5..000000000 --- a/tests/UnifiedSpecTests/crud/updateOne-validation.json +++ /dev/null @@ -1,80 +0,0 @@ -{ - "description": "updateOne-validation", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne requires atomic modifiers", - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "x": 22 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/crud/updateWithPipelines.json b/tests/UnifiedSpecTests/crud/updateWithPipelines.json deleted file mode 100644 index 164f2f6a1..000000000 --- a/tests/UnifiedSpecTests/crud/updateWithPipelines.json +++ /dev/null @@ -1,494 +0,0 @@ -{ - "description": "updateWithPipelines", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.1.11" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 1, - "y": 1, - "t": { - "u": { - "v": 1 - } - } - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - ], - "tests": [ - { - "description": "UpdateOne using pipelines", - "operations": [ - { - "object": "collection0", - "name": "updateOne", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "u": { - "v": 1 - }, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - ] - }, - { - "description": "UpdateMany using pipelines", - "operations": [ - { - "object": "collection0", - "name": "updateMany", - "arguments": { - "filter": {}, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "foo": 1 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate using pipelines", - "operations": [ - { - "object": "collection0", - "name": "findOneAndUpdate", - "arguments": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - }, - "commandName": "findAndModify", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - ] - }, - { - "description": "UpdateOne in bulk write using pipelines", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "_id": 1 - }, - "update": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ] - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 1 - }, - "u": [ - { - "$replaceRoot": { - "newRoot": "$t" - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "u": { - "v": 1 - }, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "y": 1 - } - ] - } - ] - }, - { - "description": "UpdateMany in bulk write using pipelines", - "operations": [ - { - "object": "collection0", - "name": "bulkWrite", - "arguments": { - "requests": [ - { - "updateMany": { - "filter": {}, - "update": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ] - } - } - ] - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2, - "upsertedCount": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": {}, - "u": [ - { - "$project": { - "x": 1 - } - }, - { - "$addFields": { - "foo": 1 - } - } - ], - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ] - }, - "commandName": "update", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 1, - "foo": 1 - }, - { - "_id": 2, - "x": 2, - "foo": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/gridfs/delete.json b/tests/UnifiedSpecTests/gridfs/delete.json deleted file mode 100644 index 7a4ec27f8..000000000 --- a/tests/UnifiedSpecTests/gridfs/delete.json +++ /dev/null @@ -1,799 +0,0 @@ -{ - "description": "gridfs-delete", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - } - ] - } - ], - "tests": [ - { - "description": "delete when length is 0", - "operations": [ - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "delete when length is 0 and there is one extra empty chunk", - "operations": [ - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000002" - } - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "delete when length is 8", - "operations": [ - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - } - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "delete when files entry does not exist", - "operations": [ - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - } - }, - "expectError": { - "isError": true - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "delete when files entry does not exist and there are orphaned chunks", - "operations": [ - { - "name": "deleteOne", - "object": "bucket0_files_collection", - "arguments": { - "filter": { - "_id": { - "$oid": "000000000000000000000004" - } - } - }, - "expectResult": { - "deletedCount": 1 - } - }, - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - } - }, - "expectError": { - "isError": true - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/gridfs/download.json b/tests/UnifiedSpecTests/gridfs/download.json deleted file mode 100644 index 48d324621..000000000 --- a/tests/UnifiedSpecTests/gridfs/download.json +++ /dev/null @@ -1,558 +0,0 @@ -{ - "description": "gridfs-download", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "d41d8cd98f00b204e9800998ecf8427e", - "filename": "length-0-with-empty-chunk", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "filename": "length-2", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "dd254cdc958e53abaa67da9f797125f5", - "filename": "length-8", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "length": 10, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "57d83cd477bfb1ccd975ab33d827a92b", - "filename": "length-10", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000006" - }, - "length": 2, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "c700ed4fdb1d27055aa3faa2c2432283", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000006" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000007" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2, - "data": { - "$binary": { - "base64": "mao=", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000008" - }, - "files_id": { - "$oid": "000000000000000000000006" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESI=", - "subType": "00" - } - } - } - ] - } - ], - "tests": [ - { - "description": "download when length is zero", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000001" - } - }, - "expectResult": { - "$$matchesHexBytes": "" - } - } - ] - }, - { - "description": "download when length is zero and there is one empty chunk", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000002" - } - }, - "expectResult": { - "$$matchesHexBytes": "" - } - } - ] - }, - { - "description": "download when there is one chunk", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000003" - } - }, - "expectResult": { - "$$matchesHexBytes": "1122" - } - } - ] - }, - { - "description": "download when there are two chunks", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000004" - } - }, - "expectResult": { - "$$matchesHexBytes": "1122334455667788" - } - } - ] - }, - { - "description": "download when there are three chunks", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectResult": { - "$$matchesHexBytes": "112233445566778899aa" - } - } - ] - }, - { - "description": "download when files entry does not exist", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "download when an intermediate chunk is missing", - "operations": [ - { - "name": "deleteOne", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - } - }, - "expectResult": { - "deletedCount": 1 - } - }, - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "download when final chunk is missing", - "operations": [ - { - "name": "deleteOne", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2 - } - }, - "expectResult": { - "deletedCount": 1 - } - }, - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "download when an intermediate chunk is the wrong size", - "operations": [ - { - "name": "bulkWrite", - "object": "bucket0_chunks_collection", - "arguments": { - "requests": [ - { - "updateOne": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - }, - "update": { - "$set": { - "data": { - "$binary": { - "base64": "VWZ3", - "subType": "00" - } - } - } - } - } - }, - { - "updateOne": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2 - }, - "update": { - "$set": { - "data": { - "$binary": { - "base64": "iJmq", - "subType": "00" - } - } - } - } - } - } - ] - }, - "expectResult": { - "matchedCount": 2, - "modifiedCount": 2 - } - }, - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "download when final chunk is the wrong size", - "operations": [ - { - "name": "updateOne", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2 - }, - "update": { - "$set": { - "data": { - "$binary": { - "base64": "mQ==", - "subType": "00" - } - } - } - } - }, - "expectResult": { - "matchedCount": 1, - "modifiedCount": 1 - } - }, - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "download legacy file with no name", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000006" - } - }, - "expectResult": { - "$$matchesHexBytes": "1122" - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/gridfs/downloadByName.json b/tests/UnifiedSpecTests/gridfs/downloadByName.json deleted file mode 100644 index cd4466395..000000000 --- a/tests/UnifiedSpecTests/gridfs/downloadByName.json +++ /dev/null @@ -1,330 +0,0 @@ -{ - "description": "gridfs-downloadByName", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "47ed733b8d10be225eceba344d533586", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-02T00:00:00.000Z" - }, - "md5": "b15835f133ff2e27c7cb28117bfae8f4", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-03T00:00:00.000Z" - }, - "md5": "eccbc87e4b5ce2fe28308fd9f2a7baf3", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-04T00:00:00.000Z" - }, - "md5": "f623e75af30e62bbd73d6df5b50bb7b5", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-05T00:00:00.000Z" - }, - "md5": "4c614360da93c0a041b22e537de151eb", - "filename": "abc", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000001" - }, - "files_id": { - "$oid": "000000000000000000000001" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000002" - }, - "files_id": { - "$oid": "000000000000000000000002" - }, - "n": 0, - "data": { - "$binary": { - "base64": "Ig==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000003" - }, - "files_id": { - "$oid": "000000000000000000000003" - }, - "n": 0, - "data": { - "$binary": { - "base64": "Mw==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000004" - }, - "files_id": { - "$oid": "000000000000000000000004" - }, - "n": 0, - "data": { - "$binary": { - "base64": "RA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 0, - "data": { - "$binary": { - "base64": "VQ==", - "subType": "00" - } - } - } - ] - } - ], - "tests": [ - { - "description": "downloadByName defaults to latest revision (-1)", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc" - }, - "expectResult": { - "$$matchesHexBytes": "55" - } - } - ] - }, - { - "description": "downloadByName when revision is 0", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": 0 - }, - "expectResult": { - "$$matchesHexBytes": "11" - } - } - ] - }, - { - "description": "downloadByName when revision is 1", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": 1 - }, - "expectResult": { - "$$matchesHexBytes": "22" - } - } - ] - }, - { - "description": "downloadByName when revision is 2", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": 2 - }, - "expectResult": { - "$$matchesHexBytes": "33" - } - } - ] - }, - { - "description": "downloadByName when revision is -2", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": -2 - }, - "expectResult": { - "$$matchesHexBytes": "44" - } - } - ] - }, - { - "description": "downloadByName when revision is -1", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": -1 - }, - "expectResult": { - "$$matchesHexBytes": "55" - } - } - ] - }, - { - "description": "downloadByName when files entry does not exist", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "xyz" - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "downloadByName when revision does not exist", - "operations": [ - { - "name": "downloadByName", - "object": "bucket0", - "arguments": { - "filename": "abc", - "revision": 999 - }, - "expectError": { - "isError": true - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json b/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json deleted file mode 100644 index d5a9d6f4a..000000000 --- a/tests/UnifiedSpecTests/gridfs/upload-disableMD5.json +++ /dev/null @@ -1,172 +0,0 @@ -{ - "description": "gridfs-upload-disableMD5", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "upload when length is 0 sans MD5", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "" - }, - "chunkSizeBytes": 4, - "disableMD5": true - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$exists": false - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [] - } - ] - }, - { - "description": "upload when length is 1 sans MD5", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "11" - }, - "chunkSizeBytes": 4, - "disableMD5": true - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$exists": false - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/gridfs/upload.json b/tests/UnifiedSpecTests/gridfs/upload.json deleted file mode 100644 index 97e18d2bc..000000000 --- a/tests/UnifiedSpecTests/gridfs/upload.json +++ /dev/null @@ -1,616 +0,0 @@ -{ - "description": "gridfs-upload", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "upload when length is 0", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 0, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "d41d8cd98f00b204e9800998ecf8427e" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [] - } - ] - }, - { - "description": "upload when length is 1", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "11" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when length is 3", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "112233" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 3, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "bafae3a174ab91fc70db7a6aa50f4f52" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIz", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when length is 4", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "11223344" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 4, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "7e7c77cff5705d1f7574a25ef6662117" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when length is 5", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "1122334455" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 5, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "283d4fea5dded59cf837d3047328f5af" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {}, - "sort": { - "n": 1 - } - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VQ==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when length is 8", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "1122334455667788" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 8, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "dd254cdc958e53abaa67da9f797125f5" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {}, - "sort": { - "n": 1 - } - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when contentType is provided", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "11" - }, - "chunkSizeBytes": 4, - "contentType": "image/jpeg" - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" - }, - "filename": "filename", - "contentType": "image/jpeg" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - } - ] - }, - { - "description": "upload when metadata is provided", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "11" - }, - "chunkSizeBytes": 4, - "metadata": { - "x": 1 - } - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "uploadedObjectId" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "length": 1, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "47ed733b8d10be225eceba344d533586" - }, - "filename": "filename", - "metadata": { - "x": 1 - } - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "uploadedObjectId" - }, - "n": 0, - "data": { - "$binary": { - "base64": "EQ==", - "subType": "00" - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/cursors.json b/tests/UnifiedSpecTests/load-balancers/cursors.json deleted file mode 100644 index 8454f130d..000000000 --- a/tests/UnifiedSpecTests/load-balancers/cursors.json +++ /dev/null @@ -1,1238 +0,0 @@ -{ - "description": "cursors are correctly pinned to connections for load-balanced clusters", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent", - "connectionReadyEvent", - "connectionClosedEvent", - "connectionCheckedOutEvent", - "connectionCheckedInEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1" - } - }, - { - "collection": { - "id": "collection2", - "database": "database0", - "collectionName": "coll2" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - }, - { - "collectionName": "coll1", - "databaseName": "database0Name", - "documents": [] - }, - { - "collectionName": "coll2", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "no connection is pinned if all documents are returned in the initial batch", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {} - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {} - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": 0, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connections are returned when the cursor is drained", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 2 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 3 - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - }, - { - "name": "close", - "object": "cursor0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": 0, - "ns": { - "$$type": "string" - }, - "nextBatch": { - "$$type": "array" - } - } - }, - "commandName": "getMore" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connections are returned to the pool when the cursor is closed", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "commandName": "killCursors" - } - }, - { - "commandSucceededEvent": { - "commandName": "killCursors" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connections are not returned after an network error during getMore", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 2 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectError": { - "isClientError": true - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandFailedEvent": { - "commandName": "getMore" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - } - ] - } - ] - }, - { - "description": "pinned connections are returned after a network error during a killCursors request", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "killCursors" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "commandName": "killCursors" - } - }, - { - "commandFailedEvent": { - "commandName": "killCursors" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - } - ] - } - ] - }, - { - "description": "pinned connections are not returned to the pool after a non-network error on getMore", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "getMore" - ], - "errorCode": 7 - } - } - } - }, - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 2 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectError": { - "errorCode": 7 - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "firstBatch": { - "$$type": "array" - }, - "ns": { - "$$type": "string" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandFailedEvent": { - "commandName": "getMore" - } - }, - { - "commandStartedEvent": { - "commandName": "killCursors" - } - }, - { - "commandSucceededEvent": { - "commandName": "killCursors" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "aggregate pins the cursor to a connection", - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [], - "batchSize": 2 - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll0", - "cursor": { - "batchSize": 2 - } - }, - "commandName": "aggregate" - } - }, - { - "commandSucceededEvent": { - "commandName": "aggregate" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": 0, - "ns": { - "$$type": "string" - }, - "nextBatch": { - "$$type": "array" - } - } - }, - "commandName": "getMore" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "listCollections pins the cursor to a connection", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "listCollections", - "object": "database0", - "arguments": { - "filter": {}, - "batchSize": 2 - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listCollections": 1, - "cursor": { - "batchSize": 2 - } - }, - "commandName": "listCollections", - "databaseName": "database0Name" - } - }, - { - "commandSucceededEvent": { - "commandName": "listCollections" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": { - "$$type": "string" - } - }, - "commandName": "getMore" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": 0, - "ns": { - "$$type": "string" - }, - "nextBatch": { - "$$type": "array" - } - } - }, - "commandName": "getMore" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "listIndexes pins the cursor to a connection", - "operations": [ - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "x": 1 - }, - "name": "x_1" - } - }, - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "keys": { - "y": 1 - }, - "name": "y_1" - } - }, - { - "name": "listIndexes", - "object": "collection0", - "arguments": { - "batchSize": 2 - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "createIndexes": "coll0", - "indexes": [ - { - "name": "x_1", - "key": { - "x": 1 - } - } - ] - }, - "commandName": "createIndexes" - } - }, - { - "commandSucceededEvent": { - "commandName": "createIndexes" - } - }, - { - "commandStartedEvent": { - "command": { - "createIndexes": "coll0", - "indexes": [ - { - "name": "y_1", - "key": { - "y": 1 - } - } - ] - }, - "commandName": "createIndexes" - } - }, - { - "commandSucceededEvent": { - "commandName": "createIndexes" - } - }, - { - "commandStartedEvent": { - "command": { - "listIndexes": "coll0", - "cursor": { - "batchSize": 2 - } - }, - "commandName": "listIndexes", - "databaseName": "database0Name" - } - }, - { - "commandSucceededEvent": { - "commandName": "listIndexes" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": 0, - "ns": { - "$$type": "string" - }, - "nextBatch": { - "$$type": "array" - } - } - }, - "commandName": "getMore" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "change streams pin to a connection", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "changeStream0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "aggregate" - } - }, - { - "commandSucceededEvent": { - "commandName": "aggregate" - } - }, - { - "commandStartedEvent": { - "commandName": "killCursors" - } - }, - { - "commandSucceededEvent": { - "commandName": "killCursors" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/event-monitoring.json b/tests/UnifiedSpecTests/load-balancers/event-monitoring.json deleted file mode 100644 index 938c70bf3..000000000 --- a/tests/UnifiedSpecTests/load-balancers/event-monitoring.json +++ /dev/null @@ -1,184 +0,0 @@ -{ - "description": "monitoring events include correct fields", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "uriOptions": { - "retryReads": false - }, - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent", - "poolClearedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "databaseName": "database0", - "collectionName": "coll0", - "documents": [] - } - ], - "tests": [ - { - "description": "command started and succeeded events include serviceId", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert", - "hasServiceId": true - } - }, - { - "commandSucceededEvent": { - "commandName": "insert", - "hasServiceId": true - } - } - ] - } - ] - }, - { - "description": "command failed events include serviceId", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "$or": true - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "hasServiceId": true - } - }, - { - "commandFailedEvent": { - "commandName": "find", - "hasServiceId": true - } - } - ] - } - ] - }, - { - "description": "poolClearedEvent events include serviceId", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {} - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "find", - "hasServiceId": true - } - }, - { - "commandFailedEvent": { - "commandName": "find", - "hasServiceId": true - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "poolClearedEvent": { - "hasServiceId": true - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json b/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json deleted file mode 100644 index 0eaadf30c..000000000 --- a/tests/UnifiedSpecTests/load-balancers/lb-connection-establishment.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "description": "connection establishment for load-balanced clusters", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "uriOptions": { - "loadBalanced": false - }, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - } - ], - "tests": [ - { - "description": "operations against load balancers fail if URI contains loadBalanced=false", - "skipReason": "servers have not implemented LB support yet so they will not fail the connection handshake in this case", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json b/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json deleted file mode 100644 index 6aaa7bdf9..000000000 --- a/tests/UnifiedSpecTests/load-balancers/non-lb-connection-establishment.json +++ /dev/null @@ -1,92 +0,0 @@ -{ - "description": "connection establishment if loadBalanced is specified for non-load balanced clusters", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "single", - "sharded" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "lbTrueClient", - "useMultipleMongoses": false, - "uriOptions": { - "loadBalanced": true - } - } - }, - { - "database": { - "id": "lbTrueDatabase", - "client": "lbTrueClient", - "databaseName": "lbTrueDb" - } - }, - { - "client": { - "id": "lbFalseClient", - "uriOptions": { - "loadBalanced": false - } - } - }, - { - "database": { - "id": "lbFalseDatabase", - "client": "lbFalseClient", - "databaseName": "lbFalseDb" - } - } - ], - "_yamlAnchors": { - "runCommandArguments": [ - { - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - } - } - ] - }, - "tests": [ - { - "description": "operations against non-load balanced clusters fail if URI contains loadBalanced=true", - "operations": [ - { - "name": "runCommand", - "object": "lbTrueDatabase", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - }, - "expectError": { - "errorContains": "Driver attempted to initialize in load balancing mode, but the server does not support this mode" - } - } - ] - }, - { - "description": "operations against non-load balanced clusters succeed if URI contains loadBalanced=false", - "operations": [ - { - "name": "runCommand", - "object": "lbFalseDatabase", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json b/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json deleted file mode 100644 index 083c98e94..000000000 --- a/tests/UnifiedSpecTests/load-balancers/sdam-error-handling.json +++ /dev/null @@ -1,514 +0,0 @@ -{ - "description": "state change errors are correctly handled", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "_yamlAnchors": { - "observedEvents": [ - "connectionCreatedEvent", - "connectionReadyEvent", - "connectionCheckedOutEvent", - "connectionCheckOutFailedEvent", - "connectionCheckedInEvent", - "connectionClosedEvent", - "poolClearedEvent" - ] - }, - "createEntities": [ - { - "client": { - "id": "failPointClient", - "useMultipleMongoses": false - } - }, - { - "client": { - "id": "singleClient", - "useMultipleMongoses": false, - "uriOptions": { - "appname": "lbSDAMErrorTestClient", - "retryWrites": false - }, - "observeEvents": [ - "connectionCreatedEvent", - "connectionReadyEvent", - "connectionCheckedOutEvent", - "connectionCheckOutFailedEvent", - "connectionCheckedInEvent", - "connectionClosedEvent", - "poolClearedEvent" - ] - } - }, - { - "database": { - "id": "singleDB", - "client": "singleClient", - "databaseName": "singleDB" - } - }, - { - "collection": { - "id": "singleColl", - "database": "singleDB", - "collectionName": "singleColl" - } - }, - { - "client": { - "id": "multiClient", - "useMultipleMongoses": true, - "uriOptions": { - "retryWrites": false - }, - "observeEvents": [ - "connectionCreatedEvent", - "connectionReadyEvent", - "connectionCheckedOutEvent", - "connectionCheckOutFailedEvent", - "connectionCheckedInEvent", - "connectionClosedEvent", - "poolClearedEvent" - ] - } - }, - { - "database": { - "id": "multiDB", - "client": "multiClient", - "databaseName": "multiDB" - } - }, - { - "collection": { - "id": "multiColl", - "database": "multiDB", - "collectionName": "multiColl" - } - } - ], - "initialData": [ - { - "collectionName": "singleColl", - "databaseName": "singleDB", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - }, - { - "collectionName": "multiColl", - "databaseName": "multiDB", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ], - "tests": [ - { - "description": "only connections for a specific serviceId are closed when pools are cleared", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "createFindCursor", - "object": "multiColl", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "createFindCursor", - "object": "multiColl", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor1" - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "close", - "object": "cursor1" - }, - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "multiClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11600 - } - } - } - }, - { - "name": "insertOne", - "object": "multiColl", - "arguments": { - "document": { - "x": 1 - } - }, - "expectError": { - "errorCode": 11600 - } - }, - { - "name": "insertOne", - "object": "multiColl", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "multiClient", - "eventType": "cmap", - "events": [ - { - "connectionCreatedEvent": {} - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCreatedEvent": {} - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "poolClearedEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "stale" - } - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "errors during the initial connection hello are ignored", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "failPointClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "isMaster", - "hello" - ], - "closeConnection": true, - "appName": "lbSDAMErrorTestClient" - } - } - } - }, - { - "name": "insertOne", - "object": "singleColl", - "arguments": { - "document": { - "x": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "singleClient", - "eventType": "cmap", - "events": [ - { - "connectionCreatedEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - }, - { - "connectionCheckOutFailedEvent": { - "reason": "connectionError" - } - } - ] - } - ] - }, - { - "description": "errors during authentication are processed", - "runOnRequirements": [ - { - "auth": true - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "failPointClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "saslContinue" - ], - "closeConnection": true, - "appName": "lbSDAMErrorTestClient" - } - } - } - }, - { - "name": "insertOne", - "object": "singleColl", - "arguments": { - "document": { - "x": 1 - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "singleClient", - "eventType": "cmap", - "events": [ - { - "connectionCreatedEvent": {} - }, - { - "poolClearedEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - }, - { - "connectionCheckOutFailedEvent": { - "reason": "connectionError" - } - } - ] - } - ] - }, - { - "description": "stale errors are ignored", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "failPointClient", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createFindCursor", - "object": "singleColl", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "createFindCursor", - "object": "singleColl", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor1" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectError": { - "isClientError": true - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor1" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor1" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor1", - "expectError": { - "isClientError": true - } - }, - { - "name": "close", - "object": "cursor1" - } - ], - "expectEvents": [ - { - "client": "singleClient", - "eventType": "cmap", - "events": [ - { - "connectionCreatedEvent": {} - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCreatedEvent": {} - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "poolClearedEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/server-selection.json b/tests/UnifiedSpecTests/load-balancers/server-selection.json deleted file mode 100644 index 00c7e4c95..000000000 --- a/tests/UnifiedSpecTests/load-balancers/server-selection.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "description": "server selection for load-balanced clusters", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0", - "collectionOptions": { - "readPreference": { - "mode": "secondaryPreferred" - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "$readPreference is sent for load-balanced clusters", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {} - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "$readPreference": { - "mode": "secondaryPreferred" - } - }, - "commandName": "find", - "databaseName": "database0Name" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/transactions.json b/tests/UnifiedSpecTests/load-balancers/transactions.json deleted file mode 100644 index 0dd04ee85..000000000 --- a/tests/UnifiedSpecTests/load-balancers/transactions.json +++ /dev/null @@ -1,1621 +0,0 @@ -{ - "description": "transactions are correctly pinned to connections for load-balanced clusters", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent", - "connectionReadyEvent", - "connectionClosedEvent", - "connectionCheckedOutEvent", - "connectionCheckedInEvent" - ] - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ], - "_yamlAnchors": { - "documents": [ - { - "_id": 4 - } - ] - }, - "tests": [ - { - "description": "sessions are reused in LB mode", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ] - }, - { - "description": "all operations go to the same mongos", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - } - ] - } - ] - }, - { - "description": "transaction can be committed multiple times", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is not released after a non-transient CRUD error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 51 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - }, - "expectError": { - "errorCode": 51, - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is not released after a non-transient commit error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 51 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0", - "expectError": { - "errorCode": 51, - "errorLabelsOmit": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a non-transient abort error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 51 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient non-network CRUD error", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 24 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - }, - "expectError": { - "errorCode": 24, - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient network CRUD error", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - }, - "expectError": { - "isClientError": true, - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient non-network commit error", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 24 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0", - "expectError": { - "errorCode": 24, - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient network commit error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "closeConnection": true - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0", - "ignoreResultAndError": true - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient non-network abort error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 24 - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released after a transient network abort error", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "closeConnection": true - } - } - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionClosedEvent": { - "reason": "error" - } - }, - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is released on successful abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is returned when a new transaction is started", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - } - ] - } - ] - }, - { - "description": "pinned connection is returned when a non-transaction operation uses the session", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "commitTransaction" - } - }, - { - "commandStartedEvent": { - "commandName": "insert" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - }, - { - "description": "a connection can be shared by a transaction and a cursor", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2, - "session": "session0" - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "close", - "object": "cursor0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "insert" - } - }, - { - "commandStartedEvent": { - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "commandName": "killCursors" - } - }, - { - "commandStartedEvent": { - "commandName": "abortTransaction" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json b/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json deleted file mode 100644 index 3dc6e46cf..000000000 --- a/tests/UnifiedSpecTests/load-balancers/wait-queue-timeouts.json +++ /dev/null @@ -1,153 +0,0 @@ -{ - "description": "wait queue timeout errors include details about checked out connections", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "topologies": [ - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "uriOptions": { - "maxPoolSize": 1, - "waitQueueTimeoutMS": 50 - }, - "observeEvents": [ - "connectionCheckedOutEvent", - "connectionCheckOutFailedEvent" - ] - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ], - "tests": [ - { - "description": "wait queue timeout errors include cursor statistics", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - }, - "expectError": { - "isClientError": true, - "errorContains": "maxPoolSize: 1, connections in use by cursors: 1, connections in use by transactions: 0, connections in use by other operations: 0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckOutFailedEvent": {} - } - ] - } - ] - }, - { - "description": "wait queue timeout errors include transaction statistics", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - }, - "expectError": { - "isClientError": true, - "errorContains": "maxPoolSize: 1, connections in use by cursors: 0, connections in use by transactions: 1, connections in use by other operations: 0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckOutFailedEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/runner/entity-map-observer.json b/tests/UnifiedSpecTests/runner/entity-map-observer.json new file mode 100644 index 000000000..833600950 --- /dev/null +++ b/tests/UnifiedSpecTests/runner/entity-map-observer.json @@ -0,0 +1,68 @@ +{ + "description": "find", + "schemaVersion": "1.0", + "createEntities": [ + { + "client": { + "id": "client0", + "useMultipleMongoses": true, + "observeEvents": [ + "commandStartedEvent" + ] + } + }, + { + "database": { + "id": "database0", + "client": "client0", + "databaseName": "runner-tests" + } + }, + { + "collection": { + "id": "collection0", + "database": "database0", + "collectionName": "coll0" + } + } + ], + "initialData": [ + { + "collectionName": "coll0", + "databaseName": "runner-tests", + "documents": [] + } + ], + "tests": [ + { + "description": "basic find", + "operations": [ + { + "name": "find", + "arguments": { + "filter": {} + }, + "object": "collection0", + "expectResult": [] + } + ], + "expectEvents": [ + { + "client": "client0", + "events": [ + { + "commandStartedEvent": { + "command": { + "find": "coll0", + "filter": {} + }, + "commandName": "find", + "databaseName": "runner-tests" + } + } + ] + } + ] + } + ] +} diff --git a/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json b/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json deleted file mode 100644 index 361ea83d7..000000000 --- a/tests/UnifiedSpecTests/sessions/driver-sessions-dirty-session-errors.json +++ /dev/null @@ -1,968 +0,0 @@ -{ - "description": "driver-sessions-dirty-session-errors", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "session-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Dirty explicit session is discarded (insert)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "assertSessionDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 3 - } - } - } - }, - { - "name": "assertSessionDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 2 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ] - }, - { - "description": "Dirty explicit session is discarded (findAndModify)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - } - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "expectResult": { - "_id": 1 - } - }, - { - "name": "assertSessionDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "readConcern": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "findAndModify", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "readConcern": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "findAndModify", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1, - "x": 1 - } - ] - } - ] - }, - { - "description": "Dirty implicit session is discarded (insert)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$type": "object" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$type": "object" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "Dirty implicit session is discarded (findAndModify)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "findAndModify" - ], - "closeConnection": true - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "expectResult": { - "_id": 1 - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": { - "$$type": "object" - }, - "txnNumber": 1, - "readConcern": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "findAndModify", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "new": false, - "lsid": { - "$$type": "object" - }, - "txnNumber": 1, - "readConcern": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "findAndModify", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1, - "x": 1 - } - ] - } - ] - }, - { - "description": "Dirty implicit session is discarded (read returning cursor)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - } - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ] - }, - "expectResult": [ - { - "_id": 1 - } - ] - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "lsid": { - "$$type": "object" - } - }, - "commandName": "aggregate", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$project": { - "_id": 1 - } - } - ], - "lsid": { - "$$type": "object" - } - }, - "commandName": "aggregate", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "Dirty implicit session is discarded (read not returning cursor)", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "closeConnection": true - } - } - } - }, - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {} - }, - "expectResult": 1 - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "lsid": { - "$$type": "object" - } - }, - "commandName": "aggregate", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": {} - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "lsid": { - "$$type": "object" - } - }, - "commandName": "aggregate", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json b/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json deleted file mode 100644 index 55312b32e..000000000 --- a/tests/UnifiedSpecTests/sessions/driver-sessions-server-support.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "description": "driver-sessions-server-support", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.6" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "session-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Server supports explicit sessions", - "operations": [ - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "Server supports implicit sessions", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$type": "object" - } - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json deleted file mode 100644 index 208e4cfe6..000000000 --- a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-client-error.json +++ /dev/null @@ -1,128 +0,0 @@ -{ - "description": "snapshot-sessions-not-supported-client-error", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "maxServerVersion": "4.4.99" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "snapshot": true - } - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "Client error on find with snapshot", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": {} - }, - "expectError": { - "isClientError": true, - "errorContains": "Snapshot reads require MongoDB 5.0 or later" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Client error on aggregate with snapshot", - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "session": "session0", - "pipeline": [] - }, - "expectError": { - "isClientError": true, - "errorContains": "Snapshot reads require MongoDB 5.0 or later" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - }, - { - "description": "Client error on distinct with snapshot", - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session0" - }, - "expectError": { - "isClientError": true, - "errorContains": "Snapshot reads require MongoDB 5.0 or later" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json deleted file mode 100644 index 79213f314..000000000 --- a/tests/UnifiedSpecTests/sessions/snapshot-sessions-not-supported-server-error.json +++ /dev/null @@ -1,187 +0,0 @@ -{ - "description": "snapshot-sessions-not-supported-server-error", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "topologies": [ - "single" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "snapshot": true - } - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "Server returns an error on find with snapshot", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": {} - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "find" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on aggregate with snapshot", - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "session": "session0", - "pipeline": [] - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "aggregate" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on distinct with snapshot", - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session0" - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "distinct" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json deleted file mode 100644 index 1021b7f26..000000000 --- a/tests/UnifiedSpecTests/sessions/snapshot-sessions-unsupported-ops.json +++ /dev/null @@ -1,493 +0,0 @@ -{ - "description": "snapshot-sessions-unsupported-ops", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "topologies": [ - "replicaset", - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0" - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "snapshot": true - } - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "Server returns an error on insertOne with snapshot", - "runOnRequirements": [ - { - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 22, - "x": 22 - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on insertMany with snapshot", - "runOnRequirements": [ - { - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "session": "session0", - "documents": [ - { - "_id": 22, - "x": 22 - }, - { - "_id": 33, - "x": 33 - } - ] - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on deleteOne with snapshot", - "runOnRequirements": [ - { - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "deleteOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": {} - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "delete" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on updateOne with snapshot", - "runOnRequirements": [ - { - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "update" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on findOneAndUpdate with snapshot", - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "findAndModify" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on listDatabases with snapshot", - "operations": [ - { - "name": "listDatabases", - "object": "client0", - "arguments": { - "session": "session0" - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listDatabases": 1, - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "listDatabases" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on listCollections with snapshot", - "operations": [ - { - "name": "listCollections", - "object": "database0", - "arguments": { - "session": "session0" - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listCollections": 1, - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "listCollections" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on listIndexes with snapshot", - "operations": [ - { - "name": "listIndexes", - "object": "collection0", - "arguments": { - "session": "session0" - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listIndexes": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "listIndexes" - } - } - ] - } - ] - }, - { - "description": "Server returns an error on runCommand with snapshot", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "session": "session0", - "commandName": "listCollections", - "command": { - "listCollections": 1 - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listCollections": 1, - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandFailedEvent": { - "commandName": "listCollections" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/sessions/snapshot-sessions.json b/tests/UnifiedSpecTests/sessions/snapshot-sessions.json deleted file mode 100644 index 75b577b03..000000000 --- a/tests/UnifiedSpecTests/sessions/snapshot-sessions.json +++ /dev/null @@ -1,993 +0,0 @@ -{ - "description": "snapshot-sessions", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "5.0", - "topologies": [ - "replicaset", - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "findAndModify", - "insert", - "update" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "collection0", - "collectionOptions": { - "writeConcern": { - "w": "majority" - } - } - } - }, - { - "session": { - "id": "session0", - "client": "client0", - "sessionOptions": { - "snapshot": true - } - } - }, - { - "session": { - "id": "session1", - "client": "client0", - "sessionOptions": { - "snapshot": true - } - } - } - ], - "initialData": [ - { - "collectionName": "collection0", - "databaseName": "database0", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "Find operation with snapshot", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 1, - "x": 12 - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 12 - } - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 1, - "x": 13 - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 13 - } - ] - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session1", - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 12 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "Distinct operation with snapshot", - "operations": [ - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session0" - }, - "expectResult": [ - 11 - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 2, - "x": 12 - } - }, - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session1" - }, - "expectResult": [ - 11, - 12 - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 2, - "x": 13 - } - }, - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {} - }, - "expectResult": [ - 11, - 13 - ] - }, - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session0" - }, - "expectResult": [ - 11 - ] - }, - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session1" - }, - "expectResult": [ - 11, - 12 - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "Aggregate operation with snapshot", - "operations": [ - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "session": "session0" - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 1, - "x": 12 - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "session": "session1" - }, - "expectResult": [ - { - "_id": 1, - "x": 12 - } - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 1, - "x": 13 - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ] - }, - "expectResult": [ - { - "_id": 1, - "x": 13 - } - ] - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "session": "session0" - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "session": "session1" - }, - "expectResult": [ - { - "_id": 1, - "x": 12 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "countDocuments operation with snapshot", - "operations": [ - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {}, - "session": "session0" - }, - "expectResult": 2 - }, - { - "name": "countDocuments", - "object": "collection0", - "arguments": { - "filter": {}, - "session": "session0" - }, - "expectResult": 2 - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "Mixed operation with snapshot", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "After" - }, - "expectResult": { - "_id": 1, - "x": 12 - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - } - }, - "expectResult": [ - { - "_id": 1, - "x": 12 - } - ] - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": 1 - } - } - ], - "session": "session0" - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "name": "distinct", - "object": "collection0", - "arguments": { - "fieldName": "x", - "filter": {}, - "session": "session0" - }, - "expectResult": [ - 11 - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "distinct": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "Write commands with snapshot session do not affect snapshot reads", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 22, - "x": 33 - } - } - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "session": "session0" - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": true - } - } - } - } - } - ] - } - ] - }, - { - "description": "First snapshot read does not send atClusterTime", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "session": "session0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "collection0", - "readConcern": { - "level": "snapshot", - "atClusterTime": { - "$$exists": false - } - } - }, - "commandName": "find", - "databaseName": "database0" - } - } - ] - } - ] - }, - { - "description": "StartTransaction fails in snapshot session", - "operations": [ - { - "name": "startTransaction", - "object": "session0", - "expectError": { - "isError": true, - "isClientError": true, - "errorContains": "Transactions are not supported in snapshot sessions" - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json b/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json deleted file mode 100644 index 6d9dc704b..000000000 --- a/tests/UnifiedSpecTests/transactions/do-not-retry-read-in-transaction.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "description": "do not retry read in a transaction", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.0.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.2.0", - "topologies": [ - "sharded", - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ], - "uriOptions": { - "retryReads": true - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-read-in-transaction-test" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "tests": [ - { - "description": "find does not retry in a transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "session": "session0" - }, - "expectError": { - "isError": true, - "errorLabelsContain": [ - "TransientTransactionError" - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {}, - "startTransaction": true - }, - "commandName": "find", - "databaseName": "retryable-read-in-transaction-test" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/transactions/mongos-unpin.json b/tests/UnifiedSpecTests/transactions/mongos-unpin.json deleted file mode 100644 index 4f7ae4379..000000000 --- a/tests/UnifiedSpecTests/transactions/mongos-unpin.json +++ /dev/null @@ -1,437 +0,0 @@ -{ - "description": "mongos-unpin", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "mongos-unpin-db" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "mongos-unpin-db", - "documents": [] - } - ], - "_yamlAnchors": { - "anchors": 24 - }, - "tests": [ - { - "description": "unpin after TransientTransactionError error on commit", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "commitTransaction" - ], - "errorCode": 24 - } - } - } - }, - { - "name": "commitTransaction", - "object": "session0", - "expectError": { - "errorCode": 24, - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ] - }, - { - "description": "unpin on successful abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ] - }, - { - "description": "unpin after non-transient error on abort", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 24 - } - } - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ] - }, - { - "description": "unpin after TransientTransactionError error on abort", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "abortTransaction" - ], - "errorCode": 91 - } - } - } - }, - { - "name": "abortTransaction", - "object": "session0" - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ] - }, - { - "description": "unpin when a new transaction is started", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ] - }, - { - "description": "unpin when a non-transaction write operation uses a session", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ] - }, - { - "description": "unpin when a non-transaction read operation uses a session", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "x": 1 - }, - "session": "session0" - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json b/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json deleted file mode 100644 index 4ad56e2f2..000000000 --- a/tests/UnifiedSpecTests/transactions/retryable-abort-handshake.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "description": "retryable abortTransaction on handshake errors", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ], - "serverless": "forbid", - "auth": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent", - "connectionCheckOutStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-handshake-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - }, - { - "session": { - "id": "session1", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "AbortTransaction succeeds after handshake network error", - "skipReason": "DRIVERS-2032: Pinned servers need to be checked if they are still selectable", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "session": "session1", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "closeConnection": true - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - }, - "session": "session1" - }, - "expectError": { - "isError": true - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "startTransaction": true - }, - "commandName": "insert", - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "abortTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "abortTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json b/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json deleted file mode 100644 index d9315a8fc..000000000 --- a/tests/UnifiedSpecTests/transactions/retryable-commit-handshake.json +++ /dev/null @@ -1,211 +0,0 @@ -{ - "description": "retryable commitTransaction on handshake errors", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.2", - "topologies": [ - "replicaset", - "sharded", - "load-balanced" - ], - "serverless": "forbid", - "auth": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent", - "connectionCheckOutStartedEvent" - ], - "uriOptions": { - "retryWrites": false - } - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-handshake-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - }, - { - "session": { - "id": "session1", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "CommitTransaction succeeds after handshake network error", - "skipReason": "DRIVERS-2032: Pinned servers need to be checked if they are still selectable", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2, - "x": 22 - } - } - }, - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "session": "session1", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "saslContinue", - "ping" - ], - "closeConnection": true - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1 - }, - "session": "session1" - }, - "expectError": { - "isError": true - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - }, - { - "connectionCheckOutStartedEvent": {} - } - ] - }, - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll", - "documents": [ - { - "_id": 2, - "x": 22 - } - ], - "startTransaction": true - }, - "commandName": "insert", - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "ping": 1 - }, - "databaseName": "retryable-handshake-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-handshake-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json b/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json deleted file mode 100644 index 9799bb2f6..000000000 --- a/tests/UnifiedSpecTests/valid-fail/assertNumberConnectionsCheckedOut.json +++ /dev/null @@ -1,63 +0,0 @@ -{ - "description": "assertNumberConnectionsCheckedOut", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - } - ], - "tests": [ - { - "description": "operation fails if client field is not specified", - "operations": [ - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "connections": 1 - } - } - ] - }, - { - "description": "operation fails if connections field is not specified", - "operations": [ - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ] - }, - { - "description": "operation fails if client entity does not exist", - "operations": [ - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client1" - } - } - ] - }, - { - "description": "operation fails if number of connections is incorrect", - "operations": [ - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 1 - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-bucket-database-undefined.json b/tests/UnifiedSpecTests/valid-fail/entity-bucket-database-undefined.json deleted file mode 100644 index 7f7f1978c..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-bucket-database-undefined.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "description": "entity-bucket-database-undefined", - "schemaVersion": "1.0", - "createEntities": [ - { - "bucket": { - "id": "bucket0", - "database": "foo" - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json b/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json deleted file mode 100644 index d92d23dca..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-client-apiVersion-unsupported.json +++ /dev/null @@ -1,20 +0,0 @@ -{ - "description": "entity-client-apiVersion-unsupported", - "schemaVersion": "1.1", - "createEntities": [ - { - "client": { - "id": "client0", - "serverApi": { - "version": "server_will_never_support_this_api_version" - } - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json deleted file mode 100644 index 8c0c4d204..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_with_client_id.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "description": "entity-client-storeEventsAsEntities-conflict_with_client_id", - "schemaVersion": "1.2", - "createEntities": [ - { - "client": { - "id": "client0", - "storeEventsAsEntities": [ - { - "id": "client0", - "events": [ - "PoolCreatedEvent", - "PoolReadyEvent", - "PoolClearedEvent", - "PoolClosedEvent" - ] - } - ] - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json deleted file mode 100644 index 77bc4abf2..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_different_array.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "description": "entity-client-storeEventsAsEntities-conflict_within_different_array", - "schemaVersion": "1.2", - "createEntities": [ - { - "client": { - "id": "client0", - "storeEventsAsEntities": [ - { - "id": "events", - "events": [ - "PoolCreatedEvent", - "PoolReadyEvent", - "PoolClearedEvent", - "PoolClosedEvent" - ] - } - ] - } - }, - { - "client": { - "id": "client1", - "storeEventsAsEntities": [ - { - "id": "events", - "events": [ - "CommandStartedEvent", - "CommandSucceededEvent", - "CommandFailedEvent" - ] - } - ] - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json b/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json deleted file mode 100644 index e1a949988..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-client-storeEventsAsEntities-conflict_within_same_array.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "description": "entity-client-storeEventsAsEntities-conflict_within_same_array", - "schemaVersion": "1.2", - "createEntities": [ - { - "client": { - "id": "client0", - "storeEventsAsEntities": [ - { - "id": "events", - "events": [ - "PoolCreatedEvent", - "PoolReadyEvent", - "PoolClearedEvent", - "PoolClosedEvent" - ] - }, - { - "id": "events", - "events": [ - "CommandStartedEvent", - "CommandSucceededEvent", - "CommandFailedEvent" - ] - } - ] - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-collection-database-undefined.json b/tests/UnifiedSpecTests/valid-fail/entity-collection-database-undefined.json deleted file mode 100644 index 20b0733e3..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-collection-database-undefined.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "description": "entity-collection-database-undefined", - "schemaVersion": "1.0", - "createEntities": [ - { - "collection": { - "id": "collection0", - "database": "foo", - "collectionName": "foo" - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-database-client-undefined.json b/tests/UnifiedSpecTests/valid-fail/entity-database-client-undefined.json deleted file mode 100644 index 0f8110e6d..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-database-client-undefined.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "description": "entity-database-client-undefined", - "schemaVersion": "1.0", - "createEntities": [ - { - "database": { - "id": "database0", - "client": "foo", - "databaseName": "foo" - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json b/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json deleted file mode 100644 index 0956efa4c..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-findCursor-malformed.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "description": "entity-findCursor-malformed", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "databaseName": "database0Name", - "collectionName": "coll0", - "documents": [] - } - ], - "tests": [ - { - "description": "createFindCursor fails if filter is not specified", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "saveResultAsEntity": "cursor0" - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json b/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json deleted file mode 100644 index 389e448c0..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-findCursor.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "description": "entity-findCursor", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "databaseName": "database0Name", - "collectionName": "coll0", - "documents": [] - } - ], - "tests": [ - { - "description": "iterateUntilDocumentOrError fails if it references a nonexistent entity", - "operations": [ - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0" - } - ] - }, - { - "description": "close fails if it references a nonexistent entity", - "operations": [ - { - "name": "close", - "object": "cursor0" - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/entity-session-client-undefined.json b/tests/UnifiedSpecTests/valid-fail/entity-session-client-undefined.json deleted file mode 100644 index 260356436..000000000 --- a/tests/UnifiedSpecTests/valid-fail/entity-session-client-undefined.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "description": "entity-session-client-undefined", - "schemaVersion": "1.0", - "createEntities": [ - { - "session": { - "id": "session0", - "client": "foo" - } - } - ], - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json deleted file mode 100644 index b64779c72..000000000 --- a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError-malformed.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "description": "ignoreResultAndError-malformed", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "malformed operation fails if ignoreResultAndError is true", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "foo": "bar" - }, - "ignoreResultAndError": true - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json b/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json deleted file mode 100644 index 01b2421a9..000000000 --- a/tests/UnifiedSpecTests/valid-fail/ignoreResultAndError.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "description": "ignoreResultAndError", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "operation errors are not ignored if ignoreResultAndError is false", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - }, - "ignoreResultAndError": false - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json deleted file mode 100644 index e62de8003..000000000 --- a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_aws_kms_credentials.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "description": "kmsProviders-missing_aws_kms_credentials", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": "accessKeyId" - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json deleted file mode 100644 index 8ef805d0f..000000000 --- a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_azure_kms_credentials.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "description": "kmsProviders-missing_azure_kms_credentials", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "azure": { - "tenantId": "tenantId" - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json deleted file mode 100644 index c6da1ce58..000000000 --- a/tests/UnifiedSpecTests/valid-fail/kmsProviders-missing_gcp_kms_credentials.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "description": "kmsProviders-missing_gcp_kms_credentials", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "gcp": { - "email": "email" - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json b/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json deleted file mode 100644 index 57499b4ea..000000000 --- a/tests/UnifiedSpecTests/valid-fail/kmsProviders-no_kms.json +++ /dev/null @@ -1,32 +0,0 @@ -{ - "description": "clientEncryptionOpts-no_kms", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": {} - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/operation-failure.json b/tests/UnifiedSpecTests/valid-fail/operation-failure.json deleted file mode 100644 index 8f6cae152..000000000 --- a/tests/UnifiedSpecTests/valid-fail/operation-failure.json +++ /dev/null @@ -1,56 +0,0 @@ -{ - "description": "operation-failure", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "operation-failure" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "tests": [ - { - "description": "Unsupported command", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "unsupportedCommand", - "command": { - "unsupportedCommand": 1 - } - } - } - ] - }, - { - "description": "Unsupported query operator", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "$unsupportedQueryOperator": 1 - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json b/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json deleted file mode 100644 index d8ef5ab1c..000000000 --- a/tests/UnifiedSpecTests/valid-fail/operation-unsupported.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "description": "operation-unsupported", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - } - ], - "tests": [ - { - "description": "Unsupported operation", - "operations": [ - { - "name": "unsupportedOperation", - "object": "client0" - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/returnDocument-enum-invalid.json b/tests/UnifiedSpecTests/valid-fail/returnDocument-enum-invalid.json deleted file mode 100644 index ea425fb56..000000000 --- a/tests/UnifiedSpecTests/valid-fail/returnDocument-enum-invalid.json +++ /dev/null @@ -1,66 +0,0 @@ -{ - "description": "returnDocument-enum-invalid", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "test" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - } - ], - "tests": [ - { - "description": "FindOneAndReplace returnDocument invalid enum value", - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "_id": 1, - "x": 111 - }, - "returnDocument": "invalid" - } - } - ] - }, - { - "description": "FindOneAndUpdate returnDocument invalid enum value", - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "invalid" - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-fail/schemaVersion-unsupported.json b/tests/UnifiedSpecTests/valid-fail/schemaVersion-unsupported.json deleted file mode 100644 index ceb553291..000000000 --- a/tests/UnifiedSpecTests/valid-fail/schemaVersion-unsupported.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "description": "schemaVersion-unsupported", - "schemaVersion": "0.1", - "tests": [ - { - "description": "foo", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json b/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json deleted file mode 100644 index a9fc063f3..000000000 --- a/tests/UnifiedSpecTests/valid-pass/assertNumberConnectionsCheckedOut.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "description": "assertNumberConnectionsCheckedOut", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - } - ], - "tests": [ - { - "description": "basic assertion succeeds", - "operations": [ - { - "name": "assertNumberConnectionsCheckedOut", - "object": "testRunner", - "arguments": { - "client": "client0", - "connections": 0 - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json b/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json deleted file mode 100644 index 64f8fb02f..000000000 --- a/tests/UnifiedSpecTests/valid-pass/collectionData-createOptions.json +++ /dev/null @@ -1,69 +0,0 @@ -{ - "description": "collectionData-createOptions", - "schemaVersion": "1.9", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0", - "createOptions": { - "capped": true, - "size": 4096 - }, - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "collection is created with the correct options", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "collStats", - "command": { - "collStats": "coll0", - "scale": 1 - } - }, - "expectResult": { - "capped": true, - "maxSize": 4096 - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json b/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json deleted file mode 100644 index 3fde42919..000000000 --- a/tests/UnifiedSpecTests/valid-pass/createEntities-operation.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "description": "createEntities-operation", - "schemaVersion": "1.9", - "tests": [ - { - "description": "createEntities operation", - "operations": [ - { - "name": "createEntities", - "object": "testRunner", - "arguments": { - "entities": [ - { - "client": { - "id": "client1", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "database1" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "coll1" - } - } - ] - } - }, - { - "name": "deleteOne", - "object": "collection1", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client1", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "coll1", - "deletes": [ - { - "q": { - "_id": 1 - }, - "limit": 1 - } - ] - }, - "commandName": "delete", - "databaseName": "database1" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json b/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json deleted file mode 100644 index 3209033de..000000000 --- a/tests/UnifiedSpecTests/valid-pass/entity-client-cmap-events.json +++ /dev/null @@ -1,71 +0,0 @@ -{ - "description": "entity-client-cmap-events", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "connectionReadyEvent", - "connectionCheckedOutEvent", - "connectionCheckedInEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "events are captured during an operation", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "x": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - }, - { - "connectionCheckedOutEvent": {} - }, - { - "connectionCheckedInEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json b/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json deleted file mode 100644 index e37e5a1ac..000000000 --- a/tests/UnifiedSpecTests/valid-pass/entity-client-storeEventsAsEntities.json +++ /dev/null @@ -1,67 +0,0 @@ -{ - "description": "entity-client-storeEventsAsEntities", - "schemaVersion": "1.2", - "createEntities": [ - { - "client": { - "id": "client0", - "storeEventsAsEntities": [ - { - "id": "client0_events", - "events": [ - "CommandStartedEvent", - "CommandSucceededEvent", - "CommandFailedEvent" - ] - } - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "test" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "test", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ], - "tests": [ - { - "description": "storeEventsAsEntities captures events", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {} - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json b/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json deleted file mode 100644 index 88fc28e34..000000000 --- a/tests/UnifiedSpecTests/valid-pass/entity-cursor-iterateOnce.json +++ /dev/null @@ -1,108 +0,0 @@ -{ - "description": "entity-cursor-iterateOnce", - "schemaVersion": "1.9", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "databaseName": "database0", - "collectionName": "coll0", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ], - "tests": [ - { - "description": "iterateOnce", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 2 - } - }, - { - "name": "iterateOnce", - "object": "cursor0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find", - "databaseName": "database0" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json b/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json deleted file mode 100644 index 85b8f69d7..000000000 --- a/tests/UnifiedSpecTests/valid-pass/entity-find-cursor.json +++ /dev/null @@ -1,182 +0,0 @@ -{ - "description": "entity-find-cursor", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "databaseName": "database0Name", - "collectionName": "coll0", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - }, - { - "_id": 4 - }, - { - "_id": 5 - } - ] - } - ], - "tests": [ - { - "description": "cursors can be created, iterated, and closed", - "operations": [ - { - "name": "createFindCursor", - "object": "collection0", - "arguments": { - "filter": {}, - "batchSize": 2 - }, - "saveResultAsEntity": "cursor0" - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 1 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 2 - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "cursor0", - "expectResult": { - "_id": 3 - } - }, - { - "name": "close", - "object": "cursor0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll0", - "filter": {}, - "batchSize": 2 - }, - "commandName": "find", - "databaseName": "database0Name" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "ns": { - "$$type": "string" - }, - "firstBatch": { - "$$type": "array" - } - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": "long" - }, - "collection": "coll0" - }, - "commandName": "getMore" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursor": { - "id": { - "$$type": "long" - }, - "ns": { - "$$type": "string" - }, - "nextBatch": { - "$$type": "array" - } - } - }, - "commandName": "getMore" - } - }, - { - "commandStartedEvent": { - "command": { - "killCursors": "coll0", - "cursors": { - "$$type": "array" - } - }, - "commandName": "killCursors" - } - }, - { - "commandSucceededEvent": { - "reply": { - "cursorsKilled": { - "$$unsetOrMatches": { - "$$type": "array" - } - } - }, - "commandName": "killCursors" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json b/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json deleted file mode 100644 index 177b1baf5..000000000 --- a/tests/UnifiedSpecTests/valid-pass/expectedError-errorResponse.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "description": "expectedError-errorResponse", - "schemaVersion": "1.12", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "test" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "tests": [ - { - "description": "Unsupported command", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "unsupportedCommand", - "command": { - "unsupportedCommand": 1 - } - }, - "expectError": { - "errorResponse": { - "errmsg": { - "$$type": "string" - } - } - } - } - ] - }, - { - "description": "Unsupported query operator", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "$unsupportedQueryOperator": 1 - } - }, - "expectError": { - "errorResponse": { - "errmsg": { - "$$type": "string" - } - } - } - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json deleted file mode 100644 index fe308df96..000000000 --- a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-eventType.json +++ /dev/null @@ -1,126 +0,0 @@ -{ - "description": "expectedEventsForClient-eventType", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent", - "connectionReadyEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "eventType can be set to command and cmap", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "eventType": "command", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1 - } - ] - }, - "commandName": "insert" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - } - ] - } - ] - }, - { - "description": "eventType defaults to command if unset", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1 - } - ] - }, - "commandName": "insert" - } - } - ] - }, - { - "client": "client0", - "eventType": "cmap", - "events": [ - { - "connectionReadyEvent": {} - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json b/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json deleted file mode 100644 index 178b756c2..000000000 --- a/tests/UnifiedSpecTests/valid-pass/expectedEventsForClient-ignoreExtraEvents.json +++ /dev/null @@ -1,151 +0,0 @@ -{ - "description": "expectedEventsForClient-ignoreExtraEvents", - "schemaVersion": "1.7", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "ignoreExtraEvents can be set to false", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": false, - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 1 - } - ] - }, - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "ignoreExtraEvents can be set to true", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2 - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 3 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "ignoreExtraEvents": true, - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 2 - } - ] - }, - "commandName": "insert" - } - } - ] - } - ] - }, - { - "description": "ignoreExtraEvents defaults to false if unset", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 4 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": 4 - } - ] - }, - "commandName": "insert" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json b/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json deleted file mode 100644 index 2e9b1c58a..000000000 --- a/tests/UnifiedSpecTests/valid-pass/ignoreResultAndError.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "description": "ignoreResultAndError", - "schemaVersion": "1.3", - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "operation errors are ignored if ignoreResultAndError is true", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1 - } - }, - "ignoreResultAndError": true - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json deleted file mode 100644 index 7cc74939e..000000000 --- a/tests/UnifiedSpecTests/valid-pass/kmsProviders-explicit_kms_credentials.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "description": "kmsProviders-explicit_kms_credentials", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": "accessKeyId", - "secretAccessKey": "secretAccessKey" - }, - "azure": { - "tenantId": "tenantId", - "clientId": "clientId", - "clientSecret": "clientSecret" - }, - "gcp": { - "email": "email", - "privateKey": "cHJpdmF0ZUtleQo=" - }, - "kmip": { - "endpoint": "endpoint" - }, - "local": { - "key": "a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5a2V5" - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json deleted file mode 100644 index 363f2a457..000000000 --- a/tests/UnifiedSpecTests/valid-pass/kmsProviders-mixed_kms_credential_fields.json +++ /dev/null @@ -1,54 +0,0 @@ -{ - "description": "kmsProviders-mixed_kms_credential_fields", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": "accessKeyId", - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": "tenantId", - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": "email", - "privateKey": { - "$$placeholder": 1 - } - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json deleted file mode 100644 index 3f7721f01..000000000 --- a/tests/UnifiedSpecTests/valid-pass/kmsProviders-placeholder_kms_credentials.json +++ /dev/null @@ -1,70 +0,0 @@ -{ - "description": "kmsProviders-placeholder_kms_credentials", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": { - "accessKeyId": { - "$$placeholder": 1 - }, - "secretAccessKey": { - "$$placeholder": 1 - } - }, - "azure": { - "tenantId": { - "$$placeholder": 1 - }, - "clientId": { - "$$placeholder": 1 - }, - "clientSecret": { - "$$placeholder": 1 - } - }, - "gcp": { - "email": { - "$$placeholder": 1 - }, - "privateKey": { - "$$placeholder": 1 - } - }, - "kmip": { - "endpoint": { - "$$placeholder": 1 - } - }, - "local": { - "key": { - "$$placeholder": 1 - } - } - } - } - } - } - ], - "tests": [ - { - "description": "", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json b/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json deleted file mode 100644 index 12ca58094..000000000 --- a/tests/UnifiedSpecTests/valid-pass/kmsProviders-unconfigured_kms.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "description": "kmsProviders-unconfigured_kms", - "schemaVersion": "1.8", - "runOnRequirements": [ - { - "csfle": true - } - ], - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "clientEncryption": { - "id": "clientEncryption0", - "clientEncryptionOpts": { - "keyVaultClient": "client0", - "keyVaultNamespace": "keyvault.datakeys", - "kmsProviders": { - "aws": {}, - "azure": {}, - "gcp": {}, - "kmip": {}, - "local": {} - } - } - } - } - ], - "tests": [ - { - "description": "", - "skipReason": "DRIVERS-2280: waiting on driver support for on-demand credentials", - "operations": [] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json b/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json deleted file mode 100644 index 4de65c583..000000000 --- a/tests/UnifiedSpecTests/valid-pass/matches-lte-operator.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "description": "matches-lte-operator", - "schemaVersion": "1.9", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "database0Name" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "database0Name", - "documents": [] - } - ], - "tests": [ - { - "description": "special lte matching operator", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 1, - "y": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "coll0", - "documents": [ - { - "_id": { - "$$lte": 1 - }, - "y": { - "$$lte": 2 - } - } - ] - }, - "commandName": "insert", - "databaseName": "database0Name" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json b/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json deleted file mode 100644 index 411ca19c5..000000000 --- a/tests/UnifiedSpecTests/valid-pass/observeSensitiveCommands.json +++ /dev/null @@ -1,691 +0,0 @@ -{ - "description": "observeSensitiveCommands", - "schemaVersion": "1.5", - "runOnRequirements": [ - { - "auth": false - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent" - ], - "observeSensitiveCommands": true - } - }, - { - "client": { - "id": "client1", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent" - ], - "observeSensitiveCommands": false - } - }, - { - "client": { - "id": "client2", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "observeSensitiveCommands" - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "observeSensitiveCommands" - } - }, - { - "database": { - "id": "database2", - "client": "client2", - "databaseName": "observeSensitiveCommands" - } - } - ], - "tests": [ - { - "description": "getnonce is observed with observeSensitiveCommands=true", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "getnonce", - "command": { - "getnonce": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "getnonce", - "command": { - "getnonce": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "getnonce", - "reply": { - "ok": { - "$$exists": false - }, - "nonce": { - "$$exists": false - } - } - } - } - ] - } - ] - }, - { - "description": "getnonce is not observed with observeSensitiveCommands=false", - "operations": [ - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "getnonce", - "command": { - "getnonce": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client1", - "events": [] - } - ] - }, - { - "description": "getnonce is not observed by default", - "operations": [ - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "getnonce", - "command": { - "getnonce": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client2", - "events": [] - } - ] - }, - { - "description": "hello with speculativeAuthenticate", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - } - ] - }, - { - "client": "client1", - "events": [] - }, - { - "client": "client2", - "events": [] - } - ] - }, - { - "description": "hello without speculativeAuthenticate is always observed", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": true - } - } - } - } - ] - }, - { - "client": "client1", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": true - } - } - } - } - ] - }, - { - "client": "client2", - "events": [ - { - "commandStartedEvent": { - "commandName": "hello", - "command": { - "hello": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "hello", - "reply": { - "isWritablePrimary": { - "$$exists": true - } - } - } - } - ] - } - ] - }, - { - "description": "legacy hello with speculativeAuthenticate", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1, - "speculativeAuthenticate": { - "saslStart": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": false - }, - "speculativeAuthenticate": { - "$$exists": false - } - } - } - } - ] - }, - { - "client": "client1", - "events": [] - }, - { - "client": "client2", - "events": [] - } - ] - }, - { - "description": "legacy hello without speculativeAuthenticate is always observed", - "operations": [ - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database0", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database1", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "name": "runCommand", - "object": "database2", - "arguments": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - } - ] - }, - { - "client": "client1", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - } - ] - }, - { - "client": "client2", - "events": [ - { - "commandStartedEvent": { - "commandName": "ismaster", - "command": { - "ismaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "ismaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - }, - { - "commandStartedEvent": { - "commandName": "isMaster", - "command": { - "isMaster": 1 - } - } - }, - { - "commandSucceededEvent": { - "commandName": "isMaster", - "reply": { - "ismaster": { - "$$exists": true - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json b/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json deleted file mode 100644 index 50f0d06f0..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-change-streams.json +++ /dev/null @@ -1,455 +0,0 @@ -{ - "description": "poc-change-streams", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ], - "ignoreCommandMonitoringEvents": [ - "getMore", - "killCursors" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "change-stream-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "client": { - "id": "client1", - "useMultipleMongoses": false - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "change-stream-tests" - } - }, - { - "database": { - "id": "database2", - "client": "client1", - "databaseName": "change-stream-tests-2" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "test" - } - }, - { - "collection": { - "id": "collection2", - "database": "database1", - "collectionName": "test2" - } - }, - { - "collection": { - "id": "collection3", - "database": "database2", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "change-stream-tests", - "documents": [] - }, - { - "collectionName": "test2", - "databaseName": "change-stream-tests", - "documents": [] - }, - { - "collectionName": "test", - "databaseName": "change-stream-tests-2", - "documents": [] - } - ], - "tests": [ - { - "description": "saveResultAsEntity is optional for createChangeStream", - "runOnRequirements": [ - { - "minServerVersion": "3.8.0", - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "client0", - "arguments": { - "pipeline": [] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1 - }, - "commandName": "aggregate", - "databaseName": "admin" - } - } - ] - } - ] - }, - { - "description": "Executing a watch helper on a MongoClient results in notifications for changes to all collections in all databases in the cluster.", - "runOnRequirements": [ - { - "minServerVersion": "3.8.0", - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "createChangeStream", - "object": "client0", - "arguments": { - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection2", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection3", - "arguments": { - "document": { - "y": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "z": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test2" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests-2", - "coll": "test" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "y": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "z": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "cursor": {}, - "pipeline": [ - { - "$changeStream": { - "allChangesForCluster": true, - "fullDocument": { - "$$unsetOrMatches": "default" - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "admin" - } - } - ] - } - ] - }, - { - "description": "Test consecutive resume", - "runOnRequirements": [ - { - "minServerVersion": "4.1.7", - "topologies": [ - "replicaset" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "getMore" - ], - "closeConnection": true - } - } - } - }, - { - "name": "createChangeStream", - "object": "collection0", - "arguments": { - "batchSize": 1, - "pipeline": [] - }, - "saveResultAsEntity": "changeStream0" - }, - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "x": 1 - } - } - }, - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "x": 2 - } - } - }, - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "x": 3 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "x": 1 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "x": 2 - } - } - }, - { - "name": "iterateUntilDocumentOrError", - "object": "changeStream0", - "expectResult": { - "operationType": "insert", - "ns": { - "db": "change-stream-tests", - "coll": "test" - }, - "fullDocument": { - "_id": { - "$$type": "objectId" - }, - "x": 3 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "cursor": { - "batchSize": 1 - }, - "pipeline": [ - { - "$changeStream": { - "fullDocument": { - "$$unsetOrMatches": "default" - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "change-stream-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "cursor": { - "batchSize": 1 - }, - "pipeline": [ - { - "$changeStream": { - "fullDocument": { - "$$unsetOrMatches": "default" - }, - "resumeAfter": { - "$$exists": true - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "change-stream-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "cursor": { - "batchSize": 1 - }, - "pipeline": [ - { - "$changeStream": { - "fullDocument": { - "$$unsetOrMatches": "default" - }, - "resumeAfter": { - "$$exists": true - } - } - } - ] - }, - "commandName": "aggregate", - "databaseName": "change-stream-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json b/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json deleted file mode 100644 index fe0a5ae99..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-command-monitoring.json +++ /dev/null @@ -1,223 +0,0 @@ -{ - "description": "poc-command-monitoring", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent", - "commandSucceededEvent", - "commandFailedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "command-monitoring-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "command-monitoring-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "tests": [ - { - "description": "A successful find event with a getmore and the server kills the cursor (<= 4.4)", - "runOnRequirements": [ - { - "minServerVersion": "3.1", - "maxServerVersion": "4.4.99", - "topologies": [ - "single", - "replicaset" - ] - } - ], - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3, - "limit": 4 - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": { - "$gte": 1 - } - }, - "sort": { - "_id": 1 - }, - "batchSize": 3, - "limit": 4 - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": { - "$$type": [ - "int", - "long" - ] - }, - "ns": "command-monitoring-tests.test", - "firstBatch": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - }, - "commandName": "find" - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "collection": "test", - "batchSize": 1 - }, - "commandName": "getMore", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandSucceededEvent": { - "reply": { - "ok": 1, - "cursor": { - "id": 0, - "ns": "command-monitoring-tests.test", - "nextBatch": [ - { - "_id": 4, - "x": 44 - } - ] - } - }, - "commandName": "getMore" - } - } - ] - } - ] - }, - { - "description": "A failed find event", - "operations": [ - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "$or": true - } - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "$or": true - } - }, - "commandName": "find", - "databaseName": "command-monitoring-tests" - } - }, - { - "commandFailedEvent": { - "commandName": "find" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-crud.json b/tests/UnifiedSpecTests/valid-pass/poc-crud.json deleted file mode 100644 index 0790d9b78..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-crud.json +++ /dev/null @@ -1,450 +0,0 @@ -{ - "description": "poc-crud", - "schemaVersion": "1.4", - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "crud-tests" - } - }, - { - "database": { - "id": "database1", - "client": "client0", - "databaseName": "admin" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll0" - } - }, - { - "collection": { - "id": "collection1", - "database": "database0", - "collectionName": "coll1" - } - }, - { - "collection": { - "id": "collection2", - "database": "database0", - "collectionName": "coll2", - "collectionOptions": { - "readConcern": { - "level": "majority" - } - } - } - } - ], - "initialData": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - }, - { - "collectionName": "coll1", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - }, - { - "collectionName": "coll2", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - }, - { - "collectionName": "aggregate_out", - "databaseName": "crud-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "BulkWrite with mixed ordered operations", - "operations": [ - { - "name": "bulkWrite", - "object": "collection0", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 3, - "x": 33 - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "insertOne": { - "document": { - "_id": 4, - "x": 44 - } - } - }, - { - "deleteMany": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "ordered": true - }, - "expectResult": { - "deletedCount": 2, - "insertedCount": 2, - "insertedIds": { - "$$unsetOrMatches": { - "0": 3, - "3": 4 - } - }, - "matchedCount": 3, - "modifiedCount": 3, - "upsertedCount": 1, - "upsertedIds": { - "5": 4 - } - } - } - ], - "outcome": [ - { - "collectionName": "coll0", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2, - "x": 24 - }, - { - "_id": 3, - "x": 34 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "InsertMany continue-on-error behavior with unordered (duplicate key in requests)", - "operations": [ - { - "name": "insertMany", - "object": "collection1", - "arguments": { - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ], - "ordered": false - }, - "expectError": { - "expectResult": { - "$$unsetOrMatches": { - "deletedCount": 0, - "insertedCount": 2, - "matchedCount": 0, - "modifiedCount": 0, - "upsertedCount": 0, - "upsertedIds": {} - } - } - } - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "ReplaceOne prohibits atomic modifiers", - "operations": [ - { - "name": "replaceOne", - "object": "collection1", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "$set": { - "x": 22 - } - } - }, - "expectError": { - "isClientError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [] - } - ], - "outcome": [ - { - "collectionName": "coll1", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 1, - "x": 11 - } - ] - } - ] - }, - { - "description": "readConcern majority with out stage", - "runOnRequirements": [ - { - "minServerVersion": "4.1.0", - "topologies": [ - "replicaset", - "sharded-replicaset" - ], - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "collection2", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "aggregate_out" - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll2", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$out": "aggregate_out" - } - ], - "readConcern": { - "level": "majority" - } - }, - "commandName": "aggregate", - "databaseName": "crud-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "aggregate_out", - "databaseName": "crud-tests", - "documents": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - }, - { - "description": "Aggregate with $listLocalSessions", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0", - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "database1", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - }, - { - "$addFields": { - "dummy": "dummy field" - } - }, - { - "$project": { - "_id": 0, - "dummy": 1 - } - } - ] - }, - "expectResult": [ - { - "dummy": "dummy field" - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json b/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json deleted file mode 100644 index 1f07a19bf..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-gridfs.json +++ /dev/null @@ -1,301 +0,0 @@ -{ - "description": "poc-gridfs", - "schemaVersion": "1.0", - "createEntities": [ - { - "client": { - "id": "client0" - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "gridfs-tests" - } - }, - { - "bucket": { - "id": "bucket0", - "database": "database0" - } - }, - { - "collection": { - "id": "bucket0_files_collection", - "database": "database0", - "collectionName": "fs.files" - } - }, - { - "collection": { - "id": "bucket0_chunks_collection", - "database": "database0", - "collectionName": "fs.chunks" - } - } - ], - "initialData": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000005" - }, - "length": 10, - "chunkSize": 4, - "uploadDate": { - "$date": "1970-01-01T00:00:00.000Z" - }, - "md5": "57d83cd477bfb1ccd975ab33d827a92b", - "filename": "length-10", - "contentType": "application/octet-stream", - "aliases": [], - "metadata": {} - } - ] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [ - { - "_id": { - "$oid": "000000000000000000000005" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000006" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VWZ3iA==", - "subType": "00" - } - } - }, - { - "_id": { - "$oid": "000000000000000000000007" - }, - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 2, - "data": { - "$binary": { - "base64": "mao=", - "subType": "00" - } - } - } - ] - } - ], - "tests": [ - { - "description": "Delete when length is 10", - "operations": [ - { - "name": "delete", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - } - } - ], - "outcome": [ - { - "collectionName": "fs.files", - "databaseName": "gridfs-tests", - "documents": [] - }, - { - "collectionName": "fs.chunks", - "databaseName": "gridfs-tests", - "documents": [] - } - ] - }, - { - "description": "Download when there are three chunks", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectResult": { - "$$matchesHexBytes": "112233445566778899aa" - } - } - ] - }, - { - "description": "Download when files entry does not exist", - "operations": [ - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000000" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "Download when an intermediate chunk is missing", - "operations": [ - { - "name": "deleteOne", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": { - "files_id": { - "$oid": "000000000000000000000005" - }, - "n": 1 - } - }, - "expectResult": { - "deletedCount": 1 - } - }, - { - "name": "download", - "object": "bucket0", - "arguments": { - "id": { - "$oid": "000000000000000000000005" - } - }, - "expectError": { - "isError": true - } - } - ] - }, - { - "description": "Upload when length is 5", - "operations": [ - { - "name": "upload", - "object": "bucket0", - "arguments": { - "filename": "filename", - "source": { - "$$hexBytes": "1122334455" - }, - "chunkSizeBytes": 4 - }, - "expectResult": { - "$$type": "objectId" - }, - "saveResultAsEntity": "oid0" - }, - { - "name": "find", - "object": "bucket0_files_collection", - "arguments": { - "filter": {}, - "sort": { - "uploadDate": -1 - }, - "limit": 1 - }, - "expectResult": [ - { - "_id": { - "$$matchesEntity": "oid0" - }, - "length": 5, - "chunkSize": 4, - "uploadDate": { - "$$type": "date" - }, - "md5": { - "$$unsetOrMatches": "283d4fea5dded59cf837d3047328f5af" - }, - "filename": "filename" - } - ] - }, - { - "name": "find", - "object": "bucket0_chunks_collection", - "arguments": { - "filter": { - "_id": { - "$gt": { - "$oid": "000000000000000000000007" - } - } - }, - "sort": { - "n": 1 - } - }, - "expectResult": [ - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "oid0" - }, - "n": 0, - "data": { - "$binary": { - "base64": "ESIzRA==", - "subType": "00" - } - } - }, - { - "_id": { - "$$type": "objectId" - }, - "files_id": { - "$$matchesEntity": "oid0" - }, - "n": 1, - "data": { - "$binary": { - "base64": "VQ==", - "subType": "00" - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-retryable-reads.json b/tests/UnifiedSpecTests/valid-pass/poc-retryable-reads.json deleted file mode 100644 index 2b65d501a..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-retryable-reads.json +++ /dev/null @@ -1,433 +0,0 @@ -{ - "description": "poc-retryable-reads", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "single", - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topologies": [ - "sharded" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "client": { - "id": "client1", - "uriOptions": { - "retryReads": false - }, - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-reads-tests" - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "retryable-reads-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-reads-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "tests": [ - { - "description": "Aggregate succeeds after InterruptedAtShutdown", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "aggregate" - ], - "errorCode": 11600 - } - } - } - }, - { - "name": "aggregate", - "object": "collection0", - "arguments": { - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "expectResult": [ - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "databaseName": "retryable-reads-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "aggregate": "coll", - "pipeline": [ - { - "$match": { - "_id": { - "$gt": 1 - } - } - }, - { - "$sort": { - "x": 1 - } - } - ] - }, - "databaseName": "retryable-reads-tests" - } - } - ] - } - ] - }, - { - "description": "Find succeeds on second attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 2 - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 2 - }, - "databaseName": "retryable-reads-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {}, - "sort": { - "_id": 1 - }, - "limit": 2 - }, - "databaseName": "retryable-reads-tests" - } - } - ] - } - ] - }, - { - "description": "Find fails on first attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection1", - "arguments": { - "filter": {} - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client1", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {} - }, - "databaseName": "retryable-reads-tests" - } - } - ] - } - ] - }, - { - "description": "Find fails on second attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "find" - ], - "closeConnection": true - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": {} - }, - "expectError": { - "isError": true - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {} - }, - "databaseName": "retryable-reads-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "coll", - "filter": {} - }, - "databaseName": "retryable-reads-tests" - } - } - ] - } - ] - }, - { - "description": "ListDatabases succeeds on second attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "listDatabases" - ], - "closeConnection": true - } - } - } - }, - { - "name": "listDatabases", - "object": "client0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "listDatabases": 1 - } - } - }, - { - "commandStartedEvent": { - "command": { - "listDatabases": 1 - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json b/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json deleted file mode 100644 index 50160799f..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-retryable-writes.json +++ /dev/null @@ -1,482 +0,0 @@ -{ - "description": "poc-retryable-writes", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.6", - "topologies": [ - "replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "client": { - "id": "client1", - "uriOptions": { - "retryWrites": false - }, - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "retryable-writes-tests" - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "retryable-writes-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "coll" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "coll" - } - } - ], - "initialData": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ], - "tests": [ - { - "description": "FindOneAndUpdate is committed on first attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate is not committed on first attempt", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 1 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "expectResult": { - "_id": 1, - "x": 11 - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 12 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "FindOneAndUpdate is never committed", - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "onPrimaryTransactionalWrite", - "mode": { - "times": 2 - }, - "data": { - "failBeforeCommitExceptionCode": 1 - } - } - } - }, - { - "name": "findOneAndUpdate", - "object": "collection0", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "returnDocument": "Before" - }, - "expectError": { - "isError": true - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "InsertMany succeeds after PrimarySteppedDown", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 189, - "errorLabels": [ - "RetryableWriteError" - ] - } - } - } - }, - { - "name": "insertMany", - "object": "collection0", - "arguments": { - "documents": [ - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ], - "ordered": true - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedIds": { - "$$unsetOrMatches": { - "0": 3, - "1": 4 - } - } - } - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - } - ] - } - ] - }, - { - "description": "InsertOne fails after connection failure when retryWrites option is false", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client1", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - }, - "expectError": { - "errorLabelsOmit": [ - "RetryableWriteError" - ] - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - } - ] - } - ] - }, - { - "description": "InsertOne fails after multiple retryable writeConcernErrors", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.7", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 2 - }, - "data": { - "failCommands": [ - "insert" - ], - "writeConcernError": { - "code": 91, - "errmsg": "Replication is being shut down" - } - } - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 3, - "x": 33 - } - }, - "expectError": { - "errorLabelsContain": [ - "RetryableWriteError" - ] - } - } - ], - "outcome": [ - { - "collectionName": "coll", - "databaseName": "retryable-writes-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-sessions.json b/tests/UnifiedSpecTests/valid-pass/poc-sessions.json deleted file mode 100644 index 75f348942..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-sessions.json +++ /dev/null @@ -1,466 +0,0 @@ -{ - "description": "poc-sessions", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "3.6.0" - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": false, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "session-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ], - "tests": [ - { - "description": "Server supports explicit sessions", - "operations": [ - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$sessionLsid": "session0" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "Server supports implicit sessions", - "operations": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertSameLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$type": "object" - } - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - }, - { - "description": "Dirty explicit session is discarded", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "failPoint", - "object": "testRunner", - "arguments": { - "client": "client0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "assertSessionNotDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 2 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 2 - } - } - } - }, - { - "name": "assertSessionDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 3 - } - } - } - }, - { - "name": "assertSessionDirty", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "endSession", - "object": "session0" - }, - { - "name": "find", - "object": "collection0", - "arguments": { - "filter": { - "_id": -1 - } - }, - "expectResult": [] - }, - { - "name": "assertDifferentLsidOnLastTwoCommands", - "object": "testRunner", - "arguments": { - "client": "client0" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 2 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 2 - }, - "commandName": "insert", - "databaseName": "session-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "find": "test", - "filter": { - "_id": -1 - }, - "lsid": { - "$$type": "object" - } - }, - "commandName": "find", - "databaseName": "session-tests" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "session-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json deleted file mode 100644 index 820ed6592..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions-convenient-api.json +++ /dev/null @@ -1,505 +0,0 @@ -{ - "description": "poc-transactions-convenient-api", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "client": { - "id": "client1", - "uriOptions": { - "readConcernLevel": "local", - "w": 1 - }, - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "transaction-tests" - } - }, - { - "database": { - "id": "database1", - "client": "client1", - "databaseName": "transaction-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "collection": { - "id": "collection1", - "database": "database1", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - }, - { - "session": { - "id": "session1", - "client": "client1" - } - }, - { - "session": { - "id": "session2", - "client": "client0", - "sessionOptions": { - "defaultTransactionOptions": { - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "withTransaction and no transaction options set", - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "autocommit": false, - "readConcern": { - "$$exists": false - }, - "startTransaction": { - "$$exists": false - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "withTransaction inherits transaction options from client", - "operations": [ - { - "name": "withTransaction", - "object": "session1", - "arguments": { - "callback": [ - { - "name": "insertOne", - "object": "collection1", - "arguments": { - "session": "session1", - "document": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client1", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session1" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "local" - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session1" - }, - "txnNumber": 1, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": { - "$$exists": false - }, - "startTransaction": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "withTransaction inherits transaction options from defaultTransactionOptions", - "operations": [ - { - "name": "withTransaction", - "object": "session2", - "arguments": { - "callback": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session2", - "document": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session2" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session2" - }, - "txnNumber": 1, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": { - "$$exists": false - }, - "startTransaction": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - }, - { - "description": "withTransaction explicit transaction options", - "operations": [ - { - "name": "withTransaction", - "object": "session0", - "arguments": { - "callback": [ - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 1 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 1 - } - } - } - } - ], - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "w": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 1 - } - ], - "ordered": true, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "readConcern": { - "level": "majority" - }, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "autocommit": false, - "writeConcern": { - "w": 1 - }, - "readConcern": { - "$$exists": false - }, - "startTransaction": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json deleted file mode 100644 index a0b297d59..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions-mongos-pin-auto.json +++ /dev/null @@ -1,409 +0,0 @@ -{ - "description": "poc-transactions-mongos-pin-auto", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.1.8", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "useMultipleMongoses": true, - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "transaction-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ], - "tests": [ - { - "description": "remain pinned after non-transient Interrupted error on insertOne", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 3 - } - } - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "errorCode": 11601 - } - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "expectError": { - "errorLabelsOmit": [ - "TransientTransactionError", - "UnknownTransactionCommitResult" - ], - "errorCodeName": "Interrupted" - } - }, - { - "name": "assertSessionPinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "commitTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - }, - "recoveryToken": { - "$$type": "object" - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - }, - { - "_id": 3 - } - ] - } - ] - }, - { - "description": "unpin after transient error within a transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 3 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 3 - } - } - } - }, - { - "name": "targetedFailPoint", - "object": "testRunner", - "arguments": { - "session": "session0", - "failPoint": { - "configureFailPoint": "failCommand", - "mode": { - "times": 1 - }, - "data": { - "failCommands": [ - "insert" - ], - "closeConnection": true - } - } - } - }, - { - "name": "insertOne", - "object": "collection0", - "arguments": { - "session": "session0", - "document": { - "_id": 4 - } - }, - "expectError": { - "errorLabelsContain": [ - "TransientTransactionError" - ], - "errorLabelsOmit": [ - "UnknownTransactionCommitResult" - ] - } - }, - { - "name": "assertSessionUnpinned", - "object": "testRunner", - "arguments": { - "session": "session0" - } - }, - { - "name": "abortTransaction", - "object": "session0" - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 3 - } - ], - "ordered": true, - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 4 - } - ], - "ordered": true, - "readConcern": { - "$$exists": false - }, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "insert", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "abortTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - }, - "recoveryToken": { - "$$type": "object" - } - }, - "commandName": "abortTransaction", - "databaseName": "admin" - } - } - ] - } - ], - "outcome": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [ - { - "_id": 1 - }, - { - "_id": 2 - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/valid-pass/poc-transactions.json b/tests/UnifiedSpecTests/valid-pass/poc-transactions.json deleted file mode 100644 index 0355ca206..000000000 --- a/tests/UnifiedSpecTests/valid-pass/poc-transactions.json +++ /dev/null @@ -1,323 +0,0 @@ -{ - "description": "poc-transactions", - "schemaVersion": "1.0", - "runOnRequirements": [ - { - "minServerVersion": "4.0", - "topologies": [ - "replicaset" - ] - }, - { - "minServerVersion": "4.1.8", - "topologies": [ - "sharded-replicaset" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client0", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database0", - "client": "client0", - "databaseName": "transaction-tests" - } - }, - { - "collection": { - "id": "collection0", - "database": "database0", - "collectionName": "test" - } - }, - { - "session": { - "id": "session0", - "client": "client0" - } - } - ], - "initialData": [ - { - "collectionName": "test", - "databaseName": "transaction-tests", - "documents": [] - } - ], - "tests": [ - { - "description": "Client side error in command starting transaction", - "operations": [ - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "updateOne", - "object": "collection0", - "arguments": { - "session": "session0", - "filter": { - "_id": 1 - }, - "update": { - "x": 1 - } - }, - "expectError": { - "isClientError": true - } - }, - { - "name": "assertSessionTransactionState", - "object": "testRunner", - "arguments": { - "session": "session0", - "state": "starting" - } - } - ] - }, - { - "description": "explicitly create collection using create command", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4", - "topologies": [ - "replicaset", - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "createCollection", - "object": "database0", - "arguments": { - "session": "session0", - "collection": "test" - } - }, - { - "name": "assertCollectionNotExists", - "object": "testRunner", - "arguments": { - "databaseName": "transaction-tests", - "collectionName": "test" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertCollectionExists", - "object": "testRunner", - "arguments": { - "databaseName": "transaction-tests", - "collectionName": "test" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test", - "writeConcern": { - "$$exists": false - } - }, - "commandName": "drop", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "create": "test", - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "create", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ] - }, - { - "description": "create index on a non-existing collection", - "runOnRequirements": [ - { - "minServerVersion": "4.3.4", - "topologies": [ - "replicaset", - "sharded-replicaset" - ] - } - ], - "operations": [ - { - "name": "dropCollection", - "object": "database0", - "arguments": { - "collection": "test" - } - }, - { - "name": "startTransaction", - "object": "session0" - }, - { - "name": "createIndex", - "object": "collection0", - "arguments": { - "session": "session0", - "name": "x_1", - "keys": { - "x": 1 - } - } - }, - { - "name": "assertIndexNotExists", - "object": "testRunner", - "arguments": { - "databaseName": "transaction-tests", - "collectionName": "test", - "indexName": "x_1" - } - }, - { - "name": "commitTransaction", - "object": "session0" - }, - { - "name": "assertIndexExists", - "object": "testRunner", - "arguments": { - "databaseName": "transaction-tests", - "collectionName": "test", - "indexName": "x_1" - } - } - ], - "expectEvents": [ - { - "client": "client0", - "events": [ - { - "commandStartedEvent": { - "command": { - "drop": "test", - "writeConcern": { - "$$exists": false - } - }, - "commandName": "drop", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "createIndexes": "test", - "indexes": [ - { - "name": "x_1", - "key": { - "x": 1 - } - } - ], - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": true, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "createIndexes", - "databaseName": "transaction-tests" - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session0" - }, - "txnNumber": 1, - "startTransaction": { - "$$exists": false - }, - "autocommit": false, - "writeConcern": { - "$$exists": false - } - }, - "commandName": "commitTransaction", - "databaseName": "admin" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json deleted file mode 100644 index c1c8ecce0..000000000 --- a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1-strict.json +++ /dev/null @@ -1,1109 +0,0 @@ -{ - "description": "CRUD Api Version 1 (strict)", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ], - "serverApi": { - "version": "1", - "strict": true - } - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - }, - { - "database": { - "id": "adminDatabase", - "client": "client", - "databaseName": "admin" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "_yamlAnchors": { - "versions": [ - { - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - ] - }, - "initialData": [ - { - "collectionName": "test", - "databaseName": "versioned-api-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "tests": [ - { - "description": "aggregate on collection appends declared API version", - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "aggregate on database appends declared API version", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "adminDatabase", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - } - ] - }, - "expectError": { - "errorCodeName": "APIStrictError" - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "bulkWrite appends declared API version", - "operations": [ - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 6, - "x": 66 - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "deleteMany": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 7 - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "ordered": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "x": { - "$nin": [ - 24, - 34 - ] - } - }, - "limit": 0 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 7 - }, - "limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "_id": 4, - "x": 44 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": true - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "countDocuments appends declared API version", - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "x": { - "$gt": 11 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "x": { - "$gt": 11 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "deleteMany appends declared API version", - "operations": [ - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "x": { - "$nin": [ - 24, - 34 - ] - } - }, - "limit": 0 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "deleteOne appends declared API version", - "operations": [ - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 7 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 7 - }, - "limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "distinct appends declared API version", - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": {} - }, - "expectError": { - "isError": true, - "errorContains": "command distinct is not in API Version 1", - "errorCodeName": "APIStrictError" - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "test", - "key": "x", - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount appends declared API version", - "runOnRequirements": [ - { - "minServerVersion": "5.0.9", - "maxServerVersion": "5.0.99" - }, - { - "minServerVersion": "5.3.2" - } - ], - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "arguments": {} - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "test", - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "find and getMore append API version", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "batchSize": 3 - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndDelete appends declared API version", - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "remove": true, - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndReplace appends declared API version", - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "findOneAndUpdate appends declared API version", - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "insertMany appends declared API version", - "operations": [ - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 6, - "x": 66 - }, - { - "_id": 7, - "x": 77 - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - }, - { - "_id": 7, - "x": 77 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "insertOne appends declared API version", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 6, - "x": 66 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "replaceOne appends declared API version", - "operations": [ - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "_id": 4, - "x": 44 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": true - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "updateMany appends declared API version", - "operations": [ - { - "name": "updateMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "updateOne appends declared API version", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json b/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json deleted file mode 100644 index a387d0587..000000000 --- a/tests/UnifiedSpecTests/versioned-api/crud-api-version-1.json +++ /dev/null @@ -1,1101 +0,0 @@ -{ - "description": "CRUD Api Version 1", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.9" - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ], - "serverApi": { - "version": "1", - "deprecationErrors": true - } - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - }, - { - "database": { - "id": "adminDatabase", - "client": "client", - "databaseName": "admin" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - } - ], - "_yamlAnchors": { - "versions": [ - { - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - ] - }, - "initialData": [ - { - "collectionName": "test", - "databaseName": "versioned-api-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "tests": [ - { - "description": "aggregate on collection appends declared API version", - "operations": [ - { - "name": "aggregate", - "object": "collection", - "arguments": { - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$sort": { - "x": 1 - } - }, - { - "$match": { - "_id": { - "$gt": 1 - } - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "aggregate on database appends declared API version", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "aggregate", - "object": "adminDatabase", - "arguments": { - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": 1, - "pipeline": [ - { - "$listLocalSessions": {} - }, - { - "$limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "bulkWrite appends declared API version", - "operations": [ - { - "name": "bulkWrite", - "object": "collection", - "arguments": { - "requests": [ - { - "insertOne": { - "document": { - "_id": 6, - "x": 66 - } - } - }, - { - "updateOne": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "deleteMany": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - }, - { - "updateMany": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - }, - { - "deleteOne": { - "filter": { - "_id": 7 - } - } - }, - { - "replaceOne": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "ordered": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "x": { - "$nin": [ - 24, - 34 - ] - } - }, - "limit": 0 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 7 - }, - "limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "_id": 4, - "x": 44 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": true - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "countDocuments appends declared API version", - "operations": [ - { - "name": "countDocuments", - "object": "collection", - "arguments": { - "filter": { - "x": { - "$gt": 11 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "aggregate": "test", - "pipeline": [ - { - "$match": { - "x": { - "$gt": 11 - } - } - }, - { - "$group": { - "_id": 1, - "n": { - "$sum": 1 - } - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "deleteMany appends declared API version", - "operations": [ - { - "name": "deleteMany", - "object": "collection", - "arguments": { - "filter": { - "x": { - "$nin": [ - 24, - 34 - ] - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "x": { - "$nin": [ - 24, - 34 - ] - } - }, - "limit": 0 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "deleteOne appends declared API version", - "operations": [ - { - "name": "deleteOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 7 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "delete": "test", - "deletes": [ - { - "q": { - "_id": 7 - }, - "limit": 1 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "distinct appends declared API version", - "operations": [ - { - "name": "distinct", - "object": "collection", - "arguments": { - "fieldName": "x", - "filter": {} - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "distinct": "test", - "key": "x", - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "estimatedDocumentCount appends declared API version", - "runOnRequirements": [ - { - "minServerVersion": "5.0.9", - "maxServerVersion": "5.0.99" - }, - { - "minServerVersion": "5.3.2" - } - ], - "operations": [ - { - "name": "estimatedDocumentCount", - "object": "collection", - "arguments": {} - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "count": "test", - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "find and getMore append API version", - "operations": [ - { - "name": "find", - "object": "collection", - "arguments": { - "filter": {}, - "sort": { - "_id": 1 - }, - "batchSize": 3 - }, - "expectResult": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "find": "test", - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - }, - { - "commandStartedEvent": { - "command": { - "getMore": { - "$$type": [ - "int", - "long" - ] - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "findOneAndDelete appends declared API version", - "operations": [ - { - "name": "findOneAndDelete", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "remove": true, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "findOneAndReplace appends declared API version", - "operations": [ - { - "name": "findOneAndReplace", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - }, - "replacement": { - "x": 33 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "x": 33 - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "findOneAndUpdate appends declared API version", - "operations": [ - { - "name": "findOneAndUpdate", - "object": "collection", - "arguments": { - "filter": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "findAndModify": "test", - "query": { - "_id": 1 - }, - "update": { - "$inc": { - "x": 1 - } - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "insertMany appends declared API version", - "operations": [ - { - "name": "insertMany", - "object": "collection", - "arguments": { - "documents": [ - { - "_id": 6, - "x": 66 - }, - { - "_id": 7, - "x": 77 - } - ] - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - }, - { - "_id": 7, - "x": 77 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "insertOne appends declared API version", - "operations": [ - { - "name": "insertOne", - "object": "collection", - "arguments": { - "document": { - "_id": 6, - "x": 66 - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "replaceOne appends declared API version", - "operations": [ - { - "name": "replaceOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 4 - }, - "replacement": { - "_id": 4, - "x": 44 - }, - "upsert": true - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 4 - }, - "u": { - "_id": 4, - "x": 44 - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": true - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "updateMany appends declared API version", - "operations": [ - { - "name": "updateMany", - "object": "collection", - "arguments": { - "filter": { - "_id": { - "$gt": 1 - } - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": { - "$gt": 1 - } - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": true, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - }, - { - "description": "updateOne appends declared API version", - "operations": [ - { - "name": "updateOne", - "object": "collection", - "arguments": { - "filter": { - "_id": 2 - }, - "update": { - "$inc": { - "x": 1 - } - } - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "update": "test", - "updates": [ - { - "q": { - "_id": 2 - }, - "u": { - "$inc": { - "x": 1 - } - }, - "multi": { - "$$unsetOrMatches": false - }, - "upsert": { - "$$unsetOrMatches": false - } - } - ], - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json b/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json deleted file mode 100644 index 17e0126d1..000000000 --- a/tests/UnifiedSpecTests/versioned-api/runcommand-helper-no-api-version-declared.json +++ /dev/null @@ -1,127 +0,0 @@ -{ - "description": "RunCommand helper: No API version declared", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.9", - "serverParameters": { - "requireApiVersion": false - } - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - } - ], - "tests": [ - { - "description": "runCommand does not inspect or change the command document", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1, - "apiVersion": "server_will_never_support_this_api_version" - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1, - "apiVersion": "server_will_never_support_this_api_version", - "apiStrict": { - "$$exists": false - }, - "apiDeprecationErrors": { - "$$exists": false - } - }, - "commandName": "ping", - "databaseName": "versioned-api-tests" - } - } - ] - } - ] - }, - { - "description": "runCommand does not prevent sending invalid API version declarations", - "runOnRequirements": [ - { - "serverless": "forbid" - } - ], - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "ping", - "command": { - "ping": 1, - "apiStrict": true - } - }, - "expectError": { - "isError": true, - "isClientError": false - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "ping": 1, - "apiVersion": { - "$$exists": false - }, - "apiStrict": true, - "apiDeprecationErrors": { - "$$exists": false - } - }, - "commandName": "ping", - "databaseName": "versioned-api-tests" - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json b/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json deleted file mode 100644 index 0668df830..000000000 --- a/tests/UnifiedSpecTests/versioned-api/test-commands-deprecation-errors.json +++ /dev/null @@ -1,74 +0,0 @@ -{ - "description": "Test commands: deprecation errors", - "schemaVersion": "1.1", - "runOnRequirements": [ - { - "minServerVersion": "4.9", - "serverParameters": { - "enableTestCommands": true, - "acceptApiVersion2": true, - "requireApiVersion": false - } - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ] - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - } - ], - "tests": [ - { - "description": "Running a command that is deprecated raises a deprecation error", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "testDeprecationInVersion2", - "command": { - "testDeprecationInVersion2": 1, - "apiVersion": "2", - "apiDeprecationErrors": true - } - }, - "expectError": { - "isError": true, - "errorContains": "command testDeprecationInVersion2 is deprecated in API Version 2", - "errorCodeName": "APIDeprecationError" - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "testDeprecationInVersion2": 1, - "apiVersion": "2", - "apiStrict": { - "$$exists": false - }, - "apiDeprecationErrors": true - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json b/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json deleted file mode 100644 index 9c4ebea78..000000000 --- a/tests/UnifiedSpecTests/versioned-api/test-commands-strict-mode.json +++ /dev/null @@ -1,75 +0,0 @@ -{ - "description": "Test commands: strict mode", - "schemaVersion": "1.4", - "runOnRequirements": [ - { - "minServerVersion": "4.9", - "serverParameters": { - "enableTestCommands": true - }, - "serverless": "forbid" - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ], - "serverApi": { - "version": "1", - "strict": true - } - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - } - ], - "tests": [ - { - "description": "Running a command that is not part of the versioned API results in an error", - "operations": [ - { - "name": "runCommand", - "object": "database", - "arguments": { - "commandName": "testVersion2", - "command": { - "testVersion2": 1 - } - }, - "expectError": { - "isError": true, - "errorContains": "command testVersion2 is not in API Version 1", - "errorCodeName": "APIStrictError" - } - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "testVersion2": 1, - "apiVersion": "1", - "apiStrict": true, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/UnifiedSpecTests/versioned-api/transaction-handling.json b/tests/UnifiedSpecTests/versioned-api/transaction-handling.json deleted file mode 100644 index c00c5240a..000000000 --- a/tests/UnifiedSpecTests/versioned-api/transaction-handling.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "description": "Transaction handling", - "schemaVersion": "1.3", - "runOnRequirements": [ - { - "minServerVersion": "4.9", - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ] - } - ], - "createEntities": [ - { - "client": { - "id": "client", - "observeEvents": [ - "commandStartedEvent" - ], - "serverApi": { - "version": "1" - } - } - }, - { - "database": { - "id": "database", - "client": "client", - "databaseName": "versioned-api-tests" - } - }, - { - "collection": { - "id": "collection", - "database": "database", - "collectionName": "test" - } - }, - { - "session": { - "id": "session", - "client": "client" - } - } - ], - "_yamlAnchors": { - "versions": [ - { - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - ] - }, - "initialData": [ - { - "collectionName": "test", - "databaseName": "versioned-api-tests", - "documents": [ - { - "_id": 1, - "x": 11 - }, - { - "_id": 2, - "x": 22 - }, - { - "_id": 3, - "x": 33 - }, - { - "_id": 4, - "x": 44 - }, - { - "_id": 5, - "x": 55 - } - ] - } - ], - "tests": [ - { - "description": "All commands in a transaction declare an API version", - "runOnRequirements": [ - { - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ] - } - ], - "operations": [ - { - "name": "startTransaction", - "object": "session" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session", - "document": { - "_id": 6, - "x": 66 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 6 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session", - "document": { - "_id": 7, - "x": 77 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 7 - } - } - } - }, - { - "name": "commitTransaction", - "object": "session" - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "lsid": { - "$$sessionLsid": "session" - }, - "startTransaction": true, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 7, - "x": 77 - } - ], - "lsid": { - "$$sessionLsid": "session" - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "commitTransaction": 1, - "lsid": { - "$$sessionLsid": "session" - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - }, - { - "description": "abortTransaction includes an API version", - "runOnRequirements": [ - { - "topologies": [ - "replicaset", - "sharded-replicaset", - "load-balanced" - ] - } - ], - "operations": [ - { - "name": "startTransaction", - "object": "session" - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session", - "document": { - "_id": 6, - "x": 66 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 6 - } - } - } - }, - { - "name": "insertOne", - "object": "collection", - "arguments": { - "session": "session", - "document": { - "_id": 7, - "x": 77 - } - }, - "expectResult": { - "$$unsetOrMatches": { - "insertedId": { - "$$unsetOrMatches": 7 - } - } - } - }, - { - "name": "abortTransaction", - "object": "session" - } - ], - "expectEvents": [ - { - "client": "client", - "events": [ - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 6, - "x": 66 - } - ], - "lsid": { - "$$sessionLsid": "session" - }, - "startTransaction": true, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "insert": "test", - "documents": [ - { - "_id": 7, - "x": 77 - } - ], - "lsid": { - "$$sessionLsid": "session" - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - }, - { - "commandStartedEvent": { - "command": { - "abortTransaction": 1, - "lsid": { - "$$sessionLsid": "session" - }, - "apiVersion": "1", - "apiStrict": { - "$$unsetOrMatches": false - }, - "apiDeprecationErrors": { - "$$unsetOrMatches": false - } - } - } - } - ] - } - ] - } - ] -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php new file mode 100644 index 000000000..93032dc29 --- /dev/null +++ b/tests/bootstrap.php @@ -0,0 +1,22 @@ +register(new Int64Comparator()); +ComparatorFactory::getInstance()->register(new ServerComparator()); + +/* Ugly workaround for event system changes in PHPUnit + * PHPUnit 10 introduces a new event system, and at the same time removes the + * event system present in previous versions. This makes it near impossible to + * support PHPUnit 8.5 (required for PHP 7.2) and PHPUnit 9 (PHP < 8.1) + * alongside PHPUnit 10. For this reason, we use this bootstrap file to print + * test configuration summary. + * @todo PHP_VERSION_ID >= 80100: Remove this and use an extension + */ +TestCase::printConfiguration(); diff --git a/tests/drivers-evergreen-tools b/tests/drivers-evergreen-tools new file mode 160000 index 000000000..f8ab2a54f --- /dev/null +++ b/tests/drivers-evergreen-tools @@ -0,0 +1 @@ +Subproject commit f8ab2a54f774cab1e92bcf222949181baf8b2f1c diff --git a/tests/specifications b/tests/specifications new file mode 160000 index 000000000..d41d48b90 --- /dev/null +++ b/tests/specifications @@ -0,0 +1 @@ +Subproject commit d41d48b90ef22aedae934dd22f3a21c65b5a13bc diff --git a/tools/connect.php b/tools/connect.php index 121f727c7..1b7dfa567 100644 --- a/tools/connect.php +++ b/tools/connect.php @@ -2,14 +2,14 @@ function getHosts(string $uri): array { - if (strpos($uri, '://') === false) { + if (! str_contains($uri, '://')) { return [$uri]; } $parsed = parse_url($uri); if (isset($parsed['scheme']) && $parsed['scheme'] !== 'mongodb') { - // TODO: Resolve SRV records (https://github.com/mongodb/specifications/blob/master/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.rst) + // TODO: Resolve SRV records (https://github.com/mongodb/specifications/blob/master/source/initial-dns-seedlist-discovery/initial-dns-seedlist-discovery.md) throw new RuntimeException('Unsupported scheme: ' . $parsed['scheme']); } @@ -92,7 +92,7 @@ function connect(string $host, bool $ssl): void 'admin.$cmd', /* namespace */ 0, /* numberToSkip */ 1, /* numberToReturn */ - hex2bin('130000001069734d6173746572000100000000') /* { "isMaster": 1 } */ + hex2bin('130000001069734d6173746572000100000000'), /* { "isMaster": 1 } */ ); $requestLength = 16 /* MsgHeader length */ + strlen($request); $header = pack('V4', $requestLength, 0 /* requestID */, 0 /* responseTo */, 2004 /* OP_QUERY */); diff --git a/tools/detect-extension.php b/tools/detect-extension.php index 4b599fd4b..1741a4328 100644 --- a/tools/detect-extension.php +++ b/tools/detect-extension.php @@ -5,11 +5,11 @@ function grepIniFile(string $filename, string $extension): int $lines = []; foreach (new SplFileObject($filename) as $i => $line) { - if (strpos($line, 'extension') === false) { + if (! str_contains($line, 'extension')) { continue; } - if (strpos($line, $extension) === false) { + if (! str_contains($line, $extension)) { continue; } @@ -85,7 +85,7 @@ function grepIniFile(string $filename, string $extension): int if (defined('PHP_WINDOWS_VERSION_BUILD')) { $zts = PHP_ZTS ? 'Thread Safe (TS)' : 'Non Thread Safe (NTS)'; $arch = PHP_INT_SIZE === 8 ? 'x64' : 'x86'; - $dll = sprintf("%d.%d %s %s", PHP_MAJOR_VERSION, PHP_MINOR_VERSION, $zts, $arch); + $dll = sprintf('%d.%d %s %s', PHP_MAJOR_VERSION, PHP_MINOR_VERSION, $zts, $arch); printf("You likely need to download a Windows DLL for: %s\n", $dll); printf("Windows DLLs should be available from: https://pecl.php.net/package/%s\n", $extension);